1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk1/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 #ifndef WX_PRECOMP
17     #include "wx/log.h"
18     #include "wx/frame.h"
19     #include "wx/dialog.h"
20 #endif
21 
22 #include "wx/gtk1/private.h"
23 #include <gdk/gdkkeysyms.h>
24 
25 #include "wx/gtk1/win_gtk.h"
26 
27 //-----------------------------------------------------------------------------
28 // idle system
29 //-----------------------------------------------------------------------------
30 
31 extern void wxapp_install_idle_handler();
32 extern bool g_isIdle;
33 
34 //-----------------------------------------------------------------------------
35 // data
36 //-----------------------------------------------------------------------------
37 
38 extern bool          g_blockEventsOnDrag;
39 extern wxWindowGTK  *g_delayedFocus;
40 
41 //-----------------------------------------------------------------------------
42 // "clicked"
43 //-----------------------------------------------------------------------------
44 
45 extern "C" {
gtk_radiobutton_clicked_callback(GtkToggleButton * button,wxRadioBox * rb)46 static void gtk_radiobutton_clicked_callback( GtkToggleButton *button, wxRadioBox *rb )
47 {
48     if (g_isIdle) wxapp_install_idle_handler();
49 
50     if (!rb->m_hasVMT) return;
51     if (g_blockEventsOnDrag) return;
52 
53     if (!button->active) return;
54 
55     wxCommandEvent event( wxEVT_RADIOBOX, rb->GetId() );
56     event.SetInt( rb->GetSelection() );
57     event.SetString( rb->GetStringSelection() );
58     event.SetEventObject( rb );
59     rb->HandleWindowEvent(event);
60 }
61 }
62 
63 //-----------------------------------------------------------------------------
64 // "key_press_event"
65 //-----------------------------------------------------------------------------
66 
67 extern "C" {
gtk_radiobox_keypress_callback(GtkWidget * widget,GdkEventKey * gdk_event,wxRadioBox * rb)68 static gint gtk_radiobox_keypress_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxRadioBox *rb )
69 {
70     if (g_isIdle)
71         wxapp_install_idle_handler();
72 
73     if (!rb->m_hasVMT) return FALSE;
74     if (g_blockEventsOnDrag) return FALSE;
75 
76     if ((gdk_event->keyval != GDK_Up) &&
77         (gdk_event->keyval != GDK_Down) &&
78         (gdk_event->keyval != GDK_Left) &&
79         (gdk_event->keyval != GDK_Right))
80     {
81         return FALSE;
82     }
83 
84     wxList::compatibility_iterator node = rb->m_boxes.Find( (wxObject*) widget );
85     if (!node)
86     {
87         return FALSE;
88     }
89 
90     gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
91 
92     if ((gdk_event->keyval == GDK_Up) ||
93         (gdk_event->keyval == GDK_Left))
94     {
95         if (node == rb->m_boxes.GetFirst())
96             node = rb->m_boxes.GetLast();
97         else
98             node = node->GetPrevious();
99     }
100     else
101     {
102         if (node == rb->m_boxes.GetLast())
103             node = rb->m_boxes.GetFirst();
104         else
105             node = node->GetNext();
106     }
107 
108     GtkWidget *button = (GtkWidget*) node->GetData();
109 
110     gtk_widget_grab_focus( button );
111 
112     return TRUE;
113 }
114 }
115 
116 extern "C" {
gtk_radiobutton_focus_in(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxRadioBox * win)117 static gint gtk_radiobutton_focus_in( GtkWidget *WXUNUSED(widget),
118                                       GdkEvent *WXUNUSED(event),
119                                       wxRadioBox *win )
120 {
121     if ( win->m_lostFocus )
122     {
123         // no, we didn't really lose it
124         win->m_lostFocus = FALSE;
125     }
126     else if ( !win->m_hasFocus )
127     {
128         win->m_hasFocus = true;
129 
130         wxFocusEvent event( wxEVT_SET_FOCUS, win->GetId() );
131         event.SetEventObject( win );
132 
133         // never stop the signal emission, it seems to break the kbd handling
134         // inside the radiobox
135         (void)win->HandleWindowEvent( event );
136     }
137 
138     return FALSE;
139 }
140 }
141 
142 extern "C" {
gtk_radiobutton_focus_out(GtkWidget * WXUNUSED (widget),GdkEvent * WXUNUSED (event),wxRadioBox * win)143 static gint gtk_radiobutton_focus_out( GtkWidget *WXUNUSED(widget),
144                                        GdkEvent *WXUNUSED(event),
145                                        wxRadioBox *win )
146 {
147   //    wxASSERT_MSG( win->m_hasFocus, wxT("got focus out without any focus in?") );
148   // Replace with a warning, else we dump core a lot!
149   //  if (!win->m_hasFocus)
150   //      wxLogWarning(wxT("Radiobox got focus out without any focus in.") );
151 
152     // we might have lost the focus, but may be not - it may have just gone to
153     // another button in the same radiobox, so we'll check for it in the next
154     // idle iteration (leave m_hasFocus == true for now)
155     win->m_lostFocus = true;
156 
157     return FALSE;
158 }
159 }
160 
161 //-----------------------------------------------------------------------------
162 // wxRadioBox
163 //-----------------------------------------------------------------------------
164 
IMPLEMENT_DYNAMIC_CLASS(wxRadioBox,wxControl)165 IMPLEMENT_DYNAMIC_CLASS(wxRadioBox,wxControl)
166 
167 void wxRadioBox::Init()
168 {
169     m_needParent = true;
170     m_acceptsFocus = true;
171 
172     m_hasFocus =
173     m_lostFocus = false;
174 }
175 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,int majorDim,long style,const wxValidator & validator,const wxString & name)176 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id,
177                          const wxString& title,
178                          const wxPoint &pos, const wxSize &size,
179                          const wxArrayString& choices, int majorDim,
180                          long style, const wxValidator& validator,
181                          const wxString &name )
182 {
183     wxCArrayString chs(choices);
184 
185     return Create( parent, id, title, pos, size, chs.GetCount(),
186                    chs.GetStrings(), majorDim, style, validator, name );
187 }
188 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],int majorDim,long style,const wxValidator & validator,const wxString & name)189 bool wxRadioBox::Create( wxWindow *parent, wxWindowID id, const wxString& title,
190                          const wxPoint &pos, const wxSize &size,
191                          int n, const wxString choices[], int majorDim,
192                          long style, const wxValidator& validator,
193                          const wxString &name )
194 {
195     if (!PreCreation( parent, pos, size ) ||
196         !CreateBase( parent, id, pos, size, style, validator, name ))
197     {
198         wxFAIL_MSG( wxT("wxRadioBox creation failed") );
199         return false;
200     }
201 
202     m_widget = gtk_frame_new(NULL);
203     SetLabel(title);
204     if ( HasFlag(wxNO_BORDER) )
205     {
206         // If we don't do this here, the wxNO_BORDER style is ignored in Show()
207         gtk_frame_set_shadow_type(GTK_FRAME(m_widget), GTK_SHADOW_NONE);
208     }
209 
210     // majorDim may be 0 if all trailing parameters were omitted, so don't
211     // assert here but just use the correct value for it
212     SetMajorDim(majorDim == 0 ? n : majorDim, style);
213 
214 
215     unsigned int num_of_cols = GetColumnCount();
216     unsigned int num_of_rows = GetRowCount();
217 
218     GtkRadioButton *m_radio = NULL;
219 
220     GtkWidget *table = gtk_table_new( num_of_rows, num_of_cols, FALSE );
221     gtk_table_set_col_spacings( GTK_TABLE(table), 1 );
222     gtk_table_set_row_spacings( GTK_TABLE(table), 1 );
223     gtk_widget_show( table );
224     gtk_container_add( GTK_CONTAINER(m_widget), table );
225 
226     wxString label;
227     GSList *radio_button_group = NULL;
228     for (int i = 0; i < n; i++)
229     {
230         if ( i != 0 )
231             radio_button_group = gtk_radio_button_group( GTK_RADIO_BUTTON(m_radio) );
232 
233         label.Empty();
234         for ( const wxChar *pc = choices[i]; *pc; pc++ )
235         {
236             if ( *pc != wxT('&') )
237                 label += *pc;
238         }
239 
240         m_radio = GTK_RADIO_BUTTON( gtk_radio_button_new_with_label( radio_button_group, wxGTK_CONV( label ) ) );
241         gtk_widget_show( GTK_WIDGET(m_radio) );
242 
243         gtk_signal_connect( GTK_OBJECT(m_radio), "key_press_event",
244            GTK_SIGNAL_FUNC(gtk_radiobox_keypress_callback), (gpointer)this );
245 
246         m_boxes.Append( (wxObject*) m_radio );
247 
248         if (HasFlag(wxRA_SPECIFY_COLS))
249         {
250             int left = i%num_of_cols;
251             int right = (i%num_of_cols) + 1;
252             int top = i/num_of_cols;
253             int bottom = (i/num_of_cols)+1;
254             gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(m_radio), left, right, top, bottom,
255                   GTK_FILL, GTK_FILL, 1, 1 );
256         }
257         else
258         {
259             int left = i/num_of_rows;
260             int right = (i/num_of_rows) + 1;
261             int top = i%num_of_rows;
262             int bottom = (i%num_of_rows)+1;
263             gtk_table_attach( GTK_TABLE(table), GTK_WIDGET(m_radio), left, right, top, bottom,
264                   GTK_FILL, GTK_FILL, 1, 1 );
265         }
266 
267         ConnectWidget( GTK_WIDGET(m_radio) );
268 
269         if (!i) gtk_toggle_button_set_state( GTK_TOGGLE_BUTTON(m_radio), TRUE );
270 
271         gtk_signal_connect( GTK_OBJECT(m_radio), "clicked",
272             GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
273 
274         gtk_signal_connect( GTK_OBJECT(m_radio), "focus_in_event",
275             GTK_SIGNAL_FUNC(gtk_radiobutton_focus_in), (gpointer)this );
276 
277         gtk_signal_connect( GTK_OBJECT(m_radio), "focus_out_event",
278             GTK_SIGNAL_FUNC(gtk_radiobutton_focus_out), (gpointer)this );
279     }
280 
281     m_parent->DoAddChild( this );
282 
283     PostCreation(size);
284 
285     return true;
286 }
287 
~wxRadioBox()288 wxRadioBox::~wxRadioBox()
289 {
290     wxList::compatibility_iterator node = m_boxes.GetFirst();
291     while (node)
292     {
293         GtkWidget *button = GTK_WIDGET( node->GetData() );
294         gtk_widget_destroy( button );
295         node = node->GetNext();
296     }
297 }
298 
Show(bool show)299 bool wxRadioBox::Show(bool show)
300 {
301     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
302 
303     if (!wxControl::Show(show))
304     {
305         // nothing to do
306         return false;
307     }
308 
309     if ( HasFlag(wxNO_BORDER) )
310         gtk_widget_hide( m_widget );
311 
312     wxList::compatibility_iterator node = m_boxes.GetFirst();
313     while (node)
314     {
315         GtkWidget *button = GTK_WIDGET( node->GetData() );
316 
317         if (show)
318             gtk_widget_show( button );
319         else
320             gtk_widget_hide( button );
321 
322         node = node->GetNext();
323     }
324 
325     return true;
326 }
327 
SetFocus()328 void wxRadioBox::SetFocus()
329 {
330     wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
331 
332     if (m_boxes.GetCount() == 0) return;
333 
334     wxList::compatibility_iterator node = m_boxes.GetFirst();
335     while (node)
336     {
337         GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
338         if (button->active)
339         {
340             gtk_widget_grab_focus( GTK_WIDGET(button) );
341             return;
342         }
343         node = node->GetNext();
344     }
345 }
346 
SetSelection(int n)347 void wxRadioBox::SetSelection( int n )
348 {
349     wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
350 
351     wxList::compatibility_iterator node = m_boxes.Item( n );
352 
353     wxCHECK_RET( node, wxT("radiobox wrong index") );
354 
355     GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
356 
357     GtkDisableEvents();
358 
359     gtk_toggle_button_set_active( button, 1 );
360 
361     GtkEnableEvents();
362 }
363 
GetSelection(void) const364 int wxRadioBox::GetSelection(void) const
365 {
366     wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid radiobox") );
367 
368     int count = 0;
369 
370     wxList::compatibility_iterator node = m_boxes.GetFirst();
371     while (node)
372     {
373         GtkToggleButton *button = GTK_TOGGLE_BUTTON( node->GetData() );
374         if (button->active) return count;
375         count++;
376         node = node->GetNext();
377     }
378 
379     wxFAIL_MSG( wxT("wxRadioBox none selected") );
380 
381     return wxNOT_FOUND;
382 }
383 
GetString(unsigned int n) const384 wxString wxRadioBox::GetString(unsigned int n) const
385 {
386     wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid radiobox") );
387 
388     wxList::compatibility_iterator node = m_boxes.Item( n );
389 
390     wxCHECK_MSG( node, wxEmptyString, wxT("radiobox wrong index") );
391 
392     GtkLabel *label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
393 
394     wxString str( label->label );
395 
396     return str;
397 }
398 
SetLabel(const wxString & label)399 void wxRadioBox::SetLabel( const wxString& label )
400 {
401     wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
402 
403     GTKSetLabelForFrame(GTK_FRAME(m_widget), label);
404 }
405 
SetString(unsigned int item,const wxString & label)406 void wxRadioBox::SetString(unsigned int item, const wxString& label)
407 {
408     wxCHECK_RET( m_widget != NULL, wxT("invalid radiobox") );
409 
410     wxList::compatibility_iterator node = m_boxes.Item( item );
411 
412     wxCHECK_RET( node, wxT("radiobox wrong index") );
413 
414     GtkLabel *g_label = GTK_LABEL( BUTTON_CHILD(node->GetData()) );
415 
416     gtk_label_set( g_label, wxGTK_CONV( label ) );
417 }
418 
Enable(bool enable)419 bool wxRadioBox::Enable( bool enable )
420 {
421     if ( !wxControl::Enable( enable ) )
422         return false;
423 
424     wxList::compatibility_iterator node = m_boxes.GetFirst();
425     while (node)
426     {
427         GtkButton *button = GTK_BUTTON( node->GetData() );
428         GtkLabel *label = GTK_LABEL( BUTTON_CHILD(button) );
429 
430         gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
431         gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
432         node = node->GetNext();
433     }
434 
435     return true;
436 }
437 
Enable(unsigned int item,bool enable)438 bool wxRadioBox::Enable(unsigned int item, bool enable)
439 {
440     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
441 
442     wxList::compatibility_iterator node = m_boxes.Item( item );
443 
444     wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
445 
446     GtkButton *button = GTK_BUTTON( node->GetData() );
447     GtkLabel *label = GTK_LABEL( BUTTON_CHILD(button) );
448 
449     gtk_widget_set_sensitive( GTK_WIDGET(button), enable );
450     gtk_widget_set_sensitive( GTK_WIDGET(label), enable );
451 
452     return true;
453 }
454 
IsItemEnabled(unsigned int item) const455 bool wxRadioBox::IsItemEnabled(unsigned int item) const
456 {
457     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
458 
459     wxList::compatibility_iterator node = m_boxes.Item( item );
460 
461     wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
462 
463     GtkButton *button = GTK_BUTTON( node->GetData() );
464 
465     // don't use GTK_WIDGET_IS_SENSITIVE() here, we want to return true even if
466     // the parent radiobox is disabled
467     return GTK_WIDGET_SENSITIVE(GTK_WIDGET(button));
468 }
469 
Show(unsigned int item,bool show)470 bool wxRadioBox::Show(unsigned int item, bool show)
471 {
472     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
473 
474     wxList::compatibility_iterator node = m_boxes.Item( item );
475 
476     wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
477 
478     GtkWidget *button = GTK_WIDGET( node->GetData() );
479 
480     if (show)
481         gtk_widget_show( button );
482     else
483         gtk_widget_hide( button );
484 
485     return true;
486 }
487 
IsItemShown(unsigned int item) const488 bool wxRadioBox::IsItemShown(unsigned int item) const
489 {
490     wxCHECK_MSG( m_widget != NULL, false, wxT("invalid radiobox") );
491 
492     wxList::compatibility_iterator node = m_boxes.Item( item );
493 
494     wxCHECK_MSG( node, false, wxT("radiobox wrong index") );
495 
496     GtkButton *button = GTK_BUTTON( node->GetData() );
497 
498     return GTK_WIDGET_VISIBLE(GTK_WIDGET(button));
499 }
500 
GetCount() const501 unsigned int wxRadioBox::GetCount() const
502 {
503     return m_boxes.GetCount();
504 }
505 
GtkDisableEvents()506 void wxRadioBox::GtkDisableEvents()
507 {
508     wxList::compatibility_iterator node = m_boxes.GetFirst();
509     while (node)
510     {
511         gtk_signal_disconnect_by_func( GTK_OBJECT(node->GetData()),
512            GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
513 
514         node = node->GetNext();
515     }
516 }
517 
GtkEnableEvents()518 void wxRadioBox::GtkEnableEvents()
519 {
520     wxList::compatibility_iterator node = m_boxes.GetFirst();
521     while (node)
522     {
523         gtk_signal_connect( GTK_OBJECT(node->GetData()), "clicked",
524            GTK_SIGNAL_FUNC(gtk_radiobutton_clicked_callback), (gpointer*)this );
525 
526         node = node->GetNext();
527     }
528 }
529 
DoApplyWidgetStyle(GtkRcStyle * style)530 void wxRadioBox::DoApplyWidgetStyle(GtkRcStyle *style)
531 {
532     gtk_widget_modify_style( m_widget, style );
533 
534     wxList::compatibility_iterator node = m_boxes.GetFirst();
535     while (node)
536     {
537         GtkWidget *widget = GTK_WIDGET( node->GetData() );
538 
539         gtk_widget_modify_style( widget, style );
540         gtk_widget_modify_style( BUTTON_CHILD(node->GetData()), style );
541 
542         node = node->GetNext();
543     }
544 }
545 
546 #if wxUSE_TOOLTIPS
ApplyToolTip(GtkTooltips * tips,const wxChar * tip)547 void wxRadioBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip )
548 {
549     wxList::compatibility_iterator node = m_boxes.GetFirst();
550     while (node)
551     {
552         GtkWidget *widget = GTK_WIDGET( node->GetData() );
553         gtk_tooltips_set_tip( tips, widget, wxConvCurrent->cWX2MB(tip), NULL );
554         node = node->GetNext();
555     }
556 }
557 #endif // wxUSE_TOOLTIPS
558 
IsOwnGtkWindow(GdkWindow * window)559 bool wxRadioBox::IsOwnGtkWindow( GdkWindow *window )
560 {
561     if (window == m_widget->window)
562         return true;
563 
564     wxList::compatibility_iterator node = m_boxes.GetFirst();
565     while (node)
566     {
567         GtkWidget *button = GTK_WIDGET( node->GetData() );
568 
569         if (window == button->window)
570             return true;
571 
572         node = node->GetNext();
573     }
574 
575     return false;
576 }
577 
OnInternalIdle()578 void wxRadioBox::OnInternalIdle()
579 {
580     if ( m_lostFocus )
581     {
582         m_hasFocus = false;
583         m_lostFocus = false;
584 
585         wxFocusEvent event( wxEVT_KILL_FOCUS, GetId() );
586         event.SetEventObject( this );
587 
588         (void)HandleWindowEvent( event );
589     }
590 
591     if (g_delayedFocus == this)
592     {
593         if (GTK_WIDGET_REALIZED(m_widget))
594         {
595             g_delayedFocus = NULL;
596             SetFocus();
597         }
598     }
599 }
600 
601 // static
602 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))603 wxRadioBox::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
604 {
605     wxVisualAttributes attr;
606     // NB: we need toplevel window so that GTK+ can find the right style
607     GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
608     GtkWidget* widget = gtk_radio_button_new_with_label(NULL, "");
609     gtk_container_add(GTK_CONTAINER(wnd), widget);
610     attr = GetDefaultAttributesFromGTKWidget(widget);
611     gtk_widget_destroy(wnd);
612     return attr;
613 }
614 
615 #endif // wxUSE_RADIOBOX
616