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