1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
29 #include "gtkmarshalers.h"
30 #include "gtkradiobutton.h"
31 #include "gtkprivate.h"
35 * SECTION:gtkradiobutton
36 * @Short_description: A choice from multiple check buttons
37 * @Title: GtkRadioButton
38 * @See_also: #GtkComboBox
40 * A single radio button performs the same basic function as a #GtkCheckButton,
41 * as its position in the object hierarchy reflects. It is only when multiple
42 * radio buttons are grouped together that they become a different user
43 * interface component in their own right.
45 * Every radio button is a member of some group of radio buttons. When one is
46 * selected, all other radio buttons in the same group are deselected. A
47 * #GtkRadioButton is one way of giving the user a choice from many options.
49 * Radio button widgets are created with gtk_radio_button_new(), passing %NULL
50 * as the argument if this is the first radio button in a group. In subsequent
51 * calls, the group you wish to add this button to should be passed as an
52 * argument. Optionally, gtk_radio_button_new_with_label() can be used if you
53 * want a text label on the radio button.
55 * Alternatively, when adding widgets to an existing group of radio buttons,
56 * use gtk_radio_button_new_from_widget() with a #GtkRadioButton that already
57 * has a group assigned to it. The convenience function
58 * gtk_radio_button_new_with_label_from_widget() is also provided.
60 * To retrieve the group a #GtkRadioButton is assigned to, use
61 * gtk_radio_button_get_group().
63 * To remove a #GtkRadioButton from one group and make it part of a new one,
64 * use gtk_radio_button_set_group().
66 * The group list does not need to be freed, as each #GtkRadioButton will remove
67 * itself and its list item when it is destroyed.
70 * <title>How to create a group of two radio buttons.</title>
72 * void create_radio_buttons (void) {
74 * GtkWidget *window, *radio1, *radio2, *box, *entry;
75 * window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
76 * box = gtk_vbox_new (TRUE, 2);
78 * /* Create a radio button with a GtkEntry widget */
79 * radio1 = gtk_radio_button_new (NULL);
80 * entry = gtk_entry_new (<!-- -->);
81 * gtk_container_add (GTK_CONTAINER (radio1), entry);
84 * /* Create a radio button with a label */
85 * radio2 = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (radio1),
86 * "I'm the second radio button.");
88 * /* Pack them into a box, then show all the widgets */
89 * gtk_box_pack_start (GTK_BOX (box), radio1, TRUE, TRUE, 2);
90 * gtk_box_pack_start (GTK_BOX (box), radio2, TRUE, TRUE, 2);
91 * gtk_container_add (GTK_CONTAINER (window), box);
92 * gtk_widget_show_all (window);
98 * When an unselected button in the group is clicked the clicked button
99 * receives the #GtkToggleButton::toggled signal, as does the previously
101 * Inside the #GtkToggleButton::toggled handler, gtk_toggle_button_get_active()
102 * can be used to determine if the button has been selected or deselected.
112 static void gtk_radio_button_destroy (GtkObject *object);
113 static gboolean gtk_radio_button_focus (GtkWidget *widget,
114 GtkDirectionType direction);
115 static void gtk_radio_button_clicked (GtkButton *button);
116 static void gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
118 static void gtk_radio_button_set_property (GObject *object,
122 static void gtk_radio_button_get_property (GObject *object,
127 G_DEFINE_TYPE (GtkRadioButton, gtk_radio_button, GTK_TYPE_CHECK_BUTTON)
129 static guint group_changed_signal = 0;
132 gtk_radio_button_class_init (GtkRadioButtonClass *class)
134 GObjectClass *gobject_class;
135 GtkObjectClass *object_class;
136 GtkButtonClass *button_class;
137 GtkCheckButtonClass *check_button_class;
138 GtkWidgetClass *widget_class;
140 gobject_class = G_OBJECT_CLASS (class);
141 object_class = (GtkObjectClass*) class;
142 widget_class = (GtkWidgetClass*) class;
143 button_class = (GtkButtonClass*) class;
144 check_button_class = (GtkCheckButtonClass*) class;
146 gobject_class->set_property = gtk_radio_button_set_property;
147 gobject_class->get_property = gtk_radio_button_get_property;
150 * GtkRadioButton:group:
152 * Sets a new group for a radio button.
154 g_object_class_install_property (gobject_class,
156 g_param_spec_object ("group",
158 P_("The radio button whose group this widget belongs to."),
159 GTK_TYPE_RADIO_BUTTON,
160 GTK_PARAM_WRITABLE));
161 object_class->destroy = gtk_radio_button_destroy;
163 widget_class->focus = gtk_radio_button_focus;
165 button_class->clicked = gtk_radio_button_clicked;
167 check_button_class->draw_indicator = gtk_radio_button_draw_indicator;
169 class->group_changed = NULL;
172 * GtkRadioButton::group-changed:
173 * @style: the object which received the signal
175 * Emitted when the group of radio buttons that a radio button belongs
176 * to changes. This is emitted when a radio button switches from
177 * being alone to being part of a group of 2 or more buttons, or
178 * vice-versa, and when a button is moved from one group of 2 or
179 * more buttons to a different one, but not when the composition
180 * of the group that a button belongs to changes.
184 group_changed_signal = g_signal_new (I_("group-changed"),
185 G_OBJECT_CLASS_TYPE (object_class),
187 G_STRUCT_OFFSET (GtkRadioButtonClass, group_changed),
189 _gtk_marshal_VOID__VOID,
194 gtk_radio_button_init (GtkRadioButton *radio_button)
196 gtk_widget_set_has_window (GTK_WIDGET (radio_button), FALSE);
197 gtk_widget_set_receives_default (GTK_WIDGET (radio_button), FALSE);
199 GTK_TOGGLE_BUTTON (radio_button)->active = TRUE;
201 GTK_BUTTON (radio_button)->depress_on_activate = FALSE;
203 radio_button->group = g_slist_prepend (NULL, radio_button);
205 _gtk_button_set_depressed (GTK_BUTTON (radio_button), TRUE);
206 gtk_widget_set_state (GTK_WIDGET (radio_button), GTK_STATE_ACTIVE);
210 gtk_radio_button_set_property (GObject *object,
215 GtkRadioButton *radio_button;
217 radio_button = GTK_RADIO_BUTTON (object);
222 GtkRadioButton *button;
225 button = g_value_get_object (value);
228 slist = gtk_radio_button_get_group (button);
231 gtk_radio_button_set_group (radio_button, slist);
234 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240 gtk_radio_button_get_property (GObject *object,
248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
254 * gtk_radio_button_set_group:
255 * @radio_button: a #GtkRadioButton.
256 * @group: an existing radio button group, such as one returned from
257 * gtk_radio_button_get_group().
259 * Sets a #GtkRadioButton's group. It should be noted that this does not change
260 * the layout of your interface in any way, so if you are changing the group,
261 * it is likely you will need to re-arrange the user interface to reflect these
265 gtk_radio_button_set_group (GtkRadioButton *radio_button,
268 GtkWidget *old_group_singleton = NULL;
269 GtkWidget *new_group_singleton = NULL;
271 g_return_if_fail (GTK_IS_RADIO_BUTTON (radio_button));
272 g_return_if_fail (!g_slist_find (group, radio_button));
274 if (radio_button->group)
278 radio_button->group = g_slist_remove (radio_button->group, radio_button);
280 if (radio_button->group && !radio_button->group->next)
281 old_group_singleton = g_object_ref (radio_button->group->data);
283 for (slist = radio_button->group; slist; slist = slist->next)
285 GtkRadioButton *tmp_button;
287 tmp_button = slist->data;
289 tmp_button->group = radio_button->group;
293 if (group && !group->next)
294 new_group_singleton = g_object_ref (group->data);
296 radio_button->group = g_slist_prepend (group, radio_button);
302 for (slist = group; slist; slist = slist->next)
304 GtkRadioButton *tmp_button;
306 tmp_button = slist->data;
308 tmp_button->group = radio_button->group;
312 g_object_ref (radio_button);
314 g_object_notify (G_OBJECT (radio_button), "group");
315 g_signal_emit (radio_button, group_changed_signal, 0);
316 if (old_group_singleton)
318 g_signal_emit (old_group_singleton, group_changed_signal, 0);
319 g_object_unref (old_group_singleton);
321 if (new_group_singleton)
323 g_signal_emit (new_group_singleton, group_changed_signal, 0);
324 g_object_unref (new_group_singleton);
327 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_button), group == NULL);
329 g_object_unref (radio_button);
333 * gtk_radio_button_new:
334 * @group: an existing radio button group, or %NULL if you are creating a new group.
336 * Creates a new #GtkRadioButton. To be of any practical value, a widget should
337 * then be packed into the radio button.
339 * Returns: a new radio button.
342 gtk_radio_button_new (GSList *group)
344 GtkRadioButton *radio_button;
346 radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, NULL);
349 gtk_radio_button_set_group (radio_button, group);
351 return GTK_WIDGET (radio_button);
355 * gtk_radio_button_new_with_label:
356 * @group: an existing radio button group, or %NULL if you are creating a new
358 * @label: the text label to display next to the radio button.
360 * Creates a new #GtkRadioButton with a text label.
362 * Returns: a new radio button.
365 gtk_radio_button_new_with_label (GSList *group,
368 GtkWidget *radio_button;
370 radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "label", label, NULL) ;
373 gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
380 * gtk_radio_button_new_with_mnemonic:
381 * @group: the radio button group
382 * @label: the text of the button, with an underscore in front of the
384 * @returns: a new #GtkRadioButton
386 * Creates a new #GtkRadioButton containing a label, adding it to the same
387 * group as @group. The label will be created using
388 * gtk_label_new_with_mnemonic(), so underscores in @label indicate the
389 * mnemonic for the button.
392 gtk_radio_button_new_with_mnemonic (GSList *group,
395 GtkWidget *radio_button;
397 radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON,
399 "use-underline", TRUE,
403 gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
409 * gtk_radio_button_new_from_widget:
410 * @radio_group_member: an existing #GtkRadioButton.
412 * Creates a new #GtkRadioButton, adding it to the same group as @radio_group_member.
413 * As with gtk_radio_button_new(), a widget should be packed into the radio button.
415 * Returns: a new radio button.
418 gtk_radio_button_new_from_widget (GtkRadioButton *radio_group_member)
421 if (radio_group_member)
422 l = gtk_radio_button_get_group (radio_group_member);
423 return gtk_radio_button_new (l);
427 * gtk_radio_button_new_with_label_from_widget:
428 * @radio_group_member: widget to get radio group from or %NULL
429 * @label: a text string to display next to the radio button.
431 * Creates a new #GtkRadioButton with a text label, adding it to the same group
432 * as @radio_group_member.
434 * Returns: a new radio button.
437 gtk_radio_button_new_with_label_from_widget (GtkRadioButton *radio_group_member,
441 if (radio_group_member)
442 l = gtk_radio_button_get_group (radio_group_member);
443 return gtk_radio_button_new_with_label (l, label);
447 * gtk_radio_button_new_with_mnemonic_from_widget:
448 * @radio_group_member: (allow-none): widget to get radio group from or %NULL
449 * @label: the text of the button, with an underscore in front of the
451 * @returns: a new #GtkRadioButton
453 * Creates a new #GtkRadioButton containing a label. The label
454 * will be created using gtk_label_new_with_mnemonic(), so underscores
455 * in @label indicate the mnemonic for the button.
458 gtk_radio_button_new_with_mnemonic_from_widget (GtkRadioButton *radio_group_member,
462 if (radio_group_member)
463 l = gtk_radio_button_get_group (radio_group_member);
464 return gtk_radio_button_new_with_mnemonic (l, label);
469 * gtk_radio_button_get_group:
470 * @radio_button: a #GtkRadioButton.
472 * Retrieves the group assigned to a radio button.
474 * Return value: (element-type GtkRadioButton) (transfer none): a linked list
475 * containing all the radio buttons in the same group
476 * as @radio_button. The returned list is owned by the radio button
477 * and must not be modified or freed.
480 gtk_radio_button_get_group (GtkRadioButton *radio_button)
482 g_return_val_if_fail (GTK_IS_RADIO_BUTTON (radio_button), NULL);
484 return radio_button->group;
489 gtk_radio_button_destroy (GtkObject *object)
491 GtkWidget *old_group_singleton = NULL;
492 GtkRadioButton *radio_button;
493 GtkRadioButton *tmp_button;
495 gboolean was_in_group;
497 radio_button = GTK_RADIO_BUTTON (object);
499 was_in_group = radio_button->group && radio_button->group->next;
501 radio_button->group = g_slist_remove (radio_button->group, radio_button);
502 if (radio_button->group && !radio_button->group->next)
503 old_group_singleton = radio_button->group->data;
505 tmp_list = radio_button->group;
509 tmp_button = tmp_list->data;
510 tmp_list = tmp_list->next;
512 tmp_button->group = radio_button->group;
515 /* this button is no longer in the group */
516 radio_button->group = NULL;
518 if (old_group_singleton)
519 g_signal_emit (old_group_singleton, group_changed_signal, 0);
521 g_signal_emit (radio_button, group_changed_signal, 0);
523 GTK_OBJECT_CLASS (gtk_radio_button_parent_class)->destroy (object);
527 get_coordinates (GtkWidget *widget,
528 GtkWidget *reference,
532 *x = widget->allocation.x + widget->allocation.width / 2;
533 *y = widget->allocation.y + widget->allocation.height / 2;
535 gtk_widget_translate_coordinates (widget, reference, *x, *y, x, y);
539 left_right_compare (gconstpointer a,
545 get_coordinates ((GtkWidget *)a, data, &x1, &y1);
546 get_coordinates ((GtkWidget *)b, data, &x2, &y2);
549 return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
551 return (y1 < y2) ? -1 : 1;
555 up_down_compare (gconstpointer a,
561 get_coordinates ((GtkWidget *)a, data, &x1, &y1);
562 get_coordinates ((GtkWidget *)b, data, &x2, &y2);
565 return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
567 return (x1 < x2) ? -1 : 1;
571 gtk_radio_button_focus (GtkWidget *widget,
572 GtkDirectionType direction)
574 GtkRadioButton *radio_button = GTK_RADIO_BUTTON (widget);
577 /* Radio buttons with draw_indicator unset focus "normally", since
578 * they look like buttons to the user.
580 if (!GTK_TOGGLE_BUTTON (widget)->draw_indicator)
581 return GTK_WIDGET_CLASS (gtk_radio_button_parent_class)->focus (widget, direction);
583 if (gtk_widget_is_focus (widget))
585 GtkSettings *settings = gtk_widget_get_settings (widget);
586 GSList *focus_list, *tmp_list;
587 GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
588 GtkWidget *new_focus = NULL;
589 gboolean cursor_only;
590 gboolean wrap_around;
596 focus_list = g_slist_copy (radio_button->group);
597 focus_list = g_slist_sort_with_data (focus_list, left_right_compare, toplevel);
601 focus_list = g_slist_copy (radio_button->group);
602 focus_list = g_slist_sort_with_data (focus_list, up_down_compare, toplevel);
604 case GTK_DIR_TAB_FORWARD:
605 case GTK_DIR_TAB_BACKWARD:
611 if (direction == GTK_DIR_LEFT || direction == GTK_DIR_UP)
612 focus_list = g_slist_reverse (focus_list);
614 tmp_list = g_slist_find (focus_list, widget);
618 tmp_list = tmp_list->next;
622 GtkWidget *child = tmp_list->data;
624 if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
630 tmp_list = tmp_list->next;
634 g_object_get (settings,
635 "gtk-keynav-cursor-only", &cursor_only,
636 "gtk-keynav-wrap-around", &wrap_around,
643 g_slist_free (focus_list);
649 g_slist_free (focus_list);
650 gtk_widget_error_bell (widget);
654 tmp_list = focus_list;
658 GtkWidget *child = tmp_list->data;
660 if (gtk_widget_get_mapped (child) && gtk_widget_is_sensitive (child))
666 tmp_list = tmp_list->next;
670 g_slist_free (focus_list);
674 gtk_widget_grab_focus (new_focus);
677 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (new_focus), TRUE);
684 GtkRadioButton *selected_button = NULL;
686 /* We accept the focus if, we don't have the focus and
687 * - we are the currently active button in the group
688 * - there is no currently active radio button.
691 tmp_slist = radio_button->group;
694 if (GTK_TOGGLE_BUTTON (tmp_slist->data)->active)
695 selected_button = tmp_slist->data;
696 tmp_slist = tmp_slist->next;
699 if (selected_button && selected_button != radio_button)
702 gtk_widget_grab_focus (widget);
708 gtk_radio_button_clicked (GtkButton *button)
710 GtkToggleButton *toggle_button;
711 GtkRadioButton *radio_button;
712 GtkToggleButton *tmp_button;
713 GtkStateType new_state;
718 radio_button = GTK_RADIO_BUTTON (button);
719 toggle_button = GTK_TOGGLE_BUTTON (button);
722 g_object_ref (GTK_WIDGET (button));
724 if (toggle_button->active)
727 tmp_list = radio_button->group;
731 tmp_button = tmp_list->data;
732 tmp_list = tmp_list->next;
734 if (tmp_button->active && tmp_button != toggle_button)
742 new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
747 toggle_button->active = !toggle_button->active;
748 new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
754 toggle_button->active = !toggle_button->active;
756 tmp_list = radio_button->group;
759 tmp_button = tmp_list->data;
760 tmp_list = tmp_list->next;
762 if (tmp_button->active && (tmp_button != toggle_button))
764 gtk_button_clicked (GTK_BUTTON (tmp_button));
769 new_state = (button->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
772 if (toggle_button->inconsistent)
774 else if (button->in_button && button->button_down)
775 depressed = !toggle_button->active;
777 depressed = toggle_button->active;
779 if (gtk_widget_get_state (GTK_WIDGET (button)) != new_state)
780 gtk_widget_set_state (GTK_WIDGET (button), new_state);
784 gtk_toggle_button_toggled (toggle_button);
786 g_object_notify (G_OBJECT (toggle_button), "active");
789 _gtk_button_set_depressed (button, depressed);
791 gtk_widget_queue_draw (GTK_WIDGET (button));
793 g_object_unref (button);
797 gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
803 GtkToggleButton *toggle_button;
804 GtkStateType state_type;
805 GtkShadowType shadow_type;
807 gint indicator_size, indicator_spacing;
811 gboolean interior_focus;
813 widget = GTK_WIDGET (check_button);
815 if (gtk_widget_is_drawable (widget))
817 button = GTK_BUTTON (check_button);
818 toggle_button = GTK_TOGGLE_BUTTON (check_button);
820 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
822 gtk_widget_style_get (widget,
823 "interior-focus", &interior_focus,
824 "focus-line-width", &focus_width,
825 "focus-padding", &focus_pad,
828 _gtk_check_button_get_props (check_button, &indicator_size, &indicator_spacing);
830 x = widget->allocation.x + indicator_spacing + border_width;
831 y = widget->allocation.y + (widget->allocation.height - indicator_size) / 2;
833 child = gtk_bin_get_child (GTK_BIN (check_button));
834 if (!interior_focus || !(child && gtk_widget_get_visible (child)))
835 x += focus_width + focus_pad;
837 if (toggle_button->inconsistent)
838 shadow_type = GTK_SHADOW_ETCHED_IN;
839 else if (toggle_button->active)
840 shadow_type = GTK_SHADOW_IN;
842 shadow_type = GTK_SHADOW_OUT;
844 if (button->activate_timeout || (button->button_down && button->in_button))
845 state_type = GTK_STATE_ACTIVE;
846 else if (button->in_button)
847 state_type = GTK_STATE_PRELIGHT;
848 else if (!gtk_widget_is_sensitive (widget))
849 state_type = GTK_STATE_INSENSITIVE;
851 state_type = GTK_STATE_NORMAL;
853 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
854 x = widget->allocation.x + widget->allocation.width - (indicator_size + x - widget->allocation.x);
856 if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
858 GdkRectangle restrict_area;
859 GdkRectangle new_area;
861 restrict_area.x = widget->allocation.x + border_width;
862 restrict_area.y = widget->allocation.y + border_width;
863 restrict_area.width = widget->allocation.width - (2 * border_width);
864 restrict_area.height = widget->allocation.height - (2 * border_width);
866 if (gdk_rectangle_intersect (area, &restrict_area, &new_area))
868 gtk_paint_flat_box (widget->style, widget->window, GTK_STATE_PRELIGHT,
869 GTK_SHADOW_ETCHED_OUT,
870 area, widget, "checkbutton",
871 new_area.x, new_area.y,
872 new_area.width, new_area.height);
876 gtk_paint_option (widget->style, widget->window,
877 state_type, shadow_type,
878 area, widget, "radiobutton",
879 x, y, indicator_size, indicator_size);