]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/button.cpp
Correct the bounding box calculation in wxGTK wxDC::DrawBitmap().
[wxWidgets.git] / src / msw / button.cpp
index 70edb407a22d70eec9470ce214ee4c063ff18ffa..2f68d8b36253c3d791cdf091c10a95679a64d3f8 100644 (file)
     #include "wx/dcscreen.h"
     #include "wx/dcclient.h"
     #include "wx/toplevel.h"
     #include "wx/dcscreen.h"
     #include "wx/dcclient.h"
     #include "wx/toplevel.h"
-    #include "wx/imaglist.h"
 #endif
 
 #endif
 
+#include "wx/imaglist.h"
 #include "wx/stockitem.h"
 #include "wx/msw/private.h"
 #include "wx/msw/private/button.h"
 #include "wx/msw/private/dc.h"
 #include "wx/stockitem.h"
 #include "wx/msw/private.h"
 #include "wx/msw/private/button.h"
 #include "wx/msw/private/dc.h"
+#include "wx/private/window.h"
 
 using namespace wxMSWImpl;
 
 
 using namespace wxMSWImpl;
 
@@ -423,7 +424,7 @@ void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label)
     // the control unless it already has new lines in its label)
     long styleOld = ::GetWindowLong(hwnd, GWL_STYLE),
          styleNew;
     // the control unless it already has new lines in its label)
     long styleOld = ::GetWindowLong(hwnd, GWL_STYLE),
          styleNew;
-    if ( label.find(_T('\n')) != wxString::npos )
+    if ( label.find(wxT('\n')) != wxString::npos )
         styleNew = styleOld | BS_MULTILINE;
     else
         styleNew = styleOld & ~BS_MULTILINE;
         styleNew = styleOld | BS_MULTILINE;
     else
         styleNew = styleOld & ~BS_MULTILINE;
@@ -457,7 +458,10 @@ wxSize wxMSWButton::ComputeBestSize(wxControl *btn)
     // creating the button
     if ( !btn->HasFlag(wxBU_EXACTFIT) )
     {
     // creating the button
     if ( !btn->HasFlag(wxBU_EXACTFIT) )
     {
-        wxSize sizeDef = wxButton::GetDefaultSize();
+        // The size of a standard button in the dialog units is 50x14, use it.
+        // Note that we intentionally don't use GetDefaultSize() here, because
+        // it's inexact -- dialog units depend on this dialog's font.
+        wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
         if ( sizeBtn.x < sizeDef.x )
             sizeBtn.x = sizeDef.x;
         if ( sizeBtn.y < sizeDef.y )
         if ( sizeBtn.x < sizeDef.x )
             sizeBtn.x = sizeDef.x;
         if ( sizeBtn.y < sizeDef.y )
@@ -509,7 +513,7 @@ bool wxButton::Create(wxWindow *parent,
     //     value and the label is not set yet when MSWGetStyle() is called
     msStyle |= wxMSWButton::GetMultilineStyle(label);
 
     //     value and the label is not set yet when MSWGetStyle() is called
     msStyle |= wxMSWButton::GetMultilineStyle(label);
 
-    return MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, exstyle);
+    return MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, exstyle);
 }
 
 wxButton::~wxButton()
 }
 
 wxButton::~wxButton()
@@ -574,8 +578,10 @@ wxSize wxButton::DoGetBestSize() const
 {
     wxSize size;
 
 {
     wxSize size;
 
-    // account for the text part
-    if ( ShowsLabel() )
+    // account for the text part if we have it or if we don't have any image at
+    // all (buttons initially created with empty label should still have a non
+    // zero size)
+    if ( ShowsLabel() || !m_imageData )
     {
         size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
     }
     {
         size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
     }
@@ -601,42 +607,45 @@ wxSize wxButton::DoGetBestSize() const
         // account for the user-specified margins
         size += 2*m_imageData->GetBitmapMargins();
 
         // account for the user-specified margins
         size += 2*m_imageData->GetBitmapMargins();
 
-        // and also for the margins we always add internally
-        int marginH = 0,
-            marginV = 0;
-#if wxUSE_UXTHEME
-        if ( wxUxThemeEngine::GetIfActive() )
+        // and also for the margins we always add internally (unless we have no
+        // border at all in which case the button has exactly the same size as
+        // bitmap and so no margins should be used)
+        if ( !HasFlag(wxBORDER_NONE) )
         {
         {
-            wxUxThemeHandle theme(const_cast<wxButton *>(this), L"BUTTON");
+            int marginH = 0,
+                marginV = 0;
+#if wxUSE_UXTHEME
+            if ( wxUxThemeEngine::GetIfActive() )
+            {
+                wxUxThemeHandle theme(const_cast<wxButton *>(this), L"BUTTON");
 
 
-            MARGINS margins;
-            wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
-                                                    BP_PUSHBUTTON, PBS_NORMAL,
-                                                    TMT_CONTENTMARGINS, NULL,
-                                                    &margins);
+                MARGINS margins;
+                wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
+                                                        BP_PUSHBUTTON,
+                                                        PBS_NORMAL,
+                                                        TMT_CONTENTMARGINS,
+                                                        NULL,
+                                                        &margins);
 
 
-            // XP doesn't draw themed buttons correctly when the client area is
-            // smaller than 8x8 - enforce this minimum size for small bitmaps
-            size.IncTo(wxSize(8, 8));
+                // XP doesn't draw themed buttons correctly when the client
+                // area is smaller than 8x8 - enforce this minimum size for
+                // small bitmaps
+                size.IncTo(wxSize(8, 8));
 
 
-            // don't add margins for the borderless buttons, they don't need
-            // them and it just makes them appear larger than needed
-            if ( !HasFlag(wxBORDER_NONE) )
-            {
                 marginH = margins.cxLeftWidth + margins.cxRightWidth
                             + 2*XP_BUTTON_EXTRA_MARGIN;
                 marginV = margins.cyTopHeight + margins.cyBottomHeight
                             + 2*XP_BUTTON_EXTRA_MARGIN;
             }
                 marginH = margins.cxLeftWidth + margins.cxRightWidth
                             + 2*XP_BUTTON_EXTRA_MARGIN;
                 marginV = margins.cyTopHeight + margins.cyBottomHeight
                             + 2*XP_BUTTON_EXTRA_MARGIN;
             }
-        }
-        else
+            else
 #endif // wxUSE_UXTHEME
 #endif // wxUSE_UXTHEME
-        {
-            marginH =
-            marginV = OD_BUTTON_MARGIN;
-        }
+            {
+                marginH =
+                marginV = OD_BUTTON_MARGIN;
+            }
 
 
-        size.IncBy(marginH, marginV);
+            size.IncBy(marginH, marginV);
+        }
 
         CacheBestSize(size);
     }
 
         CacheBestSize(size);
     }
@@ -654,16 +663,20 @@ wxSize wxButtonBase::GetDefaultSize()
         wxScreenDC dc;
         dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 
         wxScreenDC dc;
         dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
 
-        // the size of a standard button in the dialog units is 50x14,
-        // translate this to pixels
-        // NB1: the multipliers come from the Windows convention
-        // NB2: the extra +1/+2 were needed to get the size be the same as the
-        //      size of the buttons in the standard dialog - I don't know how
-        //      this happens, but on my system this size is 75x23 in pixels and
-        //      23*8 isn't even divisible by 14... Would be nice to understand
-        //      why these constants are needed though!
-        s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4;
-        s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8;
+        // The size of a standard button in the dialog units is 50x14,
+        // translate this to pixels.
+        //
+        // Windows' computes dialog units using average character width over
+        // upper- and lower-case ASCII alphabet and not using the average
+        // character width metadata stored in the font; see
+        // http://support.microsoft.com/default.aspx/kb/145994 for detailed
+        // discussion.
+        //
+        // NB: wxMulDivInt32() is used, because it correctly rounds the result
+
+        const wxSize base = wxPrivate::GetAverageASCIILetterSize(dc);
+        s_sizeBtn.x = wxMulDivInt32(50, base.x, 4);
+        s_sizeBtn.y = wxMulDivInt32(14, base.y, 8);
     }
 
     return s_sizeBtn;
     }
 
     return s_sizeBtn;
@@ -674,6 +687,14 @@ wxSize wxButtonBase::GetDefaultSize()
 // ----------------------------------------------------------------------------
 
 /*
 // ----------------------------------------------------------------------------
 
 /*
+   The comment below and all this code is probably due to not using WM_NEXTDLGCTL
+   message when changing focus (but just SetFocus() which is not enough), see
+   http://blogs.msdn.com/oldnewthing/archive/2004/08/02/205624.aspx for the
+   full explanation.
+
+   TODO: Do use WM_NEXTDLGCTL and get rid of all this code.
+
+
    "Everything you ever wanted to know about the default buttons" or "Why do we
    have to do all this?"
 
    "Everything you ever wanted to know about the default buttons" or "Why do we
    have to do all this?"
 
@@ -740,10 +761,10 @@ static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win)
         win = parent;
     }
 
         win = parent;
     }
 
-    wxASSERT_MSG( win, _T("button without top level parent?") );
+    wxASSERT_MSG( win, wxT("button without top level parent?") );
 
     wxTopLevelWindow * const tlw = wxDynamicCast(win, wxTopLevelWindow);
 
     wxTopLevelWindow * const tlw = wxDynamicCast(win, wxTopLevelWindow);
-    wxASSERT_MSG( tlw, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
+    wxASSERT_MSG( tlw, wxT("logic error in GetTLWParentIfNotBeingDeleted()") );
 
     return tlw;
 }
 
     return tlw;
 }
@@ -795,7 +816,7 @@ wxButton::SetDefaultStyle(wxButton *btn, bool on)
             return;
 
         wxWindow * const tlw = wxGetTopLevelParent(btn);
             return;
 
         wxWindow * const tlw = wxGetTopLevelParent(btn);
-        wxCHECK_RET( tlw, _T("button without top level window?") );
+        wxCHECK_RET( tlw, wxT("button without top level window?") );
 
         ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L);
 
 
         ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L);
 
@@ -956,16 +977,18 @@ void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
             m_imageData = new wxODButtonImageData(this, bitmap);
             MakeOwnerDrawn();
         }
             m_imageData = new wxODButtonImageData(this, bitmap);
             MakeOwnerDrawn();
         }
-
-        // if a bitmap was assigned to the bitmap, its best size must be
-        // changed to account for it
-        InvalidateBestSize();
     }
     else
     {
         m_imageData->SetBitmap(bitmap, which);
     }
 
     }
     else
     {
         m_imageData->SetBitmap(bitmap, which);
     }
 
+    // it should be enough to only invalidate the best size when the normal
+    // bitmap changes as all bitmaps assigned to the button should be of the
+    // same size anyhow
+    if ( which == State_Normal )
+        InvalidateBestSize();
+
     Refresh();
 }
 
     Refresh();
 }
 
@@ -1027,7 +1050,7 @@ void DrawButtonText(HDC hdc,
     // center text horizontally in any case
     flags |= DT_CENTER;
 
     // center text horizontally in any case
     flags |= DT_CENTER;
 
-    if ( text.find(_T('\n')) != wxString::npos )
+    if ( text.find(wxT('\n')) != wxString::npos )
     {
         // draw multiline label
 
     {
         // draw multiline label
 
@@ -1263,48 +1286,51 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
     CopyRect(&rectBtn, &lpDIS->rcItem);
 
     // draw the button background
     CopyRect(&rectBtn, &lpDIS->rcItem);
 
     // draw the button background
-#if wxUSE_UXTHEME
-    if ( wxUxThemeEngine::GetIfActive() )
+    if ( !HasFlag(wxBORDER_NONE) )
     {
     {
-        DrawXPBackground(this, hdc, rectBtn, state);
-    }
-    else
+#if wxUSE_UXTHEME
+        if ( wxUxThemeEngine::GetIfActive() )
+        {
+            DrawXPBackground(this, hdc, rectBtn, state);
+        }
+        else
 #endif // wxUSE_UXTHEME
 #endif // wxUSE_UXTHEME
-    {
-        COLORREF colBg = wxColourToRGB(GetBackgroundColour());
+        {
+            COLORREF colBg = wxColourToRGB(GetBackgroundColour());
 
 
-        // first, draw the background
-        AutoHBRUSH hbrushBackground(colBg);
-        FillRect(hdc, &rectBtn, hbrushBackground);
+            // first, draw the background
+            AutoHBRUSH hbrushBackground(colBg);
+            FillRect(hdc, &rectBtn, hbrushBackground);
 
 
-        // draw the border for the current state
-        bool selected = (state & ODS_SELECTED) != 0;
-        if ( !selected )
-        {
-            wxTopLevelWindow *
-                tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
-            if ( tlw )
+            // draw the border for the current state
+            bool selected = (state & ODS_SELECTED) != 0;
+            if ( !selected )
             {
             {
-                selected = tlw->GetDefaultItem() == this;
+                wxTopLevelWindow *
+                    tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
+                if ( tlw )
+                {
+                    selected = tlw->GetDefaultItem() == this;
+                }
             }
             }
-        }
 
 
-        DrawButtonFrame(hdc, rectBtn, selected, pushed);
-    }
+            DrawButtonFrame(hdc, rectBtn, selected, pushed);
+        }
 
 
-    // draw the focus rectangle if we need it
-    if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
-    {
-        DrawFocusRect(hdc, &rectBtn);
+        // draw the focus rectangle if we need it
+        if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
+        {
+            DrawFocusRect(hdc, &rectBtn);
 
 #if wxUSE_UXTHEME
 
 #if wxUSE_UXTHEME
-        if ( !wxUxThemeEngine::GetIfActive() )
+            if ( !wxUxThemeEngine::GetIfActive() )
 #endif // wxUSE_UXTHEME
 #endif // wxUSE_UXTHEME
-        {
-            if ( pushed )
             {
             {
-                // the label is shifted by 1 pixel to create "pushed" effect
-                OffsetRect(&rectBtn, 1, 1);
+                if ( pushed )
+                {
+                    // the label is shifted by 1 pixel to create "pushed" effect
+                    OffsetRect(&rectBtn, 1, 1);
+                }
             }
         }
     }
             }
         }
     }