Removed erroneous copyright names and corrected licence spelling
[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 #ifdef __GNUG__
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 IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
51
52 void wxRadioButton::Init()
53 {
54 m_focusJustSet = FALSE;
55 }
56
57 bool wxRadioButton::Create(wxWindow *parent,
58 wxWindowID id,
59 const wxString& label,
60 const wxPoint& pos,
61 const wxSize& size,
62 long style,
63 const wxValidator& validator,
64 const wxString& name)
65 {
66 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
67 return FALSE;
68
69 long msStyle = WS_TABSTOP;
70 if ( HasFlag(wxRB_GROUP) )
71 msStyle |= WS_GROUP;
72
73 /*
74 wxRB_SINGLE is a temporary workaround for the following problem: if you
75 have 2 radiobuttons in the same group but which are not consecutive in
76 the dialog, Windows can enter an infinite loop! The simplest way to
77 reproduce it is to create radio button, then a panel and then another
78 radio button: then checking the last button hangs the app.
79
80 Ideally, we'd detect (and avoid) such situation automatically but for
81 now, as I don't know how to do it, just allow the user to create
82 BS_RADIOBUTTON buttons for such situations.
83 */
84 msStyle |= HasFlag(wxRB_SINGLE) ? BS_RADIOBUTTON : BS_AUTORADIOBUTTON;
85
86 if ( HasFlag(wxCLIP_SIBLINGS) )
87 msStyle |= WS_CLIPSIBLINGS;
88
89 if ( !MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, 0) )
90 return FALSE;
91
92 // for compatibility with wxGTK, the first radio button in a group is
93 // always checked (this makes sense anyhow as you need to ensure that at
94 // least one button in the group is checked and this is the simlpest way to
95 // do it)
96 if ( HasFlag(wxRB_GROUP) )
97 SetValue(TRUE);
98
99 return TRUE;
100 }
101
102 // ----------------------------------------------------------------------------
103 // wxRadioButton functions
104 // ----------------------------------------------------------------------------
105
106 void wxRadioButton::SetValue(bool value)
107 {
108 // BST_CHECKED is defined as 1, BST_UNCHECKED as 0, so we can just pass
109 // value as is (we don't use BST_XXX here as they're not defined for Win16)
110 (void)::SendMessage(GetHwnd(), BM_SETCHECK, (WPARAM)value, 0L);
111
112 // if we set the value of one radio button we also must clear all the other
113 // buttons in the same group: Windows doesn't do it automatically
114 if ( value )
115 {
116 const wxWindowList& siblings = GetParent()->GetChildren();
117 wxWindowList::Node *nodeThis = siblings.Find(this);
118 wxCHECK_RET( nodeThis, _T("radio button not a child of its parent?") );
119
120 // turn off all radio buttons before this one
121 for ( wxWindowList::Node *nodeBefore = nodeThis->GetPrevious();
122 nodeBefore;
123 nodeBefore = nodeBefore->GetPrevious() )
124 {
125 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
126 wxRadioButton);
127 if ( !btn )
128 {
129 // the radio buttons in a group must be consecutive, so there
130 // are no more of them
131 break;
132 }
133
134 btn->SetValue(FALSE);
135
136 if ( btn->HasFlag(wxRB_GROUP) )
137 {
138 // even if there are other radio buttons before this one,
139 // they're not in the same group with us
140 break;
141 }
142 }
143
144 // ... and all after this one
145 for ( wxWindowList::Node *nodeAfter = nodeThis->GetNext();
146 nodeAfter;
147 nodeAfter = nodeAfter->GetNext() )
148 {
149 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
150 wxRadioButton);
151
152 if ( !btn || btn->HasFlag(wxRB_GROUP) )
153 {
154 // no more buttons or the first button of the next group
155 break;
156 }
157
158 btn->SetValue(FALSE);
159 }
160 }
161 }
162
163 bool wxRadioButton::GetValue() const
164 {
165 // NB: this will also return TRUE for BST_INDETERMINATE value if we ever
166 // have 3-state radio buttons
167 return ::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0;
168 }
169
170 // ----------------------------------------------------------------------------
171 // wxRadioButton event processing
172 // ----------------------------------------------------------------------------
173
174 void wxRadioButton::Command (wxCommandEvent& event)
175 {
176 SetValue(event.m_commandInt != 0);
177 ProcessCommand(event);
178 }
179
180 void wxRadioButton::SetFocus()
181 {
182 // when the radio button receives a WM_SETFOCUS message it generates a
183 // BN_CLICKED which is totally unexpected and leads to catastrophic results
184 // if you pop up a dialog from the radio button event handler as, when the
185 // dialog is dismissed, the focus is returned to the radio button which
186 // generates BN_CLICKED which leads to showing another dialog and so on
187 // without end!
188 //
189 // to avoid this, we drop the pseudo BN_CLICKED events generated when the
190 // button gains focus
191 m_focusJustSet = TRUE;
192
193 wxControl::SetFocus();
194 }
195
196 bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
197 {
198 if ( param != BN_CLICKED )
199 return FALSE;
200
201 if ( m_focusJustSet )
202 {
203 // see above: we want to ignore this event
204 m_focusJustSet = FALSE;
205 }
206 else // a real clicked event
207 {
208 bool isChecked = GetValue();
209
210 if ( HasFlag(wxRB_SINGLE) )
211 {
212 // when we use a "manual" radio button, we have to check the button
213 // ourselves -- but it's reset to unchecked state by the user code
214 // (presumably when another button is pressed)
215 if ( !isChecked )
216 SetValue(TRUE);
217 }
218
219 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
220 event.SetEventObject( this );
221 event.SetInt(isChecked);
222
223 ProcessCommand(event);
224 }
225
226 return TRUE;
227 }
228
229 // ----------------------------------------------------------------------------
230 // wxRadioButton geometry
231 // ----------------------------------------------------------------------------
232
233 wxSize wxRadioButton::DoGetBestSize() const
234 {
235 static int s_radioSize = 0;
236
237 if ( !s_radioSize )
238 {
239 wxScreenDC dc;
240 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
241
242 s_radioSize = dc.GetCharHeight();
243 }
244
245 wxString str = GetLabel();
246
247 int wRadio, hRadio;
248 if ( !str.empty() )
249 {
250 GetTextExtent(str, &wRadio, &hRadio);
251 wRadio += s_radioSize + GetCharWidth();
252
253 if ( hRadio < s_radioSize )
254 hRadio = s_radioSize;
255 }
256 else
257 {
258 wRadio = s_radioSize;
259 hRadio = s_radioSize;
260 }
261
262 return wxSize(wRadio, hRadio);
263 }
264
265 long wxRadioButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
266 {
267 if (nMsg == WM_SETFOCUS)
268 {
269 m_focusJustSet = TRUE;
270
271 long ret = wxControl::MSWWindowProc(nMsg, wParam, lParam);
272
273 m_focusJustSet = FALSE;
274
275 return ret;
276 }
277 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
278 }
279
280 #endif // wxUSE_RADIOBTN