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