Provide shorter synonyms for wxEVT_XXX constants.
[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 void wxRadioButton::Init()
48 {
49 m_isChecked = false;
50 }
51
52 bool wxRadioButton::Create(wxWindow *parent,
53 wxWindowID id,
54 const wxString& label,
55 const wxPoint& pos,
56 const wxSize& size,
57 long style,
58 const wxValidator& validator,
59 const wxString& name)
60 {
61 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
62 return false;
63
64 WXDWORD exstyle = 0;
65 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
66
67 if ( !MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, exstyle) )
68 return false;
69
70 // for compatibility with wxGTK, the first radio button in a group is
71 // always checked (this makes sense anyhow as you need to ensure that at
72 // least one button in the group is checked and this is the simplest way to
73 // do it)
74 if ( HasFlag(wxRB_GROUP) )
75 SetValue(true);
76
77 return true;
78 }
79
80 // ----------------------------------------------------------------------------
81 // wxRadioButton functions
82 // ----------------------------------------------------------------------------
83
84 void wxRadioButton::SetValue(bool value)
85 {
86 ::SendMessage(GetHwnd(), BM_SETCHECK,
87 value ? BST_CHECKED : BST_UNCHECKED, 0);
88
89 m_isChecked = value;
90
91 if ( !value )
92 return;
93
94 // if we set the value of one radio button we also must clear all the other
95 // buttons in the same group: Windows doesn't do it automatically
96 //
97 // moreover, if another radiobutton in the group currently has the focus,
98 // we have to set it to this radiobutton, else the old radiobutton will be
99 // reselected automatically, if a parent window loses the focus and regains
100 // it.
101 wxWindow * const focus = FindFocus();
102 wxTopLevelWindow * const
103 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
104 wxCHECK_RET( tlw, wxT("radio button outside of TLW?") );
105 wxWindow * const focusInTLW = tlw->GetLastFocus();
106
107 const wxWindowList& siblings = GetParent()->GetChildren();
108 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
109 wxCHECK_RET( nodeThis, wxT("radio button not a child of its parent?") );
110
111 // this will be set to true in the code below if the focus is in our TLW
112 // and belongs to one of the other buttons in the same group
113 bool shouldSetFocus = false;
114
115 // this will be set to true if the focus is outside of our TLW currently
116 // but the remembered focus of this TLW is one of the other buttons in the
117 // same group
118 bool shouldSetTLWFocus = false;
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 // don't stop on non radio buttons, we could have intermixed
133 // buttons and e.g. static labels
134 continue;
135 }
136
137 if ( btn->HasFlag(wxRB_SINGLE) )
138 {
139 // A wxRB_SINGLE button isn't part of this group
140 break;
141 }
142
143 if ( btn == focus )
144 shouldSetFocus = true;
145 else if ( btn == focusInTLW )
146 shouldSetTLWFocus = true;
147
148 btn->SetValue(false);
149
150 if ( btn->HasFlag(wxRB_GROUP) )
151 {
152 // even if there are other radio buttons before this one,
153 // they're not in the same group with us
154 break;
155 }
156 }
157 }
158
159 // ... and also turn off all buttons after this one
160 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
161 nodeAfter;
162 nodeAfter = nodeAfter->GetNext() )
163 {
164 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
165 wxRadioButton);
166
167 if ( !btn )
168 continue;
169
170 if ( btn->HasFlag(wxRB_GROUP | wxRB_SINGLE) )
171 {
172 // no more buttons or the first button of the next group
173 break;
174 }
175
176 if ( btn == focus )
177 shouldSetFocus = true;
178 else if ( btn == focusInTLW )
179 shouldSetTLWFocus = true;
180
181 btn->SetValue(false);
182 }
183
184 if ( shouldSetFocus )
185 SetFocus();
186 else if ( shouldSetTLWFocus )
187 tlw->SetLastFocus(this);
188 }
189
190 bool wxRadioButton::GetValue() const
191 {
192 wxASSERT_MSG( m_isChecked ==
193 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
194 wxT("wxRadioButton::m_isChecked is out of sync?") );
195
196 return m_isChecked;
197 }
198
199 // ----------------------------------------------------------------------------
200 // wxRadioButton event processing
201 // ----------------------------------------------------------------------------
202
203 void wxRadioButton::Command (wxCommandEvent& event)
204 {
205 SetValue(event.GetInt() != 0);
206 ProcessCommand(event);
207 }
208
209 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
210 {
211 if ( param != BN_CLICKED )
212 return false;
213
214 if ( !m_isChecked )
215 {
216 // we need to manually update the button state as we use BS_RADIOBUTTON
217 // and not BS_AUTORADIOBUTTON
218 SetValue(true);
219
220 wxCommandEvent event(wxEVT_RADIOBUTTON, GetId());
221 event.SetEventObject( this );
222 event.SetInt(true); // always checked
223
224 ProcessCommand(event);
225 }
226
227 return true;
228 }
229
230 // ----------------------------------------------------------------------------
231 // wxRadioButton geometry
232 // ----------------------------------------------------------------------------
233
234 wxSize wxRadioButton::DoGetBestSize() const
235 {
236 static int s_radioSize = 0;
237
238 if ( !s_radioSize )
239 {
240 wxScreenDC dc;
241 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
242
243 s_radioSize = dc.GetCharHeight();
244
245 // radio button bitmap size under CE is bigger than the font height,
246 // adding just one pixel seems to work fine for the default font but it
247 // would be nice to find some better way to find the correct height
248 #ifdef __WXWINCE__
249 s_radioSize++;
250 #endif // __WXWINCE__
251 }
252
253 wxString str = GetLabel();
254
255 int wRadio, hRadio;
256 if ( !str.empty() )
257 {
258 GetTextExtent(GetLabelText(str), &wRadio, &hRadio);
259 wRadio += s_radioSize + GetCharWidth();
260
261 if ( hRadio < s_radioSize )
262 hRadio = s_radioSize;
263 }
264 else
265 {
266 wRadio = s_radioSize;
267 hRadio = s_radioSize;
268 }
269
270 wxSize best(wRadio, hRadio);
271 CacheBestSize(best);
272 return best;
273 }
274
275 WXDWORD wxRadioButton::MSWGetStyle(long style, WXDWORD *exstyle) const
276 {
277 WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);
278
279 if ( HasFlag(wxRB_GROUP) )
280 msStyle |= WS_GROUP;
281
282 // we use BS_RADIOBUTTON and not BS_AUTORADIOBUTTON because the use of the
283 // latter can easily result in the application entering an infinite loop
284 // inside IsDialogMessage()
285 //
286 // we used to use BS_RADIOBUTTON only for wxRB_SINGLE buttons but there
287 // doesn't seem to be any harm to always use it and it prevents some hangs,
288 // see #9786
289 msStyle |= BS_RADIOBUTTON;
290
291 if ( style & wxCLIP_SIBLINGS )
292 msStyle |= WS_CLIPSIBLINGS;
293 if ( style & wxALIGN_RIGHT )
294 msStyle |= BS_LEFTTEXT | BS_RIGHT;
295
296
297 return msStyle;
298 }
299
300 #endif // wxUSE_RADIOBTN