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