]> git.saurik.com Git - wxWidgets.git/blame - src/generic/combog.cpp
Allow passing multi-line strings to wxDC::DrawText(), even under MSW.
[wxWidgets.git] / src / generic / combog.cpp
CommitLineData
a340b80d 1/////////////////////////////////////////////////////////////////////////////
85fed18c 2// Name: src/generic/combog.cpp
a57d600f 3// Purpose: Generic wxComboCtrl
a340b80d
VZ
4// Author: Jaakko Salli
5// Modified by:
6// Created: Apr-30-2006
7// RCS-ID: $Id$
8// Copyright: (c) 2005 Jaakko Salli
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
a57d600f 26#if wxUSE_COMBOCTRL
a340b80d 27
85fed18c
WS
28#include "wx/combo.h"
29
a340b80d
VZ
30#ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/combobox.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
eae20a30 35 #include "wx/textctrl.h"
a340b80d
VZ
36#endif
37
38#include "wx/dcbuffer.h"
39
a340b80d
VZ
40// ----------------------------------------------------------------------------
41// Some constant adjustments to make the generic more bearable
42
43#if defined(__WXUNIVERSAL__)
44
0847e36e
JS
45// position adjustment for wxTextCtrl, to achieve zero left margin
46// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
47#define TEXTCTRLXADJUST 0
48
a340b80d
VZ
49#define TEXTCTRLYADJUST 0
50#define TEXTXADJUST 0 // how much is read-only text's x adjusted
51#define DEFAULT_DROPBUTTON_WIDTH 19
52
53#elif defined(__WXMSW__)
54
0847e36e
JS
55// position adjustment for wxTextCtrl, to achieve zero left margin
56// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
57#define TEXTCTRLXADJUST 2
58
a340b80d
VZ
59#define TEXTCTRLYADJUST 3
60#define TEXTXADJUST 0 // how much is read-only text's x adjusted
61#define DEFAULT_DROPBUTTON_WIDTH 17
62
63#elif defined(__WXGTK__)
64
0847e36e
JS
65// position adjustment for wxTextCtrl, to achieve zero left margin
66// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
67#define TEXTCTRLXADJUST -1
68
a340b80d
VZ
69#define TEXTCTRLYADJUST 0
70#define TEXTXADJUST 1 // how much is read-only text's x adjusted
71#define DEFAULT_DROPBUTTON_WIDTH 23
72
73#elif defined(__WXMAC__)
74
0847e36e
JS
75// position adjustment for wxTextCtrl, to achieve zero left margin
76// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
77#define TEXTCTRLXADJUST 0
78
a340b80d
VZ
79#define TEXTCTRLYADJUST 0
80#define TEXTXADJUST 0 // how much is read-only text's x adjusted
f3fcecd6 81#define DEFAULT_DROPBUTTON_WIDTH 22
a340b80d
VZ
82
83#else
84
0847e36e
JS
85// position adjustment for wxTextCtrl, to achieve zero left margin
86// meaningless if LEFT_MARGIN_CAN_BE_SET set to 1 in combocmn.cpp
87#define TEXTCTRLXADJUST 0
88
a340b80d
VZ
89#define TEXTCTRLYADJUST 0
90#define TEXTXADJUST 0 // how much is read-only text's x adjusted
91#define DEFAULT_DROPBUTTON_WIDTH 19
92
93#endif
94
95
96// ============================================================================
97// implementation
98// ============================================================================
99
100// Only implement if no native or it wasn't fully featured
101#ifndef wxCOMBOCONTROL_FULLY_FEATURED
102
103
104// ----------------------------------------------------------------------------
129c8cf3 105// wxGenericComboCtrl
a340b80d
VZ
106// ----------------------------------------------------------------------------
107
129c8cf3
RR
108BEGIN_EVENT_TABLE(wxGenericComboCtrl, wxComboCtrlBase)
109 EVT_PAINT(wxGenericComboCtrl::OnPaintEvent)
110 EVT_MOUSE_EVENTS(wxGenericComboCtrl::OnMouseEvent)
a340b80d
VZ
111END_EVENT_TABLE()
112
113
129c8cf3 114IMPLEMENT_DYNAMIC_CLASS(wxGenericComboCtrl, wxComboCtrlBase)
a340b80d 115
129c8cf3 116void wxGenericComboCtrl::Init()
a340b80d
VZ
117{
118}
119
129c8cf3
RR
120bool wxGenericComboCtrl::Create(wxWindow *parent,
121 wxWindowID id,
122 const wxString& value,
123 const wxPoint& pos,
124 const wxSize& size,
125 long style,
126 const wxValidator& validator,
127 const wxString& name)
a340b80d 128{
8e9ec723
RR
129 //
130 // Note that technically we only support 'default' border and wxNO_BORDER.
a340b80d 131 long border = style & wxBORDER_MASK;
8e9ec723 132 int tcBorder = wxNO_BORDER;
a340b80d 133
a340b80d 134#if defined(__WXUNIVERSAL__)
8e9ec723 135 if ( !border )
a340b80d
VZ
136 border = wxBORDER_SIMPLE;
137#elif defined(__WXMSW__)
8e9ec723 138 if ( !border )
a340b80d 139 // For XP, have 1-width custom border, for older version use sunken
129c8cf3 140 /*if ( wxUxThemeEngine::GetIfActive() )
a340b80d
VZ
141 {
142 border = wxBORDER_NONE;
143 m_widthCustomBorder = 1;
144 }
129c8cf3 145 else*/
a340b80d 146 border = wxBORDER_SUNKEN;
a340b80d 147#else
a340b80d 148
8e9ec723
RR
149 //
150 // Generic version is optimized for wxGTK
151 //
152
153 #define UNRELIABLE_TEXTCTRL_BORDER
154
155 if ( !border )
156 {
157 if ( style & wxCB_READONLY )
158 {
159 m_widthCustomBorder = 1;
160 }
161 else
162 {
163 m_widthCustomBorder = 0;
164 tcBorder = 0;
165 }
166 }
167 else
168 {
169 // Have textctrl instead use the border given.
170 tcBorder = border;
a340b80d
VZ
171 }
172
8e9ec723
RR
173 // Because we are going to have button outside the border,
174 // let's use wxBORDER_NONE for the whole control.
175 border = wxBORDER_NONE;
176
a340b80d 177 Customize( wxCC_BUTTON_OUTSIDE_BORDER |
c905c0d6
VZ
178 wxCC_NO_TEXT_AUTO_SELECT |
179 wxCC_BUTTON_STAYS_DOWN );
8e9ec723 180
a340b80d
VZ
181#endif
182
8e9ec723 183 style = (style & ~(wxBORDER_MASK)) | border;
a340b80d
VZ
184 if ( style & wxCC_STD_BUTTON )
185 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
186
187 // create main window
a57d600f 188 if ( !wxComboCtrlBase::Create(parent,
93f7f8be
WS
189 id,
190 value,
191 pos,
192 size,
193 style | wxFULL_REPAINT_ON_RESIZE,
194 wxDefaultValidator,
195 name) )
a340b80d
VZ
196 return false;
197
198 // Create textctrl, if necessary
8e9ec723 199 CreateTextCtrl( tcBorder, validator );
a340b80d
VZ
200
201 // Add keyboard input handlers for main control and textctrl
b445b6a7 202 InstallInputHandlers();
a340b80d
VZ
203
204 // Set background
205 SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
206
170acdc9
RD
207 // SetInitialSize should be called last
208 SetInitialSize(size);
a340b80d
VZ
209
210 return true;
211}
212
129c8cf3 213wxGenericComboCtrl::~wxGenericComboCtrl()
a340b80d
VZ
214{
215}
216
129c8cf3 217void wxGenericComboCtrl::OnResize()
a340b80d
VZ
218{
219
220 // Recalculates button and textctrl areas
221 CalculateAreas(DEFAULT_DROPBUTTON_WIDTH);
222
223#if 0
224 // Move separate button control, if any, to correct position
225 if ( m_btn )
226 {
227 wxSize sz = GetClientSize();
228 m_btn->SetSize( m_btnArea.x + m_btnSpacingX,
229 (sz.y-m_btnSize.y)/2,
230 m_btnSize.x,
231 m_btnSize.y );
232 }
233#endif
234
235 // Move textctrl, if any, accordingly
236 PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
237}
238
129c8cf3 239void wxGenericComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
a340b80d
VZ
240{
241 wxSize sz = GetClientSize();
2e992e06 242 wxAutoBufferedPaintDC dc(this);
a340b80d
VZ
243
244 const wxRect& rectb = m_btnArea;
245 wxRect rect = m_tcArea;
246
247 // artificial simple border
248 if ( m_widthCustomBorder )
249 {
250 int customBorder = m_widthCustomBorder;
251
252 // Set border colour
253 wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
254 customBorder,
04ee05f9 255 wxPENSTYLE_SOLID);
a340b80d
VZ
256 dc.SetPen( pen1 );
257
258 // area around both controls
259 wxRect rect2(0,0,sz.x,sz.y);
260 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
261 {
262 rect2 = m_tcArea;
263 if ( customBorder == 1 )
264 {
265 rect2.Inflate(1);
266 }
267 else
268 {
269 #ifdef __WXGTK__
270 rect2.x -= 1;
271 rect2.y -= 1;
272 #else
273 rect2.x -= customBorder;
274 rect2.y -= customBorder;
275 #endif
276 rect2.width += 1 + customBorder;
277 rect2.height += 1 + customBorder;
278 }
279 }
280
281 dc.SetBrush( *wxTRANSPARENT_BRUSH );
282 dc.DrawRectangle(rect2);
283 }
284
2dfa37d6 285#ifndef __WXMAC__ // see note in OnThemeChange
a340b80d 286 wxColour winCol = GetBackgroundColour();
2dfa37d6
RD
287#else
288 wxColour winCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
289#endif
a340b80d
VZ
290 dc.SetBrush(winCol);
291 dc.SetPen(winCol);
292
293 //wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
294 //wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
295
296 // clear main background
297 dc.DrawRectangle(rect);
03647350 298
a340b80d 299 if ( !m_btn )
373d466f 300 {
a340b80d 301 // Standard button rendering
373d466f
VZ
302 DrawButton(dc,rectb);
303 }
a340b80d
VZ
304
305 // paint required portion on the control
6d0ce565 306 if ( (!m_text || m_widthCustomPaint) )
a340b80d
VZ
307 {
308 wxASSERT( m_widthCustomPaint >= 0 );
309
310 // this is intentionally here to allow drawed rectangle's
311 // right edge to be hidden
312 if ( m_text )
313 rect.width = m_widthCustomPaint;
314
315 dc.SetFont( GetFont() );
316
317 dc.SetClippingRegion(rect);
6d0ce565
VZ
318 if ( m_popupInterface )
319 m_popupInterface->PaintComboControl(dc,rect);
320 else
321 wxComboPopup::DefaultPaintComboControl(this,dc,rect);
a340b80d
VZ
322 }
323}
324
129c8cf3 325void wxGenericComboCtrl::OnMouseEvent( wxMouseEvent& event )
a340b80d 326{
1efad474
RR
327 int mx = event.m_x;
328 bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
a340b80d
VZ
329 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
330
a340b80d
VZ
331 if ( PreprocessMouseEvent(event,handlerFlags) )
332 return;
333
406d283a 334 const bool ctrlIsButton = wxPlatformIs(wxOS_WINDOWS);
6d0ce565
VZ
335
336 if ( ctrlIsButton &&
337 (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
a340b80d
VZ
338 {
339 // if no textctrl and no special double-click, then the entire control acts
340 // as a button
341 handlerFlags |= wxCC_MF_ON_BUTTON;
342 if ( HandleButtonMouseEvent(event,handlerFlags) )
343 return;
344 }
345 else
346 {
1efad474
RR
347 if ( isOnButtonArea || HasCapture() ||
348 (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
a340b80d 349 {
1efad474
RR
350 handlerFlags |= wxCC_MF_ON_CLICK_AREA;
351
a340b80d
VZ
352 if ( HandleButtonMouseEvent(event,handlerFlags) )
353 return;
354 }
355 else if ( m_btnState )
356 {
357 // otherwise need to clear the hover status
358 m_btnState = 0;
359 RefreshRect(m_btnArea);
360 }
361 }
362
363 //
364 // This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
365 // See header file for further information on this method.
366 HandleNormalMouseEvent(event);
367
368}
369
8e9ec723
RR
370void wxGenericComboCtrl::SetCustomPaintWidth( int width )
371{
372#ifdef UNRELIABLE_TEXTCTRL_BORDER
373 //
374 // If starting/stopping to show an image in front
375 // of a writable text-field, then re-create textctrl
376 // with different kind of border (because we can't
377 // assume that textctrl fully supports wxNO_BORDER).
378 //
379 wxTextCtrl* tc = GetTextCtrl();
380
381 if ( tc && (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) )
382 {
383 int borderType = tc->GetWindowStyle() & wxBORDER_MASK;
384 int tcCreateStyle = -1;
385
386 if ( width > 0 )
387 {
388 // Re-create textctrl with no border
389 if ( borderType != wxNO_BORDER )
390 {
391 m_widthCustomBorder = 1;
392 tcCreateStyle = wxNO_BORDER;
393 }
394 }
395 else if ( width == 0 )
396 {
397 // Re-create textctrl with normal border
398 if ( borderType == wxNO_BORDER )
399 {
400 m_widthCustomBorder = 0;
401 tcCreateStyle = 0;
402 }
403 }
404
405 // Common textctrl re-creation code
406 if ( tcCreateStyle != -1 )
407 {
408 tc->RemoveEventHandler(m_textEvtHandler);
409 delete m_textEvtHandler;
410
43147cef 411#if wxUSE_VALIDATORS
8e9ec723
RR
412 wxValidator* pValidator = tc->GetValidator();
413 if ( pValidator )
414 {
415 pValidator = (wxValidator*) pValidator->Clone();
416 CreateTextCtrl( tcCreateStyle, *pValidator );
417 delete pValidator;
418 }
419 else
43147cef 420#endif
8e9ec723
RR
421 {
422 CreateTextCtrl( tcCreateStyle, wxDefaultValidator );
423 }
424
425 InstallInputHandlers();
426 }
427 }
428#endif // UNRELIABLE_TEXTCTRL_BORDER
429
430 wxComboCtrlBase::SetCustomPaintWidth( width );
431}
432
129c8cf3 433bool wxGenericComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
b445b6a7
VZ
434{
435 int keycode = event.GetKeyCode();
436 bool isPopupShown = IsPopupShown();
437
438 // This code is AFAIK appropriate for wxGTK.
439
440 if ( isPopupShown )
441 {
442 if ( keycode == WXK_ESCAPE ||
443 ( keycode == WXK_UP && event.AltDown() ) )
444 return true;
445 }
446 else
447 {
3b354b96
RR
448 if ( (keycode == WXK_DOWN && event.AltDown()) ||
449 (keycode == WXK_F4) )
b445b6a7
VZ
450 return true;
451 }
452
453 return false;
454}
455
a340b80d
VZ
456#ifdef __WXUNIVERSAL__
457
129c8cf3
RR
458bool wxGenericComboCtrl::PerformAction(const wxControlAction& action,
459 long numArg,
460 const wxString& strArg)
a340b80d
VZ
461{
462 bool processed = false;
463 if ( action == wxACTION_COMBOBOX_POPUP )
464 {
a305d600 465 if ( !IsPopupShown() )
a340b80d
VZ
466 {
467 ShowPopup();
468
469 processed = true;
470 }
471 }
472 else if ( action == wxACTION_COMBOBOX_DISMISS )
473 {
a305d600 474 if ( IsPopupShown() )
a340b80d
VZ
475 {
476 HidePopup();
477
478 processed = true;
479 }
480 }
481
482 if ( !processed )
483 {
484 // pass along
485 return wxControl::PerformAction(action, numArg, strArg);
486 }
487
488 return true;
489}
490
491#endif // __WXUNIVERSAL__
492
a57d600f 493// If native wxComboCtrl was not defined, then prepare a simple
a340b80d
VZ
494// front-end so that wxRTTI works as expected.
495#ifndef _WX_COMBOCONTROL_H_
129c8cf3 496IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxGenericComboCtrl)
a340b80d
VZ
497#endif
498
499#endif // !wxCOMBOCONTROL_FULLY_FEATURED
500
a57d600f 501#endif // wxUSE_COMBOCTRL