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