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 WX_DEFINE_FLAGS( wxRadioButtonStyle
)
54 WX_BEGIN_FLAGS( wxRadioButtonStyle
)
55 // new style border flags, we put them first to
56 // use them for streaming out
57 WX_FLAGS_MEMBER(wxBORDER_SIMPLE
)
58 WX_FLAGS_MEMBER(wxBORDER_SUNKEN
)
59 WX_FLAGS_MEMBER(wxBORDER_DOUBLE
)
60 WX_FLAGS_MEMBER(wxBORDER_RAISED
)
61 WX_FLAGS_MEMBER(wxBORDER_STATIC
)
62 WX_FLAGS_MEMBER(wxBORDER_NONE
)
64 // old style border flags
65 WX_FLAGS_MEMBER(wxSIMPLE_BORDER
)
66 WX_FLAGS_MEMBER(wxSUNKEN_BORDER
)
67 WX_FLAGS_MEMBER(wxDOUBLE_BORDER
)
68 WX_FLAGS_MEMBER(wxRAISED_BORDER
)
69 WX_FLAGS_MEMBER(wxSTATIC_BORDER
)
70 WX_FLAGS_MEMBER(wxNO_BORDER
)
72 // standard window styles
73 WX_FLAGS_MEMBER(wxTAB_TRAVERSAL
)
74 WX_FLAGS_MEMBER(wxCLIP_CHILDREN
)
75 WX_FLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
76 WX_FLAGS_MEMBER(wxWANTS_CHARS
)
77 WX_FLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE
)
78 WX_FLAGS_MEMBER(wxALWAYS_SHOW_SB
)
79 WX_FLAGS_MEMBER(wxVSCROLL
)
80 WX_FLAGS_MEMBER(wxHSCROLL
)
82 WX_FLAGS_MEMBER(wxRB_GROUP
)
84 WX_END_FLAGS( wxRadioButtonStyle
)
86 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton
, wxControl
,"wx/radiobut.h")
88 WX_BEGIN_PROPERTIES_TABLE(wxRadioButton
)
89 WX_EVENT_PROPERTY( Click
, wxEVT_COMMAND_RADIOBUTTON_SELECTED
, wxCommandEvent
)
90 WX_PROPERTY( Font
, wxFont
, SetFont
, GetFont
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
91 WX_PROPERTY( Label
,wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
92 WX_PROPERTY( Value
,bool, SetValue
, GetValue
,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
93 WX_PROPERTY_FLAGS( WindowStyle
, wxRadioButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
94 WX_END_PROPERTIES_TABLE()
96 WX_BEGIN_HANDLERS_TABLE(wxRadioButton
)
97 WX_END_HANDLERS_TABLE()
99 WX_CONSTRUCTOR_6( wxRadioButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
102 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton
, wxControl
)
106 void wxRadioButton::Init()
108 m_focusJustSet
= FALSE
;
111 bool wxRadioButton::Create(wxWindow
*parent
,
113 const wxString
& label
,
117 const wxValidator
& validator
,
118 const wxString
& name
)
120 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
123 long msStyle
= WS_TABSTOP
;
124 if ( HasFlag(wxRB_GROUP
) )
128 wxRB_SINGLE is a temporary workaround for the following problem: if you
129 have 2 radiobuttons in the same group but which are not consecutive in
130 the dialog, Windows can enter an infinite loop! The simplest way to
131 reproduce it is to create radio button, then a panel and then another
132 radio button: then checking the last button hangs the app.
134 Ideally, we'd detect (and avoid) such situation automatically but for
135 now, as I don't know how to do it, just allow the user to create
136 BS_RADIOBUTTON buttons for such situations.
138 msStyle
|= HasFlag(wxRB_SINGLE
) ? BS_RADIOBUTTON
: BS_AUTORADIOBUTTON
;
140 if ( HasFlag(wxCLIP_SIBLINGS
) )
141 msStyle
|= WS_CLIPSIBLINGS
;
143 if ( !MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, 0) )
146 // for compatibility with wxGTK, the first radio button in a group is
147 // always checked (this makes sense anyhow as you need to ensure that at
148 // least one button in the group is checked and this is the simlpest way to
150 if ( HasFlag(wxRB_GROUP
) )
156 // ----------------------------------------------------------------------------
157 // wxRadioButton functions
158 // ----------------------------------------------------------------------------
160 void wxRadioButton::SetValue(bool value
)
162 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
163 // value as is (we don't use BST_XXX here as they're not defined for Win16)
164 (void)::SendMessage(GetHwnd(), BM_SETCHECK
, (WPARAM
)value
, 0L);
166 // if we set the value of one radio button we also must clear all the other
167 // buttons in the same group: Windows doesn't do it automatically
170 const wxWindowList
& siblings
= GetParent()->GetChildren();
171 wxWindowList::compatibility_iterator nodeThis
= siblings
.Find(this);
172 wxCHECK_RET( nodeThis
, _T("radio button not a child of its parent?") );
174 // if it's not the first item of the group ...
175 if ( !HasFlag(wxRB_GROUP
) )
177 // ... turn off all radio buttons before it
178 for ( wxWindowList::compatibility_iterator nodeBefore
= nodeThis
->GetPrevious();
180 nodeBefore
= nodeBefore
->GetPrevious() )
182 wxRadioButton
*btn
= wxDynamicCast(nodeBefore
->GetData(),
186 // the radio buttons in a group must be consecutive, so
187 // there are no more of them
191 btn
->SetValue(FALSE
);
193 if ( btn
->HasFlag(wxRB_GROUP
) )
195 // even if there are other radio buttons before this one,
196 // they're not in the same group with us
202 // ... and also turn off all buttons after this one
203 for ( wxWindowList::compatibility_iterator nodeAfter
= nodeThis
->GetNext();
205 nodeAfter
= nodeAfter
->GetNext() )
207 wxRadioButton
*btn
= wxDynamicCast(nodeAfter
->GetData(),
210 if ( !btn
|| btn
->HasFlag(wxRB_GROUP
) )
212 // no more buttons or the first button of the next group
216 btn
->SetValue(FALSE
);
221 bool wxRadioButton::GetValue() const
223 // NB: this will also return TRUE for BST_INDETERMINATE value if we ever
224 // have 3-state radio buttons
225 return ::SendMessage(GetHwnd(), BM_GETCHECK
, 0, 0L) != 0;
228 // ----------------------------------------------------------------------------
229 // wxRadioButton event processing
230 // ----------------------------------------------------------------------------
232 void wxRadioButton::Command (wxCommandEvent
& event
)
234 SetValue(event
.m_commandInt
!= 0);
235 ProcessCommand(event
);
238 void wxRadioButton::SetFocus()
240 // when the radio button receives a WM_SETFOCUS message it generates a
241 // BN_CLICKED which is totally unexpected and leads to catastrophic results
242 // if you pop up a dialog from the radio button event handler as, when the
243 // dialog is dismissed, the focus is returned to the radio button which
244 // generates BN_CLICKED which leads to showing another dialog and so on
247 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
248 // button gains focus
249 m_focusJustSet
= TRUE
;
251 wxControl::SetFocus();
254 bool wxRadioButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
256 if ( param
!= BN_CLICKED
)
259 if ( m_focusJustSet
)
261 // see above: we want to ignore this event
262 m_focusJustSet
= FALSE
;
264 else // a real clicked event
266 bool isChecked
= GetValue();
268 if ( HasFlag(wxRB_SINGLE
) )
270 // when we use a "manual" radio button, we have to check the button
271 // ourselves -- but it's reset to unchecked state by the user code
272 // (presumably when another button is pressed)
277 wxCommandEvent
event(wxEVT_COMMAND_RADIOBUTTON_SELECTED
, GetId());
278 event
.SetEventObject( this );
279 event
.SetInt(isChecked
);
281 ProcessCommand(event
);
287 // ----------------------------------------------------------------------------
288 // wxRadioButton geometry
289 // ----------------------------------------------------------------------------
291 wxSize
wxRadioButton::DoGetBestSize() const
293 static int s_radioSize
= 0;
298 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
300 s_radioSize
= dc
.GetCharHeight();
303 wxString str
= GetLabel();
308 GetTextExtent(str
, &wRadio
, &hRadio
);
309 wRadio
+= s_radioSize
+ GetCharWidth();
311 if ( hRadio
< s_radioSize
)
312 hRadio
= s_radioSize
;
316 wRadio
= s_radioSize
;
317 hRadio
= s_radioSize
;
320 return wxSize(wRadio
, hRadio
);
323 long wxRadioButton::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
)
325 if (nMsg
== WM_SETFOCUS
)
327 m_focusJustSet
= TRUE
;
329 long ret
= wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
331 m_focusJustSet
= FALSE
;
335 return wxControl::MSWWindowProc(nMsg
, wParam
, lParam
);
338 #endif // wxUSE_RADIOBTN