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 // ============================================================================
142 // ============================================================================
144 // ----------------------------------------------------------------------------
145 // creation/destruction
146 // ----------------------------------------------------------------------------
148 bool wxButton::Create(wxWindow
*parent
,
154 const wxValidator
& validator
,
155 const wxString
& name
)
158 if (label
.empty() && wxIsStockID(id
))
160 // On Windows, some buttons aren't supposed to have mnemonics
161 label
= wxGetStockLabel
164 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
166 : wxSTOCK_WITH_MNEMONIC
170 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
174 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
176 // if the label contains several lines we must explicitly tell the button
177 // about it or it wouldn't draw it correctly ("\n"s would just appear as
180 // NB: we do it here and not in MSWGetStyle() because we need the label
181 // value and the label is not set yet when MSWGetStyle() is called
182 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
184 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
187 wxButton::~wxButton()
189 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
190 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
196 // ----------------------------------------------------------------------------
198 // ----------------------------------------------------------------------------
200 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
202 // buttons never have an external border, they draw their own one
203 WXDWORD msStyle
= wxControl::MSWGetStyle
205 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
208 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
209 // each other in any resizeable dialog which has more than one button in
211 msStyle
|= WS_CLIPSIBLINGS
;
213 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
214 // and wxBU_RIGHT to get BS_CENTER!
215 if ( style
& wxBU_LEFT
)
217 if ( style
& wxBU_RIGHT
)
219 if ( style
& wxBU_TOP
)
221 if ( style
& wxBU_BOTTOM
)
222 msStyle
|= BS_BOTTOM
;
225 if ( style
& wxNO_BORDER
)
227 #endif // __WXWINCE__
232 void wxButton::SetLabel(const wxString
& label
)
234 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
236 wxButtonBase::SetLabel(label
);
239 // ----------------------------------------------------------------------------
240 // size management including autosizing
241 // ----------------------------------------------------------------------------
243 wxSize
wxButton::DoGetBestSize() const
245 return wxMSWButton::ComputeBestSize(wx_const_cast(wxButton
*, this));
249 wxSize
wxButtonBase::GetDefaultSize()
251 static wxSize s_sizeBtn
;
253 if ( s_sizeBtn
.x
== 0 )
256 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
258 // the size of a standard button in the dialog units is 50x14,
259 // translate this to pixels
260 // NB1: the multipliers come from the Windows convention
261 // NB2: the extra +1/+2 were needed to get the size be the same as the
262 // size of the buttons in the standard dialog - I don't know how
263 // this happens, but on my system this size is 75x23 in pixels and
264 // 23*8 isn't even divisible by 14... Would be nice to understand
265 // why these constants are needed though!
266 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
267 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
273 // ----------------------------------------------------------------------------
274 // default button handling
275 // ----------------------------------------------------------------------------
278 "Everything you ever wanted to know about the default buttons" or "Why do we
279 have to do all this?"
281 In MSW the default button should be activated when the user presses Enter
282 and the current control doesn't process Enter itself somehow. This is
283 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
284 Another aspect of "defaultness" is that the default button has different
285 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
286 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
287 be unset if our parent window is not active so it should be unset whenever
288 we lose activation and set back when we regain it.
290 Final complication is that when a button is active, it should be the default
291 one, i.e. pressing Enter on a button always activates it and not another
294 We handle this by maintaining a permanent and a temporary default items in
295 wxControlContainer (both may be NULL). When a button becomes the current
296 control (i.e. gets focus) it sets itself as the temporary default which
297 ensures that it has the right appearance and that Enter will be redirected
298 to it. When the button loses focus, it unsets the temporary default and so
299 the default item will be the permanent default -- that is the default button
300 if any had been set or none otherwise, which is just what we want.
302 NB: all this is quite complicated by now and the worst is that normally
303 it shouldn't be necessary at all as for the normal Windows programs
304 DefWindowProc() and IsDialogMessage() take care of all this
305 automatically -- however in wxWidgets programs this doesn't work for
306 nested hierarchies (i.e. a notebook inside a notebook) for unknown
307 reason and so we have to reproduce all this code ourselves. It would be
308 very nice if we could avoid doing it.
311 // set this button as the (permanently) default one in its panel
312 wxWindow
*wxButton::SetDefault()
314 // set this one as the default button both for wxWidgets ...
315 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
318 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
319 SetDefaultStyle(this, true);
321 return winOldDefault
;
324 // return the top level parent window if it's not being deleted yet, otherwise
326 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
330 // IsTopLevel() will return false for a wxTLW being deleted, so we also
331 // need the parent test for this case
332 wxWindow
* const parent
= win
->GetParent();
333 if ( !parent
|| win
->IsTopLevel() )
335 if ( win
->IsBeingDeleted() )
344 wxASSERT_MSG( win
, _T("button without top level parent?") );
346 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
347 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
352 // set this button as being currently default
353 void wxButton::SetTmpDefault()
355 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
359 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
360 tlw
->SetTmpDefaultItem(this);
362 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
363 SetDefaultStyle(this, true);
366 // unset this button as currently default, it may still stay permanent default
367 void wxButton::UnsetTmpDefault()
369 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
373 tlw
->SetTmpDefaultItem(NULL
);
375 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
377 SetDefaultStyle(this, false);
378 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
383 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
385 // we may be called with NULL pointer -- simpler to do the check here than
386 // in the caller which does wxDynamicCast()
390 // first, let DefDlgProc() know about the new default button
393 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
394 // focus at all any more
395 if ( !wxTheApp
->IsActive() )
398 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
399 wxCHECK_RET( tlw
, _T("button without top level window?") );
401 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
403 // sending DM_SETDEFID also changes the button style to
404 // BS_DEFPUSHBUTTON so there is nothing more to do
407 // then also change the style as needed
408 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
409 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
411 // don't do it with the owner drawn buttons because it will
412 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
413 // BS_DEFPUSHBUTTON != 0)!
414 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
416 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
417 on
? style
| BS_DEFPUSHBUTTON
418 : style
& ~BS_DEFPUSHBUTTON
,
423 // redraw the button - it will notice itself that it's
424 // [not] the default one [any longer]
428 //else: already has correct style
431 // ----------------------------------------------------------------------------
433 // ----------------------------------------------------------------------------
435 bool wxButton::SendClickEvent()
437 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
438 event
.SetEventObject(this);
440 return ProcessCommand(event
);
443 void wxButton::Command(wxCommandEvent
& event
)
445 ProcessCommand(event
);
448 // ----------------------------------------------------------------------------
449 // event/message handlers
450 // ----------------------------------------------------------------------------
452 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
454 bool processed
= false;
457 // NOTE: Apparently older versions (NT 4?) of the common controls send
458 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
459 // buttons, so in order to send two EVT_BUTTON events we should
460 // catch both types. Currently (Feb 2003) up-to-date versions of
461 // win98, win2k and winXP all send two BN_CLICKED messages for
462 // all button types, so we don't catch BN_DOUBLECLICKED anymore
463 // in order to not get 3 EVT_BUTTON events. If this is a problem
464 // then we need to figure out which version of the comctl32 changed
465 // this behaviour and test for it.
467 case 1: // message came from an accelerator
468 case BN_CLICKED
: // normal buttons send this
469 processed
= SendClickEvent();
476 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
478 // when we receive focus, we want to temporarily become the default button in
479 // our parent panel so that pressing "Enter" would activate us -- and when
480 // losing it we should restore the previous default button as well
481 if ( nMsg
== WM_SETFOCUS
)
485 // let the default processing take place too
487 else if ( nMsg
== WM_KILLFOCUS
)
491 else if ( nMsg
== WM_LBUTTONDBLCLK
)
493 // emulate a click event to force an owner-drawn button to change its
494 // appearance - without this, it won't do it
495 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
497 // and continue with processing the message normally as well
500 else if ( nMsg
== WM_THEMECHANGED
)
502 // need to recalculate the best size here
503 // as the theme size might have changed
504 InvalidateBestSize();
506 else if ( wxUxThemeEngine::GetIfActive() )
508 // we need to Refresh() if mouse has entered or left window
509 // so we can update the hot tracking state
510 // must use m_mouseInWindow here instead of IsMouseInWindow()
511 // since we need to know the first time the mouse enters the window
512 // and IsMouseInWindow() would return true in this case
513 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
514 nMsg
== WM_MOUSELEAVE
)
519 #endif // wxUSE_UXTHEME
521 // let the base class do all real processing
522 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
525 // ----------------------------------------------------------------------------
526 // owner-drawn buttons support
527 // ----------------------------------------------------------------------------
531 static void DrawButtonText(HDC hdc
,
533 const wxString
& text
,
536 COLORREF colOld
= SetTextColor(hdc
, col
);
537 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
539 if ( text
.find(_T('\n')) != wxString::npos
)
541 // draw multiline label
543 // first we need to compute its bounding rect
545 ::CopyRect(&rc
, pRect
);
546 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
547 DT_CENTER
| DT_CALCRECT
);
549 // now center this rect inside the entire button area
550 const LONG w
= rc
.right
- rc
.left
;
551 const LONG h
= rc
.bottom
- rc
.top
;
552 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
553 rc
.right
= rc
.left
+w
;
554 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
555 rc
.bottom
= rc
.top
+h
;
557 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, DT_CENTER
);
559 else // single line label
561 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
562 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
563 DT_SINGLELINE
| DT_CENTER
| DT_VCENTER
);
566 SetBkMode(hdc
, modeOld
);
567 SetTextColor(hdc
, colOld
);
570 static void DrawRect(HDC hdc
, const RECT
& r
)
572 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
573 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
574 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
575 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
578 void wxButton::MakeOwnerDrawn()
580 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
581 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
584 style
|= BS_OWNERDRAW
;
585 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
589 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
591 if ( !wxControl::SetBackgroundColour(colour
) )
604 bool wxButton::SetForegroundColour(const wxColour
&colour
)
606 if ( !wxControl::SetForegroundColour(colour
) )
620 The button frame looks like this normally:
623 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
624 WH GB H = light grey (LIGHT)
625 WH GB G = dark grey (SHADOW)
626 WH GB B = black (DKSHADOW)
631 When the button is selected, the button becomes like this (the total button
632 size doesn't change):
643 When the button is pushed (while selected) it is like:
655 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
656 bool selected
, bool pushed
)
659 CopyRect(&r
, &rectBtn
);
661 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
662 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
663 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
664 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
666 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
675 (void)SelectObject(hdc
, hpenGrey
);
676 ::InflateRect(&r
, -1, -1);
686 ::InflateRect(&r
, -1, -1);
689 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
690 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
692 (void)SelectObject(hdc
, hpenWhite
);
693 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
694 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
696 (void)SelectObject(hdc
, hpenLightGr
);
697 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
698 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
700 (void)SelectObject(hdc
, hpenGrey
);
701 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
702 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
705 (void)SelectObject(hdc
, hpenOld
);
706 DeleteObject(hpenWhite
);
707 DeleteObject(hpenLightGr
);
708 DeleteObject(hpenGrey
);
709 DeleteObject(hpenBlack
);
714 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
716 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
717 HDC hdc
= lpDIS
->hDC
;
718 UINT state
= lpDIS
->itemState
;
720 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
722 wxUxThemeHandle
theme(button
, L
"BUTTON");
725 if ( state
& ODS_SELECTED
)
727 iState
= PBS_PRESSED
;
729 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
733 else if ( state
& ODS_FOCUS
)
735 iState
= PBS_DEFAULTED
;
737 else if ( state
& ODS_DISABLED
)
739 iState
= PBS_DISABLED
;
746 // draw parent background if needed
747 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
751 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
755 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
758 // calculate content area margins
760 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
761 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
763 ::CopyRect(&rectClient
, &rectBtn
);
764 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
766 // if focused and !nofocus rect
767 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
769 DrawFocusRect(hdc
, &rectClient
);
772 if ( button
->UseBgCol() )
774 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
775 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
777 // don't overwrite the focus rect
778 ::InflateRect(&rectClient
, -1, -1);
779 FillRect(hdc
, &rectClient
, hbrushBackground
);
780 ::DeleteObject(hbrushBackground
);
783 #endif // wxUSE_UXTHEME
785 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
787 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
788 HDC hdc
= lpDIS
->hDC
;
789 UINT state
= lpDIS
->itemState
;
791 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
794 if ( wxUxThemeEngine::GetIfActive() )
796 MSWDrawXPBackground(this, wxdis
);
799 #endif // wxUSE_UXTHEME
801 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
803 // first, draw the background
804 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
805 FillRect(hdc
, &rectBtn
, hbrushBackground
);
806 ::DeleteObject(hbrushBackground
);
808 // draw the border for the current state
809 bool selected
= (state
& ODS_SELECTED
) != 0;
812 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
815 selected
= tlw
->GetDefaultItem() == this;
818 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
820 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
822 // if focused and !nofocus rect
823 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
826 CopyRect(&rectFocus
, &rectBtn
);
828 // I don't know where does this constant come from, but this is how
829 // Windows draws them
830 InflateRect(&rectFocus
, -4, -4);
832 DrawFocusRect(hdc
, &rectFocus
);
837 // the label is shifted by 1 pixel to create "pushed" effect
838 OffsetRect(&rectBtn
, 1, 1);
842 COLORREF colFg
= wxColourToRGB(GetForegroundColour());
843 if ( state
& ODS_DISABLED
) colFg
= GetSysColor(COLOR_GRAYTEXT
) ;
844 wxString label
= GetLabel();
845 if ( state
& ODS_NOACCEL
) label
= GetLabelText() ;
846 DrawButtonText(hdc
, &rectBtn
, label
, colFg
);
851 #endif // wxUSE_BUTTON