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