1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/button.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/button.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/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"
48 using namespace wxMSWImpl
;
51 #include "wx/msw/uxtheme.h"
53 // no need to include tmschema.h
55 #define BP_PUSHBUTTON 1
60 #define PBS_DISABLED 4
61 #define PBS_DEFAULTED 5
63 #define TMT_CONTENTMARGINS 3602
66 // provide the necessary declarations ourselves if they're missing from
68 #ifndef BCM_SETIMAGELIST
69 #define BCM_SETIMAGELIST 0x1602
70 #define BCM_SETTEXTMARGIN 0x1604
74 BUTTON_IMAGELIST_ALIGN_LEFT
,
75 BUTTON_IMAGELIST_ALIGN_RIGHT
,
76 BUTTON_IMAGELIST_ALIGN_TOP
,
77 BUTTON_IMAGELIST_ALIGN_BOTTOM
80 struct BUTTON_IMAGELIST
87 #endif // wxUSE_UXTHEME
89 #ifndef WM_THEMECHANGED
90 #define WM_THEMECHANGED 0x031A
94 #define ODS_NOACCEL 0x0100
97 #ifndef ODS_NOFOCUSRECT
98 #define ODS_NOFOCUSRECT 0x0200
101 #ifndef DT_HIDEPREFIX
102 #define DT_HIDEPREFIX 0x00100000
105 // ----------------------------------------------------------------------------
107 // ----------------------------------------------------------------------------
109 // we use different data classes for owner drawn buttons and for themed XP ones
111 class wxButtonImageData
114 wxButtonImageData() { }
115 virtual ~wxButtonImageData() { }
117 virtual wxBitmap
GetBitmap(wxButton::State which
) const = 0;
118 virtual void SetBitmap(const wxBitmap
& bitmap
, wxButton::State which
) = 0;
120 virtual wxSize
GetBitmapMargins() const = 0;
121 virtual void SetBitmapMargins(wxCoord x
, wxCoord y
) = 0;
123 virtual wxDirection
GetBitmapPosition() const = 0;
124 virtual void SetBitmapPosition(wxDirection dir
) = 0;
127 wxDECLARE_NO_COPY_CLASS(wxButtonImageData
);
133 // the gap between button edge and the interior area used by Windows for the
135 const int OD_BUTTON_MARGIN
= 4;
137 class wxODButtonImageData
: public wxButtonImageData
140 wxODButtonImageData(wxButton
*btn
)
144 m_margin
.x
= btn
->GetCharWidth();
145 m_margin
.y
= btn
->GetCharHeight() / 2;
148 virtual wxBitmap
GetBitmap(wxButton::State which
) const
150 return m_bitmaps
[which
];
153 virtual void SetBitmap(const wxBitmap
& bitmap
, wxButton::State which
)
155 m_bitmaps
[which
] = bitmap
;
158 virtual wxSize
GetBitmapMargins() const
160 return m_margin
+ wxSize(OD_BUTTON_MARGIN
, OD_BUTTON_MARGIN
);
163 virtual void SetBitmapMargins(wxCoord x
, wxCoord y
)
165 m_margin
= wxSize(x
, y
);
168 virtual wxDirection
GetBitmapPosition() const
173 virtual void SetBitmapPosition(wxDirection dir
)
179 // just store the values passed to us to be able to retrieve them later
180 // from the drawing code
181 wxBitmap m_bitmaps
[wxButton::State_Max
];
185 wxDECLARE_NO_COPY_CLASS(wxODButtonImageData
);
190 class wxXPButtonImageData
: public wxButtonImageData
193 // we must be constructed with the size of our images as we need to create
195 wxXPButtonImageData(wxButton
*btn
, const wxSize
& size
)
196 : m_iml(size
.x
, size
.y
, true /* use mask */, wxButton::State_Max
),
197 m_hwndBtn(GetHwndOf(btn
))
199 m_data
.himl
= GetHimagelistOf(&m_iml
);
201 // use default margins
203 m_data
.margin
.right
= btn
->GetCharWidth();
205 m_data
.margin
.bottom
= btn
->GetCharHeight() / 2;
207 // and default alignment
208 m_data
.uAlign
= BUTTON_IMAGELIST_ALIGN_LEFT
;
211 virtual wxBitmap
GetBitmap(wxButton::State which
) const
213 return m_iml
.GetBitmap(which
);
216 virtual void SetBitmap(const wxBitmap
& bitmap
, wxButton::State which
)
218 const int imagesToAdd
= which
- m_iml
.GetImageCount();
219 if ( imagesToAdd
>= 0 )
221 if ( imagesToAdd
> 0 )
223 const wxBitmap bmpNormal
= GetBitmap(wxButton::State_Normal
);
224 for ( int n
= 0; n
< imagesToAdd
; n
++ )
225 m_iml
.Add(bmpNormal
);
230 else // we already have this bitmap
232 m_iml
.Replace(which
, bitmap
);
238 virtual wxSize
GetBitmapMargins() const
240 return wxSize(m_data
.margin
.left
, m_data
.margin
.top
);
243 virtual void SetBitmapMargins(wxCoord x
, wxCoord y
)
245 RECT
& margin
= m_data
.margin
;
251 if ( !::SendMessage(m_hwndBtn
, BCM_SETTEXTMARGIN
, 0, (LPARAM
)&margin
) )
253 wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed");
257 virtual wxDirection
GetBitmapPosition() const
259 switch ( m_data
.uAlign
)
262 wxFAIL_MSG( "invalid image alignment" );
265 case BUTTON_IMAGELIST_ALIGN_LEFT
:
268 case BUTTON_IMAGELIST_ALIGN_RIGHT
:
271 case BUTTON_IMAGELIST_ALIGN_TOP
:
274 case BUTTON_IMAGELIST_ALIGN_BOTTOM
:
279 virtual void SetBitmapPosition(wxDirection dir
)
285 wxFAIL_MSG( "invalid direction" );
289 alignNew
= BUTTON_IMAGELIST_ALIGN_LEFT
;
293 alignNew
= BUTTON_IMAGELIST_ALIGN_RIGHT
;
297 alignNew
= BUTTON_IMAGELIST_ALIGN_TOP
;
301 alignNew
= BUTTON_IMAGELIST_ALIGN_BOTTOM
;
305 if ( alignNew
!= m_data
.uAlign
)
307 m_data
.uAlign
= alignNew
;
313 void UpdateImageInfo()
315 if ( !::SendMessage(m_hwndBtn
, BCM_SETIMAGELIST
, 0, (LPARAM
)&m_data
) )
317 wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed");
321 // we store image list separately to be able to use convenient wxImageList
322 // methods instead of working with raw HIMAGELIST
325 // store the rest of the data in BCM_SETIMAGELIST-friendly form
326 BUTTON_IMAGELIST m_data
;
328 // the button we're associated with
329 const HWND m_hwndBtn
;
332 wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData
);
335 #endif // wxUSE_UXTHEME
337 } // anonymous namespace
339 // ----------------------------------------------------------------------------
341 // ----------------------------------------------------------------------------
343 #if wxUSE_EXTENDED_RTTI
345 WX_DEFINE_FLAGS( wxButtonStyle
)
347 wxBEGIN_FLAGS( wxButtonStyle
)
348 // new style border flags, we put them first to
349 // use them for streaming out
350 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
351 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
352 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
353 wxFLAGS_MEMBER(wxBORDER_RAISED
)
354 wxFLAGS_MEMBER(wxBORDER_STATIC
)
355 wxFLAGS_MEMBER(wxBORDER_NONE
)
357 // old style border flags
358 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
359 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
360 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
361 wxFLAGS_MEMBER(wxRAISED_BORDER
)
362 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
363 wxFLAGS_MEMBER(wxBORDER
)
365 // standard window styles
366 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
367 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
368 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
369 wxFLAGS_MEMBER(wxWANTS_CHARS
)
370 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
371 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
372 wxFLAGS_MEMBER(wxVSCROLL
)
373 wxFLAGS_MEMBER(wxHSCROLL
)
375 wxFLAGS_MEMBER(wxBU_LEFT
)
376 wxFLAGS_MEMBER(wxBU_RIGHT
)
377 wxFLAGS_MEMBER(wxBU_TOP
)
378 wxFLAGS_MEMBER(wxBU_BOTTOM
)
379 wxFLAGS_MEMBER(wxBU_EXACTFIT
)
380 wxEND_FLAGS( wxButtonStyle
)
382 IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton
, wxControl
,"wx/button.h")
384 wxBEGIN_PROPERTIES_TABLE(wxButton
)
385 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_BUTTON_CLICKED
, wxCommandEvent
)
387 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
388 wxPROPERTY( Label
, wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
390 wxPROPERTY_FLAGS( WindowStyle
, wxButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
392 wxEND_PROPERTIES_TABLE()
394 wxBEGIN_HANDLERS_TABLE(wxButton
)
395 wxEND_HANDLERS_TABLE()
397 wxCONSTRUCTOR_6( wxButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
401 IMPLEMENT_DYNAMIC_CLASS(wxButton
, wxControl
)
404 // ============================================================================
406 // ============================================================================
408 // ----------------------------------------------------------------------------
409 // helper functions from wx/msw/private/button.h
410 // ----------------------------------------------------------------------------
412 void wxMSWButton::UpdateMultilineStyle(HWND hwnd
, const wxString
& label
)
414 // update BS_MULTILINE style depending on the new label (resetting it
415 // doesn't seem to do anything very useful but it shouldn't hurt and we do
416 // have to set it whenever the label becomes multi line as otherwise it
417 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
418 // the control unless it already has new lines in its label)
419 long styleOld
= ::GetWindowLong(hwnd
, GWL_STYLE
),
421 if ( label
.find(_T('\n')) != wxString::npos
)
422 styleNew
= styleOld
| BS_MULTILINE
;
424 styleNew
= styleOld
& ~BS_MULTILINE
;
426 if ( styleNew
!= styleOld
)
427 ::SetWindowLong(hwnd
, GWL_STYLE
, styleNew
);
430 wxSize
wxMSWButton::GetFittingSize(wxWindow
*win
, const wxSize
& sizeLabel
)
432 // FIXME: this is pure guesswork, need to retrieve the real button margins
433 wxSize sizeBtn
= sizeLabel
;
435 sizeBtn
.x
+= 3*win
->GetCharWidth();
436 sizeBtn
.y
= 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel
.y
)/10;
441 wxSize
wxMSWButton::ComputeBestSize(wxControl
*btn
)
446 dc
.GetMultiLineTextExtent(btn
->GetLabelText(), &sizeBtn
.x
, &sizeBtn
.y
);
448 sizeBtn
= GetFittingSize(btn
, sizeBtn
);
450 // all buttons have at least the standard size unless the user explicitly
451 // wants them to be of smaller size and used wxBU_EXACTFIT style when
452 // creating the button
453 if ( !btn
->HasFlag(wxBU_EXACTFIT
) )
455 wxSize sizeDef
= wxButton::GetDefaultSize();
456 if ( sizeBtn
.x
< sizeDef
.x
)
457 sizeBtn
.x
= sizeDef
.x
;
458 if ( sizeBtn
.y
< sizeDef
.y
)
459 sizeBtn
.y
= sizeDef
.y
;
462 btn
->CacheBestSize(sizeBtn
);
467 // ----------------------------------------------------------------------------
468 // creation/destruction
469 // ----------------------------------------------------------------------------
471 bool wxButton::Create(wxWindow
*parent
,
477 const wxValidator
& validator
,
478 const wxString
& name
)
481 if (label
.empty() && wxIsStockID(id
))
483 // On Windows, some buttons aren't supposed to have mnemonics
484 label
= wxGetStockLabel
487 id
== wxID_OK
|| id
== wxID_CANCEL
|| id
== wxID_CLOSE
489 : wxSTOCK_WITH_MNEMONIC
493 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
497 WXDWORD msStyle
= MSWGetStyle(style
, &exstyle
);
499 // if the label contains several lines we must explicitly tell the button
500 // about it or it wouldn't draw it correctly ("\n"s would just appear as
503 // NB: we do it here and not in MSWGetStyle() because we need the label
504 // value and the label is not set yet when MSWGetStyle() is called
505 msStyle
|= wxMSWButton::GetMultilineStyle(label
);
507 return MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, exstyle
);
510 wxButton::~wxButton()
512 wxTopLevelWindow
*tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
513 if ( tlw
&& tlw
->GetTmpDefaultItem() == this )
521 // ----------------------------------------------------------------------------
523 // ----------------------------------------------------------------------------
525 WXDWORD
wxButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
527 // buttons never have an external border, they draw their own one
528 WXDWORD msStyle
= wxControl::MSWGetStyle
530 (style
& ~wxBORDER_MASK
) | wxBORDER_NONE
, exstyle
533 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
534 // each other in any resizeable dialog which has more than one button in
536 msStyle
|= WS_CLIPSIBLINGS
;
538 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
539 // and wxBU_RIGHT to get BS_CENTER!
540 if ( style
& wxBU_LEFT
)
542 if ( style
& wxBU_RIGHT
)
544 if ( style
& wxBU_TOP
)
546 if ( style
& wxBU_BOTTOM
)
547 msStyle
|= BS_BOTTOM
;
550 if ( style
& wxNO_BORDER
)
552 #endif // __WXWINCE__
557 void wxButton::SetLabel(const wxString
& label
)
559 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label
);
561 wxButtonBase::SetLabel(label
);
564 // ----------------------------------------------------------------------------
565 // size management including autosizing
566 // ----------------------------------------------------------------------------
568 wxSize
wxButton::DoGetBestSize() const
570 wxSize size
= wxMSWButton::ComputeBestSize(const_cast<wxButton
*>(this));
573 const wxSize sizeBmp
= m_imageData
->GetBitmap(State_Normal
).GetSize();
574 const wxDirection dirBmp
= m_imageData
->GetBitmapPosition();
575 if ( dirBmp
== wxLEFT
|| dirBmp
== wxRIGHT
)
578 if ( sizeBmp
.y
> size
.y
)
581 else // bitmap on top/below the text
584 if ( sizeBmp
.x
> size
.x
)
588 size
+= 2*m_imageData
->GetBitmapMargins();
597 wxSize
wxButtonBase::GetDefaultSize()
599 static wxSize s_sizeBtn
;
601 if ( s_sizeBtn
.x
== 0 )
604 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
606 // the size of a standard button in the dialog units is 50x14,
607 // translate this to pixels
608 // NB1: the multipliers come from the Windows convention
609 // NB2: the extra +1/+2 were needed to get the size be the same as the
610 // size of the buttons in the standard dialog - I don't know how
611 // this happens, but on my system this size is 75x23 in pixels and
612 // 23*8 isn't even divisible by 14... Would be nice to understand
613 // why these constants are needed though!
614 s_sizeBtn
.x
= (50 * (dc
.GetCharWidth() + 1))/4;
615 s_sizeBtn
.y
= ((14 * dc
.GetCharHeight()) + 2)/8;
621 // ----------------------------------------------------------------------------
622 // default button handling
623 // ----------------------------------------------------------------------------
626 "Everything you ever wanted to know about the default buttons" or "Why do we
627 have to do all this?"
629 In MSW the default button should be activated when the user presses Enter
630 and the current control doesn't process Enter itself somehow. This is
631 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
632 Another aspect of "defaultness" is that the default button has different
633 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
634 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
635 be unset if our parent window is not active so it should be unset whenever
636 we lose activation and set back when we regain it.
638 Final complication is that when a button is active, it should be the default
639 one, i.e. pressing Enter on a button always activates it and not another
642 We handle this by maintaining a permanent and a temporary default items in
643 wxControlContainer (both may be NULL). When a button becomes the current
644 control (i.e. gets focus) it sets itself as the temporary default which
645 ensures that it has the right appearance and that Enter will be redirected
646 to it. When the button loses focus, it unsets the temporary default and so
647 the default item will be the permanent default -- that is the default button
648 if any had been set or none otherwise, which is just what we want.
650 NB: all this is quite complicated by now and the worst is that normally
651 it shouldn't be necessary at all as for the normal Windows programs
652 DefWindowProc() and IsDialogMessage() take care of all this
653 automatically -- however in wxWidgets programs this doesn't work for
654 nested hierarchies (i.e. a notebook inside a notebook) for unknown
655 reason and so we have to reproduce all this code ourselves. It would be
656 very nice if we could avoid doing it.
659 // set this button as the (permanently) default one in its panel
660 wxWindow
*wxButton::SetDefault()
662 // set this one as the default button both for wxWidgets ...
663 wxWindow
*winOldDefault
= wxButtonBase::SetDefault();
666 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
667 SetDefaultStyle(this, true);
669 return winOldDefault
;
672 // return the top level parent window if it's not being deleted yet, otherwise
674 static wxTopLevelWindow
*GetTLWParentIfNotBeingDeleted(wxWindow
*win
)
678 // IsTopLevel() will return false for a wxTLW being deleted, so we also
679 // need the parent test for this case
680 wxWindow
* const parent
= win
->GetParent();
681 if ( !parent
|| win
->IsTopLevel() )
683 if ( win
->IsBeingDeleted() )
692 wxASSERT_MSG( win
, _T("button without top level parent?") );
694 wxTopLevelWindow
* const tlw
= wxDynamicCast(win
, wxTopLevelWindow
);
695 wxASSERT_MSG( tlw
, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
700 // set this button as being currently default
701 void wxButton::SetTmpDefault()
703 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
707 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
708 tlw
->SetTmpDefaultItem(this);
710 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), false);
711 SetDefaultStyle(this, true);
714 // unset this button as currently default, it may still stay permanent default
715 void wxButton::UnsetTmpDefault()
717 wxTopLevelWindow
* const tlw
= GetTLWParentIfNotBeingDeleted(GetParent());
721 tlw
->SetTmpDefaultItem(NULL
);
723 wxWindow
*winOldDefault
= tlw
->GetDefaultItem();
725 SetDefaultStyle(this, false);
726 SetDefaultStyle(wxDynamicCast(winOldDefault
, wxButton
), true);
731 wxButton::SetDefaultStyle(wxButton
*btn
, bool on
)
733 // we may be called with NULL pointer -- simpler to do the check here than
734 // in the caller which does wxDynamicCast()
738 // first, let DefDlgProc() know about the new default button
741 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
742 // focus at all any more
743 if ( !wxTheApp
->IsActive() )
746 wxWindow
* const tlw
= wxGetTopLevelParent(btn
);
747 wxCHECK_RET( tlw
, _T("button without top level window?") );
749 ::SendMessage(GetHwndOf(tlw
), DM_SETDEFID
, btn
->GetId(), 0L);
751 // sending DM_SETDEFID also changes the button style to
752 // BS_DEFPUSHBUTTON so there is nothing more to do
755 // then also change the style as needed
756 long style
= ::GetWindowLong(GetHwndOf(btn
), GWL_STYLE
);
757 if ( !(style
& BS_DEFPUSHBUTTON
) == on
)
759 // don't do it with the owner drawn buttons because it will
760 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
761 // BS_DEFPUSHBUTTON != 0)!
762 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
764 ::SendMessage(GetHwndOf(btn
), BM_SETSTYLE
,
765 on
? style
| BS_DEFPUSHBUTTON
766 : style
& ~BS_DEFPUSHBUTTON
,
771 // redraw the button - it will notice itself that it's
772 // [not] the default one [any longer]
776 //else: already has correct style
779 // ----------------------------------------------------------------------------
781 // ----------------------------------------------------------------------------
783 bool wxButton::SendClickEvent()
785 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
786 event
.SetEventObject(this);
788 return ProcessCommand(event
);
791 void wxButton::Command(wxCommandEvent
& event
)
793 ProcessCommand(event
);
796 // ----------------------------------------------------------------------------
797 // event/message handlers
798 // ----------------------------------------------------------------------------
800 bool wxButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
802 bool processed
= false;
805 // NOTE: Apparently older versions (NT 4?) of the common controls send
806 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
807 // buttons, so in order to send two EVT_BUTTON events we should
808 // catch both types. Currently (Feb 2003) up-to-date versions of
809 // win98, win2k and winXP all send two BN_CLICKED messages for
810 // all button types, so we don't catch BN_DOUBLECLICKED anymore
811 // in order to not get 3 EVT_BUTTON events. If this is a problem
812 // then we need to figure out which version of the comctl32 changed
813 // this behaviour and test for it.
815 case 1: // message came from an accelerator
816 case BN_CLICKED
: // normal buttons send this
817 processed
= SendClickEvent();
824 WXLRESULT
wxButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
826 // when we receive focus, we want to temporarily become the default button in
827 // our parent panel so that pressing "Enter" would activate us -- and when
828 // losing it we should restore the previous default button as well
829 if ( nMsg
== WM_SETFOCUS
)
833 // let the default processing take place too
835 else if ( nMsg
== WM_KILLFOCUS
)
839 else if ( nMsg
== WM_LBUTTONDBLCLK
)
841 // emulate a click event to force an owner-drawn button to change its
842 // appearance - without this, it won't do it
843 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN
, wParam
, lParam
);
845 // and continue with processing the message normally as well
848 else if ( nMsg
== WM_THEMECHANGED
)
850 // need to recalculate the best size here
851 // as the theme size might have changed
852 InvalidateBestSize();
854 else if ( wxUxThemeEngine::GetIfActive() )
856 // we need to Refresh() if mouse has entered or left window
857 // so we can update the hot tracking state
858 // must use m_mouseInWindow here instead of IsMouseInWindow()
859 // since we need to know the first time the mouse enters the window
860 // and IsMouseInWindow() would return true in this case
861 if ( ( nMsg
== WM_MOUSEMOVE
&& !m_mouseInWindow
) ||
862 nMsg
== WM_MOUSELEAVE
)
867 #endif // wxUSE_UXTHEME
869 // let the base class do all real processing
870 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
873 // ----------------------------------------------------------------------------
875 // ----------------------------------------------------------------------------
877 wxBitmap
wxButton::DoGetBitmap(State which
) const
879 return m_imageData
? m_imageData
->GetBitmap(which
) : wxBitmap();
882 void wxButton::DoSetBitmap(const wxBitmap
& bitmap
, State which
)
884 // allocate the image data when the first bitmap is set
888 if ( wxUxThemeEngine::GetIfActive() )
890 m_imageData
= new wxXPButtonImageData(this, bitmap
.GetSize());
893 #endif // wxUSE_UXTHEME
895 m_imageData
= new wxODButtonImageData(this);
899 // if a bitmap was assigned to the bitmap, its best size must be
900 // changed to account for it
901 InvalidateBestSize();
904 m_imageData
->SetBitmap(bitmap
, which
);
907 void wxButton::DoSetBitmapMargins(wxCoord x
, wxCoord y
)
909 wxCHECK_RET( m_imageData
, "SetBitmap() must be called first" );
911 m_imageData
->SetBitmapMargins(x
, y
);
914 void wxButton::DoSetBitmapPosition(wxDirection dir
)
916 wxCHECK_RET( m_imageData
, "SetBitmap() must be called first" );
918 m_imageData
->SetBitmapPosition(dir
);
921 // ----------------------------------------------------------------------------
922 // owner-drawn buttons support
923 // ----------------------------------------------------------------------------
929 void DrawButtonText(HDC hdc
,
931 const wxString
& text
,
935 wxTextColoursChanger
changeFg(hdc
, col
, CLR_INVALID
);
936 wxBkModeChanger
changeBkMode(hdc
, wxBRUSHSTYLE_TRANSPARENT
);
938 // center text horizontally in any case
941 if ( text
.find(_T('\n')) != wxString::npos
)
943 // draw multiline label
945 // first we need to compute its bounding rect
947 ::CopyRect(&rc
, pRect
);
948 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
,
949 DT_CENTER
| DT_CALCRECT
);
951 // now center this rect inside the entire button area
952 const LONG w
= rc
.right
- rc
.left
;
953 const LONG h
= rc
.bottom
- rc
.top
;
954 rc
.left
= (pRect
->right
- pRect
->left
)/2 - w
/2;
955 rc
.right
= rc
.left
+w
;
956 rc
.top
= (pRect
->bottom
- pRect
->top
)/2 - h
/2;
957 rc
.bottom
= rc
.top
+h
;
959 ::DrawText(hdc
, text
.wx_str(), text
.length(), &rc
, flags
);
961 else // single line label
963 // centre text vertically too (notice that we must have DT_SINGLELINE
964 // for DT_VCENTER to work)
965 ::DrawText(hdc
, text
.wx_str(), text
.length(), pRect
,
966 flags
| DT_SINGLELINE
| DT_VCENTER
);
970 void DrawRect(HDC hdc
, const RECT
& r
)
972 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
973 wxDrawLine(hdc
, r
.right
, r
.top
, r
.right
, r
.bottom
);
974 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.left
, r
.bottom
);
975 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.left
, r
.top
);
979 The button frame looks like this normally:
982 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
983 WH GB H = light grey (LIGHT)
984 WH GB G = dark grey (SHADOW)
985 WH GB B = black (DKSHADOW)
990 When the button is selected, the button becomes like this (the total button
991 size doesn't change):
1002 When the button is pushed (while selected) it is like:
1013 void DrawButtonFrame(HDC hdc
, RECT
& rectBtn
,
1014 bool selected
, bool pushed
)
1017 CopyRect(&r
, &rectBtn
);
1019 AutoHPEN
hpenBlack(GetSysColor(COLOR_3DDKSHADOW
)),
1020 hpenGrey(GetSysColor(COLOR_3DSHADOW
)),
1021 hpenLightGr(GetSysColor(COLOR_3DLIGHT
)),
1022 hpenWhite(GetSysColor(COLOR_3DHILIGHT
));
1024 SelectInHDC
selectPen(hdc
, hpenBlack
);
1033 (void)SelectObject(hdc
, hpenGrey
);
1034 ::InflateRect(&r
, -1, -1);
1044 ::InflateRect(&r
, -1, -1);
1047 wxDrawLine(hdc
, r
.left
, r
.bottom
, r
.right
, r
.bottom
);
1048 wxDrawLine(hdc
, r
.right
, r
.bottom
, r
.right
, r
.top
- 1);
1050 (void)SelectObject(hdc
, hpenWhite
);
1051 wxDrawLine(hdc
, r
.left
, r
.bottom
- 1, r
.left
, r
.top
);
1052 wxDrawLine(hdc
, r
.left
, r
.top
, r
.right
, r
.top
);
1054 (void)SelectObject(hdc
, hpenLightGr
);
1055 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 2, r
.left
+ 1, r
.top
+ 1);
1056 wxDrawLine(hdc
, r
.left
+ 1, r
.top
+ 1, r
.right
- 1, r
.top
+ 1);
1058 (void)SelectObject(hdc
, hpenGrey
);
1059 wxDrawLine(hdc
, r
.left
+ 1, r
.bottom
- 1, r
.right
- 1, r
.bottom
- 1);
1060 wxDrawLine(hdc
, r
.right
- 1, r
.bottom
- 1, r
.right
- 1, r
.top
);
1063 InflateRect(&rectBtn
, -OD_BUTTON_MARGIN
, -OD_BUTTON_MARGIN
);
1067 void MSWDrawXPBackground(wxButton
*button
, HDC hdc
, RECT
& rectBtn
, UINT state
)
1069 wxUxThemeHandle
theme(button
, L
"BUTTON");
1072 if ( state
& ODS_SELECTED
)
1074 iState
= PBS_PRESSED
;
1076 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
1080 else if ( state
& ODS_FOCUS
)
1082 iState
= PBS_DEFAULTED
;
1084 else if ( state
& ODS_DISABLED
)
1086 iState
= PBS_DISABLED
;
1090 iState
= PBS_NORMAL
;
1093 // draw parent background if needed
1094 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
1098 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
1102 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
1105 // calculate content area margins
1107 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
1108 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
1109 ::InflateRect(&rectBtn
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
1111 if ( button
->UseBgCol() )
1113 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
1114 AutoHBRUSH
hbrushBackground(colBg
);
1116 // don't overwrite the focus rect
1118 ::CopyRect(&rectClient
, &rectBtn
);
1119 ::InflateRect(&rectClient
, -1, -1);
1120 FillRect(hdc
, &rectClient
, hbrushBackground
);
1123 #endif // wxUSE_UXTHEME
1125 } // anonymous namespace
1127 // ----------------------------------------------------------------------------
1128 // owner drawn buttons support
1129 // ----------------------------------------------------------------------------
1131 void wxButton::MakeOwnerDrawn()
1133 long style
= GetWindowLong(GetHwnd(), GWL_STYLE
);
1134 if ( (style
& BS_OWNERDRAW
) != BS_OWNERDRAW
)
1137 style
|= BS_OWNERDRAW
;
1138 SetWindowLong(GetHwnd(), GWL_STYLE
, style
);
1142 bool wxButton::SetBackgroundColour(const wxColour
&colour
)
1144 if ( !wxControl::SetBackgroundColour(colour
) )
1157 bool wxButton::SetForegroundColour(const wxColour
&colour
)
1159 if ( !wxControl::SetForegroundColour(colour
) )
1172 bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT
*wxdis
)
1174 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
1175 HDC hdc
= lpDIS
->hDC
;
1177 UINT state
= lpDIS
->itemState
;
1178 bool pushed
= (SendMessage(GetHwnd(), BM_GETSTATE
, 0, 0) & BST_PUSHED
) != 0;
1181 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
1183 // draw the button background
1185 if ( wxUxThemeEngine::GetIfActive() )
1187 MSWDrawXPBackground(this, hdc
, rectBtn
, state
);
1190 #endif // wxUSE_UXTHEME
1192 COLORREF colBg
= wxColourToRGB(GetBackgroundColour());
1194 // first, draw the background
1195 AutoHBRUSH
hbrushBackground(colBg
);
1196 FillRect(hdc
, &rectBtn
, hbrushBackground
);
1198 // draw the border for the current state
1199 bool selected
= (state
& ODS_SELECTED
) != 0;
1203 tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
1206 selected
= tlw
->GetDefaultItem() == this;
1210 DrawButtonFrame(hdc
, rectBtn
, selected
, pushed
);
1213 // draw the focus rectangle if we need it
1214 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
1216 DrawFocusRect(hdc
, &rectBtn
);
1219 if ( !wxUxThemeEngine::GetIfActive() )
1220 #endif // wxUSE_UXTHEME
1224 // the label is shifted by 1 pixel to create "pushed" effect
1225 OffsetRect(&rectBtn
, 1, 1);
1231 // draw the image, if any
1234 wxBitmap bmp
= m_imageData
->GetBitmap(State_Normal
);
1235 const wxSize sizeBmp
= bmp
.GetSize();
1236 const wxSize margin
= m_imageData
->GetBitmapMargins();
1237 const wxSize
sizeBmpWithMargins(sizeBmp
+ 2*margin
);
1238 wxRect
rectButton(wxRectFromRECT(rectBtn
));
1240 // for simplicity, we start with centred rectangle and then move it to
1241 // the appropriate edge
1242 wxRect rectBitmap
= wxRect(sizeBmp
).CentreIn(rectButton
);
1243 switch ( m_imageData
->GetBitmapPosition() )
1246 wxFAIL_MSG( "invalid direction" );
1250 rectBitmap
.x
= rectButton
.x
+ margin
.x
;
1251 rectButton
.x
+= sizeBmpWithMargins
.x
;
1252 rectButton
.width
-= sizeBmpWithMargins
.x
;
1256 rectBitmap
.x
= rectButton
.GetRight() - sizeBmp
.x
- margin
.x
;
1257 rectButton
.width
-= sizeBmpWithMargins
.x
;
1261 rectBitmap
.y
= rectButton
.y
+ margin
.y
;
1262 rectButton
.y
+= sizeBmpWithMargins
.y
;
1263 rectButton
.height
-= sizeBmpWithMargins
.y
;
1267 rectBitmap
.y
= rectButton
.GetBottom() - sizeBmp
.y
- margin
.y
;
1268 rectButton
.height
-= sizeBmpWithMargins
.y
;
1272 wxDCTemp
dst((WXHDC
)hdc
);
1273 dst
.DrawBitmap(bmp
, rectBitmap
.GetPosition(), true);
1275 wxCopyRectToRECT(rectButton
, rectBtn
);
1279 // finally draw the label
1280 COLORREF colFg
= state
& ODS_DISABLED
1281 ? ::GetSysColor(COLOR_GRAYTEXT
)
1282 : wxColourToRGB(GetForegroundColour());
1284 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000) systems
1285 // but by happy coincidence ODS_NOACCEL is not used under them neither so
1286 // DT_HIDEPREFIX should never be used there
1287 DrawButtonText(hdc
, &rectBtn
, GetLabel(), colFg
,
1288 state
& ODS_NOACCEL
? DT_HIDEPREFIX
: 0);
1293 #endif // wxUSE_BUTTON