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