Use flat generic status bar by default and add wxSB_SUNKEN.
[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 #ifdef __WXMAC__
259 wxPen pen1( wxColour(133,133,133),
260 customBorder,
261 wxSOLID );
262 #else
263 wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
264 customBorder,
265 wxPENSTYLE_SOLID);
266 #endif
267 dc.SetPen( pen1 );
268
269 // area around both controls
270 wxRect rect2(fullRect);
271 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
272 {
273 rect2 = tcRect;
274 if ( customBorder == 1 )
275 {
276 rect2.Inflate(1);
277 }
278 else
279 {
280 #ifdef __WXGTK__
281 rect2.x -= 1;
282 rect2.y -= 1;
283 #else
284 rect2.x -= customBorder;
285 rect2.y -= customBorder;
286 #endif
287 rect2.width += 1 + customBorder;
288 rect2.height += 1 + customBorder;
289 }
290 }
291
292 dc.SetBrush( *wxTRANSPARENT_BRUSH );
293 dc.DrawRectangle(rect2);
294 }
295
296 // Clear the main background if the system doesn't do it by itself
297 if ( !HasTransparentBackground() &&
298 (tcRect.x > 0 || tcRect.y > 0) )
299 {
300 wxColour winCol = GetParent()->GetBackgroundColour();
301 dc.SetBrush(winCol);
302 dc.SetPen(winCol);
303
304 dc.DrawRectangle(fullRect);
305 }
306
307 if ( !m_btn )
308 {
309 // Standard button rendering
310 DrawButton(dc, butRect);
311 }
312
313 // paint required portion on the control
314 if ( !m_text || m_widthCustomPaint )
315 {
316 wxASSERT( m_widthCustomPaint >= 0 );
317
318 // Clear the text-control area background
319 wxColour tcCol = GetBackgroundColour();
320 dc.SetBrush(tcCol);
321 dc.SetPen(tcCol);
322 dc.DrawRectangle(tcRect);
323
324 // this is intentionally here to allow drawed rectangle's
325 // right edge to be hidden
326 if ( m_text )
327 tcRect.width = m_widthCustomPaint;
328
329 dc.SetFont( GetFont() );
330
331 dc.SetClippingRegion(tcRect);
332 if ( m_popupInterface )
333 m_popupInterface->PaintComboControl(dc, tcRect);
334 else
335 wxComboPopup::DefaultPaintComboControl(this, dc, tcRect);
336 }
337
338 delete dcPtr;
339 }
340
341 void wxGenericComboCtrl::OnMouseEvent( wxMouseEvent& event )
342 {
343 int mx = event.m_x;
344 bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
345 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
346
347 if ( PreprocessMouseEvent(event,handlerFlags) )
348 return;
349
350 const bool ctrlIsButton = wxPlatformIs(wxOS_WINDOWS);
351
352 if ( ctrlIsButton &&
353 (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
354 {
355 // if no textctrl and no special double-click, then the entire control acts
356 // as a button
357 handlerFlags |= wxCC_MF_ON_BUTTON;
358 if ( HandleButtonMouseEvent(event,handlerFlags) )
359 return;
360 }
361 else
362 {
363 if ( isOnButtonArea || HasCapture() ||
364 (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
365 {
366 handlerFlags |= wxCC_MF_ON_CLICK_AREA;
367
368 if ( HandleButtonMouseEvent(event,handlerFlags) )
369 return;
370 }
371 else if ( m_btnState )
372 {
373 // otherwise need to clear the hover status
374 m_btnState = 0;
375 RefreshRect(m_btnArea);
376 }
377 }
378
379 //
380 // This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
381 // See header file for further information on this method.
382 HandleNormalMouseEvent(event);
383
384 }
385
386 void wxGenericComboCtrl::SetCustomPaintWidth( int width )
387 {
388 #ifdef UNRELIABLE_TEXTCTRL_BORDER
389 //
390 // If starting/stopping to show an image in front
391 // of a writable text-field, then re-create textctrl
392 // with different kind of border (because we can't
393 // assume that textctrl fully supports wxNO_BORDER).
394 //
395 wxTextCtrl* tc = GetTextCtrl();
396
397 if ( tc && (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) )
398 {
399 int borderType = tc->GetWindowStyle() & wxBORDER_MASK;
400 int tcCreateStyle = -1;
401
402 if ( width > 0 )
403 {
404 // Re-create textctrl with no border
405 if ( borderType != wxNO_BORDER )
406 {
407 m_widthCustomBorder = 1;
408 tcCreateStyle = wxNO_BORDER;
409 }
410 }
411 else if ( width == 0 )
412 {
413 // Re-create textctrl with normal border
414 if ( borderType == wxNO_BORDER )
415 {
416 m_widthCustomBorder = 0;
417 tcCreateStyle = 0;
418 }
419 }
420
421 // Common textctrl re-creation code
422 if ( tcCreateStyle != -1 )
423 {
424 tc->RemoveEventHandler(m_textEvtHandler);
425 delete m_textEvtHandler;
426
427 CreateTextCtrl( tcCreateStyle );
428
429 InstallInputHandlers();
430 }
431 }
432 #endif // UNRELIABLE_TEXTCTRL_BORDER
433
434 wxComboCtrlBase::SetCustomPaintWidth( width );
435 }
436
437 bool wxGenericComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
438 {
439 int keycode = event.GetKeyCode();
440 bool isPopupShown = IsPopupShown();
441
442 // This code is AFAIK appropriate for wxGTK.
443
444 if ( isPopupShown )
445 {
446 if ( keycode == WXK_ESCAPE ||
447 ( keycode == WXK_UP && event.AltDown() ) )
448 return true;
449 }
450 else
451 {
452 if ( (keycode == WXK_DOWN && event.AltDown()) ||
453 (keycode == WXK_F4) )
454 return true;
455 }
456
457 return false;
458 }
459
460 #ifdef __WXUNIVERSAL__
461
462 bool wxGenericComboCtrl::PerformAction(const wxControlAction& action,
463 long numArg,
464 const wxString& strArg)
465 {
466 bool processed = false;
467 if ( action == wxACTION_COMBOBOX_POPUP )
468 {
469 if ( !IsPopupShown() )
470 {
471 ShowPopup();
472
473 processed = true;
474 }
475 }
476 else if ( action == wxACTION_COMBOBOX_DISMISS )
477 {
478 if ( IsPopupShown() )
479 {
480 HidePopup();
481
482 processed = true;
483 }
484 }
485
486 if ( !processed )
487 {
488 // pass along
489 return wxControl::PerformAction(action, numArg, strArg);
490 }
491
492 return true;
493 }
494
495 #endif // __WXUNIVERSAL__
496
497 // If native wxComboCtrl was not defined, then prepare a simple
498 // front-end so that wxRTTI works as expected.
499 #ifndef _WX_COMBOCONTROL_H_
500 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxGenericComboCtrl)
501 #endif
502
503 #endif // !wxCOMBOCONTROL_FULLY_FEATURED
504
505 #endif // wxUSE_COMBOCTRL