]> git.saurik.com Git - wxWidgets.git/blame - src/msw/radiobut.cpp
Do not allow multiple selection when dragging in the 'value' column
[wxWidgets.git] / src / msw / radiobut.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
b0b5881a 2// Name: src/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 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
5f199b71
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
f607434a 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
1e6feb95
VZ
27#if wxUSE_RADIOBTN
28
b0b5881a
WS
29#include "wx/radiobut.h"
30
2bda0e17 31#ifndef WX_PRECOMP
f607434a
VZ
32 #include "wx/settings.h"
33 #include "wx/dcscreen.h"
6b81a8ea 34 #include "wx/toplevel.h"
2bda0e17
KB
35#endif
36
37#include "wx/msw/private.h"
38
5f199b71
VZ
39// ============================================================================
40// wxRadioButton implementation
41// ============================================================================
42
43// ----------------------------------------------------------------------------
44// wxRadioButton creation
45// ----------------------------------------------------------------------------
46
2bda0e17 47
51741307 48#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
49WX_DEFINE_FLAGS( wxRadioButtonStyle )
50
3ff066a4 51wxBEGIN_FLAGS( wxRadioButtonStyle )
bc9fb572
JS
52 // new style border flags, we put them first to
53 // use them for streaming out
3ff066a4
SC
54 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
55 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
56 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
57 wxFLAGS_MEMBER(wxBORDER_RAISED)
58 wxFLAGS_MEMBER(wxBORDER_STATIC)
59 wxFLAGS_MEMBER(wxBORDER_NONE)
4d08943e 60
bc9fb572 61 // old style border flags
3ff066a4
SC
62 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
63 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
64 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
65 wxFLAGS_MEMBER(wxRAISED_BORDER)
66 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 67 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
68
69 // standard window styles
3ff066a4
SC
70 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
71 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
72 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
73 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 74 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
75 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
76 wxFLAGS_MEMBER(wxVSCROLL)
77 wxFLAGS_MEMBER(wxHSCROLL)
bc9fb572 78
3ff066a4 79 wxFLAGS_MEMBER(wxRB_GROUP)
bc9fb572 80
3ff066a4 81wxEND_FLAGS( wxRadioButtonStyle )
bc9fb572 82
51741307
SC
83IMPLEMENT_DYNAMIC_CLASS_XTI(wxRadioButton, wxControl,"wx/radiobut.h")
84
3ff066a4 85wxBEGIN_PROPERTIES_TABLE(wxRadioButton)
f607434a 86 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_RADIOBUTTON_SELECTED , wxCommandEvent )
af498247 87 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
f607434a 88 wxPROPERTY( Label,wxString, SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
af498247
VZ
89 wxPROPERTY( Value ,bool, SetValue, GetValue, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
90 wxPROPERTY_FLAGS( WindowStyle , wxRadioButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
3ff066a4 91wxEND_PROPERTIES_TABLE()
51741307 92
3ff066a4
SC
93wxBEGIN_HANDLERS_TABLE(wxRadioButton)
94wxEND_HANDLERS_TABLE()
51741307 95
4d08943e 96wxCONSTRUCTOR_6( wxRadioButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
51741307
SC
97
98#else
99IMPLEMENT_DYNAMIC_CLASS(wxRadioButton, wxControl)
100#endif
066f1b7a 101
066f1b7a 102
5f199b71 103void wxRadioButton::Init()
621793f4 104{
f607434a 105 m_isChecked = false;
621793f4
JS
106}
107
5f199b71
VZ
108bool wxRadioButton::Create(wxWindow *parent,
109 wxWindowID id,
110 const wxString& label,
111 const wxPoint& pos,
112 const wxSize& size,
113 long style,
114 const wxValidator& validator,
115 const wxString& name)
2bda0e17 116{
5f199b71 117 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
f607434a 118 return false;
2bda0e17 119
2b5f62a0
VZ
120 long msStyle = WS_TABSTOP;
121 if ( HasFlag(wxRB_GROUP) )
122 msStyle |= WS_GROUP;
123
cb738de0
VZ
124 // we use BS_RADIOBUTTON and not BS_AUTORADIOBUTTON because the use of the
125 // latter can easily result in the application entering an infinite loop
126 // inside IsDialogMessage()
127 //
128 // we used to use BS_RADIOBUTTON only for wxRB_SINGLE buttons but there
129 // doesn't seem to be any harm to always use it and it prevents some hangs,
130 // see #9786
131 msStyle |= BS_RADIOBUTTON;
2bda0e17 132
5f199b71 133 if ( HasFlag(wxCLIP_SIBLINGS) )
b0766406 134 msStyle |= WS_CLIPSIBLINGS;
93212fee
VZ
135 if ( HasFlag(wxALIGN_RIGHT) )
136 msStyle |= BS_LEFTTEXT | BS_RIGHT;
b0766406 137
9a83f860 138 if ( !MSWCreateControl(wxT("BUTTON"), msStyle, pos, size, label, 0) )
f607434a 139 return false;
b0766406 140
5f199b71
VZ
141 // for compatibility with wxGTK, the first radio button in a group is
142 // always checked (this makes sense anyhow as you need to ensure that at
cb738de0 143 // least one button in the group is checked and this is the simplest way to
5f199b71
VZ
144 // do it)
145 if ( HasFlag(wxRB_GROUP) )
f607434a 146 SetValue(true);
c085e333 147
f607434a 148 return true;
2bda0e17
KB
149}
150
5f199b71
VZ
151// ----------------------------------------------------------------------------
152// wxRadioButton functions
153// ----------------------------------------------------------------------------
2bda0e17 154
5f199b71 155void wxRadioButton::SetValue(bool value)
2bda0e17 156{
48fbe8ee
VZ
157 ::SendMessage(GetHwnd(), BM_SETCHECK,
158 value ? BST_CHECKED : BST_UNCHECKED, 0);
2b5f62a0 159
f607434a
VZ
160 m_isChecked = value;
161
48fbe8ee
VZ
162 if ( !value )
163 return;
164
2b5f62a0
VZ
165 // if we set the value of one radio button we also must clear all the other
166 // buttons in the same group: Windows doesn't do it automatically
48fbe8ee
VZ
167 //
168 // moreover, if another radiobutton in the group currently has the focus,
cb738de0 169 // we have to set it to this radiobutton, else the old radiobutton will be
48fbe8ee
VZ
170 // reselected automatically, if a parent window loses the focus and regains
171 // it.
172 wxWindow * const focus = FindFocus();
173 wxTopLevelWindow * const
174 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
9a83f860 175 wxCHECK_RET( tlw, wxT("radio button outside of TLW?") );
48fbe8ee
VZ
176 wxWindow * const focusInTLW = tlw->GetLastFocus();
177
178 const wxWindowList& siblings = GetParent()->GetChildren();
179 wxWindowList::compatibility_iterator nodeThis = siblings.Find(this);
9a83f860 180 wxCHECK_RET( nodeThis, wxT("radio button not a child of its parent?") );
48fbe8ee
VZ
181
182 // this will be set to true in the code below if the focus is in our TLW
183 // and belongs to one of the other buttons in the same group
184 bool shouldSetFocus = false;
185
186 // this will be set to true if the focus is outside of our TLW currently
187 // but the remembered focus of this TLW is one of the other buttons in the
188 // same group
189 bool shouldSetTLWFocus = false;
190
191 // if it's not the first item of the group ...
192 if ( !HasFlag(wxRB_GROUP) )
2b5f62a0 193 {
48fbe8ee
VZ
194 // ... turn off all radio buttons before it
195 for ( wxWindowList::compatibility_iterator nodeBefore = nodeThis->GetPrevious();
196 nodeBefore;
197 nodeBefore = nodeBefore->GetPrevious() )
2b5f62a0 198 {
48fbe8ee
VZ
199 wxRadioButton *btn = wxDynamicCast(nodeBefore->GetData(),
200 wxRadioButton);
201 if ( !btn )
2b5f62a0 202 {
48fbe8ee
VZ
203 // don't stop on non radio buttons, we could have intermixed
204 // buttons and e.g. static labels
205 continue;
206 }
207
208 if ( btn->HasFlag(wxRB_SINGLE) )
2b17e391 209 {
c5b3143a 210 // A wxRB_SINGLE button isn't part of this group
2b17e391
VZ
211 break;
212 }
b0b5881a 213
48fbe8ee
VZ
214 if ( btn == focus )
215 shouldSetFocus = true;
216 else if ( btn == focusInTLW )
217 shouldSetTLWFocus = true;
c5b3143a 218
48fbe8ee 219 btn->SetValue(false);
2b5f62a0 220
48fbe8ee 221 if ( btn->HasFlag(wxRB_GROUP) )
2b5f62a0 222 {
48fbe8ee
VZ
223 // even if there are other radio buttons before this one,
224 // they're not in the same group with us
2b5f62a0
VZ
225 break;
226 }
48fbe8ee
VZ
227 }
228 }
2b5f62a0 229
48fbe8ee
VZ
230 // ... and also turn off all buttons after this one
231 for ( wxWindowList::compatibility_iterator nodeAfter = nodeThis->GetNext();
232 nodeAfter;
233 nodeAfter = nodeAfter->GetNext() )
234 {
235 wxRadioButton *btn = wxDynamicCast(nodeAfter->GetData(),
236 wxRadioButton);
f469584d 237
48fbe8ee
VZ
238 if ( !btn )
239 continue;
240
241 if ( btn->HasFlag(wxRB_GROUP | wxRB_SINGLE) )
242 {
243 // no more buttons or the first button of the next group
244 break;
2b5f62a0 245 }
48fbe8ee
VZ
246
247 if ( btn == focus )
248 shouldSetFocus = true;
249 else if ( btn == focusInTLW )
250 shouldSetTLWFocus = true;
251
252 btn->SetValue(false);
2b5f62a0 253 }
48fbe8ee
VZ
254
255 if ( shouldSetFocus )
256 SetFocus();
257 else if ( shouldSetTLWFocus )
258 tlw->SetLastFocus(this);
2bda0e17
KB
259}
260
5f199b71 261bool wxRadioButton::GetValue() const
2bda0e17 262{
f607434a
VZ
263 wxASSERT_MSG( m_isChecked ==
264 (::SendMessage(GetHwnd(), BM_GETCHECK, 0, 0L) != 0),
9a83f860 265 wxT("wxRadioButton::m_isChecked is out of sync?") );
f607434a
VZ
266
267 return m_isChecked;
2bda0e17
KB
268}
269
5f199b71
VZ
270// ----------------------------------------------------------------------------
271// wxRadioButton event processing
272// ----------------------------------------------------------------------------
273
274void wxRadioButton::Command (wxCommandEvent& event)
2bda0e17 275{
687706f5 276 SetValue(event.GetInt() != 0);
5f199b71 277 ProcessCommand(event);
2bda0e17
KB
278}
279
5f199b71 280bool wxRadioButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
f6bcfd97 281{
5f199b71 282 if ( param != BN_CLICKED )
f607434a 283 return false;
5f199b71 284
f607434a 285 if ( !m_isChecked )
f6bcfd97 286 {
cb738de0
VZ
287 // we need to manually update the button state as we use BS_RADIOBUTTON
288 // and not BS_AUTORADIOBUTTON
f607434a 289 SetValue(true);
2b5f62a0 290
5f199b71
VZ
291 wxCommandEvent event(wxEVT_COMMAND_RADIOBUTTON_SELECTED, GetId());
292 event.SetEventObject( this );
f607434a 293 event.SetInt(true); // always checked
f6bcfd97 294
5f199b71
VZ
295 ProcessCommand(event);
296 }
f6bcfd97 297
f607434a 298 return true;
f6bcfd97 299}
2bda0e17 300
5f199b71
VZ
301// ----------------------------------------------------------------------------
302// wxRadioButton geometry
303// ----------------------------------------------------------------------------
304
305wxSize wxRadioButton::DoGetBestSize() const
2bda0e17 306{
5f199b71 307 static int s_radioSize = 0;
2bda0e17 308
5f199b71
VZ
309 if ( !s_radioSize )
310 {
311 wxScreenDC dc;
312 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
2bda0e17 313
5f199b71 314 s_radioSize = dc.GetCharHeight();
7eee3607
VZ
315
316 // radio button bitmap size under CE is bigger than the font height,
317 // adding just one pixel seems to work fine for the default font but it
318 // would be nice to find some better way to find the correct height
319#ifdef __WXWINCE__
320 s_radioSize++;
321#endif // __WXWINCE__
5f199b71 322 }
2bda0e17 323
5f199b71 324 wxString str = GetLabel();
2bda0e17 325
5f199b71
VZ
326 int wRadio, hRadio;
327 if ( !str.empty() )
328 {
32cd189d 329 GetTextExtent(GetLabelText(str), &wRadio, &hRadio);
5f199b71 330 wRadio += s_radioSize + GetCharWidth();
2bda0e17 331
5f199b71
VZ
332 if ( hRadio < s_radioSize )
333 hRadio = s_radioSize;
334 }
335 else
336 {
337 wRadio = s_radioSize;
338 hRadio = s_radioSize;
339 }
2bda0e17 340
31582e4e
RD
341 wxSize best(wRadio, hRadio);
342 CacheBestSize(best);
343 return best;
2bda0e17
KB
344}
345
bec08b39
VZ
346WXDWORD wxRadioButton::MSWGetStyle(long style, WXDWORD *exstyle) const
347{
348 WXDWORD styleMSW = wxControl::MSWGetStyle(style, exstyle);
349
350 if ( style & wxRB_GROUP )
351 styleMSW |= WS_GROUP;
352
353 return styleMSW;
354}
355
1e6feb95 356#endif // wxUSE_RADIOBTN