1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/button.cpp 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/button.h" 
  35     #include "wx/bmpbuttn.h" 
  36     #include "wx/settings.h" 
  37     #include "wx/dcscreen.h" 
  38     #include "wx/dcclient.h" 
  39     #include "wx/toplevel.h" 
  42 #include "wx/stockitem.h" 
  43 #include "wx/tokenzr.h" 
  44 #include "wx/msw/private.h" 
  47     #include "wx/msw/uxtheme.h" 
  49     // no need to include tmschema.h 
  51         #define BP_PUSHBUTTON 1 
  56         #define PBS_DISABLED  4 
  57         #define PBS_DEFAULTED 5 
  59         #define TMT_CONTENTMARGINS 3602 
  61 #endif // wxUSE_UXTHEME 
  63 #ifndef WM_THEMECHANGED 
  64     #define WM_THEMECHANGED     0x031A 
  68     #define ODS_NOACCEL         0x0100 
  71 #ifndef ODS_NOFOCUSRECT 
  72     #define ODS_NOFOCUSRECT     0x0200 
  75 // ---------------------------------------------------------------------------- 
  77 // ---------------------------------------------------------------------------- 
  79 #if wxUSE_EXTENDED_RTTI 
  81 WX_DEFINE_FLAGS( wxButtonStyle 
) 
  83 wxBEGIN_FLAGS( wxButtonStyle 
) 
  84     // new style border flags, we put them first to 
  85     // use them for streaming out 
  86     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  87     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  88     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  89     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  90     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  91     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  93     // old style border flags 
  94     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  95     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  96     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  97     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  98     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  99     wxFLAGS_MEMBER(wxBORDER
) 
 101     // standard window styles 
 102     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 103     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 104     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 105     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 106     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
 107     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 108     wxFLAGS_MEMBER(wxVSCROLL
) 
 109     wxFLAGS_MEMBER(wxHSCROLL
) 
 111     wxFLAGS_MEMBER(wxBU_LEFT
) 
 112     wxFLAGS_MEMBER(wxBU_RIGHT
) 
 113     wxFLAGS_MEMBER(wxBU_TOP
) 
 114     wxFLAGS_MEMBER(wxBU_BOTTOM
) 
 115     wxFLAGS_MEMBER(wxBU_EXACTFIT
) 
 116 wxEND_FLAGS( wxButtonStyle 
) 
 118 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton
, wxControl
,"wx/button.h") 
 120 wxBEGIN_PROPERTIES_TABLE(wxButton
) 
 121     wxEVENT_PROPERTY( Click 
, wxEVT_COMMAND_BUTTON_CLICKED 
, wxCommandEvent
) 
 123     wxPROPERTY( Font 
, wxFont 
, SetFont 
, GetFont  
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 124     wxPROPERTY( Label
, wxString 
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") ) 
 126     wxPROPERTY_FLAGS( WindowStyle 
, wxButtonStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 128 wxEND_PROPERTIES_TABLE() 
 130 wxBEGIN_HANDLERS_TABLE(wxButton
) 
 131 wxEND_HANDLERS_TABLE() 
 133 wxCONSTRUCTOR_6( wxButton 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxString 
, Label 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle  
) 
 137 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
) 
 140 // this macro tries to adjust the default button height to a reasonable value 
 141 // using the char height as the base 
 142 #define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10) 
 144 // ============================================================================ 
 146 // ============================================================================ 
 148 // ---------------------------------------------------------------------------- 
 149 // creation/destruction 
 150 // ---------------------------------------------------------------------------- 
 152 bool wxButton::Create(wxWindow 
*parent
, 
 158                       const wxValidator
& validator
, 
 159                       const wxString
& name
) 
 162     if (label
.empty() && wxIsStockID(id
)) 
 164         // On Windows, some buttons aren't supposed to have 
 165         // mnemonics, so strip them out. 
 167         label 
= wxGetStockLabel(id
 
 168 #if defined(__WXMSW__) || defined(__WXWINCE__) 
 176     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 180     WXDWORD msStyle 
= MSWGetStyle(style
, &exstyle
); 
 183     // if the label contains several lines we must explicitly tell the button 
 184     // about it or it wouldn't draw it correctly ("\n"s would just appear as 
 187     // NB: we do it here and not in MSWGetStyle() because we need the label 
 188     //     value and m_label is not set yet when MSWGetStyle() is called; 
 189     //     besides changing BS_MULTILINE during run-time is pointless anyhow 
 190     if ( label
.find(_T('\n')) != wxString::npos 
) 
 192         msStyle 
|= BS_MULTILINE
; 
 196     return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
); 
 199 wxButton::~wxButton() 
 201     wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 202     if ( tlw 
&& tlw
->GetTmpDefaultItem() == this ) 
 208 // ---------------------------------------------------------------------------- 
 210 // ---------------------------------------------------------------------------- 
 212 WXDWORD 
wxButton::MSWGetStyle(long style
, WXDWORD 
*exstyle
) const 
 214     // buttons never have an external border, they draw their own one 
 215     WXDWORD msStyle 
= wxControl::MSWGetStyle
 
 217                         (style 
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
 
 220     // we must use WS_CLIPSIBLINGS with the buttons or they would draw over 
 221     // each other in any resizeable dialog which has more than one button in 
 223     msStyle 
|= WS_CLIPSIBLINGS
; 
 226     // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT 
 227     // and wxBU_RIGHT to get BS_CENTER! 
 228     if ( style 
& wxBU_LEFT 
) 
 230     if ( style 
& wxBU_RIGHT 
) 
 232     if ( style 
& wxBU_TOP 
) 
 234     if ( style 
& wxBU_BOTTOM 
) 
 235         msStyle 
|= BS_BOTTOM
; 
 238     if ( style 
& wxNO_BORDER 
) 
 240 #endif // __WXWINCE__ 
 246 // ---------------------------------------------------------------------------- 
 247 // size management including autosizing 
 248 // ---------------------------------------------------------------------------- 
 250 wxSize 
wxButton::DoGetBestSize() const 
 252     wxClientDC 
dc(wx_const_cast(wxButton 
*, this)); 
 253     dc
.SetFont(GetFont()); 
 257     dc
.GetMultiLineTextExtent(GetLabelText(), &wBtn
, &hBtn
); 
 259     // add a margin -- the button is wider than just its label 
 260     wBtn 
+= 3*GetCharWidth(); 
 261     hBtn 
= BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hBtn
); 
 263     // all buttons have at least the standard size unless the user explicitly 
 264     // wants them to be of smaller size and used wxBU_EXACTFIT style when 
 265     // creating the button 
 266     if ( !HasFlag(wxBU_EXACTFIT
) ) 
 268         wxSize sz 
= GetDefaultSize(); 
 277     wxSize 
best(wBtn
, hBtn
); 
 283 wxSize 
wxButtonBase::GetDefaultSize() 
 285     static wxSize s_sizeBtn
; 
 287     if ( s_sizeBtn
.x 
== 0 ) 
 290         dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
)); 
 292         // the size of a standard button in the dialog units is 50x14, 
 293         // translate this to pixels 
 294         // NB1: the multipliers come from the Windows convention 
 295         // NB2: the extra +1/+2 were needed to get the size be the same as the 
 296         //      size of the buttons in the standard dialog - I don't know how 
 297         //      this happens, but on my system this size is 75x23 in pixels and 
 298         //      23*8 isn't even divisible by 14... Would be nice to understand 
 299         //      why these constants are needed though! 
 300         s_sizeBtn
.x 
= (50 * (dc
.GetCharWidth() + 1))/4; 
 301         s_sizeBtn
.y 
= ((14 * dc
.GetCharHeight()) + 2)/8; 
 307 // ---------------------------------------------------------------------------- 
 308 // default button handling 
 309 // ---------------------------------------------------------------------------- 
 312    "Everything you ever wanted to know about the default buttons" or "Why do we 
 313    have to do all this?" 
 315    In MSW the default button should be activated when the user presses Enter 
 316    and the current control doesn't process Enter itself somehow. This is 
 317    handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID 
 318    Another aspect of "defaultness" is that the default button has different 
 319    appearance: this is due to BS_DEFPUSHBUTTON style which is completely 
 320    separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should 
 321    be unset if our parent window is not active so it should be unset whenever 
 322    we lose activation and set back when we regain it. 
 324    Final complication is that when a button is active, it should be the default 
 325    one, i.e. pressing Enter on a button always activates it and not another 
 328    We handle this by maintaining a permanent and a temporary default items in 
 329    wxControlContainer (both may be NULL). When a button becomes the current 
 330    control (i.e. gets focus) it sets itself as the temporary default which 
 331    ensures that it has the right appearance and that Enter will be redirected 
 332    to it. When the button loses focus, it unsets the temporary default and so 
 333    the default item will be the permanent default -- that is the default button 
 334    if any had been set or none otherwise, which is just what we want. 
 336    NB: all this is quite complicated by now and the worst is that normally 
 337        it shouldn't be necessary at all as for the normal Windows programs 
 338        DefWindowProc() and IsDialogMessage() take care of all this 
 339        automatically -- however in wxWidgets programs this doesn't work for 
 340        nested hierarchies (i.e. a notebook inside a notebook) for unknown 
 341        reason and so we have to reproduce all this code ourselves. It would be 
 342        very nice if we could avoid doing it. 
 345 // set this button as the (permanently) default one in its panel 
 346 void wxButton::SetDefault() 
 348     wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 350     wxCHECK_RET( tlw
, _T("button without top level window?") ); 
 352     // set this one as the default button both for wxWidgets ... 
 353     wxWindow 
*winOldDefault 
= tlw
->SetDefaultItem(this); 
 356     SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false); 
 357     SetDefaultStyle(this, true); 
 360 // set this button as being currently default 
 361 void wxButton::SetTmpDefault() 
 363     wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 365     wxCHECK_RET( tlw
, _T("button without top level window?") ); 
 367     wxWindow 
*winOldDefault 
= tlw
->GetDefaultItem(); 
 368     tlw
->SetTmpDefaultItem(this); 
 370     SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false); 
 371     SetDefaultStyle(this, true); 
 374 // unset this button as currently default, it may still stay permanent default 
 375 void wxButton::UnsetTmpDefault() 
 377     wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 379     wxCHECK_RET( tlw
, _T("button without top level window?") ); 
 381     tlw
->SetTmpDefaultItem(NULL
); 
 383     wxWindow 
*winOldDefault 
= tlw
->GetDefaultItem(); 
 385     SetDefaultStyle(this, false); 
 386     SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true); 
 391 wxButton::SetDefaultStyle(wxButton 
*btn
, bool on
) 
 393     // we may be called with NULL pointer -- simpler to do the check here than 
 394     // in the caller which does wxDynamicCast() 
 398     // first, let DefDlgProc() know about the new default button 
 401         // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have 
 402         // focus at all any more 
 403         if ( !wxTheApp
->IsActive() ) 
 406         wxWindow 
* const tlw 
= wxGetTopLevelParent(btn
); 
 407         wxCHECK_RET( tlw
, _T("button without top level window?") ); 
 409         ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L); 
 411         // sending DM_SETDEFID also changes the button style to 
 412         // BS_DEFPUSHBUTTON so there is nothing more to do 
 415     // then also change the style as needed 
 416     long style 
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
); 
 417     if ( !(style 
& BS_DEFPUSHBUTTON
) == on 
) 
 419         // don't do it with the owner drawn buttons because it will 
 420         // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW & 
 421         // BS_DEFPUSHBUTTON != 0)! 
 422         if ( (style 
& BS_OWNERDRAW
) != BS_OWNERDRAW 
) 
 424             ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
, 
 425                           on 
? style 
| BS_DEFPUSHBUTTON
 
 426                              : style 
& ~BS_DEFPUSHBUTTON
, 
 431             // redraw the button - it will notice itself that it's 
 432             // [not] the default one [any longer] 
 436     //else: already has correct style 
 439 // ---------------------------------------------------------------------------- 
 441 // ---------------------------------------------------------------------------- 
 443 bool wxButton::SendClickEvent() 
 445     wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId()); 
 446     event
.SetEventObject(this); 
 448     return ProcessCommand(event
); 
 451 void wxButton::Command(wxCommandEvent 
& event
) 
 453     ProcessCommand(event
); 
 456 // ---------------------------------------------------------------------------- 
 457 // event/message handlers 
 458 // ---------------------------------------------------------------------------- 
 460 bool wxButton::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 462     bool processed 
= false; 
 465         // NOTE: Apparently older versions (NT 4?) of the common controls send 
 466         //       BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn 
 467         //       buttons, so in order to send two EVT_BUTTON events we should 
 468         //       catch both types.  Currently (Feb 2003) up-to-date versions of 
 469         //       win98, win2k and winXP all send two BN_CLICKED messages for 
 470         //       all button types, so we don't catch BN_DOUBLECLICKED anymore 
 471         //       in order to not get 3 EVT_BUTTON events.  If this is a problem 
 472         //       then we need to figure out which version of the comctl32 changed 
 473         //       this behaviour and test for it. 
 475         case 1:                     // message came from an accelerator 
 476         case BN_CLICKED
:            // normal buttons send this 
 477             processed 
= SendClickEvent(); 
 484 WXLRESULT 
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 486     // when we receive focus, we want to temporarily become the default button in 
 487     // our parent panel so that pressing "Enter" would activate us -- and when 
 488     // losing it we should restore the previous default button as well 
 489     if ( nMsg 
== WM_SETFOCUS 
) 
 493         // let the default processing take place too 
 495     else if ( nMsg 
== WM_KILLFOCUS 
) 
 499     else if ( nMsg 
== WM_LBUTTONDBLCLK 
) 
 501         // emulate a click event to force an owner-drawn button to change its 
 502         // appearance - without this, it won't do it 
 503         (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
); 
 505         // and continue with processing the message normally as well 
 508     else if ( nMsg 
== WM_THEMECHANGED 
) 
 510         // need to recalculate the best size here 
 511         // as the theme size might have changed 
 512         InvalidateBestSize(); 
 514     else if ( wxUxThemeEngine::GetIfActive() ) 
 516         // we need to Refresh() if mouse has entered or left window 
 517         // so we can update the hot tracking state 
 518         // must use m_mouseInWindow here instead of IsMouseInWindow() 
 519         // since we need to know the first time the mouse enters the window 
 520         // and IsMouseInWindow() would return true in this case 
 521         if ( ( nMsg 
== WM_MOUSEMOVE 
&& !m_mouseInWindow 
) || 
 522              nMsg 
== WM_MOUSELEAVE 
) 
 527 #endif // wxUSE_UXTHEME 
 529     // let the base class do all real processing 
 530     return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
 533 // ---------------------------------------------------------------------------- 
 534 // owner-drawn buttons support 
 535 // ---------------------------------------------------------------------------- 
 541 static void DrawButtonText(HDC hdc
, 
 543                            const wxString
& text
, 
 546     COLORREF colOld 
= SetTextColor(hdc
, col
); 
 547     int modeOld 
= SetBkMode(hdc
, TRANSPARENT
); 
 549     if ( text
.find(_T('\n')) != wxString::npos 
) 
 551         // draw multiline label 
 553         // first we need to compute its bounding rect 
 555         ::CopyRect(&rc
, pRect
); 
 556         ::DrawText(hdc
, text
, text
.length(), &rc
, DT_CENTER 
| DT_CALCRECT
); 
 558         // now center this rect inside the entire button area 
 559         const LONG w 
= rc
.right 
- rc
.left
; 
 560         const LONG h 
= rc
.bottom 
- rc
.top
; 
 561         rc
.left 
= (pRect
->right 
- pRect
->left
)/2 - w
/2; 
 562         rc
.right 
= rc
.left
+w
; 
 563         rc
.top 
= (pRect
->bottom 
- pRect
->top
)/2 - h
/2; 
 564         rc
.bottom 
= rc
.top
+h
; 
 566         ::DrawText(hdc
, text
, text
.length(), &rc
, DT_CENTER
); 
 568     else // single line label 
 570         // Note: we must have DT_SINGLELINE for DT_VCENTER to work. 
 571         ::DrawText(hdc
, text
, text
.length(), pRect
, 
 572                    DT_SINGLELINE 
| DT_CENTER 
| DT_VCENTER
); 
 575     SetBkMode(hdc
, modeOld
); 
 576     SetTextColor(hdc
, colOld
); 
 579 static void DrawRect(HDC hdc
, const RECT
& r
) 
 581     wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
); 
 582     wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
); 
 583     wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
); 
 584     wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
); 
 587 void wxButton::MakeOwnerDrawn() 
 589     long style 
= GetWindowLong(GetHwnd(), GWL_STYLE
); 
 590     if ( (style 
& BS_OWNERDRAW
) != BS_OWNERDRAW 
) 
 593         style 
|= BS_OWNERDRAW
; 
 594         SetWindowLong(GetHwnd(), GWL_STYLE
, style
); 
 598 bool wxButton::SetBackgroundColour(const wxColour 
&colour
) 
 600     if ( !wxControl::SetBackgroundColour(colour
) ) 
 613 bool wxButton::SetForegroundColour(const wxColour 
&colour
) 
 615     if ( !wxControl::SetForegroundColour(colour
) ) 
 629    The button frame looks like this normally: 
 632    WHHHHHHHHHHHHHHHHGB  W = white       (HILIGHT) 
 633    WH               GB  H = light grey  (LIGHT) 
 634    WH               GB  G = dark grey   (SHADOW) 
 635    WH               GB  B = black       (DKSHADOW) 
 640    When the button is selected, the button becomes like this (the total button 
 641    size doesn't change): 
 652    When the button is pushed (while selected) it is like: 
 664 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
, 
 665                             bool selected
, bool pushed
) 
 668     CopyRect(&r
, &rectBtn
); 
 670     HPEN hpenBlack   
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)), 
 671          hpenGrey    
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)), 
 672          hpenLightGr 
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)), 
 673          hpenWhite   
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
)); 
 675     HPEN hpenOld 
= (HPEN
)SelectObject(hdc
, hpenBlack
); 
 684         (void)SelectObject(hdc
, hpenGrey
); 
 685         ::InflateRect(&r
, -1, -1); 
 695             ::InflateRect(&r
, -1, -1); 
 698         wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
); 
 699         wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top 
- 1); 
 701         (void)SelectObject(hdc
, hpenWhite
); 
 702         wxDrawLine(hdc
, r
.left
, r
.bottom 
- 1, r
.left
, r
.top
); 
 703         wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
); 
 705         (void)SelectObject(hdc
, hpenLightGr
); 
 706         wxDrawLine(hdc
, r
.left 
+ 1, r
.bottom 
- 2, r
.left 
+ 1, r
.top 
+ 1); 
 707         wxDrawLine(hdc
, r
.left 
+ 1, r
.top 
+ 1, r
.right 
- 1, r
.top 
+ 1); 
 709         (void)SelectObject(hdc
, hpenGrey
); 
 710         wxDrawLine(hdc
, r
.left 
+ 1, r
.bottom 
- 1, r
.right 
- 1, r
.bottom 
- 1); 
 711         wxDrawLine(hdc
, r
.right 
- 1, r
.bottom 
- 1, r
.right 
- 1, r
.top
); 
 714     (void)SelectObject(hdc
, hpenOld
); 
 715     DeleteObject(hpenWhite
); 
 716     DeleteObject(hpenLightGr
); 
 717     DeleteObject(hpenGrey
); 
 718     DeleteObject(hpenBlack
); 
 723 void MSWDrawXPBackground(wxButton 
*button
, WXDRAWITEMSTRUCT 
*wxdis
) 
 725     LPDRAWITEMSTRUCT lpDIS 
= (LPDRAWITEMSTRUCT
)wxdis
; 
 726     HDC hdc 
= lpDIS
->hDC
; 
 727     UINT state 
= lpDIS
->itemState
; 
 729     CopyRect(&rectBtn
, &lpDIS
->rcItem
); 
 731     wxUxThemeHandle 
theme(button
, L
"BUTTON"); 
 734     if ( state 
& ODS_SELECTED 
) 
 736         iState 
= PBS_PRESSED
; 
 738     else if ( button
->HasCapture() || button
->IsMouseInWindow() ) 
 742     else if ( state 
& ODS_FOCUS 
) 
 744         iState 
= PBS_DEFAULTED
; 
 746     else if ( state 
& ODS_DISABLED 
) 
 748         iState 
= PBS_DISABLED
; 
 755     // draw parent background if needed 
 756     if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
, 
 760         wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
); 
 764     wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
, 
 767     // calculate content area margins 
 769     wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
, 
 770                                             TMT_CONTENTMARGINS
, &rectBtn
, &margins
); 
 772     ::CopyRect(&rectClient
, &rectBtn
); 
 773     ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
); 
 775     // if focused and !nofocus rect 
 776     if ( (state 
& ODS_FOCUS
) && !(state 
& ODS_NOFOCUSRECT
) ) 
 778         DrawFocusRect(hdc
, &rectClient
); 
 781     if ( button
->UseBgCol() ) 
 783         COLORREF colBg 
= wxColourToRGB(button
->GetBackgroundColour()); 
 784         HBRUSH hbrushBackground 
= ::CreateSolidBrush(colBg
); 
 786         // don't overwrite the focus rect 
 787         ::InflateRect(&rectClient
, -1, -1); 
 788         FillRect(hdc
, &rectClient
, hbrushBackground
); 
 789         ::DeleteObject(hbrushBackground
); 
 792 #endif // wxUSE_UXTHEME 
 794 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT 
*wxdis
) 
 796     LPDRAWITEMSTRUCT lpDIS 
= (LPDRAWITEMSTRUCT
)wxdis
; 
 797     HDC hdc 
= lpDIS
->hDC
; 
 798     UINT state 
= lpDIS
->itemState
; 
 800     CopyRect(&rectBtn
, &lpDIS
->rcItem
); 
 803     if ( wxUxThemeEngine::GetIfActive() ) 
 805         MSWDrawXPBackground(this, wxdis
); 
 808 #endif // wxUSE_UXTHEME 
 810         COLORREF colBg 
= wxColourToRGB(GetBackgroundColour()); 
 812         // first, draw the background 
 813         HBRUSH hbrushBackground 
= ::CreateSolidBrush(colBg
); 
 814         FillRect(hdc
, &rectBtn
, hbrushBackground
); 
 815         ::DeleteObject(hbrushBackground
); 
 817         // draw the border for the current state 
 818         bool selected 
= (state 
& ODS_SELECTED
) != 0; 
 821             wxTopLevelWindow 
*tlw 
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
); 
 824                 selected 
= tlw
->GetDefaultItem() == this; 
 827         bool pushed 
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0; 
 829         DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
); 
 831         // if focused and !nofocus rect 
 832         if ( (state 
& ODS_FOCUS
) && !(state 
& ODS_NOFOCUSRECT
) ) 
 835             CopyRect(&rectFocus
, &rectBtn
); 
 837             // I don't know where does this constant come from, but this is how 
 838             // Windows draws them 
 839             InflateRect(&rectFocus
, -4, -4); 
 841             DrawFocusRect(hdc
, &rectFocus
); 
 846             // the label is shifted by 1 pixel to create "pushed" effect 
 847             OffsetRect(&rectBtn
, 1, 1); 
 851     COLORREF colFg 
= wxColourToRGB(GetForegroundColour()); 
 852     DrawButtonText(hdc
, &rectBtn
, 
 853                    state 
& ODS_NOACCEL 
? GetLabelText() 
 855                    state 
& ODS_DISABLED 
? GetSysColor(COLOR_GRAYTEXT
) 
 863 #endif // wxUSE_BUTTON