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