1. moved m_majorDim duplicated in many ports to wxRadioBoxBase
[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 #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::Show(int n, bool show)
308 {
309 wxCHECK_MSG( IsValid(n), false, _T("invalid index in wxRadioBox::Show") );
310
311 return m_buttons[n]->Show(show);
312 }
313
314 // ----------------------------------------------------------------------------
315 // methods forwarded to the static box
316 // ----------------------------------------------------------------------------
317
318 bool wxRadioBox::Enable(bool enable)
319 {
320 if ( !wxStaticBox::Enable(enable) )
321 return false;
322
323 // also enable/disable the buttons
324 size_t count = m_buttons.GetCount();
325 for ( size_t n = 0; n < count; n++ )
326 {
327 Enable(n, enable);
328 }
329
330 return true;
331 }
332
333 bool wxRadioBox::Show(bool show)
334 {
335 if ( !wxStaticBox::Show(show) )
336 return false;
337
338 // also show/hide the buttons
339 size_t count = m_buttons.GetCount();
340 for ( size_t n = 0; n < count; n++ )
341 {
342 Show(n, show);
343 }
344
345 return true;
346 }
347
348 wxString wxRadioBox::GetLabel() const
349 {
350 return wxStaticBox::GetLabel();
351 }
352
353 void wxRadioBox::SetLabel(const wxString& label)
354 {
355 wxStaticBox::SetLabel(label);
356 }
357
358 #if wxUSE_TOOLTIPS
359 void wxRadioBox::DoSetToolTip(wxToolTip *tooltip)
360 {
361 wxControl::DoSetToolTip(tooltip);
362
363 // Also set them for all Radio Buttons
364 size_t count = m_buttons.GetCount();
365 for ( size_t n = 0; n < count; n++ )
366 {
367 if (tooltip)
368 m_buttons[n]->SetToolTip(tooltip->GetTip());
369 else
370 m_buttons[n]->SetToolTip(NULL);
371 }
372 }
373 #endif // wxUSE_TOOLTIPS
374
375 // ----------------------------------------------------------------------------
376 // buttons positioning
377 // ----------------------------------------------------------------------------
378
379 wxSize wxRadioBox::GetMaxButtonSize() const
380 {
381 int widthMax, heightMax, width, height;
382 widthMax = heightMax = 0;
383
384 int count = GetCount();
385 for ( int n = 0; n < count; n++ )
386 {
387 m_buttons[n]->GetBestSize(&width, &height);
388
389 if ( width > widthMax )
390 widthMax = width;
391 if ( height > heightMax )
392 heightMax = height;
393 }
394
395 return wxSize(widthMax + BUTTON_BORDER_X, heightMax + BUTTON_BORDER_Y);
396 }
397
398 wxSize wxRadioBox::DoGetBestClientSize() const
399 {
400 wxSize sizeBtn = GetMaxButtonSize();
401
402 sizeBtn.x *= GetColumnCount();
403 sizeBtn.y *= GetRowCount();
404
405 // add a border around all buttons
406 sizeBtn.x += 2*BOX_BORDER_X;
407 sizeBtn.y += 2*BOX_BORDER_Y;
408
409 // account for the area taken by static box
410 wxRect rect = GetBorderGeometry();
411 sizeBtn.x += rect.x + rect.width;
412 sizeBtn.y += rect.y + rect.height;
413
414 return sizeBtn;
415 }
416
417 void wxRadioBox::DoMoveWindow(int x0, int y0, int width, int height)
418 {
419 wxStaticBox::DoMoveWindow(x0, y0, width, height);
420
421 wxSize sizeBtn = GetMaxButtonSize();
422 wxPoint ptOrigin = GetBoxAreaOrigin();
423 wxPoint clientOrigin = GetParent() ? GetParent()->GetClientAreaOrigin() : wxPoint(0,0);
424
425 x0 += ptOrigin.x + BOX_BORDER_X - clientOrigin.x;
426 y0 += ptOrigin.y + BOX_BORDER_Y - clientOrigin.y;
427
428 int x = x0,
429 y = y0;
430
431 int count = GetCount();
432 for ( int n = 0; n < count; n++ )
433 {
434 m_buttons[n]->SetSize(x, y, sizeBtn.x, sizeBtn.y);
435
436 if ( GetWindowStyle() & wxRA_TOPTOBOTTOM )
437 {
438 // from top to bottom
439 if ( (n + 1) % GetRowCount() )
440 {
441 // continue in this column
442 y += sizeBtn.y;
443 }
444 else
445 {
446 // start a new column
447 x += sizeBtn.x;
448 y = y0;
449 }
450 }
451 else // wxRA_LEFTTORIGHT: mirror the code above
452 {
453 // from left to right
454 if ( (n + 1) % GetColumnCount() )
455 {
456 // continue in this row
457 x += sizeBtn.x;
458 }
459 else
460 {
461 // start a new row
462 y += sizeBtn.y;
463 x = x0;
464 }
465 }
466 }
467 }
468
469 // ----------------------------------------------------------------------------
470 // keyboard navigation
471 // ----------------------------------------------------------------------------
472
473 bool wxRadioBox::OnKeyDown(wxKeyEvent& event)
474 {
475 wxDirection dir;
476 switch ( event.GetKeyCode() )
477 {
478 case WXK_UP:
479 dir = wxUP;
480 break;
481
482 case WXK_LEFT:
483 dir = wxLEFT;
484 break;
485
486 case WXK_DOWN:
487 dir = wxDOWN;
488 break;
489
490 case WXK_RIGHT:
491 dir = wxRIGHT;
492 break;
493
494 default:
495 return false;
496 }
497
498 int selOld = GetSelection();
499 int selNew = GetNextItem(selOld, dir, GetWindowStyle());
500 if ( selNew != selOld )
501 {
502 SetSelection(selNew);
503
504 // emulate the button click
505 SendRadioEvent();
506 }
507
508 return true;
509 }
510
511 #endif // wxUSE_RADIOBOX
512