]> git.saurik.com Git - wxWidgets.git/blame - src/msw/radiobut.cpp
use (new) safer GetTraitsIfExists() in wxMutexGuiEnter/Leave() to avoid crashing...
[wxWidgets.git] / src / msw / radiobut.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
b0b5881a 2// Name: src/msw/radiobut.cpp
2bda0e17
KB
3// Purpose: wxRadioButton
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
5f199b71
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
f607434a 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
1e6feb95
VZ
27#if wxUSE_RADIOBTN
28
b0b5881a
WS
29#include "wx/radiobut.h"
30
2bda0e17 31#ifndef WX_PRECOMP
f607434a
VZ
32 #include "wx/settings.h"
33 #include "wx/dcscreen.h"
6b81a8ea 34 #include "wx/toplevel.h"
2bda0e17
KB
35#endif
36
37#include "wx/msw/private.h"
38
5f199b71
VZ
39// ============================================================================
40// wxRadioButton implementation
41// ============================================================================
42
43// ----------------------------------------------------------------------------
44// wxRadioButton creation
45// ----------------------------------------------------------------------------
46
2bda0e17 47
51741307 48#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
49WX_DEFINE_FLAGS( wxRadioButtonStyle )
50
3ff066a4 51wxBEGIN_FLAGS( wxRadioButtonStyle )
bc9fb572
JS
52 // new style border flags, we put them first to
53 // use them for streaming out
3ff066a4
SC
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)
4d08943e 60
bc9fb572 61 // old style border flags
3ff066a4
SC
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)
cb0afb26 67 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
68
69 // standard window styles
3ff066a4
SC
70 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
71 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
72 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
73 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 74 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
75 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
76 wxFLAGS_MEMBER(wxVSCROLL)
77 wxFLAGS_MEMBER(wxHSCROLL)
bc9fb572 78
3ff066a4 79 wxFLAGS_MEMBER(wxRB_GROUP)
bc9fb572 80
3ff066a4 81wxEND_FLAGS( wxRadioButtonStyle )
bc9fb572 82
51741307
SC
83IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
84
3ff066a4 85wxBEGIN_PROPERTIES_TABLE(wxRadioButton)
f607434a 86 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
af498247 87 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
f607434a 88 wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
af498247
VZ
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
3ff066a4 91wxEND_PROPERTIES_TABLE()
51741307 92
3ff066a4
SC
93wxBEGIN_HANDLERS_TABLE(wxRadioButton)
94wxEND_HANDLERS_TABLE()
51741307 95
4d08943e 96wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
51741307
SC
97
98#else
99IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
100#endif
066f1b7a 101
066f1b7a 102
5f199b71 103void wxRadioButton::Init()
621793f4 104{
f607434a 105 m_isChecked = false;
621793f4
JS
106}
107
5f199b71
VZ
108bool 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)
2bda0e17 116{
5f199b71 117 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
f607434a 118 return false;
2bda0e17 119
2b5f62a0
VZ
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.
2bda0e17 130
2b5f62a0
VZ
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;
2bda0e17 136
5f199b71 137 if ( HasFlag(wxCLIP_SIBLINGS) )
b0766406 138 msStyle |= WS_CLIPSIBLINGS;
93212fee
VZ
139 if ( HasFlag(wxALIGN_RIGHT) )
140 msStyle |= BS_LEFTTEXT | BS_RIGHT;
b0766406 141
5f199b71 142 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
f607434a 143 return false;
b0766406 144
5f199b71
VZ
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) )
f607434a 150 SetValue(true);
c085e333 151
f607434a 152 return true;
2bda0e17
KB
153}
154
5f199b71
VZ
155// ----------------------------------------------------------------------------
156// wxRadioButton functions
157// ----------------------------------------------------------------------------
2bda0e17 158
5f199b71 159void wxRadioButton::SetValue(bool value)
2bda0e17 160{
48fbe8ee
VZ
161 ::SendMessage(GetHwnd(), BM_SETCHECK,
162 value ? BST_CHECKED : BST_UNCHECKED, 0);
2b5f62a0 163
f607434a
VZ
164 m_isChecked = value;
165
48fbe8ee
VZ
166 if ( !value )
167 return;
168
2b5f62a0
VZ
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
48fbe8ee
VZ
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) )
2b5f62a0 197 {
48fbe8ee
VZ
198 // ... turn off all radio buttons before it
199 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
200 nodeBefore;
201 nodeBefore = nodeBefore->GetPrevious() )
2b5f62a0 202 {
48fbe8ee
VZ
203 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
204 wxRadioButton);
205 if ( !btn )
2b5f62a0 206 {
48fbe8ee
VZ
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) )
2b17e391 213 {
c5b3143a 214 // A wxRB_SINGLE button isn't part of this group
2b17e391
VZ
215 break;
216 }
b0b5881a 217
48fbe8ee
VZ
218 if ( btn == focus )
219 shouldSetFocus = true;
220 else if ( btn == focusInTLW )
221 shouldSetTLWFocus = true;
c5b3143a 222
48fbe8ee 223 btn->SetValue(false);
2b5f62a0 224
48fbe8ee 225 if ( btn->HasFlag(wxRB_GROUP) )
2b5f62a0 226 {
48fbe8ee
VZ
227 // even if there are other radio buttons before this one,
228 // they're not in the same group with us
2b5f62a0
VZ
229 break;
230 }
48fbe8ee
VZ
231 }
232 }
2b5f62a0 233
48fbe8ee
VZ
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);
f469584d 241
48fbe8ee
VZ
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;
2b5f62a0 249 }
48fbe8ee
VZ
250
251 if ( btn == focus )
252 shouldSetFocus = true;
253 else if ( btn == focusInTLW )
254 shouldSetTLWFocus = true;
255
256 btn->SetValue(false);
2b5f62a0 257 }
48fbe8ee
VZ
258
259 if ( shouldSetFocus )
260 SetFocus();
261 else if ( shouldSetTLWFocus )
262 tlw->SetLastFocus(this);
2bda0e17
KB
263}
264
5f199b71 265bool wxRadioButton::GetValue() const
2bda0e17 266{
f607434a
VZ
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;
2bda0e17
KB
272}
273
5f199b71
VZ
274// ----------------------------------------------------------------------------
275// wxRadioButton event processing
276// ----------------------------------------------------------------------------
277
278void wxRadioButton::Command (wxCommandEvent& event)
2bda0e17 279{
687706f5 280 SetValue(event.GetInt() != 0);
5f199b71 281 ProcessCommand(event);
2bda0e17
KB
282}
283
5f199b71 284bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
f6bcfd97 285{
5f199b71 286 if ( param != BN_CLICKED )
f607434a 287 return false;
5f199b71 288
f607434a 289 if ( !m_isChecked )
f6bcfd97 290 {
f607434a
VZ
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);
2b5f62a0 295
5f199b71
VZ
296 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
297 event.SetEventObject( this );
f607434a 298 event.SetInt(true); // always checked
f6bcfd97 299
5f199b71
VZ
300 ProcessCommand(event);
301 }
f6bcfd97 302
f607434a 303 return true;
f6bcfd97 304}
2bda0e17 305
5f199b71
VZ
306// ----------------------------------------------------------------------------
307// wxRadioButton geometry
308// ----------------------------------------------------------------------------
309
310wxSize wxRadioButton::DoGetBestSize() const
2bda0e17 311{
5f199b71 312 static int s_radioSize = 0;
2bda0e17 313
5f199b71
VZ
314 if ( !s_radioSize )
315 {
316 wxScreenDC dc;
317 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
2bda0e17 318
5f199b71 319 s_radioSize = dc.GetCharHeight();
7eee3607
VZ
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__
5f199b71 327 }
2bda0e17 328
5f199b71 329 wxString str = GetLabel();
2bda0e17 330
5f199b71
VZ
331 int wRadio, hRadio;
332 if ( !str.empty() )
333 {
32cd189d 334 GetTextExtent(GetLabelText(str), &wRadio, &hRadio);
5f199b71 335 wRadio += s_radioSize + GetCharWidth();
2bda0e17 336
5f199b71
VZ
337 if ( hRadio < s_radioSize )
338 hRadio = s_radioSize;
339 }
340 else
341 {
342 wRadio = s_radioSize;
343 hRadio = s_radioSize;
344 }
2bda0e17 345
31582e4e
RD
346 wxSize best(wRadio, hRadio);
347 CacheBestSize(best);
348 return best;
2bda0e17
KB
349}
350
bec08b39
VZ
351WXDWORD 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
1e6feb95 361#endif // wxUSE_RADIOBTN