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: combo.cpp 64477 2010-06-03 15:25:41Z JMS $
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     #include "wx/stopwatch.h"
35 #endif
36 
37 #include "wx/dcbuffer.h"
38 #include "wx/combo.h"
39 
40 #include "wx/msw/registry.h"
41 
42 #if wxUSE_UXTHEME
43 #include "wx/msw/uxtheme.h"
44 #endif
45 
46 //
47 // Define wxUSE_COMBOCTRL_VISTA_RENDERING as 1 to have read-only wxComboCtrl
48 // and wxOwnerDrawnComboBox appear much nicer on Win Vista/7. Disabled by
49 // default as this may cause some subtle rendering bugs with custom
50 // wxComboCtrls.
51 //
52 #ifndef wxUSE_COMBOCTRL_VISTA_RENDERING
53     #define wxUSE_COMBOCTRL_VISTA_RENDERING 0
54 #endif
55 
56 // Change to #if 1 to include tmschema.h for easier testing of theme
57 // parameters.
58 #if 0
59     #include <tmschema.h>
60     #include <VSStyle.h>
61 #else
62     //----------------------------------
63     #define EP_EDITTEXT         1
64     #define ETS_NORMAL          1
65     #define ETS_HOT             2
66     #define ETS_SELECTED        3
67     #define ETS_DISABLED        4
68     #define ETS_FOCUSED         5
69     #define ETS_READONLY        6
70     #define ETS_ASSIST          7
71     #define TMT_FILLCOLOR       3802
72     #define TMT_TEXTCOLOR       3803
73     #define TMT_BORDERCOLOR     3801
74     #define TMT_EDGEFILLCOLOR   3808
75     #define TMT_BGTYPE          4001
76 
77     #define BT_IMAGEFILE        0
78     #define BT_BORDERFILL       1
79 
80     #define CP_DROPDOWNBUTTON           1
81     #define CP_BACKGROUND               2 // This and above are Vista and later only
82     #define CP_TRANSPARENTBACKGROUND    3
83     #define CP_BORDER                   4
84     #define CP_READONLY                 5
85     #define CP_DROPDOWNBUTTONRIGHT      6
86     #define CP_DROPDOWNBUTTONLEFT       7
87     #define CP_CUEBANNER                8
88 
89     #define CBXS_NORMAL                 1
90     #define CBXS_HOT                    2
91     #define CBXS_PRESSED                3
92     #define CBXS_DISABLED               4
93 
94     #define CBXSR_NORMAL                1
95     #define CBXSR_HOT                   2
96     #define CBXSR_PRESSED               3
97     #define CBXSR_DISABLED              4
98 
99     #define CBXSL_NORMAL                1
100     #define CBXSL_HOT                   2
101     #define CBXSL_PRESSED               3
102     #define CBXSL_DISABLED              4
103 
104     #define CBTBS_NORMAL                1
105     #define CBTBS_HOT                   2
106     #define CBTBS_DISABLED              3
107     #define CBTBS_FOCUSED               4
108 
109     #define CBB_NORMAL                  1
110     #define CBB_HOT                     2
111     #define CBB_FOCUSED                 3
112     #define CBB_DISABLED                4
113 
114     #define CBRO_NORMAL                 1
115     #define CBRO_HOT                    2
116     #define CBRO_PRESSED                3
117     #define CBRO_DISABLED               4
118 
119     #define CBCB_NORMAL                 1
120     #define CBCB_HOT                    2
121     #define CBCB_PRESSED                3
122     #define CBCB_DISABLED               4
123 
124 #endif
125 
126 // In wx2.9, this is defined in msw\private.h
127 #define wxWinVersion_Vista 0x0600
128 
129 #define NATIVE_TEXT_INDENT_XP       4
130 #define NATIVE_TEXT_INDENT_CLASSIC  2
131 
132 #define TEXTCTRLXADJUST_XP          1
133 #define TEXTCTRLYADJUST_XP          3
134 #define TEXTCTRLXADJUST_CLASSIC     1
135 #define TEXTCTRLYADJUST_CLASSIC     2
136 
137 #define COMBOBOX_ANIMATION_RESOLUTION   10
138 
139 #define COMBOBOX_ANIMATION_DURATION     200  // In milliseconds
140 
141 #define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM    (1<<2)
142 
143 
144 // ============================================================================
145 // implementation
146 // ============================================================================
147 
148 
BEGIN_EVENT_TABLE(wxComboCtrl,wxComboCtrlBase)149 BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
150     EVT_PAINT(wxComboCtrl::OnPaintEvent)
151     EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
152 #if wxUSE_COMBOCTRL_POPUP_ANIMATION
153     EVT_TIMER(wxID_ANY, wxComboCtrl::OnTimerEvent)
154 #endif
155 END_EVENT_TABLE()
156 
157 
158 IMPLEMENT_DYNAMIC_CLASS(wxComboCtrl, wxComboCtrlBase)
159 
160 void wxComboCtrl::Init()
161 {
162 }
163 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)164 bool wxComboCtrl::Create(wxWindow *parent,
165                             wxWindowID id,
166                             const wxString& value,
167                             const wxPoint& pos,
168                             const wxSize& size,
169                             long style,
170                             const wxValidator& validator,
171                             const wxString& name)
172 {
173 
174     // Set border
175     long border = style & wxBORDER_MASK;
176 
177 #if wxUSE_UXTHEME
178     wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
179 #endif
180 
181     if ( !border )
182     {
183         // For XP, have 1-width custom border, for older version use sunken
184 #if wxUSE_UXTHEME
185         if ( theme )
186         {
187             border = wxBORDER_NONE;
188             m_widthCustomBorder = 1;
189         }
190         else
191 #endif
192             border = wxBORDER_SUNKEN;
193 
194         style = (style & ~(wxBORDER_MASK)) | border;
195     }
196 
197     // create main window
198     if ( !wxComboCtrlBase::Create(parent,
199                            id,
200                            value,
201                            pos,
202                            size,
203                            style | wxFULL_REPAINT_ON_RESIZE,
204                            wxDefaultValidator,
205                            name) )
206         return false;
207 
208     if ( style & wxCC_STD_BUTTON )
209         m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
210 
211     // Create textctrl, if necessary
212     CreateTextCtrl( wxNO_BORDER, validator );
213 
214     // Add keyboard input handlers for main control and textctrl
215     InstallInputHandlers();
216 
217     // Prepare background for double-buffering
218     SetBackgroundStyle( wxBG_STYLE_CUSTOM );
219 
220     // SetInitialSize should be called last
221     SetInitialSize(size);
222 
223     return true;
224 }
225 
~wxComboCtrl()226 wxComboCtrl::~wxComboCtrl()
227 {
228 }
229 
OnThemeChange()230 void wxComboCtrl::OnThemeChange()
231 {
232     // there doesn't seem to be any way to get the text colour using themes
233     // API: TMT_TEXTCOLOR doesn't work neither for EDIT nor COMBOBOX
234     if ( !m_hasFgCol )
235     {
236         wxColour fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
237         SetForegroundColour(fgCol);
238         m_hasFgCol = false;
239     }
240 
241     wxColour bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
242 
243 #if wxUSE_UXTHEME
244     wxUxThemeEngine * const theme = wxUxThemeEngine::GetIfActive();
245     if ( theme )
246     {
247         // NB: use EDIT, not COMBOBOX (the latter works in XP but not Vista)
248         wxUxThemeHandle hTheme(this, L"EDIT");
249         COLORREF col;
250         HRESULT hr = theme->GetThemeColor
251                             (
252                                 hTheme,
253                                 EP_EDITTEXT,
254                                 ETS_NORMAL,
255                                 TMT_FILLCOLOR,
256                                 &col
257                             );
258         if ( SUCCEEDED(hr) )
259         {
260             bgCol = wxRGBToColour(col);
261         }
262         else
263         {
264             wxLogApiError(_T("GetThemeColor(EDIT, ETS_NORMAL, TMT_FILLCOLOR)"),
265                           hr);
266         }
267     }
268 #endif
269 
270     if ( !m_hasBgCol )
271     {
272         SetBackgroundColour(bgCol);
273         m_hasBgCol = false;
274     }
275 }
276 
OnResize()277 void wxComboCtrl::OnResize()
278 {
279     //
280     // Recalculates button and textctrl areas
281 
282     int textCtrlXAdjust;
283     int textCtrlYAdjust;
284 
285 #if wxUSE_UXTHEME
286     if ( wxUxThemeEngine::GetIfActive() )
287     {
288         textCtrlXAdjust = TEXTCTRLXADJUST_XP;
289         textCtrlYAdjust = TEXTCTRLYADJUST_XP;
290     }
291     else
292 #endif
293     {
294         textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
295         textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
296     }
297 
298     // Technically Classic Windows style combo has more narrow button,
299     // but the native renderer doesn't paint it well like that.
300     int btnWidth = 17;
301     CalculateAreas(btnWidth);
302 
303     // Position textctrl using standard routine
304     PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
305 }
306 
307 // Draws non-XP GUI dotted line around the focus area
wxMSWDrawFocusRect(wxDC & dc,const wxRect & rect)308 static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
309 {
310 #if !defined(__WXWINCE__)
311     /*
312     RECT mswRect;
313     mswRect.left = rect.x;
314     mswRect.top = rect.y;
315     mswRect.right = rect.x + rect.width;
316     mswRect.bottom = rect.y + rect.height;
317     HDC hdc = (HDC) dc.GetHDC();
318     SetMapMode(hdc,MM_TEXT); // Just in case...
319     DrawFocusRect(hdc,&mswRect);
320     */
321     // FIXME: Use DrawFocusRect code above (currently it draws solid line
322     //   for caption focus but works ok for other stuff).
323     //   Also, this code below may not work in future wx versions, since
324     //   it employs wxCAP_BUTT hack to have line of width 1.
325     dc.SetLogicalFunction(wxINVERT);
326 
327     wxPen pen(*wxBLACK,1,wxDOT);
328     pen.SetCap(wxCAP_BUTT);
329     dc.SetPen(pen);
330     dc.SetBrush(*wxTRANSPARENT_BRUSH);
331 
332     dc.DrawRectangle(rect);
333 
334     dc.SetLogicalFunction(wxCOPY);
335 #else
336     dc.SetLogicalFunction(wxINVERT);
337 
338     dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
339     dc.SetBrush(*wxTRANSPARENT_BRUSH);
340 
341     dc.DrawRectangle(rect);
342 
343     dc.SetLogicalFunction(wxCOPY);
344 #endif
345 }
346 
347 // draw focus background on area in a way typical on platform
348 void
PrepareBackground(wxDC & dc,const wxRect & rect,int flags) const349 wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
350 {
351 #if wxUSE_UXTHEME
352     wxUxThemeHandle hTheme(this, L"COMBOBOX");
353 #endif
354 
355     wxSize sz = GetClientSize();
356     bool isEnabled;
357     bool doDrawFocusRect; // also selected
358 
359     // For smaller size control (and for disabled background) use less spacing
360     int focusSpacingX;
361     int focusSpacingY;
362 
363     if ( !(flags & wxCONTROL_ISSUBMENU) )
364     {
365         // Drawing control
366         isEnabled = IsEnabled();
367         doDrawFocusRect = ShouldDrawFocus();
368 
369 #if wxUSE_UXTHEME
370         // Windows-style: for smaller size control (and for disabled background) use less spacing
371         if ( hTheme )
372         {
373             // WinXP  Theme
374             focusSpacingX = isEnabled ? 2 : 1;
375             focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
376         }
377         else
378 #endif
379         {
380             // Classic Theme
381             if ( isEnabled )
382             {
383                 focusSpacingX = 1;
384                 focusSpacingY = 1;
385             }
386             else
387             {
388                 focusSpacingX = 0;
389                 focusSpacingY = 0;
390             }
391         }
392     }
393     else
394     {
395         // Drawing a list item
396         isEnabled = true; // they are never disabled
397         doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
398 
399         focusSpacingX = 0;
400         focusSpacingY = 0;
401     }
402 
403     // Set the background sub-rectangle for selection, disabled etc
404     wxRect selRect(rect);
405     selRect.y += focusSpacingY;
406     selRect.height -= (focusSpacingY*2);
407 
408     int wcp = 0;
409 
410     if ( !(flags & wxCONTROL_ISSUBMENU) )
411         wcp += m_widthCustomPaint;
412 
413     selRect.x += wcp + focusSpacingX;
414     selRect.width -= wcp + (focusSpacingX*2);
415 
416     //wxUxThemeEngine* theme = NULL;
417     //if ( hTheme )
418     //    theme = wxUxThemeEngine::GetIfActive();
419 
420     wxColour fgCol;
421     wxColour bgCol;
422     bool doDrawDottedEdge = false;
423     bool doDrawSelRect = true;
424 
425     // TODO: doDrawDottedEdge = true when focus has arrived to control via tab.
426     //       (and other cases which are not that apparent).
427 
428     if ( isEnabled )
429     {
430         // If popup is hidden and this control is focused,
431         // then draw the focus-indicator (selbgcolor background etc.).
432         if ( doDrawFocusRect )
433         {
434             // NB: We can't really use XP visual styles to get TMT_TEXTCOLOR since
435             //     it is not properly defined for combo boxes. Instead, they expect
436             //     you to use DrawThemeText.
437             //
438             //    Here is, however, sample code how to get theme colours:
439             //
440             //    COLORREF cref;
441             //    theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
442             //    dc.SetTextForeground( wxRGBToColour(cref) );
443             if ( (m_iFlags & wxCC_FULL_BUTTON) && !(flags & wxCONTROL_ISSUBMENU) )
444             {
445                 // Vista style read-only combo
446                 fgCol = GetForegroundColour();
447                 bgCol = GetBackgroundColour();
448                 doDrawSelRect = false;
449                 doDrawDottedEdge = true;
450             }
451             else
452             {
453                 fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
454                 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
455             }
456         }
457         else
458         {
459             fgCol = GetForegroundColour();
460             bgCol = GetBackgroundColour();
461             doDrawSelRect = false;
462         }
463     }
464     else
465     {
466         fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
467         bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
468     }
469 
470     dc.SetTextForeground(fgCol);
471     dc.SetBrush(bgCol);
472     if ( doDrawSelRect )
473     {
474         dc.SetPen(bgCol);
475         dc.DrawRectangle(selRect);
476     }
477 
478     if ( doDrawDottedEdge )
479         wxMSWDrawFocusRect(dc, selRect);
480 
481     // Don't clip exactly to the selection rectangle so we can draw
482     // to the non-selected area in front of it.
483     wxRect clipRect(rect.x,rect.y,
484                     (selRect.x+selRect.width)-rect.x-1,rect.height);
485     dc.SetClippingRegion(clipRect);
486 }
487 
OnPaintEvent(wxPaintEvent & WXUNUSED (event))488 void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
489 {
490     // TODO: Convert drawing in this function to Windows API Code
491 
492     // TODO: Convert drawing in this function to Windows API Code
493 
494     wxSize sz = GetClientSize();
495     wxAutoBufferedPaintDC dc(this);
496 
497     const wxRect& rectButton = m_btnArea;
498     wxRect rectTextField = m_tcArea;
499     wxColour bgCol = GetBackgroundColour();
500 
501 #if wxUSE_UXTHEME
502     const bool isEnabled = IsEnabled();
503 
504     HDC hDc = GetHdcOf(dc);
505     HWND hWnd = GetHwndOf(this);
506 
507     wxUxThemeEngine* theme = NULL;
508     wxUxThemeHandle hTheme(this, L"COMBOBOX");
509 
510     if ( hTheme )
511         theme = wxUxThemeEngine::GetIfActive();
512 #endif // wxUSE_UXTHEME
513 
514     wxRect borderRect(0,0,sz.x,sz.y);
515 
516     if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
517     {
518         borderRect = m_tcArea;
519         borderRect.Inflate(1);
520     }
521 
522     int drawButFlags = 0;
523 
524 #if wxUSE_UXTHEME
525     if ( hTheme )
526     {
527   #if wxUSE_COMBOCTRL_VISTA_RENDERING
528         const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista;
529   #else
530         const bool useVistaComboBox = false;
531   #endif
532 
533         RECT rFull;
534         wxCopyRectToRECT(borderRect, rFull);
535 
536         RECT rButton;
537         wxCopyRectToRECT(rectButton, rButton);
538 
539         RECT rBorder;
540         wxCopyRectToRECT(borderRect, rBorder);
541 
542         bool isNonStdButton = (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) ||
543                               (m_iFlags & wxCC_IFLAG_HAS_NONSTANDARD_BUTTON);
544 
545         //
546         // Get some states for themed drawing
547         int butState;
548 
549         if ( !isEnabled )
550         {
551             butState = CBXS_DISABLED;
552         }
553         // Vista will display the drop-button as depressed always
554         // when the popup window is visilbe
555         else if ( (m_btnState & wxCONTROL_PRESSED) ||
556                   (useVistaComboBox && !IsPopupWindowState(Hidden)) )
557         {
558             butState = CBXS_PRESSED;
559         }
560         else if ( m_btnState & wxCONTROL_CURRENT )
561         {
562             butState = CBXS_HOT;
563         }
564         else
565         {
566             butState = CBXS_NORMAL;
567         }
568 
569         int comboBoxPart = 0;  // For XP, use the 'default' part
570         RECT* rUseForBg = &rBorder;
571 
572         int bgState = butState;
573 
574   #if wxUSE_COMBOCTRL_VISTA_RENDERING
575         bool drawFullButton = false;
576 
577         if ( useVistaComboBox )
578         {
579             const bool isFocused =
580                 (FindFocus() == GetMainWindowOfCompositeControl()) ?
581                     true : false;
582 
583             // FIXME: Either SetBackgroundColour or GetBackgroundColour
584             //        doesn't work under Vista, so here's a temporary
585             //        workaround.
586             bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
587 
588             // Draw the entire control as a single button?
589             if ( !isNonStdButton )
590             {
591                 if ( HasFlag(wxCB_READONLY) )
592                     drawFullButton = true;
593             }
594 
595             if ( drawFullButton )
596             {
597                 comboBoxPart = CP_READONLY;
598                 rUseForBg = &rFull;
599 
600                 // It should be safe enough to update this flag here.
601                 m_iFlags |= wxCC_FULL_BUTTON;
602             }
603             else
604             {
605                 comboBoxPart = CP_BORDER;
606                 m_iFlags &= ~wxCC_FULL_BUTTON;
607 
608                 if ( isFocused )
609                     bgState = CBB_FOCUSED;
610                 else
611                     bgState = CBB_NORMAL;
612             }
613         }
614   #endif
615 
616         //
617         // Draw parent's background, if necessary
618         RECT* rUseForTb = NULL;
619 
620         if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
621             rUseForTb = &rFull;
622         else if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
623             rUseForTb = &rButton;
624 
625         if ( rUseForTb )
626             theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
627 
628         //
629         // Draw the control background (including the border)
630         if ( m_widthCustomBorder > 0 )
631         {
632             theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
633         }
634         else
635         {
636             // No border. We can't use theme, since it cannot be relied on
637             // to deliver borderless drawing, even with DrawThemeBackgroundEx.
638             dc.SetBrush(bgCol);
639             dc.SetPen(bgCol);
640             dc.DrawRectangle(borderRect);
641         }
642 
643         //
644         // Draw the drop-button
645         if ( !isNonStdButton )
646         {
647             drawButFlags = Draw_BitmapOnly;
648 
649             int butPart = CP_DROPDOWNBUTTON;
650 
651   #if wxUSE_COMBOCTRL_VISTA_RENDERING
652             if ( useVistaComboBox )
653             {
654                 if ( drawFullButton )
655                 {
656                     // We need to alter the button style slightly before
657                     // drawing the actual button (but it was good above
658                     // when background etc was done).
659                     if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
660                         butState = CBXS_NORMAL;
661                 }
662 
663                 if ( m_btnSide == wxRIGHT )
664                     butPart = CP_DROPDOWNBUTTONRIGHT;
665                 else
666                     butPart = CP_DROPDOWNBUTTONLEFT;
667 
668             }
669   #endif
670             theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
671         }
672         else if ( useVistaComboBox &&
673                   (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) )
674         {
675             // We'll do this, because DrawThemeParentBackground
676             // doesn't seem to be reliable on Vista.
677             drawButFlags |= Draw_PaintBg;
678         }
679     }
680     else
681 #endif
682     {
683         // Windows 2000 and earlier
684         drawButFlags = Draw_PaintBg;
685 
686         dc.SetBrush(bgCol);
687         dc.SetPen(bgCol);
688         dc.DrawRectangle(borderRect);
689     }
690 
691     // Button rendering (may only do the bitmap on button, depending on the flags)
692     DrawButton( dc, rectButton, drawButFlags );
693 
694     // Paint required portion of the custom image on the control
695     if ( (!m_text || m_widthCustomPaint) )
696     {
697         wxASSERT( m_widthCustomPaint >= 0 );
698 
699         // this is intentionally here to allow drawed rectangle's
700         // right edge to be hidden
701         if ( m_text )
702             rectTextField.width = m_widthCustomPaint;
703 
704         dc.SetFont( GetFont() );
705 
706         dc.SetClippingRegion(rectTextField);
707         if ( m_popupInterface )
708             m_popupInterface->PaintComboControl(dc,rectTextField);
709         else
710             wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField);
711     }
712 }
713 
OnMouseEvent(wxMouseEvent & event)714 void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
715 {
716     int mx = event.m_x;
717     bool isOnButtonArea = m_btnArea.Contains(mx,event.m_y);
718     int handlerFlags = isOnButtonArea ? wxCC_MF_ON_BUTTON : 0;
719 
720     if ( PreprocessMouseEvent(event,isOnButtonArea) )
721         return;
722 
723     if ( (m_windowStyle & (wxCC_SPECIAL_DCLICK|wxCB_READONLY)) == wxCB_READONLY )
724     {
725         // if no textctrl and no special double-click, then the entire control acts
726         // as a button
727         handlerFlags |= wxCC_MF_ON_BUTTON;
728         if ( HandleButtonMouseEvent(event,handlerFlags) )
729             return;
730     }
731     else
732     {
733         if ( isOnButtonArea || HasCapture() ||
734              (m_widthCustomPaint && mx < (m_tcArea.x+m_widthCustomPaint)) )
735         {
736             handlerFlags |= wxCC_MF_ON_CLICK_AREA;
737 
738             if ( HandleButtonMouseEvent(event,handlerFlags) )
739                 return;
740         }
741         else if ( m_btnState )
742         {
743             // otherwise need to clear the hover status
744             m_btnState = 0;
745             RefreshRect(m_btnArea);
746         }
747     }
748 
749     //
750     // This will handle left_down and left_dclick events outside button in a Windows-like manner.
751     // See header file for further information on this method.
752     HandleNormalMouseEvent(event);
753 
754 }
755 
756 #if wxUSE_COMBOCTRL_POPUP_ANIMATION
GetUserPreferencesMask()757 static wxUint32 GetUserPreferencesMask()
758 {
759     static wxUint32 userPreferencesMask = 0;
760     static bool valueSet = false;
761 
762     if ( valueSet )
763         return userPreferencesMask;
764 
765     wxRegKey* pKey = NULL;
766     wxRegKey key1(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Control Panel"));
767     wxRegKey key2(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Windows\\Control Panel"));
768     wxRegKey key3(wxRegKey::HKCU, wxT("Control Panel\\Desktop"));
769 
770     if ( key1.Exists() )
771         pKey = &key1;
772     else if ( key2.Exists() )
773         pKey = &key2;
774     else if ( key3.Exists() )
775         pKey = &key3;
776 
777     if ( pKey && pKey->Open(wxRegKey::Read) )
778     {
779         wxMemoryBuffer buf;
780         if ( pKey->HasValue(wxT("UserPreferencesMask")) &&
781              pKey->QueryValue(wxT("UserPreferencesMask"), buf) )
782         {
783             if ( buf.GetDataLen() >= 4 )
784             {
785                 wxUint32* p = (wxUint32*) buf.GetData();
786                 userPreferencesMask = *p;
787             }
788         }
789     }
790 
791     valueSet = true;
792 
793     return userPreferencesMask;
794 }
795 #endif
796 
797 #if wxUSE_COMBOCTRL_POPUP_ANIMATION
OnTimerEvent(wxTimerEvent & WXUNUSED (event))798 void wxComboCtrl::OnTimerEvent( wxTimerEvent& WXUNUSED(event) )
799 {
800     bool stopTimer = false;
801 
802     wxWindow* win = GetPopupWindow();
803     wxWindow* popup = GetPopupControl()->GetControl();
804 
805     // Popup was hidden before it was fully shown?
806     if ( IsPopupWindowState(Hidden) )
807     {
808         stopTimer = true;
809     }
810     else
811     {
812         wxLongLong t = ::wxGetLocalTimeMillis();
813         const wxRect& rect = m_animRect;
814 
815         int pos = (int) (t-m_animStart).GetLo();
816         if ( pos < COMBOBOX_ANIMATION_DURATION )
817         {
818             int height = rect.height;
819             //int h0 = rect.height;
820             int h = (((pos*256)/COMBOBOX_ANIMATION_DURATION)*height)/256;
821             int y = (height - h);
822             if ( y < 0 )
823                 y = 0;
824 
825             if ( m_animFlags & ShowAbove )
826             {
827                 win->SetSize( rect.x, rect.y + height - h, rect.width, h );
828             }
829             else
830             {
831                 // Note that apparently Move() should be called after
832                 // SetSize() to reduce (or even eliminate) animation garbage
833                 win->SetSize( rect.x, rect.y, rect.width, h );
834                 popup->Move( 0, -y );
835             }
836         }
837         else
838         {
839             stopTimer = true;
840         }
841     }
842 
843     if ( stopTimer )
844     {
845         m_animTimer.Stop();
846         DoShowPopup( m_animRect, m_animFlags );
847         popup->Move( 0, 0 );
848 
849         // Do a one final refresh to clean up the rare cases of animation
850         // garbage
851         win->Refresh();
852     }
853 }
854 #endif
855 
856 #if wxUSE_COMBOCTRL_POPUP_ANIMATION
AnimateShow(const wxRect & rect,int flags)857 bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
858 {
859     if ( GetUserPreferencesMask() & wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM )
860     {
861         m_animStart = ::wxGetLocalTimeMillis();
862         m_animRect = rect;
863         m_animFlags = flags;
864 
865         wxWindow* win = GetPopupWindow();
866         win->SetSize( rect.x, rect.y, rect.width, 0 );
867         win->Show();
868 
869         m_animTimer.SetOwner( this, wxID_ANY );
870         m_animTimer.Start( COMBOBOX_ANIMATION_RESOLUTION, wxTIMER_CONTINUOUS );
871 
872         OnTimerEvent(*((wxTimerEvent*)NULL));  // Event is never used, so we can give NULL
873 
874         return false;
875     }
876 
877     return true;
878 }
879 #endif
880 
GetNativeTextIndent() const881 wxCoord wxComboCtrl::GetNativeTextIndent() const
882 {
883 #if wxUSE_UXTHEME
884     if ( wxUxThemeEngine::GetIfActive() )
885         return NATIVE_TEXT_INDENT_XP;
886 #endif
887     return NATIVE_TEXT_INDENT_CLASSIC;
888 }
889 
IsKeyPopupToggle(const wxKeyEvent & event) const890 bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
891 {
892     const bool isPopupShown = IsPopupShown();
893 
894     switch ( event.GetKeyCode() )
895     {
896         case WXK_F4:
897             // F4 toggles the popup in the native comboboxes, so emulate them
898             if ( !event.AltDown() )
899                 return true;
900             break;
901 
902         case WXK_ESCAPE:
903             if ( isPopupShown )
904                 return true;
905             break;
906 
907         case WXK_DOWN:
908         case WXK_UP:
909         case WXK_NUMPAD_DOWN:
910         case WXK_NUMPAD_UP:
911             // Alt plus arrow key toggles the popup in the native combo box
912             if ( event.AltDown() )
913                 return true;
914             break;
915     }
916 
917     return false;
918 }
919 
920 #endif // wxUSE_COMBOCTRL
921