X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/233f10bf15c7201e8733426cffdeb6595df539ae..8a31648287be0ef976f133de2786b137f1e98340:/src/msw/button.cpp diff --git a/src/msw/button.cpp b/src/msw/button.cpp index ead9214d2c..3e9bfb5739 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -37,403 +37,39 @@ #include "wx/dcscreen.h" #include "wx/dcclient.h" #include "wx/toplevel.h" - #include "wx/imaglist.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/msw/private.h" #include "wx/msw/private/button.h" +#include "wx/msw/private/dc.h" +#include "wx/private/window.h" -#if wxUSE_UXTHEME - #include "wx/msw/uxtheme.h" +#if wxUSE_MARKUP + #include "wx/generic/private/markuptext.h" +#endif // wxUSE_MARKUP - // 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 - - // provide the necessary declarations ourselves if they're missing from - // headers - #ifndef BCM_SETIMAGELIST - #define BCM_SETIMAGELIST 0x1602 - #define BCM_SETTEXTMARGIN 0x1604 - - enum - { - BUTTON_IMAGELIST_ALIGN_LEFT, - BUTTON_IMAGELIST_ALIGN_RIGHT, - BUTTON_IMAGELIST_ALIGN_TOP, - BUTTON_IMAGELIST_ALIGN_BOTTOM - }; - - struct BUTTON_IMAGELIST - { - HIMAGELIST himl; - RECT margin; - UINT uAlign; - }; - #endif -#endif // wxUSE_UXTHEME - -#ifndef WM_THEMECHANGED - #define WM_THEMECHANGED 0x031A +// 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 -#ifndef ODS_NOACCEL - #define ODS_NOACCEL 0x0100 -#endif - -#ifndef ODS_NOFOCUSRECT - #define ODS_NOFOCUSRECT 0x0200 -#endif - -#ifndef DT_HIDEPREFIX - #define DT_HIDEPREFIX 0x00100000 -#endif - -// ---------------------------------------------------------------------------- -// button image data -// ---------------------------------------------------------------------------- - -// we use different data classes for owner drawn buttons and for themed XP ones - -class wxButtonImageData -{ -public: - wxButtonImageData() { } - - virtual wxBitmap GetBitmap(wxButton::State which) const = 0; - virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) = 0; - - virtual wxSize GetBitmapMargins() const = 0; - virtual void SetBitmapMargins(wxCoord x, wxCoord y) = 0; - - virtual bool IsHorizontal() const = 0; - virtual void SetBitmapPosition(wxDirection dir) = 0; - -private: - wxDECLARE_NO_COPY_CLASS(wxButtonImageData); -}; - -namespace -{ - -class wxODButtonImageData : public wxButtonImageData -{ -public: - wxODButtonImageData() { m_dir = wxLEFT; } - - virtual wxBitmap GetBitmap(wxButton::State which) const - { - return m_bitmaps[which]; - } - - virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) - { - m_bitmaps[which] = bitmap; - } - - virtual wxSize GetBitmapMargins() const - { - return m_margin; - } - - virtual void SetBitmapMargins(wxCoord x, wxCoord y) - { - m_margin = wxSize(x, y); - } - - virtual bool IsHorizontal() const - { - return m_dir == wxLEFT || m_dir == wxRIGHT; - } - - virtual void SetBitmapPosition(wxDirection dir) - { - m_dir = dir; - } - -private: - // just store the values passed to us to be able to retrieve them later - // from the drawing code - wxBitmap m_bitmaps[wxButton::State_Max]; - wxSize m_margin; - wxDirection m_dir; - - wxDECLARE_NO_COPY_CLASS(wxODButtonImageData); -}; - -#if wxUSE_UXTHEME - -class wxXPButtonImageData : public wxButtonImageData -{ -public: - // we must be constructed with the size of our images as we need to create - // the image list - wxXPButtonImageData(wxButton *btn, const wxSize& size) - : m_iml(size.x, size.y, true /* use mask */, wxButton::State_Max), - m_hwndBtn(GetHwndOf(btn)) - { - m_data.himl = GetHimagelistOf(&m_iml); - - // use default margins - m_data.margin.left = - m_data.margin.right = btn->GetCharWidth(); - m_data.margin.top = - m_data.margin.bottom = btn->GetCharHeight() / 2; - - // and default alignment - m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; - } - - virtual wxBitmap GetBitmap(wxButton::State which) const - { - return m_iml.GetBitmap(which); - } - - virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) - { - const int imagesToAdd = which - m_iml.GetImageCount(); - if ( imagesToAdd >= 0 ) - { - if ( imagesToAdd > 0 ) - { - const wxBitmap bmpNormal = GetBitmap(wxButton::State_Normal); - for ( int n = 0; n < imagesToAdd; n++ ) - m_iml.Add(bmpNormal); - } - - m_iml.Add(bitmap); - } - else // we already have this bitmap - { - m_iml.Replace(which, bitmap); - } - - UpdateImageInfo(); - } - - virtual wxSize GetBitmapMargins() const - { - return wxSize(m_data.margin.left, m_data.margin.top); - } - - virtual void SetBitmapMargins(wxCoord x, wxCoord y) - { - RECT& margin = m_data.margin; - margin.left = - margin.right = x; - margin.top = - margin.bottom = y; - - if ( !::SendMessage(m_hwndBtn, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin) ) - { - wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed"); - } - } - - virtual bool IsHorizontal() const - { - return m_data.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || - m_data.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT; - } - - virtual void SetBitmapPosition(wxDirection dir) - { - UINT alignNew; - switch ( dir ) - { - default: - wxFAIL_MSG( "invalid direction" ); - // fall through - - case wxLEFT: - alignNew = BUTTON_IMAGELIST_ALIGN_LEFT; - break; - - case wxRIGHT: - alignNew = BUTTON_IMAGELIST_ALIGN_RIGHT; - break; - - case wxTOP: - alignNew = BUTTON_IMAGELIST_ALIGN_TOP; - break; - - case wxBOTTOM: - alignNew = BUTTON_IMAGELIST_ALIGN_BOTTOM; - break; - } - - if ( alignNew != m_data.uAlign ) - { - m_data.uAlign = alignNew; - UpdateImageInfo(); - } - } - -private: - void UpdateImageInfo() - { - if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) ) - { - wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed"); - } - } - - // we store image list separately to be able to use convenient wxImageList - // methods instead of working with raw HIMAGELIST - wxImageList m_iml; - - // store the rest of the data in BCM_SETIMAGELIST-friendly form - BUTTON_IMAGELIST m_data; - - // the button we're associated with - const HWND m_hwndBtn; - - - wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData); -}; - -#endif // wxUSE_UXTHEME - -} // anonymous namespace - // ---------------------------------------------------------------------------- // 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 +BEGIN_EVENT_TABLE(wxButton, wxButtonBase) + EVT_CHAR_HOOK(wxButton::OnCharHook) +END_EVENT_TABLE() // ============================================================================ // 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 // ---------------------------------------------------------------------------- @@ -474,7 +110,7 @@ bool wxButton::Create(wxWindow *parent, // 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() @@ -484,8 +120,6 @@ wxButton::~wxButton() { UnsetTmpDefault(); } - - delete m_imageData; } // ---------------------------------------------------------------------------- @@ -501,7 +135,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; @@ -524,44 +158,6 @@ WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const return msStyle; } -void wxButton::SetLabel(const wxString& label) -{ - wxMSWButton::UpdateMultilineStyle(GetHwnd(), label); - - wxButtonBase::SetLabel(label); -} - -// ---------------------------------------------------------------------------- -// size management including autosizing -// ---------------------------------------------------------------------------- - -wxSize wxButton::DoGetBestSize() const -{ - wxSize size = wxMSWButton::ComputeBestSize(const_cast(this)); - if ( m_imageData ) - { - const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize(); - if ( m_imageData->IsHorizontal() ) - { - size.x += sizeBmp.x; - if ( sizeBmp.y > size.y ) - size.y = sizeBmp.y; - } - else // bitmap on top/below the text - { - size.y += sizeBmp.y; - if ( sizeBmp.x > size.x ) - size.x = sizeBmp.x; - } - - size += 2*m_imageData->GetBitmapMargins(); - - CacheBestSize(size); - } - - return size; -} - /* static */ wxSize wxButtonBase::GetDefaultSize() { @@ -572,16 +168,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; @@ -592,6 +192,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?" @@ -658,10 +266,10 @@ static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win) 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, _T("logic error in GetTLWParentIfNotBeingDeleted()") ); + wxASSERT_MSG( tlw, wxT("logic error in GetTLWParentIfNotBeingDeleted()") ); return tlw; } @@ -713,7 +321,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); @@ -766,6 +374,25 @@ void wxButton::Command(wxCommandEvent & event) // event/message handlers // ---------------------------------------------------------------------------- +void wxButton::OnCharHook(wxKeyEvent& event) +{ + // We want to ensure that the button always processes Enter key events + // itself, even if it's inside some control that normally takes over them + // (this happens when the button is part of an in-place editor control for + // example). + if ( event.GetKeyCode() == WXK_RETURN ) + { + // We should ensure that subsequent key events are still generated even + // if we did handle EVT_CHAR_HOOK (normally this would suppress their + // generation). + event.DoAllowNextEvent(); + } + else + { + event.Skip(); + } +} + bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id)) { bool processed = false; @@ -805,416 +432,29 @@ 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); } // ---------------------------------------------------------------------------- -// button images +// authentication needed handling // ---------------------------------------------------------------------------- -wxBitmap wxButton::DoGetBitmap(State which) const +bool wxButton::DoGetAuthNeeded() const { - return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap(); + return m_authNeeded; } -void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which) +void wxButton::DoSetAuthNeeded(bool show) { - // allocate the image data when the first bitmap is set - if ( !m_imageData ) + // show/hide UAC symbol on Windows Vista and later + if ( wxGetWinVersion() >= wxWinVersion_6 ) { -#if wxUSE_UXTHEME - if ( wxUxThemeEngine::GetIfActive() ) - m_imageData = new wxXPButtonImageData(this, bitmap.GetSize()); - else -#endif // wxUSE_UXTHEME - m_imageData = new wxODButtonImageData; - - // if a bitmap was assigned to the bitmap, its best size must be - // changed to account for it + m_authNeeded = show; + ::SendMessage(GetHwnd(), BCM_SETSHIELD, 0, show); InvalidateBestSize(); } - - m_imageData->SetBitmap(bitmap, which); -} - -void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y) -{ - wxCHECK_RET( m_imageData, "SetBitmap() must be called first" ); - - m_imageData->SetBitmapMargins(x, y); -} - -void wxButton::DoSetBitmapPosition(wxDirection dir) -{ - wxCHECK_RET( m_imageData, "SetBitmap() must be called first" ); - - m_imageData->SetBitmapPosition(dir); -} - -// ---------------------------------------------------------------------------- -// owner-drawn buttons support -// ---------------------------------------------------------------------------- - -// drawing helpers - -static void DrawButtonText(HDC hdc, - RECT *pRect, - const wxString& text, - 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 - - // 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, flags); - } - else // single line label - { - // 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); - 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() -{ - long style = GetWindowLong(GetHwnd(), GWL_STYLE); - if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW ) - { - // make it so - style |= BS_OWNERDRAW; - SetWindowLong(GetHwnd(), GWL_STYLE, style); - } -} - -bool wxButton::SetBackgroundColour(const wxColour &colour) -{ - if ( !wxControl::SetBackgroundColour(colour) ) - { - // 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); - } - } - - 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 // wxUSE_BUTTON