1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/radiobut.cpp
3 // Purpose: wxRadioButton
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/radiobut.h"
32 #include "wx/settings.h"
33 #include "wx/dcscreen.h"
36 #include "wx/msw/private.h"
38 // ============================================================================
39 // wxRadioButton implementation
40 // ============================================================================
42 // ----------------------------------------------------------------------------
43 // wxRadioButton creation
44 // ----------------------------------------------------------------------------
47 #if wxUSE_EXTENDED_RTTI
48 WX_DEFINE_FLAGS( wxRadioButtonStyle
)
50 wxBEGIN_FLAGS( wxRadioButtonStyle
)
51 // new style border flags, we put them first to
52 // use them for streaming out
53 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
54 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
55 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
56 wxFLAGS_MEMBER(wxBORDER_RAISED
)
57 wxFLAGS_MEMBER(wxBORDER_STATIC
)
58 wxFLAGS_MEMBER(wxBORDER_NONE
)
60 // old style border flags
61 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
62 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
63 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
64 wxFLAGS_MEMBER(wxRAISED_BORDER
)
65 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
66 wxFLAGS_MEMBER(wxBORDER
)
68 // standard window styles
69 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
70 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
71 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
72 wxFLAGS_MEMBER(wxWANTS_CHARS
)
73 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
74 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
75 wxFLAGS_MEMBER(wxVSCROLL
)
76 wxFLAGS_MEMBER(wxHSCROLL
)
78 wxFLAGS_MEMBER(wxRB_GROUP
)
80 wxEND_FLAGS( wxRadioButtonStyle
)
82 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton
, wxControl
,"wx/radiobut.h")
84 wxBEGIN_PROPERTIES_TABLE(wxRadioButton
)
85 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_RADIOBUTTON_SELECTED
, wxCommandEvent
)
86 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
87 wxPROPERTY( Label
,wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
88 wxPROPERTY( Value
,bool, SetValue
, GetValue
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
89 wxPROPERTY_FLAGS( WindowStyle
, wxRadioButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
90 wxEND_PROPERTIES_TABLE()
92 wxBEGIN_HANDLERS_TABLE(wxRadioButton
)
93 wxEND_HANDLERS_TABLE()
95 wxCONSTRUCTOR_6( wxRadioButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
98 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton
, wxControl
)
102 void wxRadioButton::Init()
107 bool wxRadioButton::Create(wxWindow
*parent
,
109 const wxString
& label
,
113 const wxValidator
& validator
,
114 const wxString
& name
)
116 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
119 long msStyle
= WS_TABSTOP
;
120 if ( HasFlag(wxRB_GROUP
) )
124 wxRB_SINGLE is a temporary workaround for the following problem: if you
125 have 2 radiobuttons in the same group but which are not consecutive in
126 the dialog, Windows can enter an infinite loop! The simplest way to
127 reproduce it is to create radio button, then a panel and then another
128 radio button: then checking the last button hangs the app.
130 Ideally, we'd detect (and avoid) such situation automatically but for
131 now, as I don't know how to do it, just allow the user to create
132 BS_RADIOBUTTON buttons for such situations.
134 msStyle
|= HasFlag(wxRB_SINGLE
) ? BS_RADIOBUTTON
: BS_AUTORADIOBUTTON
;
136 if ( HasFlag(wxCLIP_SIBLINGS
) )
137 msStyle
|= WS_CLIPSIBLINGS
;
138 if ( HasFlag(wxALIGN_RIGHT
) )
139 msStyle
|= BS_LEFTTEXT
| BS_RIGHT
;
141 if ( !MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, 0) )
144 // for compatibility with wxGTK, the first radio button in a group is
145 // always checked (this makes sense anyhow as you need to ensure that at
146 // least one button in the group is checked and this is the simlpest way to
148 if ( HasFlag(wxRB_GROUP
) )
154 // ----------------------------------------------------------------------------
155 // wxRadioButton functions
156 // ----------------------------------------------------------------------------
158 void wxRadioButton::SetValue(bool value
)
160 (void)::SendMessage(GetHwnd(), BM_SETCHECK
, (value
?BST_CHECKED
:BST_UNCHECKED
), 0L);
164 // if we set the value of one radio button we also must clear all the other
165 // buttons in the same group: Windows doesn't do it automatically
168 // If another radiobutton in the group currently has the focus, we have to
169 // set it to this radiobutton, else the old readiobutton will be reselected
170 // automatically, if a parent window loses the focus and regains it.
171 bool shouldSetFocus
= false;
172 wxWindow
* pFocusWnd
= FindFocus();
174 const wxWindowList
& siblings
= GetParent()->GetChildren();
175 wxWindowList::compatibility_iterator nodeThis
= siblings
.Find(this);
176 wxCHECK_RET( nodeThis
, _T("radio button not a child of its parent?") );
178 // if it's not the first item of the group ...
179 if ( !HasFlag(wxRB_GROUP
) )
181 // ... turn off all radio buttons before it
182 for ( wxWindowList::compatibility_iterator nodeBefore
= nodeThis
->GetPrevious();
184 nodeBefore
= nodeBefore
->GetPrevious() )
186 wxRadioButton
*btn
= wxDynamicCast(nodeBefore
->GetData(),
188 if ( btn
&& btn
->HasFlag(wxRB_SINGLE
) )
190 // A wxRB_SINGLE button isn't part of this group
196 if (btn
== pFocusWnd
)
197 shouldSetFocus
= true;
199 btn
->SetValue(false);
201 if ( btn
->HasFlag(wxRB_GROUP
) )
203 // even if there are other radio buttons before this one,
204 // they're not in the same group with us
211 // ... and also turn off all buttons after this one
212 for ( wxWindowList::compatibility_iterator nodeAfter
= nodeThis
->GetNext();
214 nodeAfter
= nodeAfter
->GetNext() )
216 wxRadioButton
*btn
= wxDynamicCast(nodeAfter
->GetData(),
219 if ( btn
&& (btn
->HasFlag(wxRB_GROUP
) || btn
->HasFlag(wxRB_SINGLE
) ) )
221 // no more buttons or the first button of the next group
227 if (btn
== pFocusWnd
)
228 shouldSetFocus
= true;
230 btn
->SetValue(false);
238 bool wxRadioButton::GetValue() const
240 wxASSERT_MSG( m_isChecked
==
241 (::SendMessage(GetHwnd(), BM_GETCHECK
, 0, 0L) != 0),
242 _T("wxRadioButton::m_isChecked is out of sync?") );
247 // ----------------------------------------------------------------------------
248 // wxRadioButton event processing
249 // ----------------------------------------------------------------------------
251 void wxRadioButton::Command (wxCommandEvent
& event
)
253 SetValue(event
.GetInt() != 0);
254 ProcessCommand(event
);
257 bool wxRadioButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
259 if ( param
!= BN_CLICKED
)
264 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
265 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
266 // receive focus the button gets BN_CLICKED but stays unchecked!)
269 wxCommandEvent
event(wxEVT_COMMAND_RADIOBUTTON_SELECTED
, GetId());
270 event
.SetEventObject( this );
271 event
.SetInt(true); // always checked
273 ProcessCommand(event
);
279 // ----------------------------------------------------------------------------
280 // wxRadioButton geometry
281 // ----------------------------------------------------------------------------
283 wxSize
wxRadioButton::DoGetBestSize() const
285 static int s_radioSize
= 0;
290 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
292 s_radioSize
= dc
.GetCharHeight();
295 wxString str
= GetLabel();
300 GetTextExtent(GetLabelText(str
), &wRadio
, &hRadio
);
301 wRadio
+= s_radioSize
+ GetCharWidth();
303 if ( hRadio
< s_radioSize
)
304 hRadio
= s_radioSize
;
308 wRadio
= s_radioSize
;
309 hRadio
= s_radioSize
;
312 wxSize
best(wRadio
, hRadio
);
317 #endif // wxUSE_RADIOBTN