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