1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/anybutton.cpp
3 // Purpose:     wxAnyButton
4 // Author:      Julian Smart
5 // Created:     1998-01-04 (extracted from button.cpp)
6 // Copyright:   (c) Julian Smart
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #ifdef wxHAS_ANY_BUTTON
26 
27 #include "wx/anybutton.h"
28 
29 #ifndef WX_PRECOMP
30     #include "wx/app.h"
31     #include "wx/brush.h"
32     #include "wx/panel.h"
33     #include "wx/bmpbuttn.h"
34     #include "wx/settings.h"
35     #include "wx/dcscreen.h"
36     #include "wx/dcclient.h"
37     #include "wx/toplevel.h"
38     #include "wx/msw/wrapcctl.h"
39     #include "wx/msw/private.h"
40     #include "wx/msw/missing.h"
41 #endif
42 
43 #include "wx/imaglist.h"
44 #include "wx/stockitem.h"
45 #include "wx/msw/private/button.h"
46 #include "wx/msw/private/dc.h"
47 #include "wx/private/window.h"
48 
49 #if wxUSE_MARKUP
50     #include "wx/generic/private/markuptext.h"
51 #endif // wxUSE_MARKUP
52 
53 using namespace wxMSWImpl;
54 
55 #if wxUSE_UXTHEME
56     #include "wx/msw/uxtheme.h"
57 
58     // no need to include tmschema.h
59     #ifndef BP_PUSHBUTTON
60         #define BP_PUSHBUTTON 1
61 
62         #define PBS_NORMAL    1
63         #define PBS_HOT       2
64         #define PBS_PRESSED   3
65         #define PBS_DISABLED  4
66         #define PBS_DEFAULTED 5
67 
68         #define TMT_CONTENTMARGINS 3602
69     #endif
70 
71     // provide the necessary declarations ourselves if they're missing from
72     // headers
73     #ifndef BCM_SETIMAGELIST
74         #define BCM_SETIMAGELIST    0x1602
75         #define BCM_SETTEXTMARGIN   0x1604
76 
77         enum
78         {
79             BUTTON_IMAGELIST_ALIGN_LEFT,
80             BUTTON_IMAGELIST_ALIGN_RIGHT,
81             BUTTON_IMAGELIST_ALIGN_TOP,
82             BUTTON_IMAGELIST_ALIGN_BOTTOM
83         };
84 
85         struct BUTTON_IMAGELIST
86         {
87             HIMAGELIST himl;
88             RECT margin;
89             UINT uAlign;
90         };
91     #endif
92 #endif // wxUSE_UXTHEME
93 
94 #ifndef WM_THEMECHANGED
95     #define WM_THEMECHANGED     0x031A
96 #endif
97 
98 #ifndef ODS_NOACCEL
99     #define ODS_NOACCEL         0x0100
100 #endif
101 
102 #ifndef ODS_NOFOCUSRECT
103     #define ODS_NOFOCUSRECT     0x0200
104 #endif
105 
106 #if wxUSE_UXTHEME
107 extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp
108 #endif // wxUSE_UXTHEME
109 
110 // ----------------------------------------------------------------------------
111 // button image data
112 // ----------------------------------------------------------------------------
113 
114 // we use different data classes for owner drawn buttons and for themed XP ones
115 
116 class wxButtonImageData
117 {
118 public:
wxButtonImageData()119     wxButtonImageData() { }
~wxButtonImageData()120     virtual ~wxButtonImageData() { }
121 
122     virtual wxBitmap GetBitmap(wxAnyButton::State which) const = 0;
123     virtual void SetBitmap(const wxBitmap& bitmap, wxAnyButton::State which) = 0;
124 
125     virtual wxSize GetBitmapMargins() const = 0;
126     virtual void SetBitmapMargins(wxCoord x, wxCoord y) = 0;
127 
128     virtual wxDirection GetBitmapPosition() const = 0;
129     virtual void SetBitmapPosition(wxDirection dir) = 0;
130 
131 private:
132     wxDECLARE_NO_COPY_CLASS(wxButtonImageData);
133 };
134 
135 namespace
136 {
137 
138 // the gap between button edge and the interior area used by Windows for the
139 // standard buttons
140 const int OD_BUTTON_MARGIN = 4;
141 
142 class wxODButtonImageData : public wxButtonImageData
143 {
144 public:
wxODButtonImageData(wxAnyButton * btn,const wxBitmap & bitmap)145     wxODButtonImageData(wxAnyButton *btn, const wxBitmap& bitmap)
146     {
147         SetBitmap(bitmap, wxAnyButton::State_Normal);
148 #if wxUSE_IMAGE
149         SetBitmap(bitmap.ConvertToDisabled(), wxAnyButton::State_Disabled);
150 #endif
151         m_dir = wxLEFT;
152 
153         // we use margins when we have both bitmap and text, but when we have
154         // only the bitmap it should take up the entire button area
155         if ( btn->ShowsLabel() )
156         {
157             m_margin.x = btn->GetCharWidth();
158             m_margin.y = btn->GetCharHeight() / 2;
159         }
160     }
161 
GetBitmap(wxAnyButton::State which) const162     virtual wxBitmap GetBitmap(wxAnyButton::State which) const
163     {
164         return m_bitmaps[which];
165     }
166 
SetBitmap(const wxBitmap & bitmap,wxAnyButton::State which)167     virtual void SetBitmap(const wxBitmap& bitmap, wxAnyButton::State which)
168     {
169         m_bitmaps[which] = bitmap;
170     }
171 
GetBitmapMargins() const172     virtual wxSize GetBitmapMargins() const
173     {
174         return m_margin;
175     }
176 
SetBitmapMargins(wxCoord x,wxCoord y)177     virtual void SetBitmapMargins(wxCoord x, wxCoord y)
178     {
179         m_margin = wxSize(x, y);
180     }
181 
GetBitmapPosition() const182     virtual wxDirection GetBitmapPosition() const
183     {
184         return m_dir;
185     }
186 
SetBitmapPosition(wxDirection dir)187     virtual void SetBitmapPosition(wxDirection dir)
188     {
189         m_dir = dir;
190     }
191 
192 private:
193     // just store the values passed to us to be able to retrieve them later
194     // from the drawing code
195     wxBitmap m_bitmaps[wxAnyButton::State_Max];
196     wxSize m_margin;
197     wxDirection m_dir;
198 
199     wxDECLARE_NO_COPY_CLASS(wxODButtonImageData);
200 };
201 
202 #if wxUSE_UXTHEME
203 
204 // somehow the margin is one pixel greater than the value returned by
205 // GetThemeMargins() call
206 const int XP_BUTTON_EXTRA_MARGIN = 1;
207 
208 class wxXPButtonImageData : public wxButtonImageData
209 {
210 public:
211     // we must be constructed with the size of our images as we need to create
212     // the image list
wxXPButtonImageData(wxAnyButton * btn,const wxBitmap & bitmap)213     wxXPButtonImageData(wxAnyButton *btn, const wxBitmap& bitmap)
214         : m_iml(bitmap.GetWidth(), bitmap.GetHeight(),
215                 !bitmap.HasAlpha() /* use mask only if no alpha */,
216                 wxAnyButton::State_Max + 1 /* see "pulse" comment below */),
217           m_hwndBtn(GetHwndOf(btn))
218     {
219         // initialize all bitmaps except for the disabled one to normal state
220         for ( int n = 0; n < wxAnyButton::State_Max; n++ )
221         {
222 #if wxUSE_IMAGE
223             m_iml.Add(n == wxAnyButton::State_Disabled ? bitmap.ConvertToDisabled()
224                                                     : bitmap);
225 #else
226             m_iml.Add(bitmap);
227 #endif
228         }
229 
230         // In addition to the states supported by wxWidgets such as normal,
231         // hot, pressed, disabled and focused, we need to add bitmap for
232         // another state when running under Windows 7 -- the so called "stylus
233         // hot" state corresponding to PBS_STYLUSHOT constant. While it's
234         // documented in MSDN as being only used with tablets, it is a lie as
235         // a focused button actually alternates between the image list elements
236         // with PBS_DEFAULTED and PBS_STYLUSHOT indices and, in particular,
237         // just disappears during half of the time if the latter is not set so
238         // we absolutely must set it.
239         //
240         // This also explains why we need to allocate an extra slot in the
241         // image list ctor above, the slot State_Max is used for this one.
242         m_iml.Add(bitmap);
243 
244         m_data.himl = GetHimagelistOf(&m_iml);
245 
246         // no margins by default
247         m_data.margin.left =
248         m_data.margin.right =
249         m_data.margin.top =
250         m_data.margin.bottom = 0;
251 
252         // use default alignment
253         m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
254 
255         UpdateImageInfo();
256     }
257 
GetBitmap(wxAnyButton::State which) const258     virtual wxBitmap GetBitmap(wxAnyButton::State which) const
259     {
260         return m_iml.GetBitmap(which);
261     }
262 
SetBitmap(const wxBitmap & bitmap,wxAnyButton::State which)263     virtual void SetBitmap(const wxBitmap& bitmap, wxAnyButton::State which)
264     {
265         m_iml.Replace(which, bitmap);
266 
267         // As we want the focused button to always show its bitmap, we need to
268         // update the "stylus hot" one to match it to avoid any pulsing.
269         if ( which == wxAnyButton::State_Focused )
270             m_iml.Replace(wxAnyButton::State_Max, bitmap);
271 
272         UpdateImageInfo();
273     }
274 
GetBitmapMargins() const275     virtual wxSize GetBitmapMargins() const
276     {
277         return wxSize(m_data.margin.left, m_data.margin.top);
278     }
279 
SetBitmapMargins(wxCoord x,wxCoord y)280     virtual void SetBitmapMargins(wxCoord x, wxCoord y)
281     {
282         RECT& margin = m_data.margin;
283         margin.left =
284         margin.right = x;
285         margin.top =
286         margin.bottom = y;
287 
288         if ( !::SendMessage(m_hwndBtn, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin) )
289         {
290             wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed");
291         }
292     }
293 
GetBitmapPosition() const294     virtual wxDirection GetBitmapPosition() const
295     {
296         switch ( m_data.uAlign )
297         {
298             default:
299                 wxFAIL_MSG( "invalid image alignment" );
300                 // fall through
301 
302             case BUTTON_IMAGELIST_ALIGN_LEFT:
303                 return wxLEFT;
304 
305             case BUTTON_IMAGELIST_ALIGN_RIGHT:
306                 return wxRIGHT;
307 
308             case BUTTON_IMAGELIST_ALIGN_TOP:
309                 return wxTOP;
310 
311             case BUTTON_IMAGELIST_ALIGN_BOTTOM:
312                 return wxBOTTOM;
313         }
314     }
315 
SetBitmapPosition(wxDirection dir)316     virtual void SetBitmapPosition(wxDirection dir)
317     {
318         UINT alignNew;
319         switch ( dir )
320         {
321             default:
322                 wxFAIL_MSG( "invalid direction" );
323                 // fall through
324 
325             case wxLEFT:
326                 alignNew = BUTTON_IMAGELIST_ALIGN_LEFT;
327                 break;
328 
329             case wxRIGHT:
330                 alignNew = BUTTON_IMAGELIST_ALIGN_RIGHT;
331                 break;
332 
333             case wxTOP:
334                 alignNew = BUTTON_IMAGELIST_ALIGN_TOP;
335                 break;
336 
337             case wxBOTTOM:
338                 alignNew = BUTTON_IMAGELIST_ALIGN_BOTTOM;
339                 break;
340         }
341 
342         if ( alignNew != m_data.uAlign )
343         {
344             m_data.uAlign = alignNew;
345             UpdateImageInfo();
346         }
347     }
348 
349 private:
UpdateImageInfo()350     void UpdateImageInfo()
351     {
352         if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) )
353         {
354             wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed");
355         }
356     }
357 
358     // we store image list separately to be able to use convenient wxImageList
359     // methods instead of working with raw HIMAGELIST
360     wxImageList m_iml;
361 
362     // store the rest of the data in BCM_SETIMAGELIST-friendly form
363     BUTTON_IMAGELIST m_data;
364 
365     // the button we're associated with
366     const HWND m_hwndBtn;
367 
368 
369     wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData);
370 };
371 
372 #endif // wxUSE_UXTHEME
373 
374 } // anonymous namespace
375 
376 // ----------------------------------------------------------------------------
377 // macros
378 // ----------------------------------------------------------------------------
379 
380 // ============================================================================
381 // implementation
382 // ============================================================================
383 
384 // ----------------------------------------------------------------------------
385 // helper functions from wx/msw/private/button.h
386 // ----------------------------------------------------------------------------
387 
UpdateMultilineStyle(HWND hwnd,const wxString & label)388 void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label)
389 {
390     // update BS_MULTILINE style depending on the new label (resetting it
391     // doesn't seem to do anything very useful but it shouldn't hurt and we do
392     // have to set it whenever the label becomes multi line as otherwise it
393     // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
394     // the control unless it already has new lines in its label)
395     long styleOld = ::GetWindowLong(hwnd, GWL_STYLE),
396          styleNew;
397     if ( label.find(wxT('\n')) != wxString::npos )
398         styleNew = styleOld | BS_MULTILINE;
399     else
400         styleNew = styleOld & ~BS_MULTILINE;
401 
402     if ( styleNew != styleOld )
403         ::SetWindowLong(hwnd, GWL_STYLE, styleNew);
404 }
405 
GetFittingSize(wxWindow * win,const wxSize & sizeLabel,int flags)406 wxSize wxMSWButton::GetFittingSize(wxWindow *win,
407                                    const wxSize& sizeLabel,
408                                    int flags)
409 {
410     wxSize sizeBtn = sizeLabel;
411 
412     // FIXME: The numbers here are pure guesswork, no idea how should the
413     //        button margins be really calculated.
414     if ( flags & Size_ExactFit )
415     {
416         // We still need some margin or the text would be overwritten, just
417         // make it as small as possible.
418         sizeBtn.x += (3*win->GetCharWidth());
419     }
420     else
421     {
422         sizeBtn.x += 3*win->GetCharWidth();
423         sizeBtn.y += win->GetCharHeight()/2;
424     }
425 
426     // account for the shield UAC icon if we have it
427     if ( flags & Size_AuthNeeded )
428         sizeBtn.x += wxSystemSettings::GetMetric(wxSYS_SMALLICON_X);
429 
430     return sizeBtn;
431 }
432 
ComputeBestFittingSize(wxControl * btn,int flags)433 wxSize wxMSWButton::ComputeBestFittingSize(wxControl *btn, int flags)
434 {
435     wxClientDC dc(btn);
436 
437     wxSize sizeBtn;
438     dc.GetMultiLineTextExtent(btn->GetLabelText(), &sizeBtn.x, &sizeBtn.y);
439 
440     return GetFittingSize(btn, sizeBtn, flags);
441 }
442 
IncreaseToStdSizeAndCache(wxControl * btn,const wxSize & size)443 wxSize wxMSWButton::IncreaseToStdSizeAndCache(wxControl *btn, const wxSize& size)
444 {
445     wxSize sizeBtn(size);
446 
447     // The 50x14 button size is documented in the "Recommended sizing and
448     // spacing" section of MSDN layout article.
449     //
450     // Note that we intentionally don't use GetDefaultSize() here, because
451     // it's inexact -- dialog units depend on this dialog's font.
452     const wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
453 
454     // All buttons should have at least the standard size, unless the user
455     // explicitly wants them to be as small as possible and used wxBU_EXACTFIT
456     // style to indicate this.
457     const bool incToStdSize = !btn->HasFlag(wxBU_EXACTFIT);
458     if ( incToStdSize )
459     {
460         if ( sizeBtn.x < sizeDef.x )
461             sizeBtn.x = sizeDef.x;
462     }
463 
464     // Notice that we really want to make all buttons with text label equally
465     // high, otherwise they look ugly and the existing code using wxBU_EXACTFIT
466     // only uses it to control width and not height.
467     if ( incToStdSize || !btn->GetLabel().empty() )
468     {
469         if ( sizeBtn.y < sizeDef.y )
470             sizeBtn.y = sizeDef.y;
471     }
472 
473     btn->CacheBestSize(sizeBtn);
474 
475     return sizeBtn;
476 }
477 
478 // ----------------------------------------------------------------------------
479 // creation/destruction
480 // ----------------------------------------------------------------------------
481 
~wxAnyButton()482 wxAnyButton::~wxAnyButton()
483 {
484     delete m_imageData;
485 #if wxUSE_MARKUP
486     delete m_markupText;
487 #endif // wxUSE_MARKUP
488 }
489 
SetLabel(const wxString & label)490 void wxAnyButton::SetLabel(const wxString& label)
491 {
492     wxMSWButton::UpdateMultilineStyle(GetHwnd(), label);
493 
494     wxAnyButtonBase::SetLabel(label);
495 
496 #if wxUSE_MARKUP
497     // If we have a plain text label, we shouldn't be using markup any longer.
498     if ( m_markupText )
499     {
500         delete m_markupText;
501         m_markupText = NULL;
502 
503         // Unfortunately we don't really know whether we can reset the button
504         // to be non-owner-drawn or not: if we had made it owner-drawn just
505         // because of a call to SetLabelMarkup(), we could, but not if there
506         // were [also] calls to Set{Fore,Back}groundColour(). If it's really a
507         // problem to have button remain owner-drawn forever just because it
508         // had markup label once, we should record the reason for our current
509         // owner-drawnness and check it here.
510     }
511 #endif // wxUSE_MARKUP
512 }
513 
514 // ----------------------------------------------------------------------------
515 // size management including autosizing
516 // ----------------------------------------------------------------------------
517 
AdjustForBitmapSize(wxSize & size) const518 void wxAnyButton::AdjustForBitmapSize(wxSize &size) const
519 {
520     wxCHECK_RET( m_imageData, wxT("shouldn't be called if no image") );
521 
522     // account for the bitmap size, including the user-specified margins
523     const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize()
524                                 + 2*m_imageData->GetBitmapMargins();
525     const wxDirection dirBmp = m_imageData->GetBitmapPosition();
526     if ( dirBmp == wxLEFT || dirBmp == wxRIGHT )
527     {
528         size.x += sizeBmp.x;
529         if ( sizeBmp.y > size.y )
530             size.y = sizeBmp.y;
531     }
532     else // bitmap on top/below the text
533     {
534         size.y += sizeBmp.y;
535         if ( sizeBmp.x > size.x )
536             size.x = sizeBmp.x;
537     }
538 
539     // and also for the margins we always add internally (unless we have no
540     // border at all in which case the button has exactly the same size as
541     // bitmap and so no margins should be used)
542     if ( !HasFlag(wxBORDER_NONE) )
543     {
544         int marginH = 0,
545             marginV = 0;
546 #if wxUSE_UXTHEME
547         if ( wxUxThemeEngine::GetIfActive() )
548         {
549             wxUxThemeHandle theme(const_cast<wxAnyButton *>(this), L"BUTTON");
550 
551             MARGINS margins;
552             wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
553                                                     BP_PUSHBUTTON,
554                                                     PBS_NORMAL,
555                                                     TMT_CONTENTMARGINS,
556                                                     NULL,
557                                                     &margins);
558 
559             // XP doesn't draw themed buttons correctly when the client
560             // area is smaller than 8x8 - enforce this minimum size for
561             // small bitmaps
562             size.IncTo(wxSize(8, 8));
563 
564             marginH = margins.cxLeftWidth + margins.cxRightWidth
565                         + 2*XP_BUTTON_EXTRA_MARGIN;
566             marginV = margins.cyTopHeight + margins.cyBottomHeight
567                         + 2*XP_BUTTON_EXTRA_MARGIN;
568         }
569         else
570 #endif // wxUSE_UXTHEME
571         {
572             marginH =
573             marginV = OD_BUTTON_MARGIN;
574         }
575 
576         size.IncBy(marginH, marginV);
577     }
578 }
579 
DoGetBestSize() const580 wxSize wxAnyButton::DoGetBestSize() const
581 {
582     wxAnyButton * const self = const_cast<wxAnyButton *>(this);
583 
584     wxSize size;
585 
586     // Account for the text part if we have it.
587     if ( ShowsLabel() )
588     {
589         int flags = 0;
590         if ( HasFlag(wxBU_EXACTFIT) )
591             flags |= wxMSWButton::Size_ExactFit;
592         if ( DoGetAuthNeeded() )
593             flags |= wxMSWButton::Size_AuthNeeded;
594 
595 #if wxUSE_MARKUP
596         if ( m_markupText )
597         {
598             wxClientDC dc(self);
599             size = wxMSWButton::GetFittingSize(self,
600                                                m_markupText->Measure(dc),
601                                                flags);
602         }
603         else // Normal plain text (but possibly multiline) label.
604 #endif // wxUSE_MARKUP
605         {
606             size = wxMSWButton::ComputeBestFittingSize(self, flags);
607         }
608     }
609 
610     if ( m_imageData )
611         AdjustForBitmapSize(size);
612 
613     return wxMSWButton::IncreaseToStdSizeAndCache(self, size);
614 }
615 
616 // ----------------------------------------------------------------------------
617 // event/message handlers
618 // ----------------------------------------------------------------------------
619 
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)620 WXLRESULT wxAnyButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
621 {
622     if ( nMsg == WM_LBUTTONDBLCLK )
623     {
624         // emulate a click event to force an owner-drawn button to change its
625         // appearance - without this, it won't do it
626         (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
627 
628         // and continue with processing the message normally as well
629     }
630 #if wxUSE_UXTHEME
631     else if ( nMsg == WM_THEMECHANGED )
632     {
633         // need to recalculate the best size here
634         // as the theme size might have changed
635         InvalidateBestSize();
636     }
637 #endif // wxUSE_UXTHEME
638     // must use m_mouseInWindow here instead of IsMouseInWindow()
639     // since we need to know the first time the mouse enters the window
640     // and IsMouseInWindow() would return true in this case
641     else if ( (nMsg == WM_MOUSEMOVE && !m_mouseInWindow) ||
642                 nMsg == WM_MOUSELEAVE )
643     {
644         if (
645                 IsEnabled() &&
646                 (
647 #if wxUSE_UXTHEME
648                 wxUxThemeEngine::GetIfActive() ||
649 #endif // wxUSE_UXTHEME
650                  (m_imageData && m_imageData->GetBitmap(State_Current).IsOk())
651                 )
652            )
653         {
654             Refresh();
655         }
656     }
657 
658     // let the base class do all real processing
659     return wxControl::MSWWindowProc(nMsg, wParam, lParam);
660 }
661 
662 // ----------------------------------------------------------------------------
663 // button images
664 // ----------------------------------------------------------------------------
665 
DoGetBitmap(State which) const666 wxBitmap wxAnyButton::DoGetBitmap(State which) const
667 {
668     return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap();
669 }
670 
DoSetBitmap(const wxBitmap & bitmap,State which)671 void wxAnyButton::DoSetBitmap(const wxBitmap& bitmap, State which)
672 {
673     if ( !bitmap.IsOk() )
674     {
675         if ( m_imageData  )
676         {
677             // Normal image is special: setting it enables images for the
678             // button and resetting it to nothing disables all of them.
679             if ( which == State_Normal )
680             {
681                 delete m_imageData;
682                 m_imageData = NULL;
683             }
684             else
685             {
686                 // Replace the removed bitmap with the normal one.
687                 wxBitmap bmpNormal = m_imageData->GetBitmap(State_Normal);
688                 m_imageData->SetBitmap(which == State_Disabled
689                                             ? bmpNormal.ConvertToDisabled()
690                                             : bmpNormal,
691                                        which);
692             }
693         }
694 
695         return;
696     }
697 
698 #if wxUSE_UXTHEME
699     wxXPButtonImageData *oldData = NULL;
700 #endif // wxUSE_UXTHEME
701 
702     // Check if we already had bitmaps of different size.
703     if ( m_imageData &&
704           bitmap.GetSize() != m_imageData->GetBitmap(State_Normal).GetSize() )
705     {
706         wxASSERT_MSG( (which == State_Normal) || bitmap.IsNull(),
707                       "Must set normal bitmap with the new size first" );
708 
709 #if wxUSE_UXTHEME
710         if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
711         {
712             // We can't change the size of the images stored in wxImageList
713             // in wxXPButtonImageData::m_iml so force recreating it below but
714             // keep the current data to copy its values into the new one.
715             oldData = static_cast<wxXPButtonImageData *>(m_imageData);
716             m_imageData = NULL;
717         }
718 #endif // wxUSE_UXTHEME
719         //else: wxODButtonImageData doesn't require anything special
720     }
721 
722     // allocate the image data when the first bitmap is set
723     if ( !m_imageData )
724     {
725 #if wxUSE_UXTHEME
726         // using image list doesn't work correctly if we don't have any label
727         // (even if we use BUTTON_IMAGELIST_ALIGN_CENTER alignment and
728         // BS_BITMAP style), at least under Windows 2003 so use owner drawn
729         // strategy for bitmap-only buttons
730         if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
731         {
732             m_imageData = new wxXPButtonImageData(this, bitmap);
733 
734             if ( oldData )
735             {
736                 // Preserve the old values in case the user changed them.
737                 m_imageData->SetBitmapPosition(oldData->GetBitmapPosition());
738 
739                 const wxSize oldMargins = oldData->GetBitmapMargins();
740                 m_imageData->SetBitmapMargins(oldMargins.x, oldMargins.y);
741 
742                 // No need to preserve the bitmaps though as they were of wrong
743                 // size anyhow.
744 
745                 delete oldData;
746             }
747         }
748         else
749 #endif // wxUSE_UXTHEME
750         {
751             m_imageData = new wxODButtonImageData(this, bitmap);
752             MakeOwnerDrawn();
753         }
754     }
755     else
756     {
757         m_imageData->SetBitmap(bitmap, which);
758     }
759 
760     // it should be enough to only invalidate the best size when the normal
761     // bitmap changes as all bitmaps assigned to the button should be of the
762     // same size anyhow
763     if ( which == State_Normal )
764         InvalidateBestSize();
765 
766     Refresh();
767 }
768 
DoGetBitmapMargins() const769 wxSize wxAnyButton::DoGetBitmapMargins() const
770 {
771     return m_imageData ? m_imageData->GetBitmapMargins() : wxSize(0, 0);
772 }
773 
DoSetBitmapMargins(wxCoord x,wxCoord y)774 void wxAnyButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
775 {
776     wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
777 
778     m_imageData->SetBitmapMargins(x, y);
779     InvalidateBestSize();
780 }
781 
DoSetBitmapPosition(wxDirection dir)782 void wxAnyButton::DoSetBitmapPosition(wxDirection dir)
783 {
784     if ( m_imageData )
785         m_imageData->SetBitmapPosition(dir);
786     InvalidateBestSize();
787 }
788 
789 // ----------------------------------------------------------------------------
790 // markup support
791 // ----------------------------------------------------------------------------
792 
793 #if wxUSE_MARKUP
794 
DoSetLabelMarkup(const wxString & markup)795 bool wxAnyButton::DoSetLabelMarkup(const wxString& markup)
796 {
797     if ( !wxAnyButtonBase::DoSetLabelMarkup(markup) )
798         return false;
799 
800     if ( !m_markupText )
801     {
802         m_markupText = new wxMarkupText(markup);
803         MakeOwnerDrawn();
804     }
805     else
806     {
807         // We are already owner-drawn so just update the text.
808         m_markupText->SetMarkup(markup);
809     }
810 
811     Refresh();
812 
813     return true;
814 }
815 
816 #endif // wxUSE_MARKUP
817 
818 // ----------------------------------------------------------------------------
819 // owner-drawn buttons support
820 // ----------------------------------------------------------------------------
821 
822 // drawing helpers
823 namespace
824 {
825 
826 // return the button state using both the ODS_XXX flags specified in state
827 // parameter and the current button state
GetButtonState(wxAnyButton * btn,UINT state)828 wxAnyButton::State GetButtonState(wxAnyButton *btn, UINT state)
829 {
830     if ( state & ODS_DISABLED )
831         return wxAnyButton::State_Disabled;
832 
833     // We need to check for the pressed state of the button itself before the
834     // other checks because even if it is selected or current, it it still
835     // pressed first and foremost.
836     const wxAnyButton::State btnState = btn->GetNormalState();
837 
838     if ( btnState == wxAnyButton::State_Pressed || state & ODS_SELECTED )
839         return wxAnyButton::State_Pressed;
840 
841     if ( btn->HasCapture() || btn->IsMouseInWindow() )
842         return wxAnyButton::State_Current;
843 
844     if ( state & ODS_FOCUS )
845         return wxAnyButton::State_Focused;
846 
847     return btnState;
848 }
849 
DrawButtonText(HDC hdc,RECT * pRect,wxAnyButton * btn,int flags)850 void DrawButtonText(HDC hdc,
851                     RECT *pRect,
852                     wxAnyButton *btn,
853                     int flags)
854 {
855     const wxString text = btn->GetLabel();
856 
857     // To get a native look for owner-drawn button in disabled state (without
858     // theming) we must use DrawState() to draw the text label.
859     if ( !wxUxThemeEngine::GetIfActive() && !btn->IsEnabled() )
860     {
861         // However using DrawState() has some drawbacks:
862         // 1. It generally doesn't support alignment flags (except right
863         //    alignment), so we need to align the text on our own.
864         // 2. It doesn't support multliline texts and there is necessary to
865         //    draw/align multiline text line by line.
866 
867         // Compute bounding rect for the whole text.
868         RECT rc;
869         ::SetRectEmpty(&rc);
870         ::DrawText(hdc, text.t_str(), text.length(), &rc, DT_CALCRECT);
871 
872         const LONG h = rc.bottom - rc.top;
873 
874         // Based on wxButton flags determine bottom edge of the drawing rect
875         // inside the entire button area.
876         int y0;
877         if ( btn->HasFlag(wxBU_BOTTOM) )
878         {
879             y0 = pRect->bottom - h;
880         }
881         else if ( !btn->HasFlag(wxBU_TOP) )
882         {
883             // DT_VCENTER
884             y0 = pRect->top + (pRect->bottom - pRect->top)/2 - h/2;
885         }
886         else // DT_TOP is the default
887         {
888             y0 = pRect->top;
889         }
890 
891         UINT dsFlags = DSS_DISABLED;
892         if( flags & DT_HIDEPREFIX )
893             dsFlags |= (DSS_HIDEPREFIX | DST_PREFIXTEXT);
894         else
895             dsFlags |= DST_TEXT;
896 
897         const wxArrayString lines = wxSplit(text, '\n', '\0');
898         const int hLine = h / lines.size();
899         for ( size_t lineNum = 0; lineNum < lines.size(); lineNum++ )
900         {
901             // Each line must be aligned in horizontal direction individually.
902             ::SetRectEmpty(&rc);
903             ::DrawText(hdc, lines[lineNum].t_str(), lines[lineNum].length(),
904                        &rc, DT_CALCRECT);
905             const LONG w = rc.right - rc.left;
906 
907             // Based on wxButton flags set horizontal position of the rect
908             // inside the entire button area. Text is always centered for
909             // multiline label.
910             if ( (!btn->HasFlag(wxBU_LEFT) && !btn->HasFlag(wxBU_RIGHT)) ||
911                     lines.size() > 1 )
912             {
913                 // DT_CENTER
914                 rc.left = pRect->left + (pRect->right - pRect->left)/2 - w/2;
915                 rc.right = rc.left + w;
916             }
917             else if ( btn->HasFlag(wxBU_RIGHT) )
918             {
919                 rc.right = pRect->right;
920                 rc.left = rc.right - w;
921             }
922             else // DT_LEFT is the default
923             {
924                 rc.left = pRect->left;
925                 rc.right = rc.left  + w;
926             }
927 
928             ::OffsetRect(&rc, 0, y0 + lineNum * hLine);
929 
930             ::DrawState(hdc, NULL, NULL, wxMSW_CONV_LPARAM(lines[lineNum]),
931                         lines[lineNum].length(),
932                         rc.left, rc.top, rc.right, rc.bottom, dsFlags);
933         }
934     }
935     else // Button is enabled or using themes.
936     {
937         if ( text.find(wxT('\n')) != wxString::npos )
938         {
939             // draw multiline label
940 
941             // first we need to compute its bounding rect
942             RECT rc;
943             ::CopyRect(&rc, pRect);
944             ::DrawText(hdc, text.t_str(), text.length(), &rc,
945                        flags | DT_CALCRECT);
946 
947             // now position this rect inside the entire button area: notice
948             // that DrawText() doesn't respect alignment flags for multiline
949             // text, which is why we have to do it on our own (but still use
950             // the horizontal alignment flags for the individual lines to be
951             // aligned correctly)
952             const LONG w = rc.right - rc.left;
953             const LONG h = rc.bottom - rc.top;
954 
955             if ( btn->HasFlag(wxBU_RIGHT) )
956             {
957                 rc.left = pRect->right - w;
958 
959                 flags |= DT_RIGHT;
960             }
961             else if ( !btn->HasFlag(wxBU_LEFT) )
962             {
963                 rc.left = pRect->left + (pRect->right - pRect->left)/2 - w/2;
964 
965                 flags |= DT_CENTER;
966             }
967             else // wxBU_LEFT
968             {
969                 rc.left = pRect->left;
970             }
971 
972             if ( btn->HasFlag(wxBU_BOTTOM) )
973             {
974                 rc.top = pRect->bottom - h;
975             }
976             else if ( !btn->HasFlag(wxBU_TOP) )
977             {
978                 rc.top = pRect->top + (pRect->bottom - pRect->top)/2 - h/2;
979             }
980             else // wxBU_TOP
981             {
982                 rc.top = pRect->top;
983             }
984 
985             rc.right = rc.left+w;
986             rc.bottom = rc.top+h;
987 
988             ::DrawText(hdc, text.t_str(), text.length(), &rc, flags);
989         }
990         else // single line label
991         {
992             // translate wx button flags to alignment flags for DrawText()
993             if ( btn->HasFlag(wxBU_RIGHT) )
994             {
995                 flags |= DT_RIGHT;
996             }
997             else if ( !btn->HasFlag(wxBU_LEFT) )
998             {
999                 flags |= DT_CENTER;
1000             }
1001             //else: DT_LEFT is the default anyhow (and its value is 0 too)
1002 
1003             if ( btn->HasFlag(wxBU_BOTTOM) )
1004             {
1005                 flags |= DT_BOTTOM;
1006             }
1007             else if ( !btn->HasFlag(wxBU_TOP) )
1008             {
1009                 flags |= DT_VCENTER;
1010             }
1011             //else: as above, DT_TOP is the default
1012 
1013             // notice that we must have DT_SINGLELINE for vertical alignment
1014             // flags to work
1015             ::DrawText(hdc, text.t_str(), text.length(), pRect,
1016                        flags | DT_SINGLELINE );
1017         }
1018     }
1019 }
1020 
DrawRect(HDC hdc,const RECT & r)1021 void DrawRect(HDC hdc, const RECT& r)
1022 {
1023     wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1024     wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
1025     wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
1026     wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
1027 }
1028 
1029 /*
1030    The button frame looks like this normally:
1031 
1032    WWWWWWWWWWWWWWWWWWB
1033    WHHHHHHHHHHHHHHHHGB  W = white       (HILIGHT)
1034    WH               GB  H = light grey  (LIGHT)
1035    WH               GB  G = dark grey   (SHADOW)
1036    WH               GB  B = black       (DKSHADOW)
1037    WH               GB
1038    WGGGGGGGGGGGGGGGGGB
1039    BBBBBBBBBBBBBBBBBBB
1040 
1041    When the button is selected, the button becomes like this (the total button
1042    size doesn't change):
1043 
1044    BBBBBBBBBBBBBBBBBBB
1045    BWWWWWWWWWWWWWWWWBB
1046    BWHHHHHHHHHHHHHHGBB
1047    BWH             GBB
1048    BWH             GBB
1049    BWGGGGGGGGGGGGGGGBB
1050    BBBBBBBBBBBBBBBBBBB
1051    BBBBBBBBBBBBBBBBBBB
1052 
1053    When the button is pushed (while selected) it is like:
1054 
1055    BBBBBBBBBBBBBBBBBBB
1056    BGGGGGGGGGGGGGGGGGB
1057    BG               GB
1058    BG               GB
1059    BG               GB
1060    BG               GB
1061    BGGGGGGGGGGGGGGGGGB
1062    BBBBBBBBBBBBBBBBBBB
1063 */
DrawButtonFrame(HDC hdc,RECT & rectBtn,bool selected,bool pushed)1064 void DrawButtonFrame(HDC hdc, RECT& rectBtn,
1065                      bool selected, bool pushed)
1066 {
1067     RECT r;
1068     CopyRect(&r, &rectBtn);
1069 
1070     AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
1071              hpenGrey(GetSysColor(COLOR_3DSHADOW)),
1072              hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
1073              hpenWhite(GetSysColor(COLOR_3DHILIGHT));
1074 
1075     SelectInHDC selectPen(hdc, hpenBlack);
1076 
1077     r.right--;
1078     r.bottom--;
1079 
1080     if ( pushed )
1081     {
1082         DrawRect(hdc, r);
1083 
1084         (void)SelectObject(hdc, hpenGrey);
1085         ::InflateRect(&r, -1, -1);
1086 
1087         DrawRect(hdc, r);
1088     }
1089     else // !pushed
1090     {
1091         if ( selected )
1092         {
1093             DrawRect(hdc, r);
1094 
1095             ::InflateRect(&r, -1, -1);
1096         }
1097 
1098         wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
1099         wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
1100 
1101         (void)SelectObject(hdc, hpenWhite);
1102         wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
1103         wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1104 
1105         (void)SelectObject(hdc, hpenLightGr);
1106         wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
1107         wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
1108 
1109         (void)SelectObject(hdc, hpenGrey);
1110         wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
1111         wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
1112     }
1113 
1114     InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
1115 }
1116 
1117 #if wxUSE_UXTHEME
DrawXPBackground(wxAnyButton * button,HDC hdc,RECT & rectBtn,UINT state)1118 void DrawXPBackground(wxAnyButton *button, HDC hdc, RECT& rectBtn, UINT state)
1119 {
1120     wxUxThemeHandle theme(button, L"BUTTON");
1121 
1122     // this array is indexed by wxAnyButton::State values and so must be kept in
1123     // sync with it
1124     static const int uxStates[] =
1125     {
1126         PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
1127     };
1128 
1129     int iState = uxStates[GetButtonState(button, state)];
1130 
1131     wxUxThemeEngine * const engine = wxUxThemeEngine::Get();
1132 
1133     // draw parent background if needed
1134     if ( engine->IsThemeBackgroundPartiallyTransparent
1135                  (
1136                     theme,
1137                     BP_PUSHBUTTON,
1138                     iState
1139                  ) )
1140     {
1141         // Set this button as the one whose background is being erased: this
1142         // allows our WM_ERASEBKGND handler used by DrawThemeParentBackground()
1143         // to correctly align the background brush with this window instead of
1144         // the parent window to which WM_ERASEBKGND is sent. Notice that this
1145         // doesn't work with custom user-defined EVT_ERASE_BACKGROUND handlers
1146         // as they won't be aligned but unfortunately all the attempts to fix
1147         // it by shifting DC origin before calling DrawThemeParentBackground()
1148         // failed to work so we at least do this, even though this is far from
1149         // being the perfect solution.
1150         wxWindowBeingErased = button;
1151 
1152         engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
1153 
1154         wxWindowBeingErased = NULL;
1155     }
1156 
1157     // draw background
1158     engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
1159                                 &rectBtn, NULL);
1160 
1161     // calculate content area margins
1162     MARGINS margins;
1163     engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
1164                             TMT_CONTENTMARGINS, &rectBtn, &margins);
1165     ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
1166     ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
1167 
1168     if ( button->UseBgCol() )
1169     {
1170         COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
1171         AutoHBRUSH hbrushBackground(colBg);
1172 
1173         // don't overwrite the focus rect
1174         RECT rectClient;
1175         ::CopyRect(&rectClient, &rectBtn);
1176         ::InflateRect(&rectClient, -1, -1);
1177         FillRect(hdc, &rectClient, hbrushBackground);
1178     }
1179 }
1180 #endif // wxUSE_UXTHEME
1181 
1182 } // anonymous namespace
1183 
1184 // ----------------------------------------------------------------------------
1185 // owner drawn buttons support
1186 // ----------------------------------------------------------------------------
1187 
MakeOwnerDrawn()1188 void wxAnyButton::MakeOwnerDrawn()
1189 {
1190     if ( !IsOwnerDrawn() )
1191     {
1192         // make it so
1193         // note that BS_OWNERDRAW is not independent from other style bits
1194         long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1195         style &= ~(BS_3STATE | BS_AUTO3STATE | BS_AUTOCHECKBOX | BS_AUTORADIOBUTTON | BS_CHECKBOX | BS_DEFPUSHBUTTON | BS_GROUPBOX | BS_PUSHBUTTON | BS_RADIOBUTTON | BS_PUSHLIKE);
1196         style |= BS_OWNERDRAW;
1197         SetWindowLong(GetHwnd(), GWL_STYLE, style);
1198     }
1199 }
1200 
IsOwnerDrawn() const1201 bool wxAnyButton::IsOwnerDrawn() const
1202 {
1203     long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1204     return ( (style & BS_OWNERDRAW) == BS_OWNERDRAW );
1205 }
1206 
SetBackgroundColour(const wxColour & colour)1207 bool wxAnyButton::SetBackgroundColour(const wxColour &colour)
1208 {
1209     if ( !wxControl::SetBackgroundColour(colour) )
1210     {
1211         // nothing to do
1212         return false;
1213     }
1214 
1215     MakeOwnerDrawn();
1216 
1217     Refresh();
1218 
1219     return true;
1220 }
1221 
SetForegroundColour(const wxColour & colour)1222 bool wxAnyButton::SetForegroundColour(const wxColour &colour)
1223 {
1224     if ( !wxControl::SetForegroundColour(colour) )
1225     {
1226         // nothing to do
1227         return false;
1228     }
1229 
1230     MakeOwnerDrawn();
1231 
1232     Refresh();
1233 
1234     return true;
1235 }
1236 
MSWOnDraw(WXDRAWITEMSTRUCT * wxdis)1237 bool wxAnyButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
1238 {
1239     LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
1240     HDC hdc = lpDIS->hDC;
1241 
1242     UINT state = lpDIS->itemState;
1243     switch ( GetButtonState(this, state) )
1244     {
1245         case State_Disabled:
1246             state |= ODS_DISABLED;
1247             break;
1248         case State_Pressed:
1249             state |= ODS_SELECTED;
1250             break;
1251         case State_Focused:
1252             state |= ODS_FOCUS;
1253             break;
1254         default:
1255             break;
1256     }
1257 
1258     bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
1259 
1260     RECT rectBtn;
1261     CopyRect(&rectBtn, &lpDIS->rcItem);
1262 
1263     // draw the button background
1264     if ( !HasFlag(wxBORDER_NONE) )
1265     {
1266 #if wxUSE_UXTHEME
1267         if ( wxUxThemeEngine::GetIfActive() )
1268         {
1269             DrawXPBackground(this, hdc, rectBtn, state);
1270         }
1271         else
1272 #endif // wxUSE_UXTHEME
1273         {
1274             COLORREF colBg = wxColourToRGB(GetBackgroundColour());
1275 
1276             // first, draw the background
1277             AutoHBRUSH hbrushBackground(colBg);
1278             FillRect(hdc, &rectBtn, hbrushBackground);
1279 
1280             // draw the border for the current state
1281             bool selected = (state & ODS_SELECTED) != 0;
1282             if ( !selected )
1283             {
1284                 wxTopLevelWindow *
1285                     tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
1286                 if ( tlw )
1287                 {
1288                     selected = tlw->GetDefaultItem() == this;
1289                 }
1290             }
1291 
1292             DrawButtonFrame(hdc, rectBtn, selected, pushed);
1293         }
1294 
1295         // draw the focus rectangle if we need it
1296         if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
1297         {
1298             DrawFocusRect(hdc, &rectBtn);
1299 
1300 #if wxUSE_UXTHEME
1301             if ( !wxUxThemeEngine::GetIfActive() )
1302 #endif // wxUSE_UXTHEME
1303             {
1304                 if ( pushed )
1305                 {
1306                     // the label is shifted by 1 pixel to create "pushed" effect
1307                     OffsetRect(&rectBtn, 1, 1);
1308                 }
1309             }
1310         }
1311     }
1312 
1313 
1314     // draw the image, if any
1315     if ( m_imageData )
1316     {
1317         wxBitmap bmp = m_imageData->GetBitmap(GetButtonState(this, state));
1318         if ( !bmp.IsOk() )
1319             bmp = m_imageData->GetBitmap(State_Normal);
1320 
1321         const wxSize sizeBmp = bmp.GetSize();
1322         const wxSize margin = m_imageData->GetBitmapMargins();
1323         const wxSize sizeBmpWithMargins(sizeBmp + 2*margin);
1324         wxRect rectButton(wxRectFromRECT(rectBtn));
1325 
1326         // for simplicity, we start with centred rectangle and then move it to
1327         // the appropriate edge
1328         wxRect rectBitmap = wxRect(sizeBmp).CentreIn(rectButton);
1329 
1330         // move bitmap only if we have a label, otherwise keep it centered
1331         if ( ShowsLabel() )
1332         {
1333             switch ( m_imageData->GetBitmapPosition() )
1334             {
1335                 default:
1336                     wxFAIL_MSG( "invalid direction" );
1337                     // fall through
1338 
1339                 case wxLEFT:
1340                     rectBitmap.x = rectButton.x + margin.x;
1341                     rectButton.x += sizeBmpWithMargins.x;
1342                     rectButton.width -= sizeBmpWithMargins.x;
1343                     break;
1344 
1345                 case wxRIGHT:
1346                     rectBitmap.x = rectButton.GetRight() - sizeBmp.x - margin.x;
1347                     rectButton.width -= sizeBmpWithMargins.x;
1348                     break;
1349 
1350                 case wxTOP:
1351                     rectBitmap.y = rectButton.y + margin.y;
1352                     rectButton.y += sizeBmpWithMargins.y;
1353                     rectButton.height -= sizeBmpWithMargins.y;
1354                     break;
1355 
1356                 case wxBOTTOM:
1357                     rectBitmap.y = rectButton.GetBottom() - sizeBmp.y - margin.y;
1358                     rectButton.height -= sizeBmpWithMargins.y;
1359                     break;
1360             }
1361         }
1362 
1363         wxDCTemp dst((WXHDC)hdc);
1364         dst.DrawBitmap(bmp, rectBitmap.GetPosition(), true);
1365 
1366         wxCopyRectToRECT(rectButton, rectBtn);
1367     }
1368 
1369 
1370     // finally draw the label
1371     if ( ShowsLabel() )
1372     {
1373         COLORREF colFg = state & ODS_DISABLED
1374                             ? ::GetSysColor(COLOR_GRAYTEXT)
1375                             : wxColourToRGB(GetForegroundColour());
1376 
1377         wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
1378         wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
1379 
1380 #if wxUSE_MARKUP
1381         if ( m_markupText )
1382         {
1383             wxDCTemp dc((WXHDC)hdc);
1384             dc.SetTextForeground(wxColour(colFg));
1385             dc.SetFont(GetFont());
1386 
1387             m_markupText->Render(dc, wxRectFromRECT(rectBtn),
1388                                  state & ODS_NOACCEL
1389                                     ? wxMarkupText::Render_Default
1390                                     : wxMarkupText::Render_ShowAccels);
1391         }
1392         else // Plain text label
1393 #endif // wxUSE_MARKUP
1394         {
1395             // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
1396             // systems but by happy coincidence ODS_NOACCEL is not used under
1397             // them neither so DT_HIDEPREFIX should never be used there
1398             DrawButtonText(hdc, &rectBtn, this,
1399                            state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
1400         }
1401     }
1402 
1403     return true;
1404 }
1405 
1406 #endif // wxHAS_ANY_BUTTON
1407