don't use BS_AUTORADIOBUTTON at all, BS_RADIOBUTTON works just as well and doesn...
[wxWidgets.git] / src / msw / radiobut.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_RADIOBTN
28
29 #include "wx/radiobut.h"
30
31 #ifndef WX_PRECOMP
32 #include "wx/settings.h"
33 #include "wx/dcscreen.h"
34 #include "wx/toplevel.h"
35 #endif
36
37 #include "wx/msw/private.h"
38
39 // ============================================================================
40 // wxRadioButton implementation
41 // ============================================================================
42
43 // ----------------------------------------------------------------------------
44 // wxRadioButton creation
45 // ----------------------------------------------------------------------------
46
47
48 #if wxUSE_EXTENDED_RTTI
49 WX_DEFINE_FLAGS( wxRadioButtonStyle )
50
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)
60
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)
68
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)
78
79 wxFLAGS_MEMBER(wxRB_GROUP)
80
81 wxEND_FLAGS( wxRadioButtonStyle )
82
83 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
84
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()
92
93 wxBEGIN_HANDLERS_TABLE(wxRadioButton)
94 wxEND_HANDLERS_TABLE()
95
96 wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
97
98 #else
99 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
100 #endif
101
102
103 void wxRadioButton::Init()
104 {
105 m_isChecked = false;
106 }
107
108 bool wxRadioButton::Create(wxWindow *parent,
109 wxWindowID id,
110 const wxString& label,
111 const wxPoint& pos,
112 const wxSize& size,
113 long style,
114 const wxValidator& validator,
115 const wxString& name)
116 {
117 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
118 return false;
119
120 long msStyle = WS_TABSTOP;
121 if ( HasFlag(wxRB_GROUP) )
122 msStyle |= WS_GROUP;
123
124 // we use BS_RADIOBUTTON and not BS_AUTORADIOBUTTON because the use of the
125 // latter can easily result in the application entering an infinite loop
126 // inside IsDialogMessage()
127 //
128 // we used to use BS_RADIOBUTTON only for wxRB_SINGLE buttons but there
129 // doesn't seem to be any harm to always use it and it prevents some hangs,
130 // see #9786
131 msStyle |= BS_RADIOBUTTON;
132
133 if ( HasFlag(wxCLIP_SIBLINGS) )
134 msStyle |= WS_CLIPSIBLINGS;
135 if ( HasFlag(wxALIGN_RIGHT) )
136 msStyle |= BS_LEFTTEXT | BS_RIGHT;
137
138 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
139 return false;
140
141 // for compatibility with wxGTK, the first radio button in a group is
142 // always checked (this makes sense anyhow as you need to ensure that at
143 // least one button in the group is checked and this is the simplest way to
144 // do it)
145 if ( HasFlag(wxRB_GROUP) )
146 SetValue(true);
147
148 return true;
149 }
150
151 // ----------------------------------------------------------------------------
152 // wxRadioButton functions
153 // ----------------------------------------------------------------------------
154
155 void wxRadioButton::SetValue(bool value)
156 {
157 ::SendMessage(GetHwnd(), BM_SETCHECK,
158 value ? BST_CHECKED : BST_UNCHECKED, 0);
159
160 m_isChecked = value;
161
162 if ( !value )
163 return;
164
165 // if we set the value of one radio button we also must clear all the other
166 // buttons in the same group: Windows doesn't do it automatically
167 //
168 // moreover, if another radiobutton in the group currently has the focus,
169 // we have to set it to this radiobutton, else the old radiobutton will be
170 // reselected automatically, if a parent window loses the focus and regains
171 // it.
172 wxWindow * const focus = FindFocus();
173 wxTopLevelWindow * const
174 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
175 wxCHECK_RET( tlw, _T("radio button outside of TLW?") );
176 wxWindow * const focusInTLW = tlw->GetLastFocus();
177
178 const wxWindowList& siblings = GetParent()->GetChildren();
179 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
180 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
181
182 // this will be set to true in the code below if the focus is in our TLW
183 // and belongs to one of the other buttons in the same group
184 bool shouldSetFocus = false;
185
186 // this will be set to true if the focus is outside of our TLW currently
187 // but the remembered focus of this TLW is one of the other buttons in the
188 // same group
189 bool shouldSetTLWFocus = false;
190
191 // if it's not the first item of the group ...
192 if ( !HasFlag(wxRB_GROUP) )
193 {
194 // ... turn off all radio buttons before it
195 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
196 nodeBefore;
197 nodeBefore = nodeBefore->GetPrevious() )
198 {
199 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
200 wxRadioButton);
201 if ( !btn )
202 {
203 // don't stop on non radio buttons, we could have intermixed
204 // buttons and e.g. static labels
205 continue;
206 }
207
208 if ( btn->HasFlag(wxRB_SINGLE) )
209 {
210 // A wxRB_SINGLE button isn't part of this group
211 break;
212 }
213
214 if ( btn == focus )
215 shouldSetFocus = true;
216 else if ( btn == focusInTLW )
217 shouldSetTLWFocus = true;
218
219 btn->SetValue(false);
220
221 if ( btn->HasFlag(wxRB_GROUP) )
222 {
223 // even if there are other radio buttons before this one,
224 // they're not in the same group with us
225 break;
226 }
227 }
228 }
229
230 // ... and also turn off all buttons after this one
231 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
232 nodeAfter;
233 nodeAfter = nodeAfter->GetNext() )
234 {
235 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
236 wxRadioButton);
237
238 if ( !btn )
239 continue;
240
241 if ( btn->HasFlag(wxRB_GROUP | wxRB_SINGLE) )
242 {
243 // no more buttons or the first button of the next group
244 break;
245 }
246
247 if ( btn == focus )
248 shouldSetFocus = true;
249 else if ( btn == focusInTLW )
250 shouldSetTLWFocus = true;
251
252 btn->SetValue(false);
253 }
254
255 if ( shouldSetFocus )
256 SetFocus();
257 else if ( shouldSetTLWFocus )
258 tlw->SetLastFocus(this);
259 }
260
261 bool wxRadioButton::GetValue() const
262 {
263 wxASSERT_MSG( m_isChecked ==
264 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
265 _T("wxRadioButton::m_isChecked is out of sync?") );
266
267 return m_isChecked;
268 }
269
270 // ----------------------------------------------------------------------------
271 // wxRadioButton event processing
272 // ----------------------------------------------------------------------------
273
274 void wxRadioButton::Command (wxCommandEvent& event)
275 {
276 SetValue(event.GetInt() != 0);
277 ProcessCommand(event);
278 }
279
280 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
281 {
282 if ( param != BN_CLICKED )
283 return false;
284
285 if ( !m_isChecked )
286 {
287 // we need to manually update the button state as we use BS_RADIOBUTTON
288 // and not BS_AUTORADIOBUTTON
289 SetValue(true);
290
291 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
292 event.SetEventObject( this );
293 event.SetInt(true); // always checked
294
295 ProcessCommand(event);
296 }
297
298 return true;
299 }
300
301 // ----------------------------------------------------------------------------
302 // wxRadioButton geometry
303 // ----------------------------------------------------------------------------
304
305 wxSize wxRadioButton::DoGetBestSize() const
306 {
307 static int s_radioSize = 0;
308
309 if ( !s_radioSize )
310 {
311 wxScreenDC dc;
312 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
313
314 s_radioSize = dc.GetCharHeight();
315
316 // radio button bitmap size under CE is bigger than the font height,
317 // adding just one pixel seems to work fine for the default font but it
318 // would be nice to find some better way to find the correct height
319 #ifdef __WXWINCE__
320 s_radioSize++;
321 #endif // __WXWINCE__
322 }
323
324 wxString str = GetLabel();
325
326 int wRadio, hRadio;
327 if ( !str.empty() )
328 {
329 GetTextExtent(GetLabelText(str), &wRadio, &hRadio);
330 wRadio += s_radioSize + GetCharWidth();
331
332 if ( hRadio < s_radioSize )
333 hRadio = s_radioSize;
334 }
335 else
336 {
337 wRadio = s_radioSize;
338 hRadio = s_radioSize;
339 }
340
341 wxSize best(wRadio, hRadio);
342 CacheBestSize(best);
343 return best;
344 }
345
346 WXDWORD wxRadioButton::MSWGetStyle(long style, WXDWORD *exstyle) const
347 {
348 WXDWORD styleMSW = wxControl::MSWGetStyle(style, exstyle);
349
350 if ( style & wxRB_GROUP )
351 styleMSW |= WS_GROUP;
352
353 return styleMSW;
354 }
355
356 #endif // wxUSE_RADIOBTN