]> git.saurik.com Git - wxWidgets.git/blob - src/msw/radiobut.cpp
fixes to radio button handling (patch 803360)
[wxWidgets.git] / src / msw / radiobut.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/radiobut.cpp
3 // Purpose: wxRadioButton
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "radiobut.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_RADIOBTN
32
33 #ifndef WX_PRECOMP
34 #include "wx/radiobut.h"
35 #include "wx/settings.h"
36 #include "wx/dcscreen.h"
37 #endif
38
39 #include "wx/msw/private.h"
40
41 // ============================================================================
42 // wxRadioButton implementation
43 // ============================================================================
44
45 // ----------------------------------------------------------------------------
46 // wxRadioButton creation
47 // ----------------------------------------------------------------------------
48
49
50 #if wxUSE_EXTENDED_RTTI
51 WX_DEFINE_FLAGS( wxRadioButtonStyle )
52
53 wxBEGIN_FLAGS( wxRadioButtonStyle )
54 // new style border flags, we put them first to
55 // use them for streaming out
56 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
57 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
58 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
59 wxFLAGS_MEMBER(wxBORDER_RAISED)
60 wxFLAGS_MEMBER(wxBORDER_STATIC)
61 wxFLAGS_MEMBER(wxBORDER_NONE)
62
63 // old style border flags
64 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
65 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
66 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
67 wxFLAGS_MEMBER(wxRAISED_BORDER)
68 wxFLAGS_MEMBER(wxSTATIC_BORDER)
69 wxFLAGS_MEMBER(wxBORDER)
70
71 // standard window styles
72 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
73 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
74 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
75 wxFLAGS_MEMBER(wxWANTS_CHARS)
76 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
77 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
78 wxFLAGS_MEMBER(wxVSCROLL)
79 wxFLAGS_MEMBER(wxHSCROLL)
80
81 wxFLAGS_MEMBER(wxRB_GROUP)
82
83 wxEND_FLAGS( wxRadioButtonStyle )
84
85 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
86
87 wxBEGIN_PROPERTIES_TABLE(wxRadioButton)
88 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
89 wxPROPERTY( Font , wxFont , SetFont , GetFont , , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
90 wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
91 wxPROPERTY( Value ,bool, SetValue, GetValue,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
92 wxPROPERTY_FLAGS( WindowStyle , wxRadioButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
93 wxEND_PROPERTIES_TABLE()
94
95 wxBEGIN_HANDLERS_TABLE(wxRadioButton)
96 wxEND_HANDLERS_TABLE()
97
98 wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
99
100 #else
101 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
102 #endif
103
104
105 void wxRadioButton::Init()
106 {
107 m_isChecked = false;
108 }
109
110 bool wxRadioButton::Create(wxWindow *parent,
111 wxWindowID id,
112 const wxString& label,
113 const wxPoint& pos,
114 const wxSize& size,
115 long style,
116 const wxValidator& validator,
117 const wxString& name)
118 {
119 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
120 return false;
121
122 long msStyle = WS_TABSTOP;
123 if ( HasFlag(wxRB_GROUP) )
124 msStyle |= WS_GROUP;
125
126 /*
127 wxRB_SINGLE is a temporary workaround for the following problem: if you
128 have 2 radiobuttons in the same group but which are not consecutive in
129 the dialog, Windows can enter an infinite loop! The simplest way to
130 reproduce it is to create radio button, then a panel and then another
131 radio button: then checking the last button hangs the app.
132
133 Ideally, we'd detect (and avoid) such situation automatically but for
134 now, as I don't know how to do it, just allow the user to create
135 BS_RADIOBUTTON buttons for such situations.
136 */
137 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
138
139 if ( HasFlag(wxCLIP_SIBLINGS) )
140 msStyle |= WS_CLIPSIBLINGS;
141
142 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
143 return false;
144
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
148 // do it)
149 if ( HasFlag(wxRB_GROUP) )
150 SetValue(true);
151
152 return true;
153 }
154
155 // ----------------------------------------------------------------------------
156 // wxRadioButton functions
157 // ----------------------------------------------------------------------------
158
159 void wxRadioButton::SetValue(bool value)
160 {
161 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
162 // value as is (we don't use BST_XXX here as they're not defined for Win16)
163 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM)value, 0L);
164
165 m_isChecked = value;
166
167 // if we set the value of one radio button we also must clear all the other
168 // buttons in the same group: Windows doesn't do it automatically
169 if ( m_isChecked )
170 {
171 const wxWindowList& siblings = GetParent()->GetChildren();
172 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
173 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
174
175 // if it's not the first item of the group ...
176 if ( !HasFlag(wxRB_GROUP) )
177 {
178 // ... turn off all radio buttons before it
179 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
180 nodeBefore;
181 nodeBefore = nodeBefore->GetPrevious() )
182 {
183 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
184 wxRadioButton);
185 if ( !btn )
186 {
187 // the radio buttons in a group must be consecutive, so
188 // there are no more of them
189 break;
190 }
191
192 btn->SetValue(false);
193
194 if ( btn->HasFlag(wxRB_GROUP) )
195 {
196 // even if there are other radio buttons before this one,
197 // they're not in the same group with us
198 break;
199 }
200 }
201 }
202
203 // ... and also turn off all buttons after this one
204 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
205 nodeAfter;
206 nodeAfter = nodeAfter->GetNext() )
207 {
208 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
209 wxRadioButton);
210
211 if ( !btn || btn->HasFlag(wxRB_GROUP) )
212 {
213 // no more buttons or the first button of the next group
214 break;
215 }
216
217 btn->SetValue(false);
218 }
219 }
220 }
221
222 bool wxRadioButton::GetValue() const
223 {
224 wxASSERT_MSG( m_isChecked ==
225 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
226 _T("wxRadioButton::m_isChecked is out of sync?") );
227
228 return m_isChecked;
229 }
230
231 // ----------------------------------------------------------------------------
232 // wxRadioButton event processing
233 // ----------------------------------------------------------------------------
234
235 void wxRadioButton::Command (wxCommandEvent& event)
236 {
237 SetValue(event.m_commandInt != 0);
238 ProcessCommand(event);
239 }
240
241 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
242 {
243 if ( param != BN_CLICKED )
244 return false;
245
246 if ( !m_isChecked )
247 {
248 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
249 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
250 // receive focus the button gets BN_CLICKED but stays unchecked!)
251 SetValue(true);
252
253 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
254 event.SetEventObject( this );
255 event.SetInt(true); // always checked
256
257 ProcessCommand(event);
258 }
259
260 return true;
261 }
262
263 // ----------------------------------------------------------------------------
264 // wxRadioButton geometry
265 // ----------------------------------------------------------------------------
266
267 wxSize wxRadioButton::DoGetBestSize() const
268 {
269 static int s_radioSize = 0;
270
271 if ( !s_radioSize )
272 {
273 wxScreenDC dc;
274 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
275
276 s_radioSize = dc.GetCharHeight();
277 }
278
279 wxString str = GetLabel();
280
281 int wRadio, hRadio;
282 if ( !str.empty() )
283 {
284 GetTextExtent(str, &wRadio, &hRadio);
285 wRadio += s_radioSize + GetCharWidth();
286
287 if ( hRadio < s_radioSize )
288 hRadio = s_radioSize;
289 }
290 else
291 {
292 wRadio = s_radioSize;
293 hRadio = s_radioSize;
294 }
295
296 return wxSize(wRadio, hRadio);
297 }
298
299 #endif // wxUSE_RADIOBTN
300