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