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