]> git.saurik.com Git - wxWidgets.git/blame - src/univ/radiobox.cpp
fix for crash when clicking below the items
[wxWidgets.git] / src / univ / radiobox.cpp
CommitLineData
1e6feb95
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: univ/radiobox.cpp
3// Purpose: wxRadioBox implementation
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 11.09.00
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
1e6feb95
VZ
9// Licence: wxWindows license
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#ifdef __GNUG__
a3870b2f 21 #pragma implementation "univradiobox.h"
1e6feb95
VZ
22#endif
23
24#include "wx/wxprec.h"
25
26#ifdef __BORLANDC__
27 #pragma hdrstop
28#endif
29
30#if wxUSE_RADIOBOX
31
32#ifndef WX_PRECOMP
33 #include "wx/dcclient.h"
34 #include "wx/radiobox.h"
35 #include "wx/radiobut.h"
36 #include "wx/validate.h"
37#endif
38
39#include "wx/univ/theme.h"
40#include "wx/univ/renderer.h"
41#include "wx/univ/inphand.h"
42#include "wx/univ/colschem.h"
43
44// ----------------------------------------------------------------------------
45// constants
46// ----------------------------------------------------------------------------
47
48static const int BUTTON_BORDER_X = 2;
49static const int BUTTON_BORDER_Y = 4;
50
51static const int BOX_BORDER_X = 2;
52static const int BOX_BORDER_Y = 2;
53
54// ----------------------------------------------------------------------------
55// wxRadioBox event handler
56// ----------------------------------------------------------------------------
57
58class wxRadioHookHandler : public wxEvtHandler
59{
60public:
61 wxRadioHookHandler(wxRadioBox *radio) { m_radio = radio; }
62
63 virtual bool ProcessEvent(wxEvent& event)
64 {
65 // we intercept the command events from radio buttons
66 if ( event.GetEventType() == wxEVT_COMMAND_RADIOBUTTON_SELECTED )
67 {
68 m_radio->OnRadioButton(event);
69 }
70 else if ( event.GetEventType() == wxEVT_KEY_DOWN )
71 {
72 if ( m_radio->OnKeyDown((wxKeyEvent &)event) )
73 {
74 return TRUE;
75 }
76 }
77
78 // just pass it on
79 return GetNextHandler()->ProcessEvent(event);
80 }
81
82private:
83 wxRadioBox *m_radio;
84};
85
86// ============================================================================
87// implementation
88// ============================================================================
89
90IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
91
92// ----------------------------------------------------------------------------
93// wxRadioBox creation
94// ----------------------------------------------------------------------------
95
96void wxRadioBox::Init()
97{
98 m_selection = -1;
99 m_majorDim = 0;
100}
101
102bool wxRadioBox::Create(wxWindow *parent,
103 wxWindowID id,
104 const wxString& title,
105 const wxPoint& pos,
106 const wxSize& size,
107 int n,
108 const wxString *choices,
109 int majorDim,
110 long style,
111 const wxValidator& val,
112 const wxString& name)
113{
114 // for compatibility with the other ports which don't handle (yet?)
115 // wxRA_LEFTTORIGHT and wxRA_TOPTOBOTTOM flags, we add them ourselves if
116 // not specified
117 if ( !(style & (wxRA_LEFTTORIGHT | wxRA_TOPTOBOTTOM)) )
118 {
119 // horizontal radiobox use left to right layout
120 if ( style & wxRA_HORIZONTAL )
121 {
122 style |= wxRA_LEFTTORIGHT;
123 }
124 else if ( style & wxRA_VERTICAL )
125 {
126 style |= wxRA_TOPTOBOTTOM;
127 }
128 else
129 {
130 wxFAIL_MSG( _T("you must specify wxRA_XXX style!") );
131
132 // use default
133 style = wxRA_HORIZONTAL | wxRA_LEFTTORIGHT;
134 }
135 }
136
137 if ( !wxStaticBox::Create(parent, id, title, pos, size, style, name) )
138 return FALSE;
139
140#if wxUSE_VALIDATORS
141 SetValidator(val);
142#endif // wxUSE_VALIDATORS
143
144 Append(n, choices);
145
146 // majorDim default value is 0 which means make one row/column
147 SetMajorDim(majorDim == 0 ? n : majorDim);
148
149 if ( size == wxDefaultSize )
150 {
151 SetClientSize(DoGetBestClientSize());
152 }
153
154 // radiobox should already have selection so select at least one item
155 SetSelection(0);
156
157 return TRUE;
158}
159
160wxRadioBox::~wxRadioBox()
161{
162 // remove the event handlers we pushed on them from all buttons and delete
163 // the buttons themselves: this must be done as the user code expects them
164 // to disappear now and not some time later when they will be deleted by
165 // our (common) parent
166 size_t count = m_buttons.GetCount();
167 for ( size_t n = 0; n < count; n++ )
168 {
169 m_buttons[n]->PopEventHandler(TRUE /* delete it */);
170
171 delete m_buttons[n];
172 }
173}
174
175// ----------------------------------------------------------------------------
176// wxRadioBox init
177// ----------------------------------------------------------------------------
178
179void wxRadioBox::SetMajorDim(int majorDim)
180{
181 wxCHECK_RET( majorDim != 0, _T("major radiobox dimension can't be 0") );
182
183 m_majorDim = majorDim;
184
185 int minorDim = (GetCount() + m_majorDim - 1) / m_majorDim;
186
187 if ( GetWindowStyle() & wxRA_SPECIFY_COLS )
188 {
189 m_numCols = majorDim;
190 m_numRows = minorDim;
191 }
192 else // wxRA_SPECIFY_ROWS
193 {
194 m_numCols = minorDim;
195 m_numRows = majorDim;
196 }
197}
198
199void wxRadioBox::Append(int count, const wxString *choices)
200{
201 if ( !count )
202 return;
203
204 wxWindow *parent = GetParent();
205 m_buttons.Alloc(count);
206 for ( int n = 0; n < count; n++ )
207 {
208 // make the first button in the box the start of new group by giving it
209 // wxRB_GROUP style
210 wxRadioButton *btn = new wxRadioButton(parent, -1, choices[n],
211 wxDefaultPosition,
212 wxDefaultSize,
213 n == 0 ? wxRB_GROUP : 0);
214
215 // we want to get the events from the buttons to translate it into
216 btn->PushEventHandler(new wxRadioHookHandler(this));
217 m_buttons.Add(btn);
218 }
219}
220
221// ----------------------------------------------------------------------------
222// selection
223// ----------------------------------------------------------------------------
224
225void wxRadioBox::SetSelection(int n)
226{
227 wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::SetSelection") );
228
229 m_selection = n;
230
231 wxRadioButton *btn = m_buttons[n];
232
233 // the selected button is always focused in the radiobox
234 btn->SetFocus();
235
236 // this will also unselect the previously selected button in our group
237 btn->SetValue(TRUE);
238}
239
240int wxRadioBox::GetSelection() const
241{
242 return m_selection;
243}
244
245void wxRadioBox::SendRadioEvent()
246{
247 wxCHECK_RET( m_selection != -1, _T("no active radio button") );
248
249 wxCommandEvent event(wxEVT_COMMAND_RADIOBOX_SELECTED, GetId());
250 InitCommandEvent(event);
251 event.SetInt(m_selection);
252 event.SetString(GetString(m_selection));
253
254 Command(event);
255}
256
257void wxRadioBox::OnRadioButton(wxEvent& event)
258{
259 int n = m_buttons.Index((wxRadioButton *)event.GetEventObject());
260 wxCHECK_RET( n != wxNOT_FOUND, _T("click from alien radio button") );
261
262 m_selection = n;
263
264 SendRadioEvent();
265}
266
267// ----------------------------------------------------------------------------
268// methods forwarded to the buttons
269// ----------------------------------------------------------------------------
270
271wxString wxRadioBox::GetString(int n) const
272{
273 wxCHECK_MSG( IsValid(n), _T(""),
274 _T("invalid index in wxRadioBox::GetString") );
275
276 return m_buttons[n]->GetLabel();
277}
278
279void wxRadioBox::SetString(int n, const wxString& label)
280{
281 wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::SetString") );
282
283 m_buttons[n]->SetLabel(label);
284}
285
286void wxRadioBox::Enable(int n, bool enable)
287{
288 wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::Enable") );
289
290 m_buttons[n]->Enable(enable);
291}
292
293void wxRadioBox::Show(int n, bool show)
294{
295 wxCHECK_RET( IsValid(n), _T("invalid index in wxRadioBox::Show") );
296
297 m_buttons[n]->Show(show);
298}
299
300// ----------------------------------------------------------------------------
301// methods forwarded to the static box
302// ----------------------------------------------------------------------------
303
304bool wxRadioBox::Enable(bool enable)
305{
306 return wxStaticBox::Enable(enable);
307}
308
309bool wxRadioBox::Show(bool show)
310{
311 return wxStaticBox::Show(show);
312}
313
314wxString wxRadioBox::GetLabel() const
315{
316 return wxStaticBox::GetLabel();
317}
318
319void wxRadioBox::SetLabel(const wxString& label)
320{
321 wxStaticBox::SetLabel(label);
322}
323
324// ----------------------------------------------------------------------------
325// buttons positioning
326// ----------------------------------------------------------------------------
327
328wxSize wxRadioBox::GetMaxButtonSize() const
329{
330 int widthMax, heightMax, width, height;
331 widthMax = heightMax = 0;
332
333 int count = GetCount();
334 for ( int n = 0; n < count; n++ )
335 {
336 m_buttons[n]->GetBestSize(&width, &height);
337
338 if ( width > widthMax )
339 widthMax = width;
340 if ( height > heightMax )
341 heightMax = height;
342 }
343
344 return wxSize(widthMax + BUTTON_BORDER_X, heightMax + BUTTON_BORDER_Y);
345}
346
347wxSize wxRadioBox::DoGetBestClientSize() const
348{
349 wxSize sizeBtn = GetMaxButtonSize();
350
351 sizeBtn.x *= m_numCols;
352 sizeBtn.y *= m_numRows;
353
354 // add a border around all buttons
355 sizeBtn.x += 2*BOX_BORDER_X;
356 sizeBtn.y += 2*BOX_BORDER_Y;
357
358 // account for the area taken by static box
359 wxRect rect = GetBorderGeometry();
360 sizeBtn.x += rect.x + rect.width;
361 sizeBtn.y += rect.y + rect.height;
362
363 return sizeBtn;
364}
365
366void wxRadioBox::DoMoveWindow(int x0, int y0, int width, int height)
367{
368 wxStaticBox::DoMoveWindow(x0, y0, width, height);
369
370 wxSize sizeBtn = GetMaxButtonSize();
371 wxPoint ptOrigin = GetBoxAreaOrigin();
372
373 x0 += ptOrigin.x + BOX_BORDER_X;
374 y0 += ptOrigin.y + BOX_BORDER_Y;
375
376 int x = x0,
377 y = y0;
378
379 int count = GetCount();
380 for ( int n = 0; n < count; n++ )
381 {
382 m_buttons[n]->SetSize(x, y, sizeBtn.x, sizeBtn.y);
383
384 if ( GetWindowStyle() & wxRA_TOPTOBOTTOM )
385 {
386 // from top to bottom
387 if ( (n + 1) % m_numRows )
388 {
389 // continue in this column
390 y += sizeBtn.y;
391 }
392 else
393 {
394 // start a new column
395 x += sizeBtn.x;
396 y = y0;
397 }
398 }
399 else // wxRA_LEFTTORIGHT: mirror the code above
400 {
401 // from left to right
402 if ( (n + 1) % m_numCols )
403 {
404 // continue in this row
405 x += sizeBtn.x;
406 }
407 else
408 {
409 // start a new row
410 y += sizeBtn.y;
411 x = x0;
412 }
413 }
414 }
415}
416
417// ----------------------------------------------------------------------------
418// keyboard navigation
419// ----------------------------------------------------------------------------
420
421bool wxRadioBox::OnKeyDown(wxKeyEvent& event)
422{
423 wxDirection dir;
424 switch ( event.GetKeyCode() )
425 {
426 case WXK_UP:
427 dir = wxUP;
428 break;
429
430 case WXK_LEFT:
431 dir = wxLEFT;
432 break;
433
434 case WXK_DOWN:
435 dir = wxDOWN;
436 break;
437
438 case WXK_RIGHT:
439 dir = wxRIGHT;
440 break;
441
442 default:
443 return FALSE;
444 }
445
446 int selOld = GetSelection();
447 int selNew = GetNextItem(selOld, dir, GetWindowStyle());
448 if ( selNew != selOld )
449 {
450 SetSelection(selNew);
451
452 // emulate the button click
453 SendRadioEvent();
454 }
455
456 return TRUE;
457}
458
459#endif // wxUSE_RADIOBOX
460