Define _CRT_NONSTDC_NO_WARNINGS for zlib compilation with MSVC.
[wxWidgets.git] / src / gtk / radiobox.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/radiobox.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_RADIOBOX
13
14 #include "wx/radiobox.h"
15
16 #if wxUSE_TOOLTIPS
17 #include "wx/tooltip.h"
18 #endif
19
20 #include <gtk/gtk.h>
21 #include "wx/gtk/private.h"
22 #include "wx/gtk/private/gtk2-compat.h"
23
24 #include <gdk/gdkkeysyms.h>
25 #if GTK_CHECK_VERSION(3,0,0)
26 #include <gdk/gdkkeysyms-compat.h>
27 #endif
28
29 //-----------------------------------------------------------------------------
30 // wxGTKRadioButtonInfo
31 //-----------------------------------------------------------------------------
32 // structure internally used by wxRadioBox to store its child buttons
33
34 class wxGTKRadioButtonInfo : public wxObject
35 {
36 public:
37 wxGTKRadioButtonInfo( GtkRadioButton * abutton, const wxRect & arect )
38 : button( abutton ), rect( arect ) {}
39
40 GtkRadioButton * button;
41 wxRect rect;
42 };
43
44 //-----------------------------------------------------------------------------
45 // data
46 //-----------------------------------------------------------------------------
47
48 #include "wx/listimpl.cpp"
49 WX_DEFINE_LIST( wxRadioBoxButtonsInfoList )
50
51 extern bool g_blockEventsOnDrag;
52
53 //-----------------------------------------------------------------------------
54 // "clicked"
55 //-----------------------------------------------------------------------------
56
57 extern "C" {
58 static void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioBox *rb )
59 {
60 if (g_blockEventsOnDrag) return;
61
62 if (!gtk_toggle_button_get_active(button)) return;
63
64 wxCommandEvent event( wxEVT_RADIOBOX, rb->GetId() );
65 event.SetInt( rb->GetSelection() );
66 event.SetString( rb->GetStringSelection() );
67 event.SetEventObject( rb );
68 rb->HandleWindowEvent(event);
69 }
70 }
71
72 //-----------------------------------------------------------------------------
73 // "key_press_event"
74 //-----------------------------------------------------------------------------
75
76 extern "C" {
77 static gint gtk_radiobox_keypress_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxRadioBox *rb )
78 {
79 if (g_blockEventsOnDrag) return FALSE;
80
81 if ( ((gdk_event->keyval == GDK_Tab) ||
82 (gdk_event->keyval == GDK_ISO_Left_Tab)) &&
83 rb->GetParent() && (rb->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
84 {
85 wxNavigationKeyEvent new_event;
86 new_event.SetEventObject( rb->GetParent() );
87 // GDK reports GDK_ISO_Left_Tab for SHIFT-TAB
88 new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
89 // CTRL-TAB changes the (parent) window, i.e. switch notebook page
90 new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) != 0 );
91 new_event.SetCurrentFocus( rb );
92 return rb->GetParent()->HandleWindowEvent(new_event);
93 }
94
95 if ((gdk_event->keyval != GDK_Up) &&
96 (gdk_event->keyval != GDK_Down) &&
97 (gdk_event->keyval != GDK_Left) &&
98 (gdk_event->keyval != GDK_Right))
99 {
100 return FALSE;
101 }
102
103 wxRadioBoxButtonsInfoList::compatibility_iterator node = rb->m_buttonsInfo.GetFirst();
104 while( node && GTK_WIDGET( node->GetData()->button ) != widget )
105 {
106 node = node->GetNext();
107 }
108 if (!node)
109 {
110 return FALSE;
111 }
112
113 if ((gdk_event->keyval == GDK_Up) ||
114 (gdk_event->keyval == GDK_Left))
115 {
116 if (node == rb->m_buttonsInfo.GetFirst())
117 node = rb->m_buttonsInfo.GetLast();
118 else
119 node = node->GetPrevious();
120 }
121 else
122 {
123 if (node == rb->m_buttonsInfo.GetLast())
124 node = rb->m_buttonsInfo.GetFirst();
125 else
126 node = node->GetNext();
127 }
128
129 GtkWidget *button = (GtkWidget*) node->GetData()->button;
130
131 gtk_widget_grab_focus( button );
132
133 return TRUE;
134 }
135 }
136
137 extern "C" {
138 static gint gtk_radiobutton_focus_out( GtkWidget * WXUNUSED(widget),
139 GdkEventFocus *WXUNUSED(event),
140 wxRadioBox *win )
141 {
142 // NB: This control is composed of several GtkRadioButton widgets and
143 // when focus changes from one of them to another in the same
144 // wxRadioBox, we get a focus-out event followed by focus-in for
145 // another GtkRadioButton owned by the same control. We don't want
146 // to generate two spurious wxEVT_SET_FOCUS events in this case,
147 // so we defer sending wx events until idle time.
148 win->GTKHandleFocusOut();
149
150 // never stop the signal emission, it seems to break the kbd handling
151 // inside the radiobox
152 return FALSE;
153 }
154 }
155
156 extern "C" {
157 static gint gtk_radiobutton_focus_in( GtkWidget * WXUNUSED(widget),
158 GdkEventFocus *WXUNUSED(event),
159 wxRadioBox *win )
160 {
161 win->GTKHandleFocusIn();
162
163 // never stop the signal emission, it seems to break the kbd handling
164 // inside the radiobox
165 return FALSE;
166 }
167 }
168
169 extern "C" {
170 static void gtk_radiobutton_size_allocate( GtkWidget *widget,
171 GtkAllocation * alloc,
172 wxRadioBox *win )
173 {
174 for ( wxRadioBoxButtonsInfoList::compatibility_iterator node = win->m_buttonsInfo.GetFirst();
175 node;
176 node = node->GetNext())
177 {
178 if (widget == GTK_WIDGET(node->GetData()->button))
179 {
180 const wxPoint origin = win->GetPosition();
181 wxRect rect = wxRect( alloc->x - origin.x, alloc->y - origin.y,
182 alloc->width, alloc->height );
183 node->GetData()->rect = rect;
184 break;
185 }
186 }
187 }
188 }
189
190
191 //-----------------------------------------------------------------------------
192 // wxRadioBox
193 //-----------------------------------------------------------------------------
194
195 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox,wxControl)
196
197 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id,
198 const wxString& title,
199 const wxPoint &pos, const wxSize &size,
200 const wxArrayString& choices, int majorDim,
201 long style, const wxValidator& validator,
202 const wxString &name )
203 {
204 wxCArrayString chs(choices);
205
206 return Create( parent, id, title, pos, size, chs.GetCount(),
207 chs.GetStrings(), majorDim, style, validator, name );
208 }
209
210 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title,
211 const wxPoint &pos, const wxSize &size,
212 int n, const wxString choices[], int majorDim,
213 long style, const wxValidator& validator,
214 const wxString &name )
215 {
216 if (!PreCreation( parent, pos, size ) ||
217 !CreateBase( parent, id, pos, size, style, validator, name ))
218 {
219 wxFAIL_MSG( wxT("wxRadioBox creation failed") );
220 return false;
221 }
222
223 m_widget = GTKCreateFrame(title);
224 g_object_ref(m_widget);
225 wxControl::SetLabel(title);
226 if ( HasFlag(wxNO_BORDER) )
227 {
228 // If we don't do this here, the wxNO_BORDER style is ignored in Show()
229 gtk_frame_set_shadow_type(GTK_FRAME(m_widget), GTK_SHADOW_NONE);
230 }
231
232
233 // majorDim may be 0 if all trailing parameters were omitted, so don't
234 // assert here but just use the correct value for it
235 SetMajorDim(majorDim == 0 ? n : majorDim, style);
236
237
238 unsigned int num_of_cols = GetColumnCount();
239 unsigned int num_of_rows = GetRowCount();
240
241 GtkRadioButton *rbtn = NULL;
242
243 GtkWidget *table = gtk_table_new( num_of_rows, num_of_cols, FALSE );
244 gtk_table_set_col_spacings( GTK_TABLE(table), 1 );
245 gtk_table_set_row_spacings( GTK_TABLE(table), 1 );
246 gtk_widget_show( table );
247 gtk_container_add( GTK_CONTAINER(m_widget), table );
248
249 wxString label;
250 GSList *radio_button_group = NULL;
251 for (unsigned int i = 0; i < (unsigned int)n; i++)
252 {
253 if ( i != 0 )
254 radio_button_group = gtk_radio_button_get_group( GTK_RADIO_BUTTON(rbtn) );
255
256 label.Empty();
257 for ( wxString::const_iterator pc = choices[i].begin();
258 pc != choices[i].end(); ++pc )
259 {
260 if ( *pc != wxT('&') )
261 label += *pc;
262 }
263
264 rbtn = GTK_RADIO_BUTTON( gtk_radio_button_new_with_label( radio_button_group, wxGTK_CONV( label ) ) );
265 gtk_widget_show( GTK_WIDGET(rbtn) );
266
267 g_signal_connect (rbtn, "key_press_event",
268 G_CALLBACK (gtk_radiobox_keypress_callback), this);
269
270 m_buttonsInfo.Append( new wxGTKRadioButtonInfo( rbtn, wxRect() ) );
271
272 if (HasFlag(wxRA_SPECIFY_COLS))
273 {
274 int left = i%num_of_cols;
275 int right = (i%num_of_cols) + 1;
276 int top = i/num_of_cols;
277 int bottom = (i/num_of_cols)+1;
278 gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(rbtn), left, right, top, bottom,
279 GTK_FILL, GTK_FILL, 1, 1 );
280 }
281 else
282 {
283 int left = i/num_of_rows;
284 int right = (i/num_of_rows) + 1;
285 int top = i%num_of_rows;
286 int bottom = (i%num_of_rows)+1;
287 gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(rbtn), left, right, top, bottom,
288 GTK_FILL, GTK_FILL, 1, 1 );
289 }
290
291 ConnectWidget( GTK_WIDGET(rbtn) );
292
293 if (!i)
294 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(rbtn), TRUE );
295
296 g_signal_connect (rbtn, "clicked",
297 G_CALLBACK (gtk_radiobutton_clicked_callback), this);
298 g_signal_connect (rbtn, "focus_in_event",
299 G_CALLBACK (gtk_radiobutton_focus_in), this);
300 g_signal_connect (rbtn, "focus_out_event",
301 G_CALLBACK (gtk_radiobutton_focus_out), this);
302 g_signal_connect (rbtn, "size_allocate",
303 G_CALLBACK (gtk_radiobutton_size_allocate), this);
304 }
305
306 m_parent->DoAddChild( this );
307
308 PostCreation(size);
309
310 return true;
311 }
312
313 wxRadioBox::~wxRadioBox()
314 {
315 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
316 while (node)
317 {
318 GtkWidget *button = GTK_WIDGET( node->GetData()->button );
319 GTKDisconnect(button);
320 gtk_widget_destroy( button );
321 node = node->GetNext();
322 }
323 WX_CLEAR_LIST( wxRadioBoxButtonsInfoList, m_buttonsInfo );
324 }
325
326 bool wxRadioBox::Show( bool show )
327 {
328 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
329
330 if (!wxControl::Show(show))
331 {
332 // nothing to do
333 return false;
334 }
335
336 if ( HasFlag(wxNO_BORDER) )
337 gtk_widget_hide( m_widget );
338
339 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
340 while (node)
341 {
342 GtkWidget *button = GTK_WIDGET( node->GetData()->button );
343
344 if (show)
345 gtk_widget_show( button );
346 else
347 gtk_widget_hide( button );
348
349 node = node->GetNext();
350 }
351
352 return true;
353 }
354
355 void wxRadioBox::SetSelection( int n )
356 {
357 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
358
359 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( n );
360
361 wxCHECK_RET( node, wxT("radiobox wrong index") );
362
363 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData()->button );
364
365 GtkDisableEvents();
366
367 gtk_toggle_button_set_active( button, 1 );
368
369 GtkEnableEvents();
370 }
371
372 int wxRadioBox::GetSelection(void) const
373 {
374 wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid radiobox") );
375
376 int count = 0;
377
378 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
379 while (node)
380 {
381 GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData()->button );
382 if (gtk_toggle_button_get_active(button)) return count;
383 count++;
384 node = node->GetNext();
385 }
386
387 wxFAIL_MSG( wxT("wxRadioBox none selected") );
388
389 return wxNOT_FOUND;
390 }
391
392 wxString wxRadioBox::GetString(unsigned int n) const
393 {
394 wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid radiobox") );
395
396 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( n );
397
398 wxCHECK_MSG( node, wxEmptyString, wxT("radiobox wrong index") );
399
400 GtkLabel* label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(node->GetData()->button)));
401
402 wxString str( wxGTK_CONV_BACK( gtk_label_get_text(label) ) );
403
404 return str;
405 }
406
407 void wxRadioBox::SetLabel( const wxString& label )
408 {
409 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
410
411 GTKSetLabelForFrame(GTK_FRAME(m_widget), label);
412 }
413
414 void wxRadioBox::SetString(unsigned int item, const wxString& label)
415 {
416 wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
417
418 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( item );
419
420 wxCHECK_RET( node, wxT("radiobox wrong index") );
421
422 GtkLabel* g_label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(node->GetData()->button)));
423
424 gtk_label_set_text( g_label, wxGTK_CONV( label ) );
425 }
426
427 bool wxRadioBox::Enable( bool enable )
428 {
429 if ( !wxControl::Enable( enable ) )
430 return false;
431
432 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
433 while (node)
434 {
435 GtkButton *button = GTK_BUTTON( node->GetData()->button );
436 GtkLabel *label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(button)));
437
438 gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
439 gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
440 node = node->GetNext();
441 }
442
443 if (enable)
444 GTKFixSensitivity();
445
446 return true;
447 }
448
449 bool wxRadioBox::Enable(unsigned int item, bool enable)
450 {
451 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
452
453 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( item );
454
455 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
456
457 GtkButton *button = GTK_BUTTON( node->GetData()->button );
458 GtkLabel *label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(button)));
459
460 gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
461 gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
462
463 return true;
464 }
465
466 bool wxRadioBox::IsItemEnabled(unsigned int item) const
467 {
468 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
469
470 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( item );
471
472 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
473
474 GtkButton *button = GTK_BUTTON( node->GetData()->button );
475
476 // don't use GTK_WIDGET_IS_SENSITIVE() here, we want to return true even if
477 // the parent radiobox is disabled
478 return gtk_widget_get_sensitive(GTK_WIDGET(button)) != 0;
479 }
480
481 bool wxRadioBox::Show(unsigned int item, bool show)
482 {
483 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
484
485 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( item );
486
487 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
488
489 GtkWidget *button = GTK_WIDGET( node->GetData()->button );
490
491 if (show)
492 gtk_widget_show( button );
493 else
494 gtk_widget_hide( button );
495
496 return true;
497 }
498
499 bool wxRadioBox::IsItemShown(unsigned int item) const
500 {
501 wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
502
503 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.Item( item );
504
505 wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
506
507 GtkButton *button = GTK_BUTTON( node->GetData()->button );
508
509 return gtk_widget_get_visible(GTK_WIDGET(button)) != 0;
510 }
511
512 unsigned int wxRadioBox::GetCount() const
513 {
514 return m_buttonsInfo.GetCount();
515 }
516
517 void wxRadioBox::GtkDisableEvents()
518 {
519 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
520 while (node)
521 {
522 g_signal_handlers_block_by_func(node->GetData()->button,
523 (gpointer)gtk_radiobutton_clicked_callback, this);
524
525 node = node->GetNext();
526 }
527 }
528
529 void wxRadioBox::GtkEnableEvents()
530 {
531 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
532 while (node)
533 {
534 g_signal_handlers_unblock_by_func(node->GetData()->button,
535 (gpointer)gtk_radiobutton_clicked_callback, this);
536
537 node = node->GetNext();
538 }
539 }
540
541 void wxRadioBox::DoApplyWidgetStyle(GtkRcStyle *style)
542 {
543 GTKFrameApplyWidgetStyle(GTK_FRAME(m_widget), style);
544
545 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
546 while (node)
547 {
548 GtkWidget *widget = GTK_WIDGET( node->GetData()->button );
549
550 GTKApplyStyle(widget, style);
551 GTKApplyStyle(gtk_bin_get_child(GTK_BIN(widget)), style);
552
553 node = node->GetNext();
554 }
555 }
556
557 bool wxRadioBox::GTKWidgetNeedsMnemonic() const
558 {
559 return true;
560 }
561
562 void wxRadioBox::GTKWidgetDoSetMnemonic(GtkWidget* w)
563 {
564 GTKFrameSetMnemonicWidget(GTK_FRAME(m_widget), w);
565 }
566
567 #if wxUSE_TOOLTIPS
568 void wxRadioBox::GTKApplyToolTip(const char* tip)
569 {
570 // set this tooltip for all radiobuttons which don't have their own tips
571 unsigned n = 0;
572 for ( wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
573 node;
574 node = node->GetNext(), n++ )
575 {
576 if ( !GetItemToolTip(n) )
577 {
578 wxToolTip::GTKApply(GTK_WIDGET(node->GetData()->button), tip);
579 }
580 }
581 }
582
583 void wxRadioBox::DoSetItemToolTip(unsigned int n, wxToolTip *tooltip)
584 {
585 wxCharBuffer buf;
586 if ( !tooltip )
587 tooltip = GetToolTip();
588 if ( tooltip )
589 buf = wxGTK_CONV(tooltip->GetTip());
590
591 wxToolTip::GTKApply(GTK_WIDGET(m_buttonsInfo[n]->button), buf);
592 }
593
594 #endif // wxUSE_TOOLTIPS
595
596 GdkWindow *wxRadioBox::GTKGetWindow(wxArrayGdkWindows& windows) const
597 {
598 windows.push_back(gtk_widget_get_window(m_widget));
599
600 wxRadioBoxButtonsInfoList::compatibility_iterator node = m_buttonsInfo.GetFirst();
601 while (node)
602 {
603 GtkWidget *button = GTK_WIDGET( node->GetData()->button );
604
605 // don't put NULL pointers in the 'windows' array!
606 if (gtk_widget_get_window(button))
607 windows.push_back(gtk_widget_get_window(button));
608
609 node = node->GetNext();
610 }
611
612 return NULL;
613 }
614
615 // static
616 wxVisualAttributes
617 wxRadioBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
618 {
619 return GetDefaultAttributesFromGTKWidget(gtk_radio_button_new_with_label(NULL, ""));
620 }
621
622 int wxRadioBox::GetItemFromPoint(const wxPoint& point) const
623 {
624 const wxPoint pt = ScreenToClient(point);
625 unsigned n = 0;
626 for ( wxRadioBoxButtonsInfoList::compatibility_iterator
627 node = m_buttonsInfo.GetFirst(); node; node = node->GetNext(), n++ )
628 {
629 if ( m_buttonsInfo[n]->rect.Contains(pt) )
630 return n;
631 }
632
633 return wxNOT_FOUND;
634 }
635
636 #endif // wxUSE_RADIOBOX