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