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
76 #define DT_HIDEPREFIX 0x00100000
79 // ----------------------------------------------------------------------------
81 // ----------------------------------------------------------------------------
83 #if wxUSE_EXTENDED_RTTI
85 WX_DEFINE_FLAGS( wxButtonStyle
)
87 wxBEGIN_FLAGS( wxButtonStyle
)
88 // new style border flags, we put them first to
89 // use them for streaming out
90 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
91 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
92 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
93 wxFLAGS_MEMBER(wxBORDER_RAISED
)
94 wxFLAGS_MEMBER(wxBORDER_STATIC
)
95 wxFLAGS_MEMBER(wxBORDER_NONE
)
97 // old style border flags
98 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
99 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
100 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
101 wxFLAGS_MEMBER(wxRAISED_BORDER
)
102 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
103 wxFLAGS_MEMBER(wxBORDER
)
105 // standard window styles
106 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
107 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
108 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
109 wxFLAGS_MEMBER(wxWANTS_CHARS
)
110 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
111 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
112 wxFLAGS_MEMBER(wxVSCROLL
)
113 wxFLAGS_MEMBER(wxHSCROLL
)
115 wxFLAGS_MEMBER(wxBU_LEFT
)
116 wxFLAGS_MEMBER(wxBU_RIGHT
)
117 wxFLAGS_MEMBER(wxBU_TOP
)
118 wxFLAGS_MEMBER(wxBU_BOTTOM
)
119 wxFLAGS_MEMBER(wxBU_EXACTFIT
)
120 wxEND_FLAGS( wxButtonStyle
)
122 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton
, wxControl
,"wx/button.h")
124 wxBEGIN_PROPERTIES_TABLE(wxButton
)
125 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEvent
)
127 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
128 wxPROPERTY( Label
, wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
130 wxPROPERTY_FLAGS( WindowStyle
, wxButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
132 wxEND_PROPERTIES_TABLE()
134 wxBEGIN_HANDLERS_TABLE(wxButton
)
135 wxEND_HANDLERS_TABLE()
137 wxCONSTRUCTOR_6( wxButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
141 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
)
144 // ============================================================================
146 // ============================================================================
148 // ----------------------------------------------------------------------------
149 // helper functions from wx/msw/private/button.h
150 // ----------------------------------------------------------------------------
152 void wxMSWButton::UpdateMultilineStyle(HWND hwnd
, const wxString
& label
)
154 // update BS_MULTILINE style depending on the new label (resetting it
155 // doesn't seem to do anything very useful but it shouldn't hurt and we do
156 // have to set it whenever the label becomes multi line as otherwise it
157 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
158 // the control unless it already has new lines in its label)
159 long styleOld
= ::GetWindowLong(hwnd
, GWL_STYLE
),
161 if ( label
.find(_T('\n')) != wxString::npos
)
162 styleNew
= styleOld
| BS_MULTILINE
;
164 styleNew
= styleOld
& ~BS_MULTILINE
;
166 if ( styleNew
!= styleOld
)
167 ::SetWindowLong(hwnd
, GWL_STYLE
, styleNew
);
170 wxSize
wxMSWButton::GetFittingSize(wxWindow
*win
, const wxSize
& sizeLabel
)
172 // FIXME: this is pure guesswork, need to retrieve the real button margins
173 wxSize sizeBtn
= sizeLabel
;
175 sizeBtn
.x
+= 3*win
->GetCharWidth();
176 sizeBtn
.y
= 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel
.y
)/10;
181 wxSize
wxMSWButton::ComputeBestSize(wxControl
*btn
)
186 dc
.GetMultiLineTextExtent(btn
->GetLabelText(), &sizeBtn
.x
, &sizeBtn
.y
);
188 sizeBtn
= GetFittingSize(btn
, sizeBtn
);
190 // all buttons have at least the standard size unless the user explicitly
191 // wants them to be of smaller size and used wxBU_EXACTFIT style when
192 // creating the button
193 if ( !btn
->HasFlag(wxBU_EXACTFIT
) )
195 wxSize sizeDef
= wxButton::GetDefaultSize();
196 if ( sizeBtn
.x
< sizeDef
.x
)
197 sizeBtn
.x
= sizeDef
.x
;
198 if ( sizeBtn
.y
< sizeDef
.y
)
199 sizeBtn
.y
= sizeDef
.y
;
202 btn
->CacheBestSize(sizeBtn
);
207 // ----------------------------------------------------------------------------
208 // creation/destruction
209 // ----------------------------------------------------------------------------
211 bool wxButton::Create(wxWindow
*parent
,
217 const wxValidator
& validator
,
218 const wxString
& name
)
221 if (label
.empty() && wxIsStockID(id
))
223 // On Windows, some buttons aren't supposed to have mnemonics
224 label
= wxGetStockLabel
227 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
229 : wxSTOCK_WITH_MNEMONIC
233 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
237 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
239 // if the label contains several lines we must explicitly tell the button
240 // about it or it wouldn't draw it correctly ("\n"s would just appear as
243 // NB: we do it here and not in MSWGetStyle() because we need the label
244 // value and the label is not set yet when MSWGetStyle() is called
245 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
247 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
250 wxButton::~wxButton()
252 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
253 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
259 // ----------------------------------------------------------------------------
261 // ----------------------------------------------------------------------------
263 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
265 // buttons never have an external border, they draw their own one
266 WXDWORD msStyle
= wxControl::MSWGetStyle
268 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
271 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
272 // each other in any resizeable dialog which has more than one button in
274 msStyle
|= WS_CLIPSIBLINGS
;
276 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
277 // and wxBU_RIGHT to get BS_CENTER!
278 if ( style
& wxBU_LEFT
)
280 if ( style
& wxBU_RIGHT
)
282 if ( style
& wxBU_TOP
)
284 if ( style
& wxBU_BOTTOM
)
285 msStyle
|= BS_BOTTOM
;
288 if ( style
& wxNO_BORDER
)
290 #endif // __WXWINCE__
295 void wxButton::SetLabel(const wxString
& label
)
297 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
299 wxButtonBase::SetLabel(label
);
302 // ----------------------------------------------------------------------------
303 // size management including autosizing
304 // ----------------------------------------------------------------------------
306 wxSize
wxButton::DoGetBestSize() const
308 return wxMSWButton::ComputeBestSize(const_cast<wxButton
*>(this));
312 wxSize
wxButtonBase::GetDefaultSize()
314 static wxSize s_sizeBtn
;
316 if ( s_sizeBtn
.x
== 0 )
319 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
321 // the size of a standard button in the dialog units is 50x14,
322 // translate this to pixels
323 // NB1: the multipliers come from the Windows convention
324 // NB2: the extra +1/+2 were needed to get the size be the same as the
325 // size of the buttons in the standard dialog - I don't know how
326 // this happens, but on my system this size is 75x23 in pixels and
327 // 23*8 isn't even divisible by 14... Would be nice to understand
328 // why these constants are needed though!
329 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
330 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
336 // ----------------------------------------------------------------------------
337 // default button handling
338 // ----------------------------------------------------------------------------
341 "Everything you ever wanted to know about the default buttons" or "Why do we
342 have to do all this?"
344 In MSW the default button should be activated when the user presses Enter
345 and the current control doesn't process Enter itself somehow. This is
346 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
347 Another aspect of "defaultness" is that the default button has different
348 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
349 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
350 be unset if our parent window is not active so it should be unset whenever
351 we lose activation and set back when we regain it.
353 Final complication is that when a button is active, it should be the default
354 one, i.e. pressing Enter on a button always activates it and not another
357 We handle this by maintaining a permanent and a temporary default items in
358 wxControlContainer (both may be NULL). When a button becomes the current
359 control (i.e. gets focus) it sets itself as the temporary default which
360 ensures that it has the right appearance and that Enter will be redirected
361 to it. When the button loses focus, it unsets the temporary default and so
362 the default item will be the permanent default -- that is the default button
363 if any had been set or none otherwise, which is just what we want.
365 NB: all this is quite complicated by now and the worst is that normally
366 it shouldn't be necessary at all as for the normal Windows programs
367 DefWindowProc() and IsDialogMessage() take care of all this
368 automatically -- however in wxWidgets programs this doesn't work for
369 nested hierarchies (i.e. a notebook inside a notebook) for unknown
370 reason and so we have to reproduce all this code ourselves. It would be
371 very nice if we could avoid doing it.
374 // set this button as the (permanently) default one in its panel
375 wxWindow
*wxButton::SetDefault()
377 // set this one as the default button both for wxWidgets ...
378 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
381 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
382 SetDefaultStyle(this, true);
384 return winOldDefault
;
387 // return the top level parent window if it's not being deleted yet, otherwise
389 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
393 // IsTopLevel() will return false for a wxTLW being deleted, so we also
394 // need the parent test for this case
395 wxWindow
* const parent
= win
->GetParent();
396 if ( !parent
|| win
->IsTopLevel() )
398 if ( win
->IsBeingDeleted() )
407 wxASSERT_MSG( win
, _T("button without top level parent?") );
409 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
410 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
415 // set this button as being currently default
416 void wxButton::SetTmpDefault()
418 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
422 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
423 tlw
->SetTmpDefaultItem(this);
425 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
426 SetDefaultStyle(this, true);
429 // unset this button as currently default, it may still stay permanent default
430 void wxButton::UnsetTmpDefault()
432 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
436 tlw
->SetTmpDefaultItem(NULL
);
438 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
440 SetDefaultStyle(this, false);
441 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
446 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
448 // we may be called with NULL pointer -- simpler to do the check here than
449 // in the caller which does wxDynamicCast()
453 // first, let DefDlgProc() know about the new default button
456 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
457 // focus at all any more
458 if ( !wxTheApp
->IsActive() )
461 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
462 wxCHECK_RET( tlw
, _T("button without top level window?") );
464 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
466 // sending DM_SETDEFID also changes the button style to
467 // BS_DEFPUSHBUTTON so there is nothing more to do
470 // then also change the style as needed
471 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
472 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
474 // don't do it with the owner drawn buttons because it will
475 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
476 // BS_DEFPUSHBUTTON != 0)!
477 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
479 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
480 on
? style
| BS_DEFPUSHBUTTON
481 : style
& ~BS_DEFPUSHBUTTON
,
486 // redraw the button - it will notice itself that it's
487 // [not] the default one [any longer]
491 //else: already has correct style
494 // ----------------------------------------------------------------------------
496 // ----------------------------------------------------------------------------
498 bool wxButton::SendClickEvent()
500 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
501 event
.SetEventObject(this);
503 return ProcessCommand(event
);
506 void wxButton::Command(wxCommandEvent
& event
)
508 ProcessCommand(event
);
511 // ----------------------------------------------------------------------------
512 // event/message handlers
513 // ----------------------------------------------------------------------------
515 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
517 bool processed
= false;
520 // NOTE: Apparently older versions (NT 4?) of the common controls send
521 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
522 // buttons, so in order to send two EVT_BUTTON events we should
523 // catch both types. Currently (Feb 2003) up-to-date versions of
524 // win98, win2k and winXP all send two BN_CLICKED messages for
525 // all button types, so we don't catch BN_DOUBLECLICKED anymore
526 // in order to not get 3 EVT_BUTTON events. If this is a problem
527 // then we need to figure out which version of the comctl32 changed
528 // this behaviour and test for it.
530 case 1: // message came from an accelerator
531 case BN_CLICKED
: // normal buttons send this
532 processed
= SendClickEvent();
539 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
541 // when we receive focus, we want to temporarily become the default button in
542 // our parent panel so that pressing "Enter" would activate us -- and when
543 // losing it we should restore the previous default button as well
544 if ( nMsg
== WM_SETFOCUS
)
548 // let the default processing take place too
550 else if ( nMsg
== WM_KILLFOCUS
)
554 else if ( nMsg
== WM_LBUTTONDBLCLK
)
556 // emulate a click event to force an owner-drawn button to change its
557 // appearance - without this, it won't do it
558 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
560 // and continue with processing the message normally as well
563 else if ( nMsg
== WM_THEMECHANGED
)
565 // need to recalculate the best size here
566 // as the theme size might have changed
567 InvalidateBestSize();
569 else if ( wxUxThemeEngine::GetIfActive() )
571 // we need to Refresh() if mouse has entered or left window
572 // so we can update the hot tracking state
573 // must use m_mouseInWindow here instead of IsMouseInWindow()
574 // since we need to know the first time the mouse enters the window
575 // and IsMouseInWindow() would return true in this case
576 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
577 nMsg
== WM_MOUSELEAVE
)
582 #endif // wxUSE_UXTHEME
584 // let the base class do all real processing
585 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
588 // ----------------------------------------------------------------------------
589 // owner-drawn buttons support
590 // ----------------------------------------------------------------------------
594 static void DrawButtonText(HDC hdc
,
596 const wxString
& text
,
600 COLORREF colOld
= SetTextColor(hdc
, col
);
601 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
603 // center text horizontally in any case
606 if ( text
.find(_T('\n')) != wxString::npos
)
608 // draw multiline label
610 // first we need to compute its bounding rect
612 ::CopyRect(&rc
, pRect
);
613 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
614 DT_CENTER
| DT_CALCRECT
);
616 // now center this rect inside the entire button area
617 const LONG w
= rc
.right
- rc
.left
;
618 const LONG h
= rc
.bottom
- rc
.top
;
619 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
620 rc
.right
= rc
.left
+w
;
621 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
622 rc
.bottom
= rc
.top
+h
;
624 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, flags
);
626 else // single line label
628 // centre text vertically too (notice that we must have DT_SINGLELINE
629 // for DT_VCENTER to work)
630 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
631 flags
| DT_SINGLELINE
| DT_VCENTER
);
634 SetBkMode(hdc
, modeOld
);
635 SetTextColor(hdc
, colOld
);
638 static void DrawRect(HDC hdc
, const RECT
& r
)
640 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
641 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
642 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
643 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
646 void wxButton::MakeOwnerDrawn()
648 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
649 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
652 style
|= BS_OWNERDRAW
;
653 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
657 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
659 if ( !wxControl::SetBackgroundColour(colour
) )
672 bool wxButton::SetForegroundColour(const wxColour
&colour
)
674 if ( !wxControl::SetForegroundColour(colour
) )
688 The button frame looks like this normally:
691 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
692 WH GB H = light grey (LIGHT)
693 WH GB G = dark grey (SHADOW)
694 WH GB B = black (DKSHADOW)
699 When the button is selected, the button becomes like this (the total button
700 size doesn't change):
711 When the button is pushed (while selected) it is like:
723 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
724 bool selected
, bool pushed
)
727 CopyRect(&r
, &rectBtn
);
729 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
730 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
731 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
732 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
734 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
743 (void)SelectObject(hdc
, hpenGrey
);
744 ::InflateRect(&r
, -1, -1);
754 ::InflateRect(&r
, -1, -1);
757 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
758 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
760 (void)SelectObject(hdc
, hpenWhite
);
761 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
762 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
764 (void)SelectObject(hdc
, hpenLightGr
);
765 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
766 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
768 (void)SelectObject(hdc
, hpenGrey
);
769 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
770 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
773 (void)SelectObject(hdc
, hpenOld
);
774 DeleteObject(hpenWhite
);
775 DeleteObject(hpenLightGr
);
776 DeleteObject(hpenGrey
);
777 DeleteObject(hpenBlack
);
782 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
784 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
785 HDC hdc
= lpDIS
->hDC
;
786 UINT state
= lpDIS
->itemState
;
788 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
790 wxUxThemeHandle
theme(button
, L
"BUTTON");
793 if ( state
& ODS_SELECTED
)
795 iState
= PBS_PRESSED
;
797 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
801 else if ( state
& ODS_FOCUS
)
803 iState
= PBS_DEFAULTED
;
805 else if ( state
& ODS_DISABLED
)
807 iState
= PBS_DISABLED
;
814 // draw parent background if needed
815 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
819 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
823 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
826 // calculate content area margins
828 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
829 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
831 ::CopyRect(&rectClient
, &rectBtn
);
832 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
834 // if focused and !nofocus rect
835 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
837 DrawFocusRect(hdc
, &rectClient
);
840 if ( button
->UseBgCol() )
842 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
843 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
845 // don't overwrite the focus rect
846 ::InflateRect(&rectClient
, -1, -1);
847 FillRect(hdc
, &rectClient
, hbrushBackground
);
848 ::DeleteObject(hbrushBackground
);
851 #endif // wxUSE_UXTHEME
853 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
855 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
856 HDC hdc
= lpDIS
->hDC
;
857 UINT state
= lpDIS
->itemState
;
859 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
862 if ( wxUxThemeEngine::GetIfActive() )
864 MSWDrawXPBackground(this, wxdis
);
867 #endif // wxUSE_UXTHEME
869 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
871 // first, draw the background
872 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
873 FillRect(hdc
, &rectBtn
, hbrushBackground
);
874 ::DeleteObject(hbrushBackground
);
876 // draw the border for the current state
877 bool selected
= (state
& ODS_SELECTED
) != 0;
880 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
883 selected
= tlw
->GetDefaultItem() == this;
886 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
888 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
890 // if focused and !nofocus rect
891 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
894 CopyRect(&rectFocus
, &rectBtn
);
896 // I don't know where does this constant come from, but this is how
897 // Windows draws them
898 InflateRect(&rectFocus
, -4, -4);
900 DrawFocusRect(hdc
, &rectFocus
);
905 // the label is shifted by 1 pixel to create "pushed" effect
906 OffsetRect(&rectBtn
, 1, 1);
910 COLORREF colFg
= state
& ODS_DISABLED
911 ? ::GetSysColor(COLOR_GRAYTEXT
)
912 : wxColourToRGB(GetForegroundColour());
914 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000) systems
915 // but by happy coincidence ODS_NOACCEL is not used under them neither so
916 // DT_HIDEPREFIX should never be used there
917 DrawButtonText(hdc
, &rectBtn
, GetLabel(), colFg
,
918 state
& ODS_NOACCEL
? DT_HIDEPREFIX
: 0);
923 #endif // wxUSE_BUTTON