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"
34 #include "wx/toplevel.h"
37 #include "wx/msw/private.h"
39 // ============================================================================
40 // wxRadioButton implementation
41 // ============================================================================
43 // ----------------------------------------------------------------------------
44 // wxRadioButton creation
45 // ----------------------------------------------------------------------------
48 #if wxUSE_EXTENDED_RTTI
49 WX_DEFINE_FLAGS( wxRadioButtonStyle
)
51 wxBEGIN_FLAGS( wxRadioButtonStyle
)
52 // new style border flags, we put them first to
53 // use them for streaming out
54 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
55 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
56 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
57 wxFLAGS_MEMBER(wxBORDER_RAISED
)
58 wxFLAGS_MEMBER(wxBORDER_STATIC
)
59 wxFLAGS_MEMBER(wxBORDER_NONE
)
61 // old style border flags
62 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
63 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
64 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
65 wxFLAGS_MEMBER(wxRAISED_BORDER
)
66 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
67 wxFLAGS_MEMBER(wxBORDER
)
69 // standard window styles
70 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
71 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
72 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
73 wxFLAGS_MEMBER(wxWANTS_CHARS
)
74 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
75 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
76 wxFLAGS_MEMBER(wxVSCROLL
)
77 wxFLAGS_MEMBER(wxHSCROLL
)
79 wxFLAGS_MEMBER(wxRB_GROUP
)
81 wxEND_FLAGS( wxRadioButtonStyle
)
83 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton
, wxControl
,"wx/radiobut.h")
85 wxBEGIN_PROPERTIES_TABLE(wxRadioButton
)
86 wxEVENT_PROPERTY( Click
, wxEVT_COMMAND_RADIOBUTTON_SELECTED
, wxCommandEvent
)
87 wxPROPERTY( Font
, wxFont
, SetFont
, GetFont
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
88 wxPROPERTY( Label
,wxString
, SetLabel
, GetLabel
, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
89 wxPROPERTY( Value
,bool, SetValue
, GetValue
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
90 wxPROPERTY_FLAGS( WindowStyle
, wxRadioButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
91 wxEND_PROPERTIES_TABLE()
93 wxBEGIN_HANDLERS_TABLE(wxRadioButton
)
94 wxEND_HANDLERS_TABLE()
96 wxCONSTRUCTOR_6( wxRadioButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxString
, Label
, wxPoint
, Position
, wxSize
, Size
, long , WindowStyle
)
99 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton
, wxControl
)
103 void wxRadioButton::Init()
108 bool wxRadioButton::Create(wxWindow
*parent
,
110 const wxString
& label
,
114 const wxValidator
& validator
,
115 const wxString
& name
)
117 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
120 long msStyle
= WS_TABSTOP
;
121 if ( HasFlag(wxRB_GROUP
) )
125 wxRB_SINGLE is a temporary workaround for the following problem: if you
126 have 2 radiobuttons in the same group but which are not consecutive in
127 the dialog, Windows can enter an infinite loop! The simplest way to
128 reproduce it is to create radio button, then a panel and then another
129 radio button: then checking the last button hangs the app.
131 Ideally, we'd detect (and avoid) such situation automatically but for
132 now, as I don't know how to do it, just allow the user to create
133 BS_RADIOBUTTON buttons for such situations.
135 msStyle
|= HasFlag(wxRB_SINGLE
) ? BS_RADIOBUTTON
: BS_AUTORADIOBUTTON
;
137 if ( HasFlag(wxCLIP_SIBLINGS
) )
138 msStyle
|= WS_CLIPSIBLINGS
;
139 if ( HasFlag(wxALIGN_RIGHT
) )
140 msStyle
|= BS_LEFTTEXT
| BS_RIGHT
;
142 if ( !MSWCreateControl(_T("BUTTON"), msStyle
, pos
, size
, label
, 0) )
145 // for compatibility with wxGTK, the first radio button in a group is
146 // always checked (this makes sense anyhow as you need to ensure that at
147 // least one button in the group is checked and this is the simlpest way to
149 if ( HasFlag(wxRB_GROUP
) )
155 // ----------------------------------------------------------------------------
156 // wxRadioButton functions
157 // ----------------------------------------------------------------------------
159 void wxRadioButton::SetValue(bool value
)
161 ::SendMessage(GetHwnd(), BM_SETCHECK
,
162 value
? BST_CHECKED
: BST_UNCHECKED
, 0);
169 // if we set the value of one radio button we also must clear all the other
170 // buttons in the same group: Windows doesn't do it automatically
172 // moreover, if another radiobutton in the group currently has the focus,
173 // we have to set it to this radiobutton, else the old readiobutton will be
174 // reselected automatically, if a parent window loses the focus and regains
176 wxWindow
* const focus
= FindFocus();
177 wxTopLevelWindow
* const
178 tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
179 wxCHECK_RET( tlw
, _T("radio button outside of TLW?") );
180 wxWindow
* const focusInTLW
= tlw
->GetLastFocus();
182 const wxWindowList
& siblings
= GetParent()->GetChildren();
183 wxWindowList::compatibility_iterator nodeThis
= siblings
.Find(this);
184 wxCHECK_RET( nodeThis
, _T("radio button not a child of its parent?") );
186 // this will be set to true in the code below if the focus is in our TLW
187 // and belongs to one of the other buttons in the same group
188 bool shouldSetFocus
= false;
190 // this will be set to true if the focus is outside of our TLW currently
191 // but the remembered focus of this TLW is one of the other buttons in the
193 bool shouldSetTLWFocus
= false;
195 // if it's not the first item of the group ...
196 if ( !HasFlag(wxRB_GROUP
) )
198 // ... turn off all radio buttons before it
199 for ( wxWindowList::compatibility_iterator nodeBefore
= nodeThis
->GetPrevious();
201 nodeBefore
= nodeBefore
->GetPrevious() )
203 wxRadioButton
*btn
= wxDynamicCast(nodeBefore
->GetData(),
207 // don't stop on non radio buttons, we could have intermixed
208 // buttons and e.g. static labels
212 if ( btn
->HasFlag(wxRB_SINGLE
) )
214 // A wxRB_SINGLE button isn't part of this group
219 shouldSetFocus
= true;
220 else if ( btn
== focusInTLW
)
221 shouldSetTLWFocus
= true;
223 btn
->SetValue(false);
225 if ( btn
->HasFlag(wxRB_GROUP
) )
227 // even if there are other radio buttons before this one,
228 // they're not in the same group with us
234 // ... and also turn off all buttons after this one
235 for ( wxWindowList::compatibility_iterator nodeAfter
= nodeThis
->GetNext();
237 nodeAfter
= nodeAfter
->GetNext() )
239 wxRadioButton
*btn
= wxDynamicCast(nodeAfter
->GetData(),
245 if ( btn
->HasFlag(wxRB_GROUP
| wxRB_SINGLE
) )
247 // no more buttons or the first button of the next group
252 shouldSetFocus
= true;
253 else if ( btn
== focusInTLW
)
254 shouldSetTLWFocus
= true;
256 btn
->SetValue(false);
259 if ( shouldSetFocus
)
261 else if ( shouldSetTLWFocus
)
262 tlw
->SetLastFocus(this);
265 bool wxRadioButton::GetValue() const
267 wxASSERT_MSG( m_isChecked
==
268 (::SendMessage(GetHwnd(), BM_GETCHECK
, 0, 0L) != 0),
269 _T("wxRadioButton::m_isChecked is out of sync?") );
274 // ----------------------------------------------------------------------------
275 // wxRadioButton event processing
276 // ----------------------------------------------------------------------------
278 void wxRadioButton::Command (wxCommandEvent
& event
)
280 SetValue(event
.GetInt() != 0);
281 ProcessCommand(event
);
284 bool wxRadioButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
286 if ( param
!= BN_CLICKED
)
291 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
292 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
293 // receive focus the button gets BN_CLICKED but stays unchecked!)
296 wxCommandEvent
event(wxEVT_COMMAND_RADIOBUTTON_SELECTED
, GetId());
297 event
.SetEventObject( this );
298 event
.SetInt(true); // always checked
300 ProcessCommand(event
);
306 // ----------------------------------------------------------------------------
307 // wxRadioButton geometry
308 // ----------------------------------------------------------------------------
310 wxSize
wxRadioButton::DoGetBestSize() const
312 static int s_radioSize
= 0;
317 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
319 s_radioSize
= dc
.GetCharHeight();
321 // radio button bitmap size under CE is bigger than the font height,
322 // adding just one pixel seems to work fine for the default font but it
323 // would be nice to find some better way to find the correct height
326 #endif // __WXWINCE__
329 wxString str
= GetLabel();
334 GetTextExtent(GetLabelText(str
), &wRadio
, &hRadio
);
335 wRadio
+= s_radioSize
+ GetCharWidth();
337 if ( hRadio
< s_radioSize
)
338 hRadio
= s_radioSize
;
342 wRadio
= s_radioSize
;
343 hRadio
= s_radioSize
;
346 wxSize
best(wRadio
, hRadio
);
351 WXDWORD
wxRadioButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
353 WXDWORD styleMSW
= wxControl::MSWGetStyle(style
, exstyle
);
355 if ( style
& wxRB_GROUP
)
356 styleMSW
|= WS_GROUP
;
361 #endif // wxUSE_RADIOBTN