Use event modifiers and accessors rather than m_ variables directly, which are now...
[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/dcscreen.h"
37 #endif
38
39 #include "wx/msw/private.h"
40
41 // ============================================================================
42 // wxRadioButton implementation
43 // ============================================================================
44
45 // ----------------------------------------------------------------------------
46 // wxRadioButton creation
47 // ----------------------------------------------------------------------------
48
49
50 #if wxUSE_EXTENDED_RTTI
51 WX_DEFINE_FLAGS( wxRadioButtonStyle )
52
53 wxBEGIN_FLAGS( wxRadioButtonStyle )
54 // new style border flags, we put them first to
55 // use them for streaming out
56 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
57 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
58 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
59 wxFLAGS_MEMBER(wxBORDER_RAISED)
60 wxFLAGS_MEMBER(wxBORDER_STATIC)
61 wxFLAGS_MEMBER(wxBORDER_NONE)
62
63 // old style border flags
64 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
65 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
66 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
67 wxFLAGS_MEMBER(wxRAISED_BORDER)
68 wxFLAGS_MEMBER(wxSTATIC_BORDER)
69 wxFLAGS_MEMBER(wxBORDER)
70
71 // standard window styles
72 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
73 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
74 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
75 wxFLAGS_MEMBER(wxWANTS_CHARS)
76 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
77 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
78 wxFLAGS_MEMBER(wxVSCROLL)
79 wxFLAGS_MEMBER(wxHSCROLL)
80
81 wxFLAGS_MEMBER(wxRB_GROUP)
82
83 wxEND_FLAGS( wxRadioButtonStyle )
84
85 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
86
87 wxBEGIN_PROPERTIES_TABLE(wxRadioButton)
88 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
89 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
90 wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
91 wxPROPERTY( Value ,bool, SetValue, GetValue, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
92 wxPROPERTY_FLAGS( WindowStyle , wxRadioButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
93 wxEND_PROPERTIES_TABLE()
94
95 wxBEGIN_HANDLERS_TABLE(wxRadioButton)
96 wxEND_HANDLERS_TABLE()
97
98 wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
99
100 #else
101 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
102 #endif
103
104
105 void wxRadioButton::Init()
106 {
107 m_isChecked = false;
108 }
109
110 bool wxRadioButton::Create(wxWindow *parent,
111 wxWindowID id,
112 const wxString& label,
113 const wxPoint& pos,
114 const wxSize& size,
115 long style,
116 const wxValidator& validator,
117 const wxString& name)
118 {
119 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
120 return false;
121
122 long msStyle = WS_TABSTOP;
123 if ( HasFlag(wxRB_GROUP) )
124 msStyle |= WS_GROUP;
125
126 /*
127 wxRB_SINGLE is a temporary workaround for the following problem: if you
128 have 2 radiobuttons in the same group but which are not consecutive in
129 the dialog, Windows can enter an infinite loop! The simplest way to
130 reproduce it is to create radio button, then a panel and then another
131 radio button: then checking the last button hangs the app.
132
133 Ideally, we'd detect (and avoid) such situation automatically but for
134 now, as I don't know how to do it, just allow the user to create
135 BS_RADIOBUTTON buttons for such situations.
136 */
137 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
138
139 if ( HasFlag(wxCLIP_SIBLINGS) )
140 msStyle |= WS_CLIPSIBLINGS;
141 if ( HasFlag(wxALIGN_RIGHT) )
142 msStyle |= BS_LEFTTEXT | BS_RIGHT;
143
144 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
145 return false;
146
147 // for compatibility with wxGTK, the first radio button in a group is
148 // always checked (this makes sense anyhow as you need to ensure that at
149 // least one button in the group is checked and this is the simlpest way to
150 // do it)
151 if ( HasFlag(wxRB_GROUP) )
152 SetValue(true);
153
154 return true;
155 }
156
157 // ----------------------------------------------------------------------------
158 // wxRadioButton functions
159 // ----------------------------------------------------------------------------
160
161 void wxRadioButton::SetValue(bool value)
162 {
163 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (value?BST_CHECKED:BST_UNCHECKED), 0L);
164
165 m_isChecked = value;
166
167 // if we set the value of one radio button we also must clear all the other
168 // buttons in the same group: Windows doesn't do it automatically
169 if ( m_isChecked )
170 {
171 const wxWindowList& siblings = GetParent()->GetChildren();
172 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
173 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
174
175 // if it's not the first item of the group ...
176 if ( !HasFlag(wxRB_GROUP) )
177 {
178 // ... turn off all radio buttons before it
179 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
180 nodeBefore;
181 nodeBefore = nodeBefore->GetPrevious() )
182 {
183 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
184 wxRadioButton);
185 if ( btn && btn->HasFlag(wxRB_SINGLE) )
186 {
187 // A wxRB_SINGLE button isn't part of this group
188 break;
189 }
190
191 if (btn)
192 {
193 btn->SetValue(false);
194
195 if ( btn->HasFlag(wxRB_GROUP) )
196 {
197 // even if there are other radio buttons before this one,
198 // they're not in the same group with us
199 break;
200 }
201 }
202 }
203 }
204
205 // ... and also turn off all buttons after this one
206 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
207 nodeAfter;
208 nodeAfter = nodeAfter->GetNext() )
209 {
210 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
211 wxRadioButton);
212
213 if ( btn && (btn->HasFlag(wxRB_GROUP) || btn->HasFlag(wxRB_SINGLE) ) )
214 {
215 // no more buttons or the first button of the next group
216 break;
217 }
218
219 if (btn)
220 btn->SetValue(false);
221 }
222 }
223 }
224
225 bool wxRadioButton::GetValue() const
226 {
227 wxASSERT_MSG( m_isChecked ==
228 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
229 _T("wxRadioButton::m_isChecked is out of sync?") );
230
231 return m_isChecked;
232 }
233
234 // ----------------------------------------------------------------------------
235 // wxRadioButton event processing
236 // ----------------------------------------------------------------------------
237
238 void wxRadioButton::Command (wxCommandEvent& event)
239 {
240 SetValue(event.GetInt() != 0);
241 ProcessCommand(event);
242 }
243
244 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
245 {
246 if ( param != BN_CLICKED )
247 return false;
248
249 if ( !m_isChecked )
250 {
251 // we have to do this for BS_RADIOBUTTON anyhow and, strangely enough,
252 // sometimes this is needed even for BS_AUTORADIOBUTTON (when we
253 // receive focus the button gets BN_CLICKED but stays unchecked!)
254 SetValue(true);
255
256 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
257 event.SetEventObject( this );
258 event.SetInt(true); // always checked
259
260 ProcessCommand(event);
261 }
262
263 return true;
264 }
265
266 // ----------------------------------------------------------------------------
267 // wxRadioButton geometry
268 // ----------------------------------------------------------------------------
269
270 wxSize wxRadioButton::DoGetBestSize() const
271 {
272 static int s_radioSize = 0;
273
274 if ( !s_radioSize )
275 {
276 wxScreenDC dc;
277 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
278
279 s_radioSize = dc.GetCharHeight();
280 }
281
282 wxString str = GetLabel();
283
284 int wRadio, hRadio;
285 if ( !str.empty() )
286 {
287 GetTextExtent(str, &wRadio, &hRadio);
288 wRadio += s_radioSize + GetCharWidth();
289
290 if ( hRadio < s_radioSize )
291 hRadio = s_radioSize;
292 }
293 else
294 {
295 wRadio = s_radioSize;
296 hRadio = s_radioSize;
297 }
298
299 return wxSize(wRadio, hRadio);
300 }
301
302 #endif // wxUSE_RADIOBTN
303