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
62 #ifndef BCM_SETIMAGELIST
63 #define BCM_SETIMAGELIST 0x1602
64 #define BCM_SETTEXTMARGIN 0x1604
66 #endif // wxUSE_UXTHEME
68 #ifndef WM_THEMECHANGED
69 #define WM_THEMECHANGED 0x031A
73 #define ODS_NOACCEL 0x0100
76 #ifndef ODS_NOFOCUSRECT
77 #define ODS_NOFOCUSRECT 0x0200
81 #define DT_HIDEPREFIX 0x00100000
84 // ----------------------------------------------------------------------------
86 // ----------------------------------------------------------------------------
88 #if wxUSE_EXTENDED_RTTI
90 WX_DEFINE_FLAGS( wxButtonStyle
)
92 wxBEGIN_FLAGS( wxButtonStyle
)
93 // new style border flags, we put them first to
94 // use them for streaming out
95 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
96 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
97 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
98 wxFLAGS_MEMBER(wxBORDER_RAISED
)
99 wxFLAGS_MEMBER(wxBORDER_STATIC
)
100 wxFLAGS_MEMBER(wxBORDER_NONE
)
102 // old style border flags
103 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
104 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
105 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
106 wxFLAGS_MEMBER(wxRAISED_BORDER
)
107 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
108 wxFLAGS_MEMBER(wxBORDER
)
110 // standard window styles
111 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
112 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
113 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
114 wxFLAGS_MEMBER(wxWANTS_CHARS
)
115 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
116 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
117 wxFLAGS_MEMBER(wxVSCROLL
)
118 wxFLAGS_MEMBER(wxHSCROLL
)
120 wxFLAGS_MEMBER(wxBU_LEFT
)
121 wxFLAGS_MEMBER(wxBU_RIGHT
)
122 wxFLAGS_MEMBER(wxBU_TOP
)
123 wxFLAGS_MEMBER(wxBU_BOTTOM
)
124 wxFLAGS_MEMBER(wxBU_EXACTFIT
)
125 wxEND_FLAGS( wxButtonStyle
)
127 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton
, wxControl
,"wx/button.h")
129 wxBEGIN_PROPERTIES_TABLE(wxButton
)
130 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEvent
)
132 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
133 wxPROPERTY( Label
, wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
135 wxPROPERTY_FLAGS( WindowStyle
, wxButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
137 wxEND_PROPERTIES_TABLE()
139 wxBEGIN_HANDLERS_TABLE(wxButton
)
140 wxEND_HANDLERS_TABLE()
142 wxCONSTRUCTOR_6( wxButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
146 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
)
149 // ============================================================================
151 // ============================================================================
153 // ----------------------------------------------------------------------------
154 // helper functions from wx/msw/private/button.h
155 // ----------------------------------------------------------------------------
157 void wxMSWButton::UpdateMultilineStyle(HWND hwnd
, const wxString
& label
)
159 // update BS_MULTILINE style depending on the new label (resetting it
160 // doesn't seem to do anything very useful but it shouldn't hurt and we do
161 // have to set it whenever the label becomes multi line as otherwise it
162 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
163 // the control unless it already has new lines in its label)
164 long styleOld
= ::GetWindowLong(hwnd
, GWL_STYLE
),
166 if ( label
.find(_T('\n')) != wxString::npos
)
167 styleNew
= styleOld
| BS_MULTILINE
;
169 styleNew
= styleOld
& ~BS_MULTILINE
;
171 if ( styleNew
!= styleOld
)
172 ::SetWindowLong(hwnd
, GWL_STYLE
, styleNew
);
175 wxSize
wxMSWButton::GetFittingSize(wxWindow
*win
, const wxSize
& sizeLabel
)
177 // FIXME: this is pure guesswork, need to retrieve the real button margins
178 wxSize sizeBtn
= sizeLabel
;
180 sizeBtn
.x
+= 3*win
->GetCharWidth();
181 sizeBtn
.y
= 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel
.y
)/10;
186 wxSize
wxMSWButton::ComputeBestSize(wxControl
*btn
)
191 dc
.GetMultiLineTextExtent(btn
->GetLabelText(), &sizeBtn
.x
, &sizeBtn
.y
);
193 sizeBtn
= GetFittingSize(btn
, sizeBtn
);
195 // all buttons have at least the standard size unless the user explicitly
196 // wants them to be of smaller size and used wxBU_EXACTFIT style when
197 // creating the button
198 if ( !btn
->HasFlag(wxBU_EXACTFIT
) )
200 wxSize sizeDef
= wxButton::GetDefaultSize();
201 if ( sizeBtn
.x
< sizeDef
.x
)
202 sizeBtn
.x
= sizeDef
.x
;
203 if ( sizeBtn
.y
< sizeDef
.y
)
204 sizeBtn
.y
= sizeDef
.y
;
207 btn
->CacheBestSize(sizeBtn
);
212 // ----------------------------------------------------------------------------
213 // creation/destruction
214 // ----------------------------------------------------------------------------
216 bool wxButton::Create(wxWindow
*parent
,
222 const wxValidator
& validator
,
223 const wxString
& name
)
226 if (label
.empty() && wxIsStockID(id
))
228 // On Windows, some buttons aren't supposed to have mnemonics
229 label
= wxGetStockLabel
232 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
234 : wxSTOCK_WITH_MNEMONIC
238 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
242 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
244 // if the label contains several lines we must explicitly tell the button
245 // about it or it wouldn't draw it correctly ("\n"s would just appear as
248 // NB: we do it here and not in MSWGetStyle() because we need the label
249 // value and the label is not set yet when MSWGetStyle() is called
250 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
252 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
255 wxButton::~wxButton()
257 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
258 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
264 // ----------------------------------------------------------------------------
266 // ----------------------------------------------------------------------------
268 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
270 // buttons never have an external border, they draw their own one
271 WXDWORD msStyle
= wxControl::MSWGetStyle
273 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
276 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
277 // each other in any resizeable dialog which has more than one button in
279 msStyle
|= WS_CLIPSIBLINGS
;
281 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
282 // and wxBU_RIGHT to get BS_CENTER!
283 if ( style
& wxBU_LEFT
)
285 if ( style
& wxBU_RIGHT
)
287 if ( style
& wxBU_TOP
)
289 if ( style
& wxBU_BOTTOM
)
290 msStyle
|= BS_BOTTOM
;
293 if ( style
& wxNO_BORDER
)
295 #endif // __WXWINCE__
300 void wxButton::SetLabel(const wxString
& label
)
302 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
304 wxButtonBase::SetLabel(label
);
307 // ----------------------------------------------------------------------------
308 // size management including autosizing
309 // ----------------------------------------------------------------------------
311 wxSize
wxButton::DoGetBestSize() const
313 return wxMSWButton::ComputeBestSize(const_cast<wxButton
*>(this));
317 wxSize
wxButtonBase::GetDefaultSize()
319 static wxSize s_sizeBtn
;
321 if ( s_sizeBtn
.x
== 0 )
324 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
326 // the size of a standard button in the dialog units is 50x14,
327 // translate this to pixels
328 // NB1: the multipliers come from the Windows convention
329 // NB2: the extra +1/+2 were needed to get the size be the same as the
330 // size of the buttons in the standard dialog - I don't know how
331 // this happens, but on my system this size is 75x23 in pixels and
332 // 23*8 isn't even divisible by 14... Would be nice to understand
333 // why these constants are needed though!
334 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
335 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
341 // ----------------------------------------------------------------------------
342 // default button handling
343 // ----------------------------------------------------------------------------
346 "Everything you ever wanted to know about the default buttons" or "Why do we
347 have to do all this?"
349 In MSW the default button should be activated when the user presses Enter
350 and the current control doesn't process Enter itself somehow. This is
351 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
352 Another aspect of "defaultness" is that the default button has different
353 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
354 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
355 be unset if our parent window is not active so it should be unset whenever
356 we lose activation and set back when we regain it.
358 Final complication is that when a button is active, it should be the default
359 one, i.e. pressing Enter on a button always activates it and not another
362 We handle this by maintaining a permanent and a temporary default items in
363 wxControlContainer (both may be NULL). When a button becomes the current
364 control (i.e. gets focus) it sets itself as the temporary default which
365 ensures that it has the right appearance and that Enter will be redirected
366 to it. When the button loses focus, it unsets the temporary default and so
367 the default item will be the permanent default -- that is the default button
368 if any had been set or none otherwise, which is just what we want.
370 NB: all this is quite complicated by now and the worst is that normally
371 it shouldn't be necessary at all as for the normal Windows programs
372 DefWindowProc() and IsDialogMessage() take care of all this
373 automatically -- however in wxWidgets programs this doesn't work for
374 nested hierarchies (i.e. a notebook inside a notebook) for unknown
375 reason and so we have to reproduce all this code ourselves. It would be
376 very nice if we could avoid doing it.
379 // set this button as the (permanently) default one in its panel
380 wxWindow
*wxButton::SetDefault()
382 // set this one as the default button both for wxWidgets ...
383 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
386 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
387 SetDefaultStyle(this, true);
389 return winOldDefault
;
392 // return the top level parent window if it's not being deleted yet, otherwise
394 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
398 // IsTopLevel() will return false for a wxTLW being deleted, so we also
399 // need the parent test for this case
400 wxWindow
* const parent
= win
->GetParent();
401 if ( !parent
|| win
->IsTopLevel() )
403 if ( win
->IsBeingDeleted() )
412 wxASSERT_MSG( win
, _T("button without top level parent?") );
414 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
415 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
420 // set this button as being currently default
421 void wxButton::SetTmpDefault()
423 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
427 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
428 tlw
->SetTmpDefaultItem(this);
430 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
431 SetDefaultStyle(this, true);
434 // unset this button as currently default, it may still stay permanent default
435 void wxButton::UnsetTmpDefault()
437 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
441 tlw
->SetTmpDefaultItem(NULL
);
443 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
445 SetDefaultStyle(this, false);
446 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
451 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
453 // we may be called with NULL pointer -- simpler to do the check here than
454 // in the caller which does wxDynamicCast()
458 // first, let DefDlgProc() know about the new default button
461 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
462 // focus at all any more
463 if ( !wxTheApp
->IsActive() )
466 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
467 wxCHECK_RET( tlw
, _T("button without top level window?") );
469 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
471 // sending DM_SETDEFID also changes the button style to
472 // BS_DEFPUSHBUTTON so there is nothing more to do
475 // then also change the style as needed
476 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
477 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
479 // don't do it with the owner drawn buttons because it will
480 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
481 // BS_DEFPUSHBUTTON != 0)!
482 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
484 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
485 on
? style
| BS_DEFPUSHBUTTON
486 : style
& ~BS_DEFPUSHBUTTON
,
491 // redraw the button - it will notice itself that it's
492 // [not] the default one [any longer]
496 //else: already has correct style
499 // ----------------------------------------------------------------------------
501 // ----------------------------------------------------------------------------
503 bool wxButton::SendClickEvent()
505 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
506 event
.SetEventObject(this);
508 return ProcessCommand(event
);
511 void wxButton::Command(wxCommandEvent
& event
)
513 ProcessCommand(event
);
516 // ----------------------------------------------------------------------------
517 // event/message handlers
518 // ----------------------------------------------------------------------------
520 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
522 bool processed
= false;
525 // NOTE: Apparently older versions (NT 4?) of the common controls send
526 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
527 // buttons, so in order to send two EVT_BUTTON events we should
528 // catch both types. Currently (Feb 2003) up-to-date versions of
529 // win98, win2k and winXP all send two BN_CLICKED messages for
530 // all button types, so we don't catch BN_DOUBLECLICKED anymore
531 // in order to not get 3 EVT_BUTTON events. If this is a problem
532 // then we need to figure out which version of the comctl32 changed
533 // this behaviour and test for it.
535 case 1: // message came from an accelerator
536 case BN_CLICKED
: // normal buttons send this
537 processed
= SendClickEvent();
544 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
546 // when we receive focus, we want to temporarily become the default button in
547 // our parent panel so that pressing "Enter" would activate us -- and when
548 // losing it we should restore the previous default button as well
549 if ( nMsg
== WM_SETFOCUS
)
553 // let the default processing take place too
555 else if ( nMsg
== WM_KILLFOCUS
)
559 else if ( nMsg
== WM_LBUTTONDBLCLK
)
561 // emulate a click event to force an owner-drawn button to change its
562 // appearance - without this, it won't do it
563 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
565 // and continue with processing the message normally as well
568 else if ( nMsg
== WM_THEMECHANGED
)
570 // need to recalculate the best size here
571 // as the theme size might have changed
572 InvalidateBestSize();
574 else if ( wxUxThemeEngine::GetIfActive() )
576 // we need to Refresh() if mouse has entered or left window
577 // so we can update the hot tracking state
578 // must use m_mouseInWindow here instead of IsMouseInWindow()
579 // since we need to know the first time the mouse enters the window
580 // and IsMouseInWindow() would return true in this case
581 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
582 nMsg
== WM_MOUSELEAVE
)
587 #endif // wxUSE_UXTHEME
589 // let the base class do all real processing
590 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
593 // ----------------------------------------------------------------------------
594 // owner-drawn buttons support
595 // ----------------------------------------------------------------------------
599 static void DrawButtonText(HDC hdc
,
601 const wxString
& text
,
605 COLORREF colOld
= SetTextColor(hdc
, col
);
606 int modeOld
= SetBkMode(hdc
, TRANSPARENT
);
608 // center text horizontally in any case
611 if ( text
.find(_T('\n')) != wxString::npos
)
613 // draw multiline label
615 // first we need to compute its bounding rect
617 ::CopyRect(&rc
, pRect
);
618 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
619 DT_CENTER
| DT_CALCRECT
);
621 // now center this rect inside the entire button area
622 const LONG w
= rc
.right
- rc
.left
;
623 const LONG h
= rc
.bottom
- rc
.top
;
624 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
625 rc
.right
= rc
.left
+w
;
626 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
627 rc
.bottom
= rc
.top
+h
;
629 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, flags
);
631 else // single line label
633 // centre text vertically too (notice that we must have DT_SINGLELINE
634 // for DT_VCENTER to work)
635 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
636 flags
| DT_SINGLELINE
| DT_VCENTER
);
639 SetBkMode(hdc
, modeOld
);
640 SetTextColor(hdc
, colOld
);
643 static void DrawRect(HDC hdc
, const RECT
& r
)
645 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
646 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
647 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
648 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
651 void wxButton::MakeOwnerDrawn()
653 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
654 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
657 style
|= BS_OWNERDRAW
;
658 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
662 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
664 if ( !wxControl::SetBackgroundColour(colour
) )
677 bool wxButton::SetForegroundColour(const wxColour
&colour
)
679 if ( !wxControl::SetForegroundColour(colour
) )
693 The button frame looks like this normally:
696 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
697 WH GB H = light grey (LIGHT)
698 WH GB G = dark grey (SHADOW)
699 WH GB B = black (DKSHADOW)
704 When the button is selected, the button becomes like this (the total button
705 size doesn't change):
716 When the button is pushed (while selected) it is like:
728 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
,
729 bool selected
, bool pushed
)
732 CopyRect(&r
, &rectBtn
);
734 HPEN hpenBlack
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)),
735 hpenGrey
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)),
736 hpenLightGr
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)),
737 hpenWhite
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
));
739 HPEN hpenOld
= (HPEN
)SelectObject(hdc
, hpenBlack
);
748 (void)SelectObject(hdc
, hpenGrey
);
749 ::InflateRect(&r
, -1, -1);
759 ::InflateRect(&r
, -1, -1);
762 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
763 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
765 (void)SelectObject(hdc
, hpenWhite
);
766 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
767 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
769 (void)SelectObject(hdc
, hpenLightGr
);
770 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
771 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
773 (void)SelectObject(hdc
, hpenGrey
);
774 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
775 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
778 (void)SelectObject(hdc
, hpenOld
);
779 DeleteObject(hpenWhite
);
780 DeleteObject(hpenLightGr
);
781 DeleteObject(hpenGrey
);
782 DeleteObject(hpenBlack
);
787 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
789 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
790 HDC hdc
= lpDIS
->hDC
;
791 UINT state
= lpDIS
->itemState
;
793 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
795 wxUxThemeHandle
theme(button
, L
"BUTTON");
798 if ( state
& ODS_SELECTED
)
800 iState
= PBS_PRESSED
;
802 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
806 else if ( state
& ODS_FOCUS
)
808 iState
= PBS_DEFAULTED
;
810 else if ( state
& ODS_DISABLED
)
812 iState
= PBS_DISABLED
;
819 // draw parent background if needed
820 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
824 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
828 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
831 // calculate content area margins
833 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
834 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
836 ::CopyRect(&rectClient
, &rectBtn
);
837 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
839 // if focused and !nofocus rect
840 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
842 DrawFocusRect(hdc
, &rectClient
);
845 if ( button
->UseBgCol() )
847 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
848 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
850 // don't overwrite the focus rect
851 ::InflateRect(&rectClient
, -1, -1);
852 FillRect(hdc
, &rectClient
, hbrushBackground
);
853 ::DeleteObject(hbrushBackground
);
856 #endif // wxUSE_UXTHEME
858 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
860 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
861 HDC hdc
= lpDIS
->hDC
;
862 UINT state
= lpDIS
->itemState
;
864 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
867 if ( wxUxThemeEngine::GetIfActive() )
869 MSWDrawXPBackground(this, wxdis
);
872 #endif // wxUSE_UXTHEME
874 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
876 // first, draw the background
877 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
878 FillRect(hdc
, &rectBtn
, hbrushBackground
);
879 ::DeleteObject(hbrushBackground
);
881 // draw the border for the current state
882 bool selected
= (state
& ODS_SELECTED
) != 0;
885 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
888 selected
= tlw
->GetDefaultItem() == this;
891 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
893 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
895 // if focused and !nofocus rect
896 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
899 CopyRect(&rectFocus
, &rectBtn
);
901 // I don't know where does this constant come from, but this is how
902 // Windows draws them
903 InflateRect(&rectFocus
, -4, -4);
905 DrawFocusRect(hdc
, &rectFocus
);
910 // the label is shifted by 1 pixel to create "pushed" effect
911 OffsetRect(&rectBtn
, 1, 1);
915 COLORREF colFg
= state
& ODS_DISABLED
916 ? ::GetSysColor(COLOR_GRAYTEXT
)
917 : wxColourToRGB(GetForegroundColour());
919 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000) systems
920 // but by happy coincidence ODS_NOACCEL is not used under them neither so
921 // DT_HIDEPREFIX should never be used there
922 DrawButtonText(hdc
, &rectBtn
, GetLabel(), colFg
,
923 state
& ODS_NOACCEL
? DT_HIDEPREFIX
: 0);
928 #endif // wxUSE_BUTTON