new stock buttons API
[wxWidgets.git] / src / msw / button.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/button.cpp
3 // Purpose: wxButton
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "button.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_BUTTON
32
33 #ifndef WX_PRECOMP
34 #include "wx/app.h"
35 #include "wx/button.h"
36 #include "wx/brush.h"
37 #include "wx/panel.h"
38 #include "wx/bmpbuttn.h"
39 #include "wx/settings.h"
40 #include "wx/dcscreen.h"
41 #endif
42
43 #include "wx/stockitem.h"
44 #include "wx/msw/private.h"
45
46 // ----------------------------------------------------------------------------
47 // macros
48 // ----------------------------------------------------------------------------
49
50 #if wxUSE_EXTENDED_RTTI
51
52 WX_DEFINE_FLAGS( wxButtonStyle )
53
54 wxBEGIN_FLAGS( wxButtonStyle )
55 // new style border flags, we put them first to
56 // use them for streaming out
57 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
58 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
59 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
60 wxFLAGS_MEMBER(wxBORDER_RAISED)
61 wxFLAGS_MEMBER(wxBORDER_STATIC)
62 wxFLAGS_MEMBER(wxBORDER_NONE)
63
64 // old style border flags
65 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
66 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
67 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
68 wxFLAGS_MEMBER(wxRAISED_BORDER)
69 wxFLAGS_MEMBER(wxSTATIC_BORDER)
70 wxFLAGS_MEMBER(wxBORDER)
71
72 // standard window styles
73 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
74 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
75 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
76 wxFLAGS_MEMBER(wxWANTS_CHARS)
77 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
78 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
79 wxFLAGS_MEMBER(wxVSCROLL)
80 wxFLAGS_MEMBER(wxHSCROLL)
81
82 wxFLAGS_MEMBER(wxBU_LEFT)
83 wxFLAGS_MEMBER(wxBU_RIGHT)
84 wxFLAGS_MEMBER(wxBU_TOP)
85 wxFLAGS_MEMBER(wxBU_BOTTOM)
86 wxFLAGS_MEMBER(wxBU_EXACTFIT)
87 wxEND_FLAGS( wxButtonStyle )
88
89 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton, wxControl,"wx/button.h")
90
91 wxBEGIN_PROPERTIES_TABLE(wxButton)
92 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_BUTTON_CLICKED , wxCommandEvent)
93
94 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
95 wxPROPERTY( Label, wxString , SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
96
97 wxPROPERTY_FLAGS( WindowStyle , wxButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
98
99 wxEND_PROPERTIES_TABLE()
100
101 wxBEGIN_HANDLERS_TABLE(wxButton)
102 wxEND_HANDLERS_TABLE()
103
104 wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
105
106
107 #else
108 IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
109 #endif
110
111 // this macro tries to adjust the default button height to a reasonable value
112 // using the char height as the base
113 #define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10)
114
115 // ============================================================================
116 // implementation
117 // ============================================================================
118
119 // ----------------------------------------------------------------------------
120 // creation/destruction
121 // ----------------------------------------------------------------------------
122
123 bool wxButton::Create(wxWindow *parent,
124 wxWindowID id,
125 const wxString& lbl,
126 const wxPoint& pos,
127 const wxSize& size,
128 long style,
129 const wxValidator& validator,
130 const wxString& name)
131 {
132 wxString label(lbl);
133 if (label.empty() && wxIsStockID(id))
134 label = wxGetStockLabel(id);
135
136 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
137 return false;
138
139 WXDWORD exstyle;
140 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
141
142 #ifdef __WIN32__
143 // if the label contains several lines we must explicitly tell the button
144 // about it or it wouldn't draw it correctly ("\n"s would just appear as
145 // black boxes)
146 //
147 // NB: we do it here and not in MSWGetStyle() because we need the label
148 // value and m_label is not set yet when MSWGetStyle() is called;
149 // besides changing BS_MULTILINE during run-time is pointless anyhow
150 if ( label.find(_T('\n')) != wxString::npos )
151 {
152 msStyle |= BS_MULTILINE;
153 }
154 #endif // __WIN32__
155
156 return MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, exstyle);
157 }
158
159 wxButton::~wxButton()
160 {
161 }
162
163 // ----------------------------------------------------------------------------
164 // flags
165 // ----------------------------------------------------------------------------
166
167 WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
168 {
169 // buttons never have an external border, they draw their own one
170 WXDWORD msStyle = wxControl::MSWGetStyle
171 (
172 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
173 );
174
175 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
176 // each other in any resizeable dialog which has more than one button in
177 // the bottom
178 msStyle |= WS_CLIPSIBLINGS;
179
180 #ifdef __WIN32__
181 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
182 // and wxBU_RIGHT to get BS_CENTER!
183 if ( style & wxBU_LEFT )
184 msStyle |= BS_LEFT;
185 if ( style & wxBU_RIGHT )
186 msStyle |= BS_RIGHT;
187 if ( style & wxBU_TOP )
188 msStyle |= BS_TOP;
189 if ( style & wxBU_BOTTOM )
190 msStyle |= BS_BOTTOM;
191 #ifndef __WXWINCE__
192 // flat 2d buttons
193 if ( style & wxNO_BORDER )
194 msStyle |= BS_FLAT;
195 #endif // __WXWINCE__
196 #endif // __WIN32__
197
198 return msStyle;
199 }
200
201 // ----------------------------------------------------------------------------
202 // size management including autosizing
203 // ----------------------------------------------------------------------------
204
205 wxSize wxButton::DoGetBestSize() const
206 {
207 int wBtn;
208 GetTextExtent(wxGetWindowText(GetHWND()), &wBtn, NULL);
209
210 int wChar, hChar;
211 wxGetCharSize(GetHWND(), &wChar, &hChar, GetFont());
212
213 // add a margin -- the button is wider than just its label
214 wBtn += 3*wChar;
215
216 // the button height is proportional to the height of the font used
217 int hBtn = BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hChar);
218
219 // all buttons have at least the standard size unless the user explicitly
220 // wants them to be of smaller size and used wxBU_EXACTFIT style when
221 // creating the button
222 if ( !HasFlag(wxBU_EXACTFIT) )
223 {
224 wxSize sz = GetDefaultSize();
225 if (wBtn > sz.x)
226 sz.x = wBtn;
227 if (hBtn > sz.y)
228 sz.y = hBtn;
229
230 return sz;
231 }
232
233 return wxSize(wBtn, hBtn);
234 }
235
236 /* static */
237 wxSize wxButtonBase::GetDefaultSize()
238 {
239 static wxSize s_sizeBtn;
240
241 if ( s_sizeBtn.x == 0 )
242 {
243 wxScreenDC dc;
244 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
245
246 // the size of a standard button in the dialog units is 50x14,
247 // translate this to pixels
248 // NB1: the multipliers come from the Windows convention
249 // NB2: the extra +1/+2 were needed to get the size be the same as the
250 // size of the buttons in the standard dialog - I don't know how
251 // this happens, but on my system this size is 75x23 in pixels and
252 // 23*8 isn't even divisible by 14... Would be nice to understand
253 // why these constants are needed though!
254 s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4;
255 s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8;
256 }
257
258 return s_sizeBtn;
259 }
260
261 // ----------------------------------------------------------------------------
262 // default button handling
263 // ----------------------------------------------------------------------------
264
265 /*
266 "Everything you ever wanted to know about the default buttons" or "Why do we
267 have to do all this?"
268
269 In MSW the default button should be activated when the user presses Enter
270 and the current control doesn't process Enter itself somehow. This is
271 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
272 Another aspect of "defaultness" is that the default button has different
273 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
274 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
275 be unset if our parent window is not active so it should be unset whenever
276 we lose activation and set back when we regain it.
277
278 Final complication is that when a button is active, it should be the default
279 one, i.e. pressing Enter on a button always activates it and not another
280 one.
281
282 We handle this by maintaining a permanent and a temporary default items in
283 wxControlContainer (both may be NULL). When a button becomes the current
284 control (i.e. gets focus) it sets itself as the temporary default which
285 ensures that it has the right appearance and that Enter will be redirected
286 to it. When the button loses focus, it unsets the temporary default and so
287 the default item will be the permanent default -- that is the default button
288 if any had been set or none otherwise, which is just what we want.
289
290 NB: all this is quite complicated by now and the worst is that normally
291 it shouldn't be necessary at all as for the normal Windows programs
292 DefWindowProc() and IsDialogMessage() take care of all this
293 automatically -- however in wxWidgets programs this doesn't work for
294 nested hierarchies (i.e. a notebook inside a notebook) for unknown
295 reason and so we have to reproduce all this code ourselves. It would be
296 very nice if we could avoid doing it.
297 */
298
299 // set this button as the (permanently) default one in its panel
300 void wxButton::SetDefault()
301 {
302 wxWindow *parent = GetParent();
303
304 wxCHECK_RET( parent, _T("button without parent?") );
305
306 // set this one as the default button both for wxWidgets ...
307 wxWindow *winOldDefault = parent->SetDefaultItem(this);
308
309 // ... and Windows
310 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
311 SetDefaultStyle(this, true);
312 }
313
314 // set this button as being currently default
315 void wxButton::SetTmpDefault()
316 {
317 wxWindow *parent = GetParent();
318
319 wxCHECK_RET( parent, _T("button without parent?") );
320
321 wxWindow *winOldDefault = parent->GetDefaultItem();
322 parent->SetTmpDefaultItem(this);
323
324 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
325 SetDefaultStyle(this, true);
326 }
327
328 // unset this button as currently default, it may still stay permanent default
329 void wxButton::UnsetTmpDefault()
330 {
331 wxWindow *parent = GetParent();
332
333 wxCHECK_RET( parent, _T("button without parent?") );
334
335 parent->SetTmpDefaultItem(NULL);
336
337 wxWindow *winOldDefault = parent->GetDefaultItem();
338
339 SetDefaultStyle(this, false);
340 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true);
341 }
342
343 /* static */
344 void
345 wxButton::SetDefaultStyle(wxButton *btn, bool on)
346 {
347 // we may be called with NULL pointer -- simpler to do the check here than
348 // in the caller which does wxDynamicCast()
349 if ( !btn )
350 return;
351
352 // first, let DefDlgProc() know about the new default button
353 if ( on )
354 {
355 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
356 // focus at all any more
357 if ( !wxTheApp->IsActive() )
358 return;
359
360 // look for a panel-like window
361 wxWindow *win = btn->GetParent();
362 while ( win && !win->HasFlag(wxTAB_TRAVERSAL) )
363 win = win->GetParent();
364
365 if ( win )
366 {
367 ::SendMessage(GetHwndOf(win), DM_SETDEFID, btn->GetId(), 0L);
368
369 // sending DM_SETDEFID also changes the button style to
370 // BS_DEFPUSHBUTTON so there is nothing more to do
371 }
372 }
373
374 // then also change the style as needed
375 long style = ::GetWindowLong(GetHwndOf(btn), GWL_STYLE);
376 if ( !(style & BS_DEFPUSHBUTTON) == on )
377 {
378 // don't do it with the owner drawn buttons because it will
379 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
380 // BS_DEFPUSHBUTTON != 0)!
381 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
382 {
383 ::SendMessage(GetHwndOf(btn), BM_SETSTYLE,
384 on ? style | BS_DEFPUSHBUTTON
385 : style & ~BS_DEFPUSHBUTTON,
386 1L /* redraw */);
387 }
388 else // owner drawn
389 {
390 // redraw the button - it will notice itself that it's
391 // [not] the default one [any longer]
392 btn->Refresh();
393 }
394 }
395 //else: already has correct style
396 }
397
398 // ----------------------------------------------------------------------------
399 // helpers
400 // ----------------------------------------------------------------------------
401
402 bool wxButton::SendClickEvent()
403 {
404 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
405 event.SetEventObject(this);
406
407 return ProcessCommand(event);
408 }
409
410 void wxButton::Command(wxCommandEvent & event)
411 {
412 ProcessCommand(event);
413 }
414
415 // ----------------------------------------------------------------------------
416 // event/message handlers
417 // ----------------------------------------------------------------------------
418
419 bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
420 {
421 bool processed = false;
422 switch ( param )
423 {
424 // NOTE: Apparently older versions (NT 4?) of the common controls send
425 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
426 // buttons, so in order to send two EVT_BUTTON events we should
427 // catch both types. Currently (Feb 2003) up-to-date versions of
428 // win98, win2k and winXP all send two BN_CLICKED messages for
429 // all button types, so we don't catch BN_DOUBLECLICKED anymore
430 // in order to not get 3 EVT_BUTTON events. If this is a problem
431 // then we need to figure out which version of the comctl32 changed
432 // this behaviour and test for it.
433
434 case 1: // message came from an accelerator
435 case BN_CLICKED: // normal buttons send this
436 processed = SendClickEvent();
437 break;
438 }
439
440 return processed;
441 }
442
443 WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
444 {
445 // when we receive focus, we want to temporarily become the default button in
446 // our parent panel so that pressing "Enter" would activate us -- and when
447 // losing it we should restore the previous default button as well
448 if ( nMsg == WM_SETFOCUS )
449 {
450 SetTmpDefault();
451
452 // let the default processing take place too
453 }
454 else if ( nMsg == WM_KILLFOCUS )
455 {
456 UnsetTmpDefault();
457 }
458 else if ( nMsg == WM_LBUTTONDBLCLK )
459 {
460 // emulate a click event to force an owner-drawn button to change its
461 // appearance - without this, it won't do it
462 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
463
464 // and continue with processing the message normally as well
465 }
466
467 // let the base class do all real processing
468 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
469 }
470
471 // ----------------------------------------------------------------------------
472 // owner-drawn buttons support
473 // ----------------------------------------------------------------------------
474
475 #ifdef __WIN32__
476
477 // drawing helpers
478
479 static void DrawButtonText(HDC hdc,
480 RECT *pRect,
481 const wxString& text,
482 COLORREF col)
483 {
484 COLORREF colOld = SetTextColor(hdc, col);
485 int modeOld = SetBkMode(hdc, TRANSPARENT);
486
487 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
488 ::DrawText(hdc, text, text.length(), pRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
489
490 SetBkMode(hdc, modeOld);
491 SetTextColor(hdc, colOld);
492 }
493
494 static void DrawRect(HDC hdc, const RECT& r)
495 {
496 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
497 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
498 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
499 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
500 }
501
502 void wxButton::MakeOwnerDrawn()
503 {
504 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
505 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
506 {
507 // make it so
508 style |= BS_OWNERDRAW;
509 SetWindowLong(GetHwnd(), GWL_STYLE, style);
510 }
511 }
512
513 bool wxButton::SetBackgroundColour(const wxColour &colour)
514 {
515 if ( !wxControl::SetBackgroundColour(colour) )
516 {
517 // nothing to do
518 return false;
519 }
520
521 MakeOwnerDrawn();
522
523 Refresh();
524
525 return true;
526 }
527
528 bool wxButton::SetForegroundColour(const wxColour &colour)
529 {
530 if ( !wxControl::SetForegroundColour(colour) )
531 {
532 // nothing to do
533 return false;
534 }
535
536 MakeOwnerDrawn();
537
538 Refresh();
539
540 return true;
541 }
542
543 /*
544 The button frame looks like this normally:
545
546 WWWWWWWWWWWWWWWWWWB
547 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
548 WH GB H = light grey (LIGHT)
549 WH GB G = dark grey (SHADOW)
550 WH GB B = black (DKSHADOW)
551 WH GB
552 WGGGGGGGGGGGGGGGGGB
553 BBBBBBBBBBBBBBBBBBB
554
555 When the button is selected, the button becomes like this (the total button
556 size doesn't change):
557
558 BBBBBBBBBBBBBBBBBBB
559 BWWWWWWWWWWWWWWWWBB
560 BWHHHHHHHHHHHHHHGBB
561 BWH GBB
562 BWH GBB
563 BWGGGGGGGGGGGGGGGBB
564 BBBBBBBBBBBBBBBBBBB
565 BBBBBBBBBBBBBBBBBBB
566
567 When the button is pushed (while selected) it is like:
568
569 BBBBBBBBBBBBBBBBBBB
570 BGGGGGGGGGGGGGGGGGB
571 BG GB
572 BG GB
573 BG GB
574 BG GB
575 BGGGGGGGGGGGGGGGGGB
576 BBBBBBBBBBBBBBBBBBB
577 */
578
579 static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
580 bool selected, bool pushed)
581 {
582 RECT r;
583 CopyRect(&r, &rectBtn);
584
585 HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
586 hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
587 hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
588 hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
589
590 HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
591
592 r.right--;
593 r.bottom--;
594
595 if ( pushed )
596 {
597 DrawRect(hdc, r);
598
599 (void)SelectObject(hdc, hpenGrey);
600 ::InflateRect(&r, -1, -1);
601
602 DrawRect(hdc, r);
603 }
604 else // !pushed
605 {
606 if ( selected )
607 {
608 DrawRect(hdc, r);
609
610 ::InflateRect(&r, -1, -1);
611 }
612
613 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
614 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
615
616 (void)SelectObject(hdc, hpenWhite);
617 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
618 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
619
620 (void)SelectObject(hdc, hpenLightGr);
621 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
622 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
623
624 (void)SelectObject(hdc, hpenGrey);
625 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
626 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
627 }
628
629 (void)SelectObject(hdc, hpenOld);
630 DeleteObject(hpenWhite);
631 DeleteObject(hpenLightGr);
632 DeleteObject(hpenGrey);
633 DeleteObject(hpenBlack);
634 }
635
636 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
637 {
638 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
639
640 RECT rectBtn;
641 CopyRect(&rectBtn, &lpDIS->rcItem);
642
643 COLORREF colBg = wxColourToRGB(GetBackgroundColour()),
644 colFg = wxColourToRGB(GetForegroundColour());
645
646 HDC hdc = lpDIS->hDC;
647 UINT state = lpDIS->itemState;
648
649 // first, draw the background
650 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
651
652 FillRect(hdc, &rectBtn, hbrushBackground);
653
654 // draw the border for the current state
655 bool selected = (state & ODS_SELECTED) != 0;
656 if ( !selected )
657 {
658 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
659 if ( panel )
660 {
661 selected = panel->GetDefaultItem() == this;
662 }
663 }
664 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
665
666 DrawButtonFrame(hdc, rectBtn, selected, pushed);
667
668 // draw the focus rect if needed
669 if ( state & ODS_FOCUS )
670 {
671 RECT rectFocus;
672 CopyRect(&rectFocus, &rectBtn);
673
674 // I don't know where does this constant come from, but this is how
675 // Windows draws them
676 InflateRect(&rectFocus, -4, -4);
677
678 DrawFocusRect(hdc, &rectFocus);
679 }
680
681 if ( pushed )
682 {
683 // the label is shifted by 1 pixel to create "pushed" effect
684 OffsetRect(&rectBtn, 1, 1);
685 }
686
687 DrawButtonText(hdc, &rectBtn, GetLabel(),
688 state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT)
689 : colFg);
690
691 ::DeleteObject(hbrushBackground);
692
693 return true;
694 }
695
696 #endif // __WIN32__
697
698 #endif // wxUSE_BUTTON
699