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