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 // helper functions from wx/msw/private/button.h
146 // ----------------------------------------------------------------------------
148 void wxMSWButton::UpdateMultilineStyle(HWND hwnd
, const wxString
& label
)
150 // update BS_MULTILINE style depending on the new label (resetting it
151 // doesn't seem to do anything very useful but it shouldn't hurt and we do
152 // have to set it whenever the label becomes multi line as otherwise it
153 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
154 // the control unless it already has new lines in its label)
155 long styleOld
= ::GetWindowLong(hwnd
, GWL_STYLE
),
157 if ( label
.find(_T('\n')) != wxString::npos
)
158 styleNew
= styleOld
| BS_MULTILINE
;
160 styleNew
= styleOld
& ~BS_MULTILINE
;
162 if ( styleNew
!= styleOld
)
163 ::SetWindowLong(hwnd
, GWL_STYLE
, styleNew
);
166 wxSize
wxMSWButton::ComputeBestSize(wxControl
*btn
)
172 dc
.GetMultiLineTextExtent(btn
->GetLabelText(), &wBtn
, &hBtn
);
174 // FIXME: this is pure guesswork, need to retrieve the real button margins
175 wBtn
+= 3*btn
->GetCharWidth();
176 hBtn
= 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(hBtn
)/10;
178 // all buttons have at least the standard size unless the user explicitly
179 // wants them to be of smaller size and used wxBU_EXACTFIT style when
180 // creating the button
181 if ( !btn
->HasFlag(wxBU_EXACTFIT
) )
183 wxSize sz
= wxButton::GetDefaultSize();
190 wxSize
best(wBtn
, hBtn
);
191 btn
->CacheBestSize(best
);
195 // ----------------------------------------------------------------------------
196 // creation/destruction
197 // ----------------------------------------------------------------------------
199 bool wxButton::Create(wxWindow
*parent
,
205 const wxValidator
& validator
,
206 const wxString
& name
)
209 if (label
.empty() && wxIsStockID(id
))
211 // On Windows, some buttons aren't supposed to have mnemonics
212 label
= wxGetStockLabel
215 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
217 : wxSTOCK_WITH_MNEMONIC
221 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
225 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
227 // if the label contains several lines we must explicitly tell the button
228 // about it or it wouldn't draw it correctly ("\n"s would just appear as
231 // NB: we do it here and not in MSWGetStyle() because we need the label
232 // value and the label is not set yet when MSWGetStyle() is called
233 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
235 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
238 wxButton::~wxButton()
240 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
241 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
247 // ----------------------------------------------------------------------------
249 // ----------------------------------------------------------------------------
251 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
253 // buttons never have an external border, they draw their own one
254 WXDWORD msStyle
= wxControl::MSWGetStyle
256 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
259 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
260 // each other in any resizeable dialog which has more than one button in
262 msStyle
|= WS_CLIPSIBLINGS
;
264 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
265 // and wxBU_RIGHT to get BS_CENTER!
266 if ( style
& wxBU_LEFT
)
268 if ( style
& wxBU_RIGHT
)
270 if ( style
& wxBU_TOP
)
272 if ( style
& wxBU_BOTTOM
)
273 msStyle
|= BS_BOTTOM
;
276 if ( style
& wxNO_BORDER
)
278 #endif // __WXWINCE__
283 void wxButton::SetLabel(const wxString
& label
)
285 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
287 wxButtonBase::SetLabel(label
);
290 // ----------------------------------------------------------------------------
291 // size management including autosizing
292 // ----------------------------------------------------------------------------
294 wxSize
wxButton::DoGetBestSize() const
296 return wxMSWButton::ComputeBestSize(wx_const_cast(wxButton
*, this));
300 wxSize
wxButtonBase::GetDefaultSize()
302 static wxSize s_sizeBtn
;
304 if ( s_sizeBtn
.x
== 0 )
307 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
309 // the size of a standard button in the dialog units is 50x14,
310 // translate this to pixels
311 // NB1: the multipliers come from the Windows convention
312 // NB2: the extra +1/+2 were needed to get the size be the same as the
313 // size of the buttons in the standard dialog - I don't know how
314 // this happens, but on my system this size is 75x23 in pixels and
315 // 23*8 isn't even divisible by 14... Would be nice to understand
316 // why these constants are needed though!
317 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
318 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
324 // ----------------------------------------------------------------------------
325 // default button handling
326 // ----------------------------------------------------------------------------
329 "Everything you ever wanted to know about the default buttons" or "Why do we
330 have to do all this?"
332 In MSW the default button should be activated when the user presses Enter
333 and the current control doesn't process Enter itself somehow. This is
334 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
335 Another aspect of "defaultness" is that the default button has different
336 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
337 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
338 be unset if our parent window is not active so it should be unset whenever
339 we lose activation and set back when we regain it.
341 Final complication is that when a button is active, it should be the default
342 one, i.e. pressing Enter on a button always activates it and not another
345 We handle this by maintaining a permanent and a temporary default items in
346 wxControlContainer (both may be NULL). When a button becomes the current
347 control (i.e. gets focus) it sets itself as the temporary default which
348 ensures that it has the right appearance and that Enter will be redirected
349 to it. When the button loses focus, it unsets the temporary default and so
350 the default item will be the permanent default -- that is the default button
351 if any had been set or none otherwise, which is just what we want.
353 NB: all this is quite complicated by now and the worst is that normally
354 it shouldn't be necessary at all as for the normal Windows programs
355 DefWindowProc() and IsDialogMessage() take care of all this
356 automatically -- however in wxWidgets programs this doesn't work for
357 nested hierarchies (i.e. a notebook inside a notebook) for unknown
358 reason and so we have to reproduce all this code ourselves. It would be
359 very nice if we could avoid doing it.
362 // set this button as the (permanently) default one in its panel
363 wxWindow
*wxButton::SetDefault()
365 // set this one as the default button both for wxWidgets ...
366 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
369 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
370 SetDefaultStyle(this, true);
372 return winOldDefault
;
375 // return the top level parent window if it's not being deleted yet, otherwise
377 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
381 // IsTopLevel() will return false for a wxTLW being deleted, so we also
382 // need the parent test for this case
383 wxWindow
* const parent
= win
->GetParent();
384 if ( !parent
|| win
->IsTopLevel() )
386 if ( win
->IsBeingDeleted() )
395 wxASSERT_MSG( win
, _T("button without top level parent?") );
397 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
398 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
403 // set this button as being currently default
404 void wxButton::SetTmpDefault()
406 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
410 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
411 tlw
->SetTmpDefaultItem(this);
413 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
414 SetDefaultStyle(this, true);
417 // unset this button as currently default, it may still stay permanent default
418 void wxButton::UnsetTmpDefault()
420 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
424 tlw
->SetTmpDefaultItem(NULL
);
426 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
428 SetDefaultStyle(this, false);
429 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
434 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
436 // we may be called with NULL pointer -- simpler to do the check here than
437 // in the caller which does wxDynamicCast()
441 // first, let DefDlgProc() know about the new default button
444 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
445 // focus at all any more
446 if ( !wxTheApp
->IsActive() )
449 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
450 wxCHECK_RET( tlw
, _T("button without top level window?") );
452 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
454 // sending DM_SETDEFID also changes the button style to
455 // BS_DEFPUSHBUTTON so there is nothing more to do
458 // then also change the style as needed
459 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
460 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
462 // don't do it with the owner drawn buttons because it will
463 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
464 // BS_DEFPUSHBUTTON != 0)!
465 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
467 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
468 on
? style
| BS_DEFPUSHBUTTON
469 : style
& ~BS_DEFPUSHBUTTON
,
474 // redraw the button - it will notice itself that it's
475 // [not] the default one [any longer]
479 //else: already has correct style
482 // ----------------------------------------------------------------------------
484 // ----------------------------------------------------------------------------
486 bool wxButton::SendClickEvent()
488 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
489 event
.SetEventObject(this);
491 return ProcessCommand(event
);
494 void wxButton::Command(wxCommandEvent
& event
)
496 ProcessCommand(event
);
499 // ----------------------------------------------------------------------------
500 // event/message handlers
501 // ----------------------------------------------------------------------------
503 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
505 bool processed
= false;
508 // NOTE: Apparently older versions (NT 4?) of the common controls send
509 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
510 // buttons, so in order to send two EVT_BUTTON events we should
511 // catch both types. Currently (Feb 2003) up-to-date versions of
512 // win98, win2k and winXP all send two BN_CLICKED messages for
513 // all button types, so we don't catch BN_DOUBLECLICKED anymore
514 // in order to not get 3 EVT_BUTTON events. If this is a problem
515 // then we need to figure out which version of the comctl32 changed
516 // this behaviour and test for it.
518 case 1: // message came from an accelerator
519 case BN_CLICKED
: // normal buttons send this
520 processed
= SendClickEvent();
527 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
529 // when we receive focus, we want to temporarily become the default button in
530 // our parent panel so that pressing "Enter" would activate us -- and when
531 // losing it we should restore the previous default button as well
532 if ( nMsg
== WM_SETFOCUS
)
536 // let the default processing take place too
538 else if ( nMsg
== WM_KILLFOCUS
)
542 else if ( nMsg
== WM_LBUTTONDBLCLK
)
544 // emulate a click event to force an owner-drawn button to change its
545 // appearance - without this, it won't do it
546 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
548 // and continue with processing the message normally as well
551 else if ( nMsg
== WM_THEMECHANGED
)
553 // need to recalculate the best size here
554 // as the theme size might have changed
555 InvalidateBestSize();
557 else if ( wxUxThemeEngine::GetIfActive() )
559 // we need to Refresh() if mouse has entered or left window
560 // so we can update the hot tracking state
561 // must use m_mouseInWindow here instead of IsMouseInWindow()
562 // since we need to know the first time the mouse enters the window
563 // and IsMouseInWindow() would return true in this case
564 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
565 nMsg
== WM_MOUSELEAVE
)
570 #endif // wxUSE_UXTHEME
572 // let the base class do all real processing
573 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
576 // ----------------------------------------------------------------------------
577 // owner-drawn buttons support
578 // ----------------------------------------------------------------------------
582 static void DrawButtonText(HDC hdc
,
584 const wxString
& text
,
587 COLORREF colOld
= SetTextColor(hdc
, col
);
588 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
590 if ( text
.find(_T('\n')) != wxString::npos
)
592 // draw multiline label
594 // first we need to compute its bounding rect
596 ::CopyRect(&rc
, pRect
);
597 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
598 DT_CENTER
| DT_CALCRECT
);
600 // now center this rect inside the entire button area
601 const LONG w
= rc
.right
- rc
.left
;
602 const LONG h
= rc
.bottom
- rc
.top
;
603 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
604 rc
.right
= rc
.left
+w
;
605 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
606 rc
.bottom
= rc
.top
+h
;
608 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, DT_CENTER
);
610 else // single line label
612 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
613 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
614 DT_SINGLELINE
| DT_CENTER
| DT_VCENTER
);
617 SetBkMode(hdc
, modeOld
);
618 SetTextColor(hdc
, colOld
);
621 static void DrawRect(HDC hdc
, const RECT
& r
)
623 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
624 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
625 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
626 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
629 void wxButton::MakeOwnerDrawn()
631 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
632 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
635 style
|= BS_OWNERDRAW
;
636 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
640 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
642 if ( !wxControl::SetBackgroundColour(colour
) )
655 bool wxButton::SetForegroundColour(const wxColour
&colour
)
657 if ( !wxControl::SetForegroundColour(colour
) )
671 The button frame looks like this normally:
674 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
675 WH GB H = light grey (LIGHT)
676 WH GB G = dark grey (SHADOW)
677 WH GB B = black (DKSHADOW)
682 When the button is selected, the button becomes like this (the total button
683 size doesn't change):
694 When the button is pushed (while selected) it is like:
706 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
707 bool selected
, bool pushed
)
710 CopyRect(&r
, &rectBtn
);
712 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
713 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
714 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
715 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
717 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
726 (void)SelectObject(hdc
, hpenGrey
);
727 ::InflateRect(&r
, -1, -1);
737 ::InflateRect(&r
, -1, -1);
740 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
741 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
743 (void)SelectObject(hdc
, hpenWhite
);
744 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
745 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
747 (void)SelectObject(hdc
, hpenLightGr
);
748 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
749 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
751 (void)SelectObject(hdc
, hpenGrey
);
752 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
753 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
756 (void)SelectObject(hdc
, hpenOld
);
757 DeleteObject(hpenWhite
);
758 DeleteObject(hpenLightGr
);
759 DeleteObject(hpenGrey
);
760 DeleteObject(hpenBlack
);
765 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
767 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
768 HDC hdc
= lpDIS
->hDC
;
769 UINT state
= lpDIS
->itemState
;
771 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
773 wxUxThemeHandle
theme(button
, L
"BUTTON");
776 if ( state
& ODS_SELECTED
)
778 iState
= PBS_PRESSED
;
780 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
784 else if ( state
& ODS_FOCUS
)
786 iState
= PBS_DEFAULTED
;
788 else if ( state
& ODS_DISABLED
)
790 iState
= PBS_DISABLED
;
797 // draw parent background if needed
798 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
802 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
806 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
809 // calculate content area margins
811 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
812 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
814 ::CopyRect(&rectClient
, &rectBtn
);
815 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
817 // if focused and !nofocus rect
818 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
820 DrawFocusRect(hdc
, &rectClient
);
823 if ( button
->UseBgCol() )
825 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
826 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
828 // don't overwrite the focus rect
829 ::InflateRect(&rectClient
, -1, -1);
830 FillRect(hdc
, &rectClient
, hbrushBackground
);
831 ::DeleteObject(hbrushBackground
);
834 #endif // wxUSE_UXTHEME
836 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
838 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
839 HDC hdc
= lpDIS
->hDC
;
840 UINT state
= lpDIS
->itemState
;
842 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
845 if ( wxUxThemeEngine::GetIfActive() )
847 MSWDrawXPBackground(this, wxdis
);
850 #endif // wxUSE_UXTHEME
852 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
854 // first, draw the background
855 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
856 FillRect(hdc
, &rectBtn
, hbrushBackground
);
857 ::DeleteObject(hbrushBackground
);
859 // draw the border for the current state
860 bool selected
= (state
& ODS_SELECTED
) != 0;
863 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
866 selected
= tlw
->GetDefaultItem() == this;
869 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
871 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
873 // if focused and !nofocus rect
874 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
877 CopyRect(&rectFocus
, &rectBtn
);
879 // I don't know where does this constant come from, but this is how
880 // Windows draws them
881 InflateRect(&rectFocus
, -4, -4);
883 DrawFocusRect(hdc
, &rectFocus
);
888 // the label is shifted by 1 pixel to create "pushed" effect
889 OffsetRect(&rectBtn
, 1, 1);
893 COLORREF colFg
= wxColourToRGB(GetForegroundColour());
894 if ( state
& ODS_DISABLED
) colFg
= GetSysColor(COLOR_GRAYTEXT
) ;
895 wxString label
= GetLabel();
896 if ( state
& ODS_NOACCEL
) label
= GetLabelText() ;
897 DrawButtonText(hdc
, &rectBtn
, label
, colFg
);
902 #endif // wxUSE_BUTTON