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