Fix changing the size of the bitmaps in wxMSW wxButton.
[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 #if wxUSE_UXTHEME
991 wxXPButtonImageData *oldData = NULL;
992 #endif // wxUSE_UXTHEME
993
994 // Check if we already had bitmaps of different size.
995 if ( m_imageData &&
996 bitmap.GetSize() != m_imageData->GetBitmap(State_Normal).GetSize() )
997 {
998 wxASSERT_MSG( which == State_Normal,
999 "Must set normal bitmap with the new size first" );
1000
1001 #if wxUSE_UXTHEME
1002 if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
1003 {
1004 // We can't change the size of the images stored in wxImageList
1005 // in wxXPButtonImageData::m_iml so force recreating it below but
1006 // keep the current data to copy its values into the new one.
1007 oldData = static_cast<wxXPButtonImageData *>(m_imageData);
1008 m_imageData = NULL;
1009 }
1010 #endif // wxUSE_UXTHEME
1011 //else: wxODButtonImageData doesn't require anything special
1012 }
1013
1014 // allocate the image data when the first bitmap is set
1015 if ( !m_imageData )
1016 {
1017 #if wxUSE_UXTHEME
1018 // using image list doesn't work correctly if we don't have any label
1019 // (even if we use BUTTON_IMAGELIST_ALIGN_CENTER alignment and
1020 // BS_BITMAP style), at least under Windows 2003 so use owner drawn
1021 // strategy for bitmap-only buttons
1022 if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
1023 {
1024 m_imageData = new wxXPButtonImageData(this, bitmap);
1025
1026 if ( oldData )
1027 {
1028 // Preserve the old values in case the user changed them.
1029 m_imageData->SetBitmapPosition(oldData->GetBitmapPosition());
1030
1031 const wxSize oldMargins = oldData->GetBitmapMargins();
1032 m_imageData->SetBitmapMargins(oldMargins.x, oldMargins.y);
1033
1034 // No need to preserve the bitmaps though as they were of wrong
1035 // size anyhow.
1036
1037 delete oldData;
1038 }
1039 }
1040 else
1041 #endif // wxUSE_UXTHEME
1042 {
1043 m_imageData = new wxODButtonImageData(this, bitmap);
1044 MakeOwnerDrawn();
1045 }
1046 }
1047 else
1048 {
1049 m_imageData->SetBitmap(bitmap, which);
1050 }
1051
1052 // it should be enough to only invalidate the best size when the normal
1053 // bitmap changes as all bitmaps assigned to the button should be of the
1054 // same size anyhow
1055 if ( which == State_Normal )
1056 InvalidateBestSize();
1057
1058 Refresh();
1059 }
1060
1061 wxSize wxButton::DoGetBitmapMargins() const
1062 {
1063 return m_imageData ? m_imageData->GetBitmapMargins() : wxSize(0, 0);
1064 }
1065
1066 void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
1067 {
1068 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
1069
1070 m_imageData->SetBitmapMargins(x, y);
1071 InvalidateBestSize();
1072 }
1073
1074 void wxButton::DoSetBitmapPosition(wxDirection dir)
1075 {
1076 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
1077
1078 m_imageData->SetBitmapPosition(dir);
1079 InvalidateBestSize();
1080 }
1081
1082 // ----------------------------------------------------------------------------
1083 // markup support
1084 // ----------------------------------------------------------------------------
1085
1086 #if wxUSE_MARKUP
1087
1088 bool wxButton::DoSetLabelMarkup(const wxString& markup)
1089 {
1090 if ( !wxButtonBase::DoSetLabelMarkup(markup) )
1091 return false;
1092
1093 if ( !m_markupText )
1094 {
1095 m_markupText = new wxMarkupText(markup);
1096 MakeOwnerDrawn();
1097 }
1098 else
1099 {
1100 // We are already owner-drawn so just update the text.
1101 m_markupText->SetMarkup(markup);
1102 }
1103
1104 Refresh();
1105
1106 return true;
1107 }
1108
1109 #endif // wxUSE_MARKUP
1110
1111 // ----------------------------------------------------------------------------
1112 // owner-drawn buttons support
1113 // ----------------------------------------------------------------------------
1114
1115 // drawing helpers
1116 namespace
1117 {
1118
1119 // return the button state using both the ODS_XXX flags specified in state
1120 // parameter and the current button state
1121 wxButton::State GetButtonState(wxButton *btn, UINT state)
1122 {
1123 if ( state & ODS_DISABLED )
1124 return wxButton::State_Disabled;
1125
1126 if ( state & ODS_SELECTED )
1127 return wxButton::State_Pressed;
1128
1129 if ( btn->HasCapture() || btn->IsMouseInWindow() )
1130 return wxButton::State_Current;
1131
1132 if ( state & ODS_FOCUS )
1133 return wxButton::State_Focused;
1134
1135 return wxButton::State_Normal;
1136 }
1137
1138 void DrawButtonText(HDC hdc,
1139 RECT *pRect,
1140 wxButton *btn,
1141 int flags)
1142 {
1143 const wxString text = btn->GetLabel();
1144
1145 if ( text.find(wxT('\n')) != wxString::npos )
1146 {
1147 // draw multiline label
1148
1149 // center text horizontally in any case
1150 flags |= DT_CENTER;
1151
1152 // first we need to compute its bounding rect
1153 RECT rc;
1154 ::CopyRect(&rc, pRect);
1155 ::DrawText(hdc, text.wx_str(), text.length(), &rc,
1156 DT_CENTER | DT_CALCRECT);
1157
1158 // now center this rect inside the entire button area
1159 const LONG w = rc.right - rc.left;
1160 const LONG h = rc.bottom - rc.top;
1161 rc.left = (pRect->right - pRect->left)/2 - w/2;
1162 rc.right = rc.left+w;
1163 rc.top = (pRect->bottom - pRect->top)/2 - h/2;
1164 rc.bottom = rc.top+h;
1165
1166 ::DrawText(hdc, text.wx_str(), text.length(), &rc, flags);
1167 }
1168 else // single line label
1169 {
1170 // translate wx button flags to alignment flags for DrawText()
1171 if ( btn->HasFlag(wxBU_RIGHT) )
1172 {
1173 flags |= DT_RIGHT;
1174 }
1175 else if ( !btn->HasFlag(wxBU_LEFT) )
1176 {
1177 flags |= DT_CENTER;
1178 }
1179 //else: DT_LEFT is the default anyhow (and its value is 0 too)
1180
1181 if ( btn->HasFlag(wxBU_BOTTOM) )
1182 {
1183 flags |= DT_BOTTOM;
1184 }
1185 else if ( !btn->HasFlag(wxBU_TOP) )
1186 {
1187 flags |= DT_VCENTER;
1188 }
1189 //else: as above, DT_TOP is the default
1190
1191 // notice that we must have DT_SINGLELINE for vertical alignment flags
1192 // to work
1193 ::DrawText(hdc, text.wx_str(), text.length(), pRect,
1194 flags | DT_SINGLELINE );
1195 }
1196 }
1197
1198 void DrawRect(HDC hdc, const RECT& r)
1199 {
1200 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1201 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
1202 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
1203 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
1204 }
1205
1206 /*
1207 The button frame looks like this normally:
1208
1209 WWWWWWWWWWWWWWWWWWB
1210 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
1211 WH GB H = light grey (LIGHT)
1212 WH GB G = dark grey (SHADOW)
1213 WH GB B = black (DKSHADOW)
1214 WH GB
1215 WGGGGGGGGGGGGGGGGGB
1216 BBBBBBBBBBBBBBBBBBB
1217
1218 When the button is selected, the button becomes like this (the total button
1219 size doesn't change):
1220
1221 BBBBBBBBBBBBBBBBBBB
1222 BWWWWWWWWWWWWWWWWBB
1223 BWHHHHHHHHHHHHHHGBB
1224 BWH GBB
1225 BWH GBB
1226 BWGGGGGGGGGGGGGGGBB
1227 BBBBBBBBBBBBBBBBBBB
1228 BBBBBBBBBBBBBBBBBBB
1229
1230 When the button is pushed (while selected) it is like:
1231
1232 BBBBBBBBBBBBBBBBBBB
1233 BGGGGGGGGGGGGGGGGGB
1234 BG GB
1235 BG GB
1236 BG GB
1237 BG GB
1238 BGGGGGGGGGGGGGGGGGB
1239 BBBBBBBBBBBBBBBBBBB
1240 */
1241 void DrawButtonFrame(HDC hdc, RECT& rectBtn,
1242 bool selected, bool pushed)
1243 {
1244 RECT r;
1245 CopyRect(&r, &rectBtn);
1246
1247 AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
1248 hpenGrey(GetSysColor(COLOR_3DSHADOW)),
1249 hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
1250 hpenWhite(GetSysColor(COLOR_3DHILIGHT));
1251
1252 SelectInHDC selectPen(hdc, hpenBlack);
1253
1254 r.right--;
1255 r.bottom--;
1256
1257 if ( pushed )
1258 {
1259 DrawRect(hdc, r);
1260
1261 (void)SelectObject(hdc, hpenGrey);
1262 ::InflateRect(&r, -1, -1);
1263
1264 DrawRect(hdc, r);
1265 }
1266 else // !pushed
1267 {
1268 if ( selected )
1269 {
1270 DrawRect(hdc, r);
1271
1272 ::InflateRect(&r, -1, -1);
1273 }
1274
1275 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
1276 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
1277
1278 (void)SelectObject(hdc, hpenWhite);
1279 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
1280 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
1281
1282 (void)SelectObject(hdc, hpenLightGr);
1283 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
1284 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
1285
1286 (void)SelectObject(hdc, hpenGrey);
1287 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
1288 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
1289 }
1290
1291 InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
1292 }
1293
1294 #if wxUSE_UXTHEME
1295 void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
1296 {
1297 wxUxThemeHandle theme(button, L"BUTTON");
1298
1299 // this array is indexed by wxButton::State values and so must be kept in
1300 // sync with it
1301 static const int uxStates[] =
1302 {
1303 PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
1304 };
1305
1306 int iState = uxStates[GetButtonState(button, state)];
1307
1308 wxUxThemeEngine * const engine = wxUxThemeEngine::Get();
1309
1310 // draw parent background if needed
1311 if ( engine->IsThemeBackgroundPartiallyTransparent
1312 (
1313 theme,
1314 BP_PUSHBUTTON,
1315 iState
1316 ) )
1317 {
1318 engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
1319 }
1320
1321 // draw background
1322 engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
1323 &rectBtn, NULL);
1324
1325 // calculate content area margins
1326 MARGINS margins;
1327 engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
1328 TMT_CONTENTMARGINS, &rectBtn, &margins);
1329 ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
1330 ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
1331
1332 if ( button->UseBgCol() )
1333 {
1334 COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
1335 AutoHBRUSH hbrushBackground(colBg);
1336
1337 // don't overwrite the focus rect
1338 RECT rectClient;
1339 ::CopyRect(&rectClient, &rectBtn);
1340 ::InflateRect(&rectClient, -1, -1);
1341 FillRect(hdc, &rectClient, hbrushBackground);
1342 }
1343 }
1344 #endif // wxUSE_UXTHEME
1345
1346 } // anonymous namespace
1347
1348 // ----------------------------------------------------------------------------
1349 // owner drawn buttons support
1350 // ----------------------------------------------------------------------------
1351
1352 void wxButton::MakeOwnerDrawn()
1353 {
1354 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1355 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
1356 {
1357 // make it so
1358 style |= BS_OWNERDRAW;
1359 SetWindowLong(GetHwnd(), GWL_STYLE, style);
1360 }
1361 }
1362
1363 bool wxButton::SetBackgroundColour(const wxColour &colour)
1364 {
1365 if ( !wxControl::SetBackgroundColour(colour) )
1366 {
1367 // nothing to do
1368 return false;
1369 }
1370
1371 MakeOwnerDrawn();
1372
1373 Refresh();
1374
1375 return true;
1376 }
1377
1378 bool wxButton::SetForegroundColour(const wxColour &colour)
1379 {
1380 if ( !wxControl::SetForegroundColour(colour) )
1381 {
1382 // nothing to do
1383 return false;
1384 }
1385
1386 MakeOwnerDrawn();
1387
1388 Refresh();
1389
1390 return true;
1391 }
1392
1393 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
1394 {
1395 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
1396 HDC hdc = lpDIS->hDC;
1397
1398 UINT state = lpDIS->itemState;
1399 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
1400
1401 RECT rectBtn;
1402 CopyRect(&rectBtn, &lpDIS->rcItem);
1403
1404 // draw the button background
1405 if ( !HasFlag(wxBORDER_NONE) )
1406 {
1407 #if wxUSE_UXTHEME
1408 if ( wxUxThemeEngine::GetIfActive() )
1409 {
1410 DrawXPBackground(this, hdc, rectBtn, state);
1411 }
1412 else
1413 #endif // wxUSE_UXTHEME
1414 {
1415 COLORREF colBg = wxColourToRGB(GetBackgroundColour());
1416
1417 // first, draw the background
1418 AutoHBRUSH hbrushBackground(colBg);
1419 FillRect(hdc, &rectBtn, hbrushBackground);
1420
1421 // draw the border for the current state
1422 bool selected = (state & ODS_SELECTED) != 0;
1423 if ( !selected )
1424 {
1425 wxTopLevelWindow *
1426 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
1427 if ( tlw )
1428 {
1429 selected = tlw->GetDefaultItem() == this;
1430 }
1431 }
1432
1433 DrawButtonFrame(hdc, rectBtn, selected, pushed);
1434 }
1435
1436 // draw the focus rectangle if we need it
1437 if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
1438 {
1439 DrawFocusRect(hdc, &rectBtn);
1440
1441 #if wxUSE_UXTHEME
1442 if ( !wxUxThemeEngine::GetIfActive() )
1443 #endif // wxUSE_UXTHEME
1444 {
1445 if ( pushed )
1446 {
1447 // the label is shifted by 1 pixel to create "pushed" effect
1448 OffsetRect(&rectBtn, 1, 1);
1449 }
1450 }
1451 }
1452 }
1453
1454
1455 // draw the image, if any
1456 if ( m_imageData )
1457 {
1458 wxBitmap bmp = m_imageData->GetBitmap(GetButtonState(this, state));
1459 if ( !bmp.IsOk() )
1460 bmp = m_imageData->GetBitmap(State_Normal);
1461
1462 const wxSize sizeBmp = bmp.GetSize();
1463 const wxSize margin = m_imageData->GetBitmapMargins();
1464 const wxSize sizeBmpWithMargins(sizeBmp + 2*margin);
1465 wxRect rectButton(wxRectFromRECT(rectBtn));
1466
1467 // for simplicity, we start with centred rectangle and then move it to
1468 // the appropriate edge
1469 wxRect rectBitmap = wxRect(sizeBmp).CentreIn(rectButton);
1470
1471 // move bitmap only if we have a label, otherwise keep it centered
1472 if ( ShowsLabel() )
1473 {
1474 switch ( m_imageData->GetBitmapPosition() )
1475 {
1476 default:
1477 wxFAIL_MSG( "invalid direction" );
1478 // fall through
1479
1480 case wxLEFT:
1481 rectBitmap.x = rectButton.x + margin.x;
1482 rectButton.x += sizeBmpWithMargins.x;
1483 rectButton.width -= sizeBmpWithMargins.x;
1484 break;
1485
1486 case wxRIGHT:
1487 rectBitmap.x = rectButton.GetRight() - sizeBmp.x - margin.x;
1488 rectButton.width -= sizeBmpWithMargins.x;
1489 break;
1490
1491 case wxTOP:
1492 rectBitmap.y = rectButton.y + margin.y;
1493 rectButton.y += sizeBmpWithMargins.y;
1494 rectButton.height -= sizeBmpWithMargins.y;
1495 break;
1496
1497 case wxBOTTOM:
1498 rectBitmap.y = rectButton.GetBottom() - sizeBmp.y - margin.y;
1499 rectButton.height -= sizeBmpWithMargins.y;
1500 break;
1501 }
1502 }
1503
1504 wxDCTemp dst((WXHDC)hdc);
1505 dst.DrawBitmap(bmp, rectBitmap.GetPosition(), true);
1506
1507 wxCopyRectToRECT(rectButton, rectBtn);
1508 }
1509
1510
1511 // finally draw the label
1512 if ( ShowsLabel() )
1513 {
1514 COLORREF colFg = state & ODS_DISABLED
1515 ? ::GetSysColor(COLOR_GRAYTEXT)
1516 : wxColourToRGB(GetForegroundColour());
1517
1518 wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
1519 wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
1520
1521 #if wxUSE_MARKUP
1522 if ( m_markupText )
1523 {
1524 wxDCTemp dc((WXHDC)hdc);
1525 dc.SetTextForeground(wxColour(colFg));
1526 dc.SetFont(GetFont());
1527
1528 m_markupText->Render(dc, wxRectFromRECT(rectBtn),
1529 state & ODS_NOACCEL
1530 ? wxMarkupText::Render_Default
1531 : wxMarkupText::Render_ShowAccels);
1532 }
1533 else // Plain text label
1534 #endif // wxUSE_MARKUP
1535 {
1536 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
1537 // systems but by happy coincidence ODS_NOACCEL is not used under
1538 // them neither so DT_HIDEPREFIX should never be used there
1539 DrawButtonText(hdc, &rectBtn, this,
1540 state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
1541 }
1542 }
1543
1544 return true;
1545 }
1546
1547 #endif // wxUSE_BUTTON
1548