]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/combo.cpp
Better fix
[wxWidgets.git] / src / msw / combo.cpp
index 367f609c50038b717db6f5aa0a705cb4fb422a86..72b053da4516681b7489e75fddb30a9307d054e6 100644 (file)
 #include "wx/combo.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 TEXTCTRLXADJUST_XP          1
 #define TEXTCTRLYADJUST_XP          3
 #define TEXTCTRLXADJUST_CLASSIC     1
-#define TEXTCTRLYADJUST_CLASSIC     2
+#define TEXTCTRLYADJUST_CLASSIC     3
 
 #define COMBOBOX_ANIMATION_RESOLUTION   10
 
@@ -110,17 +162,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;
@@ -137,6 +193,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;
 
@@ -161,22 +225,38 @@ wxComboCtrl::~wxComboCtrl()
 
 void wxComboCtrl::OnThemeChange()
 {
-    wxUxThemeEngine* theme = wxUxThemeEngine::GetIfActive();
+    // there doesn't seem to be any way to get the text colour using themes
+    // API: TMT_TEXTCOLOR doesn't work neither for EDIT nor COMBOBOX
+    SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
+
+#if wxUSE_UXTHEME
+    wxUxThemeEngine * const theme = wxUxThemeEngine::GetIfActive();
     if ( theme )
     {
-        wxUxThemeHandle hTheme(this, L"COMBOBOX");
-
+        // NB: use EDIT, not COMBOBOX (the latter works in XP but not Vista)
+        wxUxThemeHandle hTheme(this, L"EDIT");
         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));
+        HRESULT hr = theme->GetThemeColor
+                            (
+                                hTheme,
+                                EP_EDITTEXT,
+                                ETS_NORMAL,
+                                TMT_FILLCOLOR,
+                                &col
+                            );
+        if ( SUCCEEDED(hr) )
+        {
+            SetBackgroundColour(wxRGBToColour(col));
+
+            // skip the call below
+            return;
+        }
+
+        wxLogApiError(_T("GetThemeColor(EDIT, ETS_NORMAL, TMT_FILLCOLOR)"), hr);
     }
+#endif
+
+    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
 }
 
 void wxComboCtrl::OnResize()
@@ -187,12 +267,14 @@ void wxComboCtrl::OnResize()
     int textCtrlXAdjust;
     int textCtrlYAdjust;
 
+#if wxUSE_UXTHEME
     if ( wxUxThemeEngine::GetIfActive() )
     {
         textCtrlXAdjust = TEXTCTRLXADJUST_XP;
         textCtrlYAdjust = TEXTCTRLYADJUST_XP;
     }
     else
+#endif
     {
         textCtrlXAdjust = TEXTCTRLXADJUST_CLASSIC;
         textCtrlYAdjust = TEXTCTRLYADJUST_CLASSIC;
@@ -251,12 +333,13 @@ static void wxMSWDrawFocusRect( wxDC& dc, const wxRect& rect )
 void
 wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) 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;
@@ -266,8 +349,9 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
     {
         // 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 )
         {
@@ -276,6 +360,7 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
             focusSpacingY = sz.y > (GetCharHeight()+2) && isEnabled ? 2 : 1;
         }
         else
+#endif
         {
             // Classic Theme
             if ( isEnabled )
@@ -294,7 +379,7 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
     {
         // 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;
@@ -318,70 +403,61 @@ wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) const
     //    theme = wxUxThemeEngine::GetIfActive();
 
     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
+                doDrawSelRect = false;
+                doDrawDottedEdge = true;
             }
             else
-        #endif
             {
                 dc.SetTextForeground( 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();
-            //}
+            dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
+            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);
-        //}
+        dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT) );
+        bgCol = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
     }
 
     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.
@@ -397,84 +473,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;
+            }
+
+            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;
 
-            dc.SetBrush( *wxTRANSPARENT_BRUSH );
-            dc.DrawRectangle(rect2);
+                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 );
@@ -482,15 +667,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);
     }
 }
 
@@ -657,8 +842,10 @@ bool wxComboCtrl::AnimateShow( const wxRect& rect, int flags )
 
 wxCoord wxComboCtrl::GetNativeTextIndent() const
 {
+#if wxUSE_UXTHEME
     if ( wxUxThemeEngine::GetIfActive() )
         return NATIVE_TEXT_INDENT_XP;
+#endif
     return NATIVE_TEXT_INDENT_CLASSIC;
 }
 
@@ -685,8 +872,10 @@ bool wxComboCtrl::IsKeyPopupToggle(const wxKeyEvent& event) const
             // popup but Alt-arrow does
             if ( event.AltDown() ||
                     ( !isPopupShown &&
-                      HasFlag(wxCB_READONLY) &&
-                      !wxUxThemeEngine::GetIfActive()
+                      HasFlag(wxCB_READONLY)
+#if wxUSE_UXTHEME
+                      && !wxUxThemeEngine::GetIfActive()
+#endif
                     ) )
             {
                 return true;