compilation fix for PCH-less build after last commit
[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 /*
125 wxRB_SINGLE is a temporary workaround for the following problem: if you
126 have 2 radiobuttons in the same group but which are not consecutive in
127 the dialog, Windows can enter an infinite loop! The simplest way to
128 reproduce it is to create radio button, then a panel and then another
129 radio button: then checking the last button hangs the app.
130
131 Ideally, we'd detect (and avoid) such situation automatically but for
132 now, as I don't know how to do it, just allow the user to create
133 BS_RADIOBUTTON buttons for such situations.
134 */
135 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
136
137 if ( HasFlag(wxCLIP_SIBLINGS) )
138 msStyle |= WS_CLIPSIBLINGS;
139 if ( HasFlag(wxALIGN_RIGHT) )
140 msStyle |= BS_LEFTTEXT | BS_RIGHT;
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 ::SendMessage(GetHwnd(), BM_SETCHECK,
162 value ? BST_CHECKED : BST_UNCHECKED, 0);
163
164 m_isChecked = value;
165
166 if ( !value )
167 return;
168
169 // if we set the value of one radio button we also must clear all the other
170 // buttons in the same group: Windows doesn't do it automatically
171 //
172 // moreover, if another radiobutton in the group currently has the focus,
173 // we have to set it to this radiobutton, else the old readiobutton will be
174 // reselected automatically, if a parent window loses the focus and regains
175 // it.
176 wxWindow * const focus = FindFocus();
177 wxTopLevelWindow * const
178 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
179 wxCHECK_RET( tlw, _T("radio button outside of TLW?") );
180 wxWindow * const focusInTLW = tlw->GetLastFocus();
181
182 const wxWindowList& siblings = GetParent()->GetChildren();
183 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
184 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
185
186 // this will be set to true in the code below if the focus is in our TLW
187 // and belongs to one of the other buttons in the same group
188 bool shouldSetFocus = false;
189
190 // this will be set to true if the focus is outside of our TLW currently
191 // but the remembered focus of this TLW is one of the other buttons in the
192 // same group
193 bool shouldSetTLWFocus = false;
194
195 // if it's not the first item of the group ...
196 if ( !HasFlag(wxRB_GROUP) )
197 {
198 // ... turn off all radio buttons before it
199 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
200 nodeBefore;
201 nodeBefore = nodeBefore->GetPrevious() )
202 {
203 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
204 wxRadioButton);
205 if ( !btn )
206 {
207 // don't stop on non radio buttons, we could have intermixed
208 // buttons and e.g. static labels
209 continue;
210 }
211
212 if ( btn->HasFlag(wxRB_SINGLE) )
213 {
214 // A wxRB_SINGLE button isn't part of this group
215 break;
216 }
217
218 if ( btn == focus )
219 shouldSetFocus = true;
220 else if ( btn == focusInTLW )
221 shouldSetTLWFocus = true;
222
223 btn->SetValue(false);
224
225 if ( btn->HasFlag(wxRB_GROUP) )
226 {
227 // even if there are other radio buttons before this one,
228 // they're not in the same group with us
229 break;
230 }
231 }
232 }
233
234 // ... and also turn off all buttons after this one
235 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
236 nodeAfter;
237 nodeAfter = nodeAfter->GetNext() )
238 {
239 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
240 wxRadioButton);
241
242 if ( !btn )
243 continue;
244
245 if ( btn->HasFlag(wxRB_GROUP | wxRB_SINGLE) )
246 {
247 // no more buttons or the first button of the next group
248 break;
249 }
250
251 if ( btn == focus )
252 shouldSetFocus = true;
253 else if ( btn == focusInTLW )
254 shouldSetTLWFocus = true;
255
256 btn->SetValue(false);
257 }
258
259 if ( shouldSetFocus )
260 SetFocus();
261 else if ( shouldSetTLWFocus )
262 tlw->SetLastFocus(this);
263 }
264
265 bool wxRadioButton::GetValue() const
266 {
267 wxASSERT_MSG( m_isChecked ==
268 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
269 _T("wxRadioButton::m_isChecked is out of sync?") );
270
271 return m_isChecked;
272 }
273
274 // ----------------------------------------------------------------------------
275 // wxRadioButton event processing
276 // ----------------------------------------------------------------------------
277
278 void wxRadioButton::Command (wxCommandEvent& event)
279 {
280 SetValue(event.GetInt() != 0);
281 ProcessCommand(event);
282 }
283
284 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
285 {
286 if ( param != BN_CLICKED )
287 return false;
288
289 if ( !m_isChecked )
290 {
291 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
292 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
293 // receive focus the button gets BN_CLICKED but stays unchecked!)
294 SetValue(true);
295
296 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
297 event.SetEventObject( this );
298 event.SetInt(true); // always checked
299
300 ProcessCommand(event);
301 }
302
303 return true;
304 }
305
306 // ----------------------------------------------------------------------------
307 // wxRadioButton geometry
308 // ----------------------------------------------------------------------------
309
310 wxSize wxRadioButton::DoGetBestSize() const
311 {
312 static int s_radioSize = 0;
313
314 if ( !s_radioSize )
315 {
316 wxScreenDC dc;
317 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
318
319 s_radioSize = dc.GetCharHeight();
320
321 // radio button bitmap size under CE is bigger than the font height,
322 // adding just one pixel seems to work fine for the default font but it
323 // would be nice to find some better way to find the correct height
324 #ifdef __WXWINCE__
325 s_radioSize++;
326 #endif // __WXWINCE__
327 }
328
329 wxString str = GetLabel();
330
331 int wRadio, hRadio;
332 if ( !str.empty() )
333 {
334 GetTextExtent(GetLabelText(str), &wRadio, &hRadio);
335 wRadio += s_radioSize + GetCharWidth();
336
337 if ( hRadio < s_radioSize )
338 hRadio = s_radioSize;
339 }
340 else
341 {
342 wRadio = s_radioSize;
343 hRadio = s_radioSize;
344 }
345
346 wxSize best(wRadio, hRadio);
347 CacheBestSize(best);
348 return best;
349 }
350
351 WXDWORD wxRadioButton::MSWGetStyle(long style, WXDWORD *exstyle) const
352 {
353 WXDWORD styleMSW = wxControl::MSWGetStyle(style, exstyle);
354
355 if ( style & wxRB_GROUP )
356 styleMSW |= WS_GROUP;
357
358 return styleMSW;
359 }
360
361 #endif // wxUSE_RADIOBTN