Respect alignment flags for owner-drawn buttons in wxMSW.
[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 wxButton *btn,
1103 int flags)
1104 {
1105 const wxString text = btn->GetLabel();
1106
1107 if ( text.find(wxT('\n')) != wxString::npos )
1108 {
1109 // draw multiline label
1110
1111 // center text horizontally in any case
1112 flags |= DT_CENTER;
1113
1114 // first we need to compute its bounding rect
1115 RECT rc;
1116 ::CopyRect(&rc, pRect);
1117 ::DrawText(hdc, text.wx_str(), text.length(), &rc,
1118 DT_CENTER | DT_CALCRECT);
1119
1120 // now center this rect inside the entire button area
1121 const LONG w = rc.right - rc.left;
1122 const LONG h = rc.bottom - rc.top;
1123 rc.left = (pRect->right - pRect->left)/2 - w/2;
1124 rc.right = rc.left+w;
1125 rc.top = (pRect->bottom - pRect->top)/2 - h/2;
1126 rc.bottom = rc.top+h;
1127
1128 ::DrawText(hdc, text.wx_str(), text.length(), &rc, flags);
1129 }
1130 else // single line label
1131 {
1132 // translate wx button flags to alignment flags for DrawText()
1133 if ( btn->HasFlag(wxBU_RIGHT) )
1134 {
1135 flags |= DT_RIGHT;
1136 }
1137 else if ( !btn->HasFlag(wxBU_LEFT) )
1138 {
1139 flags |= DT_CENTER;
1140 }
1141 //else: DT_LEFT is the default anyhow (and its value is 0 too)
1142
1143 if ( btn->HasFlag(wxBU_BOTTOM) )
1144 {
1145 flags |= DT_BOTTOM;
1146 }
1147 else if ( !btn->HasFlag(wxBU_TOP) )
1148 {
1149 flags |= DT_VCENTER;
1150 }
1151 //else: as above, DT_TOP is the default
1152
1153 // notice that we must have DT_SINGLELINE for vertical alignment flags
1154 // to work
1155 ::DrawText(hdc, text.wx_str(), text.length(), pRect,
1156 flags | DT_SINGLELINE );
1157 }
1158 }
1159
1160 void DrawRect(HDC hdc, const RECT& r)
1161 {
1162 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1163 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
1164 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
1165 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
1166 }
1167
1168 /*
1169 The button frame looks like this normally:
1170
1171 WWWWWWWWWWWWWWWWWWB
1172 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
1173 WH GB H = light grey (LIGHT)
1174 WH GB G = dark grey (SHADOW)
1175 WH GB B = black (DKSHADOW)
1176 WH GB
1177 WGGGGGGGGGGGGGGGGGB
1178 BBBBBBBBBBBBBBBBBBB
1179
1180 When the button is selected, the button becomes like this (the total button
1181 size doesn't change):
1182
1183 BBBBBBBBBBBBBBBBBBB
1184 BWWWWWWWWWWWWWWWWBB
1185 BWHHHHHHHHHHHHHHGBB
1186 BWH GBB
1187 BWH GBB
1188 BWGGGGGGGGGGGGGGGBB
1189 BBBBBBBBBBBBBBBBBBB
1190 BBBBBBBBBBBBBBBBBBB
1191
1192 When the button is pushed (while selected) it is like:
1193
1194 BBBBBBBBBBBBBBBBBBB
1195 BGGGGGGGGGGGGGGGGGB
1196 BG GB
1197 BG GB
1198 BG GB
1199 BG GB
1200 BGGGGGGGGGGGGGGGGGB
1201 BBBBBBBBBBBBBBBBBBB
1202 */
1203 void DrawButtonFrame(HDC hdc, RECT& rectBtn,
1204 bool selected, bool pushed)
1205 {
1206 RECT r;
1207 CopyRect(&r, &rectBtn);
1208
1209 AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
1210 hpenGrey(GetSysColor(COLOR_3DSHADOW)),
1211 hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
1212 hpenWhite(GetSysColor(COLOR_3DHILIGHT));
1213
1214 SelectInHDC selectPen(hdc, hpenBlack);
1215
1216 r.right--;
1217 r.bottom--;
1218
1219 if ( pushed )
1220 {
1221 DrawRect(hdc, r);
1222
1223 (void)SelectObject(hdc, hpenGrey);
1224 ::InflateRect(&r, -1, -1);
1225
1226 DrawRect(hdc, r);
1227 }
1228 else // !pushed
1229 {
1230 if ( selected )
1231 {
1232 DrawRect(hdc, r);
1233
1234 ::InflateRect(&r, -1, -1);
1235 }
1236
1237 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
1238 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
1239
1240 (void)SelectObject(hdc, hpenWhite);
1241 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
1242 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1243
1244 (void)SelectObject(hdc, hpenLightGr);
1245 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
1246 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
1247
1248 (void)SelectObject(hdc, hpenGrey);
1249 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
1250 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
1251 }
1252
1253 InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
1254 }
1255
1256 #if wxUSE_UXTHEME
1257 void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
1258 {
1259 wxUxThemeHandle theme(button, L"BUTTON");
1260
1261 // this array is indexed by wxButton::State values and so must be kept in
1262 // sync with it
1263 static const int uxStates[] =
1264 {
1265 PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
1266 };
1267
1268 int iState = uxStates[GetButtonState(button, state)];
1269
1270 wxUxThemeEngine * const engine = wxUxThemeEngine::Get();
1271
1272 // draw parent background if needed
1273 if ( engine->IsThemeBackgroundPartiallyTransparent
1274 (
1275 theme,
1276 BP_PUSHBUTTON,
1277 iState
1278 ) )
1279 {
1280 engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
1281 }
1282
1283 // draw background
1284 engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
1285 &rectBtn, NULL);
1286
1287 // calculate content area margins
1288 MARGINS margins;
1289 engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
1290 TMT_CONTENTMARGINS, &rectBtn, &margins);
1291 ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
1292 ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
1293
1294 if ( button->UseBgCol() )
1295 {
1296 COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
1297 AutoHBRUSH hbrushBackground(colBg);
1298
1299 // don't overwrite the focus rect
1300 RECT rectClient;
1301 ::CopyRect(&rectClient, &rectBtn);
1302 ::InflateRect(&rectClient, -1, -1);
1303 FillRect(hdc, &rectClient, hbrushBackground);
1304 }
1305 }
1306 #endif // wxUSE_UXTHEME
1307
1308 } // anonymous namespace
1309
1310 // ----------------------------------------------------------------------------
1311 // owner drawn buttons support
1312 // ----------------------------------------------------------------------------
1313
1314 void wxButton::MakeOwnerDrawn()
1315 {
1316 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1317 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
1318 {
1319 // make it so
1320 style |= BS_OWNERDRAW;
1321 SetWindowLong(GetHwnd(), GWL_STYLE, style);
1322 }
1323 }
1324
1325 bool wxButton::SetBackgroundColour(const wxColour &colour)
1326 {
1327 if ( !wxControl::SetBackgroundColour(colour) )
1328 {
1329 // nothing to do
1330 return false;
1331 }
1332
1333 MakeOwnerDrawn();
1334
1335 Refresh();
1336
1337 return true;
1338 }
1339
1340 bool wxButton::SetForegroundColour(const wxColour &colour)
1341 {
1342 if ( !wxControl::SetForegroundColour(colour) )
1343 {
1344 // nothing to do
1345 return false;
1346 }
1347
1348 MakeOwnerDrawn();
1349
1350 Refresh();
1351
1352 return true;
1353 }
1354
1355 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
1356 {
1357 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
1358 HDC hdc = lpDIS->hDC;
1359
1360 UINT state = lpDIS->itemState;
1361 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
1362
1363 RECT rectBtn;
1364 CopyRect(&rectBtn, &lpDIS->rcItem);
1365
1366 // draw the button background
1367 if ( !HasFlag(wxBORDER_NONE) )
1368 {
1369 #if wxUSE_UXTHEME
1370 if ( wxUxThemeEngine::GetIfActive() )
1371 {
1372 DrawXPBackground(this, hdc, rectBtn, state);
1373 }
1374 else
1375 #endif // wxUSE_UXTHEME
1376 {
1377 COLORREF colBg = wxColourToRGB(GetBackgroundColour());
1378
1379 // first, draw the background
1380 AutoHBRUSH hbrushBackground(colBg);
1381 FillRect(hdc, &rectBtn, hbrushBackground);
1382
1383 // draw the border for the current state
1384 bool selected = (state & ODS_SELECTED) != 0;
1385 if ( !selected )
1386 {
1387 wxTopLevelWindow *
1388 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
1389 if ( tlw )
1390 {
1391 selected = tlw->GetDefaultItem() == this;
1392 }
1393 }
1394
1395 DrawButtonFrame(hdc, rectBtn, selected, pushed);
1396 }
1397
1398 // draw the focus rectangle if we need it
1399 if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
1400 {
1401 DrawFocusRect(hdc, &rectBtn);
1402
1403 #if wxUSE_UXTHEME
1404 if ( !wxUxThemeEngine::GetIfActive() )
1405 #endif // wxUSE_UXTHEME
1406 {
1407 if ( pushed )
1408 {
1409 // the label is shifted by 1 pixel to create "pushed" effect
1410 OffsetRect(&rectBtn, 1, 1);
1411 }
1412 }
1413 }
1414 }
1415
1416
1417 // draw the image, if any
1418 if ( m_imageData )
1419 {
1420 wxBitmap bmp = m_imageData->GetBitmap(GetButtonState(this, state));
1421 if ( !bmp.IsOk() )
1422 bmp = m_imageData->GetBitmap(State_Normal);
1423
1424 const wxSize sizeBmp = bmp.GetSize();
1425 const wxSize margin = m_imageData->GetBitmapMargins();
1426 const wxSize sizeBmpWithMargins(sizeBmp + 2*margin);
1427 wxRect rectButton(wxRectFromRECT(rectBtn));
1428
1429 // for simplicity, we start with centred rectangle and then move it to
1430 // the appropriate edge
1431 wxRect rectBitmap = wxRect(sizeBmp).CentreIn(rectButton);
1432
1433 // move bitmap only if we have a label, otherwise keep it centered
1434 if ( ShowsLabel() )
1435 {
1436 switch ( m_imageData->GetBitmapPosition() )
1437 {
1438 default:
1439 wxFAIL_MSG( "invalid direction" );
1440 // fall through
1441
1442 case wxLEFT:
1443 rectBitmap.x = rectButton.x + margin.x;
1444 rectButton.x += sizeBmpWithMargins.x;
1445 rectButton.width -= sizeBmpWithMargins.x;
1446 break;
1447
1448 case wxRIGHT:
1449 rectBitmap.x = rectButton.GetRight() - sizeBmp.x - margin.x;
1450 rectButton.width -= sizeBmpWithMargins.x;
1451 break;
1452
1453 case wxTOP:
1454 rectBitmap.y = rectButton.y + margin.y;
1455 rectButton.y += sizeBmpWithMargins.y;
1456 rectButton.height -= sizeBmpWithMargins.y;
1457 break;
1458
1459 case wxBOTTOM:
1460 rectBitmap.y = rectButton.GetBottom() - sizeBmp.y - margin.y;
1461 rectButton.height -= sizeBmpWithMargins.y;
1462 break;
1463 }
1464 }
1465
1466 wxDCTemp dst((WXHDC)hdc);
1467 dst.DrawBitmap(bmp, rectBitmap.GetPosition(), true);
1468
1469 wxCopyRectToRECT(rectButton, rectBtn);
1470 }
1471
1472
1473 // finally draw the label
1474 if ( ShowsLabel() )
1475 {
1476 COLORREF colFg = state & ODS_DISABLED
1477 ? ::GetSysColor(COLOR_GRAYTEXT)
1478 : wxColourToRGB(GetForegroundColour());
1479
1480 wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
1481 wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
1482
1483 #if wxUSE_MARKUP
1484 if ( m_markupText )
1485 {
1486 wxDCTemp dc((WXHDC)hdc);
1487 dc.SetTextForeground(wxColour(colFg));
1488 dc.SetFont(GetFont());
1489
1490 m_markupText->Render(dc, wxRectFromRECT(rectBtn),
1491 state & ODS_NOACCEL
1492 ? wxMarkupText::Render_Default
1493 : wxMarkupText::Render_ShowAccels);
1494 }
1495 else // Plain text label
1496 #endif // wxUSE_MARKUP
1497 {
1498 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
1499 // systems but by happy coincidence ODS_NOACCEL is not used under
1500 // them neither so DT_HIDEPREFIX should never be used there
1501 DrawButtonText(hdc, &rectBtn, this,
1502 state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
1503 }
1504 }
1505
1506 return true;
1507 }
1508
1509 #endif // wxUSE_BUTTON
1510