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 ::SendMessage(GetHwnd(), BM_SETCHECK
,
161 value
? BST_CHECKED
: BST_UNCHECKED
, 0);
168 // if we set the value of one radio button we also must clear all the other
169 // buttons in the same group: Windows doesn't do it automatically
171 // moreover, if another radiobutton in the group currently has the focus,
172 // we have to set it to this radiobutton, else the old readiobutton will be
173 // reselected automatically, if a parent window loses the focus and regains
175 wxWindow
* const focus
= FindFocus();
176 wxTopLevelWindow
* const
177 tlw
= wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow
);
178 wxCHECK_RET( tlw
, _T("radio button outside of TLW?") );
179 wxWindow
* const focusInTLW
= tlw
->GetLastFocus();
181 const wxWindowList
& siblings
= GetParent()->GetChildren();
182 wxWindowList::compatibility_iterator nodeThis
= siblings
.Find(this);
183 wxCHECK_RET( nodeThis
, _T("radio button not a child of its parent?") );
185 // this will be set to true in the code below if the focus is in our TLW
186 // and belongs to one of the other buttons in the same group
187 bool shouldSetFocus
= false;
189 // this will be set to true if the focus is outside of our TLW currently
190 // but the remembered focus of this TLW is one of the other buttons in the
192 bool shouldSetTLWFocus
= false;
194 // if it's not the first item of the group ...
195 if ( !HasFlag(wxRB_GROUP
) )
197 // ... turn off all radio buttons before it
198 for ( wxWindowList::compatibility_iterator nodeBefore
= nodeThis
->GetPrevious();
200 nodeBefore
= nodeBefore
->GetPrevious() )
202 wxRadioButton
*btn
= wxDynamicCast(nodeBefore
->GetData(),
206 // don't stop on non radio buttons, we could have intermixed
207 // buttons and e.g. static labels
211 if ( btn
->HasFlag(wxRB_SINGLE
) )
213 // A wxRB_SINGLE button isn't part of this group
218 shouldSetFocus
= true;
219 else if ( btn
== focusInTLW
)
220 shouldSetTLWFocus
= true;
222 btn
->SetValue(false);
224 if ( btn
->HasFlag(wxRB_GROUP
) )
226 // even if there are other radio buttons before this one,
227 // they're not in the same group with us
233 // ... and also turn off all buttons after this one
234 for ( wxWindowList::compatibility_iterator nodeAfter
= nodeThis
->GetNext();
236 nodeAfter
= nodeAfter
->GetNext() )
238 wxRadioButton
*btn
= wxDynamicCast(nodeAfter
->GetData(),
244 if ( btn
->HasFlag(wxRB_GROUP
| wxRB_SINGLE
) )
246 // no more buttons or the first button of the next group
251 shouldSetFocus
= true;
252 else if ( btn
== focusInTLW
)
253 shouldSetTLWFocus
= true;
255 btn
->SetValue(false);
258 if ( shouldSetFocus
)
260 else if ( shouldSetTLWFocus
)
261 tlw
->SetLastFocus(this);
264 bool wxRadioButton::GetValue() const
266 wxASSERT_MSG( m_isChecked
==
267 (::SendMessage(GetHwnd(), BM_GETCHECK
, 0, 0L) != 0),
268 _T("wxRadioButton::m_isChecked is out of sync?") );
273 // ----------------------------------------------------------------------------
274 // wxRadioButton event processing
275 // ----------------------------------------------------------------------------
277 void wxRadioButton::Command (wxCommandEvent
& event
)
279 SetValue(event
.GetInt() != 0);
280 ProcessCommand(event
);
283 bool wxRadioButton::MSWCommand(WXUINT param
, WXWORD
WXUNUSED(id
))
285 if ( param
!= BN_CLICKED
)
290 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
291 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
292 // receive focus the button gets BN_CLICKED but stays unchecked!)
295 wxCommandEvent
event(wxEVT_COMMAND_RADIOBUTTON_SELECTED
, GetId());
296 event
.SetEventObject( this );
297 event
.SetInt(true); // always checked
299 ProcessCommand(event
);
305 // ----------------------------------------------------------------------------
306 // wxRadioButton geometry
307 // ----------------------------------------------------------------------------
309 wxSize
wxRadioButton::DoGetBestSize() const
311 static int s_radioSize
= 0;
316 dc
.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
318 s_radioSize
= dc
.GetCharHeight();
320 // radio button bitmap size under CE is bigger than the font height,
321 // adding just one pixel seems to work fine for the default font but it
322 // would be nice to find some better way to find the correct height
325 #endif // __WXWINCE__
328 wxString str
= GetLabel();
333 GetTextExtent(GetLabelText(str
), &wRadio
, &hRadio
);
334 wRadio
+= s_radioSize
+ GetCharWidth();
336 if ( hRadio
< s_radioSize
)
337 hRadio
= s_radioSize
;
341 wRadio
= s_radioSize
;
342 hRadio
= s_radioSize
;
345 wxSize
best(wRadio
, hRadio
);
350 WXDWORD
wxRadioButton::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
352 WXDWORD styleMSW
= wxControl::MSWGetStyle(style
, exstyle
);
354 if ( style
& wxRB_GROUP
)
355 styleMSW
|= WS_GROUP
;
360 #endif // wxUSE_RADIOBTN