]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/button.cpp
Handle the case of NSNotFound result properly.
[wxWidgets.git] / src / msw / button.cpp
index b17908b014a00939136b117f7988e3e7a3fbe466..67494356d6945d474ca012e5e00d19d34b62f29c 100644 (file)
@@ -37,9 +37,9 @@
     #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/stockitem.h"
 #include "wx/msw/private.h"
 #include "wx/msw/private/button.h"
@@ -143,8 +143,13 @@ public:
 
         m_dir = wxLEFT;
 
 
         m_dir = wxLEFT;
 
-        m_margin.x = btn->GetCharWidth();
-        m_margin.y = btn->GetCharHeight() / 2;
+        // we use margins when we have both bitmap and text, but when we have
+        // only the bitmap it should take up the entire button area
+        if ( btn->ShowsLabel() )
+        {
+            m_margin.x = btn->GetCharWidth();
+            m_margin.y = btn->GetCharHeight() / 2;
+        }
     }
 
     virtual wxBitmap GetBitmap(wxButton::State which) const
     }
 
     virtual wxBitmap GetBitmap(wxButton::State which) const
@@ -159,7 +164,7 @@ public:
 
     virtual wxSize GetBitmapMargins() const
     {
 
     virtual wxSize GetBitmapMargins() const
     {
-        return m_margin + wxSize(OD_BUTTON_MARGIN, OD_BUTTON_MARGIN);
+        return m_margin;
     }
 
     virtual void SetBitmapMargins(wxCoord x, wxCoord y)
     }
 
     virtual void SetBitmapMargins(wxCoord x, wxCoord y)
@@ -189,6 +194,10 @@ private:
 
 #if wxUSE_UXTHEME
 
 
 #if wxUSE_UXTHEME
 
+// somehow the margin is one pixel greater than the value returned by
+// GetThemeMargins() call
+const int XP_BUTTON_EXTRA_MARGIN = 1;
+
 class wxXPButtonImageData : public wxButtonImageData
 {
 public:
 class wxXPButtonImageData : public wxButtonImageData
 {
 public:
@@ -215,6 +224,8 @@ public:
 
         // and default alignment
         m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
 
         // and default alignment
         m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
+
+        UpdateImageInfo();
     }
 
     virtual wxBitmap GetBitmap(wxButton::State which) const
     }
 
     virtual wxBitmap GetBitmap(wxButton::State which) const
@@ -412,7 +423,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;
@@ -498,7 +509,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()
@@ -561,9 +572,19 @@ void wxButton::SetLabel(const wxString& label)
 
 wxSize wxButton::DoGetBestSize() const
 {
 
 wxSize wxButton::DoGetBestSize() const
 {
-    wxSize size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
+    wxSize size;
+
+    // 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));
+    }
+
     if ( m_imageData )
     {
     if ( m_imageData )
     {
+        // account for the bitmap size
         const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
         const wxDirection dirBmp = m_imageData->GetBitmapPosition();
         if ( dirBmp == wxLEFT || dirBmp == wxRIGHT )
         const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
         const wxDirection dirBmp = m_imageData->GetBitmapPosition();
         if ( dirBmp == wxLEFT || dirBmp == wxRIGHT )
@@ -579,8 +600,49 @@ wxSize wxButton::DoGetBestSize() const
                 size.x = sizeBmp.x;
         }
 
                 size.x = sizeBmp.x;
         }
 
+        // account for the user-specified margins
         size += 2*m_imageData->GetBitmapMargins();
 
         size += 2*m_imageData->GetBitmapMargins();
 
+        // 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) )
+        {
+            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);
+
+                // 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));
+
+                marginH = margins.cxLeftWidth + margins.cxRightWidth
+                            + 2*XP_BUTTON_EXTRA_MARGIN;
+                marginV = margins.cyTopHeight + margins.cyBottomHeight
+                            + 2*XP_BUTTON_EXTRA_MARGIN;
+            }
+            else
+#endif // wxUSE_UXTHEME
+            {
+                marginH =
+                marginV = OD_BUTTON_MARGIN;
+            }
+
+            size.IncBy(marginH, marginV);
+        }
+
         CacheBestSize(size);
     }
 
         CacheBestSize(size);
     }
 
@@ -617,6 +679,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?"
 
@@ -683,10 +753,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;
 }
@@ -738,7 +808,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);
 
@@ -853,10 +923,13 @@ WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
                 nMsg == WM_MOUSELEAVE )
     {
         if (
                 nMsg == WM_MOUSELEAVE )
     {
         if (
+                IsEnabled() &&
+                (
 #if wxUSE_UXTHEME
                 wxUxThemeEngine::GetIfActive() ||
 #endif // wxUSE_UXTHEME
                 m_imageData && m_imageData->GetBitmap(State_Current).IsOk()
 #if wxUSE_UXTHEME
                 wxUxThemeEngine::GetIfActive() ||
 #endif // wxUSE_UXTHEME
                 m_imageData && m_imageData->GetBitmap(State_Current).IsOk()
+                )
            )
         {
             Refresh();
            )
         {
             Refresh();
@@ -882,7 +955,11 @@ void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
     if ( !m_imageData )
     {
 #if wxUSE_UXTHEME
     if ( !m_imageData )
     {
 #if wxUSE_UXTHEME
-        if ( wxUxThemeEngine::GetIfActive() )
+        // using image list doesn't work correctly if we don't have any label
+        // (even if we use BUTTON_IMAGELIST_ALIGN_CENTER alignment and
+        // BS_BITMAP style), at least under Windows 2003 so use owner drawn
+        // strategy for bitmap-only buttons
+        if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
         {
             m_imageData = new wxXPButtonImageData(this, bitmap);
         }
         {
             m_imageData = new wxXPButtonImageData(this, bitmap);
         }
@@ -892,15 +969,24 @@ 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();
+}
+
+wxSize wxButton::DoGetBitmapMargins() const
+{
+    return m_imageData ? m_imageData->GetBitmapMargins() : wxSize(0, 0);
 }
 
 void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
 }
 
 void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
@@ -956,7 +1042,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
 
@@ -1088,7 +1174,7 @@ void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
 
     // this array is indexed by wxButton::State values and so must be kept in
     // sync with it
 
     // this array is indexed by wxButton::State values and so must be kept in
     // sync with it
-    static const uxStates[] =
+    static const int uxStates[] =
     {
         PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
     };
     {
         PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
     };
@@ -1117,6 +1203,7 @@ void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
     engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
                             TMT_CONTENTMARGINS, &rectBtn, &margins);
     ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
     engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
                             TMT_CONTENTMARGINS, &rectBtn, &margins);
     ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
+    ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
 
     if ( button->UseBgCol() )
     {
 
     if ( button->UseBgCol() )
     {
@@ -1191,48 +1278,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);
+                }
             }
         }
     }
             }
         }
     }
@@ -1290,15 +1380,18 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
 
 
     // finally draw the label
 
 
     // finally draw the label
-    COLORREF colFg = state & ODS_DISABLED
-                        ? ::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);
+    if ( ShowsLabel() )
+    {
+        COLORREF colFg = state & ODS_DISABLED
+                            ? ::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);
+    }
 
     return true;
 }
 
     return true;
 }