]> git.saurik.com Git - wxWidgets.git/blob - src/msw/button.cpp
better ownership handling
[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 #include "wx/msw/wrapcctl.h"
41 #include "wx/msw/private.h"
42 #include "wx/msw/missing.h"
43 #endif
44
45 #include "wx/imaglist.h"
46 #include "wx/stockitem.h"
47 #include "wx/msw/private/button.h"
48 #include "wx/msw/private/dc.h"
49 #include "wx/private/window.h"
50
51 #if wxUSE_MARKUP
52 #include "wx/generic/private/markuptext.h"
53 #endif // wxUSE_MARKUP
54
55 using namespace wxMSWImpl;
56
57 #if wxUSE_UXTHEME
58 #include "wx/msw/uxtheme.h"
59
60 // no need to include tmschema.h
61 #ifndef BP_PUSHBUTTON
62 #define BP_PUSHBUTTON 1
63
64 #define PBS_NORMAL 1
65 #define PBS_HOT 2
66 #define PBS_PRESSED 3
67 #define PBS_DISABLED 4
68 #define PBS_DEFAULTED 5
69
70 #define TMT_CONTENTMARGINS 3602
71 #endif
72
73 // provide the necessary declarations ourselves if they're missing from
74 // headers
75 #ifndef BCM_SETIMAGELIST
76 #define BCM_SETIMAGELIST 0x1602
77 #define BCM_SETTEXTMARGIN 0x1604
78
79 enum
80 {
81 BUTTON_IMAGELIST_ALIGN_LEFT,
82 BUTTON_IMAGELIST_ALIGN_RIGHT,
83 BUTTON_IMAGELIST_ALIGN_TOP,
84 BUTTON_IMAGELIST_ALIGN_BOTTOM
85 };
86
87 struct BUTTON_IMAGELIST
88 {
89 HIMAGELIST himl;
90 RECT margin;
91 UINT uAlign;
92 };
93 #endif
94 #endif // wxUSE_UXTHEME
95
96 #ifndef WM_THEMECHANGED
97 #define WM_THEMECHANGED 0x031A
98 #endif
99
100 #ifndef ODS_NOACCEL
101 #define ODS_NOACCEL 0x0100
102 #endif
103
104 #ifndef ODS_NOFOCUSRECT
105 #define ODS_NOFOCUSRECT 0x0200
106 #endif
107
108 #ifndef DT_HIDEPREFIX
109 #define DT_HIDEPREFIX 0x00100000
110 #endif
111
112 // set the value for BCM_SETSHIELD (for the UAC shield) if it's not defined in
113 // the header
114 #ifndef BCM_SETSHIELD
115 #define BCM_SETSHIELD 0x160c
116 #endif
117
118 #if wxUSE_UXTHEME
119 extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp
120 #endif // wxUSE_UXTHEME
121
122 // ----------------------------------------------------------------------------
123 // button image data
124 // ----------------------------------------------------------------------------
125
126 // we use different data classes for owner drawn buttons and for themed XP ones
127
128 class wxButtonImageData
129 {
130 public:
131 wxButtonImageData() { }
132 virtual ~wxButtonImageData() { }
133
134 virtual wxBitmap GetBitmap(wxButton::State which) const = 0;
135 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) = 0;
136
137 virtual wxSize GetBitmapMargins() const = 0;
138 virtual void SetBitmapMargins(wxCoord x, wxCoord y) = 0;
139
140 virtual wxDirection GetBitmapPosition() const = 0;
141 virtual void SetBitmapPosition(wxDirection dir) = 0;
142
143 private:
144 wxDECLARE_NO_COPY_CLASS(wxButtonImageData);
145 };
146
147 namespace
148 {
149
150 // the gap between button edge and the interior area used by Windows for the
151 // standard buttons
152 const int OD_BUTTON_MARGIN = 4;
153
154 class wxODButtonImageData : public wxButtonImageData
155 {
156 public:
157 wxODButtonImageData(wxButton *btn, const wxBitmap& bitmap)
158 {
159 SetBitmap(bitmap, wxButton::State_Normal);
160 SetBitmap(bitmap.ConvertToDisabled(), wxButton::State_Disabled);
161
162 m_dir = wxLEFT;
163
164 // we use margins when we have both bitmap and text, but when we have
165 // only the bitmap it should take up the entire button area
166 if ( btn->ShowsLabel() )
167 {
168 m_margin.x = btn->GetCharWidth();
169 m_margin.y = btn->GetCharHeight() / 2;
170 }
171 }
172
173 virtual wxBitmap GetBitmap(wxButton::State which) const
174 {
175 return m_bitmaps[which];
176 }
177
178 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which)
179 {
180 m_bitmaps[which] = bitmap;
181 }
182
183 virtual wxSize GetBitmapMargins() const
184 {
185 return m_margin;
186 }
187
188 virtual void SetBitmapMargins(wxCoord x, wxCoord y)
189 {
190 m_margin = wxSize(x, y);
191 }
192
193 virtual wxDirection GetBitmapPosition() const
194 {
195 return m_dir;
196 }
197
198 virtual void SetBitmapPosition(wxDirection dir)
199 {
200 m_dir = dir;
201 }
202
203 private:
204 // just store the values passed to us to be able to retrieve them later
205 // from the drawing code
206 wxBitmap m_bitmaps[wxButton::State_Max];
207 wxSize m_margin;
208 wxDirection m_dir;
209
210 wxDECLARE_NO_COPY_CLASS(wxODButtonImageData);
211 };
212
213 #if wxUSE_UXTHEME
214
215 // somehow the margin is one pixel greater than the value returned by
216 // GetThemeMargins() call
217 const int XP_BUTTON_EXTRA_MARGIN = 1;
218
219 class wxXPButtonImageData : public wxButtonImageData
220 {
221 public:
222 // we must be constructed with the size of our images as we need to create
223 // the image list
224 wxXPButtonImageData(wxButton *btn, const wxBitmap& bitmap)
225 : m_iml(bitmap.GetWidth(), bitmap.GetHeight(), true /* use mask */,
226 wxButton::State_Max),
227 m_hwndBtn(GetHwndOf(btn))
228 {
229 // initialize all bitmaps except for the disabled one to normal state
230 for ( int n = 0; n < wxButton::State_Max; n++ )
231 {
232 m_iml.Add(n == wxButton::State_Disabled ? bitmap.ConvertToDisabled()
233 : bitmap);
234 }
235
236 m_data.himl = GetHimagelistOf(&m_iml);
237
238 // no margins by default
239 m_data.margin.left =
240 m_data.margin.right =
241 m_data.margin.top =
242 m_data.margin.bottom = 0;
243
244 // use default alignment
245 m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
246
247 UpdateImageInfo();
248 }
249
250 virtual wxBitmap GetBitmap(wxButton::State which) const
251 {
252 return m_iml.GetBitmap(which);
253 }
254
255 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which)
256 {
257 m_iml.Replace(which, bitmap);
258
259 UpdateImageInfo();
260 }
261
262 virtual wxSize GetBitmapMargins() const
263 {
264 return wxSize(m_data.margin.left, m_data.margin.top);
265 }
266
267 virtual void SetBitmapMargins(wxCoord x, wxCoord y)
268 {
269 RECT& margin = m_data.margin;
270 margin.left =
271 margin.right = x;
272 margin.top =
273 margin.bottom = y;
274
275 if ( !::SendMessage(m_hwndBtn, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin) )
276 {
277 wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed");
278 }
279 }
280
281 virtual wxDirection GetBitmapPosition() const
282 {
283 switch ( m_data.uAlign )
284 {
285 default:
286 wxFAIL_MSG( "invalid image alignment" );
287 // fall through
288
289 case BUTTON_IMAGELIST_ALIGN_LEFT:
290 return wxLEFT;
291
292 case BUTTON_IMAGELIST_ALIGN_RIGHT:
293 return wxRIGHT;
294
295 case BUTTON_IMAGELIST_ALIGN_TOP:
296 return wxTOP;
297
298 case BUTTON_IMAGELIST_ALIGN_BOTTOM:
299 return wxBOTTOM;
300 }
301 }
302
303 virtual void SetBitmapPosition(wxDirection dir)
304 {
305 UINT alignNew;
306 switch ( dir )
307 {
308 default:
309 wxFAIL_MSG( "invalid direction" );
310 // fall through
311
312 case wxLEFT:
313 alignNew = BUTTON_IMAGELIST_ALIGN_LEFT;
314 break;
315
316 case wxRIGHT:
317 alignNew = BUTTON_IMAGELIST_ALIGN_RIGHT;
318 break;
319
320 case wxTOP:
321 alignNew = BUTTON_IMAGELIST_ALIGN_TOP;
322 break;
323
324 case wxBOTTOM:
325 alignNew = BUTTON_IMAGELIST_ALIGN_BOTTOM;
326 break;
327 }
328
329 if ( alignNew != m_data.uAlign )
330 {
331 m_data.uAlign = alignNew;
332 UpdateImageInfo();
333 }
334 }
335
336 private:
337 void UpdateImageInfo()
338 {
339 if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) )
340 {
341 wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed");
342 }
343 }
344
345 // we store image list separately to be able to use convenient wxImageList
346 // methods instead of working with raw HIMAGELIST
347 wxImageList m_iml;
348
349 // store the rest of the data in BCM_SETIMAGELIST-friendly form
350 BUTTON_IMAGELIST m_data;
351
352 // the button we're associated with
353 const HWND m_hwndBtn;
354
355
356 wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData);
357 };
358
359 #endif // wxUSE_UXTHEME
360
361 } // anonymous namespace
362
363 // ----------------------------------------------------------------------------
364 // macros
365 // ----------------------------------------------------------------------------
366
367 // ============================================================================
368 // implementation
369 // ============================================================================
370
371 // ----------------------------------------------------------------------------
372 // helper functions from wx/msw/private/button.h
373 // ----------------------------------------------------------------------------
374
375 void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label)
376 {
377 // update BS_MULTILINE style depending on the new label (resetting it
378 // doesn't seem to do anything very useful but it shouldn't hurt and we do
379 // have to set it whenever the label becomes multi line as otherwise it
380 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
381 // the control unless it already has new lines in its label)
382 long styleOld = ::GetWindowLong(hwnd, GWL_STYLE),
383 styleNew;
384 if ( label.find(wxT('\n')) != wxString::npos )
385 styleNew = styleOld | BS_MULTILINE;
386 else
387 styleNew = styleOld & ~BS_MULTILINE;
388
389 if ( styleNew != styleOld )
390 ::SetWindowLong(hwnd, GWL_STYLE, styleNew);
391 }
392
393 wxSize wxMSWButton::GetFittingSize(wxWindow *win,
394 const wxSize& sizeLabel,
395 int flags)
396 {
397 // FIXME: this is pure guesswork, need to retrieve the real button margins
398 wxSize sizeBtn = sizeLabel;
399
400 sizeBtn.x += 3*win->GetCharWidth();
401 sizeBtn.y += win->GetCharHeight()/2;
402
403 // account for the shield UAC icon if we have it
404 if ( flags & Size_AuthNeeded )
405 sizeBtn.x += wxSystemSettings::GetMetric(wxSYS_SMALLICON_X);
406
407 return sizeBtn;
408 }
409
410 wxSize wxMSWButton::ComputeBestFittingSize(wxControl *btn, int flags)
411 {
412 wxClientDC dc(btn);
413
414 wxSize sizeBtn;
415 dc.GetMultiLineTextExtent(btn->GetLabelText(), &sizeBtn.x, &sizeBtn.y);
416
417 return GetFittingSize(btn, sizeBtn, flags);
418 }
419
420 wxSize wxMSWButton::IncreaseToStdSizeAndCache(wxControl *btn, const wxSize& size)
421 {
422 wxSize sizeBtn(size);
423
424 // All buttons have at least the standard height and, unless the user
425 // explicitly wants them to be as small as possible and used wxBU_EXACTFIT
426 // style to indicate this, of at least the standard width too.
427 //
428 // Notice that we really want to make all buttons equally high, otherwise
429 // they look ugly and the existing code using wxBU_EXACTFIT only uses it to
430 // control width and not height.
431
432 // The 50x14 button size is documented in the "Recommended sizing and
433 // spacing" section of MSDN layout article.
434 //
435 // Note that we intentionally don't use GetDefaultSize() here, because
436 // it's inexact -- dialog units depend on this dialog's font.
437 const wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
438 if ( !btn->HasFlag(wxBU_EXACTFIT) )
439 {
440 if ( sizeBtn.x < sizeDef.x )
441 sizeBtn.x = sizeDef.x;
442 }
443 if ( sizeBtn.y < sizeDef.y )
444 sizeBtn.y = sizeDef.y;
445
446 btn->CacheBestSize(sizeBtn);
447
448 return sizeBtn;
449 }
450
451 // ----------------------------------------------------------------------------
452 // creation/destruction
453 // ----------------------------------------------------------------------------
454
455 bool wxButton::Create(wxWindow *parent,
456 wxWindowID id,
457 const wxString& lbl,
458 const wxPoint& pos,
459 const wxSize& size,
460 long style,
461 const wxValidator& validator,
462 const wxString& name)
463 {
464 wxString label(lbl);
465 if (label.empty() && wxIsStockID(id))
466 {
467 // On Windows, some buttons aren't supposed to have mnemonics
468 label = wxGetStockLabel
469 (
470 id,
471 id == wxID_OK || id == wxID_CANCEL || id == wxID_CLOSE
472 ? wxSTOCK_NOFLAGS
473 : wxSTOCK_WITH_MNEMONIC
474 );
475 }
476
477 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
478 return false;
479
480 WXDWORD exstyle;
481 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
482
483 // if the label contains several lines we must explicitly tell the button
484 // about it or it wouldn't draw it correctly ("\n"s would just appear as
485 // black boxes)
486 //
487 // NB: we do it here and not in MSWGetStyle() because we need the label
488 // value and the label is not set yet when MSWGetStyle() is called
489 msStyle |= wxMSWButton::GetMultilineStyle(label);
490
491 return MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, exstyle);
492 }
493
494 wxButton::~wxButton()
495 {
496 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
497 if ( tlw && tlw->GetTmpDefaultItem() == this )
498 {
499 UnsetTmpDefault();
500 }
501
502 delete m_imageData;
503 #if wxUSE_MARKUP
504 delete m_markupText;
505 #endif // wxUSE_MARKUP
506 }
507
508 // ----------------------------------------------------------------------------
509 // flags
510 // ----------------------------------------------------------------------------
511
512 WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
513 {
514 // buttons never have an external border, they draw their own one
515 WXDWORD msStyle = wxControl::MSWGetStyle
516 (
517 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
518 );
519
520 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
521 // each other in any resizable dialog which has more than one button in
522 // the bottom
523 msStyle |= WS_CLIPSIBLINGS;
524
525 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
526 // and wxBU_RIGHT to get BS_CENTER!
527 if ( style & wxBU_LEFT )
528 msStyle |= BS_LEFT;
529 if ( style & wxBU_RIGHT )
530 msStyle |= BS_RIGHT;
531 if ( style & wxBU_TOP )
532 msStyle |= BS_TOP;
533 if ( style & wxBU_BOTTOM )
534 msStyle |= BS_BOTTOM;
535 #ifndef __WXWINCE__
536 // flat 2d buttons
537 if ( style & wxNO_BORDER )
538 msStyle |= BS_FLAT;
539 #endif // __WXWINCE__
540
541 return msStyle;
542 }
543
544 void wxButton::SetLabel(const wxString& label)
545 {
546 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label);
547
548 wxButtonBase::SetLabel(label);
549
550 #if wxUSE_MARKUP
551 // If we have a plain text label, we shouldn't be using markup any longer.
552 if ( m_markupText )
553 {
554 delete m_markupText;
555 m_markupText = NULL;
556
557 // Unfortunately we don't really know whether we can reset the button
558 // to be non-owner-drawn or not: if we had made it owner-drawn just
559 // because of a call to SetLabelMarkup(), we could, but not if there
560 // were [also] calls to Set{Fore,Back}groundColour(). If it's really a
561 // problem to have button remain owner-drawn forever just because it
562 // had markup label once, we should record the reason for our current
563 // owner-drawnness and check it here.
564 }
565 #endif // wxUSE_MARKUP
566 }
567
568 // ----------------------------------------------------------------------------
569 // size management including autosizing
570 // ----------------------------------------------------------------------------
571
572 void wxButton::AdjustForBitmapSize(wxSize &size) const
573 {
574 wxCHECK_RET( m_imageData, wxT("shouldn't be called if no image") );
575
576 // account for the bitmap size
577 const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
578 const wxDirection dirBmp = m_imageData->GetBitmapPosition();
579 if ( dirBmp == wxLEFT || dirBmp == wxRIGHT )
580 {
581 size.x += sizeBmp.x;
582 if ( sizeBmp.y > size.y )
583 size.y = sizeBmp.y;
584 }
585 else // bitmap on top/below the text
586 {
587 size.y += sizeBmp.y;
588 if ( sizeBmp.x > size.x )
589 size.x = sizeBmp.x;
590 }
591
592 // account for the user-specified margins
593 size += 2*m_imageData->GetBitmapMargins();
594
595 // and also for the margins we always add internally (unless we have no
596 // border at all in which case the button has exactly the same size as
597 // bitmap and so no margins should be used)
598 if ( !HasFlag(wxBORDER_NONE) )
599 {
600 int marginH = 0,
601 marginV = 0;
602 #if wxUSE_UXTHEME
603 if ( wxUxThemeEngine::GetIfActive() )
604 {
605 wxUxThemeHandle theme(const_cast<wxButton *>(this), L"BUTTON");
606
607 MARGINS margins;
608 wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
609 BP_PUSHBUTTON,
610 PBS_NORMAL,
611 TMT_CONTENTMARGINS,
612 NULL,
613 &margins);
614
615 // XP doesn't draw themed buttons correctly when the client
616 // area is smaller than 8x8 - enforce this minimum size for
617 // small bitmaps
618 size.IncTo(wxSize(8, 8));
619
620 marginH = margins.cxLeftWidth + margins.cxRightWidth
621 + 2*XP_BUTTON_EXTRA_MARGIN;
622 marginV = margins.cyTopHeight + margins.cyBottomHeight
623 + 2*XP_BUTTON_EXTRA_MARGIN;
624 }
625 else
626 #endif // wxUSE_UXTHEME
627 {
628 marginH =
629 marginV = OD_BUTTON_MARGIN;
630 }
631
632 size.IncBy(marginH, marginV);
633 }
634 }
635
636 wxSize wxButton::DoGetBestSize() const
637 {
638 wxButton * const self = const_cast<wxButton *>(this);
639
640 wxSize size;
641
642 // Account for the text part if we have it.
643 if ( ShowsLabel() )
644 {
645 int flags = 0;
646 if ( GetAuthNeeded() )
647 flags |= wxMSWButton::Size_AuthNeeded;
648
649 #if wxUSE_MARKUP
650 if ( m_markupText )
651 {
652 wxClientDC dc(self);
653 size = wxMSWButton::GetFittingSize(self,
654 m_markupText->Measure(dc),
655 flags);
656 }
657 else // Normal plain text (but possibly multiline) label.
658 #endif // wxUSE_MARKUP
659 {
660 size = wxMSWButton::ComputeBestFittingSize(self, flags);
661 }
662 }
663
664 if ( m_imageData )
665 AdjustForBitmapSize(size);
666
667 return wxMSWButton::IncreaseToStdSizeAndCache(self, size);
668 }
669
670 /* static */
671 wxSize wxButtonBase::GetDefaultSize()
672 {
673 static wxSize s_sizeBtn;
674
675 if ( s_sizeBtn.x == 0 )
676 {
677 wxScreenDC dc;
678 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
679
680 // The size of a standard button in the dialog units is 50x14,
681 // translate this to pixels.
682 //
683 // Windows' computes dialog units using average character width over
684 // upper- and lower-case ASCII alphabet and not using the average
685 // character width metadata stored in the font; see
686 // http://support.microsoft.com/default.aspx/kb/145994 for detailed
687 // discussion.
688 //
689 // NB: wxMulDivInt32() is used, because it correctly rounds the result
690
691 const wxSize base = wxPrivate::GetAverageASCIILetterSize(dc);
692 s_sizeBtn.x = wxMulDivInt32(50, base.x, 4);
693 s_sizeBtn.y = wxMulDivInt32(14, base.y, 8);
694 }
695
696 return s_sizeBtn;
697 }
698
699 // ----------------------------------------------------------------------------
700 // default button handling
701 // ----------------------------------------------------------------------------
702
703 /*
704 The comment below and all this code is probably due to not using WM_NEXTDLGCTL
705 message when changing focus (but just SetFocus() which is not enough), see
706 http://blogs.msdn.com/oldnewthing/archive/2004/08/02/205624.aspx for the
707 full explanation.
708
709 TODO: Do use WM_NEXTDLGCTL and get rid of all this code.
710
711
712 "Everything you ever wanted to know about the default buttons" or "Why do we
713 have to do all this?"
714
715 In MSW the default button should be activated when the user presses Enter
716 and the current control doesn't process Enter itself somehow. This is
717 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
718 Another aspect of "defaultness" is that the default button has different
719 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
720 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
721 be unset if our parent window is not active so it should be unset whenever
722 we lose activation and set back when we regain it.
723
724 Final complication is that when a button is active, it should be the default
725 one, i.e. pressing Enter on a button always activates it and not another
726 one.
727
728 We handle this by maintaining a permanent and a temporary default items in
729 wxControlContainer (both may be NULL). When a button becomes the current
730 control (i.e. gets focus) it sets itself as the temporary default which
731 ensures that it has the right appearance and that Enter will be redirected
732 to it. When the button loses focus, it unsets the temporary default and so
733 the default item will be the permanent default -- that is the default button
734 if any had been set or none otherwise, which is just what we want.
735
736 NB: all this is quite complicated by now and the worst is that normally
737 it shouldn't be necessary at all as for the normal Windows programs
738 DefWindowProc() and IsDialogMessage() take care of all this
739 automatically -- however in wxWidgets programs this doesn't work for
740 nested hierarchies (i.e. a notebook inside a notebook) for unknown
741 reason and so we have to reproduce all this code ourselves. It would be
742 very nice if we could avoid doing it.
743 */
744
745 // set this button as the (permanently) default one in its panel
746 wxWindow *wxButton::SetDefault()
747 {
748 // set this one as the default button both for wxWidgets ...
749 wxWindow *winOldDefault = wxButtonBase::SetDefault();
750
751 // ... and Windows
752 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
753 SetDefaultStyle(this, true);
754
755 return winOldDefault;
756 }
757
758 // return the top level parent window if it's not being deleted yet, otherwise
759 // return NULL
760 static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win)
761 {
762 for ( ;; )
763 {
764 // IsTopLevel() will return false for a wxTLW being deleted, so we also
765 // need the parent test for this case
766 wxWindow * const parent = win->GetParent();
767 if ( !parent || win->IsTopLevel() )
768 {
769 if ( win->IsBeingDeleted() )
770 return NULL;
771
772 break;
773 }
774
775 win = parent;
776 }
777
778 wxASSERT_MSG( win, wxT("button without top level parent?") );
779
780 wxTopLevelWindow * const tlw = wxDynamicCast(win, wxTopLevelWindow);
781 wxASSERT_MSG( tlw, wxT("logic error in GetTLWParentIfNotBeingDeleted()") );
782
783 return tlw;
784 }
785
786 // set this button as being currently default
787 void wxButton::SetTmpDefault()
788 {
789 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent());
790 if ( !tlw )
791 return;
792
793 wxWindow *winOldDefault = tlw->GetDefaultItem();
794 tlw->SetTmpDefaultItem(this);
795
796 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
797 SetDefaultStyle(this, true);
798 }
799
800 // unset this button as currently default, it may still stay permanent default
801 void wxButton::UnsetTmpDefault()
802 {
803 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent());
804 if ( !tlw )
805 return;
806
807 tlw->SetTmpDefaultItem(NULL);
808
809 wxWindow *winOldDefault = tlw->GetDefaultItem();
810
811 SetDefaultStyle(this, false);
812 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true);
813 }
814
815 /* static */
816 void
817 wxButton::SetDefaultStyle(wxButton *btn, bool on)
818 {
819 // we may be called with NULL pointer -- simpler to do the check here than
820 // in the caller which does wxDynamicCast()
821 if ( !btn )
822 return;
823
824 // first, let DefDlgProc() know about the new default button
825 if ( on )
826 {
827 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
828 // focus at all any more
829 if ( !wxTheApp->IsActive() )
830 return;
831
832 wxWindow * const tlw = wxGetTopLevelParent(btn);
833 wxCHECK_RET( tlw, wxT("button without top level window?") );
834
835 ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L);
836
837 // sending DM_SETDEFID also changes the button style to
838 // BS_DEFPUSHBUTTON so there is nothing more to do
839 }
840
841 // then also change the style as needed
842 long style = ::GetWindowLong(GetHwndOf(btn), GWL_STYLE);
843 if ( !(style & BS_DEFPUSHBUTTON) == on )
844 {
845 // don't do it with the owner drawn buttons because it will
846 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
847 // BS_DEFPUSHBUTTON != 0)!
848 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
849 {
850 ::SendMessage(GetHwndOf(btn), BM_SETSTYLE,
851 on ? style | BS_DEFPUSHBUTTON
852 : style & ~BS_DEFPUSHBUTTON,
853 1L /* redraw */);
854 }
855 else // owner drawn
856 {
857 // redraw the button - it will notice itself that it's
858 // [not] the default one [any longer]
859 btn->Refresh();
860 }
861 }
862 //else: already has correct style
863 }
864
865 // ----------------------------------------------------------------------------
866 // helpers
867 // ----------------------------------------------------------------------------
868
869 bool wxButton::SendClickEvent()
870 {
871 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
872 event.SetEventObject(this);
873
874 return ProcessCommand(event);
875 }
876
877 void wxButton::Command(wxCommandEvent & event)
878 {
879 ProcessCommand(event);
880 }
881
882 // ----------------------------------------------------------------------------
883 // event/message handlers
884 // ----------------------------------------------------------------------------
885
886 bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
887 {
888 bool processed = false;
889 switch ( param )
890 {
891 // NOTE: Apparently older versions (NT 4?) of the common controls send
892 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
893 // buttons, so in order to send two EVT_BUTTON events we should
894 // catch both types. Currently (Feb 2003) up-to-date versions of
895 // win98, win2k and winXP all send two BN_CLICKED messages for
896 // all button types, so we don't catch BN_DOUBLECLICKED anymore
897 // in order to not get 3 EVT_BUTTON events. If this is a problem
898 // then we need to figure out which version of the comctl32 changed
899 // this behaviour and test for it.
900
901 case 1: // message came from an accelerator
902 case BN_CLICKED: // normal buttons send this
903 processed = SendClickEvent();
904 break;
905 }
906
907 return processed;
908 }
909
910 WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
911 {
912 // when we receive focus, we want to temporarily become the default button in
913 // our parent panel so that pressing "Enter" would activate us -- and when
914 // losing it we should restore the previous default button as well
915 if ( nMsg == WM_SETFOCUS )
916 {
917 SetTmpDefault();
918
919 // let the default processing take place too
920 }
921 else if ( nMsg == WM_KILLFOCUS )
922 {
923 UnsetTmpDefault();
924 }
925 else if ( nMsg == WM_LBUTTONDBLCLK )
926 {
927 // emulate a click event to force an owner-drawn button to change its
928 // appearance - without this, it won't do it
929 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
930
931 // and continue with processing the message normally as well
932 }
933 #if wxUSE_UXTHEME
934 else if ( nMsg == WM_THEMECHANGED )
935 {
936 // need to recalculate the best size here
937 // as the theme size might have changed
938 InvalidateBestSize();
939 }
940 #endif // wxUSE_UXTHEME
941 // must use m_mouseInWindow here instead of IsMouseInWindow()
942 // since we need to know the first time the mouse enters the window
943 // and IsMouseInWindow() would return true in this case
944 else if ( (nMsg == WM_MOUSEMOVE && !m_mouseInWindow) ||
945 nMsg == WM_MOUSELEAVE )
946 {
947 if (
948 IsEnabled() &&
949 (
950 #if wxUSE_UXTHEME
951 wxUxThemeEngine::GetIfActive() ||
952 #endif // wxUSE_UXTHEME
953 (m_imageData && m_imageData->GetBitmap(State_Current).IsOk())
954 )
955 )
956 {
957 Refresh();
958 }
959 }
960
961 // let the base class do all real processing
962 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
963 }
964
965 // ----------------------------------------------------------------------------
966 // authentication needed handling
967 // ----------------------------------------------------------------------------
968
969 bool wxButton::DoGetAuthNeeded() const
970 {
971 return m_authNeeded;
972 }
973
974 void wxButton::DoSetAuthNeeded(bool show)
975 {
976 // show/hide UAC symbol on Windows Vista and later
977 if ( wxGetWinVersion() >= wxWinVersion_6 )
978 {
979 m_authNeeded = show;
980 ::SendMessage(GetHwnd(), BCM_SETSHIELD, 0, show);
981 InvalidateBestSize();
982 }
983 }
984
985 // ----------------------------------------------------------------------------
986 // button images
987 // ----------------------------------------------------------------------------
988
989 wxBitmap wxButton::DoGetBitmap(State which) const
990 {
991 return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap();
992 }
993
994 void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
995 {
996 #if wxUSE_UXTHEME
997 wxXPButtonImageData *oldData = NULL;
998 #endif // wxUSE_UXTHEME
999
1000 // Check if we already had bitmaps of different size.
1001 if ( m_imageData &&
1002 bitmap.GetSize() != m_imageData->GetBitmap(State_Normal).GetSize() )
1003 {
1004 wxASSERT_MSG( which == State_Normal,
1005 "Must set normal bitmap with the new size first" );
1006
1007 #if wxUSE_UXTHEME
1008 if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
1009 {
1010 // We can't change the size of the images stored in wxImageList
1011 // in wxXPButtonImageData::m_iml so force recreating it below but
1012 // keep the current data to copy its values into the new one.
1013 oldData = static_cast<wxXPButtonImageData *>(m_imageData);
1014 m_imageData = NULL;
1015 }
1016 #endif // wxUSE_UXTHEME
1017 //else: wxODButtonImageData doesn't require anything special
1018 }
1019
1020 // allocate the image data when the first bitmap is set
1021 if ( !m_imageData )
1022 {
1023 #if wxUSE_UXTHEME
1024 // using image list doesn't work correctly if we don't have any label
1025 // (even if we use BUTTON_IMAGELIST_ALIGN_CENTER alignment and
1026 // BS_BITMAP style), at least under Windows 2003 so use owner drawn
1027 // strategy for bitmap-only buttons
1028 if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
1029 {
1030 m_imageData = new wxXPButtonImageData(this, bitmap);
1031
1032 if ( oldData )
1033 {
1034 // Preserve the old values in case the user changed them.
1035 m_imageData->SetBitmapPosition(oldData->GetBitmapPosition());
1036
1037 const wxSize oldMargins = oldData->GetBitmapMargins();
1038 m_imageData->SetBitmapMargins(oldMargins.x, oldMargins.y);
1039
1040 // No need to preserve the bitmaps though as they were of wrong
1041 // size anyhow.
1042
1043 delete oldData;
1044 }
1045 }
1046 else
1047 #endif // wxUSE_UXTHEME
1048 {
1049 m_imageData = new wxODButtonImageData(this, bitmap);
1050 MakeOwnerDrawn();
1051 }
1052 }
1053 else
1054 {
1055 m_imageData->SetBitmap(bitmap, which);
1056 }
1057
1058 // it should be enough to only invalidate the best size when the normal
1059 // bitmap changes as all bitmaps assigned to the button should be of the
1060 // same size anyhow
1061 if ( which == State_Normal )
1062 InvalidateBestSize();
1063
1064 Refresh();
1065 }
1066
1067 wxSize wxButton::DoGetBitmapMargins() const
1068 {
1069 return m_imageData ? m_imageData->GetBitmapMargins() : wxSize(0, 0);
1070 }
1071
1072 void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
1073 {
1074 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
1075
1076 m_imageData->SetBitmapMargins(x, y);
1077 InvalidateBestSize();
1078 }
1079
1080 void wxButton::DoSetBitmapPosition(wxDirection dir)
1081 {
1082 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
1083
1084 m_imageData->SetBitmapPosition(dir);
1085 InvalidateBestSize();
1086 }
1087
1088 // ----------------------------------------------------------------------------
1089 // markup support
1090 // ----------------------------------------------------------------------------
1091
1092 #if wxUSE_MARKUP
1093
1094 bool wxButton::DoSetLabelMarkup(const wxString& markup)
1095 {
1096 if ( !wxButtonBase::DoSetLabelMarkup(markup) )
1097 return false;
1098
1099 if ( !m_markupText )
1100 {
1101 m_markupText = new wxMarkupText(markup);
1102 MakeOwnerDrawn();
1103 }
1104 else
1105 {
1106 // We are already owner-drawn so just update the text.
1107 m_markupText->SetMarkup(markup);
1108 }
1109
1110 Refresh();
1111
1112 return true;
1113 }
1114
1115 #endif // wxUSE_MARKUP
1116
1117 // ----------------------------------------------------------------------------
1118 // owner-drawn buttons support
1119 // ----------------------------------------------------------------------------
1120
1121 // drawing helpers
1122 namespace
1123 {
1124
1125 // return the button state using both the ODS_XXX flags specified in state
1126 // parameter and the current button state
1127 wxButton::State GetButtonState(wxButton *btn, UINT state)
1128 {
1129 if ( state & ODS_DISABLED )
1130 return wxButton::State_Disabled;
1131
1132 if ( state & ODS_SELECTED )
1133 return wxButton::State_Pressed;
1134
1135 if ( btn->HasCapture() || btn->IsMouseInWindow() )
1136 return wxButton::State_Current;
1137
1138 if ( state & ODS_FOCUS )
1139 return wxButton::State_Focused;
1140
1141 return wxButton::State_Normal;
1142 }
1143
1144 void DrawButtonText(HDC hdc,
1145 RECT *pRect,
1146 wxButton *btn,
1147 int flags)
1148 {
1149 const wxString text = btn->GetLabel();
1150
1151 if ( text.find(wxT('\n')) != wxString::npos )
1152 {
1153 // draw multiline label
1154
1155 // center text horizontally in any case
1156 flags |= DT_CENTER;
1157
1158 // first we need to compute its bounding rect
1159 RECT rc;
1160 ::CopyRect(&rc, pRect);
1161 ::DrawText(hdc, text.wx_str(), text.length(), &rc,
1162 DT_CENTER | DT_CALCRECT);
1163
1164 // now center this rect inside the entire button area
1165 const LONG w = rc.right - rc.left;
1166 const LONG h = rc.bottom - rc.top;
1167 rc.left = (pRect->right - pRect->left)/2 - w/2;
1168 rc.right = rc.left+w;
1169 rc.top = (pRect->bottom - pRect->top)/2 - h/2;
1170 rc.bottom = rc.top+h;
1171
1172 ::DrawText(hdc, text.wx_str(), text.length(), &rc, flags);
1173 }
1174 else // single line label
1175 {
1176 // translate wx button flags to alignment flags for DrawText()
1177 if ( btn->HasFlag(wxBU_RIGHT) )
1178 {
1179 flags |= DT_RIGHT;
1180 }
1181 else if ( !btn->HasFlag(wxBU_LEFT) )
1182 {
1183 flags |= DT_CENTER;
1184 }
1185 //else: DT_LEFT is the default anyhow (and its value is 0 too)
1186
1187 if ( btn->HasFlag(wxBU_BOTTOM) )
1188 {
1189 flags |= DT_BOTTOM;
1190 }
1191 else if ( !btn->HasFlag(wxBU_TOP) )
1192 {
1193 flags |= DT_VCENTER;
1194 }
1195 //else: as above, DT_TOP is the default
1196
1197 // notice that we must have DT_SINGLELINE for vertical alignment flags
1198 // to work
1199 ::DrawText(hdc, text.wx_str(), text.length(), pRect,
1200 flags | DT_SINGLELINE );
1201 }
1202 }
1203
1204 void DrawRect(HDC hdc, const RECT& r)
1205 {
1206 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1207 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
1208 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
1209 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
1210 }
1211
1212 /*
1213 The button frame looks like this normally:
1214
1215 WWWWWWWWWWWWWWWWWWB
1216 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
1217 WH GB H = light grey (LIGHT)
1218 WH GB G = dark grey (SHADOW)
1219 WH GB B = black (DKSHADOW)
1220 WH GB
1221 WGGGGGGGGGGGGGGGGGB
1222 BBBBBBBBBBBBBBBBBBB
1223
1224 When the button is selected, the button becomes like this (the total button
1225 size doesn't change):
1226
1227 BBBBBBBBBBBBBBBBBBB
1228 BWWWWWWWWWWWWWWWWBB
1229 BWHHHHHHHHHHHHHHGBB
1230 BWH GBB
1231 BWH GBB
1232 BWGGGGGGGGGGGGGGGBB
1233 BBBBBBBBBBBBBBBBBBB
1234 BBBBBBBBBBBBBBBBBBB
1235
1236 When the button is pushed (while selected) it is like:
1237
1238 BBBBBBBBBBBBBBBBBBB
1239 BGGGGGGGGGGGGGGGGGB
1240 BG GB
1241 BG GB
1242 BG GB
1243 BG GB
1244 BGGGGGGGGGGGGGGGGGB
1245 BBBBBBBBBBBBBBBBBBB
1246 */
1247 void DrawButtonFrame(HDC hdc, RECT& rectBtn,
1248 bool selected, bool pushed)
1249 {
1250 RECT r;
1251 CopyRect(&r, &rectBtn);
1252
1253 AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
1254 hpenGrey(GetSysColor(COLOR_3DSHADOW)),
1255 hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
1256 hpenWhite(GetSysColor(COLOR_3DHILIGHT));
1257
1258 SelectInHDC selectPen(hdc, hpenBlack);
1259
1260 r.right--;
1261 r.bottom--;
1262
1263 if ( pushed )
1264 {
1265 DrawRect(hdc, r);
1266
1267 (void)SelectObject(hdc, hpenGrey);
1268 ::InflateRect(&r, -1, -1);
1269
1270 DrawRect(hdc, r);
1271 }
1272 else // !pushed
1273 {
1274 if ( selected )
1275 {
1276 DrawRect(hdc, r);
1277
1278 ::InflateRect(&r, -1, -1);
1279 }
1280
1281 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
1282 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
1283
1284 (void)SelectObject(hdc, hpenWhite);
1285 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
1286 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1287
1288 (void)SelectObject(hdc, hpenLightGr);
1289 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
1290 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
1291
1292 (void)SelectObject(hdc, hpenGrey);
1293 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
1294 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
1295 }
1296
1297 InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
1298 }
1299
1300 #if wxUSE_UXTHEME
1301 void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
1302 {
1303 wxUxThemeHandle theme(button, L"BUTTON");
1304
1305 // this array is indexed by wxButton::State values and so must be kept in
1306 // sync with it
1307 static const int uxStates[] =
1308 {
1309 PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
1310 };
1311
1312 int iState = uxStates[GetButtonState(button, state)];
1313
1314 wxUxThemeEngine * const engine = wxUxThemeEngine::Get();
1315
1316 // draw parent background if needed
1317 if ( engine->IsThemeBackgroundPartiallyTransparent
1318 (
1319 theme,
1320 BP_PUSHBUTTON,
1321 iState
1322 ) )
1323 {
1324 // Set this button as the one whose background is being erased: this
1325 // allows our WM_ERASEBKGND handler used by DrawThemeParentBackground()
1326 // to correctly align the background brush with this window instead of
1327 // the parent window to which WM_ERASEBKGND is sent. Notice that this
1328 // doesn't work with custom user-defined EVT_ERASE_BACKGROUND handlers
1329 // as they won't be aligned but unfortunately all the attempts to fix
1330 // it by shifting DC origin before calling DrawThemeParentBackground()
1331 // failed to work so we at least do this, even though this is far from
1332 // being the perfect solution.
1333 wxWindowBeingErased = button;
1334
1335 engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
1336
1337 wxWindowBeingErased = NULL;
1338 }
1339
1340 // draw background
1341 engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
1342 &rectBtn, NULL);
1343
1344 // calculate content area margins
1345 MARGINS margins;
1346 engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
1347 TMT_CONTENTMARGINS, &rectBtn, &margins);
1348 ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
1349 ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
1350
1351 if ( button->UseBgCol() )
1352 {
1353 COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
1354 AutoHBRUSH hbrushBackground(colBg);
1355
1356 // don't overwrite the focus rect
1357 RECT rectClient;
1358 ::CopyRect(&rectClient, &rectBtn);
1359 ::InflateRect(&rectClient, -1, -1);
1360 FillRect(hdc, &rectClient, hbrushBackground);
1361 }
1362 }
1363 #endif // wxUSE_UXTHEME
1364
1365 } // anonymous namespace
1366
1367 // ----------------------------------------------------------------------------
1368 // owner drawn buttons support
1369 // ----------------------------------------------------------------------------
1370
1371 void wxButton::MakeOwnerDrawn()
1372 {
1373 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1374 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
1375 {
1376 // make it so
1377 style |= BS_OWNERDRAW;
1378 SetWindowLong(GetHwnd(), GWL_STYLE, style);
1379 }
1380 }
1381
1382 bool wxButton::SetBackgroundColour(const wxColour &colour)
1383 {
1384 if ( !wxControl::SetBackgroundColour(colour) )
1385 {
1386 // nothing to do
1387 return false;
1388 }
1389
1390 MakeOwnerDrawn();
1391
1392 Refresh();
1393
1394 return true;
1395 }
1396
1397 bool wxButton::SetForegroundColour(const wxColour &colour)
1398 {
1399 if ( !wxControl::SetForegroundColour(colour) )
1400 {
1401 // nothing to do
1402 return false;
1403 }
1404
1405 MakeOwnerDrawn();
1406
1407 Refresh();
1408
1409 return true;
1410 }
1411
1412 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
1413 {
1414 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
1415 HDC hdc = lpDIS->hDC;
1416
1417 UINT state = lpDIS->itemState;
1418 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
1419
1420 RECT rectBtn;
1421 CopyRect(&rectBtn, &lpDIS->rcItem);
1422
1423 // draw the button background
1424 if ( !HasFlag(wxBORDER_NONE) )
1425 {
1426 #if wxUSE_UXTHEME
1427 if ( wxUxThemeEngine::GetIfActive() )
1428 {
1429 DrawXPBackground(this, hdc, rectBtn, state);
1430 }
1431 else
1432 #endif // wxUSE_UXTHEME
1433 {
1434 COLORREF colBg = wxColourToRGB(GetBackgroundColour());
1435
1436 // first, draw the background
1437 AutoHBRUSH hbrushBackground(colBg);
1438 FillRect(hdc, &rectBtn, hbrushBackground);
1439
1440 // draw the border for the current state
1441 bool selected = (state & ODS_SELECTED) != 0;
1442 if ( !selected )
1443 {
1444 wxTopLevelWindow *
1445 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
1446 if ( tlw )
1447 {
1448 selected = tlw->GetDefaultItem() == this;
1449 }
1450 }
1451
1452 DrawButtonFrame(hdc, rectBtn, selected, pushed);
1453 }
1454
1455 // draw the focus rectangle if we need it
1456 if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
1457 {
1458 DrawFocusRect(hdc, &rectBtn);
1459
1460 #if wxUSE_UXTHEME
1461 if ( !wxUxThemeEngine::GetIfActive() )
1462 #endif // wxUSE_UXTHEME
1463 {
1464 if ( pushed )
1465 {
1466 // the label is shifted by 1 pixel to create "pushed" effect
1467 OffsetRect(&rectBtn, 1, 1);
1468 }
1469 }
1470 }
1471 }
1472
1473
1474 // draw the image, if any
1475 if ( m_imageData )
1476 {
1477 wxBitmap bmp = m_imageData->GetBitmap(GetButtonState(this, state));
1478 if ( !bmp.IsOk() )
1479 bmp = m_imageData->GetBitmap(State_Normal);
1480
1481 const wxSize sizeBmp = bmp.GetSize();
1482 const wxSize margin = m_imageData->GetBitmapMargins();
1483 const wxSize sizeBmpWithMargins(sizeBmp + 2*margin);
1484 wxRect rectButton(wxRectFromRECT(rectBtn));
1485
1486 // for simplicity, we start with centred rectangle and then move it to
1487 // the appropriate edge
1488 wxRect rectBitmap = wxRect(sizeBmp).CentreIn(rectButton);
1489
1490 // move bitmap only if we have a label, otherwise keep it centered
1491 if ( ShowsLabel() )
1492 {
1493 switch ( m_imageData->GetBitmapPosition() )
1494 {
1495 default:
1496 wxFAIL_MSG( "invalid direction" );
1497 // fall through
1498
1499 case wxLEFT:
1500 rectBitmap.x = rectButton.x + margin.x;
1501 rectButton.x += sizeBmpWithMargins.x;
1502 rectButton.width -= sizeBmpWithMargins.x;
1503 break;
1504
1505 case wxRIGHT:
1506 rectBitmap.x = rectButton.GetRight() - sizeBmp.x - margin.x;
1507 rectButton.width -= sizeBmpWithMargins.x;
1508 break;
1509
1510 case wxTOP:
1511 rectBitmap.y = rectButton.y + margin.y;
1512 rectButton.y += sizeBmpWithMargins.y;
1513 rectButton.height -= sizeBmpWithMargins.y;
1514 break;
1515
1516 case wxBOTTOM:
1517 rectBitmap.y = rectButton.GetBottom() - sizeBmp.y - margin.y;
1518 rectButton.height -= sizeBmpWithMargins.y;
1519 break;
1520 }
1521 }
1522
1523 wxDCTemp dst((WXHDC)hdc);
1524 dst.DrawBitmap(bmp, rectBitmap.GetPosition(), true);
1525
1526 wxCopyRectToRECT(rectButton, rectBtn);
1527 }
1528
1529
1530 // finally draw the label
1531 if ( ShowsLabel() )
1532 {
1533 COLORREF colFg = state & ODS_DISABLED
1534 ? ::GetSysColor(COLOR_GRAYTEXT)
1535 : wxColourToRGB(GetForegroundColour());
1536
1537 wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
1538 wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
1539
1540 #if wxUSE_MARKUP
1541 if ( m_markupText )
1542 {
1543 wxDCTemp dc((WXHDC)hdc);
1544 dc.SetTextForeground(wxColour(colFg));
1545 dc.SetFont(GetFont());
1546
1547 m_markupText->Render(dc, wxRectFromRECT(rectBtn),
1548 state & ODS_NOACCEL
1549 ? wxMarkupText::Render_Default
1550 : wxMarkupText::Render_ShowAccels);
1551 }
1552 else // Plain text label
1553 #endif // wxUSE_MARKUP
1554 {
1555 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
1556 // systems but by happy coincidence ODS_NOACCEL is not used under
1557 // them neither so DT_HIDEPREFIX should never be used there
1558 DrawButtonText(hdc, &rectBtn, this,
1559 state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
1560 }
1561 }
1562
1563 return true;
1564 }
1565
1566 #endif // wxUSE_BUTTON
1567