]> git.saurik.com Git - wxWidgets.git/blob - src/msw/combo.cpp
reset the dirty flag before generating the event from SetValue() in case the text...
[wxWidgets.git] / src / msw / combo.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: combo.cpp
3 // Purpose: wxMSW 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 #ifndef WX_PRECOMP
29 #include "wx/log.h"
30 #include "wx/combobox.h"
31 #include "wx/dcclient.h"
32 #include "wx/settings.h"
33 #include "wx/dialog.h"
34 #endif
35
36 #include "wx/dcbuffer.h"
37
38 #include "wx/combo.h"
39
40
41 #include "wx/msw/uxtheme.h"
42
43 // Change to #if 1 to include tmschema.h for easier testing of theme
44 // parameters.
45 #if 0
46 #include <tmschema.h>
47 #else
48 //----------------------------------
49 #define EP_EDITTEXT 1
50 #define ETS_NORMAL 1
51 #define ETS_HOT 2
52 #define ETS_SELECTED 3
53 #define ETS_DISABLED 4
54 #define ETS_FOCUSED 5
55 #define ETS_READONLY 6
56 #define ETS_ASSIST 7
57 #define TMT_FILLCOLOR 3802
58 #define TMT_TEXTCOLOR 3803
59 #define TMT_BORDERCOLOR 3801
60 #define TMT_EDGEFILLCOLOR 3808
61 //----------------------------------
62 #endif
63
64
65 #define NATIVE_TEXT_INDENT_XP 4
66 #define NATIVE_TEXT_INDENT_CLASSIC 2
67
68 #define TEXTCTRLXADJUST_XP 1
69 #define TEXTCTRLYADJUST_XP 3
70 #define TEXTCTRLXADJUST_CLASSIC 1
71 #define TEXTCTRLYADJUST_CLASSIC 2
72
73
74 // ============================================================================
75 // implementation
76 // ============================================================================
77
78
79 BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
80 EVT_PAINT(wxComboCtrl::OnPaintEvent)
81 EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
82 END_EVENT_TABLE()
83
84
85 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase)
86
87 void wxComboCtrl::Init()
88 {
89 }
90
91 bool wxComboCtrl::Create(wxWindow *parent,
92 wxWindowID id,
93 const wxString& value,
94 const wxPoint& pos,
95 const wxSize& size,
96 long style,
97 const wxValidator& validator,
98 const wxString& name)
99 {
100
101 // Set border
102 long border = style & wxBORDER_MASK;
103
104 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
105
106 if ( !border )
107 {
108 // For XP, have 1-width custom border, for older version use sunken
109 if ( theme )
110 {
111 border = wxBORDER_NONE;
112 m_widthCustomBorder = 1;
113 }
114 else
115 border = wxBORDER_SUNKEN;
116
117 style = (style & ~(wxBORDER_MASK)) | border;
118 }
119
120 // create main window
121 if ( !wxComboCtrlBase::Create(parent,
122 id,
123 value,
124 wxDefaultPosition,
125 wxDefaultSize,
126 style | wxFULL_REPAINT_ON_RESIZE,
127 wxDefaultValidator,
128 name) )
129 return false;
130
131 if ( style & wxCC_STD_BUTTON )
132 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
133
134 // Create textctrl, if necessary
135 CreateTextCtrl( wxNO_BORDER, validator );
136
137 // Add keyboard input handlers for main control and textctrl
138 InstallInputHandlers( true );
139
140 // Prepare background for double-buffering
141 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
142
143 // SetSize should be called last
144 SetSize(pos.x,pos.y,size.x,size.y);
145
146 return true;
147 }
148
149 wxComboCtrl::~wxComboCtrl()
150 {
151 }
152
153 void wxComboCtrl::OnThemeChange()
154 {
155 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
156 if ( theme )
157 {
158 wxUxThemeHandle hTheme(this, L"COMBOBOX");
159
160 COLORREF col;
161 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
162 SetBackgroundColour(wxRGBToColour(col));
163 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
164 SetForegroundColour(wxRGBToColour(col));
165 }
166 else
167 {
168 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
169 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
170 }
171 }
172
173 void wxComboCtrl::OnResize()
174 {
175 //
176 // Recalculates button and textctrl areas
177
178 int textCtrlXAdjust;
179 int textCtrlYAdjust;
180
181 if ( wxUxThemeEngine::GetIfActive() )
182 {
183 textCtrlXAdjust = TEXTCTRLXADJUST_XP;
184 textCtrlYAdjust = TEXTCTRLYADJUST_XP;
185 }
186 else
187 {
188 textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
189 textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
190 }
191
192 // Technically Classic Windows style combo has more narrow button,
193 // but the native renderer doesn't paint it well like that.
194 int btnWidth = 17;
195 CalculateAreas(btnWidth);
196
197 // Position textctrl using standard routine
198 PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
199 }
200
201 // Draws non-XP GUI dotted line around the focus area
202 static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
203 {
204 #if !defined(__WXWINCE__)
205 /*
206 RECT mswRect;
207 mswRect.left = rect.x;
208 mswRect.top = rect.y;
209 mswRect.right = rect.x + rect.width;
210 mswRect.bottom = rect.y + rect.height;
211 HDC hdc = (HDC) dc.GetHDC();
212 SetMapMode(hdc,MM_TEXT); // Just in case...
213 DrawFocusRect(hdc,&mswRect);
214 */
215 // FIXME: Use DrawFocusRect code above (currently it draws solid line
216 // for caption focus but works ok for other stuff).
217 // Also, this code below may not work in future wx versions, since
218 // it employs wxCAP_BUTT hack to have line of width 1.
219 dc.SetLogicalFunction(wxINVERT);
220
221 wxPen pen(*wxBLACK,1,wxDOT);
222 pen.SetCap(wxCAP_BUTT);
223 dc.SetPen(pen);
224 dc.SetBrush(*wxTRANSPARENT_BRUSH);
225
226 dc.DrawRectangle(rect);
227
228 dc.SetLogicalFunction(wxCOPY);
229 #else
230 dc.SetLogicalFunction(wxINVERT);
231
232 dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
233 dc.SetBrush(*wxTRANSPARENT_BRUSH);
234
235 dc.DrawRectangle(rect);
236
237 dc.SetLogicalFunction(wxCOPY);
238 #endif
239 }
240
241 // draw focus background on area in a way typical on platform
242 void wxComboCtrl::DrawFocusBackground( wxDC& dc, const wxRect& rect, int flags )
243 {
244 wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
245 wxUxThemeHandle hTheme(this, L"COMBOBOX");
246 //COLORREF cref;
247
248 wxSize sz = GetClientSize();
249 bool isEnabled;
250 bool isFocused; // also selected
251
252 // For smaller size control (and for disabled background) use less spacing
253 int focusSpacingX;
254 int focusSpacingY;
255
256 if ( !(flags & wxCONTROL_ISSUBMENU) )
257 {
258 // Drawing control
259 isEnabled = IsEnabled();
260 isFocused = ShouldDrawFocus();
261
262 // Windows-style: for smaller size control (and for disabled background) use less spacing
263 if ( hTheme )
264 {
265 // WinXP Theme
266 focusSpacingX = isEnabled ? 2 : 1;
267 focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
268 }
269 else
270 {
271 // Classic Theme
272 if ( isEnabled )
273 {
274 focusSpacingX = 1;
275 focusSpacingY = 1;
276 }
277 else
278 {
279 focusSpacingX = 0;
280 focusSpacingY = 0;
281 }
282 }
283 }
284 else
285 {
286 // Drawing a list item
287 isEnabled = true; // they are never disabled
288 isFocused = flags & wxCONTROL_SELECTED ? true : false;
289
290 focusSpacingX = 0;
291 focusSpacingY = 0;
292 }
293
294 // Set the background sub-rectangle for selection, disabled etc
295 wxRect selRect(rect);
296 selRect.y += focusSpacingY;
297 selRect.height -= (focusSpacingY*2);
298 selRect.x += m_widthCustomPaint + focusSpacingX;
299 selRect.width -= m_widthCustomPaint + (focusSpacingX*2);
300
301 if ( hTheme )
302 theme = wxUxThemeEngine::GetIfActive();
303
304 wxColour bgCol;
305 bool drawDottedEdge = false;
306
307 if ( isEnabled )
308 {
309 // If popup is hidden and this control is focused,
310 // then draw the focus-indicator (selbgcolor background etc.).
311 if ( isFocused )
312 {
313 #if 0
314 // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
315 // those below don't work)
316 if ( hTheme )
317 {
318 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
319 dc.SetTextForeground( wxRGBToColour(cref) );
320 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
321 bgCol = wxRGBToColour(cref);
322 }
323 else
324 #endif
325 {
326 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
327 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
328 if ( m_windowStyle & wxCB_READONLY )
329 drawDottedEdge = true;
330 }
331 }
332 else
333 {
334 /*if ( hTheme )
335 {
336 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
337 dc.SetTextForeground( wxRGBToColour(cref) );
338 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref);
339 bgCol = wxRGBToColour(cref);
340 }
341 else
342 {*/
343 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
344 bgCol = GetBackgroundColour();
345 //}
346 }
347 }
348 else
349 {
350 /*if ( hTheme )
351 {
352 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref);
353 dc.SetTextForeground( wxRGBToColour(cref) );
354 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref);
355 bgCol = wxRGBToColour(cref);
356 }
357 else
358 {*/
359 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
360 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
361 //}
362 }
363
364 dc.SetBrush(bgCol);
365 dc.SetPen(bgCol);
366 dc.DrawRectangle(selRect);
367 if ( drawDottedEdge )
368 wxMSWDrawFocusRect(dc,selRect);
369
370 }
371
372 void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
373 {
374 // TODO: Convert drawing in this function to Windows API Code
375
376 wxSize sz = GetClientSize();
377 wxBufferedPaintDC dc(this,GetBufferBitmap(sz));
378
379 const wxRect& rectb = m_btnArea;
380 wxRect rect = m_tcArea;
381 bool isEnabled = IsEnabled();
382 wxColour bgCol = GetBackgroundColour();
383 wxColour fgCol;
384
385 wxUxThemeEngine* theme = NULL;
386 wxUxThemeHandle hTheme(this, L"COMBOBOX");
387 int etsState;
388
389 // area around both controls
390 wxRect rect2(0,0,sz.x,sz.y);
391 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
392 {
393 rect2 = m_tcArea;
394 rect2.Inflate(1);
395 }
396
397 // Use theme to draw border on XP
398 if ( hTheme )
399 {
400 theme = wxUxThemeEngine::GetIfActive();
401 COLORREF cref;
402
403 // Select correct border colour
404 if ( !isEnabled )
405 etsState = ETS_DISABLED;
406 else
407 etsState = ETS_NORMAL;
408
409 if ( m_widthCustomBorder )
410 {
411 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref);
412
413 // Set border colour
414 dc.SetPen( wxRGBToColour(cref) );
415
416 dc.SetBrush( *wxTRANSPARENT_BRUSH );
417 dc.DrawRectangle(rect2);
418 }
419
420 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref);
421 fgCol = wxRGBToColour(cref);
422 }
423 else
424 {
425 // draw regular background
426 fgCol = GetForegroundColour();
427 }
428
429 rect2.Deflate(m_widthCustomBorder);
430
431 dc.SetBrush(bgCol);
432 dc.SetPen(bgCol);
433
434 // clear main background
435 dc.DrawRectangle(rect);
436
437 // Button background with theme?
438 bool drawButBg = true;
439 if ( hTheme && m_blankButtonBg )
440 {
441 RECT r;
442 wxCopyRectToRECT(rectb, r);
443
444 // Draw parent background if needed (since button looks like its out of
445 // the combo, this is preferred).
446 theme->DrawThemeParentBackground(GetHwndOf(this),
447 GetHdcOf(dc),
448 &r);
449
450 drawButBg = false;
451 }
452
453 // Standard button rendering
454 DrawButton(dc,rectb,drawButBg);
455
456 // paint required portion on the control
457 if ( (!m_text || m_widthCustomPaint) )
458 {
459 wxASSERT( m_widthCustomPaint >= 0 );
460
461 // this is intentionally here to allow drawed rectangle's
462 // right edge to be hidden
463 if ( m_text )
464 rect.width = m_widthCustomPaint;
465
466 dc.SetFont( GetFont() );
467
468 dc.SetClippingRegion(rect);
469 if ( m_popupInterface )
470 m_popupInterface->PaintComboControl(dc,rect);
471 else
472 wxComboPopup::DefaultPaintComboControl(this,dc,rect);
473 }
474 }
475
476 void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
477 {
478 bool isOnButtonArea = m_btnArea.Inside(event.m_x,event.m_y);
479 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
480
481 // Preprocessing fabricates double-clicks and prevents
482 // (it may also do other common things in future)
483 if ( PreprocessMouseEvent(event,isOnButtonArea) )
484 return;
485
486 if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
487 {
488 // if no textctrl and no special double-click, then the entire control acts
489 // as a button
490 handlerFlags |= wxCC_MF_ON_BUTTON;
491 if ( HandleButtonMouseEvent(event,handlerFlags) )
492 return;
493 }
494 else
495 {
496 if ( isOnButtonArea || HasCapture() )
497 {
498 if ( HandleButtonMouseEvent(event,handlerFlags) )
499 return;
500 }
501 else if ( m_btnState )
502 {
503 // otherwise need to clear the hover status
504 m_btnState = 0;
505 RefreshRect(m_btnArea);
506 }
507 }
508
509 //
510 // This will handle left_down and left_dclick events outside button in a Windows-like manner.
511 // See header file for further information on this method.
512 HandleNormalMouseEvent(event);
513
514 }
515
516 wxCoord wxComboCtrl::GetNativeTextIndent() const
517 {
518 if ( wxUxThemeEngine::GetIfActive() )
519 return NATIVE_TEXT_INDENT_XP;
520 return NATIVE_TEXT_INDENT_CLASSIC;
521 }
522
523
524 #endif // wxUSE_COMBOCTRL