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