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