]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/combo.cpp
do use the font in DoGetTextExtent()
[wxWidgets.git] / src / msw / combo.cpp
index e784c4a1cd87e77625bfd49d508dcc016a7627de..3ddde4f0af242728cbd0063657ef269c93ee166d 100644 (file)
@@ -44,6 +44,7 @@
 // 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
 
 #define COMBOBOX_ANIMATION_DURATION     200  // In milliseconds
 
-#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM    (1<<26)
+#define wxMSW_DESKTOP_USERPREFERENCESMASK_COMBOBOXANIM    (1<<2)
 
 
 // ============================================================================
 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()
 
 
@@ -112,9 +163,9 @@ bool wxComboCtrl::Create(wxWindow *parent,
 
     if ( !border )
     {
-        // For XP, have 1-width custom border, for older version use sunken
         if ( theme )
         {
+            // For XP, have 1-width custom border, for older version use sunken
             border = wxBORDER_NONE;
             m_widthCustomBorder = 1;
         }
@@ -135,6 +186,12 @@ bool wxComboCtrl::Create(wxWindow *parent,
                            name) )
         return false;
 
+    if ( theme )
+    {
+        if ( ::wxGetWinVersion() >= wxWinVersion_Vista )
+            m_iFlags |= wxCC_BUTTON_STAYS_DOWN |wxCC_BUTTON_COVERS_BORDER;
+    }
+
     if ( style & wxCC_STD_BUTTON )
         m_iFlags |= wxCC_POPUP_ON_MOUSE_UP;
 
@@ -147,8 +204,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;
 }
@@ -159,22 +216,36 @@ 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));
+
+    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);
     }
+
+    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
 }
 
 void wxComboCtrl::OnResize()
@@ -246,18 +317,14 @@ 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.
     wxUxThemeHandle hTheme(this, L"COMBOBOX");
-    //COLORREF cref;
 
     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;
@@ -267,7 +334,7 @@ void wxComboCtrl::PrepareBackground( wxDC& dc, const wxRect& rect, int flags ) c
     {
         // Drawing control
         isEnabled = IsEnabled();
-        isFocused = ShouldDrawFocus();
+        doDrawFocusRect = ShouldDrawFocus();
 
         // Windows-style: for smaller size control (and for disabled background) use less spacing
         if ( hTheme )
@@ -295,7 +362,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;
@@ -314,74 +381,66 @@ 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 = (wxUxThemeEngine*) NULL;
+    //if ( hTheme )
+    //    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 +456,187 @@ 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;
+    const bool isEnabled = IsEnabled();
     wxColour bgCol = GetBackgroundColour();
-    wxColour fgCol;
+
+    HDC hDc = GetHdcOf(dc);
+    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();
+
+    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 ( 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;
 
-        if ( m_widthCustomBorder )
+        bool drawFullButton = false;
+        int bgState = butState;
+        const bool isFocused = (FindFocus() == GetMainWindowOfCompositeControl()) ? true : false;
+
+        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);
+
+            // Draw the entire control as a single button?
+            if ( !isNonStdButton )
+            {
+                if ( HasFlag(wxCB_READONLY) )
+                    drawFullButton = true;
+            }
 
-            // Set border colour
-            dc.SetPen( wxRGBToColour(cref) );
+            if ( drawFullButton )
+            {
+                comboBoxPart = CP_READONLY;
+                rUseForBg = &rFull;
 
-            dc.SetBrush( *wxTRANSPARENT_BRUSH );
-            dc.DrawRectangle(rect2);
+                // 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
+    {
+        // 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 +644,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);
     }
 }
 
@@ -545,16 +707,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;
             }
         }
     }