]> Pileus Git - ~andy/gtk/blob - gtk/gtkradiobutton.c
Merge branch 'bgo593793-filechooser-recent-folders-master'
[~andy/gtk] / gtk / gtkradiobutton.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28
29 #include "gtkradiobutton.h"
30
31 #include "gtkbuttonprivate.h"
32 #include "gtklabel.h"
33 #include "gtkmarshalers.h"
34 #include "gtkprivate.h"
35 #include "gtkintl.h"
36 #include "a11y/gtkradiobuttonaccessible.h"
37
38 /**
39  * SECTION:gtkradiobutton
40  * @Short_description: A choice from multiple check buttons
41  * @Title: GtkRadioButton
42  * @See_also: #GtkComboBox
43  *
44  * A single radio button performs the same basic function as a #GtkCheckButton,
45  * as its position in the object hierarchy reflects. It is only when multiple
46  * radio buttons are grouped together that they become a different user
47  * interface component in their own right.
48  *
49  * Every radio button is a member of some group of radio buttons. When one is
50  * selected, all other radio buttons in the same group are deselected. A
51  * #GtkRadioButton is one way of giving the user a choice from many options.
52  *
53  * Radio button widgets are created with gtk_radio_button_new(), passing %NULL
54  * as the argument if this is the first radio button in a group. In subsequent
55  * calls, the group you wish to add this button to should be passed as an
56  * argument. Optionally, gtk_radio_button_new_with_label() can be used if you
57  * want a text label on the radio button.
58  *
59  * Alternatively, when adding widgets to an existing group of radio buttons,
60  * use gtk_radio_button_new_from_widget() with a #GtkRadioButton that already
61  * has a group assigned to it. The convenience function
62  * gtk_radio_button_new_with_label_from_widget() is also provided.
63  *
64  * To retrieve the group a #GtkRadioButton is assigned to, use
65  * gtk_radio_button_get_group().
66  *
67  * To remove a #GtkRadioButton from one group and make it part of a new one,
68  * use gtk_radio_button_set_group().
69  *
70  * The group list does not need to be freed, as each #GtkRadioButton will remove
71  * itself and its list item when it is destroyed.
72  *
73  * <example>
74  * <title>How to create a group of two radio buttons.</title>
75  * <programlisting>
76  * void create_radio_buttons (void) {
77  *
78  *    GtkWidget *window, *radio1, *radio2, *box, *entry;
79  *    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
80  *    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, TRUE, 2);
81  *
82  *    /&ast; Create a radio button with a GtkEntry widget &ast;/
83  *    radio1 = gtk_radio_button_new (NULL);
84  *    entry = gtk_entry_new (<!-- -->);
85  *    gtk_container_add (GTK_CONTAINER (radio1), entry);
86  *
87  *
88  *    /&ast; Create a radio button with a label &ast;/
89  *    radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1),
90  *                                                          "I'm the second radio button.");
91  *
92  *    /&ast; Pack them into a box, then show all the widgets &ast;/
93  *    gtk_box_pack_start (GTK_BOX (box), radio1, TRUE, TRUE, 2);
94  *    gtk_box_pack_start (GTK_BOX (box), radio2, TRUE, TRUE, 2);
95  *    gtk_container_add (GTK_CONTAINER (window), box);
96  *    gtk_widget_show_all (window);
97  *    return;
98  * }
99  * </programlisting>
100  * </example>
101  *
102  * When an unselected button in the group is clicked the clicked button
103  * receives the #GtkToggleButton::toggled signal, as does the previously
104  * selected button.
105  * Inside the #GtkToggleButton::toggled handler, gtk_toggle_button_get_active()
106  * can be used to determine if the button has been selected or deselected.
107  */
108
109
110 struct _GtkRadioButtonPrivate
111 {
112   GSList *group;
113 };
114
115 enum {
116   PROP_0,
117   PROP_GROUP
118 };
119
120
121 static void     gtk_radio_button_destroy        (GtkWidget           *widget);
122 static gboolean gtk_radio_button_focus          (GtkWidget           *widget,
123                                                  GtkDirectionType     direction);
124 static void     gtk_radio_button_clicked        (GtkButton           *button);
125 static void     gtk_radio_button_draw_indicator (GtkCheckButton      *check_button,
126                                                  cairo_t             *cr);
127 static void     gtk_radio_button_set_property   (GObject             *object,
128                                                  guint                prop_id,
129                                                  const GValue        *value,
130                                                  GParamSpec          *pspec);
131 static void     gtk_radio_button_get_property   (GObject             *object,
132                                                  guint                prop_id,
133                                                  GValue              *value,
134                                                  GParamSpec          *pspec);
135
136 G_DEFINE_TYPE (GtkRadioButton, gtk_radio_button, GTK_TYPE_CHECK_BUTTON)
137
138 static guint group_changed_signal = 0;
139
140 static void
141 gtk_radio_button_class_init (GtkRadioButtonClass *class)
142 {
143   GObjectClass *gobject_class;
144   GtkButtonClass *button_class;
145   GtkCheckButtonClass *check_button_class;
146   GtkWidgetClass *widget_class;
147
148   gobject_class = G_OBJECT_CLASS (class);
149   widget_class = (GtkWidgetClass*) class;
150   button_class = (GtkButtonClass*) class;
151   check_button_class = (GtkCheckButtonClass*) class;
152
153   gobject_class->set_property = gtk_radio_button_set_property;
154   gobject_class->get_property = gtk_radio_button_get_property;
155
156   /**
157    * GtkRadioButton:group:
158    *
159    * Sets a new group for a radio button.
160    */
161   g_object_class_install_property (gobject_class,
162                                    PROP_GROUP,
163                                    g_param_spec_object ("group",
164                                                         P_("Group"),
165                                                         P_("The radio button whose group this widget belongs to."),
166                                                         GTK_TYPE_RADIO_BUTTON,
167                                                         GTK_PARAM_WRITABLE));
168   widget_class->destroy = gtk_radio_button_destroy;
169   widget_class->focus = gtk_radio_button_focus;
170
171   button_class->clicked = gtk_radio_button_clicked;
172
173   check_button_class->draw_indicator = gtk_radio_button_draw_indicator;
174
175   class->group_changed = NULL;
176
177   /**
178    * GtkRadioButton::group-changed:
179    * @button: the object which received the signal
180    *
181    * Emitted when the group of radio buttons that a radio button belongs
182    * to changes. This is emitted when a radio button switches from
183    * being alone to being part of a group of 2 or more buttons, or
184    * vice-versa, and when a button is moved from one group of 2 or
185    * more buttons to a different one, but not when the composition
186    * of the group that a button belongs to changes.
187    *
188    * Since: 2.4
189    */
190   group_changed_signal = g_signal_new (I_("group-changed"),
191                                        G_OBJECT_CLASS_TYPE (gobject_class),
192                                        G_SIGNAL_RUN_FIRST,
193                                        G_STRUCT_OFFSET (GtkRadioButtonClass, group_changed),
194                                        NULL, NULL,
195                                        _gtk_marshal_VOID__VOID,
196                                        G_TYPE_NONE, 0);
197
198   g_type_class_add_private (class, sizeof (GtkRadioButtonPrivate));
199
200   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RADIO_BUTTON_ACCESSIBLE);
201 }
202
203 static void
204 gtk_radio_button_init (GtkRadioButton *radio_button)
205 {
206   GtkRadioButtonPrivate *priv;
207
208   radio_button->priv = G_TYPE_INSTANCE_GET_PRIVATE (radio_button,
209                                                     GTK_TYPE_RADIO_BUTTON,
210                                                     GtkRadioButtonPrivate);
211   priv = radio_button->priv;
212
213   gtk_widget_set_receives_default (GTK_WIDGET (radio_button), FALSE);
214
215   _gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), TRUE);
216
217   GTK_BUTTON (radio_button)->priv->depress_on_activate = FALSE;
218
219   priv->group = g_slist_prepend (NULL, radio_button);
220
221   _gtk_button_set_depressed (GTK_BUTTON (radio_button), TRUE);
222   gtk_widget_set_state_flags (GTK_WIDGET (radio_button), GTK_STATE_FLAG_ACTIVE, TRUE);
223 }
224
225 static void
226 gtk_radio_button_set_property (GObject      *object,
227                                guint         prop_id,
228                                const GValue *value,
229                                GParamSpec   *pspec)
230 {
231   GtkRadioButton *radio_button;
232
233   radio_button = GTK_RADIO_BUTTON (object);
234
235   switch (prop_id)
236     {
237       GSList *slist;
238       GtkRadioButton *button;
239
240     case PROP_GROUP:
241         button = g_value_get_object (value);
242
243       if (button)
244         slist = gtk_radio_button_get_group (button);
245       else
246         slist = NULL;
247       gtk_radio_button_set_group (radio_button, slist);
248       break;
249     default:
250       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251       break;
252     }
253 }
254
255 static void
256 gtk_radio_button_get_property (GObject    *object,
257                                guint       prop_id,
258                                GValue     *value,
259                                GParamSpec *pspec)
260 {
261   switch (prop_id)
262     {
263     default:
264       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
265       break;
266     }
267 }
268
269 /**
270  * gtk_radio_button_set_group:
271  * @radio_button: a #GtkRadioButton.
272  * @group: (transfer none) (element-type GtkRadioButton): an existing radio
273  *     button group, such as one returned from gtk_radio_button_get_group().
274  *
275  * Sets a #GtkRadioButton's group. It should be noted that this does not change
276  * the layout of your interface in any way, so if you are changing the group,
277  * it is likely you will need to re-arrange the user interface to reflect these
278  * changes.
279  */
280 void
281 gtk_radio_button_set_group (GtkRadioButton *radio_button,
282                             GSList         *group)
283 {
284   GtkRadioButtonPrivate *priv;
285   GtkWidget *old_group_singleton = NULL;
286   GtkWidget *new_group_singleton = NULL;
287
288   g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
289   g_return_if_fail (!g_slist_find (group, radio_button));
290
291   priv = radio_button->priv;
292
293   if (priv->group)
294     {
295       GSList *slist;
296
297       priv->group = g_slist_remove (priv->group, radio_button);
298
299       if (priv->group && !priv->group->next)
300         old_group_singleton = g_object_ref (priv->group->data);
301
302       for (slist = priv->group; slist; slist = slist->next)
303         {
304           GtkRadioButton *tmp_button;
305           
306           tmp_button = slist->data;
307
308           tmp_button->priv->group = priv->group;
309         }
310     }
311   
312   if (group && !group->next)
313     new_group_singleton = g_object_ref (group->data);
314
315   priv->group = g_slist_prepend (group, radio_button);
316
317   if (group)
318     {
319       GSList *slist;
320       
321       for (slist = 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   g_object_ref (radio_button);
332   
333   g_object_notify (G_OBJECT (radio_button), "group");
334   g_signal_emit (radio_button, group_changed_signal, 0);
335   if (old_group_singleton)
336     {
337       g_signal_emit (old_group_singleton, group_changed_signal, 0);
338       g_object_unref (old_group_singleton);
339     }
340   if (new_group_singleton)
341     {
342       g_signal_emit (new_group_singleton, group_changed_signal, 0);
343       g_object_unref (new_group_singleton);
344     }
345
346   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), group == NULL);
347
348   g_object_unref (radio_button);
349 }
350
351 /**
352  * gtk_radio_button_join_group:
353  * @radio_button: the #GtkRadioButton object
354  * @group_source: (allow-none): a radio button object whos group we are 
355  *   joining, or %NULL to remove the radio button from its group
356  *
357  * Joins a #GtkRadioButton object to the group of another #GtkRadioButton object
358  *
359  * Use this in language bindings instead of the gtk_radio_button_get_group() 
360  * and gtk_radio_button_set_group() methods
361  *
362  * A common way to set up a group of radio buttons is the following:
363  * |[
364  *   GtkRadioButton *radio_button;
365  *   GtkRadioButton *last_button;
366  *
367  *   while (/&ast; more buttons to add &ast;/)
368  *     {
369  *        radio_button = gtk_radio_button_new (...);
370  *
371  *        gtk_radio_button_join_group (radio_button, last_button);
372  *        last_button = radio_button;
373  *     }
374  * ]|
375  *
376  * Since: 3.0
377  */
378 void
379 gtk_radio_button_join_group (GtkRadioButton *radio_button, 
380                              GtkRadioButton *group_source)
381 {
382   g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
383   g_return_if_fail (group_source == NULL || GTK_IS_RADIO_BUTTON (group_source));
384
385   if (group_source)
386     {
387       GSList *group;
388       group = gtk_radio_button_get_group (group_source);
389
390       if (!group)
391         {
392           /* if we are not already part of a group we need to set up a new one
393              and then get the newly created group */
394           gtk_radio_button_set_group (group_source, NULL);
395           group = gtk_radio_button_get_group (group_source);
396         }
397
398       gtk_radio_button_set_group (radio_button, group);
399     }
400   else
401     {
402       gtk_radio_button_set_group (radio_button, NULL);
403     }
404 }
405
406 /**
407  * gtk_radio_button_new:
408  * @group: (element-type GtkRadioButton) (allow-none): an existing
409  *         radio button group, or %NULL if you are creating a new group.
410  *
411  * Creates a new #GtkRadioButton. To be of any practical value, a widget should
412  * then be packed into the radio button.
413  *
414  * Returns: a new radio button
415  */
416 GtkWidget*
417 gtk_radio_button_new (GSList *group)
418 {
419   GtkRadioButton *radio_button;
420
421   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, NULL);
422
423   if (group)
424     gtk_radio_button_set_group (radio_button, group);
425
426   return GTK_WIDGET (radio_button);
427 }
428
429 /**
430  * gtk_radio_button_new_with_label:
431  * @group: (element-type GtkRadioButton) (allow-none): an existing
432  *         radio button group, or %NULL if you are creating a new group.
433  * @label: the text label to display next to the radio button.
434  *
435  * Creates a new #GtkRadioButton with a text label.
436  *
437  * Returns: a new radio button.
438  */
439 GtkWidget*
440 gtk_radio_button_new_with_label (GSList      *group,
441                                  const gchar *label)
442 {
443   GtkWidget *radio_button;
444
445   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "label", label, NULL) ;
446
447   if (group)
448     gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
449
450   return radio_button;
451 }
452
453
454 /**
455  * gtk_radio_button_new_with_mnemonic:
456  * @group: (element-type GtkRadioButton) (allow-none): the radio button
457  *         group
458  * @label: the text of the button, with an underscore in front of the
459  *         mnemonic character
460  *
461  * Creates a new #GtkRadioButton containing a label, adding it to the same
462  * group as @group. The label will be created using
463  * gtk_label_new_with_mnemonic(), so underscores in @label indicate the
464  * mnemonic for the button.
465  *
466  * Returns: a new #GtkRadioButton
467  */
468 GtkWidget*
469 gtk_radio_button_new_with_mnemonic (GSList      *group,
470                                     const gchar *label)
471 {
472   GtkWidget *radio_button;
473
474   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, 
475                                "label", label, 
476                                "use-underline", TRUE, 
477                                NULL);
478
479   if (group)
480     gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
481
482   return radio_button;
483 }
484
485 /**
486  * gtk_radio_button_new_from_widget:
487  * @radio_group_member: (allow-none): an existing #GtkRadioButton.
488  *
489  * Creates a new #GtkRadioButton, adding it to the same group as
490  * @radio_group_member. As with gtk_radio_button_new(), a widget
491  * should be packed into the radio button.
492  *
493  * Returns: (transfer none): a new radio button.
494  */
495 GtkWidget*
496 gtk_radio_button_new_from_widget (GtkRadioButton *radio_group_member)
497 {
498   GSList *l = NULL;
499   if (radio_group_member)
500     l = gtk_radio_button_get_group (radio_group_member);
501   return gtk_radio_button_new (l);
502 }
503
504 /**
505  * gtk_radio_button_new_with_label_from_widget: (constructor)
506  * @radio_group_member: (allow-none): widget to get radio group from or %NULL
507  * @label: a text string to display next to the radio button.
508  *
509  * Creates a new #GtkRadioButton with a text label, adding it to
510  * the same group as @radio_group_member.
511  *
512  * Returns: (transfer none): a new radio button.
513  */
514 GtkWidget*
515 gtk_radio_button_new_with_label_from_widget (GtkRadioButton *radio_group_member,
516                                              const gchar    *label)
517 {
518   GSList *l = NULL;
519   if (radio_group_member)
520     l = gtk_radio_button_get_group (radio_group_member);
521   return gtk_radio_button_new_with_label (l, label);
522 }
523
524 /**
525  * gtk_radio_button_new_with_mnemonic_from_widget: (constructor)
526  * @radio_group_member: (allow-none): widget to get radio group from or %NULL
527  * @label: the text of the button, with an underscore in front of the
528  *         mnemonic character
529  *
530  * Creates a new #GtkRadioButton containing a label. The label
531  * will be created using gtk_label_new_with_mnemonic(), so underscores
532  * in @label indicate the mnemonic for the button.
533  *
534  * Returns: (transfer none): a new #GtkRadioButton
535  **/
536 GtkWidget*
537 gtk_radio_button_new_with_mnemonic_from_widget (GtkRadioButton *radio_group_member,
538                                                 const gchar    *label)
539 {
540   GSList *l = NULL;
541   if (radio_group_member)
542     l = gtk_radio_button_get_group (radio_group_member);
543   return gtk_radio_button_new_with_mnemonic (l, label);
544 }
545
546
547 /**
548  * gtk_radio_button_get_group:
549  * @radio_button: a #GtkRadioButton.
550  *
551  * Retrieves the group assigned to a radio button.
552  *
553  * Return value: (element-type GtkRadioButton) (transfer none): a linked list
554  * containing all the radio buttons in the same group
555  * as @radio_button. The returned list is owned by the radio button
556  * and must not be modified or freed.
557  */
558 GSList*
559 gtk_radio_button_get_group (GtkRadioButton *radio_button)
560 {
561   g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_button), NULL);
562
563   return radio_button->priv->group;
564 }
565
566
567 static void
568 gtk_radio_button_destroy (GtkWidget *widget)
569 {
570   GtkWidget *old_group_singleton = NULL;
571   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
572   GtkRadioButtonPrivate *priv = radio_button->priv;
573   GtkRadioButton *tmp_button;
574   GSList *tmp_list;
575   gboolean was_in_group;
576
577   was_in_group = priv->group && priv->group->next;
578
579   priv->group = g_slist_remove (priv->group, radio_button);
580   if (priv->group && !priv->group->next)
581     old_group_singleton = priv->group->data;
582
583   tmp_list = priv->group;
584
585   while (tmp_list)
586     {
587       tmp_button = tmp_list->data;
588       tmp_list = tmp_list->next;
589
590       tmp_button->priv->group = priv->group;
591     }
592
593   /* this button is no longer in the group */
594   priv->group = NULL;
595
596   if (old_group_singleton)
597     g_signal_emit (old_group_singleton, group_changed_signal, 0);
598   if (was_in_group)
599     g_signal_emit (radio_button, group_changed_signal, 0);
600
601   GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->destroy (widget);
602 }
603
604 static void
605 get_coordinates (GtkWidget    *widget,
606                  GtkWidget    *reference,
607                  gint         *x,
608                  gint         *y)
609 {
610   GtkAllocation allocation;
611
612   gtk_widget_get_allocation (widget, &allocation);
613   *x = allocation.x + allocation.width / 2;
614   *y = allocation.y + allocation.height / 2;
615
616   gtk_widget_translate_coordinates (widget, reference, *x, *y, x, y);
617 }
618
619 static gint
620 left_right_compare (gconstpointer a,
621                     gconstpointer b,
622                     gpointer      data)
623 {
624   gint x1, y1, x2, y2;
625
626   get_coordinates ((GtkWidget *)a, data, &x1, &y1);
627   get_coordinates ((GtkWidget *)b, data, &x2, &y2);
628
629   if (y1 == y2)
630     return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
631   else
632     return (y1 < y2) ? -1 : 1;
633 }
634
635 static gint
636 up_down_compare (gconstpointer a,
637                  gconstpointer b,
638                  gpointer      data)
639 {
640   gint x1, y1, x2, y2;
641   
642   get_coordinates ((GtkWidget *)a, data, &x1, &y1);
643   get_coordinates ((GtkWidget *)b, data, &x2, &y2);
644   
645   if (x1 == x2)
646     return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
647   else
648     return (x1 < x2) ? -1 : 1;
649 }
650
651 static gboolean
652 gtk_radio_button_focus (GtkWidget         *widget,
653                         GtkDirectionType   direction)
654 {
655   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
656   GtkRadioButtonPrivate *priv = radio_button->priv;
657   GSList *tmp_slist;
658
659   /* Radio buttons with draw_indicator unset focus "normally", since
660    * they look like buttons to the user.
661    */
662   if (!gtk_toggle_button_get_mode (GTK_TOGGLE_BUTTON (widget)))
663     return GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->focus (widget, direction);
664   
665   if (gtk_widget_is_focus (widget))
666     {
667       GtkSettings *settings = gtk_widget_get_settings (widget);
668       GSList *focus_list, *tmp_list;
669       GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
670       GtkWidget *new_focus = NULL;
671       gboolean cursor_only;
672       gboolean wrap_around;
673
674       switch (direction)
675         {
676         case GTK_DIR_LEFT:
677         case GTK_DIR_RIGHT:
678           focus_list = g_slist_copy (priv->group);
679           focus_list = g_slist_sort_with_data (focus_list, left_right_compare, toplevel);
680           break;
681         case GTK_DIR_UP:
682         case GTK_DIR_DOWN:
683           focus_list = g_slist_copy (priv->group);
684           focus_list = g_slist_sort_with_data (focus_list, up_down_compare, toplevel);
685           break;
686         case GTK_DIR_TAB_FORWARD:
687         case GTK_DIR_TAB_BACKWARD:
688           /* fall through */
689         default:
690           return FALSE;
691         }
692
693       if (direction == GTK_DIR_LEFT || direction == GTK_DIR_UP)
694         focus_list = g_slist_reverse (focus_list);
695
696       tmp_list = g_slist_find (focus_list, widget);
697
698       if (tmp_list)
699         {
700           tmp_list = tmp_list->next;
701           
702           while (tmp_list)
703             {
704               GtkWidget *child = tmp_list->data;
705               
706               if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
707                 {
708                   new_focus = child;
709                   break;
710                 }
711
712               tmp_list = tmp_list->next;
713             }
714         }
715
716       g_object_get (settings,
717                     "gtk-keynav-cursor-only", &cursor_only,
718                     "gtk-keynav-wrap-around", &wrap_around,
719                     NULL);
720
721       if (!new_focus)
722         {
723           if (cursor_only)
724             {
725               g_slist_free (focus_list);
726               return FALSE;
727             }
728
729           if (!wrap_around)
730             {
731               g_slist_free (focus_list);
732               gtk_widget_error_bell (widget);
733               return TRUE;
734             }
735
736           tmp_list = focus_list;
737
738           while (tmp_list)
739             {
740               GtkWidget *child = tmp_list->data;
741               
742               if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
743                 {
744                   new_focus = child;
745                   break;
746                 }
747               
748               tmp_list = tmp_list->next;
749             }
750         }
751       
752       g_slist_free (focus_list);
753
754       if (new_focus)
755         {
756           gtk_widget_grab_focus (new_focus);
757
758           if (!cursor_only)
759             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
760         }
761
762       return TRUE;
763     }
764   else
765     {
766       GtkRadioButton *selected_button = NULL;
767       
768       /* We accept the focus if, we don't have the focus and
769        *  - we are the currently active button in the group
770        *  - there is no currently active radio button.
771        */
772       
773       tmp_slist = priv->group;
774       while (tmp_slist)
775         {
776           if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tmp_slist->data)))
777             selected_button = tmp_slist->data;
778           tmp_slist = tmp_slist->next;
779         }
780       
781       if (selected_button && selected_button != radio_button)
782         return FALSE;
783
784       gtk_widget_grab_focus (widget);
785       return TRUE;
786     }
787 }
788
789 static void
790 gtk_radio_button_clicked (GtkButton *button)
791 {
792   GtkRadioButton *radio_button = GTK_RADIO_BUTTON (button);
793   GtkRadioButtonPrivate *priv = radio_button->priv;
794   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
795   GtkToggleButton *tmp_button;
796   GtkStateFlags new_state = 0;
797   GSList *tmp_list;
798   gint toggled;
799   gboolean depressed;
800
801   toggled = FALSE;
802
803   g_object_ref (GTK_WIDGET (button));
804
805   new_state = gtk_widget_get_state_flags (GTK_WIDGET (button)) &
806     ~(GTK_STATE_FLAG_PRELIGHT |
807       GTK_STATE_FLAG_ACTIVE);
808
809   if (gtk_toggle_button_get_active (toggle_button))
810     {
811       tmp_button = NULL;
812       tmp_list = priv->group;
813
814       while (tmp_list)
815         {
816           tmp_button = tmp_list->data;
817           tmp_list = tmp_list->next;
818
819           if (tmp_button != toggle_button &&
820               gtk_toggle_button_get_active (tmp_button))
821             break;
822
823           tmp_button = NULL;
824         }
825
826       if (!tmp_button)
827         {
828           if (button->priv->in_button)
829             new_state |= GTK_STATE_FLAG_PRELIGHT;
830
831           new_state |= GTK_STATE_FLAG_ACTIVE;
832         }
833       else
834         {
835           toggled = TRUE;
836           _gtk_toggle_button_set_active (toggle_button,
837                                          !gtk_toggle_button_get_active (toggle_button));
838
839           if (button->priv->in_button)
840             new_state |= GTK_STATE_FLAG_PRELIGHT;
841         }
842     }
843   else
844     {
845       toggled = TRUE;
846       _gtk_toggle_button_set_active (toggle_button,
847                                      !gtk_toggle_button_get_active (toggle_button));
848
849       tmp_list = priv->group;
850       while (tmp_list)
851         {
852           tmp_button = tmp_list->data;
853           tmp_list = tmp_list->next;
854
855           if (gtk_toggle_button_get_active (tmp_button) && (tmp_button != toggle_button))
856             {
857               gtk_button_clicked (GTK_BUTTON (tmp_button));
858               break;
859             }
860         }
861
862       if (button->priv->in_button)
863         new_state |= GTK_STATE_FLAG_PRELIGHT;
864
865       new_state |= GTK_STATE_FLAG_ACTIVE;
866     }
867
868   if (gtk_toggle_button_get_inconsistent (toggle_button))
869     depressed = FALSE;
870   else if (button->priv->in_button && button->priv->button_down)
871     depressed = !gtk_toggle_button_get_active (toggle_button);
872   else
873     depressed = gtk_toggle_button_get_active (toggle_button);
874
875   if (gtk_widget_get_state_flags (GTK_WIDGET (button)) != new_state)
876     gtk_widget_set_state_flags (GTK_WIDGET (button), new_state, TRUE);
877
878   if (toggled)
879     {
880       gtk_toggle_button_toggled (toggle_button);
881
882       g_object_notify (G_OBJECT (toggle_button), "active");
883     }
884
885   _gtk_button_set_depressed (button, depressed);
886
887   gtk_widget_queue_draw (GTK_WIDGET (button));
888
889   g_object_unref (button);
890 }
891
892 static void
893 gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
894                                  cairo_t        *cr)
895 {
896   GtkAllocation allocation;
897   GtkWidget *widget;
898   GtkWidget *child;
899   GtkButton *button;
900   GtkToggleButton *toggle_button;
901   GtkStyleContext *context;
902   GtkStateFlags state = 0;
903   gint x, y;
904   gint indicator_size, indicator_spacing;
905   gint focus_width;
906   gint focus_pad;
907   guint border_width;
908   gboolean interior_focus;
909
910   widget = GTK_WIDGET (check_button);
911   button = GTK_BUTTON (check_button);
912   toggle_button = GTK_TOGGLE_BUTTON (check_button);
913   context = gtk_widget_get_style_context (widget);
914
915   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
916
917   gtk_widget_style_get (widget,
918                         "interior-focus", &interior_focus,
919                         "focus-line-width", &focus_width,
920                         "focus-padding", &focus_pad,
921                         NULL);
922
923   _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
924
925   gtk_widget_get_allocation (widget, &allocation);
926
927   x = indicator_spacing + border_width;
928   y = (allocation.height - indicator_size) / 2;
929
930   child = gtk_bin_get_child (GTK_BIN (check_button));
931   if (!interior_focus || !(child && gtk_widget_get_visible (child)))
932     x += focus_width + focus_pad;
933
934   if (gtk_toggle_button_get_inconsistent (toggle_button))
935     state |= GTK_STATE_FLAG_INCONSISTENT;
936   else if (gtk_toggle_button_get_active (toggle_button))
937     state |= GTK_STATE_FLAG_ACTIVE;
938
939   if (button->priv->activate_timeout ||
940       (button->priv->button_down && button->priv->in_button))
941     state |= GTK_STATE_FLAG_SELECTED;
942
943   if (button->priv->in_button)
944     state |= GTK_STATE_FLAG_PRELIGHT;
945   else if (!gtk_widget_is_sensitive (widget))
946     state |= GTK_STATE_FLAG_INSENSITIVE;
947
948   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
949     x = allocation.width - (indicator_size + x);
950
951   gtk_style_context_save (context);
952   gtk_style_context_set_state (context, state);
953
954   if (state & GTK_STATE_FLAG_PRELIGHT)
955     gtk_render_background (context, cr,
956                            border_width, border_width,
957                            allocation.width - (2 * border_width),
958                            allocation.height - (2 * border_width));
959
960   gtk_style_context_add_class (context, GTK_STYLE_CLASS_RADIO);
961
962   gtk_render_option (context, cr,
963                      x, y, indicator_size, indicator_size);
964
965   gtk_style_context_restore (context);
966 }