wxComboControl and wxOwnerDrawnComboBox (patch 1479938)
[wxWidgets.git] / src / generic / combog.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: combog.cpp
3 // Purpose: Generic wxComboControl
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_COMBOCONTROL
27
28 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/combobox.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #endif
34
35 #include "wx/dcbuffer.h"
36
37 #include "wx/combo.h"
38
39
40 // ----------------------------------------------------------------------------
41 // Some constant adjustments to make the generic more bearable
42
43 #if defined(__WXUNIVERSAL__)
44
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
49
50 #elif defined(__WXMSW__)
51
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
56
57 #elif defined(__WXGTK__)
58
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
63
64 #elif defined(__WXMAC__)
65
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 19
70
71 #else
72
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
77
78 #endif
79
80
81 // ============================================================================
82 // implementation
83 // ============================================================================
84
85 // Only implement if no native or it wasn't fully featured
86 #ifndef wxCOMBOCONTROL_FULLY_FEATURED
87
88
89 // ----------------------------------------------------------------------------
90 // wxGenericComboControl
91 // ----------------------------------------------------------------------------
92
93 BEGIN_EVENT_TABLE(wxGenericComboControl, wxComboControlBase)
94 EVT_PAINT(wxGenericComboControl::OnPaintEvent)
95 EVT_MOUSE_EVENTS(wxGenericComboControl::OnMouseEvent)
96 END_EVENT_TABLE()
97
98
99 IMPLEMENT_DYNAMIC_CLASS(wxGenericComboControl, wxComboControlBase)
100
101 void wxGenericComboControl::Init()
102 {
103 }
104
105 bool wxGenericComboControl::Create(wxWindow *parent,
106 wxWindowID id,
107 const wxString& value,
108 const wxPoint& pos,
109 const wxSize& size,
110 long style,
111 const wxValidator& validator,
112 const wxString& name)
113 {
114
115 // Set border
116 long border = style & wxBORDER_MASK;
117
118 if ( !border )
119 {
120 #if defined(__WXUNIVERSAL__)
121 border = wxBORDER_SIMPLE;
122 #elif defined(__WXMSW__)
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 #elif defined(__WXGTK__)
132 border = wxBORDER_NONE;
133 //m_widthCustomBorder = 2;
134 m_widthCustomBorder = 1;
135 #else
136 border = wxBORDER_SIMPLE;
137 #endif
138
139 style = (style & ~(wxBORDER_MASK)) | border;
140 }
141
142 #if defined(__WXGTK__)
143 Customize( wxCC_BUTTON_OUTSIDE_BORDER |
144 wxCC_NO_TEXT_AUTO_SELECT );
145 #endif
146
147 if ( style & wxCC_STD_BUTTON )
148 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
149
150 // create main window
151 if ( !wxComboControlBase::Create(parent,
152 id,
153 value,
154 wxDefaultPosition,
155 wxDefaultSize,
156 style | wxFULL_REPAINT_ON_RESIZE,
157 wxDefaultValidator,
158 name) )
159 return false;
160
161 // Create textctrl, if necessary
162 CreateTextCtrl( wxNO_BORDER, validator );
163
164 // Add keyboard input handlers for main control and textctrl
165 InstallInputHandlers( true );
166
167 // Set background
168 SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // for double-buffering
169
170 // SetSize should be called last
171 SetSize(pos.x,pos.y,size.x,size.y);
172
173 return true;
174 }
175
176 wxGenericComboControl::~wxGenericComboControl()
177 {
178 }
179
180 void wxGenericComboControl::OnResize()
181 {
182
183 // Recalculates button and textctrl areas
184 CalculateAreas(DEFAULT_DROPBUTTON_WIDTH);
185
186 #if 0
187 // Move separate button control, if any, to correct position
188 if ( m_btn )
189 {
190 wxSize sz = GetClientSize();
191 m_btn->SetSize( m_btnArea.x + m_btnSpacingX,
192 (sz.y-m_btnSize.y)/2,
193 m_btnSize.x,
194 m_btnSize.y );
195 }
196 #endif
197
198 // Move textctrl, if any, accordingly
199 PositionTextCtrl( TEXTCTRLXADJUST, TEXTCTRLYADJUST );
200 }
201
202 void wxGenericComboControl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
203 {
204 wxSize sz = GetClientSize();
205 wxBufferedPaintDC dc(this,GetBufferBitmap(sz));
206
207 const wxRect& rectb = m_btnArea;
208 wxRect rect = m_tcArea;
209
210 // artificial simple border
211 if ( m_widthCustomBorder )
212 {
213 int customBorder = m_widthCustomBorder;
214
215 // Set border colour
216 wxPen pen1( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT),
217 customBorder,
218 wxSOLID );
219 dc.SetPen( pen1 );
220
221 // area around both controls
222 wxRect rect2(0,0,sz.x,sz.y);
223 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
224 {
225 rect2 = m_tcArea;
226 if ( customBorder == 1 )
227 {
228 rect2.Inflate(1);
229 }
230 else
231 {
232 #ifdef __WXGTK__
233 rect2.x -= 1;
234 rect2.y -= 1;
235 #else
236 rect2.x -= customBorder;
237 rect2.y -= customBorder;
238 #endif
239 rect2.width += 1 + customBorder;
240 rect2.height += 1 + customBorder;
241 }
242 }
243
244 dc.SetBrush( *wxTRANSPARENT_BRUSH );
245 dc.DrawRectangle(rect2);
246 }
247
248 wxColour winCol = GetBackgroundColour();
249 dc.SetBrush(winCol);
250 dc.SetPen(winCol);
251
252 //wxLogDebug(wxT("hei: %i tcy: %i tchei: %i"),GetClientSize().y,m_tcArea.y,m_tcArea.height);
253 //wxLogDebug(wxT("btnx: %i tcx: %i tcwid: %i"),m_btnArea.x,m_tcArea.x,m_tcArea.width);
254
255 // clear main background
256 dc.DrawRectangle(rect);
257
258 if ( !m_btn )
259 // Standard button rendering
260 DrawButton(dc,rectb,true);
261
262 // paint required portion on the control
263 if ( !m_text || m_widthCustomPaint )
264 {
265 wxASSERT( m_widthCustomPaint >= 0 );
266
267 // this is intentionally here to allow drawed rectangle's
268 // right edge to be hidden
269 if ( m_text )
270 rect.width = m_widthCustomPaint;
271
272 dc.SetFont( GetFont() );
273
274 dc.SetClippingRegion(rect);
275 m_popupInterface->PaintComboControl(dc,rect);
276 }
277 }
278
279 void wxGenericComboControl::OnMouseEvent( wxMouseEvent& event )
280 {
281 bool isOnButtonArea = m_btnArea.Inside(event.m_x,event.m_y);
282 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
283
284 // Preprocessing fabricates double-clicks and prevents
285 // (it may also do other common things in future)
286 if ( PreprocessMouseEvent(event,handlerFlags) )
287 return;
288
289 if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
290 {
291 // if no textctrl and no special double-click, then the entire control acts
292 // as a button
293 handlerFlags |= wxCC_MF_ON_BUTTON;
294 if ( HandleButtonMouseEvent(event,handlerFlags) )
295 return;
296 }
297 else
298 {
299 if ( isOnButtonArea || HasCapture() )
300 {
301 if ( HandleButtonMouseEvent(event,handlerFlags) )
302 return;
303 }
304 else if ( m_btnState )
305 {
306 // otherwise need to clear the hover status
307 m_btnState = 0;
308 RefreshRect(m_btnArea);
309 }
310 }
311
312 //
313 // This will handle left_down and left_dclick events outside button in a Windows/GTK-like manner.
314 // See header file for further information on this method.
315 HandleNormalMouseEvent(event);
316
317 }
318
319 #ifdef __WXUNIVERSAL__
320
321 bool wxGenericComboControl::PerformAction(const wxControlAction& action,
322 long numArg,
323 const wxString& strArg)
324 {
325 bool processed = false;
326 if ( action == wxACTION_COMBOBOX_POPUP )
327 {
328 if ( !m_isPopupShown )
329 {
330 ShowPopup();
331
332 processed = true;
333 }
334 }
335 else if ( action == wxACTION_COMBOBOX_DISMISS )
336 {
337 if ( m_isPopupShown )
338 {
339 HidePopup();
340
341 processed = true;
342 }
343 }
344
345 if ( !processed )
346 {
347 // pass along
348 return wxControl::PerformAction(action, numArg, strArg);
349 }
350
351 return true;
352 }
353
354 #endif // __WXUNIVERSAL__
355
356 // If native wxComboControl was not defined, then prepare a simple
357 // front-end so that wxRTTI works as expected.
358 #ifndef _WX_COMBOCONTROL_H_
359 IMPLEMENT_DYNAMIC_CLASS(wxComboControl, wxGenericComboControl)
360 #endif
361
362 #endif // !wxCOMBOCONTROL_FULLY_FEATURED
363
364 #endif // wxUSE_COMBOCONTROL