]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/button.cpp
Don't specialize std::numeric_limits<> for wxLongLong when using VC6.
[wxWidgets.git] / src / msw / button.cpp
index e08d58e3be205d021d25665a2bea92486a16f707..10bb135fb5c71fd46515a2427ccd58765483e146 100644 (file)
 #include "wx/msw/private/dc.h"
 #include "wx/private/window.h"
 
+#if wxUSE_MARKUP
+    #include "wx/generic/private/markuptext.h"
+#endif // wxUSE_MARKUP
+
 using namespace wxMSWImpl;
 
 #if wxUSE_UXTHEME
@@ -111,6 +115,10 @@ using namespace wxMSWImpl;
     #define BCM_SETSHIELD       0x160c
 #endif
 
+#if wxUSE_UXTHEME
+extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp
+#endif // wxUSE_UXTHEME
+
 // ----------------------------------------------------------------------------
 // button image data
 // ----------------------------------------------------------------------------
@@ -149,6 +157,7 @@ public:
     wxODButtonImageData(wxButton *btn, const wxBitmap& bitmap)
     {
         SetBitmap(bitmap, wxButton::State_Normal);
+        SetBitmap(bitmap.ConvertToDisabled(), wxButton::State_Disabled);
 
         m_dir = wxLEFT;
 
@@ -217,10 +226,11 @@ public:
                 wxButton::State_Max),
           m_hwndBtn(GetHwndOf(btn))
     {
-        // initialize all bitmaps to normal state
+        // initialize all bitmaps except for the disabled one to normal state
         for ( int n = 0; n < wxButton::State_Max; n++ )
         {
-            m_iml.Add(bitmap);
+            m_iml.Add(n == wxButton::State_Disabled ? bitmap.ConvertToDisabled()
+                                                    : bitmap);
         }
 
         m_data.himl = GetHimagelistOf(&m_iml);
@@ -388,6 +398,7 @@ wxSize wxMSWButton::GetFittingSize(wxWindow *win,
     wxSize sizeBtn = sizeLabel;
 
     sizeBtn.x += 3*win->GetCharWidth();
+    sizeBtn.y += win->GetCharHeight()/2;
 
     // account for the shield UAC icon if we have it
     if ( flags & Size_AuthNeeded )
@@ -450,8 +461,6 @@ bool wxButton::Create(wxWindow *parent,
                       const wxValidator& validator,
                       const wxString& name)
 {
-    m_authNeeded = false;
-
     wxString label(lbl);
     if (label.empty() && wxIsStockID(id))
     {
@@ -491,6 +500,9 @@ wxButton::~wxButton()
     }
 
     delete m_imageData;
+#if wxUSE_MARKUP
+    delete m_markupText;
+#endif // wxUSE_MARKUP
 }
 
 // ----------------------------------------------------------------------------
@@ -506,7 +518,7 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
                       );
 
     // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
-    // each other in any resizeable dialog which has more than one button in
+    // each other in any resizable dialog which has more than one button in
     // the bottom
     msStyle |= WS_CLIPSIBLINGS;
 
@@ -534,6 +546,23 @@ void wxButton::SetLabel(const wxString& label)
     wxMSWButton::UpdateMultilineStyle(GetHwnd(), label);
 
     wxButtonBase::SetLabel(label);
+
+#if wxUSE_MARKUP
+    // If we have a plain text label, we shouldn't be using markup any longer.
+    if ( m_markupText )
+    {
+        delete m_markupText;
+        m_markupText = NULL;
+
+        // Unfortunately we don't really know whether we can reset the button
+        // to be non-owner-drawn or not: if we had made it owner-drawn just
+        // because of a call to SetLabelMarkup(), we could, but not if there
+        // were [also] calls to Set{Fore,Back}groundColour(). If it's really a
+        // problem to have button remain owner-drawn forever just because it
+        // had markup label once, we should record the reason for our current
+        // owner-drawnness and check it here.
+    }
+#endif // wxUSE_MARKUP
 }
 
 // ----------------------------------------------------------------------------
@@ -617,7 +646,19 @@ wxSize wxButton::DoGetBestSize() const
         if ( GetAuthNeeded() )
             flags |= wxMSWButton::Size_AuthNeeded;
 
-        size = wxMSWButton::ComputeBestFittingSize(self, flags);
+#if wxUSE_MARKUP
+        if ( m_markupText )
+        {
+            wxClientDC dc(self);
+            size = wxMSWButton::GetFittingSize(self,
+                                               m_markupText->Measure(dc),
+                                               flags);
+        }
+        else // Normal plain text (but possibly multiline) label.
+#endif // wxUSE_MARKUP
+        {
+            size = wxMSWButton::ComputeBestFittingSize(self, flags);
+        }
     }
 
     if ( m_imageData )
@@ -952,6 +993,30 @@ wxBitmap wxButton::DoGetBitmap(State which) const
 
 void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
 {
+#if wxUSE_UXTHEME
+    wxXPButtonImageData *oldData = NULL;
+#endif // wxUSE_UXTHEME
+
+    // Check if we already had bitmaps of different size.
+    if ( m_imageData &&
+          bitmap.GetSize() != m_imageData->GetBitmap(State_Normal).GetSize() )
+    {
+        wxASSERT_MSG( which == State_Normal,
+                      "Must set normal bitmap with the new size first" );
+
+#if wxUSE_UXTHEME
+        if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
+        {
+            // We can't change the size of the images stored in wxImageList
+            // in wxXPButtonImageData::m_iml so force recreating it below but
+            // keep the current data to copy its values into the new one.
+            oldData = static_cast<wxXPButtonImageData *>(m_imageData);
+            m_imageData = NULL;
+        }
+#endif // wxUSE_UXTHEME
+        //else: wxODButtonImageData doesn't require anything special
+    }
+
     // allocate the image data when the first bitmap is set
     if ( !m_imageData )
     {
@@ -963,6 +1028,20 @@ void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
         if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
         {
             m_imageData = new wxXPButtonImageData(this, bitmap);
+
+            if ( oldData )
+            {
+                // Preserve the old values in case the user changed them.
+                m_imageData->SetBitmapPosition(oldData->GetBitmapPosition());
+
+                const wxSize oldMargins = oldData->GetBitmapMargins();
+                m_imageData->SetBitmapMargins(oldMargins.x, oldMargins.y);
+
+                // No need to preserve the bitmaps though as they were of wrong
+                // size anyhow.
+
+                delete oldData;
+            }
         }
         else
 #endif // wxUSE_UXTHEME
@@ -1006,6 +1085,35 @@ void wxButton::DoSetBitmapPosition(wxDirection dir)
     InvalidateBestSize();
 }
 
+// ----------------------------------------------------------------------------
+// markup support
+// ----------------------------------------------------------------------------
+
+#if wxUSE_MARKUP
+
+bool wxButton::DoSetLabelMarkup(const wxString& markup)
+{
+    if ( !wxButtonBase::DoSetLabelMarkup(markup) )
+        return false;
+
+    if ( !m_markupText )
+    {
+        m_markupText = new wxMarkupText(markup);
+        MakeOwnerDrawn();
+    }
+    else
+    {
+        // We are already owner-drawn so just update the text.
+        m_markupText->SetMarkup(markup);
+    }
+
+    Refresh();
+
+    return true;
+}
+
+#endif // wxUSE_MARKUP
+
 // ----------------------------------------------------------------------------
 // owner-drawn buttons support
 // ----------------------------------------------------------------------------
@@ -1035,20 +1143,18 @@ wxButton::State GetButtonState(wxButton *btn, UINT state)
 
 void DrawButtonText(HDC hdc,
                     RECT *pRect,
-                    const wxString& text,
-                    COLORREF col,
+                    wxButton *btn,
                     int flags)
 {
-    wxTextColoursChanger changeFg(hdc, col, CLR_INVALID);
-    wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
-
-    // center text horizontally in any case
-    flags |= DT_CENTER;
+    const wxString text = btn->GetLabel();
 
     if ( text.find(wxT('\n')) != wxString::npos )
     {
         // draw multiline label
 
+        // center text horizontally in any case
+        flags |= DT_CENTER;
+
         // first we need to compute its bounding rect
         RECT rc;
         ::CopyRect(&rc, pRect);
@@ -1067,10 +1173,31 @@ void DrawButtonText(HDC hdc,
     }
     else // single line label
     {
-        // centre text vertically too (notice that we must have DT_SINGLELINE
-        // for DT_VCENTER to work)
+        // translate wx button flags to alignment flags for DrawText()
+        if ( btn->HasFlag(wxBU_RIGHT) )
+        {
+            flags |= DT_RIGHT;
+        }
+        else if ( !btn->HasFlag(wxBU_LEFT) )
+        {
+            flags |= DT_CENTER;
+        }
+        //else: DT_LEFT is the default anyhow (and its value is 0 too)
+
+        if ( btn->HasFlag(wxBU_BOTTOM) )
+        {
+            flags |= DT_BOTTOM;
+        }
+        else if ( !btn->HasFlag(wxBU_TOP) )
+        {
+            flags |= DT_VCENTER;
+        }
+        //else: as above, DT_TOP is the default
+
+        // notice that we must have DT_SINGLELINE for vertical alignment flags
+        // to work
         ::DrawText(hdc, text.wx_str(), text.length(), pRect,
-                   flags | DT_SINGLELINE | DT_VCENTER);
+                   flags | DT_SINGLELINE );
     }
 }
 
@@ -1194,7 +1321,20 @@ void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
                     iState
                  ) )
     {
+        // Set this button as the one whose background is being erased: this
+        // allows our WM_ERASEBKGND handler used by DrawThemeParentBackground()
+        // to correctly align the background brush with this window instead of
+        // the parent window to which WM_ERASEBKGND is sent. Notice that this
+        // doesn't work with custom user-defined EVT_ERASE_BACKGROUND handlers
+        // as they won't be aligned but unfortunately all the attempts to fix
+        // it by shifting DC origin before calling DrawThemeParentBackground()
+        // failed to work so we at least do this, even though this is far from
+        // being the perfect solution.
+        wxWindowBeingErased = button;
+
         engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
+
+        wxWindowBeingErased = NULL;
     }
 
     // draw background
@@ -1394,11 +1534,30 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
                             ? ::GetSysColor(COLOR_GRAYTEXT)
                             : wxColourToRGB(GetForegroundColour());
 
-        // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
-        // systems but by happy coincidence ODS_NOACCEL is not used under them
-        // neither so DT_HIDEPREFIX should never be used there
-        DrawButtonText(hdc, &rectBtn, GetLabel(), colFg,
-                       state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
+        wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
+        wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
+
+#if wxUSE_MARKUP
+        if ( m_markupText )
+        {
+            wxDCTemp dc((WXHDC)hdc);
+            dc.SetTextForeground(wxColour(colFg));
+            dc.SetFont(GetFont());
+
+            m_markupText->Render(dc, wxRectFromRECT(rectBtn),
+                                 state & ODS_NOACCEL
+                                    ? wxMarkupText::Render_Default
+                                    : wxMarkupText::Render_ShowAccels);
+        }
+        else // Plain text label
+#endif // wxUSE_MARKUP
+        {
+            // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
+            // systems but by happy coincidence ODS_NOACCEL is not used under
+            // them neither so DT_HIDEPREFIX should never be used there
+            DrawButtonText(hdc, &rectBtn, this,
+                           state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
+        }
     }
 
     return true;