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