]> git.saurik.com Git - wxWidgets.git/blob - src/univ/radiobox.cpp
another segfault fix
[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 license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
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 #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
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 m_majorDim = 0;
100 }
101
102 bool 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
160 wxRadioBox::~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
179 void 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
199 void 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
225 void 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
240 int wxRadioBox::GetSelection() const
241 {
242 return m_selection;
243 }
244
245 void 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
257 void 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
271 wxString 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
279 void 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
286 void 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
293 void 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
304 bool wxRadioBox::Enable(bool enable)
305 {
306 if ( !wxStaticBox::Enable(enable) )
307 return FALSE;
308
309 // also enable/disable the buttons
310 size_t count = m_buttons.GetCount();
311 for ( size_t n = 0; n < count; n++ )
312 {
313 Enable(n, enable);
314 }
315
316 return TRUE;
317 }
318
319 bool wxRadioBox::Show(bool show)
320 {
321 if ( !wxStaticBox::Show(show) )
322 return FALSE;
323
324 // also show/hide the buttons
325 size_t count = m_buttons.GetCount();
326 for ( size_t n = 0; n < count; n++ )
327 {
328 Show(n, show);
329 }
330
331 return TRUE;
332 }
333
334 wxString wxRadioBox::GetLabel() const
335 {
336 return wxStaticBox::GetLabel();
337 }
338
339 void wxRadioBox::SetLabel(const wxString& label)
340 {
341 wxStaticBox::SetLabel(label);
342 }
343
344 // ----------------------------------------------------------------------------
345 // buttons positioning
346 // ----------------------------------------------------------------------------
347
348 wxSize wxRadioBox::GetMaxButtonSize() const
349 {
350 int widthMax, heightMax, width, height;
351 widthMax = heightMax = 0;
352
353 int count = GetCount();
354 for ( int n = 0; n < count; n++ )
355 {
356 m_buttons[n]->GetBestSize(&width, &height);
357
358 if ( width > widthMax )
359 widthMax = width;
360 if ( height > heightMax )
361 heightMax = height;
362 }
363
364 return wxSize(widthMax + BUTTON_BORDER_X, heightMax + BUTTON_BORDER_Y);
365 }
366
367 wxSize wxRadioBox::DoGetBestClientSize() const
368 {
369 wxSize sizeBtn = GetMaxButtonSize();
370
371 sizeBtn.x *= m_numCols;
372 sizeBtn.y *= m_numRows;
373
374 // add a border around all buttons
375 sizeBtn.x += 2*BOX_BORDER_X;
376 sizeBtn.y += 2*BOX_BORDER_Y;
377
378 // account for the area taken by static box
379 wxRect rect = GetBorderGeometry();
380 sizeBtn.x += rect.x + rect.width;
381 sizeBtn.y += rect.y + rect.height;
382
383 return sizeBtn;
384 }
385
386 void wxRadioBox::DoMoveWindow(int x0, int y0, int width, int height)
387 {
388 wxStaticBox::DoMoveWindow(x0, y0, width, height);
389
390 wxSize sizeBtn = GetMaxButtonSize();
391 wxPoint ptOrigin = GetBoxAreaOrigin();
392
393 x0 += ptOrigin.x + BOX_BORDER_X;
394 y0 += ptOrigin.y + BOX_BORDER_Y;
395
396 int x = x0,
397 y = y0;
398
399 int count = GetCount();
400 for ( int n = 0; n < count; n++ )
401 {
402 m_buttons[n]->SetSize(x, y, sizeBtn.x, sizeBtn.y);
403
404 if ( GetWindowStyle() & wxRA_TOPTOBOTTOM )
405 {
406 // from top to bottom
407 if ( (n + 1) % m_numRows )
408 {
409 // continue in this column
410 y += sizeBtn.y;
411 }
412 else
413 {
414 // start a new column
415 x += sizeBtn.x;
416 y = y0;
417 }
418 }
419 else // wxRA_LEFTTORIGHT: mirror the code above
420 {
421 // from left to right
422 if ( (n + 1) % m_numCols )
423 {
424 // continue in this row
425 x += sizeBtn.x;
426 }
427 else
428 {
429 // start a new row
430 y += sizeBtn.y;
431 x = x0;
432 }
433 }
434 }
435 }
436
437 // ----------------------------------------------------------------------------
438 // keyboard navigation
439 // ----------------------------------------------------------------------------
440
441 bool wxRadioBox::OnKeyDown(wxKeyEvent& event)
442 {
443 wxDirection dir;
444 switch ( event.GetKeyCode() )
445 {
446 case WXK_UP:
447 dir = wxUP;
448 break;
449
450 case WXK_LEFT:
451 dir = wxLEFT;
452 break;
453
454 case WXK_DOWN:
455 dir = wxDOWN;
456 break;
457
458 case WXK_RIGHT:
459 dir = wxRIGHT;
460 break;
461
462 default:
463 return FALSE;
464 }
465
466 int selOld = GetSelection();
467 int selNew = GetNextItem(selOld, dir, GetWindowStyle());
468 if ( selNew != selOld )
469 {
470 SetSelection(selNew);
471
472 // emulate the button click
473 SendRadioEvent();
474 }
475
476 return TRUE;
477 }
478
479 #endif // wxUSE_RADIOBOX
480