]> git.saurik.com Git - wxWidgets.git/blob - src/msw/combo.cpp
Added missing include for MinGW non-PCH compile
[wxWidgets.git] / src / msw / combo.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/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 #include "wx/combo.h"
38 #include "wx/stopwatch.h"
39
40 #include "wx/msw/registry.h"
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 #define COMBOBOX_ANIMATION_DURATION 200 // In milliseconds
74
75 // ============================================================================
76 // implementation
77 // ============================================================================
78
79
80 BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
81 EVT_PAINT(wxComboCtrl::OnPaintEvent)
82 EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
83 END_EVENT_TABLE()
84
85
86 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase)
87
88 void wxComboCtrl::Init()
89 {
90 }
91
92 bool wxComboCtrl::Create(wxWindow *parent,
93 wxWindowID id,
94 const wxString& value,
95 const wxPoint& pos,
96 const wxSize& size,
97 long style,
98 const wxValidator& validator,
99 const wxString& name)
100 {
101
102 // Set border
103 long border = style & wxBORDER_MASK;
104
105 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
106
107 if ( !border )
108 {
109 // For XP, have 1-width custom border, for older version use sunken
110 if ( theme )
111 {
112 border = wxBORDER_NONE;
113 m_widthCustomBorder = 1;
114 }
115 else
116 border = wxBORDER_SUNKEN;
117
118 style = (style & ~(wxBORDER_MASK)) | border;
119 }
120
121 // create main window
122 if ( !wxComboCtrlBase::Create(parent,
123 id,
124 value,
125 pos,
126 size,
127 style | wxFULL_REPAINT_ON_RESIZE,
128 wxDefaultValidator,
129 name) )
130 return false;
131
132 if ( style & wxCC_STD_BUTTON )
133 m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
134
135 // Create textctrl, if necessary
136 CreateTextCtrl( wxNO_BORDER, validator );
137
138 // Add keyboard input handlers for main control and textctrl
139 InstallInputHandlers();
140
141 // Prepare background for double-buffering
142 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
143
144 // SetBestSize should be called last
145 SetBestSize(size);
146
147 return true;
148 }
149
150 wxComboCtrl::~wxComboCtrl()
151 {
152 }
153
154 void wxComboCtrl::OnThemeChange()
155 {
156 wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
157 if ( theme )
158 {
159 wxUxThemeHandle hTheme(this, L"COMBOBOX");
160
161 COLORREF col;
162 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
163 SetBackgroundColour(wxRGBToColour(col));
164 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
165 SetForegroundColour(wxRGBToColour(col));
166 }
167 else
168 {
169 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
170 SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
171 }
172 }
173
174 void wxComboCtrl::OnResize()
175 {
176 //
177 // Recalculates button and textctrl areas
178
179 int textCtrlXAdjust;
180 int textCtrlYAdjust;
181
182 if ( wxUxThemeEngine::GetIfActive() )
183 {
184 textCtrlXAdjust = TEXTCTRLXADJUST_XP;
185 textCtrlYAdjust = TEXTCTRLYADJUST_XP;
186 }
187 else
188 {
189 textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
190 textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
191 }
192
193 // Technically Classic Windows style combo has more narrow button,
194 // but the native renderer doesn't paint it well like that.
195 int btnWidth = 17;
196 CalculateAreas(btnWidth);
197
198 // Position textctrl using standard routine
199 PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
200 }
201
202 // Draws non-XP GUI dotted line around the focus area
203 static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
204 {
205 #if !defined(__WXWINCE__)
206 /*
207 RECT mswRect;
208 mswRect.left = rect.x;
209 mswRect.top = rect.y;
210 mswRect.right = rect.x + rect.width;
211 mswRect.bottom = rect.y + rect.height;
212 HDC hdc = (HDC) dc.GetHDC();
213 SetMapMode(hdc,MM_TEXT); // Just in case...
214 DrawFocusRect(hdc,&mswRect);
215 */
216 // FIXME: Use DrawFocusRect code above (currently it draws solid line
217 // for caption focus but works ok for other stuff).
218 // Also, this code below may not work in future wx versions, since
219 // it employs wxCAP_BUTT hack to have line of width 1.
220 dc.SetLogicalFunction(wxINVERT);
221
222 wxPen pen(*wxBLACK,1,wxDOT);
223 pen.SetCap(wxCAP_BUTT);
224 dc.SetPen(pen);
225 dc.SetBrush(*wxTRANSPARENT_BRUSH);
226
227 dc.DrawRectangle(rect);
228
229 dc.SetLogicalFunction(wxCOPY);
230 #else
231 dc.SetLogicalFunction(wxINVERT);
232
233 dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
234 dc.SetBrush(*wxTRANSPARENT_BRUSH);
235
236 dc.DrawRectangle(rect);
237
238 dc.SetLogicalFunction(wxCOPY);
239 #endif
240 }
241
242 // draw focus background on area in a way typical on platform
243 void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
244 {
245 wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
246
247 // Constructor only calls GetHWND() const, so it should be safe
248 // to cast "this" to const.
249 wxUxThemeHandle hTheme(this, L"COMBOBOX");
250 //COLORREF cref;
251
252 wxSize sz = GetClientSize();
253 bool isEnabled;
254 bool isFocused; // also selected
255
256 // For smaller size control (and for disabled background) use less spacing
257 int focusSpacingX;
258 int focusSpacingY;
259
260 if ( !(flags & wxCONTROL_ISSUBMENU) )
261 {
262 // Drawing control
263 isEnabled = IsEnabled();
264 isFocused = ShouldDrawFocus();
265
266 // Windows-style: for smaller size control (and for disabled background) use less spacing
267 if ( hTheme )
268 {
269 // WinXP Theme
270 focusSpacingX = isEnabled ? 2 : 1;
271 focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
272 }
273 else
274 {
275 // Classic Theme
276 if ( isEnabled )
277 {
278 focusSpacingX = 1;
279 focusSpacingY = 1;
280 }
281 else
282 {
283 focusSpacingX = 0;
284 focusSpacingY = 0;
285 }
286 }
287 }
288 else
289 {
290 // Drawing a list item
291 isEnabled = true; // they are never disabled
292 isFocused = flags & wxCONTROL_SELECTED ? true : false;
293
294 focusSpacingX = 0;
295 focusSpacingY = 0;
296 }
297
298 // Set the background sub-rectangle for selection, disabled etc
299 wxRect selRect(rect);
300 selRect.y += focusSpacingY;
301 selRect.height -= (focusSpacingY*2);
302
303 int wcp = 0;
304
305 if ( !(flags & wxCONTROL_ISSUBMENU) )
306 wcp += m_widthCustomPaint;
307
308 selRect.x += wcp + focusSpacingX;
309 selRect.width -= wcp + (focusSpacingX*2);
310
311 if ( hTheme )
312 theme = wxUxThemeEngine::GetIfActive();
313
314 wxColour bgCol;
315 bool drawDottedEdge = false;
316
317 if ( isEnabled )
318 {
319 // If popup is hidden and this control is focused,
320 // then draw the focus-indicator (selbgcolor background etc.).
321 if ( isFocused )
322 {
323 #if 0
324 // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
325 // those below don't work)
326 if ( hTheme )
327 {
328 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
329 dc.SetTextForeground( wxRGBToColour(cref) );
330 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
331 bgCol = wxRGBToColour(cref);
332 }
333 else
334 #endif
335 {
336 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
337 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
338 if ( m_windowStyle & wxCB_READONLY )
339 drawDottedEdge = true;
340 }
341 }
342 else
343 {
344 /*if ( hTheme )
345 {
346 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
347 dc.SetTextForeground( wxRGBToColour(cref) );
348 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref);
349 bgCol = wxRGBToColour(cref);
350 }
351 else
352 {*/
353 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
354 bgCol = GetBackgroundColour();
355 //}
356 }
357 }
358 else
359 {
360 /*if ( hTheme )
361 {
362 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref);
363 dc.SetTextForeground( wxRGBToColour(cref) );
364 theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref);
365 bgCol = wxRGBToColour(cref);
366 }
367 else
368 {*/
369 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
370 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
371 //}
372 }
373
374 dc.SetBrush(bgCol);
375 dc.SetPen(bgCol);
376 dc.DrawRectangle(selRect);
377 if ( drawDottedEdge )
378 wxMSWDrawFocusRect(dc,selRect);
379
380 // Don't clip exactly to the selection rectangle so we can draw
381 // to the non-selected area in front of it.
382 wxRect clipRect(rect.x,rect.y,
383 (selRect.x+selRect.width)-rect.x-1,rect.height);
384 dc.SetClippingRegion(clipRect);
385 }
386
387 void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
388 {
389 // TODO: Convert drawing in this function to Windows API Code
390
391 wxSize sz = GetClientSize();
392 wxAutoBufferedPaintDC dc(this);
393
394 const wxRect& rectb = m_btnArea;
395 wxRect rect = m_tcArea;
396 bool isEnabled = IsEnabled();
397 wxColour bgCol = GetBackgroundColour();
398 wxColour fgCol;
399
400 wxUxThemeEngine* theme = NULL;
401 wxUxThemeHandle hTheme(this, L"COMBOBOX");
402 int etsState;
403
404 // area around both controls
405 wxRect rect2(0,0,sz.x,sz.y);
406 if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
407 {
408 rect2 = m_tcArea;
409 rect2.Inflate(1);
410 }
411
412 // Use theme to draw border on XP
413 if ( hTheme )
414 {
415 theme = wxUxThemeEngine::GetIfActive();
416 COLORREF cref;
417
418 // Select correct border colour
419 if ( !isEnabled )
420 etsState = ETS_DISABLED;
421 else
422 etsState = ETS_NORMAL;
423
424 if ( m_widthCustomBorder )
425 {
426 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref);
427
428 // Set border colour
429 dc.SetPen( wxRGBToColour(cref) );
430
431 dc.SetBrush( *wxTRANSPARENT_BRUSH );
432 dc.DrawRectangle(rect2);
433 }
434
435 theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref);
436 fgCol = wxRGBToColour(cref);
437 }
438 else
439 {
440 // draw regular background
441 fgCol = GetForegroundColour();
442 }
443
444 rect2.Deflate(m_widthCustomBorder);
445
446 dc.SetBrush(bgCol);
447 dc.SetPen(bgCol);
448
449 // clear main background
450 dc.DrawRectangle(rect);
451
452 // Button background with theme?
453 bool drawButBg = true;
454 if ( hTheme && m_blankButtonBg )
455 {
456 RECT r;
457 wxCopyRectToRECT(rectb, r);
458
459 // Draw parent background if needed (since button looks like its out of
460 // the combo, this is preferred).
461 theme->DrawThemeParentBackground(GetHwndOf(this),
462 GetHdcOf(dc),
463 &r);
464
465 drawButBg = false;
466 }
467
468 // Standard button rendering
469 DrawButton(dc,rectb,drawButBg);
470
471 // paint required portion on the control
472 if ( (!m_text || m_widthCustomPaint) )
473 {
474 wxASSERT( m_widthCustomPaint >= 0 );
475
476 // this is intentionally here to allow drawed rectangle's
477 // right edge to be hidden
478 if ( m_text )
479 rect.width = m_widthCustomPaint;
480
481 dc.SetFont( GetFont() );
482
483 dc.SetClippingRegion(rect);
484 if ( m_popupInterface )
485 m_popupInterface->PaintComboControl(dc,rect);
486 else
487 wxComboPopup::DefaultPaintComboControl(this,dc,rect);
488 }
489 }
490
491 void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
492 {
493 int mx = event.m_x;
494 bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
495 int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
496
497 if ( PreprocessMouseEvent(event,isOnButtonArea) )
498 return;
499
500 if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
501 {
502 // if no textctrl and no special double-click, then the entire control acts
503 // as a button
504 handlerFlags |= wxCC_MF_ON_BUTTON;
505 if ( HandleButtonMouseEvent(event,handlerFlags) )
506 return;
507 }
508 else
509 {
510 if ( isOnButtonArea || HasCapture() ||
511 (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
512 {
513 handlerFlags |= wxCC_MF_ON_CLICK_AREA;
514
515 if ( HandleButtonMouseEvent(event,handlerFlags) )
516 return;
517 }
518 else if ( m_btnState )
519 {
520 // otherwise need to clear the hover status
521 m_btnState = 0;
522 RefreshRect(m_btnArea);
523 }
524 }
525
526 //
527 // This will handle left_down and left_dclick events outside button in a Windows-like manner.
528 // See header file for further information on this method.
529 HandleNormalMouseEvent(event);
530
531 }
532
533 #if !defined(__WXWINCE__)
534 static wxUint32 GetUserPreferencesMask()
535 {
536 static wxUint32 userPreferencesMask = 0;
537 static bool valueSet = false;
538
539 if ( valueSet )
540 return userPreferencesMask;
541
542 wxRegKey key(wxRegKey::HKCU, wxT("Control Panel\\Desktop"));
543 if( key.Open(wxRegKey::Read) )
544 {
545 wxMemoryBuffer buf;
546 if ( key.QueryValue(wxT("UserPreferencesMask"), buf) )
547 {
548 if ( buf.GetDataLen() >= 4 )
549 {
550 wxByte* p = (wxByte*) buf.GetData();
551 userPreferencesMask = p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24);
552 }
553 }
554 }
555
556 valueSet = true;
557
558 return userPreferencesMask;
559 }
560 #endif
561
562 bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
563 {
564 #if !defined(__WXWINCE__)
565 if ( GetUserPreferencesMask() & (1<<26) )
566 {
567 wxLongLong tStart = ::wxGetLocalTimeMillis();
568
569 int height = rect.height;
570
571 wxWindow* win = GetPopupWindow();
572 wxWindow* popup = GetPopupControl()->GetControl();
573
574 const int delay = COMBOBOX_ANIMATION_DURATION;
575 const int resolution = 10;
576 int h0 = popup->GetSize().y;
577
578 win->SetSize( rect.x, rect.y, rect.width, 0 );
579 win->Show();
580
581 for (;;)
582 {
583 wxLongLong t = ::wxGetLocalTimeMillis();
584 int pos = (int) (t-tStart).GetLo();
585 if ( pos > delay )
586 break;
587
588 int h = (((pos*256)/delay)*height)/256;
589 int y = (h0 - h);
590 if ( y < 0 )
591 y = 0;
592
593 if ( flags & ShowAbove )
594 {
595 win->SetSize( rect.x, rect.y + h0 - h, rect.width, h );
596 }
597 else
598 {
599 popup->Move( 0, -y );
600 win->SetSize( rect.x, rect.y, rect.width, h );
601 }
602
603 wxMilliSleep( resolution );
604 wxYield();
605
606 // Popup was hidden before it was fully shown?
607 if ( IsPopupWindowState(Hidden) )
608 return true;
609 }
610
611 popup->Move( 0, 0 );
612 }
613 #else
614 wxUnusedVar(rect);
615 wxUnusedVar(flags);
616 #endif
617
618 return true;
619 }
620
621 wxCoord wxComboCtrl::GetNativeTextIndent() const
622 {
623 if ( wxUxThemeEngine::GetIfActive() )
624 return NATIVE_TEXT_INDENT_XP;
625 return NATIVE_TEXT_INDENT_CLASSIC;
626 }
627
628 bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
629 {
630 const bool isPopupShown = IsPopupShown();
631
632 switch ( event.GetKeyCode() )
633 {
634 case WXK_F4:
635 // F4 toggles the popup in the native comboboxes, so emulate them
636 if ( !event.AltDown() )
637 return true;
638 break;
639
640 case WXK_ESCAPE:
641 if ( isPopupShown )
642 return true;
643 break;
644
645 case WXK_DOWN:
646 case WXK_UP:
647 // On XP or with writable combo in Classic, arrows don't open the
648 // popup but Alt-arrow does
649 if ( event.AltDown() ||
650 ( !isPopupShown &&
651 HasFlag(wxCB_READONLY) &&
652 !wxUxThemeEngine::GetIfActive()
653 ) )
654 {
655 return true;
656 }
657 break;
658 }
659
660 return false;
661 }
662
663 #endif // wxUSE_COMBOCTRL