]> git.saurik.com Git - wxWidgets.git/blame - src/msw/radiobut.cpp
wxNavigationKeyEvent doesn't derive from wxCommandEvent (but from wxEvent)
[wxWidgets.git] / src / msw / radiobut.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
1e6feb95 2// Name: 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
JS
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
5f199b71
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
c9baf9d7 21#pragma implementation "radiobut.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
c9baf9d7 28#pragma hdrstop
2bda0e17
KB
29#endif
30
1e6feb95
VZ
31#if wxUSE_RADIOBTN
32
2bda0e17 33#ifndef WX_PRECOMP
c9baf9d7
RL
34#include "wx/radiobut.h"
35#include "wx/settings.h"
36#include "wx/brush.h"
37#include "wx/dcscreen.h"
2bda0e17
KB
38#endif
39
40#include "wx/msw/private.h"
41
5f199b71
VZ
42// ============================================================================
43// wxRadioButton implementation
44// ============================================================================
45
46// ----------------------------------------------------------------------------
47// wxRadioButton creation
48// ----------------------------------------------------------------------------
49
2bda0e17 50
51741307 51#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
52WX_DEFINE_FLAGS( wxRadioButtonStyle )
53
3ff066a4 54wxBEGIN_FLAGS( wxRadioButtonStyle )
bc9fb572
JS
55 // new style border flags, we put them first to
56 // use them for streaming out
3ff066a4
SC
57 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
58 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
59 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
60 wxFLAGS_MEMBER(wxBORDER_RAISED)
61 wxFLAGS_MEMBER(wxBORDER_STATIC)
62 wxFLAGS_MEMBER(wxBORDER_NONE)
bc9fb572
JS
63
64 // old style border flags
3ff066a4
SC
65 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
66 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
67 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
68 wxFLAGS_MEMBER(wxRAISED_BORDER)
69 wxFLAGS_MEMBER(wxSTATIC_BORDER)
70 wxFLAGS_MEMBER(wxNO_BORDER)
bc9fb572
JS
71
72 // standard window styles
3ff066a4
SC
73 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
74 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
75 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
76 wxFLAGS_MEMBER(wxWANTS_CHARS)
77 wxFLAGS_MEMBER(wxNO_FULL_REPAINT_ON_RESIZE)
78 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
79 wxFLAGS_MEMBER(wxVSCROLL)
80 wxFLAGS_MEMBER(wxHSCROLL)
bc9fb572 81
3ff066a4 82 wxFLAGS_MEMBER(wxRB_GROUP)
bc9fb572 83
3ff066a4 84wxEND_FLAGS( wxRadioButtonStyle )
bc9fb572 85
51741307
SC
86IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
87
3ff066a4
SC
88wxBEGIN_PROPERTIES_TABLE(wxRadioButton)
89 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
90 wxPROPERTY( Font , wxFont , SetFont , GetFont , , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
91 wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
92 wxPROPERTY( Value ,bool, SetValue, GetValue,, 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
93 wxPROPERTY_FLAGS( WindowStyle , wxRadioButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
94wxEND_PROPERTIES_TABLE()
51741307 95
3ff066a4
SC
96wxBEGIN_HANDLERS_TABLE(wxRadioButton)
97wxEND_HANDLERS_TABLE()
51741307 98
3ff066a4 99wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
51741307
SC
100
101#else
102IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
103#endif
066f1b7a 104
066f1b7a 105
5f199b71 106void wxRadioButton::Init()
621793f4 107{
5f199b71 108 m_focusJustSet = FALSE;
621793f4
JS
109}
110
5f199b71
VZ
111bool 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)
2bda0e17 119{
5f199b71
VZ
120 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
121 return FALSE;
2bda0e17 122
2b5f62a0
VZ
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.
2bda0e17 133
2b5f62a0
VZ
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;
2bda0e17 139
5f199b71 140 if ( HasFlag(wxCLIP_SIBLINGS) )
b0766406
JS
141 msStyle |= WS_CLIPSIBLINGS;
142
5f199b71
VZ
143 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
144 return FALSE;
b0766406 145
5f199b71
VZ
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);
c085e333 152
5f199b71 153 return TRUE;
2bda0e17
KB
154}
155
5f199b71
VZ
156// ----------------------------------------------------------------------------
157// wxRadioButton functions
158// ----------------------------------------------------------------------------
2bda0e17 159
5f199b71 160void wxRadioButton::SetValue(bool value)
2bda0e17 161{
5f199b71 162 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
2b5f62a0 163 // value as is (we don't use BST_XXX here as they're not defined for Win16)
5f199b71 164 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM)value, 0L);
2b5f62a0
VZ
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();
222ed1d6 171 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
2b5f62a0
VZ
172 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
173
2b17e391
VZ
174 // if it's not the first item of the group ...
175 if ( !HasFlag(wxRB_GROUP) )
2b5f62a0 176 {
2b17e391 177 // ... turn off all radio buttons before it
222ed1d6 178 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
2b17e391
VZ
179 nodeBefore;
180 nodeBefore = nodeBefore->GetPrevious() )
2b5f62a0 181 {
2b17e391
VZ
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 }
2b5f62a0
VZ
199 }
200 }
201
2b17e391 202 // ... and also turn off all buttons after this one
222ed1d6 203 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
2b5f62a0
VZ
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 }
2bda0e17
KB
219}
220
5f199b71 221bool wxRadioButton::GetValue() const
2bda0e17 222{
5f199b71
VZ
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;
2bda0e17
KB
226}
227
5f199b71
VZ
228// ----------------------------------------------------------------------------
229// wxRadioButton event processing
230// ----------------------------------------------------------------------------
231
232void wxRadioButton::Command (wxCommandEvent& event)
2bda0e17 233{
5f199b71
VZ
234 SetValue(event.m_commandInt != 0);
235 ProcessCommand(event);
2bda0e17
KB
236}
237
5f199b71 238void wxRadioButton::SetFocus()
2bda0e17 239{
5f199b71
VZ
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 //
2b5f62a0 247 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
5f199b71
VZ
248 // button gains focus
249 m_focusJustSet = TRUE;
250
251 wxControl::SetFocus();
2bda0e17
KB
252}
253
5f199b71 254bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
f6bcfd97 255{
5f199b71
VZ
256 if ( param != BN_CLICKED )
257 return FALSE;
258
259 if ( m_focusJustSet )
f6bcfd97 260 {
5f199b71
VZ
261 // see above: we want to ignore this event
262 m_focusJustSet = FALSE;
f6bcfd97 263 }
5f199b71
VZ
264 else // a real clicked event
265 {
2b5f62a0
VZ
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
5f199b71
VZ
277 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
278 event.SetEventObject( this );
2b5f62a0 279 event.SetInt(isChecked);
f6bcfd97 280
5f199b71
VZ
281 ProcessCommand(event);
282 }
f6bcfd97 283
5f199b71 284 return TRUE;
f6bcfd97 285}
2bda0e17 286
5f199b71
VZ
287// ----------------------------------------------------------------------------
288// wxRadioButton geometry
289// ----------------------------------------------------------------------------
290
291wxSize wxRadioButton::DoGetBestSize() const
2bda0e17 292{
5f199b71 293 static int s_radioSize = 0;
2bda0e17 294
5f199b71
VZ
295 if ( !s_radioSize )
296 {
297 wxScreenDC dc;
298 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
2bda0e17 299
5f199b71
VZ
300 s_radioSize = dc.GetCharHeight();
301 }
2bda0e17 302
5f199b71 303 wxString str = GetLabel();
2bda0e17 304
5f199b71
VZ
305 int wRadio, hRadio;
306 if ( !str.empty() )
307 {
308 GetTextExtent(str, &wRadio, &hRadio);
309 wRadio += s_radioSize + GetCharWidth();
2bda0e17 310
5f199b71
VZ
311 if ( hRadio < s_radioSize )
312 hRadio = s_radioSize;
313 }
314 else
315 {
316 wRadio = s_radioSize;
317 hRadio = s_radioSize;
318 }
2bda0e17 319
5f199b71 320 return wxSize(wRadio, hRadio);
2bda0e17
KB
321}
322
aca78774
JS
323long 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 }
2b5f62a0 335 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
aca78774
JS
336}
337
1e6feb95 338#endif // wxUSE_RADIOBTN