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