1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/combog.cpp
3 // Purpose: Generic wxComboCtrl
4 // Author: Jaakko Salli
6 // Created: Apr-30-2006
8 // Copyright: (c) 2005 Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
32 #include "wx/combobox.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/textctrl.h"
38 #include "wx/dcbuffer.h"
40 // ----------------------------------------------------------------------------
41 // Some constant adjustments to make the generic more bearable
43 #if defined(__WXUNIVERSAL__)
45 #define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
46 #define TEXTCTRLYADJUST 0
47 #define TEXTXADJUST 0 // how much is read-only text's x adjusted
48 #define DEFAULT_DROPBUTTON_WIDTH 19
50 #elif defined(__WXMSW__)
52 #define TEXTCTRLXADJUST 2 // position adjustment for wxTextCtrl, with zero indent
53 #define TEXTCTRLYADJUST 3
54 #define TEXTXADJUST 0 // how much is read-only text's x adjusted
55 #define DEFAULT_DROPBUTTON_WIDTH 17
57 #elif defined(__WXGTK__)
59 #define TEXTCTRLXADJUST -1 // position adjustment for wxTextCtrl, with zero indent
60 #define TEXTCTRLYADJUST 0
61 #define TEXTXADJUST 1 // how much is read-only text's x adjusted
62 #define DEFAULT_DROPBUTTON_WIDTH 23
64 #elif defined(__WXMAC__)
66 #define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
67 #define TEXTCTRLYADJUST 0
68 #define TEXTXADJUST 0 // how much is read-only text's x adjusted
69 #define DEFAULT_DROPBUTTON_WIDTH 22
73 #define TEXTCTRLXADJUST 0 // position adjustment for wxTextCtrl, with zero indent
74 #define TEXTCTRLYADJUST 0
75 #define TEXTXADJUST 0 // how much is read-only text's x adjusted
76 #define DEFAULT_DROPBUTTON_WIDTH 19
81 // ============================================================================
83 // ============================================================================
85 // Only implement if no native or it wasn't fully featured
86 #ifndef wxCOMBOCONTROL_FULLY_FEATURED
89 // ----------------------------------------------------------------------------
91 // ----------------------------------------------------------------------------
93 BEGIN_EVENT_TABLE(wxGenericComboCtrl, wxComboCtrlBase)
94 EVT_PAINT(wxGenericComboCtrl::OnPaintEvent)
95 EVT_MOUSE_EVENTS(wxGenericComboCtrl::OnMouseEvent)
99 IMPLEMENT_DYNAMIC_CLASS(wxGenericComboCtrl, wxComboCtrlBase)
101 void wxGenericComboCtrl::Init()
105 bool wxGenericComboCtrl::Create(wxWindow *parent,
107 const wxString& value,
111 const wxValidator& validator,
112 const wxString& name)
115 // Note that technically we only support 'default' border and wxNO_BORDER.
116 long border = style & wxBORDER_MASK;
117 int tcBorder = wxNO_BORDER;
119 #if defined(__WXUNIVERSAL__)
121 border = wxBORDER_SIMPLE;
122 #elif defined(__WXMSW__)
124 // For XP, have 1-width custom border, for older version use sunken
125 /*if ( wxUxThemeEngine::GetIfActive() )
127 border = wxBORDER_NONE;
128 m_widthCustomBorder = 1;
131 border = wxBORDER_SUNKEN;
135 // Generic version is optimized for wxGTK
138 #define UNRELIABLE_TEXTCTRL_BORDER
142 if ( style & wxCB_READONLY )
144 m_widthCustomBorder = 1;
148 m_widthCustomBorder = 0;
154 // Have textctrl instead use the border given.
158 // Because we are going to have button outside the border,
159 // let's use wxBORDER_NONE for the whole control.
160 border = wxBORDER_NONE;
162 Customize( wxCC_BUTTON_OUTSIDE_BORDER |
163 wxCC_NO_TEXT_AUTO_SELECT |
164 wxCC_BUTTON_STAYS_DOWN );
168 style = (style & ~(wxBORDER_MASK)) | border;
169 if ( style & wxCC_STD_BUTTON )
170 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
172 // create main window
173 if ( !wxComboCtrlBase::Create(parent,
178 style | wxFULL_REPAINT_ON_RESIZE,
183 // Create textctrl, if necessary
184 CreateTextCtrl( tcBorder, validator );
186 // Add keyboard input handlers for main control and textctrl
187 InstallInputHandlers();
190 SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
192 // SetInitialSize should be called last
193 SetInitialSize(size);
198 wxGenericComboCtrl::~wxGenericComboCtrl()
202 void wxGenericComboCtrl::OnResize()
205 // Recalculates button and textctrl areas
206 CalculateAreas(DEFAULT_DROPBUTTON_WIDTH);
209 // Move separate button control, if any, to correct position
212 wxSize sz = GetClientSize();
213 m_btn->SetSize( m_btnArea.x + m_btnSpacingX,
214 (sz.y-m_btnSize.y)/2,
220 // Move textctrl, if any, accordingly
221 PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
224 void wxGenericComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
226 wxSize sz = GetClientSize();
227 wxAutoBufferedPaintDC dc(this);
229 const wxRect& rectb = m_btnArea;
230 wxRect rect = m_tcArea;
232 // artificial simple border
233 if ( m_widthCustomBorder )
235 int customBorder = m_widthCustomBorder;
238 wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
243 // area around both controls
244 wxRect rect2(0,0,sz.x,sz.y);
245 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
248 if ( customBorder == 1 )
258 rect2.x -= customBorder;
259 rect2.y -= customBorder;
261 rect2.width += 1 + customBorder;
262 rect2.height += 1 + customBorder;
266 dc.SetBrush( *wxTRANSPARENT_BRUSH );
267 dc.DrawRectangle(rect2);
270 #ifndef __WXMAC__ // see note in OnThemeChange
271 wxColour winCol = GetBackgroundColour();
273 wxColour winCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
278 //wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
279 //wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
281 // clear main background
282 dc.DrawRectangle(rect);
286 // Standard button rendering
287 DrawButton(dc,rectb);
290 // paint required portion on the control
291 if ( (!m_text || m_widthCustomPaint) )
293 wxASSERT( m_widthCustomPaint >= 0 );
295 // this is intentionally here to allow drawed rectangle's
296 // right edge to be hidden
298 rect.width = m_widthCustomPaint;
300 dc.SetFont( GetFont() );
302 dc.SetClippingRegion(rect);
303 if ( m_popupInterface )
304 m_popupInterface->PaintComboControl(dc,rect);
306 wxComboPopup::DefaultPaintComboControl(this,dc,rect);
310 void wxGenericComboCtrl::OnMouseEvent( wxMouseEvent& event )
313 bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
314 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
316 if ( PreprocessMouseEvent(event,handlerFlags) )
319 const bool ctrlIsButton = wxPlatformIs(wxOS_WINDOWS);
322 (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
324 // if no textctrl and no special double-click, then the entire control acts
326 handlerFlags |= wxCC_MF_ON_BUTTON;
327 if ( HandleButtonMouseEvent(event,handlerFlags) )
332 if ( isOnButtonArea || HasCapture() ||
333 (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
335 handlerFlags |= wxCC_MF_ON_CLICK_AREA;
337 if ( HandleButtonMouseEvent(event,handlerFlags) )
340 else if ( m_btnState )
342 // otherwise need to clear the hover status
344 RefreshRect(m_btnArea);
349 // This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
350 // See header file for further information on this method.
351 HandleNormalMouseEvent(event);
355 void wxGenericComboCtrl::SetCustomPaintWidth( int width )
357 #ifdef UNRELIABLE_TEXTCTRL_BORDER
359 // If starting/stopping to show an image in front
360 // of a writable text-field, then re-create textctrl
361 // with different kind of border (because we can't
362 // assume that textctrl fully supports wxNO_BORDER).
364 wxTextCtrl* tc = GetTextCtrl();
366 if ( tc && (m_iFlags & wxCC_BUTTON_OUTSIDE_BORDER) )
368 int borderType = tc->GetWindowStyle() & wxBORDER_MASK;
369 int tcCreateStyle = -1;
373 // Re-create textctrl with no border
374 if ( borderType != wxNO_BORDER )
376 m_widthCustomBorder = 1;
377 tcCreateStyle = wxNO_BORDER;
380 else if ( width == 0 )
382 // Re-create textctrl with normal border
383 if ( borderType == wxNO_BORDER )
385 m_widthCustomBorder = 0;
390 // Common textctrl re-creation code
391 if ( tcCreateStyle != -1 )
393 tc->RemoveEventHandler(m_textEvtHandler);
394 delete m_textEvtHandler;
397 wxValidator* pValidator = tc->GetValidator();
400 pValidator = (wxValidator*) pValidator->Clone();
401 CreateTextCtrl( tcCreateStyle, *pValidator );
407 CreateTextCtrl( tcCreateStyle, wxDefaultValidator );
410 InstallInputHandlers();
413 #endif // UNRELIABLE_TEXTCTRL_BORDER
415 wxComboCtrlBase::SetCustomPaintWidth( width );
418 bool wxGenericComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
420 int keycode = event.GetKeyCode();
421 bool isPopupShown = IsPopupShown();
423 // This code is AFAIK appropriate for wxGTK.
427 if ( keycode == WXK_ESCAPE ||
428 ( keycode == WXK_UP && event.AltDown() ) )
433 if ( keycode == WXK_DOWN && event.AltDown() )
440 #ifdef __WXUNIVERSAL__
442 bool wxGenericComboCtrl::PerformAction(const wxControlAction& action,
444 const wxString& strArg)
446 bool processed = false;
447 if ( action == wxACTION_COMBOBOX_POPUP )
449 if ( !IsPopupShown() )
456 else if ( action == wxACTION_COMBOBOX_DISMISS )
458 if ( IsPopupShown() )
469 return wxControl::PerformAction(action, numArg, strArg);
475 #endif // __WXUNIVERSAL__
477 // If native wxComboCtrl was not defined, then prepare a simple
478 // front-end so that wxRTTI works as expected.
479 #ifndef _WX_COMBOCONTROL_H_
480 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxGenericComboCtrl)
483 #endif // !wxCOMBOCONTROL_FULLY_FEATURED
485 #endif // wxUSE_COMBOCTRL