1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/button.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/button.h"
35 #include "wx/bmpbuttn.h"
36 #include "wx/settings.h"
37 #include "wx/dcscreen.h"
38 #include "wx/dcclient.h"
39 #include "wx/toplevel.h"
42 #include "wx/stockitem.h"
43 #include "wx/msw/private.h"
44 #include "wx/msw/private/button.h"
47 #include "wx/msw/uxtheme.h"
49 // no need to include tmschema.h
51 #define BP_PUSHBUTTON 1
56 #define PBS_DISABLED 4
57 #define PBS_DEFAULTED 5
59 #define TMT_CONTENTMARGINS 3602
61 #endif // wxUSE_UXTHEME
63 #ifndef WM_THEMECHANGED
64 #define WM_THEMECHANGED 0x031A
68 #define ODS_NOACCEL 0x0100
71 #ifndef ODS_NOFOCUSRECT
72 #define ODS_NOFOCUSRECT 0x0200
75 // ----------------------------------------------------------------------------
77 // ----------------------------------------------------------------------------
79 #if wxUSE_EXTENDED_RTTI
81 WX_DEFINE_FLAGS( wxButtonStyle
)
83 wxBEGIN_FLAGS( wxButtonStyle
)
84 // new style border flags, we put them first to
85 // use them for streaming out
86 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
87 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
88 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
89 wxFLAGS_MEMBER(wxBORDER_RAISED
)
90 wxFLAGS_MEMBER(wxBORDER_STATIC
)
91 wxFLAGS_MEMBER(wxBORDER_NONE
)
93 // old style border flags
94 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
95 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
96 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
97 wxFLAGS_MEMBER(wxRAISED_BORDER
)
98 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
99 wxFLAGS_MEMBER(wxBORDER
)
101 // standard window styles
102 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
103 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
104 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
105 wxFLAGS_MEMBER(wxWANTS_CHARS
)
106 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
107 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
108 wxFLAGS_MEMBER(wxVSCROLL
)
109 wxFLAGS_MEMBER(wxHSCROLL
)
111 wxFLAGS_MEMBER(wxBU_LEFT
)
112 wxFLAGS_MEMBER(wxBU_RIGHT
)
113 wxFLAGS_MEMBER(wxBU_TOP
)
114 wxFLAGS_MEMBER(wxBU_BOTTOM
)
115 wxFLAGS_MEMBER(wxBU_EXACTFIT
)
116 wxEND_FLAGS( wxButtonStyle
)
118 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton
, wxControl
,"wx/button.h")
120 wxBEGIN_PROPERTIES_TABLE(wxButton
)
121 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEvent
)
123 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
124 wxPROPERTY( Label
, wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
126 wxPROPERTY_FLAGS( WindowStyle
, wxButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
128 wxEND_PROPERTIES_TABLE()
130 wxBEGIN_HANDLERS_TABLE(wxButton
)
131 wxEND_HANDLERS_TABLE()
133 wxCONSTRUCTOR_6( wxButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
137 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
)
140 // this macro tries to adjust the default button height to a reasonable value
141 // using the char height as the base
142 #define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10)
144 // ============================================================================
146 // ============================================================================
148 // ----------------------------------------------------------------------------
149 // creation/destruction
150 // ----------------------------------------------------------------------------
152 bool wxButton::Create(wxWindow
*parent
,
158 const wxValidator
& validator
,
159 const wxString
& name
)
162 if (label
.empty() && wxIsStockID(id
))
164 // On Windows, some buttons aren't supposed to have mnemonics
165 label
= wxGetStockLabel
168 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
170 : wxSTOCK_WITH_MNEMONIC
174 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
178 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
180 // if the label contains several lines we must explicitly tell the button
181 // about it or it wouldn't draw it correctly ("\n"s would just appear as
184 // NB: we do it here and not in MSWGetStyle() because we need the label
185 // value and the label is not set yet when MSWGetStyle() is called
186 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
188 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
191 wxButton::~wxButton()
193 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
194 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
200 // ----------------------------------------------------------------------------
202 // ----------------------------------------------------------------------------
204 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
206 // buttons never have an external border, they draw their own one
207 WXDWORD msStyle
= wxControl::MSWGetStyle
209 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
212 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
213 // each other in any resizeable dialog which has more than one button in
215 msStyle
|= WS_CLIPSIBLINGS
;
217 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
218 // and wxBU_RIGHT to get BS_CENTER!
219 if ( style
& wxBU_LEFT
)
221 if ( style
& wxBU_RIGHT
)
223 if ( style
& wxBU_TOP
)
225 if ( style
& wxBU_BOTTOM
)
226 msStyle
|= BS_BOTTOM
;
229 if ( style
& wxNO_BORDER
)
231 #endif // __WXWINCE__
236 void wxButton::SetLabel(const wxString
& label
)
238 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
240 wxButtonBase::SetLabel(label
);
243 // ----------------------------------------------------------------------------
244 // size management including autosizing
245 // ----------------------------------------------------------------------------
247 wxSize
wxButton::DoGetBestSize() const
249 wxClientDC
dc(wx_const_cast(wxButton
*, this));
250 dc
.SetFont(GetFont());
254 dc
.GetMultiLineTextExtent(GetLabelText(), &wBtn
, &hBtn
);
256 // add a margin -- the button is wider than just its label
257 wBtn
+= 3*GetCharWidth();
258 hBtn
= BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hBtn
);
260 // all buttons have at least the standard size unless the user explicitly
261 // wants them to be of smaller size and used wxBU_EXACTFIT style when
262 // creating the button
263 if ( !HasFlag(wxBU_EXACTFIT
) )
265 wxSize sz
= GetDefaultSize();
274 wxSize
best(wBtn
, hBtn
);
280 wxSize
wxButtonBase::GetDefaultSize()
282 static wxSize s_sizeBtn
;
284 if ( s_sizeBtn
.x
== 0 )
287 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
289 // the size of a standard button in the dialog units is 50x14,
290 // translate this to pixels
291 // NB1: the multipliers come from the Windows convention
292 // NB2: the extra +1/+2 were needed to get the size be the same as the
293 // size of the buttons in the standard dialog - I don't know how
294 // this happens, but on my system this size is 75x23 in pixels and
295 // 23*8 isn't even divisible by 14... Would be nice to understand
296 // why these constants are needed though!
297 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
298 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
304 // ----------------------------------------------------------------------------
305 // default button handling
306 // ----------------------------------------------------------------------------
309 "Everything you ever wanted to know about the default buttons" or "Why do we
310 have to do all this?"
312 In MSW the default button should be activated when the user presses Enter
313 and the current control doesn't process Enter itself somehow. This is
314 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
315 Another aspect of "defaultness" is that the default button has different
316 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
317 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
318 be unset if our parent window is not active so it should be unset whenever
319 we lose activation and set back when we regain it.
321 Final complication is that when a button is active, it should be the default
322 one, i.e. pressing Enter on a button always activates it and not another
325 We handle this by maintaining a permanent and a temporary default items in
326 wxControlContainer (both may be NULL). When a button becomes the current
327 control (i.e. gets focus) it sets itself as the temporary default which
328 ensures that it has the right appearance and that Enter will be redirected
329 to it. When the button loses focus, it unsets the temporary default and so
330 the default item will be the permanent default -- that is the default button
331 if any had been set or none otherwise, which is just what we want.
333 NB: all this is quite complicated by now and the worst is that normally
334 it shouldn't be necessary at all as for the normal Windows programs
335 DefWindowProc() and IsDialogMessage() take care of all this
336 automatically -- however in wxWidgets programs this doesn't work for
337 nested hierarchies (i.e. a notebook inside a notebook) for unknown
338 reason and so we have to reproduce all this code ourselves. It would be
339 very nice if we could avoid doing it.
342 // set this button as the (permanently) default one in its panel
343 wxWindow
*wxButton::SetDefault()
345 // set this one as the default button both for wxWidgets ...
346 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
349 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
350 SetDefaultStyle(this, true);
352 return winOldDefault
;
355 // return the top level parent window if it's not being deleted yet, otherwise
357 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
361 // IsTopLevel() will return false for a wxTLW being deleted, so we also
362 // need the parent test for this case
363 wxWindow
* const parent
= win
->GetParent();
364 if ( !parent
|| win
->IsTopLevel() )
366 if ( win
->IsBeingDeleted() )
375 wxASSERT_MSG( win
, _T("button without top level parent?") );
377 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
378 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
383 // set this button as being currently default
384 void wxButton::SetTmpDefault()
386 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
390 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
391 tlw
->SetTmpDefaultItem(this);
393 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
394 SetDefaultStyle(this, true);
397 // unset this button as currently default, it may still stay permanent default
398 void wxButton::UnsetTmpDefault()
400 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
404 tlw
->SetTmpDefaultItem(NULL
);
406 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
408 SetDefaultStyle(this, false);
409 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
414 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
416 // we may be called with NULL pointer -- simpler to do the check here than
417 // in the caller which does wxDynamicCast()
421 // first, let DefDlgProc() know about the new default button
424 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
425 // focus at all any more
426 if ( !wxTheApp
->IsActive() )
429 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
430 wxCHECK_RET( tlw
, _T("button without top level window?") );
432 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
434 // sending DM_SETDEFID also changes the button style to
435 // BS_DEFPUSHBUTTON so there is nothing more to do
438 // then also change the style as needed
439 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
440 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
442 // don't do it with the owner drawn buttons because it will
443 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
444 // BS_DEFPUSHBUTTON != 0)!
445 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
447 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
448 on
? style
| BS_DEFPUSHBUTTON
449 : style
& ~BS_DEFPUSHBUTTON
,
454 // redraw the button - it will notice itself that it's
455 // [not] the default one [any longer]
459 //else: already has correct style
462 // ----------------------------------------------------------------------------
464 // ----------------------------------------------------------------------------
466 bool wxButton::SendClickEvent()
468 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
469 event
.SetEventObject(this);
471 return ProcessCommand(event
);
474 void wxButton::Command(wxCommandEvent
& event
)
476 ProcessCommand(event
);
479 // ----------------------------------------------------------------------------
480 // event/message handlers
481 // ----------------------------------------------------------------------------
483 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
485 bool processed
= false;
488 // NOTE: Apparently older versions (NT 4?) of the common controls send
489 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
490 // buttons, so in order to send two EVT_BUTTON events we should
491 // catch both types. Currently (Feb 2003) up-to-date versions of
492 // win98, win2k and winXP all send two BN_CLICKED messages for
493 // all button types, so we don't catch BN_DOUBLECLICKED anymore
494 // in order to not get 3 EVT_BUTTON events. If this is a problem
495 // then we need to figure out which version of the comctl32 changed
496 // this behaviour and test for it.
498 case 1: // message came from an accelerator
499 case BN_CLICKED
: // normal buttons send this
500 processed
= SendClickEvent();
507 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
509 // when we receive focus, we want to temporarily become the default button in
510 // our parent panel so that pressing "Enter" would activate us -- and when
511 // losing it we should restore the previous default button as well
512 if ( nMsg
== WM_SETFOCUS
)
516 // let the default processing take place too
518 else if ( nMsg
== WM_KILLFOCUS
)
522 else if ( nMsg
== WM_LBUTTONDBLCLK
)
524 // emulate a click event to force an owner-drawn button to change its
525 // appearance - without this, it won't do it
526 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
528 // and continue with processing the message normally as well
531 else if ( nMsg
== WM_THEMECHANGED
)
533 // need to recalculate the best size here
534 // as the theme size might have changed
535 InvalidateBestSize();
537 else if ( wxUxThemeEngine::GetIfActive() )
539 // we need to Refresh() if mouse has entered or left window
540 // so we can update the hot tracking state
541 // must use m_mouseInWindow here instead of IsMouseInWindow()
542 // since we need to know the first time the mouse enters the window
543 // and IsMouseInWindow() would return true in this case
544 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
545 nMsg
== WM_MOUSELEAVE
)
550 #endif // wxUSE_UXTHEME
552 // let the base class do all real processing
553 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
556 // ----------------------------------------------------------------------------
557 // owner-drawn buttons support
558 // ----------------------------------------------------------------------------
562 static void DrawButtonText(HDC hdc
,
564 const wxString
& text
,
567 COLORREF colOld
= SetTextColor(hdc
, col
);
568 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
570 if ( text
.find(_T('\n')) != wxString::npos
)
572 // draw multiline label
574 // first we need to compute its bounding rect
576 ::CopyRect(&rc
, pRect
);
577 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
578 DT_CENTER
| DT_CALCRECT
);
580 // now center this rect inside the entire button area
581 const LONG w
= rc
.right
- rc
.left
;
582 const LONG h
= rc
.bottom
- rc
.top
;
583 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
584 rc
.right
= rc
.left
+w
;
585 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
586 rc
.bottom
= rc
.top
+h
;
588 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, DT_CENTER
);
590 else // single line label
592 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
593 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
594 DT_SINGLELINE
| DT_CENTER
| DT_VCENTER
);
597 SetBkMode(hdc
, modeOld
);
598 SetTextColor(hdc
, colOld
);
601 static void DrawRect(HDC hdc
, const RECT
& r
)
603 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
604 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
605 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
606 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
609 void wxButton::MakeOwnerDrawn()
611 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
612 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
615 style
|= BS_OWNERDRAW
;
616 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
620 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
622 if ( !wxControl::SetBackgroundColour(colour
) )
635 bool wxButton::SetForegroundColour(const wxColour
&colour
)
637 if ( !wxControl::SetForegroundColour(colour
) )
651 The button frame looks like this normally:
654 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
655 WH GB H = light grey (LIGHT)
656 WH GB G = dark grey (SHADOW)
657 WH GB B = black (DKSHADOW)
662 When the button is selected, the button becomes like this (the total button
663 size doesn't change):
674 When the button is pushed (while selected) it is like:
686 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
687 bool selected
, bool pushed
)
690 CopyRect(&r
, &rectBtn
);
692 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
693 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
694 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
695 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
697 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
706 (void)SelectObject(hdc
, hpenGrey
);
707 ::InflateRect(&r
, -1, -1);
717 ::InflateRect(&r
, -1, -1);
720 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
721 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
723 (void)SelectObject(hdc
, hpenWhite
);
724 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
725 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
727 (void)SelectObject(hdc
, hpenLightGr
);
728 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
729 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
731 (void)SelectObject(hdc
, hpenGrey
);
732 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
733 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
736 (void)SelectObject(hdc
, hpenOld
);
737 DeleteObject(hpenWhite
);
738 DeleteObject(hpenLightGr
);
739 DeleteObject(hpenGrey
);
740 DeleteObject(hpenBlack
);
745 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
747 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
748 HDC hdc
= lpDIS
->hDC
;
749 UINT state
= lpDIS
->itemState
;
751 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
753 wxUxThemeHandle
theme(button
, L
"BUTTON");
756 if ( state
& ODS_SELECTED
)
758 iState
= PBS_PRESSED
;
760 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
764 else if ( state
& ODS_FOCUS
)
766 iState
= PBS_DEFAULTED
;
768 else if ( state
& ODS_DISABLED
)
770 iState
= PBS_DISABLED
;
777 // draw parent background if needed
778 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
782 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
786 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
789 // calculate content area margins
791 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
792 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
794 ::CopyRect(&rectClient
, &rectBtn
);
795 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
797 // if focused and !nofocus rect
798 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
800 DrawFocusRect(hdc
, &rectClient
);
803 if ( button
->UseBgCol() )
805 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
806 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
808 // don't overwrite the focus rect
809 ::InflateRect(&rectClient
, -1, -1);
810 FillRect(hdc
, &rectClient
, hbrushBackground
);
811 ::DeleteObject(hbrushBackground
);
814 #endif // wxUSE_UXTHEME
816 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
818 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
819 HDC hdc
= lpDIS
->hDC
;
820 UINT state
= lpDIS
->itemState
;
822 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
825 if ( wxUxThemeEngine::GetIfActive() )
827 MSWDrawXPBackground(this, wxdis
);
830 #endif // wxUSE_UXTHEME
832 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
834 // first, draw the background
835 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
836 FillRect(hdc
, &rectBtn
, hbrushBackground
);
837 ::DeleteObject(hbrushBackground
);
839 // draw the border for the current state
840 bool selected
= (state
& ODS_SELECTED
) != 0;
843 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
846 selected
= tlw
->GetDefaultItem() == this;
849 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
851 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
853 // if focused and !nofocus rect
854 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
857 CopyRect(&rectFocus
, &rectBtn
);
859 // I don't know where does this constant come from, but this is how
860 // Windows draws them
861 InflateRect(&rectFocus
, -4, -4);
863 DrawFocusRect(hdc
, &rectFocus
);
868 // the label is shifted by 1 pixel to create "pushed" effect
869 OffsetRect(&rectBtn
, 1, 1);
873 COLORREF colFg
= wxColourToRGB(GetForegroundColour());
874 if ( state
& ODS_DISABLED
) colFg
= GetSysColor(COLOR_GRAYTEXT
) ;
875 wxString label
= GetLabel();
876 if ( state
& ODS_NOACCEL
) label
= GetLabelText() ;
877 DrawButtonText(hdc
, &rectBtn
, label
, colFg
);
882 #endif // wxUSE_BUTTON