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/tokenzr.h"
44 #include "wx/msw/private.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 m_label is not set yet when MSWGetStyle() is called;
186 // besides changing BS_MULTILINE during run-time is pointless anyhow
187 if ( label
.find(_T('\n')) != wxString::npos
)
189 msStyle
|= BS_MULTILINE
;
192 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
195 wxButton::~wxButton()
197 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
198 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
204 // ----------------------------------------------------------------------------
206 // ----------------------------------------------------------------------------
208 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
210 // buttons never have an external border, they draw their own one
211 WXDWORD msStyle
= wxControl::MSWGetStyle
213 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
216 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
217 // each other in any resizeable dialog which has more than one button in
219 msStyle
|= WS_CLIPSIBLINGS
;
221 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
222 // and wxBU_RIGHT to get BS_CENTER!
223 if ( style
& wxBU_LEFT
)
225 if ( style
& wxBU_RIGHT
)
227 if ( style
& wxBU_TOP
)
229 if ( style
& wxBU_BOTTOM
)
230 msStyle
|= BS_BOTTOM
;
233 if ( style
& wxNO_BORDER
)
235 #endif // __WXWINCE__
240 // ----------------------------------------------------------------------------
241 // size management including autosizing
242 // ----------------------------------------------------------------------------
244 wxSize
wxButton::DoGetBestSize() const
246 wxClientDC
dc(wx_const_cast(wxButton
*, this));
247 dc
.SetFont(GetFont());
251 dc
.GetMultiLineTextExtent(GetLabelText(), &wBtn
, &hBtn
);
253 // add a margin -- the button is wider than just its label
254 wBtn
+= 3*GetCharWidth();
255 hBtn
= BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hBtn
);
257 // all buttons have at least the standard size unless the user explicitly
258 // wants them to be of smaller size and used wxBU_EXACTFIT style when
259 // creating the button
260 if ( !HasFlag(wxBU_EXACTFIT
) )
262 wxSize sz
= GetDefaultSize();
271 wxSize
best(wBtn
, hBtn
);
277 wxSize
wxButtonBase::GetDefaultSize()
279 static wxSize s_sizeBtn
;
281 if ( s_sizeBtn
.x
== 0 )
284 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
286 // the size of a standard button in the dialog units is 50x14,
287 // translate this to pixels
288 // NB1: the multipliers come from the Windows convention
289 // NB2: the extra +1/+2 were needed to get the size be the same as the
290 // size of the buttons in the standard dialog - I don't know how
291 // this happens, but on my system this size is 75x23 in pixels and
292 // 23*8 isn't even divisible by 14... Would be nice to understand
293 // why these constants are needed though!
294 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
295 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
301 // ----------------------------------------------------------------------------
302 // default button handling
303 // ----------------------------------------------------------------------------
306 "Everything you ever wanted to know about the default buttons" or "Why do we
307 have to do all this?"
309 In MSW the default button should be activated when the user presses Enter
310 and the current control doesn't process Enter itself somehow. This is
311 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
312 Another aspect of "defaultness" is that the default button has different
313 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
314 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
315 be unset if our parent window is not active so it should be unset whenever
316 we lose activation and set back when we regain it.
318 Final complication is that when a button is active, it should be the default
319 one, i.e. pressing Enter on a button always activates it and not another
322 We handle this by maintaining a permanent and a temporary default items in
323 wxControlContainer (both may be NULL). When a button becomes the current
324 control (i.e. gets focus) it sets itself as the temporary default which
325 ensures that it has the right appearance and that Enter will be redirected
326 to it. When the button loses focus, it unsets the temporary default and so
327 the default item will be the permanent default -- that is the default button
328 if any had been set or none otherwise, which is just what we want.
330 NB: all this is quite complicated by now and the worst is that normally
331 it shouldn't be necessary at all as for the normal Windows programs
332 DefWindowProc() and IsDialogMessage() take care of all this
333 automatically -- however in wxWidgets programs this doesn't work for
334 nested hierarchies (i.e. a notebook inside a notebook) for unknown
335 reason and so we have to reproduce all this code ourselves. It would be
336 very nice if we could avoid doing it.
339 // set this button as the (permanently) default one in its panel
340 wxWindow
*wxButton::SetDefault()
342 // set this one as the default button both for wxWidgets ...
343 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
346 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
347 SetDefaultStyle(this, true);
349 return winOldDefault
;
352 // special version of wxGetTopLevelParent() which is safe to call when the
353 // parent is being destroyed: wxGetTopLevelParent() would just return NULL in
354 // this case because wxWindow version of IsTopLevel() is used when it's called
355 // during window destruction instead of wxTLW one, but we want to distinguish
356 // between these cases
357 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
359 for ( ; win
; win
= win
->GetParent() )
361 if ( win
->IsBeingDeleted() )
364 if ( win
->IsTopLevel() )
368 wxASSERT_MSG( win
, _T("button without top level parent?") );
370 return wxDynamicCast(win
, wxTopLevelWindow
);
373 // set this button as being currently default
374 void wxButton::SetTmpDefault()
376 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
380 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
381 tlw
->SetTmpDefaultItem(this);
383 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
384 SetDefaultStyle(this, true);
387 // unset this button as currently default, it may still stay permanent default
388 void wxButton::UnsetTmpDefault()
390 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
394 tlw
->SetTmpDefaultItem(NULL
);
396 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
398 SetDefaultStyle(this, false);
399 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
404 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
406 // we may be called with NULL pointer -- simpler to do the check here than
407 // in the caller which does wxDynamicCast()
411 // first, let DefDlgProc() know about the new default button
414 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
415 // focus at all any more
416 if ( !wxTheApp
->IsActive() )
419 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
420 wxCHECK_RET( tlw
, _T("button without top level window?") );
422 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
424 // sending DM_SETDEFID also changes the button style to
425 // BS_DEFPUSHBUTTON so there is nothing more to do
428 // then also change the style as needed
429 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
430 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
432 // don't do it with the owner drawn buttons because it will
433 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
434 // BS_DEFPUSHBUTTON != 0)!
435 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
437 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
438 on
? style
| BS_DEFPUSHBUTTON
439 : style
& ~BS_DEFPUSHBUTTON
,
444 // redraw the button - it will notice itself that it's
445 // [not] the default one [any longer]
449 //else: already has correct style
452 // ----------------------------------------------------------------------------
454 // ----------------------------------------------------------------------------
456 bool wxButton::SendClickEvent()
458 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
459 event
.SetEventObject(this);
461 return ProcessCommand(event
);
464 void wxButton::Command(wxCommandEvent
& event
)
466 ProcessCommand(event
);
469 // ----------------------------------------------------------------------------
470 // event/message handlers
471 // ----------------------------------------------------------------------------
473 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
475 bool processed
= false;
478 // NOTE: Apparently older versions (NT 4?) of the common controls send
479 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
480 // buttons, so in order to send two EVT_BUTTON events we should
481 // catch both types. Currently (Feb 2003) up-to-date versions of
482 // win98, win2k and winXP all send two BN_CLICKED messages for
483 // all button types, so we don't catch BN_DOUBLECLICKED anymore
484 // in order to not get 3 EVT_BUTTON events. If this is a problem
485 // then we need to figure out which version of the comctl32 changed
486 // this behaviour and test for it.
488 case 1: // message came from an accelerator
489 case BN_CLICKED
: // normal buttons send this
490 processed
= SendClickEvent();
497 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
499 // when we receive focus, we want to temporarily become the default button in
500 // our parent panel so that pressing "Enter" would activate us -- and when
501 // losing it we should restore the previous default button as well
502 if ( nMsg
== WM_SETFOCUS
)
506 // let the default processing take place too
508 else if ( nMsg
== WM_KILLFOCUS
)
512 else if ( nMsg
== WM_LBUTTONDBLCLK
)
514 // emulate a click event to force an owner-drawn button to change its
515 // appearance - without this, it won't do it
516 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
518 // and continue with processing the message normally as well
521 else if ( nMsg
== WM_THEMECHANGED
)
523 // need to recalculate the best size here
524 // as the theme size might have changed
525 InvalidateBestSize();
527 else if ( wxUxThemeEngine::GetIfActive() )
529 // we need to Refresh() if mouse has entered or left window
530 // so we can update the hot tracking state
531 // must use m_mouseInWindow here instead of IsMouseInWindow()
532 // since we need to know the first time the mouse enters the window
533 // and IsMouseInWindow() would return true in this case
534 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
535 nMsg
== WM_MOUSELEAVE
)
540 #endif // wxUSE_UXTHEME
542 // let the base class do all real processing
543 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
546 // ----------------------------------------------------------------------------
547 // owner-drawn buttons support
548 // ----------------------------------------------------------------------------
552 static void DrawButtonText(HDC hdc
,
554 const wxString
& text
,
557 COLORREF colOld
= SetTextColor(hdc
, col
);
558 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
560 if ( text
.find(_T('\n')) != wxString::npos
)
562 // draw multiline label
564 // first we need to compute its bounding rect
566 ::CopyRect(&rc
, pRect
);
567 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
568 DT_CENTER
| DT_CALCRECT
);
570 // now center this rect inside the entire button area
571 const LONG w
= rc
.right
- rc
.left
;
572 const LONG h
= rc
.bottom
- rc
.top
;
573 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
574 rc
.right
= rc
.left
+w
;
575 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
576 rc
.bottom
= rc
.top
+h
;
578 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, DT_CENTER
);
580 else // single line label
582 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
583 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
584 DT_SINGLELINE
| DT_CENTER
| DT_VCENTER
);
587 SetBkMode(hdc
, modeOld
);
588 SetTextColor(hdc
, colOld
);
591 static void DrawRect(HDC hdc
, const RECT
& r
)
593 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
594 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
595 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
596 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
599 void wxButton::MakeOwnerDrawn()
601 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
602 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
605 style
|= BS_OWNERDRAW
;
606 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
610 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
612 if ( !wxControl::SetBackgroundColour(colour
) )
625 bool wxButton::SetForegroundColour(const wxColour
&colour
)
627 if ( !wxControl::SetForegroundColour(colour
) )
641 The button frame looks like this normally:
644 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
645 WH GB H = light grey (LIGHT)
646 WH GB G = dark grey (SHADOW)
647 WH GB B = black (DKSHADOW)
652 When the button is selected, the button becomes like this (the total button
653 size doesn't change):
664 When the button is pushed (while selected) it is like:
676 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
677 bool selected
, bool pushed
)
680 CopyRect(&r
, &rectBtn
);
682 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
683 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
684 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
685 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
687 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
696 (void)SelectObject(hdc
, hpenGrey
);
697 ::InflateRect(&r
, -1, -1);
707 ::InflateRect(&r
, -1, -1);
710 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
711 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
713 (void)SelectObject(hdc
, hpenWhite
);
714 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
715 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
717 (void)SelectObject(hdc
, hpenLightGr
);
718 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
719 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
721 (void)SelectObject(hdc
, hpenGrey
);
722 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
723 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
726 (void)SelectObject(hdc
, hpenOld
);
727 DeleteObject(hpenWhite
);
728 DeleteObject(hpenLightGr
);
729 DeleteObject(hpenGrey
);
730 DeleteObject(hpenBlack
);
735 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
737 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
738 HDC hdc
= lpDIS
->hDC
;
739 UINT state
= lpDIS
->itemState
;
741 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
743 wxUxThemeHandle
theme(button
, L
"BUTTON");
746 if ( state
& ODS_SELECTED
)
748 iState
= PBS_PRESSED
;
750 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
754 else if ( state
& ODS_FOCUS
)
756 iState
= PBS_DEFAULTED
;
758 else if ( state
& ODS_DISABLED
)
760 iState
= PBS_DISABLED
;
767 // draw parent background if needed
768 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
772 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
776 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
779 // calculate content area margins
781 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
782 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
784 ::CopyRect(&rectClient
, &rectBtn
);
785 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
787 // if focused and !nofocus rect
788 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
790 DrawFocusRect(hdc
, &rectClient
);
793 if ( button
->UseBgCol() )
795 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
796 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
798 // don't overwrite the focus rect
799 ::InflateRect(&rectClient
, -1, -1);
800 FillRect(hdc
, &rectClient
, hbrushBackground
);
801 ::DeleteObject(hbrushBackground
);
804 #endif // wxUSE_UXTHEME
806 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
808 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
809 HDC hdc
= lpDIS
->hDC
;
810 UINT state
= lpDIS
->itemState
;
812 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
815 if ( wxUxThemeEngine::GetIfActive() )
817 MSWDrawXPBackground(this, wxdis
);
820 #endif // wxUSE_UXTHEME
822 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
824 // first, draw the background
825 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
826 FillRect(hdc
, &rectBtn
, hbrushBackground
);
827 ::DeleteObject(hbrushBackground
);
829 // draw the border for the current state
830 bool selected
= (state
& ODS_SELECTED
) != 0;
833 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
836 selected
= tlw
->GetDefaultItem() == this;
839 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
841 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
843 // if focused and !nofocus rect
844 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
847 CopyRect(&rectFocus
, &rectBtn
);
849 // I don't know where does this constant come from, but this is how
850 // Windows draws them
851 InflateRect(&rectFocus
, -4, -4);
853 DrawFocusRect(hdc
, &rectFocus
);
858 // the label is shifted by 1 pixel to create "pushed" effect
859 OffsetRect(&rectBtn
, 1, 1);
863 COLORREF colFg
= wxColourToRGB(GetForegroundColour());
864 if ( state
& ODS_DISABLED
) colFg
= GetSysColor(COLOR_GRAYTEXT
) ;
865 wxString label
= GetLabel();
866 if ( state
& ODS_NOACCEL
) label
= GetLabelText() ;
867 DrawButtonText(hdc
, &rectBtn
, label
, colFg
);
872 #endif // wxUSE_BUTTON