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