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