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