X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c4e1d0fc473f5213a2ea68a5eaa305f0a7164094..e28cc3ca0aa9bb1c7e9613c447ca94b13a8bc858:/src/msw/button.cpp diff --git a/src/msw/button.cpp b/src/msw/button.cpp index 01ebb8539b..e87917bd64 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -37,110 +37,31 @@ #include "wx/dcscreen.h" #include "wx/dcclient.h" #include "wx/toplevel.h" + #include "wx/msw/wrapcctl.h" + #include "wx/msw/private.h" + #include "wx/msw/missing.h" #endif +#include "wx/imaglist.h" #include "wx/stockitem.h" -#include "wx/tokenzr.h" -#include "wx/msw/private.h" - -#if wxUSE_UXTHEME - #include "wx/msw/uxtheme.h" - - // no need to include tmschema.h - #ifndef BP_PUSHBUTTON - #define BP_PUSHBUTTON 1 - - #define PBS_NORMAL 1 - #define PBS_HOT 2 - #define PBS_PRESSED 3 - #define PBS_DISABLED 4 - #define PBS_DEFAULTED 5 - - #define TMT_CONTENTMARGINS 3602 - #endif -#endif // wxUSE_UXTHEME - -#ifndef WM_THEMECHANGED - #define WM_THEMECHANGED 0x031A -#endif - -#ifndef ODS_NOACCEL - #define ODS_NOACCEL 0x0100 -#endif - -#ifndef ODS_NOFOCUSRECT - #define ODS_NOFOCUSRECT 0x0200 +#include "wx/msw/private/button.h" +#include "wx/msw/private/dc.h" +#include "wx/private/window.h" + +#if wxUSE_MARKUP + #include "wx/generic/private/markuptext.h" +#endif // wxUSE_MARKUP + +// set the value for BCM_SETSHIELD (for the UAC shield) if it's not defined in +// the header +#ifndef BCM_SETSHIELD + #define BCM_SETSHIELD 0x160c #endif // ---------------------------------------------------------------------------- // macros // ---------------------------------------------------------------------------- -#if wxUSE_EXTENDED_RTTI - -WX_DEFINE_FLAGS( wxButtonStyle ) - -wxBEGIN_FLAGS( wxButtonStyle ) - // new style border flags, we put them first to - // use them for streaming out - wxFLAGS_MEMBER(wxBORDER_SIMPLE) - wxFLAGS_MEMBER(wxBORDER_SUNKEN) - wxFLAGS_MEMBER(wxBORDER_DOUBLE) - wxFLAGS_MEMBER(wxBORDER_RAISED) - wxFLAGS_MEMBER(wxBORDER_STATIC) - wxFLAGS_MEMBER(wxBORDER_NONE) - - // old style border flags - wxFLAGS_MEMBER(wxSIMPLE_BORDER) - wxFLAGS_MEMBER(wxSUNKEN_BORDER) - wxFLAGS_MEMBER(wxDOUBLE_BORDER) - wxFLAGS_MEMBER(wxRAISED_BORDER) - wxFLAGS_MEMBER(wxSTATIC_BORDER) - wxFLAGS_MEMBER(wxBORDER) - - // standard window styles - wxFLAGS_MEMBER(wxTAB_TRAVERSAL) - wxFLAGS_MEMBER(wxCLIP_CHILDREN) - wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW) - wxFLAGS_MEMBER(wxWANTS_CHARS) - wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE) - wxFLAGS_MEMBER(wxALWAYS_SHOW_SB ) - wxFLAGS_MEMBER(wxVSCROLL) - wxFLAGS_MEMBER(wxHSCROLL) - - wxFLAGS_MEMBER(wxBU_LEFT) - wxFLAGS_MEMBER(wxBU_RIGHT) - wxFLAGS_MEMBER(wxBU_TOP) - wxFLAGS_MEMBER(wxBU_BOTTOM) - wxFLAGS_MEMBER(wxBU_EXACTFIT) -wxEND_FLAGS( wxButtonStyle ) - -IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton, wxControl,"wx/button.h") - -wxBEGIN_PROPERTIES_TABLE(wxButton) - wxEVENT_PROPERTY( Click , wxEVT_COMMAND_BUTTON_CLICKED , wxCommandEvent) - - wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) - wxPROPERTY( Label, wxString , SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) - - wxPROPERTY_FLAGS( WindowStyle , wxButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style - -wxEND_PROPERTIES_TABLE() - -wxBEGIN_HANDLERS_TABLE(wxButton) -wxEND_HANDLERS_TABLE() - -wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle ) - - -#else -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 // ============================================================================ @@ -182,14 +103,10 @@ bool wxButton::Create(wxWindow *parent, // 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; - } + // 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() @@ -214,7 +131,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; @@ -237,42 +154,6 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const return msStyle; } -// ---------------------------------------------------------------------------- -// size management including autosizing -// ---------------------------------------------------------------------------- - -wxSize wxButton::DoGetBestSize() const -{ - wxClientDC dc(wx_const_cast(wxButton *, this)); - dc.SetFont(GetFont()); - - wxCoord wBtn, - hBtn; - dc.GetMultiLineTextExtent(GetLabelText(), &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; -} - /* static */ wxSize wxButtonBase::GetDefaultSize() { @@ -283,16 +164,20 @@ wxSize wxButtonBase::GetDefaultSize() 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; @@ -303,6 +188,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?" @@ -349,25 +242,32 @@ wxWindow *wxButton::SetDefault() return winOldDefault; } -// special version of wxGetTopLevelParent() which is safe to call when the -// parent is being destroyed: wxGetTopLevelParent() would just return NULL in -// this case because wxWindow version of IsTopLevel() is used when it's called -// during window destruction instead of wxTLW one, but we want to distinguish -// between these cases +// return the top level parent window if it's not being deleted yet, otherwise +// return NULL static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win) { - for ( ; win; win = win->GetParent() ) + for ( ;; ) { - if ( win->IsBeingDeleted() ) - return NULL; + // 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; - if ( win->IsTopLevel() ) break; + } + + 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); + wxASSERT_MSG( tlw, wxT("logic error in GetTLWParentIfNotBeingDeleted()") ); - return wxDynamicCast(win, wxTopLevelWindow); + return tlw; } // set this button as being currently default @@ -417,7 +317,7 @@ wxButton::SetDefaultStyle(wxButton *btn, bool on) 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); @@ -509,364 +409,30 @@ WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { UnsetTmpDefault(); } - else if ( nMsg == WM_LBUTTONDBLCLK ) - { - // emulate a click event to force an owner-drawn button to change its - // appearance - without this, it won't do it - (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam); - - // and continue with processing the message normally as well - } -#if wxUSE_UXTHEME - else if ( nMsg == WM_THEMECHANGED ) - { - // need to recalculate the best size here - // as the theme size might have changed - InvalidateBestSize(); - } - else if ( wxUxThemeEngine::GetIfActive() ) - { - // we need to Refresh() if mouse has entered or left window - // so we can update the hot tracking state - // must use m_mouseInWindow here instead of IsMouseInWindow() - // since we need to know the first time the mouse enters the window - // and IsMouseInWindow() would return true in this case - if ( ( nMsg == WM_MOUSEMOVE && !m_mouseInWindow ) || - nMsg == WM_MOUSELEAVE ) - { - Refresh(); - } - } -#endif // wxUSE_UXTHEME // let the base class do all real processing - return wxControl::MSWWindowProc(nMsg, wParam, lParam); + return wxAnyButton::MSWWindowProc(nMsg, wParam, lParam); } // ---------------------------------------------------------------------------- -// owner-drawn buttons support +// authentication needed handling // ---------------------------------------------------------------------------- -// drawing helpers - -static void DrawButtonText(HDC hdc, - RECT *pRect, - const wxString& text, - COLORREF col) -{ - COLORREF colOld = SetTextColor(hdc, col); - int modeOld = SetBkMode(hdc, TRANSPARENT); - - if ( text.find(_T('\n')) != wxString::npos ) - { - // draw multiline label - - // first we need to compute its bounding rect - RECT rc; - ::CopyRect(&rc, pRect); - ::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; - const LONG h = rc.bottom - rc.top; - rc.left = (pRect->right - pRect->left)/2 - w/2; - rc.right = rc.left+w; - rc.top = (pRect->bottom - pRect->top)/2 - h/2; - rc.bottom = rc.top+h; - - ::DrawText(hdc, text.wx_str(), text.length(), &rc, DT_CENTER); - } - else // single line label - { - // Note: we must have DT_SINGLELINE for DT_VCENTER to work. - ::DrawText(hdc, text.wx_str(), text.length(), pRect, - DT_SINGLELINE | DT_CENTER | DT_VCENTER); - } - - SetBkMode(hdc, modeOld); - SetTextColor(hdc, colOld); -} - -static void DrawRect(HDC hdc, const RECT& r) -{ - wxDrawLine(hdc, r.left, r.top, r.right, r.top); - wxDrawLine(hdc, r.right, r.top, r.right, r.bottom); - wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom); - wxDrawLine(hdc, r.left, r.bottom, r.left, r.top); -} - -void wxButton::MakeOwnerDrawn() +bool wxButton::DoGetAuthNeeded() const { - long style = GetWindowLong(GetHwnd(), GWL_STYLE); - if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW ) - { - // make it so - style |= BS_OWNERDRAW; - SetWindowLong(GetHwnd(), GWL_STYLE, style); - } + return m_authNeeded; } -bool wxButton::SetBackgroundColour(const wxColour &colour) +void wxButton::DoSetAuthNeeded(bool show) { - if ( !wxControl::SetBackgroundColour(colour) ) + // show/hide UAC symbol on Windows Vista and later + if ( wxGetWinVersion() >= wxWinVersion_6 ) { - // nothing to do - return false; - } - - MakeOwnerDrawn(); - - Refresh(); - - return true; -} - -bool wxButton::SetForegroundColour(const wxColour &colour) -{ - if ( !wxControl::SetForegroundColour(colour) ) - { - // nothing to do - return false; - } - - MakeOwnerDrawn(); - - Refresh(); - - return true; -} - -/* - The button frame looks like this normally: - - WWWWWWWWWWWWWWWWWWB - WHHHHHHHHHHHHHHHHGB W = white (HILIGHT) - WH GB H = light grey (LIGHT) - WH GB G = dark grey (SHADOW) - WH GB B = black (DKSHADOW) - WH GB - WGGGGGGGGGGGGGGGGGB - BBBBBBBBBBBBBBBBBBB - - When the button is selected, the button becomes like this (the total button - size doesn't change): - - BBBBBBBBBBBBBBBBBBB - BWWWWWWWWWWWWWWWWBB - BWHHHHHHHHHHHHHHGBB - BWH GBB - BWH GBB - BWGGGGGGGGGGGGGGGBB - BBBBBBBBBBBBBBBBBBB - BBBBBBBBBBBBBBBBBBB - - When the button is pushed (while selected) it is like: - - BBBBBBBBBBBBBBBBBBB - BGGGGGGGGGGGGGGGGGB - BG GB - BG GB - BG GB - BG GB - BGGGGGGGGGGGGGGGGGB - BBBBBBBBBBBBBBBBBBB -*/ - -static void DrawButtonFrame(HDC hdc, const RECT& rectBtn, - bool selected, bool pushed) -{ - RECT r; - CopyRect(&r, &rectBtn); - - HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)), - hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)), - hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)), - hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT)); - - HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack); - - r.right--; - r.bottom--; - - if ( pushed ) - { - DrawRect(hdc, r); - - (void)SelectObject(hdc, hpenGrey); - ::InflateRect(&r, -1, -1); - - DrawRect(hdc, r); - } - else // !pushed - { - if ( selected ) - { - DrawRect(hdc, r); - - ::InflateRect(&r, -1, -1); - } - - wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom); - wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1); - - (void)SelectObject(hdc, hpenWhite); - wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top); - wxDrawLine(hdc, r.left, r.top, r.right, r.top); - - (void)SelectObject(hdc, hpenLightGr); - wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1); - wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1); - - (void)SelectObject(hdc, hpenGrey); - wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1); - wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top); - } - - (void)SelectObject(hdc, hpenOld); - DeleteObject(hpenWhite); - DeleteObject(hpenLightGr); - DeleteObject(hpenGrey); - DeleteObject(hpenBlack); -} - -#if wxUSE_UXTHEME -static -void MSWDrawXPBackground(wxButton *button, WXDRAWITEMSTRUCT *wxdis) -{ - LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis; - HDC hdc = lpDIS->hDC; - UINT state = lpDIS->itemState; - RECT rectBtn; - CopyRect(&rectBtn, &lpDIS->rcItem); - - wxUxThemeHandle theme(button, L"BUTTON"); - int iState; - - if ( state & ODS_SELECTED ) - { - iState = PBS_PRESSED; - } - else if ( button->HasCapture() || button->IsMouseInWindow() ) - { - iState = PBS_HOT; - } - else if ( state & ODS_FOCUS ) - { - iState = PBS_DEFAULTED; - } - else if ( state & ODS_DISABLED ) - { - iState = PBS_DISABLED; - } - else - { - iState = PBS_NORMAL; - } - - // draw parent background if needed - if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme, - BP_PUSHBUTTON, - iState) ) - { - wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn); - } - - // draw background - wxUxThemeEngine::Get()->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState, - &rectBtn, NULL); - - // calculate content area margins - MARGINS margins; - wxUxThemeEngine::Get()->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState, - TMT_CONTENTMARGINS, &rectBtn, &margins); - RECT rectClient; - ::CopyRect(&rectClient, &rectBtn); - ::InflateRect(&rectClient, -margins.cxLeftWidth, -margins.cyTopHeight); - - // if focused and !nofocus rect - if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) ) - { - DrawFocusRect(hdc, &rectClient); - } - - if ( button->UseBgCol() ) - { - COLORREF colBg = wxColourToRGB(button->GetBackgroundColour()); - HBRUSH hbrushBackground = ::CreateSolidBrush(colBg); - - // don't overwrite the focus rect - ::InflateRect(&rectClient, -1, -1); - FillRect(hdc, &rectClient, hbrushBackground); - ::DeleteObject(hbrushBackground); - } -} -#endif // wxUSE_UXTHEME - -bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) -{ - LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis; - HDC hdc = lpDIS->hDC; - UINT state = lpDIS->itemState; - RECT rectBtn; - CopyRect(&rectBtn, &lpDIS->rcItem); - -#if wxUSE_UXTHEME - if ( wxUxThemeEngine::GetIfActive() ) - { - MSWDrawXPBackground(this, wxdis); - } - else -#endif // wxUSE_UXTHEME - { - COLORREF colBg = wxColourToRGB(GetBackgroundColour()); - - // first, draw the background - HBRUSH hbrushBackground = ::CreateSolidBrush(colBg); - FillRect(hdc, &rectBtn, hbrushBackground); - ::DeleteObject(hbrushBackground); - - // draw the border for the current state - bool selected = (state & ODS_SELECTED) != 0; - if ( !selected ) - { - wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow); - if ( tlw ) - { - selected = tlw->GetDefaultItem() == this; - } - } - bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0; - - DrawButtonFrame(hdc, rectBtn, selected, pushed); - - // if focused and !nofocus rect - if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) ) - { - RECT rectFocus; - CopyRect(&rectFocus, &rectBtn); - - // I don't know where does this constant come from, but this is how - // Windows draws them - InflateRect(&rectFocus, -4, -4); - - DrawFocusRect(hdc, &rectFocus); - } - - if ( pushed ) - { - // the label is shifted by 1 pixel to create "pushed" effect - OffsetRect(&rectBtn, 1, 1); - } + m_authNeeded = show; + ::SendMessage(GetHwnd(), BCM_SETSHIELD, 0, show); + InvalidateBestSize(); } - - COLORREF colFg = wxColourToRGB(GetForegroundColour()); - if ( state & ODS_DISABLED ) colFg = GetSysColor(COLOR_GRAYTEXT) ; - wxString label = GetLabel(); - if ( state & ODS_NOACCEL ) label = GetLabelText() ; - DrawButtonText(hdc, &rectBtn, label, colFg); - - return true; } #endif // wxUSE_BUTTON +