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