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