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