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-2001. 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/.
28 #include "gtkcheckmenuitem.h"
29 #include "gtkmenuitemprivate.h"
30 #include "gtkaccellabel.h"
31 #include "gtkactivatable.h"
32 #include "gtktoggleaction.h"
33 #include "gtkmarshalers.h"
34 #include "gtkprivate.h"
36 #include "a11y/gtkcheckmenuitemaccessible.h"
37 #include "a11y/gtkchecksubmenuitemaccessible.h"
40 * SECTION:gtkcheckmenuitem
41 * @Short_description: A menu item with a check box
42 * @Title: GtkCheckMenuItem
44 * A #GtkCheckMenuItem is a menu item that maintains the state of a boolean
45 * value in addition to a #GtkMenuItem usual role in activating application
48 * A check box indicating the state of the boolean value is displayed
49 * at the left side of the #GtkMenuItem. Activating the #GtkMenuItem
54 #define INDICATOR_SIZE 16
56 struct _GtkCheckMenuItemPrivate
59 guint always_show_toggle : 1;
60 guint draw_as_radio : 1;
61 guint inconsistent : 1;
76 static gint gtk_check_menu_item_draw (GtkWidget *widget,
78 static void gtk_check_menu_item_activate (GtkMenuItem *menu_item);
79 static void gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
81 static void gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
83 static void gtk_check_menu_item_set_property (GObject *object,
87 static void gtk_check_menu_item_get_property (GObject *object,
92 static void gtk_check_menu_item_activatable_interface_init (GtkActivatableIface *iface);
93 static void gtk_check_menu_item_update (GtkActivatable *activatable,
95 const gchar *property_name);
96 static void gtk_check_menu_item_sync_action_properties (GtkActivatable *activatable,
99 static GtkActivatableIface *parent_activatable_iface;
100 static guint check_menu_item_signals[LAST_SIGNAL] = { 0 };
102 G_DEFINE_TYPE_WITH_CODE (GtkCheckMenuItem, gtk_check_menu_item, GTK_TYPE_MENU_ITEM,
103 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
104 gtk_check_menu_item_activatable_interface_init))
107 gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass)
109 GObjectClass *gobject_class;
110 GtkWidgetClass *widget_class;
111 GtkMenuItemClass *menu_item_class;
113 gobject_class = G_OBJECT_CLASS (klass);
114 widget_class = (GtkWidgetClass*) klass;
115 menu_item_class = (GtkMenuItemClass*) klass;
117 gobject_class->set_property = gtk_check_menu_item_set_property;
118 gobject_class->get_property = gtk_check_menu_item_get_property;
120 g_object_class_install_property (gobject_class,
122 g_param_spec_boolean ("active",
124 P_("Whether the menu item is checked"),
126 GTK_PARAM_READWRITE));
128 g_object_class_install_property (gobject_class,
130 g_param_spec_boolean ("inconsistent",
132 P_("Whether to display an \"inconsistent\" state"),
134 GTK_PARAM_READWRITE));
136 g_object_class_install_property (gobject_class,
138 g_param_spec_boolean ("draw-as-radio",
139 P_("Draw as radio menu item"),
140 P_("Whether the menu item looks like a radio menu item"),
142 GTK_PARAM_READWRITE));
144 gtk_widget_class_install_style_property (widget_class,
145 g_param_spec_int ("indicator-size",
146 P_("Indicator Size"),
147 P_("Size of check or radio indicator"),
151 GTK_PARAM_READABLE));
153 widget_class->draw = gtk_check_menu_item_draw;
155 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CHECK_SUBMENU_ITEM_ACCESSIBLE);
157 menu_item_class->activate = gtk_check_menu_item_activate;
158 menu_item_class->hide_on_activate = FALSE;
159 menu_item_class->toggle_size_request = gtk_check_menu_item_toggle_size_request;
161 klass->toggled = NULL;
162 klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
165 * GtkCheckMenuItem::toggled:
166 * @checkmenuitem: the object which received the signal.
168 * This signal is emitted when the state of the check box is changed.
170 * A signal handler can use gtk_check_menu_item_get_active()
171 * to discover the new state.
173 check_menu_item_signals[TOGGLED] =
174 g_signal_new (I_("toggled"),
175 G_OBJECT_CLASS_TYPE (gobject_class),
177 G_STRUCT_OFFSET (GtkCheckMenuItemClass, toggled),
179 _gtk_marshal_VOID__VOID,
182 g_type_class_add_private (klass, sizeof (GtkCheckMenuItemPrivate));
186 gtk_check_menu_item_activatable_interface_init (GtkActivatableIface *iface)
188 parent_activatable_iface = g_type_interface_peek_parent (iface);
189 iface->update = gtk_check_menu_item_update;
190 iface->sync_action_properties = gtk_check_menu_item_sync_action_properties;
194 gtk_check_menu_item_update (GtkActivatable *activatable,
196 const gchar *property_name)
198 GtkCheckMenuItem *check_menu_item;
200 check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
202 parent_activatable_iface->update (activatable, action, property_name);
204 if (strcmp (property_name, "active") == 0)
206 gtk_action_block_activate (action);
207 gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
208 gtk_action_unblock_activate (action);
211 if (!gtk_activatable_get_use_action_appearance (activatable))
214 if (strcmp (property_name, "draw-as-radio") == 0)
215 gtk_check_menu_item_set_draw_as_radio (check_menu_item,
216 gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
220 gtk_check_menu_item_sync_action_properties (GtkActivatable *activatable,
223 GtkCheckMenuItem *check_menu_item;
225 check_menu_item = GTK_CHECK_MENU_ITEM (activatable);
227 parent_activatable_iface->sync_action_properties (activatable, action);
229 if (!GTK_IS_TOGGLE_ACTION (action))
232 gtk_action_block_activate (action);
233 gtk_check_menu_item_set_active (check_menu_item, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
234 gtk_action_unblock_activate (action);
236 if (!gtk_activatable_get_use_action_appearance (activatable))
239 gtk_check_menu_item_set_draw_as_radio (check_menu_item,
240 gtk_toggle_action_get_draw_as_radio (GTK_TOGGLE_ACTION (action)));
244 * gtk_check_menu_item_new:
246 * Creates a new #GtkCheckMenuItem.
248 * Returns: a new #GtkCheckMenuItem.
251 gtk_check_menu_item_new (void)
253 return g_object_new (GTK_TYPE_CHECK_MENU_ITEM, NULL);
257 * gtk_check_menu_item_new_with_label:
258 * @label: the string to use for the label.
260 * Creates a new #GtkCheckMenuItem with a label.
262 * Returns: a new #GtkCheckMenuItem.
265 gtk_check_menu_item_new_with_label (const gchar *label)
267 return g_object_new (GTK_TYPE_CHECK_MENU_ITEM,
274 * gtk_check_menu_item_new_with_mnemonic:
275 * @label: The text of the button, with an underscore in front of the
277 * @returns: a new #GtkCheckMenuItem
279 * Creates a new #GtkCheckMenuItem containing a label. The label
280 * will be created using gtk_label_new_with_mnemonic(), so underscores
281 * in @label indicate the mnemonic for the menu item.
284 gtk_check_menu_item_new_with_mnemonic (const gchar *label)
286 return g_object_new (GTK_TYPE_CHECK_MENU_ITEM,
288 "use-underline", TRUE,
293 * gtk_check_menu_item_set_active:
294 * @check_menu_item: a #GtkCheckMenuItem.
295 * @is_active: boolean value indicating whether the check box is active.
297 * Sets the active state of the menu item's check box.
300 gtk_check_menu_item_set_active (GtkCheckMenuItem *check_menu_item,
303 GtkCheckMenuItemPrivate *priv;
305 g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
307 priv = check_menu_item->priv;
309 is_active = is_active != 0;
311 if (priv->active != is_active)
312 gtk_menu_item_activate (GTK_MENU_ITEM (check_menu_item));
316 * gtk_check_menu_item_get_active:
317 * @check_menu_item: a #GtkCheckMenuItem
319 * Returns whether the check menu item is active. See
320 * gtk_check_menu_item_set_active ().
322 * Return value: %TRUE if the menu item is checked.
325 gtk_check_menu_item_get_active (GtkCheckMenuItem *check_menu_item)
327 g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
329 return check_menu_item->priv->active;
333 gtk_check_menu_item_toggle_size_request (GtkMenuItem *menu_item,
336 guint toggle_spacing;
337 guint indicator_size;
339 g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (menu_item));
341 gtk_widget_style_get (GTK_WIDGET (menu_item),
342 "toggle-spacing", &toggle_spacing,
343 "indicator-size", &indicator_size,
346 *requisition = indicator_size + toggle_spacing;
350 * gtk_check_menu_item_toggled:
351 * @check_menu_item: a #GtkCheckMenuItem.
353 * Emits the #GtkCheckMenuItem::toggled signal.
356 gtk_check_menu_item_toggled (GtkCheckMenuItem *check_menu_item)
358 g_signal_emit (check_menu_item, check_menu_item_signals[TOGGLED], 0);
362 * gtk_check_menu_item_set_inconsistent:
363 * @check_menu_item: a #GtkCheckMenuItem
364 * @setting: %TRUE to display an "inconsistent" third state check
366 * If the user has selected a range of elements (such as some text or
367 * spreadsheet cells) that are affected by a boolean setting, and the
368 * current values in that range are inconsistent, you may want to
369 * display the check in an "in between" state. This function turns on
370 * "in between" display. Normally you would turn off the inconsistent
371 * state again if the user explicitly selects a setting. This has to be
372 * done manually, gtk_check_menu_item_set_inconsistent() only affects
373 * visual appearance, it doesn't affect the semantics of the widget.
377 gtk_check_menu_item_set_inconsistent (GtkCheckMenuItem *check_menu_item,
380 GtkCheckMenuItemPrivate *priv;
382 g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
384 priv = check_menu_item->priv;
386 setting = setting != FALSE;
388 if (setting != priv->inconsistent)
390 priv->inconsistent = setting;
391 gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
392 g_object_notify (G_OBJECT (check_menu_item), "inconsistent");
397 * gtk_check_menu_item_get_inconsistent:
398 * @check_menu_item: a #GtkCheckMenuItem
400 * Retrieves the value set by gtk_check_menu_item_set_inconsistent().
402 * Return value: %TRUE if inconsistent
405 gtk_check_menu_item_get_inconsistent (GtkCheckMenuItem *check_menu_item)
407 g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
409 return check_menu_item->priv->inconsistent;
413 * gtk_check_menu_item_set_draw_as_radio:
414 * @check_menu_item: a #GtkCheckMenuItem
415 * @draw_as_radio: whether @check_menu_item is drawn like a #GtkRadioMenuItem
417 * Sets whether @check_menu_item is drawn like a #GtkRadioMenuItem
422 gtk_check_menu_item_set_draw_as_radio (GtkCheckMenuItem *check_menu_item,
423 gboolean draw_as_radio)
425 GtkCheckMenuItemPrivate *priv;
427 g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item));
429 priv = check_menu_item->priv;
431 draw_as_radio = draw_as_radio != FALSE;
433 if (draw_as_radio != priv->draw_as_radio)
435 priv->draw_as_radio = draw_as_radio;
437 gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
439 g_object_notify (G_OBJECT (check_menu_item), "draw-as-radio");
444 * gtk_check_menu_item_get_draw_as_radio:
445 * @check_menu_item: a #GtkCheckMenuItem
447 * Returns whether @check_menu_item looks like a #GtkRadioMenuItem
449 * Return value: Whether @check_menu_item looks like a #GtkRadioMenuItem
454 gtk_check_menu_item_get_draw_as_radio (GtkCheckMenuItem *check_menu_item)
456 g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (check_menu_item), FALSE);
458 return check_menu_item->priv->draw_as_radio;
462 gtk_check_menu_item_init (GtkCheckMenuItem *check_menu_item)
464 GtkCheckMenuItemPrivate *priv;
466 check_menu_item->priv = G_TYPE_INSTANCE_GET_PRIVATE (check_menu_item,
467 GTK_TYPE_CHECK_MENU_ITEM,
468 GtkCheckMenuItemPrivate);
469 priv = check_menu_item->priv;
471 priv->active = FALSE;
472 priv->always_show_toggle = TRUE;
476 gtk_check_menu_item_draw (GtkWidget *widget,
479 GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (widget);
481 if (GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->draw)
482 GTK_WIDGET_CLASS (gtk_check_menu_item_parent_class)->draw (widget, cr);
484 if (GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator)
485 GTK_CHECK_MENU_ITEM_GET_CLASS (check_menu_item)->draw_indicator (check_menu_item, cr);
491 gtk_check_menu_item_activate (GtkMenuItem *menu_item)
493 GtkCheckMenuItemPrivate *priv;
495 GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (menu_item);
496 priv = check_menu_item->priv;
498 priv->active = !priv->active;
500 gtk_check_menu_item_toggled (check_menu_item);
501 gtk_widget_queue_draw (GTK_WIDGET (check_menu_item));
503 GTK_MENU_ITEM_CLASS (gtk_check_menu_item_parent_class)->activate (menu_item);
505 g_object_notify (G_OBJECT (check_menu_item), "active");
509 gtk_real_check_menu_item_draw_indicator (GtkCheckMenuItem *check_menu_item,
512 GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
516 widget = GTK_WIDGET (check_menu_item);
518 if (gtk_widget_is_drawable (widget))
520 GtkAllocation allocation;
521 GtkStyleContext *context;
525 guint toggle_spacing;
526 guint horizontal_padding;
527 guint indicator_size;
531 context = gtk_widget_get_style_context (widget);
532 state = gtk_widget_get_state_flags (widget);
533 gtk_style_context_get_padding (context, state, &padding);
535 gtk_widget_get_allocation (widget, &allocation);
537 gtk_widget_style_get (widget,
538 "toggle-spacing", &toggle_spacing,
539 "horizontal-padding", &horizontal_padding,
540 "indicator-size", &indicator_size,
543 toggle_size = GTK_MENU_ITEM (check_menu_item)->priv->toggle_size;
544 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
545 offset = border_width + padding.left + 2;
547 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
549 x = offset + horizontal_padding +
550 (toggle_size - toggle_spacing - indicator_size) / 2;
554 x = allocation.width -
555 offset - horizontal_padding - toggle_size + toggle_spacing +
556 (toggle_size - toggle_spacing - indicator_size) / 2;
559 y = (allocation.height - indicator_size) / 2;
562 priv->always_show_toggle ||
563 (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_PRELIGHT))
565 gtk_style_context_save (context);
567 if (priv->inconsistent)
568 state |= GTK_STATE_FLAG_INCONSISTENT;
569 else if (priv->active)
570 state |= GTK_STATE_FLAG_ACTIVE;
572 if (!gtk_widget_is_sensitive (widget))
573 state |= GTK_STATE_FLAG_INSENSITIVE;
575 gtk_style_context_set_state (context, state);
577 if (priv->draw_as_radio)
579 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RADIO);
580 gtk_render_option (context, cr, x, y,
581 indicator_size, indicator_size);
585 gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
586 gtk_render_check (context, cr, x, y,
587 indicator_size, indicator_size);
590 gtk_style_context_restore (context);
597 gtk_check_menu_item_get_property (GObject *object,
602 GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
603 GtkCheckMenuItemPrivate *priv = checkitem->priv;
608 g_value_set_boolean (value, priv->active);
610 case PROP_INCONSISTENT:
611 g_value_set_boolean (value, priv->inconsistent);
613 case PROP_DRAW_AS_RADIO:
614 g_value_set_boolean (value, priv->draw_as_radio);
617 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
624 gtk_check_menu_item_set_property (GObject *object,
629 GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (object);
634 gtk_check_menu_item_set_active (checkitem, g_value_get_boolean (value));
636 case PROP_INCONSISTENT:
637 gtk_check_menu_item_set_inconsistent (checkitem, g_value_get_boolean (value));
639 case PROP_DRAW_AS_RADIO:
640 gtk_check_menu_item_set_draw_as_radio (checkitem, g_value_get_boolean (value));
643 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652 * _gtk_check_menu_item_set_active:
653 * @check_menu_item: a #GtkCheckMenuItem
654 * @is_active: whether the action is active or not
656 * Sets the #GtkCheckMenuItem:active property directly. This function does
657 * not emit signals or notifications: it is left to the caller to do so.
660 _gtk_check_menu_item_set_active (GtkCheckMenuItem *check_menu_item,
663 GtkCheckMenuItemPrivate *priv = check_menu_item->priv;
665 priv->active = is_active;