]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/combo.cpp
wxArrayEditorDialog now uses wxEditableListBox. It has also been renamed to wxPGArray...
[wxWidgets.git] / src / msw / combo.cpp
index e8c7611295b2724255c713c413012c38e7fbe454..109c74cbd344675235324d35c46037a5afc3a6cb 100644 (file)
     #include "wx/dcclient.h"
     #include "wx/settings.h"
     #include "wx/dialog.h"
+    #include "wx/stopwatch.h"
 #endif
 
 #include "wx/dcbuffer.h"
 #include "wx/combo.h"
-#include "wx/stopwatch.h"
 
 #include "wx/msw/registry.h"
+#if wxUSE_UXTHEME
 #include "wx/msw/uxtheme.h"
+#endif
+#include "wx/msw/dc.h"
 
 // Change to #if 1 to include tmschema.h for easier testing of theme
 // parameters.
 #if 0
     #include <tmschema.h>
+    #include <VSStyle.h>
 #else
     //----------------------------------
     #define EP_EDITTEXT         1
     #define TMT_TEXTCOLOR       3803
     #define TMT_BORDERCOLOR     3801
     #define TMT_EDGEFILLCOLOR   3808
-    //----------------------------------
+    #define TMT_BGTYPE          4001
+
+    #define BT_IMAGEFILE        0
+    #define BT_BORDERFILL       1
+
+    #define CP_DROPDOWNBUTTON           1
+    #define CP_BACKGROUND               2 // This and above are Vista and later only
+    #define CP_TRANSPARENTBACKGROUND    3
+    #define CP_BORDER                   4
+    #define CP_READONLY                 5
+    #define CP_DROPDOWNBUTTONRIGHT      6
+    #define CP_DROPDOWNBUTTONLEFT       7
+    #define CP_CUEBANNER                8
+
+    #define CBXS_NORMAL                 1
+    #define CBXS_HOT                    2
+    #define CBXS_PRESSED                3
+    #define CBXS_DISABLED               4
+
+    #define CBXSR_NORMAL                1
+    #define CBXSR_HOT                   2
+    #define CBXSR_PRESSED               3
+    #define CBXSR_DISABLED              4
+
+    #define CBXSL_NORMAL                1
+    #define CBXSL_HOT                   2
+    #define CBXSL_PRESSED               3
+    #define CBXSL_DISABLED              4
+
+    #define CBTBS_NORMAL                1
+    #define CBTBS_HOT                   2
+    #define CBTBS_DISABLED              3
+    #define CBTBS_FOCUSED               4
+
+    #define CBB_NORMAL                  1
+    #define CBB_HOT                     2
+    #define CBB_FOCUSED                 3
+    #define CBB_DISABLED                4
+
+    #define CBRO_NORMAL                 1
+    #define CBRO_HOT                    2
+    #define CBRO_PRESSED                3
+    #define CBRO_DISABLED               4
+
+    #define CBCB_NORMAL                 1
+    #define CBCB_HOT                    2
+    #define CBCB_PRESSED                3
+    #define CBCB_DISABLED               4
+
 #endif
 
 
 #define NATIVE_TEXT_INDENT_XP       4
 #define NATIVE_TEXT_INDENT_CLASSIC  2
 
-#define TEXTCTRLXADJUST_XP          1
 #define TEXTCTRLYADJUST_XP          3
-#define TEXTCTRLXADJUST_CLASSIC     1
-#define TEXTCTRLYADJUST_CLASSIC     2
+#define TEXTCTRLYADJUST_CLASSIC     3
+
+#define COMBOBOX_ANIMATION_RESOLUTION   10
 
 #define COMBOBOX_ANIMATION_DURATION     200  // In milliseconds
 
+#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM    (1<<2)
+
+
 // ============================================================================
 // implementation
 // ============================================================================
 BEGIN_EVENT_TABLE(wxComboCtrl, wxComboCtrlBase)
     EVT_PAINT(wxComboCtrl::OnPaintEvent)
     EVT_MOUSE_EVENTS(wxComboCtrl::OnMouseEvent)
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+    EVT_TIMER(wxID_ANY, wxComboCtrl::OnTimerEvent)
+#endif
 END_EVENT_TABLE()
 
 
@@ -102,17 +160,21 @@ bool wxComboCtrl::Create(wxWindow *parent,
     // Set border
     long border = style & wxBORDER_MASK;
 
+#if wxUSE_UXTHEME
     wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
+#endif
 
     if ( !border )
     {
-        // For XP, have 1-width custom border, for older version use sunken
+#if wxUSE_UXTHEME
         if ( theme )
         {
+            // For XP, have 1-width custom border, for older version use sunken
             border = wxBORDER_NONE;
             m_widthCustomBorder = 1;
         }
         else
+#endif
             border = wxBORDER_SUNKEN;
 
         style = (style & ~(wxBORDER_MASK)) | border;
@@ -129,6 +191,14 @@ bool wxComboCtrl::Create(wxWindow *parent,
                            name) )
         return false;
 
+#if wxUSE_UXTHEME
+    if ( theme )
+    {
+        if ( ::wxGetWinVersion() >= wxWinVersion_Vista )
+            m_iFlags |= wxCC_BUTTON_STAYS_DOWN |wxCC_BUTTON_COVERS_BORDER;
+    }
+#endif
+
     if ( style & wxCC_STD_BUTTON )
         m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
 
@@ -141,8 +211,8 @@ bool wxComboCtrl::Create(wxWindow *parent,
     // Prepare background for double-buffering
     SetBackgroundStyle( wxBG_STYLE_CUSTOM );
 
-    // SetBestSize should be called last
-    SetBestSize(size);
+    // SetInitialSize should be called last
+    SetInitialSize(size);
 
     return true;
 }
@@ -151,42 +221,21 @@ wxComboCtrl::~wxComboCtrl()
 {
 }
 
-void wxComboCtrl::OnThemeChange()
-{
-    wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
-    if ( theme )
-    {
-        wxUxThemeHandle hTheme(this, L"COMBOBOX");
-
-        COLORREF col;
-        theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&col);
-        SetBackgroundColour(wxRGBToColour(col));
-        theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&col);
-        SetForegroundColour(wxRGBToColour(col));
-    }
-    else
-    {
-        SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-        SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
-    }
-}
-
 void wxComboCtrl::OnResize()
 {
     //
     // Recalculates button and textctrl areas
 
-    int textCtrlXAdjust;
     int textCtrlYAdjust;
 
+#if wxUSE_UXTHEME
     if ( wxUxThemeEngine::GetIfActive() )
     {
-        textCtrlXAdjust = TEXTCTRLXADJUST_XP;
         textCtrlYAdjust = TEXTCTRLYADJUST_XP;
     }
     else
+#endif
     {
-        textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
         textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
     }
 
@@ -196,7 +245,7 @@ void wxComboCtrl::OnResize()
     CalculateAreas(btnWidth);
 
     // Position textctrl using standard routine
-    PositionTextCtrl(textCtrlXAdjust,textCtrlYAdjust);
+    PositionTextCtrl(0, textCtrlYAdjust);
 }
 
 // Draws non-XP GUI dotted line around the focus area
@@ -219,7 +268,7 @@ static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
     //   it employs wxCAP_BUTT hack to have line of width 1.
     dc.SetLogicalFunction(wxINVERT);
 
-    wxPen pen(*wxBLACK,1,wxDOT);
+    wxPen pen(*wxBLACK, 1, wxPENSTYLE_DOT);
     pen.SetCap(wxCAP_BUTT);
     dc.SetPen(pen);
     dc.SetBrush(*wxTRANSPARENT_BRUSH);
@@ -240,18 +289,16 @@ static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
 }
 
 // draw focus background on area in a way typical on platform
-void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
+void
+wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
 {
-    wxUxThemeEngine* theme = (wxUxThemeEngine*) NULL;
-
-    // Constructor only calls GetHWND() const, so it should be safe
-    // to cast "this" to const.
+#if wxUSE_UXTHEME
     wxUxThemeHandle hTheme(this, L"COMBOBOX");
-    //COLORREF cref;
+#endif
 
     wxSize sz = GetClientSize();
     bool isEnabled;
-    bool isFocused; // also selected
+    bool doDrawFocusRect; // also selected
 
     // For smaller size control (and for disabled background) use less spacing
     int focusSpacingX;
@@ -261,8 +308,9 @@ void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) c
     {
         // Drawing control
         isEnabled = IsEnabled();
-        isFocused = ShouldDrawFocus();
+        doDrawFocusRect = ShouldDrawFocus();
 
+#if wxUSE_UXTHEME
         // Windows-style: for smaller size control (and for disabled background) use less spacing
         if ( hTheme )
         {
@@ -271,6 +319,7 @@ void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) c
             focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
         }
         else
+#endif
         {
             // Classic Theme
             if ( isEnabled )
@@ -289,7 +338,7 @@ void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) c
     {
         // Drawing a list item
         isEnabled = true; // they are never disabled
-        isFocused = flags & wxCONTROL_SELECTED ? true : false;
+        doDrawFocusRect = flags & wxCONTROL_SELECTED ? true : false;
 
         focusSpacingX = 0;
         focusSpacingY = 0;
@@ -308,74 +357,70 @@ void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) c
     selRect.x += wcp + focusSpacingX;
     selRect.width -= wcp + (focusSpacingX*2);
 
-    if ( hTheme )
-        theme = wxUxThemeEngine::GetIfActive();
+    //wxUxThemeEngine* theme = NULL;
+    //if ( hTheme )
+    //    theme = wxUxThemeEngine::GetIfActive();
 
+    wxColour fgCol;
     wxColour bgCol;
-    bool drawDottedEdge = false;
+    bool doDrawDottedEdge = false;
+    bool doDrawSelRect = true;
+
+    // TODO: doDrawDottedEdge = true when focus has arrived to control via tab.
+    //       (and other cases which are not that apparent).
 
     if ( isEnabled )
     {
         // If popup is hidden and this control is focused,
         // then draw the focus-indicator (selbgcolor background etc.).
-        if ( isFocused )
+        if ( doDrawFocusRect )
         {
-        #if 0
-            // TODO: Proper theme color getting (JMS: I don't know which parts/colors to use,
-            //       those below don't work)
-            if ( hTheme )
+            // NB: We can't really use XP visual styles to get TMT_TEXTCOLOR since
+            //     it is not properly defined for combo boxes. Instead, they expect
+            //     you to use DrawThemeText.
+            //
+            //    Here is, however, sample code how to get theme colours:
+            //
+            //    COLORREF cref;
+            //    theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
+            //    dc.SetTextForeground( wxRGBToColour(cref) );
+            if ( (m_iFlags & wxCC_FULL_BUTTON) && !(flags & wxCONTROL_ISSUBMENU) )
             {
-                theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_TEXTCOLOR,&cref);
-                dc.SetTextForeground( wxRGBToColour(cref) );
-                theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_SELECTED,TMT_FILLCOLOR,&cref);
-                bgCol = wxRGBToColour(cref);
+                // Vista style read-only combo
+                fgCol = GetForegroundColour();
+                bgCol = GetBackgroundColour();
+                doDrawSelRect = false;
+                doDrawDottedEdge = true;
             }
             else
-        #endif
             {
-                dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
+                fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
                 bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
-                if ( m_windowStyle & wxCB_READONLY )
-                    drawDottedEdge = true;
             }
         }
         else
         {
-            /*if ( hTheme )
-            {
-                theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_TEXTCOLOR,&cref);
-                dc.SetTextForeground( wxRGBToColour(cref) );
-                theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_NORMAL,TMT_FILLCOLOR,&cref);
-                bgCol = wxRGBToColour(cref);
-            }
-            else
-            {*/
-                dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
-                bgCol = GetBackgroundColour();
-            //}
+            fgCol = GetForegroundColour();
+            bgCol = GetBackgroundColour();
+            doDrawSelRect = false;
         }
     }
     else
     {
-        /*if ( hTheme )
-        {
-            theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_TEXTCOLOR,&cref);
-            dc.SetTextForeground( wxRGBToColour(cref) );
-            theme->GetThemeColor(hTheme,EP_EDITTEXT,ETS_DISABLED,TMT_EDGEFILLCOLOR,&cref);
-            bgCol = wxRGBToColour(cref);
-        }
-        else
-        {*/
-            dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
-            bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
-        //}
+        fgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT);
+        bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
     }
 
+    dc.SetTextForeground(fgCol);
     dc.SetBrush(bgCol);
-    dc.SetPen(bgCol);
-    dc.DrawRectangle(selRect);
-    if ( drawDottedEdge )
-        wxMSWDrawFocusRect(dc,selRect);
+    if ( doDrawSelRect )
+    {
+        dc.SetPen(bgCol);
+        dc.DrawRectangle(selRect);
+    }
+
+    if ( doDrawDottedEdge )
+        wxMSWDrawFocusRect(dc, selRect);
 
     // Don't clip exactly to the selection rectangle so we can draw
     // to the non-selected area in front of it.
@@ -391,84 +436,193 @@ void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
     wxSize sz = GetClientSize();
     wxAutoBufferedPaintDC dc(this);
 
-    const wxRect& rectb = m_btnArea;
-    wxRect rect = m_tcArea;
-    bool isEnabled = IsEnabled();
+    const wxRect& rectButton = m_btnArea;
+    wxRect rectTextField = m_tcArea;
     wxColour bgCol = GetBackgroundColour();
-    wxColour fgCol;
+
+#if wxUSE_UXTHEME
+    const bool isEnabled = IsEnabled();
+
+    wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
+    HDC hDc = GetHdcOf(*impl);
+    HWND hWnd = GetHwndOf(this);
 
     wxUxThemeEngine* theme = NULL;
     wxUxThemeHandle hTheme(this, L"COMBOBOX");
-    int etsState;
 
-    // area around both controls
-    wxRect rect2(0,0,sz.x,sz.y);
+    if ( hTheme )
+        theme = wxUxThemeEngine::GetIfActive();
+#endif // wxUSE_UXTHEME
+
+    wxRect borderRect(0,0,sz.x,sz.y);
+
     if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
     {
-        rect2 = m_tcArea;
-        rect2.Inflate(1);
+        borderRect = m_tcArea;
+        borderRect.Inflate(1);
     }
 
-    // Use theme to draw border on XP
+    int drawButFlags = 0;
+
+#if wxUSE_UXTHEME
     if ( hTheme )
     {
-        theme = wxUxThemeEngine::GetIfActive();
-        COLORREF cref;
+        const bool useVistaComboBox = ::wxGetWinVersion() >= wxWinVersion_Vista;
+
+        RECT rFull;
+        wxCopyRectToRECT(borderRect, rFull);
+
+        RECT rButton;
+        wxCopyRectToRECT(rectButton, rButton);
+
+        RECT rBorder;
+        wxCopyRectToRECT(borderRect, rBorder);
+
+        bool isNonStdButton = (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) ||
+                              (m_iFlags & wxCC_IFLAG_HAS_NONSTANDARD_BUTTON);
+
+        //
+        // Get some states for themed drawing
+        int butState;
 
-        // Select correct border colour
         if ( !isEnabled )
-            etsState = ETS_DISABLED;
+        {
+            butState = CBXS_DISABLED;
+        }
+        // Vista will display the drop-button as depressed always
+        // when the popup window is visilbe
+        else if ( (m_btnState & wxCONTROL_PRESSED) ||
+                  (useVistaComboBox && !IsPopupWindowState(Hidden)) )
+        {
+            butState = CBXS_PRESSED;
+        }
+        else if ( m_btnState & wxCONTROL_CURRENT )
+        {
+            butState = CBXS_HOT;
+        }
         else
-            etsState = ETS_NORMAL;
+        {
+            butState = CBXS_NORMAL;
+        }
+
+        int comboBoxPart = 0;  // For XP, use the 'default' part
+        RECT* rUseForBg = &rBorder;
+
+        bool drawFullButton = false;
+        int bgState = butState;
+        const bool isFocused = (FindFocus() == GetMainWindowOfCompositeControl()) ? true : false;
 
-        if ( m_widthCustomBorder )
+        if ( useVistaComboBox )
         {
-            theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_BORDERCOLOR,&cref);
+            // FIXME: Either SetBackgroundColour or GetBackgroundColour
+            //        doesn't work under Vista, so here's a temporary
+            //        workaround.
+            bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
 
-            // Set border colour
-            dc.SetPen( wxRGBToColour(cref) );
+            // Draw the entire control as a single button?
+            if ( !isNonStdButton )
+            {
+                if ( HasFlag(wxCB_READONLY) )
+                    drawFullButton = true;
+            }
 
-            dc.SetBrush( *wxTRANSPARENT_BRUSH );
-            dc.DrawRectangle(rect2);
+            if ( drawFullButton )
+            {
+                comboBoxPart = CP_READONLY;
+                rUseForBg = &rFull;
+
+                // It should be safe enough to update this flag here.
+                m_iFlags |= wxCC_FULL_BUTTON;
+            }
+            else
+            {
+                comboBoxPart = CP_BORDER;
+                m_iFlags &= ~wxCC_FULL_BUTTON;
+
+                if ( isFocused )
+                    bgState = CBB_FOCUSED;
+                else
+                    bgState = CBB_NORMAL;
+            }
         }
 
-        theme->GetThemeColor(hTheme,EP_EDITTEXT,etsState,TMT_TEXTCOLOR,&cref);
-        fgCol = wxRGBToColour(cref);
-    }
-    else
-    {
-        // draw regular background
-        fgCol = GetForegroundColour();
-    }
+        //
+        // Draw parent's background, if necessary
+        RECT* rUseForTb = NULL;
 
-    rect2.Deflate(m_widthCustomBorder);
+        if ( theme->IsThemeBackgroundPartiallyTransparent( hTheme, comboBoxPart, bgState ) )
+            rUseForTb = &rFull;
+        else if ( m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE )
+            rUseForTb = &rButton;
 
-    dc.SetBrush(bgCol);
-    dc.SetPen(bgCol);
+        if ( rUseForTb )
+            theme->DrawThemeParentBackground( hWnd, hDc, rUseForTb );
 
-    // clear main background
-    dc.DrawRectangle(rect);
+        //
+        // Draw the control background (including the border)
+        if ( m_widthCustomBorder > 0 )
+        {
+            theme->DrawThemeBackground( hTheme, hDc, comboBoxPart, bgState, rUseForBg, NULL );
+        }
+        else
+        {
+            // No border. We can't use theme, since it cannot be relied on
+            // to deliver borderless drawing, even with DrawThemeBackgroundEx.
+            dc.SetBrush(bgCol);
+            dc.SetPen(bgCol);
+            dc.DrawRectangle(borderRect);
+        }
 
-    // Button background with theme?
-    bool drawButBg = true;
-    if ( hTheme && m_blankButtonBg )
-    {
-        RECT r;
-        wxCopyRectToRECT(rectb, r);
+        //
+        // Draw the drop-button
+        if ( !isNonStdButton )
+        {
+            drawButFlags = Button_BitmapOnly;
+
+            int butPart = CP_DROPDOWNBUTTON;
 
-        // Draw parent background if needed (since button looks like its out of
-        // the combo, this is preferred).
-        theme->DrawThemeParentBackground(GetHwndOf(this),
-                                         GetHdcOf(dc),
-                                         &r);
+            if ( useVistaComboBox )
+            {
+                if ( drawFullButton )
+                {
+                    // We need to alter the button style slightly before
+                    // drawing the actual button (but it was good above
+                    // when background etc was done).
+                    if ( butState == CBXS_HOT || butState == CBXS_PRESSED )
+                        butState = CBXS_NORMAL;
+                }
+
+                if ( m_btnSide == wxRIGHT )
+                    butPart = CP_DROPDOWNBUTTONRIGHT;
+                else
+                    butPart = CP_DROPDOWNBUTTONLEFT;
 
-        drawButBg = false;
+            }
+            theme->DrawThemeBackground( hTheme, hDc, butPart, butState, &rButton, NULL );
+        }
+        else if ( useVistaComboBox &&
+                  (m_iFlags & wxCC_IFLAG_BUTTON_OUTSIDE) )
+        {
+            // We'll do this, because DrawThemeParentBackground
+            // doesn't seem to be reliable on Vista.
+            drawButFlags |= Button_PaintBackground;
+        }
+    }
+    else
+#endif
+    {
+        // Windows 2000 and earlier
+        drawButFlags = Button_PaintBackground;
+
+        dc.SetBrush(bgCol);
+        dc.SetPen(bgCol);
+        dc.DrawRectangle(borderRect);
     }
 
-    // Standard button rendering
-    DrawButton(dc,rectb,drawButBg);
+    // Button rendering (may only do the bitmap on button, depending on the flags)
+    DrawButton( dc, rectButton, drawButFlags );
 
-    // paint required portion on the control
+    // Paint required portion of the custom image on the control
     if ( (!m_text || m_widthCustomPaint) )
     {
         wxASSERT( m_widthCustomPaint >= 0 );
@@ -476,15 +630,15 @@ void wxComboCtrl::OnPaintEvent( wxPaintEvent& WXUNUSED(event) )
         // this is intentionally here to allow drawed rectangle's
         // right edge to be hidden
         if ( m_text )
-            rect.width = m_widthCustomPaint;
+            rectTextField.width = m_widthCustomPaint;
 
         dc.SetFont( GetFont() );
 
-        dc.SetClippingRegion(rect);
+        dc.SetClippingRegion(rectTextField);
         if ( m_popupInterface )
-            m_popupInterface->PaintComboControl(dc,rect);
+            m_popupInterface->PaintComboControl(dc,rectTextField);
         else
-            wxComboPopup::DefaultPaintComboControl(this,dc,rect);
+            wxComboPopup::DefaultPaintComboControl(this,dc,rectTextField);
     }
 }
 
@@ -530,7 +684,7 @@ void wxComboCtrl::OnMouseEvent( wxMouseEvent& event )
 
 }
 
-#if !defined(__WXWINCE__)
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
 static wxUint32 GetUserPreferencesMask()
 {
     static wxUint32 userPreferencesMask = 0;
@@ -539,16 +693,28 @@ static wxUint32 GetUserPreferencesMask()
     if ( valueSet )
         return userPreferencesMask;
 
-    wxRegKey key(wxRegKey::HKCU, wxT("Control Panel\\Desktop"));
-    if( key.Open(wxRegKey::Read) )
+    wxRegKey* pKey = NULL;
+    wxRegKey key1(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Control Panel"));
+    wxRegKey key2(wxRegKey::HKCU, wxT("Software\\Policies\\Microsoft\\Windows\\Control Panel"));
+    wxRegKey key3(wxRegKey::HKCU, wxT("Control Panel\\Desktop"));
+
+    if ( key1.Exists() )
+        pKey = &key1;
+    else if ( key2.Exists() )
+        pKey = &key2;
+    else if ( key3.Exists() )
+        pKey = &key3;
+
+    if ( pKey && pKey->Open(wxRegKey::Read) )
     {
         wxMemoryBuffer buf;
-        if ( key.QueryValue(wxT("UserPreferencesMask"), buf) )
+        if ( pKey->HasValue(wxT("UserPreferencesMask")) &&
+             pKey->QueryValue(wxT("UserPreferencesMask"), buf) )
         {
             if ( buf.GetDataLen() >= 4 )
             {
-                wxByte* p = (wxByte*) buf.GetData();
-                userPreferencesMask = p[3] + (p[2]<<8) + (p[1]<<16) + (p[0]<<24);
+                wxUint32* p = (wxUint32*) buf.GetData();
+                userPreferencesMask = *p;
             }
         }
     }
@@ -559,69 +725,96 @@ static wxUint32 GetUserPreferencesMask()
 }
 #endif
 
-bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+void wxComboCtrl::DoTimerEvent()
 {
-#if !defined(__WXWINCE__)
-    if ( GetUserPreferencesMask() & (1<<26) )
-    {
-        wxLongLong tStart = ::wxGetLocalTimeMillis();
+    bool stopTimer = false;
 
-        int height = rect.height;
+    wxWindow* win = GetPopupWindow();
+    wxWindow* popup = GetPopupControl()->GetControl();
 
-        wxWindow* win = GetPopupWindow();
-        wxWindow* popup = GetPopupControl()->GetControl();
-
-        const int delay = COMBOBOX_ANIMATION_DURATION;
-        const int resolution = 10;
-        int h0 = popup->GetSize().y;
-
-        win->SetSize( rect.x, rect.y, rect.width, 0 );
-        win->Show();
+    // Popup was hidden before it was fully shown?
+    if ( IsPopupWindowState(Hidden) )
+    {
+        stopTimer = true;
+    }
+    else
+    {
+        wxLongLong t = ::wxGetLocalTimeMillis();
+        const wxRect& rect = m_animRect;
 
-        for (;;)
+        int pos = (int) (t-m_animStart).GetLo();
+        if ( pos < COMBOBOX_ANIMATION_DURATION )
         {
-            wxLongLong t = ::wxGetLocalTimeMillis();
-            int pos = (int) (t-tStart).GetLo();
-            if ( pos > delay )
-                break;
-
-            int h = (((pos*256)/delay)*height)/256;
-            int y = (h0 - h);
+            int height = rect.height;
+            //int h0 = rect.height;
+            int h = (((pos*256)/COMBOBOX_ANIMATION_DURATION)*height)/256;
+            int y = (height - h);
             if ( y < 0 )
                 y = 0;
 
-            if ( flags & ShowAbove )
+            if ( m_animFlags & ShowAbove )
             {
-                win->SetSize( rect.x, rect.y + h0 - h, rect.width, h );
+                win->SetSize( rect.x, rect.y + height - h, rect.width, h );
             }
             else
             {
-                popup->Move( 0, -y );
+                // Note that apparently Move() should be called after
+                // SetSize() to reduce (or even eliminate) animation garbage
                 win->SetSize( rect.x, rect.y, rect.width, h );
+                popup->Move( 0, -y );
             }
-
-            wxMilliSleep( resolution );
-            wxYield();
-
-            // Popup was hidden before it was fully shown?
-            if ( IsPopupWindowState(Hidden) )
-                return true;
         }
+        else
+        {
+            stopTimer = true;
+        }
+    }
 
+    if ( stopTimer )
+    {
+        m_animTimer.Stop();
+        DoShowPopup( m_animRect, m_animFlags );
         popup->Move( 0, 0 );
+
+        // Do a one final refresh to clean up the rare cases of animation
+        // garbage
+        win->Refresh();
     }
-#else
-    wxUnusedVar(rect);
-    wxUnusedVar(flags);
+}
 #endif
 
+#if wxUSE_COMBOCTRL_POPUP_ANIMATION
+bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
+{
+    if ( GetUserPreferencesMask() & wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM )
+    {
+        m_animStart = ::wxGetLocalTimeMillis();
+        m_animRect = rect;
+        m_animFlags = flags;
+
+        wxWindow* win = GetPopupWindow();
+        win->SetSize( rect.x, rect.y, rect.width, 0 );
+        win->Show();
+
+        m_animTimer.SetOwner( this, wxID_ANY );
+        m_animTimer.Start( COMBOBOX_ANIMATION_RESOLUTION, wxTIMER_CONTINUOUS );
+
+        DoTimerEvent();
+
+        return false;
+    }
+
     return true;
 }
+#endif
 
 wxCoord wxComboCtrl::GetNativeTextIndent() const
 {
+#if wxUSE_UXTHEME
     if ( wxUxThemeEngine::GetIfActive() )
         return NATIVE_TEXT_INDENT_XP;
+#endif
     return NATIVE_TEXT_INDENT_CLASSIC;
 }
 
@@ -644,16 +837,12 @@ bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
 
         case WXK_DOWN:
         case WXK_UP:
-            // On XP or with writable combo in Classic, arrows don't open the
-            // popup but Alt-arrow does
-            if ( event.AltDown() ||
-                    ( !isPopupShown &&
-                      HasFlag(wxCB_READONLY) &&
-                      !wxUxThemeEngine::GetIfActive()
-                    ) )
-            {
+        case WXK_NUMPAD_DOWN:
+        case WXK_NUMPAD_UP:
+            // Arrow keys (and mouse wheel) toggle the popup in the native
+            // combo boxes
+            if ( event.AltDown() )
                 return true;
-            }
             break;
     }