Added some window style metadata
[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/brush.h"
37 #include "wx/dcscreen.h"
38 #endif
39
40 #include "wx/msw/private.h"
41
42 // ============================================================================
43 // wxRadioButton implementation
44 // ============================================================================
45
46 // ----------------------------------------------------------------------------
47 // wxRadioButton creation
48 // ----------------------------------------------------------------------------
49
50
51 #if wxUSE_EXTENDED_RTTI
52 WX_DEFINE_FLAGS( wxRadioButtonStyle )
53
54 WX_BEGIN_FLAGS( wxRadioButtonStyle )
55 // new style border flags, we put them first to
56 // use them for streaming out
57 WX_FLAGS_MEMBER(wxBORDER_SIMPLE)
58 WX_FLAGS_MEMBER(wxBORDER_SUNKEN)
59 WX_FLAGS_MEMBER(wxBORDER_DOUBLE)
60 WX_FLAGS_MEMBER(wxBORDER_RAISED)
61 WX_FLAGS_MEMBER(wxBORDER_STATIC)
62 WX_FLAGS_MEMBER(wxBORDER_NONE)
63
64 // old style border flags
65 WX_FLAGS_MEMBER(wxSIMPLE_BORDER)
66 WX_FLAGS_MEMBER(wxSUNKEN_BORDER)
67 WX_FLAGS_MEMBER(wxDOUBLE_BORDER)
68 WX_FLAGS_MEMBER(wxRAISED_BORDER)
69 WX_FLAGS_MEMBER(wxSTATIC_BORDER)
70 WX_FLAGS_MEMBER(wxNO_BORDER)
71
72 // standard window styles
73 WX_FLAGS_MEMBER(wxTAB_TRAVERSAL)
74 WX_FLAGS_MEMBER(wxCLIP_CHILDREN)
75 WX_FLAGS_MEMBER(wxTRANSPARENT_WINDOW)
76 WX_FLAGS_MEMBER(wxWANTS_CHARS)
77 WX_FLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
78 WX_FLAGS_MEMBER(wxALWAYS_SHOW_SB )
79 WX_FLAGS_MEMBER(wxVSCROLL)
80 WX_FLAGS_MEMBER(wxHSCROLL)
81
82 WX_FLAGS_MEMBER(wxRB_GROUP)
83
84 WX_END_FLAGS( wxRadioButtonStyle )
85
86 IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
87
88 WX_BEGIN_PROPERTIES_TABLE(wxRadioButton)
89 WX_DELEGATE( OnClick , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
90 WX_PROPERTY( Font , wxFont , SetFont , GetFont , , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
91 WX_PROPERTY( Label,wxString, SetLabel, GetLabel, wxEmptyString, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
92 WX_PROPERTY( Value ,bool, SetValue, GetValue,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
93 WX_PROPERTY_FLAGS( WindowStyle , wxRadioButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
94 WX_END_PROPERTIES_TABLE()
95
96 WX_BEGIN_HANDLERS_TABLE(wxRadioButton)
97 WX_END_HANDLERS_TABLE()
98
99 WX_CONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
100
101 #else
102 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
103 #endif
104
105
106 void wxRadioButton::Init()
107 {
108 m_focusJustSet = FALSE;
109 }
110
111 bool wxRadioButton::Create(wxWindow *parent,
112 wxWindowID id,
113 const wxString& label,
114 const wxPoint& pos,
115 const wxSize& size,
116 long style,
117 const wxValidator& validator,
118 const wxString& name)
119 {
120 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
121 return FALSE;
122
123 long msStyle = WS_TABSTOP;
124 if ( HasFlag(wxRB_GROUP) )
125 msStyle |= WS_GROUP;
126
127 /*
128 wxRB_SINGLE is a temporary workaround for the following problem: if you
129 have 2 radiobuttons in the same group but which are not consecutive in
130 the dialog, Windows can enter an infinite loop! The simplest way to
131 reproduce it is to create radio button, then a panel and then another
132 radio button: then checking the last button hangs the app.
133
134 Ideally, we'd detect (and avoid) such situation automatically but for
135 now, as I don't know how to do it, just allow the user to create
136 BS_RADIOBUTTON buttons for such situations.
137 */
138 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
139
140 if ( HasFlag(wxCLIP_SIBLINGS) )
141 msStyle |= WS_CLIPSIBLINGS;
142
143 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
144 return FALSE;
145
146 // for compatibility with wxGTK, the first radio button in a group is
147 // always checked (this makes sense anyhow as you need to ensure that at
148 // least one button in the group is checked and this is the simlpest way to
149 // do it)
150 if ( HasFlag(wxRB_GROUP) )
151 SetValue(TRUE);
152
153 return TRUE;
154 }
155
156 // ----------------------------------------------------------------------------
157 // wxRadioButton functions
158 // ----------------------------------------------------------------------------
159
160 void wxRadioButton::SetValue(bool value)
161 {
162 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
163 // value as is (we don't use BST_XXX here as they're not defined for Win16)
164 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM)value, 0L);
165
166 // if we set the value of one radio button we also must clear all the other
167 // buttons in the same group: Windows doesn't do it automatically
168 if ( value )
169 {
170 const wxWindowList& siblings = GetParent()->GetChildren();
171 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
172 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
173
174 // if it's not the first item of the group ...
175 if ( !HasFlag(wxRB_GROUP) )
176 {
177 // ... turn off all radio buttons before it
178 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
179 nodeBefore;
180 nodeBefore = nodeBefore->GetPrevious() )
181 {
182 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
183 wxRadioButton);
184 if ( !btn )
185 {
186 // the radio buttons in a group must be consecutive, so
187 // there are no more of them
188 break;
189 }
190
191 btn->SetValue(FALSE);
192
193 if ( btn->HasFlag(wxRB_GROUP) )
194 {
195 // even if there are other radio buttons before this one,
196 // they're not in the same group with us
197 break;
198 }
199 }
200 }
201
202 // ... and also turn off all buttons after this one
203 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
204 nodeAfter;
205 nodeAfter = nodeAfter->GetNext() )
206 {
207 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
208 wxRadioButton);
209
210 if ( !btn || btn->HasFlag(wxRB_GROUP) )
211 {
212 // no more buttons or the first button of the next group
213 break;
214 }
215
216 btn->SetValue(FALSE);
217 }
218 }
219 }
220
221 bool wxRadioButton::GetValue() const
222 {
223 // NB: this will also return TRUE for BST_INDETERMINATE value if we ever
224 // have 3-state radio buttons
225 return ::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0;
226 }
227
228 // ----------------------------------------------------------------------------
229 // wxRadioButton event processing
230 // ----------------------------------------------------------------------------
231
232 void wxRadioButton::Command (wxCommandEvent& event)
233 {
234 SetValue(event.m_commandInt != 0);
235 ProcessCommand(event);
236 }
237
238 void wxRadioButton::SetFocus()
239 {
240 // when the radio button receives a WM_SETFOCUS message it generates a
241 // BN_CLICKED which is totally unexpected and leads to catastrophic results
242 // if you pop up a dialog from the radio button event handler as, when the
243 // dialog is dismissed, the focus is returned to the radio button which
244 // generates BN_CLICKED which leads to showing another dialog and so on
245 // without end!
246 //
247 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
248 // button gains focus
249 m_focusJustSet = TRUE;
250
251 wxControl::SetFocus();
252 }
253
254 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
255 {
256 if ( param != BN_CLICKED )
257 return FALSE;
258
259 if ( m_focusJustSet )
260 {
261 // see above: we want to ignore this event
262 m_focusJustSet = FALSE;
263 }
264 else // a real clicked event
265 {
266 bool isChecked = GetValue();
267
268 if ( HasFlag(wxRB_SINGLE) )
269 {
270 // when we use a "manual" radio button, we have to check the button
271 // ourselves -- but it's reset to unchecked state by the user code
272 // (presumably when another button is pressed)
273 if ( !isChecked )
274 SetValue(TRUE);
275 }
276
277 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
278 event.SetEventObject( this );
279 event.SetInt(isChecked);
280
281 ProcessCommand(event);
282 }
283
284 return TRUE;
285 }
286
287 // ----------------------------------------------------------------------------
288 // wxRadioButton geometry
289 // ----------------------------------------------------------------------------
290
291 wxSize wxRadioButton::DoGetBestSize() const
292 {
293 static int s_radioSize = 0;
294
295 if ( !s_radioSize )
296 {
297 wxScreenDC dc;
298 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
299
300 s_radioSize = dc.GetCharHeight();
301 }
302
303 wxString str = GetLabel();
304
305 int wRadio, hRadio;
306 if ( !str.empty() )
307 {
308 GetTextExtent(str, &wRadio, &hRadio);
309 wRadio += s_radioSize + GetCharWidth();
310
311 if ( hRadio < s_radioSize )
312 hRadio = s_radioSize;
313 }
314 else
315 {
316 wRadio = s_radioSize;
317 hRadio = s_radioSize;
318 }
319
320 return wxSize(wRadio, hRadio);
321 }
322
323 long wxRadioButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
324 {
325 if (nMsg == WM_SETFOCUS)
326 {
327 m_focusJustSet = TRUE;
328
329 long ret = wxControl::MSWWindowProc(nMsg, wParam, lParam);
330
331 m_focusJustSet = FALSE;
332
333 return ret;
334 }
335 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
336 }
337
338 #endif // wxUSE_RADIOBTN