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