1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 
27 #include "gtkradiobutton.h"
28 
29 #include "gtkcontainerprivate.h"
30 #include "gtkbuttonprivate.h"
31 #include "gtktogglebuttonprivate.h"
32 #include "gtkcheckbuttonprivate.h"
33 #include "gtklabel.h"
34 #include "gtkmarshalers.h"
35 #include "gtkprivate.h"
36 #include "gtkintl.h"
37 #include "a11y/gtkradiobuttonaccessible.h"
38 #include "gtkstylecontextprivate.h"
39 
40 /**
41  * SECTION:gtkradiobutton
42  * @Short_description: A choice from multiple check buttons
43  * @Title: GtkRadioButton
44  * @See_also: #GtkComboBox
45  *
46  * A single radio button performs the same basic function as a #GtkCheckButton,
47  * as its position in the object hierarchy reflects. It is only when multiple
48  * radio buttons are grouped together that they become a different user
49  * interface component in their own right.
50  *
51  * Every radio button is a member of some group of radio buttons. When one is
52  * selected, all other radio buttons in the same group are deselected. A
53  * #GtkRadioButton is one way of giving the user a choice from many options.
54  *
55  * Radio button widgets are created with gtk_radio_button_new(), passing %NULL
56  * as the argument if this is the first radio button in a group. In subsequent
57  * calls, the group you wish to add this button to should be passed as an
58  * argument. Optionally, gtk_radio_button_new_with_label() can be used if you
59  * want a text label on the radio button.
60  *
61  * Alternatively, when adding widgets to an existing group of radio buttons,
62  * use gtk_radio_button_new_from_widget() with a #GtkRadioButton that already
63  * has a group assigned to it. The convenience function
64  * gtk_radio_button_new_with_label_from_widget() is also provided.
65  *
66  * To retrieve the group a #GtkRadioButton is assigned to, use
67  * gtk_radio_button_get_group().
68  *
69  * To remove a #GtkRadioButton from one group and make it part of a new one,
70  * use gtk_radio_button_set_group().
71  *
72  * The group list does not need to be freed, as each #GtkRadioButton will remove
73  * itself and its list item when it is destroyed.
74  *
75  * # CSS nodes
76  *
77  * |[<!-- language="plain" -->
78  * radiobutton
79  * ├── radio
80  * ╰── <child>
81  * ]|
82  *
83  * A GtkRadioButton with indicator (see gtk_toggle_button_set_mode()) has a
84  * main CSS node with name radiobutton and a subnode with name radio.
85  *
86  * |[<!-- language="plain" -->
87  * button.radio
88  * ├── radio
89  * ╰── <child>
90  * ]|
91  *
92  * A GtkRadioButton without indicator changes the name of its main node
93  * to button and adds a .radio style class to it. The subnode is invisible
94  * in this case.
95  *
96  * ## How to create a group of two radio buttons.
97  *
98  * |[<!-- language="C" -->
99  * void create_radio_buttons (void) {
100  *
101  *    GtkWidget *window, *radio1, *radio2, *box, *entry;
102  *    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
103  *    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
104  *    gtk_box_set_homogeneous (GTK_BOX (box), TRUE);
105  *
106  *    // Create a radio button with a GtkEntry widget
107  *    radio1 = gtk_radio_button_new (NULL);
108  *    entry = gtk_entry_new ();
109  *    gtk_container_add (GTK_CONTAINER (radio1), entry);
110  *
111  *
112  *    // Create a radio button with a label
113  *    radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1),
114  *                                                          "I’m the second radio button.");
115  *
116  *    // Pack them into a box, then show all the widgets
117  *    gtk_box_pack_start (GTK_BOX (box), radio1);
118  *    gtk_box_pack_start (GTK_BOX (box), radio2);
119  *    gtk_container_add (GTK_CONTAINER (window), box);
120  *    gtk_widget_show_all (window);
121  *    return;
122  * }
123  * ]|
124  *
125  * When an unselected button in the group is clicked the clicked button
126  * receives the #GtkToggleButton::toggled signal, as does the previously
127  * selected button.
128  * Inside the #GtkToggleButton::toggled handler, gtk_toggle_button_get_active()
129  * can be used to determine if the button has been selected or deselected.
130  */
131 
132 
133 struct _GtkRadioButtonPrivate
134 {
135   GSList *group;
136 };
137 
138 enum {
139   PROP_0,
140   PROP_GROUP,
141   LAST_PROP
142 };
143 
144 static GParamSpec *radio_button_props[LAST_PROP] = { NULL, };
145 
146 static void     gtk_radio_button_destroy        (GtkWidget           *widget);
147 static gboolean gtk_radio_button_focus          (GtkWidget           *widget,
148 						 GtkDirectionType     direction);
149 static void     gtk_radio_button_clicked        (GtkButton           *button);
150 static void     gtk_radio_button_set_property   (GObject             *object,
151 						 guint                prop_id,
152 						 const GValue        *value,
153 						 GParamSpec          *pspec);
154 static void     gtk_radio_button_get_property   (GObject             *object,
155 						 guint                prop_id,
156 						 GValue              *value,
157 						 GParamSpec          *pspec);
158 
159 G_DEFINE_TYPE_WITH_PRIVATE (GtkRadioButton, gtk_radio_button, GTK_TYPE_CHECK_BUTTON)
160 
161 static guint group_changed_signal = 0;
162 
163 static void
gtk_radio_button_class_init(GtkRadioButtonClass * class)164 gtk_radio_button_class_init (GtkRadioButtonClass *class)
165 {
166   GObjectClass *gobject_class;
167   GtkButtonClass *button_class;
168   GtkWidgetClass *widget_class;
169 
170   gobject_class = G_OBJECT_CLASS (class);
171   widget_class = (GtkWidgetClass*) class;
172   button_class = (GtkButtonClass*) class;
173 
174   gobject_class->set_property = gtk_radio_button_set_property;
175   gobject_class->get_property = gtk_radio_button_get_property;
176 
177   /**
178    * GtkRadioButton:group:
179    *
180    * Sets a new group for a radio button.
181    */
182   radio_button_props[PROP_GROUP] =
183       g_param_spec_object ("group",
184                            P_("Group"),
185                            P_("The radio button whose group this widget belongs to."),
186                            GTK_TYPE_RADIO_BUTTON,
187                            GTK_PARAM_WRITABLE);
188 
189   g_object_class_install_properties (gobject_class, LAST_PROP, radio_button_props);
190 
191   widget_class->destroy = gtk_radio_button_destroy;
192   widget_class->focus = gtk_radio_button_focus;
193 
194   button_class->clicked = gtk_radio_button_clicked;
195 
196   class->group_changed = NULL;
197 
198   /**
199    * GtkRadioButton::group-changed:
200    * @button: the object which received the signal
201    *
202    * Emitted when the group of radio buttons that a radio button belongs
203    * to changes. This is emitted when a radio button switches from
204    * being alone to being part of a group of 2 or more buttons, or
205    * vice-versa, and when a button is moved from one group of 2 or
206    * more buttons to a different one, but not when the composition
207    * of the group that a button belongs to changes.
208    *
209    * Since: 2.4
210    */
211   group_changed_signal = g_signal_new (I_("group-changed"),
212 				       G_OBJECT_CLASS_TYPE (gobject_class),
213 				       G_SIGNAL_RUN_FIRST,
214 				       G_STRUCT_OFFSET (GtkRadioButtonClass, group_changed),
215 				       NULL, NULL,
216 				       NULL,
217 				       G_TYPE_NONE, 0);
218 
219   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RADIO_BUTTON_ACCESSIBLE);
220   gtk_widget_class_set_css_name (widget_class, "radiobutton");
221 }
222 
223 static void
gtk_radio_button_init(GtkRadioButton * radio_button)224 gtk_radio_button_init (GtkRadioButton *radio_button)
225 {
226   GtkRadioButtonPrivate *priv;
227   GtkCssNode *css_node;
228 
229   radio_button->priv = gtk_radio_button_get_instance_private (radio_button);
230   priv = radio_button->priv;
231 
232   gtk_widget_set_receives_default (GTK_WIDGET (radio_button), FALSE);
233 
234   _gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
235 
236   priv->group = g_slist_prepend (NULL, radio_button);
237 
238   css_node = gtk_check_button_get_indicator_node (GTK_CHECK_BUTTON (radio_button));
239   gtk_css_node_set_name (css_node, I_("radio"));
240 }
241 
242 static void
gtk_radio_button_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)243 gtk_radio_button_set_property (GObject      *object,
244 			       guint         prop_id,
245 			       const GValue *value,
246 			       GParamSpec   *pspec)
247 {
248   GtkRadioButton *radio_button;
249 
250   radio_button = GTK_RADIO_BUTTON (object);
251 
252   switch (prop_id)
253     {
254       GSList *slist;
255       GtkRadioButton *button;
256 
257     case PROP_GROUP:
258         button = g_value_get_object (value);
259 
260       if (button)
261 	slist = gtk_radio_button_get_group (button);
262       else
263 	slist = NULL;
264       gtk_radio_button_set_group (radio_button, slist);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269     }
270 }
271 
272 static void
gtk_radio_button_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)273 gtk_radio_button_get_property (GObject    *object,
274 			       guint       prop_id,
275 			       GValue     *value,
276 			       GParamSpec *pspec)
277 {
278   switch (prop_id)
279     {
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
282       break;
283     }
284 }
285 
286 /**
287  * gtk_radio_button_set_group:
288  * @radio_button: a #GtkRadioButton.
289  * @group: (element-type GtkRadioButton) (allow-none): an existing radio
290  *     button group, such as one returned from gtk_radio_button_get_group(), or %NULL.
291  *
292  * Sets a #GtkRadioButton’s group. It should be noted that this does not change
293  * the layout of your interface in any way, so if you are changing the group,
294  * it is likely you will need to re-arrange the user interface to reflect these
295  * changes.
296  */
297 void
gtk_radio_button_set_group(GtkRadioButton * radio_button,GSList * group)298 gtk_radio_button_set_group (GtkRadioButton *radio_button,
299 			    GSList         *group)
300 {
301   GtkRadioButtonPrivate *priv;
302   GtkWidget *old_group_singleton = NULL;
303   GtkWidget *new_group_singleton = NULL;
304 
305   g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
306 
307   if (g_slist_find (group, radio_button))
308     return;
309 
310   priv = radio_button->priv;
311 
312   if (priv->group)
313     {
314       GSList *slist;
315 
316       priv->group = g_slist_remove (priv->group, radio_button);
317 
318       if (priv->group && !priv->group->next)
319 	old_group_singleton = g_object_ref (priv->group->data);
320 
321       for (slist = priv->group; slist; slist = slist->next)
322 	{
323 	  GtkRadioButton *tmp_button;
324 
325 	  tmp_button = slist->data;
326 
327 	  tmp_button->priv->group = priv->group;
328 	}
329     }
330 
331   if (group && !group->next)
332     new_group_singleton = g_object_ref (group->data);
333 
334   priv->group = g_slist_prepend (group, radio_button);
335 
336   if (group)
337     {
338       GSList *slist;
339 
340       for (slist = group; slist; slist = slist->next)
341 	{
342 	  GtkRadioButton *tmp_button;
343 
344 	  tmp_button = slist->data;
345 
346 	  tmp_button->priv->group = priv->group;
347 	}
348     }
349 
350   g_object_ref (radio_button);
351 
352   g_object_notify_by_pspec (G_OBJECT (radio_button), radio_button_props[PROP_GROUP]);
353   g_signal_emit (radio_button, group_changed_signal, 0);
354   if (old_group_singleton)
355     {
356       g_signal_emit (old_group_singleton, group_changed_signal, 0);
357       g_object_unref (old_group_singleton);
358     }
359   if (new_group_singleton)
360     {
361       g_signal_emit (new_group_singleton, group_changed_signal, 0);
362       g_object_unref (new_group_singleton);
363     }
364 
365   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), group == NULL);
366 
367   g_object_unref (radio_button);
368 }
369 
370 /**
371  * gtk_radio_button_join_group:
372  * @radio_button: the #GtkRadioButton object
373  * @group_source: (allow-none): a radio button object whos group we are
374  *   joining, or %NULL to remove the radio button from its group
375  *
376  * Joins a #GtkRadioButton object to the group of another #GtkRadioButton object
377  *
378  * Use this in language bindings instead of the gtk_radio_button_get_group()
379  * and gtk_radio_button_set_group() methods
380  *
381  * A common way to set up a group of radio buttons is the following:
382  * |[<!-- language="C" -->
383  *   GtkRadioButton *radio_button;
384  *   GtkRadioButton *last_button;
385  *
386  *   while (some_condition)
387  *     {
388  *        radio_button = gtk_radio_button_new (NULL);
389  *
390  *        gtk_radio_button_join_group (radio_button, last_button);
391  *        last_button = radio_button;
392  *     }
393  * ]|
394  *
395  * Since: 3.0
396  */
397 void
gtk_radio_button_join_group(GtkRadioButton * radio_button,GtkRadioButton * group_source)398 gtk_radio_button_join_group (GtkRadioButton *radio_button,
399 			     GtkRadioButton *group_source)
400 {
401   g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
402   g_return_if_fail (group_source == NULL || GTK_IS_RADIO_BUTTON (group_source));
403 
404   if (group_source)
405     {
406       GSList *group;
407       group = gtk_radio_button_get_group (group_source);
408 
409       if (!group)
410         {
411           /* if we are not already part of a group we need to set up a new one
412              and then get the newly created group */
413           gtk_radio_button_set_group (group_source, NULL);
414           group = gtk_radio_button_get_group (group_source);
415         }
416 
417       gtk_radio_button_set_group (radio_button, group);
418     }
419   else
420     {
421       gtk_radio_button_set_group (radio_button, NULL);
422     }
423 }
424 
425 /**
426  * gtk_radio_button_new:
427  * @group: (element-type GtkRadioButton) (allow-none): an existing
428  *         radio button group, or %NULL if you are creating a new group.
429  *
430  * Creates a new #GtkRadioButton. To be of any practical value, a widget should
431  * then be packed into the radio button.
432  *
433  * Returns: a new radio button
434  */
435 GtkWidget*
gtk_radio_button_new(GSList * group)436 gtk_radio_button_new (GSList *group)
437 {
438   GtkRadioButton *radio_button;
439 
440   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, NULL);
441 
442   if (group)
443     gtk_radio_button_set_group (radio_button, group);
444 
445   return GTK_WIDGET (radio_button);
446 }
447 
448 /**
449  * gtk_radio_button_new_with_label:
450  * @group: (element-type GtkRadioButton) (allow-none): an existing
451  *         radio button group, or %NULL if you are creating a new group.
452  * @label: the text label to display next to the radio button.
453  *
454  * Creates a new #GtkRadioButton with a text label.
455  *
456  * Returns: a new radio button.
457  */
458 GtkWidget*
gtk_radio_button_new_with_label(GSList * group,const gchar * label)459 gtk_radio_button_new_with_label (GSList      *group,
460 				 const gchar *label)
461 {
462   GtkWidget *radio_button;
463 
464   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "label", label, NULL) ;
465 
466   if (group)
467     gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
468 
469   return radio_button;
470 }
471 
472 
473 /**
474  * gtk_radio_button_new_with_mnemonic:
475  * @group: (element-type GtkRadioButton) (allow-none): the radio button
476  *         group, or %NULL
477  * @label: the text of the button, with an underscore in front of the
478  *         mnemonic character
479  *
480  * Creates a new #GtkRadioButton containing a label, adding it to the same
481  * group as @group. The label will be created using
482  * gtk_label_new_with_mnemonic(), so underscores in @label indicate the
483  * mnemonic for the button.
484  *
485  * Returns: a new #GtkRadioButton
486  */
487 GtkWidget*
gtk_radio_button_new_with_mnemonic(GSList * group,const gchar * label)488 gtk_radio_button_new_with_mnemonic (GSList      *group,
489 				    const gchar *label)
490 {
491   GtkWidget *radio_button;
492 
493   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON,
494 			       "label", label,
495 			       "use-underline", TRUE,
496 			       NULL);
497 
498   if (group)
499     gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
500 
501   return radio_button;
502 }
503 
504 /**
505  * gtk_radio_button_new_from_widget: (constructor)
506  * @radio_group_member: (allow-none): an existing #GtkRadioButton.
507  *
508  * Creates a new #GtkRadioButton, adding it to the same group as
509  * @radio_group_member. As with gtk_radio_button_new(), a widget
510  * should be packed into the radio button.
511  *
512  * Returns: (transfer none): a new radio button.
513  */
514 GtkWidget*
gtk_radio_button_new_from_widget(GtkRadioButton * radio_group_member)515 gtk_radio_button_new_from_widget (GtkRadioButton *radio_group_member)
516 {
517   GSList *l = NULL;
518   if (radio_group_member)
519     l = gtk_radio_button_get_group (radio_group_member);
520   return gtk_radio_button_new (l);
521 }
522 
523 /**
524  * gtk_radio_button_new_with_label_from_widget: (constructor)
525  * @radio_group_member: (allow-none): widget to get radio group from or %NULL
526  * @label: a text string to display next to the radio button.
527  *
528  * Creates a new #GtkRadioButton with a text label, adding it to
529  * the same group as @radio_group_member.
530  *
531  * Returns: (transfer none): a new radio button.
532  */
533 GtkWidget*
gtk_radio_button_new_with_label_from_widget(GtkRadioButton * radio_group_member,const gchar * label)534 gtk_radio_button_new_with_label_from_widget (GtkRadioButton *radio_group_member,
535 					     const gchar    *label)
536 {
537   GSList *l = NULL;
538   if (radio_group_member)
539     l = gtk_radio_button_get_group (radio_group_member);
540   return gtk_radio_button_new_with_label (l, label);
541 }
542 
543 /**
544  * gtk_radio_button_new_with_mnemonic_from_widget: (constructor)
545  * @radio_group_member: (allow-none): widget to get radio group from or %NULL
546  * @label: the text of the button, with an underscore in front of the
547  *         mnemonic character
548  *
549  * Creates a new #GtkRadioButton containing a label. The label
550  * will be created using gtk_label_new_with_mnemonic(), so underscores
551  * in @label indicate the mnemonic for the button.
552  *
553  * Returns: (transfer none): a new #GtkRadioButton
554  **/
555 GtkWidget*
gtk_radio_button_new_with_mnemonic_from_widget(GtkRadioButton * radio_group_member,const gchar * label)556 gtk_radio_button_new_with_mnemonic_from_widget (GtkRadioButton *radio_group_member,
557 					        const gchar    *label)
558 {
559   GSList *l = NULL;
560   if (radio_group_member)
561     l = gtk_radio_button_get_group (radio_group_member);
562   return gtk_radio_button_new_with_mnemonic (l, label);
563 }
564 
565 
566 /**
567  * gtk_radio_button_get_group:
568  * @radio_button: a #GtkRadioButton.
569  *
570  * Retrieves the group assigned to a radio button.
571  *
572  * Returns: (element-type GtkRadioButton) (transfer none): a linked list
573  * containing all the radio buttons in the same group
574  * as @radio_button. The returned list is owned by the radio button
575  * and must not be modified or freed.
576  */
577 GSList*
gtk_radio_button_get_group(GtkRadioButton * radio_button)578 gtk_radio_button_get_group (GtkRadioButton *radio_button)
579 {
580   g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_button), NULL);
581 
582   return radio_button->priv->group;
583 }
584 
585 
586 static void
gtk_radio_button_destroy(GtkWidget * widget)587 gtk_radio_button_destroy (GtkWidget *widget)
588 {
589   GtkWidget *old_group_singleton = NULL;
590   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
591   GtkRadioButtonPrivate *priv = radio_button->priv;
592   GtkRadioButton *tmp_button;
593   GSList *tmp_list;
594   gboolean was_in_group;
595 
596   was_in_group = priv->group && priv->group->next;
597 
598   priv->group = g_slist_remove (priv->group, radio_button);
599   if (priv->group && !priv->group->next)
600     old_group_singleton = priv->group->data;
601 
602   tmp_list = priv->group;
603 
604   while (tmp_list)
605     {
606       tmp_button = tmp_list->data;
607       tmp_list = tmp_list->next;
608 
609       tmp_button->priv->group = priv->group;
610     }
611 
612   /* this button is no longer in the group */
613   priv->group = NULL;
614 
615   if (old_group_singleton)
616     g_signal_emit (old_group_singleton, group_changed_signal, 0);
617   if (was_in_group)
618     g_signal_emit (radio_button, group_changed_signal, 0);
619 
620   GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->destroy (widget);
621 }
622 
623 static gboolean
gtk_radio_button_focus(GtkWidget * widget,GtkDirectionType direction)624 gtk_radio_button_focus (GtkWidget         *widget,
625 			GtkDirectionType   direction)
626 {
627   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
628   GtkRadioButtonPrivate *priv = radio_button->priv;
629   GSList *tmp_slist;
630 
631   /* Radio buttons with draw_indicator unset focus "normally", since
632    * they look like buttons to the user.
633    */
634   if (!gtk_toggle_button_get_mode (GTK_TOGGLE_BUTTON (widget)))
635     return GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->focus (widget, direction);
636 
637   if (gtk_widget_is_focus (widget))
638     {
639       GList *children, *focus_list, *tmp_list;
640       GtkWidget *toplevel;
641       GtkWidget *new_focus = NULL;
642       GSList *l;
643 
644       if (direction == GTK_DIR_TAB_FORWARD ||
645           direction == GTK_DIR_TAB_BACKWARD)
646         return FALSE;
647 
648       toplevel = gtk_widget_get_toplevel (widget);
649       children = NULL;
650       for (l = priv->group; l; l = l->next)
651         children = g_list_prepend (children, l->data);
652 
653       focus_list = _gtk_container_focus_sort (GTK_CONTAINER (toplevel), children, direction, widget);
654       tmp_list = g_list_find (focus_list, widget);
655 
656       if (tmp_list)
657 	{
658 	  tmp_list = tmp_list->next;
659 
660 	  while (tmp_list)
661 	    {
662 	      GtkWidget *child = tmp_list->data;
663 
664 	      if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
665 		{
666 		  new_focus = child;
667 		  break;
668 		}
669 
670 	      tmp_list = tmp_list->next;
671 	    }
672 	}
673 
674       if (!new_focus)
675 	{
676 	  tmp_list = focus_list;
677 
678 	  while (tmp_list)
679 	    {
680 	      GtkWidget *child = tmp_list->data;
681 
682 	      if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
683 		{
684 		  new_focus = child;
685 		  break;
686 		}
687 
688 	      tmp_list = tmp_list->next;
689 	    }
690 	}
691 
692       g_list_free (focus_list);
693       g_list_free (children);
694 
695       if (new_focus)
696 	{
697 	  gtk_widget_grab_focus (new_focus);
698 
699           gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
700 	}
701 
702       return TRUE;
703     }
704   else
705     {
706       GtkRadioButton *selected_button = NULL;
707 
708       /* We accept the focus if, we don't have the focus and
709        *  - we are the currently active button in the group
710        *  - there is no currently active radio button.
711        */
712       tmp_slist = priv->group;
713       while (tmp_slist)
714 	{
715 	  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tmp_slist->data)) &&
716 	      gtk_widget_get_visible (tmp_slist->data))
717 	    selected_button = tmp_slist->data;
718 	  tmp_slist = tmp_slist->next;
719 	}
720 
721       if (selected_button && selected_button != radio_button)
722 	return FALSE;
723 
724       gtk_widget_grab_focus (widget);
725       return TRUE;
726     }
727 }
728 
729 static void
gtk_radio_button_clicked(GtkButton * button)730 gtk_radio_button_clicked (GtkButton *button)
731 {
732   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (button);
733   GtkRadioButtonPrivate *priv = radio_button->priv;
734   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
735   GtkToggleButton *tmp_button;
736   GSList *tmp_list;
737   gint toggled;
738 
739   toggled = FALSE;
740 
741   g_object_ref (GTK_WIDGET (button));
742 
743   if (gtk_toggle_button_get_active (toggle_button))
744     {
745       tmp_button = NULL;
746       tmp_list = priv->group;
747 
748       while (tmp_list)
749 	{
750 	  tmp_button = tmp_list->data;
751 	  tmp_list = tmp_list->next;
752 
753           if (tmp_button != toggle_button &&
754               gtk_toggle_button_get_active (tmp_button))
755 	    break;
756 
757 	  tmp_button = NULL;
758 	}
759 
760       if (tmp_button)
761 	{
762 	  toggled = TRUE;
763           _gtk_toggle_button_set_active (toggle_button,
764                                          !gtk_toggle_button_get_active (toggle_button));
765 	}
766     }
767   else
768     {
769       toggled = TRUE;
770       _gtk_toggle_button_set_active (toggle_button,
771                                      !gtk_toggle_button_get_active (toggle_button));
772 
773       tmp_list = priv->group;
774       while (tmp_list)
775 	{
776 	  tmp_button = tmp_list->data;
777 	  tmp_list = tmp_list->next;
778 
779 	  if (gtk_toggle_button_get_active (tmp_button) && (tmp_button != toggle_button))
780 	    {
781 	      gtk_button_clicked (GTK_BUTTON (tmp_button));
782 	      break;
783 	    }
784 	}
785     }
786 
787   if (toggled)
788     {
789       gtk_toggle_button_toggled (toggle_button);
790 
791       g_object_notify (G_OBJECT (toggle_button), "active");
792     }
793 
794   gtk_widget_queue_draw (GTK_WIDGET (button));
795 
796   g_object_unref (button);
797 }
798