X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4e9da8b79e9218bf194f70af9b5ad706967c0099..fcdd53359135f790b85728c4254b97095a56dad8:/src/msw/button.cpp diff --git a/src/msw/button.cpp b/src/msw/button.cpp index e512e506d2..ef43802277 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -36,11 +36,12 @@ #include "wx/settings.h" #include "wx/dcscreen.h" #include "wx/dcclient.h" + #include "wx/toplevel.h" #endif #include "wx/stockitem.h" -#include "wx/tokenzr.h" #include "wx/msw/private.h" +#include "wx/msw/private/button.h" #if wxUSE_UXTHEME #include "wx/msw/uxtheme.h" @@ -71,6 +72,10 @@ #define ODS_NOFOCUSRECT 0x0200 #endif +#ifndef DT_HIDEPREFIX + #define DT_HIDEPREFIX 0x00100000 +#endif + // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- @@ -136,14 +141,69 @@ wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , La IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl) #endif -// this macro tries to adjust the default button height to a reasonable value -// using the char height as the base -#define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10) - // ============================================================================ // implementation // ============================================================================ +// ---------------------------------------------------------------------------- +// helper functions from wx/msw/private/button.h +// ---------------------------------------------------------------------------- + +void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label) +{ + // update BS_MULTILINE style depending on the new label (resetting it + // doesn't seem to do anything very useful but it shouldn't hurt and we do + // have to set it whenever the label becomes multi line as otherwise it + // wouldn't be shown correctly as we don't use BS_MULTILINE when creating + // 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 ) + styleNew = styleOld | BS_MULTILINE; + else + styleNew = styleOld & ~BS_MULTILINE; + + if ( styleNew != styleOld ) + ::SetWindowLong(hwnd, GWL_STYLE, styleNew); +} + +wxSize wxMSWButton::GetFittingSize(wxWindow *win, const wxSize& sizeLabel) +{ + // FIXME: this is pure guesswork, need to retrieve the real button margins + wxSize sizeBtn = sizeLabel; + + sizeBtn.x += 3*win->GetCharWidth(); + sizeBtn.y = 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel.y)/10; + + return sizeBtn; +} + +wxSize wxMSWButton::ComputeBestSize(wxControl *btn) +{ + wxClientDC dc(btn); + + wxSize sizeBtn; + dc.GetMultiLineTextExtent(btn->GetLabelText(), &sizeBtn.x, &sizeBtn.y); + + sizeBtn = GetFittingSize(btn, sizeBtn); + + // all buttons have at least the standard size unless the user explicitly + // wants them to be of smaller size and used wxBU_EXACTFIT style when + // creating the button + if ( !btn->HasFlag(wxBU_EXACTFIT) ) + { + wxSize sizeDef = wxButton::GetDefaultSize(); + if ( sizeBtn.x < sizeDef.x ) + sizeBtn.x = sizeDef.x; + if ( sizeBtn.y < sizeDef.y ) + sizeBtn.y = sizeDef.y; + } + + btn->CacheBestSize(sizeBtn); + + return sizeBtn; +} + // ---------------------------------------------------------------------------- // creation/destruction // ---------------------------------------------------------------------------- @@ -160,16 +220,14 @@ bool wxButton::Create(wxWindow *parent, wxString label(lbl); if (label.empty() && wxIsStockID(id)) { - // On Windows, some buttons aren't supposed to have - // mnemonics, so strip them out. - - label = wxGetStockLabel(id -#if defined(__WXMSW__) || defined(__WXWINCE__) - , ( id != wxID_OK && - id != wxID_CANCEL && - id != wxID_CLOSE ) -#endif - ); + // On Windows, some buttons aren't supposed to have mnemonics + label = wxGetStockLabel + ( + id, + id == wxID_OK || id == wxID_CANCEL || id == wxID_CLOSE + ? wxSTOCK_NOFLAGS + : wxSTOCK_WITH_MNEMONIC + ); } if ( !CreateControl(parent, id, pos, size, style, validator, name) ) @@ -178,25 +236,24 @@ bool wxButton::Create(wxWindow *parent, WXDWORD exstyle; WXDWORD msStyle = MSWGetStyle(style, &exstyle); -#ifdef __WIN32__ // if the label contains several lines we must explicitly tell the button // about it or it wouldn't draw it correctly ("\n"s would just appear as // black boxes) // // NB: we do it here and not in MSWGetStyle() because we need the label - // value and m_label is not set yet when MSWGetStyle() is called; - // besides changing BS_MULTILINE during run-time is pointless anyhow - if ( label.find(_T('\n')) != wxString::npos ) - { - msStyle |= BS_MULTILINE; - } -#endif // __WIN32__ + // 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); } wxButton::~wxButton() { + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw && tlw->GetTmpDefaultItem() == this ) + { + UnsetTmpDefault(); + } } // ---------------------------------------------------------------------------- @@ -216,7 +273,6 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const // the bottom msStyle |= WS_CLIPSIBLINGS; -#ifdef __WIN32__ // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT // and wxBU_RIGHT to get BS_CENTER! if ( style & wxBU_LEFT ) @@ -232,45 +288,24 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const if ( style & wxNO_BORDER ) msStyle |= BS_FLAT; #endif // __WXWINCE__ -#endif // __WIN32__ return msStyle; } +void wxButton::SetLabel(const wxString& label) +{ + wxMSWButton::UpdateMultilineStyle(GetHwnd(), label); + + wxButtonBase::SetLabel(label); +} + // ---------------------------------------------------------------------------- // size management including autosizing // ---------------------------------------------------------------------------- wxSize wxButton::DoGetBestSize() const { - wxClientDC dc(wx_const_cast(wxButton *, this)); - dc.SetFont(GetFont()); - - wxCoord wBtn, - hBtn; - dc.GetMultiLineTextExtent(wxStripMenuCodes(GetLabel()), &wBtn, &hBtn); - - // add a margin -- the button is wider than just its label - wBtn += 3*GetCharWidth(); - hBtn = BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hBtn); - - // all buttons have at least the standard size unless the user explicitly - // wants them to be of smaller size and used wxBU_EXACTFIT style when - // creating the button - if ( !HasFlag(wxBU_EXACTFIT) ) - { - wxSize sz = GetDefaultSize(); - if (wBtn > sz.x) - sz.x = wBtn; - if (hBtn > sz.y) - sz.y = hBtn; - - return sz; - } - - wxSize best(wBtn, hBtn); - CacheBestSize(best); - return best; + return wxMSWButton::ComputeBestSize(const_cast(this)); } /* static */ @@ -337,29 +372,55 @@ wxSize wxButtonBase::GetDefaultSize() */ // set this button as the (permanently) default one in its panel -void wxButton::SetDefault() +wxWindow *wxButton::SetDefault() { - wxWindow *parent = GetParent(); - - wxCHECK_RET( parent, _T("button without parent?") ); - // set this one as the default button both for wxWidgets ... - wxWindow *winOldDefault = parent->SetDefaultItem(this); + wxWindow *winOldDefault = wxButtonBase::SetDefault(); // ... and Windows SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false); SetDefaultStyle(this, true); + + return winOldDefault; +} + +// return the top level parent window if it's not being deleted yet, otherwise +// return NULL +static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win) +{ + for ( ;; ) + { + // IsTopLevel() will return false for a wxTLW being deleted, so we also + // need the parent test for this case + wxWindow * const parent = win->GetParent(); + if ( !parent || win->IsTopLevel() ) + { + if ( win->IsBeingDeleted() ) + return NULL; + + break; + } + + win = parent; + } + + wxASSERT_MSG( win, _T("button without top level parent?") ); + + wxTopLevelWindow * const tlw = wxDynamicCast(win, wxTopLevelWindow); + wxASSERT_MSG( tlw, _T("logic error in GetTLWParentIfNotBeingDeleted()") ); + + return tlw; } // set this button as being currently default void wxButton::SetTmpDefault() { - wxWindow *parent = GetParent(); - - wxCHECK_RET( parent, _T("button without parent?") ); + wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent()); + if ( !tlw ) + return; - wxWindow *winOldDefault = parent->GetDefaultItem(); - parent->SetTmpDefaultItem(this); + wxWindow *winOldDefault = tlw->GetDefaultItem(); + tlw->SetTmpDefaultItem(this); SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false); SetDefaultStyle(this, true); @@ -368,13 +429,13 @@ void wxButton::SetTmpDefault() // unset this button as currently default, it may still stay permanent default void wxButton::UnsetTmpDefault() { - wxWindow *parent = GetParent(); - - wxCHECK_RET( parent, _T("button without parent?") ); + wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent()); + if ( !tlw ) + return; - parent->SetTmpDefaultItem(NULL); + tlw->SetTmpDefaultItem(NULL); - wxWindow *winOldDefault = parent->GetDefaultItem(); + wxWindow *winOldDefault = tlw->GetDefaultItem(); SetDefaultStyle(this, false); SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true); @@ -397,18 +458,13 @@ wxButton::SetDefaultStyle(wxButton *btn, bool on) if ( !wxTheApp->IsActive() ) return; - // look for a panel-like window - wxWindow *win = btn->GetParent(); - while ( win && !win->HasFlag(wxTAB_TRAVERSAL) ) - win = win->GetParent(); + wxWindow * const tlw = wxGetTopLevelParent(btn); + wxCHECK_RET( tlw, _T("button without top level window?") ); - if ( win ) - { - ::SendMessage(GetHwndOf(win), DM_SETDEFID, btn->GetId(), 0L); + ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L); - // sending DM_SETDEFID also changes the button style to - // BS_DEFPUSHBUTTON so there is nothing more to do - } + // sending DM_SETDEFID also changes the button style to + // BS_DEFPUSHBUTTON so there is nothing more to do } // then also change the style as needed @@ -533,18 +589,20 @@ WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) // owner-drawn buttons support // ---------------------------------------------------------------------------- -#ifdef __WIN32__ - // drawing helpers static void DrawButtonText(HDC hdc, RECT *pRect, const wxString& text, - COLORREF col) + COLORREF col, + int flags) { COLORREF colOld = SetTextColor(hdc, col); int modeOld = SetBkMode(hdc, TRANSPARENT); + // center text horizontally in any case + flags |= DT_CENTER; + if ( text.find(_T('\n')) != wxString::npos ) { // draw multiline label @@ -552,7 +610,8 @@ static void DrawButtonText(HDC hdc, // first we need to compute its bounding rect RECT rc; ::CopyRect(&rc, pRect); - ::DrawText(hdc, text, text.length(), &rc, DT_CENTER | DT_CALCRECT); + ::DrawText(hdc, text.wx_str(), text.length(), &rc, + DT_CENTER | DT_CALCRECT); // now center this rect inside the entire button area const LONG w = rc.right - rc.left; @@ -562,13 +621,14 @@ static void DrawButtonText(HDC hdc, rc.top = (pRect->bottom - pRect->top)/2 - h/2; rc.bottom = rc.top+h; - ::DrawText(hdc, text, text.length(), &rc, DT_CENTER); + ::DrawText(hdc, text.wx_str(), text.length(), &rc, flags); } else // single line label { - // Note: we must have DT_SINGLELINE for DT_VCENTER to work. - ::DrawText(hdc, text, text.length(), pRect, - DT_SINGLELINE | DT_CENTER | DT_VCENTER); + // centre text vertically too (notice that we must have DT_SINGLELINE + // for DT_VCENTER to work) + ::DrawText(hdc, text.wx_str(), text.length(), pRect, + flags | DT_SINGLELINE | DT_VCENTER); } SetBkMode(hdc, modeOld); @@ -817,10 +877,10 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) bool selected = (state & ODS_SELECTED) != 0; if ( !selected ) { - wxPanel *panel = wxDynamicCast(GetParent(), wxPanel); - if ( panel ) + wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); + if ( tlw ) { - selected = panel->GetDefaultItem() == this; + selected = tlw->GetDefaultItem() == this; } } bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0; @@ -847,16 +907,18 @@ bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) } } - COLORREF colFg = wxColourToRGB(GetForegroundColour()); - DrawButtonText(hdc, &rectBtn, - (state & ODS_NOACCEL ? wxStripMenuCodes(GetLabel()) - : GetLabel()), - state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT) - : colFg); + 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; } -#endif // __WIN32__ - #endif // wxUSE_BUTTON +