1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        msw/button.cpp 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart and Markus Holzem 
   9 // Licence:     wxWindows license 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  21     #pragma implementation "button.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  32     #include "wx/button.h" 
  35     #include "wx/bmpbuttn.h" 
  36     #include "wx/settings.h" 
  37     #include "wx/dcscreen.h" 
  40 #include "wx/msw/private.h" 
  42 // ---------------------------------------------------------------------------- 
  44 // ---------------------------------------------------------------------------- 
  46 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
) 
  48 // this macro tries to adjust the default button height to a reasonable value 
  49 // using the char height as the base 
  50 #define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10) 
  52 // ============================================================================ 
  54 // ============================================================================ 
  56 // ---------------------------------------------------------------------------- 
  57 // creation/destruction 
  58 // ---------------------------------------------------------------------------- 
  60 bool wxButton::Create(wxWindow 
*parent
, 
  62                       const wxString
& label
, 
  66                       const wxValidator
& validator
, 
  69     if ( !CreateBase(parent
, id
, pos
, size
, style
, validator
, name
) ) 
  72     parent
->AddChild((wxButton 
*)this); 
  74     m_backgroundColour 
= parent
->GetBackgroundColour(); 
  75     m_foregroundColour 
= parent
->GetForegroundColour(); 
  77     long msStyle 
= WS_VISIBLE 
| WS_TABSTOP 
| WS_CHILD 
/* | WS_CLIPSIBLINGS */ ; 
  79     if ( m_windowStyle 
& wxCLIP_SIBLINGS 
) 
  80         msStyle 
|= WS_CLIPSIBLINGS
; 
  83     if(m_windowStyle 
& wxBU_LEFT
) 
  85     if(m_windowStyle 
& wxBU_RIGHT
) 
  87     if(m_windowStyle 
& wxBU_TOP
) 
  89     if(m_windowStyle 
& wxBU_BOTTOM
) 
  93     m_hWnd 
= (WXHWND
)CreateWindowEx
 
  95                       MakeExtendedStyle(m_windowStyle
), 
 110         msg
.Printf(wxT("CreateWindowEx failed")); 
 112         msg
.Printf(wxT("CreateWindowEx failed with error number %ld"), (long) GetLastError()); 
 117     // Subclass again for purposes of dialog editing mode 
 120     SetFont(parent
->GetFont()); 
 122     SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
); 
 127 wxButton::~wxButton() 
 129     wxPanel 
*panel 
= wxDynamicCast(GetParent(), wxPanel
); 
 132         if ( panel
->GetDefaultItem() == this ) 
 134             // don't leave the panel with invalid default item 
 135             panel
->SetDefaultItem(NULL
); 
 140 // ---------------------------------------------------------------------------- 
 141 // size management including autosizing 
 142 // ---------------------------------------------------------------------------- 
 144 wxSize 
wxButton::DoGetBestSize() const 
 146     wxString label 
= wxGetWindowText(GetHWND()); 
 148     GetTextExtent(label
, &wBtn
, NULL
); 
 151     wxGetCharSize(GetHWND(), &wChar
, &hChar
, &GetFont()); 
 153     // add a margin - the button is wider than just its label 
 156     // the button height is proportional to the height of the font used 
 157     int hBtn 
= BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hChar
); 
 159     wxSize sz 
= GetDefaultSize(); 
 160     if (wBtn 
> sz
.x
) sz
.x 
= wBtn
; 
 161     if (hBtn 
> sz
.y
) sz
.y 
= hBtn
; 
 167 wxSize 
wxButton::GetDefaultSize() 
 169     static wxSize s_sizeBtn
; 
 171     if ( s_sizeBtn
.x 
== 0 ) 
 174         dc
.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT
)); 
 176         // the size of a standard button in the dialog units is 50x14, 
 177         // translate this to pixels 
 178         // NB1: the multipliers come from the Windows convention 
 179         // NB2: the extra +1/+2 were needed to get the size be the same as the 
 180         //      size of the buttons in the standard dialog - I don't know how 
 181         //      this happens, but on my system this size is 75x23 in pixels and 
 182         //      23*8 isn't even divisible by 14... Would be nice to understand 
 183         //      why these constants are needed though! 
 184         s_sizeBtn
.x 
= (50 * (dc
.GetCharWidth() + 1))/4; 
 185         s_sizeBtn
.y 
= ((14 * dc
.GetCharHeight()) + 2)/8; 
 191 // ---------------------------------------------------------------------------- 
 192 // set this button as the default one in its panel 
 193 // ---------------------------------------------------------------------------- 
 195 void wxButton::SetDefault() 
 197     wxWindow 
*parent 
= GetParent(); 
 198     wxButton 
*btnOldDefault 
= NULL
; 
 199     wxPanel 
*panel 
= wxDynamicCast(parent
, wxPanel
); 
 202         btnOldDefault 
= panel
->GetDefaultItem(); 
 203         panel
->SetDefaultItem(this); 
 208         SendMessage(GetWinHwnd(parent
), DM_SETDEFID
, m_windowId
, 0L); 
 213         // remove the BS_DEFPUSHBUTTON style from the other button 
 214         long style 
= GetWindowLong(GetHwndOf(btnOldDefault
), GWL_STYLE
); 
 216         // don't do it with the owner drawn buttons because it will reset 
 217         // BS_OWNERDRAW style bit too (BS_OWNERDRAW & BS_DEFPUSHBUTTON != 0)! 
 218         if ( (style 
& BS_OWNERDRAW
) != BS_OWNERDRAW 
) 
 220             style 
&= ~BS_DEFPUSHBUTTON
; 
 221             SendMessage(GetHwndOf(btnOldDefault
), BM_SETSTYLE
, style
, 1L); 
 225             // redraw the button - it will notice itself that it's not the 
 226             // default one any longer 
 227             btnOldDefault
->Refresh(); 
 231     // set this button as the default 
 232     long style 
= GetWindowLong(GetHwnd(), GWL_STYLE
); 
 233     if ( (style 
& BS_OWNERDRAW
) != BS_OWNERDRAW 
) 
 235         style 
|= BS_DEFPUSHBUTTON
; 
 236         SendMessage(GetHwnd(), BM_SETSTYLE
, style
, 1L); 
 240 // ---------------------------------------------------------------------------- 
 242 // ---------------------------------------------------------------------------- 
 244 bool wxButton::SendClickEvent() 
 246     wxCommandEvent 
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId()); 
 247     event
.SetEventObject(this); 
 249     return ProcessCommand(event
); 
 252 void wxButton::Command(wxCommandEvent 
& event
) 
 254     ProcessCommand(event
); 
 257 // ---------------------------------------------------------------------------- 
 258 // event/message handlers 
 259 // ---------------------------------------------------------------------------- 
 261 bool wxButton::MSWCommand(WXUINT param
, WXWORD 
WXUNUSED(id
)) 
 263     bool processed 
= FALSE
; 
 266         case 1:                     // message came from an accelerator 
 267         case BN_CLICKED
:            // normal buttons send this 
 268         case BN_DOUBLECLICKED
:      // owner-drawn ones also send this 
 269             processed 
= SendClickEvent(); 
 276 long wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
 278     // when we receive focus, we want to become the default button in our 
 280     if ( nMsg 
== WM_SETFOCUS 
) 
 284         // let the default processign take place too 
 286     else if ( nMsg 
== WM_LBUTTONDBLCLK 
) 
 288         // emulate a click event to force an owner-drawn button to change its 
 289         // appearance - without this, it won't do it 
 290         (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
); 
 292         // and conitnue with processing the message normally as well 
 295     // let the base class do all real processing 
 296     return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
 299 // ---------------------------------------------------------------------------- 
 300 // owner-drawn buttons support 
 301 // ---------------------------------------------------------------------------- 
 307 static void DrawButtonText(HDC hdc
, 
 309                            const wxString
& text
, 
 312     COLORREF colOld 
= SetTextColor(hdc
, col
); 
 313     int modeOld 
= SetBkMode(hdc
, TRANSPARENT
); 
 315     DrawText(hdc
, text
, text
.length(), pRect
, 
 316              DT_CENTER 
| DT_VCENTER 
| DT_SINGLELINE
); 
 318     SetBkMode(hdc
, modeOld
); 
 319     SetTextColor(hdc
, colOld
); 
 322 static void DrawRect(HDC hdc
, const RECT
& r
) 
 324     MoveToEx(hdc
, r
.left
, r
.top
, NULL
); 
 325     LineTo(hdc
, r
.right
, r
.top
); 
 326     LineTo(hdc
, r
.right
, r
.bottom
); 
 327     LineTo(hdc
, r
.left
, r
.bottom
); 
 328     LineTo(hdc
, r
.left
, r
.top
); 
 331 void wxButton::MakeOwnerDrawn() 
 333     long style 
= GetWindowLong(GetHwnd(), GWL_STYLE
); 
 334     if ( (style 
& BS_OWNERDRAW
) != BS_OWNERDRAW 
) 
 337         style 
|= BS_OWNERDRAW
; 
 338         SetWindowLong(GetHwnd(), GWL_STYLE
, style
); 
 342 bool wxButton::SetBackgroundColour(const wxColour 
&colour
) 
 344     if ( !wxControl::SetBackgroundColour(colour
) ) 
 357 bool wxButton::SetForegroundColour(const wxColour 
&colour
) 
 359     if ( !wxControl::SetForegroundColour(colour
) ) 
 373    The button frame looks like this normally: 
 376    WHHHHHHHHHHHHHHHHGB  W = white       (HILIGHT) 
 377    WH               GB  H = light grey  (LIGHT) 
 378    WH               GB  G = dark grey   (SHADOW) 
 379    WH               GB  B = black       (DKSHADOW) 
 384    When the button is selected, the button becomes like this (the total button 
 385    size doesn't change): 
 396    When the button is pushed (while selected) it is like: 
 408 static void DrawButtonFrame(HDC hdc
, const RECT
& rectBtn
, 
 409                             bool selected
, bool pushed
) 
 412     CopyRect(&r
, &rectBtn
); 
 414     HPEN hpenBlack   
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DDKSHADOW
)), 
 415          hpenGrey    
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DSHADOW
)), 
 416          hpenLightGr 
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DLIGHT
)), 
 417          hpenWhite   
= CreatePen(PS_SOLID
, 1, GetSysColor(COLOR_3DHILIGHT
)); 
 419     HPEN hpenOld 
= (HPEN
)SelectObject(hdc
, hpenBlack
); 
 428         (void)SelectObject(hdc
, hpenGrey
); 
 429         InflateRect(&r
, -1, -1); 
 439             InflateRect(&r
, -1, -1); 
 442         MoveToEx(hdc
, r
.left
, r
.bottom
, NULL
); 
 443         LineTo(hdc
, r
.right
, r
.bottom
); 
 444         LineTo(hdc
, r
.right
, r
.top 
- 1); 
 446         (void)SelectObject(hdc
, hpenWhite
); 
 447         MoveToEx(hdc
, r
.left
, r
.bottom 
- 1, NULL
); 
 448         LineTo(hdc
, r
.left
, r
.top
); 
 449         LineTo(hdc
, r
.right
, r
.top
); 
 451         (void)SelectObject(hdc
, hpenLightGr
); 
 452         MoveToEx(hdc
, r
.left 
+ 1, r
.bottom 
- 2, NULL
); 
 453         LineTo(hdc
, r
.left 
+ 1, r
.top 
+ 1); 
 454         LineTo(hdc
, r
.right 
- 1, r
.top 
+ 1); 
 456         (void)SelectObject(hdc
, hpenGrey
); 
 457         MoveToEx(hdc
, r
.left 
+ 1, r
.bottom 
- 1, NULL
); 
 458         LineTo(hdc
, r
.right 
- 1, r
.bottom 
- 1); 
 459         LineTo(hdc
, r
.right 
- 1, r
.top
); 
 462     (void)SelectObject(hdc
, hpenOld
); 
 463     DeleteObject(hpenWhite
); 
 464     DeleteObject(hpenLightGr
); 
 465     DeleteObject(hpenGrey
); 
 466     DeleteObject(hpenBlack
); 
 469 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT 
*wxdis
) 
 471     LPDRAWITEMSTRUCT lpDIS 
= (LPDRAWITEMSTRUCT
)wxdis
; 
 474     CopyRect(&rectBtn
, &lpDIS
->rcItem
); 
 476     COLORREF colBg 
= wxColourToRGB(GetBackgroundColour()), 
 477              colFg 
= wxColourToRGB(GetForegroundColour()); 
 479     HDC hdc 
= lpDIS
->hDC
; 
 480     UINT state 
= lpDIS
->itemState
; 
 482     // first, draw the background 
 483     HBRUSH hbrushBackground 
= ::CreateSolidBrush(colBg
); 
 485     FillRect(hdc
, &rectBtn
, hbrushBackground
); 
 487     // draw the border for the current state 
 488     bool selected 
= (state 
& ODS_SELECTED
) != 0; 
 491         wxPanel 
*panel 
= wxDynamicCast(GetParent(), wxPanel
); 
 494             selected 
= panel
->GetDefaultItem() == this; 
 497     bool pushed 
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0; 
 499     DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
); 
 501     // draw the focus rect if needed 
 502     if ( state 
& ODS_FOCUS 
) 
 505         CopyRect(&rectFocus
, &rectBtn
); 
 507         // I don't know where does this constant come from, but this is how 
 508         // Windows draws them 
 509         InflateRect(&rectFocus
, -4, -4); 
 511         DrawFocusRect(hdc
, &rectFocus
); 
 516         // the label is shifted by 1 pixel to create "pushed" effect 
 517         OffsetRect(&rectBtn
, 1, 1); 
 520     DrawButtonText(hdc
, &rectBtn
, GetLabel(), 
 521                    state 
& ODS_DISABLED 
? GetSysColor(COLOR_GRAYTEXT
) 
 524     ::DeleteObject(hbrushBackground
);