1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "radiobut.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
34 #include "wx/radiobut.h"
35 #include "wx/settings.h"
37 #include "wx/dcscreen.h"
40 #include "wx/msw/private.h"
42 // ============================================================================
43 // wxRadioButton implementation
44 // ============================================================================
46 // ----------------------------------------------------------------------------
47 // wxRadioButton creation
48 // ----------------------------------------------------------------------------
51 #if wxUSE_EXTENDED_RTTI
52 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton
, wxControl
,"wx/radiobut.h")
54 WX_BEGIN_PROPERTIES_TABLE(wxRadioButton
)
55 WX_DELEGATE( OnClick
, wxEVT_COMMAND_RADIOBUTTON_SELECTED
, wxCommandEvent
)
56 WX_PROPERTY_SET_AND_GET_BY_REF_RET_BOOL( Font
, wxFont
, SetFont
, GetFont
, )
57 WX_PROPERTY_SET_BY_REF( Label
,wxString
, SetLabel
, GetLabel
, wxT("") )
58 WX_PROPERTY( Value
,bool, SetValue
, GetValue
, )
59 WX_END_PROPERTIES_TABLE()
61 WX_BEGIN_HANDLERS_TABLE(wxRadioButton
)
62 WX_END_HANDLERS_TABLE()
64 WX_CONSTRUCTOR_6( wxRadioButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
67 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton
, wxControl
)
71 void wxRadioButton::Init()
73 m_focusJustSet
= FALSE
;
76 bool wxRadioButton::Create(wxWindow
*parent
,
78 const wxString
& label
,
82 const wxValidator
& validator
,
85 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
88 long msStyle
= WS_TABSTOP
;
89 if ( HasFlag(wxRB_GROUP
) )
93 wxRB_SINGLE is a temporary workaround for the following problem: if you
94 have 2 radiobuttons in the same group but which are not consecutive in
95 the dialog, Windows can enter an infinite loop! The simplest way to
96 reproduce it is to create radio button, then a panel and then another
97 radio button: then checking the last button hangs the app.
99 Ideally, we'd detect (and avoid) such situation automatically but for
100 now, as I don't know how to do it, just allow the user to create
101 BS_RADIOBUTTON buttons for such situations.
103 msStyle
|= HasFlag(wxRB_SINGLE
) ? BS_RADIOBUTTON
: BS_AUTORADIOBUTTON
;
105 if ( HasFlag(wxCLIP_SIBLINGS
) )
106 msStyle
|= WS_CLIPSIBLINGS
;
108 if ( !MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, 0) )
111 // for compatibility with wxGTK, the first radio button in a group is
112 // always checked (this makes sense anyhow as you need to ensure that at
113 // least one button in the group is checked and this is the simlpest way to
115 if ( HasFlag(wxRB_GROUP
) )
121 // ----------------------------------------------------------------------------
122 // wxRadioButton functions
123 // ----------------------------------------------------------------------------
125 void wxRadioButton::SetValue(bool value
)
127 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
128 // value as is (we don't use BST_XXX here as they're not defined for Win16)
129 (void)::SendMessage(GetHwnd(), BM_SETCHECK
, (WPARAM
)value
, 0L);
131 // if we set the value of one radio button we also must clear all the other
132 // buttons in the same group: Windows doesn't do it automatically
135 const wxWindowList
& siblings
= GetParent()->GetChildren();
136 wxWindowList::compatibility_iterator nodeThis
= siblings
.Find(this);
137 wxCHECK_RET( nodeThis
, _T("radio button not a child of its parent?") );
139 // if it's not the first item of the group ...
140 if ( !HasFlag(wxRB_GROUP
) )
142 // ... turn off all radio buttons before it
143 for ( wxWindowList::compatibility_iterator nodeBefore
= nodeThis
->GetPrevious();
145 nodeBefore
= nodeBefore
->GetPrevious() )
147 wxRadioButton
*btn
= wxDynamicCast(nodeBefore
->GetData(),
151 // the radio buttons in a group must be consecutive, so
152 // there are no more of them
156 btn
->SetValue(FALSE
);
158 if ( btn
->HasFlag(wxRB_GROUP
) )
160 // even if there are other radio buttons before this one,
161 // they're not in the same group with us
167 // ... and also turn off all buttons after this one
168 for ( wxWindowList::compatibility_iterator nodeAfter
= nodeThis
->GetNext();
170 nodeAfter
= nodeAfter
->GetNext() )
172 wxRadioButton
*btn
= wxDynamicCast(nodeAfter
->GetData(),
175 if ( !btn
|| btn
->HasFlag(wxRB_GROUP
) )
177 // no more buttons or the first button of the next group
181 btn
->SetValue(FALSE
);
186 bool wxRadioButton::GetValue() const
188 // NB: this will also return TRUE for BST_INDETERMINATE value if we ever
189 // have 3-state radio buttons
190 return ::SendMessage(GetHwnd(), BM_GETCHECK
, 0, 0L) != 0;
193 // ----------------------------------------------------------------------------
194 // wxRadioButton event processing
195 // ----------------------------------------------------------------------------
197 void wxRadioButton::Command (wxCommandEvent
& event
)
199 SetValue(event
.m_commandInt
!= 0);
200 ProcessCommand(event
);
203 void wxRadioButton::SetFocus()
205 // when the radio button receives a WM_SETFOCUS message it generates a
206 // BN_CLICKED which is totally unexpected and leads to catastrophic results
207 // if you pop up a dialog from the radio button event handler as, when the
208 // dialog is dismissed, the focus is returned to the radio button which
209 // generates BN_CLICKED which leads to showing another dialog and so on
212 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
213 // button gains focus
214 m_focusJustSet
= TRUE
;
216 wxControl::SetFocus();
219 bool wxRadioButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
221 if ( param
!= BN_CLICKED
)
224 if ( m_focusJustSet
)
226 // see above: we want to ignore this event
227 m_focusJustSet
= FALSE
;
229 else // a real clicked event
231 bool isChecked
= GetValue();
233 if ( HasFlag(wxRB_SINGLE
) )
235 // when we use a "manual" radio button, we have to check the button
236 // ourselves -- but it's reset to unchecked state by the user code
237 // (presumably when another button is pressed)
242 wxCommandEvent
event(wxEVT_COMMAND_RADIOBUTTON_SELECTED
, GetId());
243 event
.SetEventObject( this );
244 event
.SetInt(isChecked
);
246 ProcessCommand(event
);
252 // ----------------------------------------------------------------------------
253 // wxRadioButton geometry
254 // ----------------------------------------------------------------------------
256 wxSize
wxRadioButton::DoGetBestSize() const
258 static int s_radioSize
= 0;
263 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
265 s_radioSize
= dc
.GetCharHeight();
268 wxString str
= GetLabel();
273 GetTextExtent(str
, &wRadio
, &hRadio
);
274 wRadio
+= s_radioSize
+ GetCharWidth();
276 if ( hRadio
< s_radioSize
)
277 hRadio
= s_radioSize
;
281 wRadio
= s_radioSize
;
282 hRadio
= s_radioSize
;
285 return wxSize(wRadio
, hRadio
);
288 long wxRadioButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
290 if (nMsg
== WM_SETFOCUS
)
292 m_focusJustSet
= TRUE
;
294 long ret
= wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
296 m_focusJustSet
= FALSE
;
300 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
303 #endif // wxUSE_RADIOBTN