]> git.saurik.com Git - wxWidgets.git/blob - src/msw/radiobut.cpp
Implement GetValue() and SetValue() and send an event when clicked
[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/brush.h"
37 #include "wx/dcscreen.h"
38 #endif
39
40 #include "wx/msw/private.h"
41
42 // ============================================================================
43 // wxRadioButton implementation
44 // ============================================================================
45
46 // ----------------------------------------------------------------------------
47 // wxRadioButton creation
48 // ----------------------------------------------------------------------------
49
50
51 #if wxUSE_EXTENDED_RTTI
52 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
53
54 WX_BEGIN_PROPERTIES_TABLE(wxRadioButton)
55 WX_DELEGATE( OnClick , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
56 WX_PROPERTY( Font , wxFont , SetFont , GetWindowFont , )
57 WX_PROPERTY( Label,wxString, SetLabel, GetLabel, wxT("") )
58 WX_PROPERTY( Value ,bool, SetValue, GetValue, )
59 WX_END_PROPERTIES_TABLE()
60
61 WX_BEGIN_HANDLERS_TABLE(wxRadioButton)
62 WX_END_HANDLERS_TABLE()
63
64 WX_CONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
65
66 #else
67 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
68 #endif
69
70
71 void wxRadioButton::Init()
72 {
73 m_focusJustSet = FALSE;
74 }
75
76 bool wxRadioButton::Create(wxWindow *parent,
77 wxWindowID id,
78 const wxString& label,
79 const wxPoint& pos,
80 const wxSize& size,
81 long style,
82 const wxValidator& validator,
83 const wxString& name)
84 {
85 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
86 return FALSE;
87
88 long msStyle = WS_TABSTOP;
89 if ( HasFlag(wxRB_GROUP) )
90 msStyle |= WS_GROUP;
91
92 /*
93 wxRB_SINGLE is a temporary workaround for the following problem: if you
94 have 2 radiobuttons in the same group but which are not consecutive in
95 the dialog, Windows can enter an infinite loop! The simplest way to
96 reproduce it is to create radio button, then a panel and then another
97 radio button: then checking the last button hangs the app.
98
99 Ideally, we'd detect (and avoid) such situation automatically but for
100 now, as I don't know how to do it, just allow the user to create
101 BS_RADIOBUTTON buttons for such situations.
102 */
103 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
104
105 if ( HasFlag(wxCLIP_SIBLINGS) )
106 msStyle |= WS_CLIPSIBLINGS;
107
108 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
109 return FALSE;
110
111 // for compatibility with wxGTK, the first radio button in a group is
112 // always checked (this makes sense anyhow as you need to ensure that at
113 // least one button in the group is checked and this is the simlpest way to
114 // do it)
115 if ( HasFlag(wxRB_GROUP) )
116 SetValue(TRUE);
117
118 return TRUE;
119 }
120
121 // ----------------------------------------------------------------------------
122 // wxRadioButton functions
123 // ----------------------------------------------------------------------------
124
125 void wxRadioButton::SetValue(bool value)
126 {
127 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
128 // value as is (we don't use BST_XXX here as they're not defined for Win16)
129 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM)value, 0L);
130
131 // if we set the value of one radio button we also must clear all the other
132 // buttons in the same group: Windows doesn't do it automatically
133 if ( value )
134 {
135 const wxWindowList& siblings = GetParent()->GetChildren();
136 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
137 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
138
139 // if it's not the first item of the group ...
140 if ( !HasFlag(wxRB_GROUP) )
141 {
142 // ... turn off all radio buttons before it
143 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
144 nodeBefore;
145 nodeBefore = nodeBefore->GetPrevious() )
146 {
147 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
148 wxRadioButton);
149 if ( !btn )
150 {
151 // the radio buttons in a group must be consecutive, so
152 // there are no more of them
153 break;
154 }
155
156 btn->SetValue(FALSE);
157
158 if ( btn->HasFlag(wxRB_GROUP) )
159 {
160 // even if there are other radio buttons before this one,
161 // they're not in the same group with us
162 break;
163 }
164 }
165 }
166
167 // ... and also turn off all buttons after this one
168 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
169 nodeAfter;
170 nodeAfter = nodeAfter->GetNext() )
171 {
172 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
173 wxRadioButton);
174
175 if ( !btn || btn->HasFlag(wxRB_GROUP) )
176 {
177 // no more buttons or the first button of the next group
178 break;
179 }
180
181 btn->SetValue(FALSE);
182 }
183 }
184 }
185
186 bool wxRadioButton::GetValue() const
187 {
188 // NB: this will also return TRUE for BST_INDETERMINATE value if we ever
189 // have 3-state radio buttons
190 return ::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0;
191 }
192
193 // ----------------------------------------------------------------------------
194 // wxRadioButton event processing
195 // ----------------------------------------------------------------------------
196
197 void wxRadioButton::Command (wxCommandEvent& event)
198 {
199 SetValue(event.m_commandInt != 0);
200 ProcessCommand(event);
201 }
202
203 void wxRadioButton::SetFocus()
204 {
205 // when the radio button receives a WM_SETFOCUS message it generates a
206 // BN_CLICKED which is totally unexpected and leads to catastrophic results
207 // if you pop up a dialog from the radio button event handler as, when the
208 // dialog is dismissed, the focus is returned to the radio button which
209 // generates BN_CLICKED which leads to showing another dialog and so on
210 // without end!
211 //
212 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
213 // button gains focus
214 m_focusJustSet = TRUE;
215
216 wxControl::SetFocus();
217 }
218
219 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
220 {
221 if ( param != BN_CLICKED )
222 return FALSE;
223
224 if ( m_focusJustSet )
225 {
226 // see above: we want to ignore this event
227 m_focusJustSet = FALSE;
228 }
229 else // a real clicked event
230 {
231 bool isChecked = GetValue();
232
233 if ( HasFlag(wxRB_SINGLE) )
234 {
235 // when we use a "manual" radio button, we have to check the button
236 // ourselves -- but it's reset to unchecked state by the user code
237 // (presumably when another button is pressed)
238 if ( !isChecked )
239 SetValue(TRUE);
240 }
241
242 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
243 event.SetEventObject( this );
244 event.SetInt(isChecked);
245
246 ProcessCommand(event);
247 }
248
249 return TRUE;
250 }
251
252 // ----------------------------------------------------------------------------
253 // wxRadioButton geometry
254 // ----------------------------------------------------------------------------
255
256 wxSize wxRadioButton::DoGetBestSize() const
257 {
258 static int s_radioSize = 0;
259
260 if ( !s_radioSize )
261 {
262 wxScreenDC dc;
263 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
264
265 s_radioSize = dc.GetCharHeight();
266 }
267
268 wxString str = GetLabel();
269
270 int wRadio, hRadio;
271 if ( !str.empty() )
272 {
273 GetTextExtent(str, &wRadio, &hRadio);
274 wRadio += s_radioSize + GetCharWidth();
275
276 if ( hRadio < s_radioSize )
277 hRadio = s_radioSize;
278 }
279 else
280 {
281 wRadio = s_radioSize;
282 hRadio = s_radioSize;
283 }
284
285 return wxSize(wRadio, hRadio);
286 }
287
288 long wxRadioButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
289 {
290 if (nMsg == WM_SETFOCUS)
291 {
292 m_focusJustSet = TRUE;
293
294 long ret = wxControl::MSWWindowProc(nMsg, wParam, lParam);
295
296 m_focusJustSet = FALSE;
297
298 return ret;
299 }
300 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
301 }
302
303 #endif // wxUSE_RADIOBTN