The rounded corners look really dumb at this size.
[wxWidgets.git] / src / osx / radiobox_osx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/radiobox_osx.cpp
3 // Purpose: wxRadioBox
4 // Author: Stefan Csomor
5 // Modified by: JS Lair (99/11/15) first implementation
6 // Created: 1998-01-01
7 // Copyright: (c) Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12
13 #if wxUSE_RADIOBOX
14
15 #include "wx/radiobox.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/radiobut.h"
19 #include "wx/arrstr.h"
20 #endif
21
22 #include "wx/osx/private.h"
23
24 // regarding layout: note that there are differences in inter-control
25 // spacing between InterfaceBuild and the Human Interface Guidelines, we stick
26 // to the latter, as those are also used eg in the System Preferences Dialogs
27
28 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox, wxControl)
29
30
31 BEGIN_EVENT_TABLE(wxRadioBox, wxControl)
32 EVT_RADIOBUTTON( wxID_ANY , wxRadioBox::OnRadioButton )
33 END_EVENT_TABLE()
34
35
36 void wxRadioBox::OnRadioButton( wxCommandEvent &outer )
37 {
38 if ( outer.IsChecked() )
39 {
40 wxCommandEvent event( wxEVT_RADIOBOX, m_windowId );
41 int i = GetSelection() ;
42 event.SetInt(i);
43 event.SetString(GetString(i));
44 event.SetEventObject( this );
45 ProcessCommand(event);
46 }
47 }
48
49 wxRadioBox::wxRadioBox()
50 {
51 m_noItems = 0;
52 m_noRowsOrCols = 0;
53 m_radioButtonCycle = NULL;
54 }
55
56 wxRadioBox::~wxRadioBox()
57 {
58 SendDestroyEvent();
59
60 wxRadioButton *next, *current;
61
62 current = m_radioButtonCycle->NextInCycle();
63 if (current != NULL)
64 {
65 while (current != m_radioButtonCycle)
66 {
67 next = current->NextInCycle();
68 delete current;
69
70 current = next;
71 }
72
73 delete current;
74 }
75 }
76
77 // Create the radiobox for two-step construction
78
79 bool wxRadioBox::Create( wxWindow *parent,
80 wxWindowID id, const wxString& label,
81 const wxPoint& pos, const wxSize& size,
82 const wxArrayString& choices,
83 int majorDim, long style,
84 const wxValidator& val, const wxString& name )
85 {
86 wxCArrayString chs(choices);
87
88 return Create(
89 parent, id, label, pos, size, chs.GetCount(),
90 chs.GetStrings(), majorDim, style, val, name);
91 }
92
93 bool wxRadioBox::Create( wxWindow *parent,
94 wxWindowID id, const wxString& label,
95 const wxPoint& pos, const wxSize& size,
96 int n, const wxString choices[],
97 int majorDim, long style,
98 const wxValidator& val, const wxString& name )
99 {
100 DontCreatePeer();
101
102 if ( !wxControl::Create( parent, id, pos, size, style, val, name ) )
103 return false;
104
105 // during construction we must keep this at 0, otherwise GetBestSize fails
106 m_noItems = 0;
107 m_noRowsOrCols = majorDim;
108 m_radioButtonCycle = NULL;
109
110 SetMajorDim( majorDim == 0 ? n : majorDim, style );
111
112 m_labelOrig = m_label = label;
113
114 SetPeer(wxWidgetImpl::CreateGroupBox( this, parent, id, label, pos, size, style, GetExtraStyle() ));
115
116 for (int i = 0; i < n; i++)
117 {
118 wxRadioButton *radBtn = new wxRadioButton(
119 this,
120 wxID_ANY,
121 GetLabelText(choices[i]),
122 wxPoint( 5, 20 * i + 10 ),
123 wxDefaultSize,
124 i == 0 ? wxRB_GROUP : 0 );
125
126 if ( i == 0 )
127 m_radioButtonCycle = radBtn;
128 // m_radioButtonCycle = radBtn->AddInCycle( m_radioButtonCycle );
129 }
130
131 // as all radiobuttons have been set-up, set the correct dimensions
132 m_noItems = (unsigned int)n;
133 SetMajorDim( majorDim == 0 ? n : majorDim, style );
134
135 SetSelection( 0 );
136 InvalidateBestSize();
137 SetInitialSize( size );
138 MacPostControlCreate( pos, size );
139
140 return true;
141 }
142
143 // Enables or disables the entire radiobox
144 //
145 bool wxRadioBox::Enable(bool enable)
146 {
147 wxRadioButton *current;
148
149 if (!wxControl::Enable( enable ))
150 return false;
151
152 current = m_radioButtonCycle;
153 for (unsigned int i = 0; i < m_noItems; i++)
154 {
155 current->Enable( enable );
156 current = current->NextInCycle();
157 }
158
159 return true;
160 }
161
162 // Enables or disables an given button
163 //
164 bool wxRadioBox::Enable(unsigned int item, bool enable)
165 {
166 if (!IsValid( item ))
167 return false;
168
169 unsigned int i = 0;
170 wxRadioButton *current = m_radioButtonCycle;
171 while (i != item)
172 {
173 i++;
174 current = current->NextInCycle();
175 }
176
177 return current->Enable( enable );
178 }
179
180 bool wxRadioBox::IsItemEnabled(unsigned int item) const
181 {
182 if (!IsValid( item ))
183 return false;
184
185 unsigned int i = 0;
186 wxRadioButton *current = m_radioButtonCycle;
187 while (i != item)
188 {
189 i++;
190 current = current->NextInCycle();
191 }
192
193 return current->IsEnabled();
194 }
195
196 // Returns the radiobox label
197 //
198 wxString wxRadioBox::GetLabel() const
199 {
200 return wxControl::GetLabel();
201 }
202
203 // Returns the label for the given button
204 //
205 wxString wxRadioBox::GetString(unsigned int item) const
206 {
207 wxRadioButton *current;
208
209 if (!IsValid( item ))
210 return wxEmptyString;
211
212 unsigned int i = 0;
213 current = m_radioButtonCycle;
214 while (i != item)
215 {
216 i++;
217 current = current->NextInCycle();
218 }
219
220 return current->GetLabel();
221 }
222
223 // Returns the zero-based position of the selected button
224 //
225 int wxRadioBox::GetSelection() const
226 {
227 int i;
228 wxRadioButton *current;
229
230 i = 0;
231 current = m_radioButtonCycle;
232 while (!current->GetValue())
233 {
234 i++;
235 current = current->NextInCycle();
236 }
237
238 return i;
239 }
240
241 // Sets the radiobox label
242 //
243 void wxRadioBox::SetLabel(const wxString& label)
244 {
245 return wxControl::SetLabel( label );
246 }
247
248 // Sets the label of a given button
249 //
250 void wxRadioBox::SetString(unsigned int item,const wxString& label)
251 {
252 if (!IsValid( item ))
253 return;
254
255 unsigned int i = 0;
256 wxRadioButton *current = m_radioButtonCycle;
257 while (i != item)
258 {
259 i++;
260 current = current->NextInCycle();
261 }
262
263 return current->SetLabel( label );
264 }
265
266 // Sets a button by passing the desired position. This does not cause
267 // wxEVT_RADIOBOX event to get emitted
268 //
269 void wxRadioBox::SetSelection(int item)
270 {
271 int i;
272 wxRadioButton *current;
273
274 if (!IsValid( item ))
275 return;
276
277 i = 0;
278 current = m_radioButtonCycle;
279 while (i != item)
280 {
281 i++;
282 current = current->NextInCycle();
283 }
284
285 current->SetValue( true );
286 }
287
288 // Shows or hides the entire radiobox
289 //
290 bool wxRadioBox::Show(bool show)
291 {
292 wxRadioButton *current;
293
294 current = m_radioButtonCycle;
295 for (unsigned int i=0; i<m_noItems; i++)
296 {
297 current->Show( show );
298 current = current->NextInCycle();
299 }
300
301 wxControl::Show( show );
302
303 return true;
304 }
305
306 // Shows or hides the given button
307 //
308 bool wxRadioBox::Show(unsigned int item, bool show)
309 {
310 if (!IsValid( item ))
311 return false;
312
313 unsigned int i = 0;
314 wxRadioButton *current = m_radioButtonCycle;
315 while (i != item)
316 {
317 i++;
318 current = current->NextInCycle();
319 }
320
321 return current->Show( show );
322 }
323
324 bool wxRadioBox::IsItemShown(unsigned int item) const
325 {
326 if (!IsValid( item ))
327 return false;
328
329 unsigned int i = 0;
330 wxRadioButton *current = m_radioButtonCycle;
331 while (i != item)
332 {
333 i++;
334 current = current->NextInCycle();
335 }
336
337 return current->IsShown();
338 }
339
340
341 // Simulates the effect of the user issuing a command to the item
342 //
343 void wxRadioBox::Command( wxCommandEvent& event )
344 {
345 SetSelection( event.GetInt() );
346 ProcessCommand( event );
347 }
348
349 // Sets the selected button to receive keyboard input
350 //
351 void wxRadioBox::SetFocus()
352 {
353 wxRadioButton *current;
354
355 current = m_radioButtonCycle;
356 while (!current->GetValue())
357 {
358 current = current->NextInCycle();
359 }
360
361 current->SetFocus();
362 }
363
364 // Simulates the effect of the user issuing a command to the item
365 //
366 #if wxOSX_USE_CARBON
367 #define RADIO_SIZE 20
368 #else
369 // Cocoa has an additional border are of about 3 pixels
370 #define RADIO_SIZE 23
371 #endif
372
373 void wxRadioBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
374 {
375 int i;
376 wxRadioButton *current;
377
378 // define the position
379
380 int x_current, y_current;
381 int x_offset, y_offset;
382 int widthOld, heightOld;
383
384 GetSize( &widthOld, &heightOld );
385 GetPosition( &x_current, &y_current );
386
387 x_offset = x;
388 y_offset = y;
389 if (!(sizeFlags & wxSIZE_ALLOW_MINUS_ONE))
390 {
391 if (x == wxDefaultCoord)
392 x_offset = x_current;
393 if (y == wxDefaultCoord)
394 y_offset = y_current;
395 }
396
397 // define size
398 int charWidth, charHeight;
399 int maxWidth, maxHeight;
400 int eachWidth[128], eachHeight[128];
401 int totWidth, totHeight;
402
403 GetTextExtent(
404 wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
405 &charWidth, &charHeight );
406
407 charWidth /= 52;
408
409 maxWidth = -1;
410 maxHeight = -1;
411 wxSize bestSizeRadio ;
412 if ( m_radioButtonCycle )
413 bestSizeRadio = m_radioButtonCycle->GetBestSize();
414
415 for (unsigned int i = 0 ; i < m_noItems; i++)
416 {
417 GetTextExtent(GetString(i), &eachWidth[i], &eachHeight[i] );
418 eachWidth[i] = eachWidth[i] + RADIO_SIZE;
419 eachHeight[i] = wxMax( eachHeight[i], bestSizeRadio.y );
420
421 if (maxWidth < eachWidth[i])
422 maxWidth = eachWidth[i];
423 if (maxHeight < eachHeight[i])
424 maxHeight = eachHeight[i];
425 }
426
427 // according to HIG (official space - 3 Pixels Diff between Frame and Layout size)
428 int space = 3;
429 if ( GetWindowVariant() == wxWINDOW_VARIANT_MINI )
430 space = 2;
431
432 totHeight = GetRowCount() * maxHeight + (GetRowCount() - 1) * space;
433 totWidth = GetColumnCount() * (maxWidth + charWidth);
434
435 wxSize sz = DoGetSizeFromClientSize( wxSize( totWidth, totHeight ) ) ;
436
437 // change the width / height only when specified
438 if ( width == wxDefaultCoord )
439 {
440 if ( sizeFlags & wxSIZE_AUTO_WIDTH )
441 width = sz.x;
442 else
443 width = widthOld;
444 }
445
446 if ( height == wxDefaultCoord )
447 {
448 if ( sizeFlags & wxSIZE_AUTO_HEIGHT )
449 height = sz.y;
450 else
451 height = heightOld;
452 }
453
454 wxControl::DoSetSize( x_offset, y_offset, width, height, wxSIZE_AUTO );
455
456 // arrange radio buttons
457 int x_start, y_start;
458
459 x_start = ( width - sz.x ) / 2;
460 y_start = ( height - sz.y ) / 2;
461
462 x_offset = x_start;
463 y_offset = y_start;
464
465 current = m_radioButtonCycle;
466 for (i = 0 ; i < (int)m_noItems; i++)
467 {
468 // not to do for the zero button!
469 if ((i > 0) && ((i % GetMajorDim()) == 0))
470 {
471 if (m_windowStyle & wxRA_SPECIFY_ROWS)
472 {
473 x_offset += maxWidth + charWidth;
474 y_offset = y_start;
475 }
476 else
477 {
478 x_offset = x_start;
479 y_offset += maxHeight + space;
480 }
481 }
482
483 current->SetSize( x_offset, y_offset, eachWidth[i], eachHeight[i] );
484 current = current->NextInCycle();
485
486 if (m_windowStyle & wxRA_SPECIFY_ROWS)
487 y_offset += maxHeight + space;
488 else
489 x_offset += maxWidth + charWidth;
490 }
491 }
492
493 wxSize wxRadioBox::DoGetBestSize() const
494 {
495 int charWidth, charHeight;
496 int maxWidth, maxHeight;
497 int eachWidth, eachHeight;
498 int totWidth, totHeight;
499
500 wxFont font = GetFont(); // GetParent()->GetFont()
501 GetTextExtent(
502 wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"),
503 &charWidth, &charHeight, NULL, NULL, &font );
504
505 charWidth /= 52;
506
507 maxWidth = -1;
508 maxHeight = -1;
509
510 wxSize bestSizeRadio ;
511 if ( m_radioButtonCycle )
512 bestSizeRadio = m_radioButtonCycle->GetBestSize();
513
514 for (unsigned int i = 0 ; i < m_noItems; i++)
515 {
516 GetTextExtent(GetString(i), &eachWidth, &eachHeight, NULL, NULL, &font );
517 eachWidth = (eachWidth + RADIO_SIZE);
518 eachHeight = wxMax(eachHeight, bestSizeRadio.y );
519 if (maxWidth < eachWidth)
520 maxWidth = eachWidth;
521 if (maxHeight < eachHeight)
522 maxHeight = eachHeight;
523 }
524
525 // according to HIG (official space - 3 Pixels Diff between Frame and Layout size)
526 int space = 3;
527 if ( GetWindowVariant() == wxWINDOW_VARIANT_MINI )
528 space = 2;
529
530 totHeight = GetRowCount() * maxHeight + (GetRowCount() - 1) * space;
531 totWidth = GetColumnCount() * (maxWidth + charWidth);
532
533 wxSize sz = DoGetSizeFromClientSize( wxSize( totWidth, totHeight ) );
534 totWidth = sz.x;
535 totHeight = sz.y;
536
537 // optimum size is an additional 5 pt border to all sides
538 totWidth += 10;
539 totHeight += 10;
540
541 // handle radio box title as well
542 GetTextExtent( GetLabel(), &eachWidth, NULL );
543 eachWidth = (int)(eachWidth + RADIO_SIZE) + 3 * charWidth;
544 if (totWidth < eachWidth)
545 totWidth = eachWidth;
546
547 return wxSize( totWidth, totHeight );
548 }
549
550 bool wxRadioBox::SetFont(const wxFont& font)
551 {
552 bool retval = wxWindowBase::SetFont( font );
553
554 // dont' update the native control, it has its own small font
555
556 // should we iterate over the children ?
557
558 return retval;
559 }
560
561 #endif // wxUSE_RADIOBOX