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/.
27 #define GTK_MENU_INTERNALS
32 #include "gtkaccellabel.h"
34 #include "gtkmarshalers.h"
36 #include "gtkmenubar.h"
37 #include "gtkseparatormenuitem.h"
38 #include "gtkprivate.h"
39 #include "gtkbuildable.h"
40 #include "gtkactivatable.h"
47 gboolean use_action_appearance;
66 /* activatable properties */
67 PROP_ACTIVATABLE_RELATED_ACTION,
68 PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
72 static void gtk_menu_item_dispose (GObject *object);
73 static void gtk_menu_item_set_property (GObject *object,
77 static void gtk_menu_item_get_property (GObject *object,
81 static void gtk_menu_item_destroy (GtkObject *object);
82 static void gtk_menu_item_size_request (GtkWidget *widget,
83 GtkRequisition *requisition);
84 static void gtk_menu_item_size_allocate (GtkWidget *widget,
85 GtkAllocation *allocation);
86 static void gtk_menu_item_realize (GtkWidget *widget);
87 static void gtk_menu_item_unrealize (GtkWidget *widget);
88 static void gtk_menu_item_map (GtkWidget *widget);
89 static void gtk_menu_item_unmap (GtkWidget *widget);
90 static void gtk_menu_item_paint (GtkWidget *widget,
92 static gint gtk_menu_item_expose (GtkWidget *widget,
93 GdkEventExpose *event);
94 static void gtk_menu_item_parent_set (GtkWidget *widget,
95 GtkWidget *previous_parent);
98 static void gtk_real_menu_item_select (GtkItem *item);
99 static void gtk_real_menu_item_deselect (GtkItem *item);
100 static void gtk_real_menu_item_activate (GtkMenuItem *item);
101 static void gtk_real_menu_item_activate_item (GtkMenuItem *item);
102 static void gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
104 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
106 static gboolean gtk_menu_item_mnemonic_activate (GtkWidget *widget,
107 gboolean group_cycling);
109 static void gtk_menu_item_ensure_label (GtkMenuItem *menu_item);
110 static gint gtk_menu_item_popup_timeout (gpointer data);
111 static void gtk_menu_item_position_menu (GtkMenu *menu,
116 static void gtk_menu_item_show_all (GtkWidget *widget);
117 static void gtk_menu_item_hide_all (GtkWidget *widget);
118 static void gtk_menu_item_forall (GtkContainer *container,
119 gboolean include_internals,
120 GtkCallback callback,
121 gpointer callback_data);
122 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
125 static void gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
127 static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
130 static void gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface);
131 static void gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
135 static void gtk_menu_item_buildable_custom_finished(GtkBuildable *buildable,
138 const gchar *tagname,
141 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface);
142 static void gtk_menu_item_update (GtkActivatable *activatable,
144 const gchar *property_name);
145 static void gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
147 static void gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
149 static void gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
150 gboolean use_appearance);
153 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
155 static GtkBuildableIface *parent_buildable_iface;
157 G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM,
158 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
159 gtk_menu_item_buildable_interface_init)
160 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
161 gtk_menu_item_activatable_interface_init))
163 #define GET_PRIVATE(object) \
164 (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate))
167 gtk_menu_item_class_init (GtkMenuItemClass *klass)
169 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
170 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
171 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
172 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
173 GtkItemClass *item_class = GTK_ITEM_CLASS (klass);
175 gobject_class->dispose = gtk_menu_item_dispose;
176 gobject_class->set_property = gtk_menu_item_set_property;
177 gobject_class->get_property = gtk_menu_item_get_property;
179 object_class->destroy = gtk_menu_item_destroy;
181 widget_class->size_request = gtk_menu_item_size_request;
182 widget_class->size_allocate = gtk_menu_item_size_allocate;
183 widget_class->expose_event = gtk_menu_item_expose;
184 widget_class->realize = gtk_menu_item_realize;
185 widget_class->unrealize = gtk_menu_item_unrealize;
186 widget_class->map = gtk_menu_item_map;
187 widget_class->unmap = gtk_menu_item_unmap;
188 widget_class->show_all = gtk_menu_item_show_all;
189 widget_class->hide_all = gtk_menu_item_hide_all;
190 widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
191 widget_class->parent_set = gtk_menu_item_parent_set;
192 widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
194 container_class->forall = gtk_menu_item_forall;
196 item_class->select = gtk_real_menu_item_select;
197 item_class->deselect = gtk_real_menu_item_deselect;
199 klass->activate = gtk_real_menu_item_activate;
200 klass->activate_item = gtk_real_menu_item_activate_item;
201 klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
202 klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
203 klass->set_label = gtk_real_menu_item_set_label;
204 klass->get_label = gtk_real_menu_item_get_label;
206 klass->hide_on_activate = TRUE;
208 menu_item_signals[ACTIVATE] =
209 g_signal_new (I_("activate"),
210 G_OBJECT_CLASS_TYPE (gobject_class),
211 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
212 G_STRUCT_OFFSET (GtkMenuItemClass, activate),
214 _gtk_marshal_VOID__VOID,
216 widget_class->activate_signal = menu_item_signals[ACTIVATE];
218 menu_item_signals[ACTIVATE_ITEM] =
219 g_signal_new (I_("activate-item"),
220 G_OBJECT_CLASS_TYPE (gobject_class),
222 G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
224 _gtk_marshal_VOID__VOID,
227 menu_item_signals[TOGGLE_SIZE_REQUEST] =
228 g_signal_new (I_("toggle-size-request"),
229 G_OBJECT_CLASS_TYPE (gobject_class),
231 G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
233 _gtk_marshal_VOID__POINTER,
237 menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
238 g_signal_new (I_("toggle-size-allocate"),
239 G_OBJECT_CLASS_TYPE (gobject_class),
241 G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
243 _gtk_marshal_VOID__INT,
248 * GtkMenuItem:right-justified:
250 * Sets whether the menu item appears justified at the right side of a menu bar.
254 g_object_class_install_property (gobject_class,
255 PROP_RIGHT_JUSTIFIED,
256 g_param_spec_boolean ("right-justified",
257 P_("Right Justified"),
258 P_("Sets whether the menu item appears justified at the right side of a menu bar"),
260 GTK_PARAM_READWRITE));
263 * GtkMenuItem:submenu:
265 * The submenu attached to the menu item, or NULL if it has none.
269 g_object_class_install_property (gobject_class,
271 g_param_spec_object ("submenu",
273 P_("The submenu attached to the menu item, or NULL if it has none"),
275 GTK_PARAM_READWRITE));
279 * GtkMenuItem:accel-path:
281 * Sets the accelerator path of the menu item, through which runtime
282 * changes of the menu item's accelerator caused by the user can be
283 * identified and saved to persistant storage.
287 g_object_class_install_property (gobject_class,
289 g_param_spec_string ("accel-path",
291 P_("Sets the accelerator path of the menu item"),
293 GTK_PARAM_READWRITE));
298 * The text for the child label.
302 g_object_class_install_property (gobject_class,
304 g_param_spec_string ("label",
306 P_("The text for the child label"),
308 GTK_PARAM_READWRITE));
311 * GtkMenuItem:use-underline:
313 * %TRUE if underlines in the text indicate mnemonics
317 g_object_class_install_property (gobject_class,
319 g_param_spec_boolean ("use-underline",
321 P_("If set, an underline in the text indicates "
322 "the next character should be used for the "
323 "mnemonic accelerator key"),
325 GTK_PARAM_READWRITE));
327 g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
328 g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
330 gtk_widget_class_install_style_property_parser (widget_class,
331 g_param_spec_enum ("selected-shadow-type",
332 "Selected Shadow Type",
333 "Shadow type when item is selected",
334 GTK_TYPE_SHADOW_TYPE,
337 gtk_rc_property_parse_enum);
339 gtk_widget_class_install_style_property (widget_class,
340 g_param_spec_int ("horizontal-padding",
341 "Horizontal Padding",
342 "Padding to left and right of the menu item",
346 GTK_PARAM_READABLE));
348 gtk_widget_class_install_style_property (widget_class,
349 g_param_spec_int ("toggle-spacing",
351 "Space between icon and label",
355 GTK_PARAM_READABLE));
357 gtk_widget_class_install_style_property (widget_class,
358 g_param_spec_int ("arrow-spacing",
360 "Space between label and arrow",
364 GTK_PARAM_READABLE));
366 gtk_widget_class_install_style_property (widget_class,
367 g_param_spec_float ("arrow-scaling",
369 P_("Amount of space used up by arrow, relative to the menu item's font size"),
371 GTK_PARAM_READABLE));
374 * GtkMenuItem:width-chars:
376 * The minimum desired width of the menu item in characters.
380 gtk_widget_class_install_style_property (widget_class,
381 g_param_spec_int ("width-chars",
382 P_("Width in Characters"),
383 P_("The minimum desired width of the menu item in characters"),
385 GTK_PARAM_READABLE));
387 g_type_class_add_private (object_class, sizeof (GtkMenuItemPrivate));
391 gtk_menu_item_init (GtkMenuItem *menu_item)
393 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
395 GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
398 priv->use_action_appearance = TRUE;
400 menu_item->submenu = NULL;
401 menu_item->toggle_size = 0;
402 menu_item->accelerator_width = 0;
403 menu_item->show_submenu_indicator = FALSE;
404 if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
405 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
407 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
408 menu_item->submenu_placement = GTK_TOP_BOTTOM;
409 menu_item->right_justify = FALSE;
411 menu_item->timer = 0;
415 gtk_menu_item_new (void)
417 return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
421 gtk_menu_item_new_with_label (const gchar *label)
423 return g_object_new (GTK_TYPE_MENU_ITEM,
430 * gtk_menu_item_new_with_mnemonic:
431 * @label: The text of the button, with an underscore in front of the
433 * @returns: a new #GtkMenuItem
435 * Creates a new #GtkMenuItem containing a label. The label
436 * will be created using gtk_label_new_with_mnemonic(), so underscores
437 * in @label indicate the mnemonic for the menu item.
440 gtk_menu_item_new_with_mnemonic (const gchar *label)
442 return g_object_new (GTK_TYPE_MENU_ITEM,
443 "use-underline", TRUE,
449 gtk_menu_item_dispose (GObject *object)
451 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
452 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
456 gtk_action_disconnect_accelerator (priv->action);
457 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL);
461 G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object);
465 gtk_menu_item_set_property (GObject *object,
470 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
474 case PROP_RIGHT_JUSTIFIED:
475 gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
478 gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
480 case PROP_ACCEL_PATH:
481 gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
484 gtk_menu_item_set_label (menu_item, g_value_get_string (value));
486 case PROP_USE_UNDERLINE:
487 gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
489 case PROP_ACTIVATABLE_RELATED_ACTION:
490 gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
492 case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
493 gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
496 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
502 gtk_menu_item_get_property (GObject *object,
507 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
508 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
512 case PROP_RIGHT_JUSTIFIED:
513 g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
516 g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
518 case PROP_ACCEL_PATH:
519 g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
522 g_value_set_string (value, gtk_menu_item_get_label (menu_item));
524 case PROP_USE_UNDERLINE:
525 g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
527 case PROP_ACTIVATABLE_RELATED_ACTION:
528 g_value_set_object (value, priv->action);
530 case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
531 g_value_set_boolean (value, priv->use_action_appearance);
534 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
540 gtk_menu_item_destroy (GtkObject *object)
542 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
544 if (menu_item->submenu)
545 gtk_widget_destroy (menu_item->submenu);
547 GTK_OBJECT_CLASS (gtk_menu_item_parent_class)->destroy (object);
551 gtk_menu_item_detacher (GtkWidget *widget,
554 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
556 g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
558 menu_item->submenu = NULL;
562 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
564 parent_buildable_iface = g_type_interface_peek_parent (iface);
565 iface->add_child = gtk_menu_item_buildable_add_child;
566 iface->custom_finished = gtk_menu_item_buildable_custom_finished;
570 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
575 if (type && strcmp (type, "submenu") == 0)
576 gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
579 parent_buildable_iface->add_child (buildable, builder, child, type);
584 gtk_menu_item_buildable_custom_finished (GtkBuildable *buildable,
587 const gchar *tagname,
592 if (strcmp (tagname, "accelerator") == 0)
594 GtkMenuShell *menu_shell = (GtkMenuShell *) GTK_WIDGET (buildable)->parent;
599 while (GTK_IS_MENU (menu_shell) &&
600 (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
601 menu_shell = (GtkMenuShell *)attach->parent;
603 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
607 /* Fall back to something ... */
608 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
610 g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
611 gtk_buildable_get_name (buildable));
614 /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
615 _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
618 parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
623 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
625 iface->update = gtk_menu_item_update;
626 iface->sync_action_properties = gtk_menu_item_sync_action_properties;
630 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
632 GtkWidget *child = GTK_BIN (menu_item)->child;
634 if (GTK_IS_LABEL (child))
638 label = gtk_action_get_label (action);
639 gtk_label_set_label (GTK_LABEL (child), label ? label : "");
643 gboolean _gtk_menu_is_empty (GtkWidget *menu);
646 gtk_menu_item_update (GtkActivatable *activatable,
648 const gchar *property_name)
650 GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
651 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
653 if (strcmp (property_name, "visible") == 0)
654 _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
655 _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
656 else if (strcmp (property_name, "sensitive") == 0)
657 gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
658 else if (priv->use_action_appearance)
660 if (strcmp (property_name, "label") == 0)
661 activatable_update_label (menu_item, action);
666 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
669 GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
670 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
675 _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
676 _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
678 gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
680 if (priv->use_action_appearance)
682 GtkWidget *label = GTK_BIN (menu_item)->child;
684 /* make sure label is a label */
685 if (label && !GTK_IS_LABEL (label))
687 gtk_container_remove (GTK_CONTAINER (menu_item), label);
692 label = g_object_new (GTK_TYPE_ACCEL_LABEL,
693 "use-underline", TRUE,
699 if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
701 "accel-closure", gtk_action_get_accel_closure (action),
704 activatable_update_label (menu_item, action);
709 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
712 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
714 if (priv->action == action)
719 gtk_action_disconnect_accelerator (priv->action);
724 const gchar *accel_path;
726 accel_path = gtk_action_get_accel_path (action);
729 gtk_action_connect_accelerator (action);
730 gtk_menu_item_set_accel_path (menu_item, accel_path);
734 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
736 priv->action = action;
740 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
741 gboolean use_appearance)
743 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
745 if (priv->use_action_appearance != use_appearance)
747 priv->use_action_appearance = use_appearance;
749 gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
755 * gtk_menu_item_set_submenu:
756 * @menu_item: a #GtkMenuItem
757 * @submenu: (allow-none): the submenu, or %NULL
759 * Sets or replaces the menu item's submenu, or removes it when a %NULL
763 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
766 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
767 g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
769 if (menu_item->submenu != submenu)
771 if (menu_item->submenu)
772 gtk_menu_detach (GTK_MENU (menu_item->submenu));
776 menu_item->submenu = submenu;
777 gtk_menu_attach_to_widget (GTK_MENU (submenu),
778 GTK_WIDGET (menu_item),
779 gtk_menu_item_detacher);
782 if (GTK_WIDGET (menu_item)->parent)
783 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
785 g_object_notify (G_OBJECT (menu_item), "submenu");
790 * gtk_menu_item_get_submenu:
791 * @menu_item: a #GtkMenuItem
793 * Gets the submenu underneath this menu item, if any. See
794 * gtk_menu_item_set_submenu().
796 * Return value: submenu for this menu item, or %NULL if none.
799 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
801 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
803 return menu_item->submenu;
807 * gtk_menu_item_remove_submenu:
808 * @menu_item: a #GtkMenuItem
810 * Removes the widget's submenu.
812 * Deprecated: 2.12: gtk_menu_item_remove_submenu() is deprecated and
813 * should not be used in newly written code. Use
814 * gtk_menu_item_set_submenu() instead.
817 gtk_menu_item_remove_submenu (GtkMenuItem *menu_item)
819 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
821 gtk_menu_item_set_submenu (menu_item, NULL);
824 void _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
825 GtkSubmenuPlacement placement);
828 _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
829 GtkSubmenuPlacement placement)
831 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
833 menu_item->submenu_placement = placement;
837 gtk_menu_item_select (GtkMenuItem *menu_item)
839 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
841 gtk_item_select (GTK_ITEM (menu_item));
843 /* Enable themeing of the parent menu item depending on whether
844 * something is selected in its submenu
846 if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
848 GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
850 if (menu->parent_menu_item)
851 gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
856 gtk_menu_item_deselect (GtkMenuItem *menu_item)
858 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
860 gtk_item_deselect (GTK_ITEM (menu_item));
862 /* Enable themeing of the parent menu item depending on whether
863 * something is selected in its submenu
865 if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
867 GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
869 if (menu->parent_menu_item)
870 gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
875 gtk_menu_item_activate (GtkMenuItem *menu_item)
877 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
879 g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
883 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
886 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
888 g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
892 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
895 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
897 g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
901 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
906 if (GTK_IS_ACCEL_LABEL (widget))
910 w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
911 *width = MAX (*width, w);
913 else if (GTK_IS_CONTAINER (widget))
914 gtk_container_foreach (GTK_CONTAINER (widget),
915 gtk_menu_item_accel_width_foreach,
920 get_minimum_width (GtkWidget *widget)
922 PangoContext *context;
923 PangoFontMetrics *metrics;
927 context = gtk_widget_get_pango_context (widget);
928 metrics = pango_context_get_metrics (context,
929 widget->style->font_desc,
930 pango_context_get_language (context));
932 width = pango_font_metrics_get_approximate_char_width (metrics);
934 pango_font_metrics_unref (metrics);
936 gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
938 return PANGO_PIXELS (width_chars * width);
942 gtk_menu_item_size_request (GtkWidget *widget,
943 GtkRequisition *requisition)
945 GtkMenuItem *menu_item;
948 guint horizontal_padding;
949 GtkPackDirection pack_dir;
950 GtkPackDirection child_pack_dir;
952 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
953 g_return_if_fail (requisition != NULL);
955 gtk_widget_style_get (widget,
956 "horizontal-padding", &horizontal_padding,
959 bin = GTK_BIN (widget);
960 menu_item = GTK_MENU_ITEM (widget);
962 if (GTK_IS_MENU_BAR (widget->parent))
964 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
965 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
969 pack_dir = GTK_PACK_DIRECTION_LTR;
970 child_pack_dir = GTK_PACK_DIRECTION_LTR;
973 requisition->width = (GTK_CONTAINER (widget)->border_width +
974 widget->style->xthickness) * 2;
975 requisition->height = (GTK_CONTAINER (widget)->border_width +
976 widget->style->ythickness) * 2;
978 if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
979 (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
980 requisition->width += 2 * horizontal_padding;
981 else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
982 (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
983 requisition->height += 2 * horizontal_padding;
985 if (bin->child && gtk_widget_get_visible (bin->child))
987 GtkRequisition child_requisition;
989 gtk_widget_size_request (bin->child, &child_requisition);
991 requisition->width += child_requisition.width;
992 requisition->height += child_requisition.height;
994 if (menu_item->submenu && menu_item->show_submenu_indicator)
998 gtk_widget_style_get (widget,
999 "arrow-spacing", &arrow_spacing,
1002 requisition->width += child_requisition.height;
1003 requisition->width += arrow_spacing;
1005 requisition->width = MAX (requisition->width, get_minimum_width (widget));
1008 else /* separator item */
1010 gboolean wide_separators;
1011 gint separator_height;
1013 gtk_widget_style_get (widget,
1014 "wide-separators", &wide_separators,
1015 "separator-height", &separator_height,
1018 if (wide_separators)
1019 requisition->height += separator_height + widget->style->ythickness;
1021 requisition->height += widget->style->ythickness * 2;
1025 gtk_container_foreach (GTK_CONTAINER (menu_item),
1026 gtk_menu_item_accel_width_foreach,
1028 menu_item->accelerator_width = accel_width;
1032 gtk_menu_item_size_allocate (GtkWidget *widget,
1033 GtkAllocation *allocation)
1035 GtkMenuItem *menu_item;
1037 GtkAllocation child_allocation;
1038 GtkTextDirection direction;
1039 GtkPackDirection pack_dir;
1040 GtkPackDirection child_pack_dir;
1042 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1043 g_return_if_fail (allocation != NULL);
1045 menu_item = GTK_MENU_ITEM (widget);
1046 bin = GTK_BIN (widget);
1048 direction = gtk_widget_get_direction (widget);
1050 if (GTK_IS_MENU_BAR (widget->parent))
1052 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
1053 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
1057 pack_dir = GTK_PACK_DIRECTION_LTR;
1058 child_pack_dir = GTK_PACK_DIRECTION_LTR;
1061 widget->allocation = *allocation;
1065 GtkRequisition child_requisition;
1066 guint horizontal_padding;
1068 gtk_widget_style_get (widget,
1069 "horizontal-padding", &horizontal_padding,
1072 child_allocation.x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
1073 child_allocation.y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
1075 if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1076 (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1077 child_allocation.x += horizontal_padding;
1078 else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1079 (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1080 child_allocation.y += horizontal_padding;
1082 child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
1083 child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
1085 if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1086 child_pack_dir == GTK_PACK_DIRECTION_RTL)
1088 if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1089 child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
1090 child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
1094 if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1095 child_allocation.y += GTK_MENU_ITEM (widget)->toggle_size;
1096 child_allocation.height -= GTK_MENU_ITEM (widget)->toggle_size;
1099 child_allocation.x += widget->allocation.x;
1100 child_allocation.y += widget->allocation.y;
1102 gtk_widget_get_child_requisition (bin->child, &child_requisition);
1103 if (menu_item->submenu && menu_item->show_submenu_indicator)
1105 if (direction == GTK_TEXT_DIR_RTL)
1106 child_allocation.x += child_requisition.height;
1107 child_allocation.width -= child_requisition.height;
1110 if (child_allocation.width < 1)
1111 child_allocation.width = 1;
1113 gtk_widget_size_allocate (bin->child, &child_allocation);
1116 if (GTK_WIDGET_REALIZED (widget))
1117 gdk_window_move_resize (menu_item->event_window,
1118 allocation->x, allocation->y,
1119 allocation->width, allocation->height);
1121 if (menu_item->submenu)
1122 gtk_menu_reposition (GTK_MENU (menu_item->submenu));
1126 gtk_menu_item_realize (GtkWidget *widget)
1128 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1129 GdkWindowAttr attributes;
1130 gint attributes_mask;
1132 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1134 widget->window = gtk_widget_get_parent_window (widget);
1135 g_object_ref (widget->window);
1137 attributes.x = widget->allocation.x;
1138 attributes.y = widget->allocation.y;
1139 attributes.width = widget->allocation.width;
1140 attributes.height = widget->allocation.height;
1141 attributes.window_type = GDK_WINDOW_CHILD;
1142 attributes.wclass = GDK_INPUT_ONLY;
1143 attributes.event_mask = (gtk_widget_get_events (widget) |
1144 GDK_BUTTON_PRESS_MASK |
1145 GDK_BUTTON_RELEASE_MASK |
1146 GDK_ENTER_NOTIFY_MASK |
1147 GDK_LEAVE_NOTIFY_MASK |
1148 GDK_POINTER_MOTION_MASK);
1150 attributes_mask = GDK_WA_X | GDK_WA_Y;
1151 menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1152 gdk_window_set_user_data (menu_item->event_window, widget);
1154 widget->style = gtk_style_attach (widget->style, widget->window);
1158 gtk_menu_item_unrealize (GtkWidget *widget)
1160 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1162 gdk_window_set_user_data (menu_item->event_window, NULL);
1163 gdk_window_destroy (menu_item->event_window);
1164 menu_item->event_window = NULL;
1166 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1170 gtk_menu_item_map (GtkWidget *widget)
1172 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1174 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1176 gdk_window_show (menu_item->event_window);
1180 gtk_menu_item_unmap (GtkWidget *widget)
1182 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1184 gdk_window_hide (menu_item->event_window);
1186 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1190 gtk_menu_item_paint (GtkWidget *widget,
1193 GtkMenuItem *menu_item;
1194 GtkStateType state_type;
1195 GtkShadowType shadow_type, selected_shadow_type;
1198 gint border_width = GTK_CONTAINER (widget)->border_width;
1200 if (gtk_widget_is_drawable (widget))
1202 menu_item = GTK_MENU_ITEM (widget);
1204 state_type = widget->state;
1206 x = widget->allocation.x + border_width;
1207 y = widget->allocation.y + border_width;
1208 width = widget->allocation.width - border_width * 2;
1209 height = widget->allocation.height - border_width * 2;
1211 if ((state_type == GTK_STATE_PRELIGHT) &&
1212 (GTK_BIN (menu_item)->child))
1214 gtk_widget_style_get (widget,
1215 "selected-shadow-type", &selected_shadow_type,
1217 gtk_paint_box (widget->style,
1220 selected_shadow_type,
1221 area, widget, "menuitem",
1222 x, y, width, height);
1225 if (menu_item->submenu && menu_item->show_submenu_indicator)
1227 gint arrow_x, arrow_y;
1230 guint horizontal_padding;
1231 gfloat arrow_scaling;
1232 GtkTextDirection direction;
1233 GtkArrowType arrow_type;
1234 PangoContext *context;
1235 PangoFontMetrics *metrics;
1237 direction = gtk_widget_get_direction (widget);
1239 gtk_widget_style_get (widget,
1240 "horizontal-padding", &horizontal_padding,
1241 "arrow-scaling", &arrow_scaling,
1244 context = gtk_widget_get_pango_context (GTK_BIN (menu_item)->child);
1245 metrics = pango_context_get_metrics (context,
1246 GTK_WIDGET (GTK_BIN (menu_item)->child)->style->font_desc,
1247 pango_context_get_language (context));
1249 arrow_size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1250 pango_font_metrics_get_descent (metrics)));
1252 pango_font_metrics_unref (metrics);
1254 arrow_extent = arrow_size * arrow_scaling;
1256 shadow_type = GTK_SHADOW_OUT;
1257 if (state_type == GTK_STATE_PRELIGHT)
1258 shadow_type = GTK_SHADOW_IN;
1260 if (direction == GTK_TEXT_DIR_LTR)
1262 arrow_x = x + width - horizontal_padding - arrow_extent;
1263 arrow_type = GTK_ARROW_RIGHT;
1267 arrow_x = x + horizontal_padding;
1268 arrow_type = GTK_ARROW_LEFT;
1271 arrow_y = y + (height - arrow_extent) / 2;
1273 gtk_paint_arrow (widget->style, widget->window,
1274 state_type, shadow_type,
1275 area, widget, "menuitem",
1278 arrow_extent, arrow_extent);
1280 else if (!GTK_BIN (menu_item)->child)
1282 gboolean wide_separators;
1283 gint separator_height;
1284 guint horizontal_padding;
1286 gtk_widget_style_get (widget,
1287 "wide-separators", &wide_separators,
1288 "separator-height", &separator_height,
1289 "horizontal-padding", &horizontal_padding,
1292 if (wide_separators)
1293 gtk_paint_box (widget->style, widget->window,
1294 GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
1295 area, widget, "hseparator",
1296 widget->allocation.x + horizontal_padding + widget->style->xthickness,
1297 widget->allocation.y + (widget->allocation.height -
1299 widget->style->ythickness) / 2,
1300 widget->allocation.width -
1301 2 * (horizontal_padding + widget->style->xthickness),
1304 gtk_paint_hline (widget->style, widget->window,
1305 GTK_STATE_NORMAL, area, widget, "menuitem",
1306 widget->allocation.x + horizontal_padding + widget->style->xthickness,
1307 widget->allocation.x + widget->allocation.width - horizontal_padding - widget->style->xthickness - 1,
1308 widget->allocation.y + (widget->allocation.height -
1309 widget->style->ythickness) / 2);
1315 gtk_menu_item_expose (GtkWidget *widget,
1316 GdkEventExpose *event)
1318 g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
1319 g_return_val_if_fail (event != NULL, FALSE);
1321 if (gtk_widget_is_drawable (widget))
1323 gtk_menu_item_paint (widget, &event->area);
1325 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->expose_event (widget, event);
1332 gtk_real_menu_item_select (GtkItem *item)
1334 GtkMenuItem *menu_item;
1335 gboolean touchscreen_mode;
1337 g_return_if_fail (GTK_IS_MENU_ITEM (item));
1339 menu_item = GTK_MENU_ITEM (item);
1341 g_object_get (gtk_widget_get_settings (GTK_WIDGET (item)),
1342 "gtk-touchscreen-mode", &touchscreen_mode,
1345 if (!touchscreen_mode &&
1346 menu_item->submenu &&
1347 (!GTK_WIDGET_MAPPED (menu_item->submenu) ||
1348 GTK_MENU (menu_item->submenu)->tearoff_active))
1350 _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1353 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
1354 gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1358 gtk_real_menu_item_deselect (GtkItem *item)
1360 GtkMenuItem *menu_item;
1362 g_return_if_fail (GTK_IS_MENU_ITEM (item));
1364 menu_item = GTK_MENU_ITEM (item);
1366 if (menu_item->submenu)
1367 _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1369 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
1370 gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1374 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1375 gboolean group_cycling)
1377 if (GTK_IS_MENU_SHELL (widget->parent))
1378 _gtk_menu_shell_set_keyboard_mode (GTK_MENU_SHELL (widget->parent), TRUE);
1380 if (group_cycling &&
1382 GTK_IS_MENU_SHELL (widget->parent) &&
1383 GTK_MENU_SHELL (widget->parent)->active)
1385 gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
1389 g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1395 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1397 GtkMenuItemPrivate *priv;
1399 priv = GET_PRIVATE (menu_item);
1402 gtk_action_activate (priv->action);
1407 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1409 GtkMenuItemPrivate *priv;
1412 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1414 priv = GET_PRIVATE (menu_item);
1415 widget = GTK_WIDGET (menu_item);
1417 if (widget->parent &&
1418 GTK_IS_MENU_SHELL (widget->parent))
1420 if (menu_item->submenu == NULL)
1421 gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
1425 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
1427 _gtk_menu_shell_activate (menu_shell);
1429 gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
1430 _gtk_menu_item_popup_submenu (widget, FALSE);
1432 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1438 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1441 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1447 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1450 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1452 menu_item->toggle_size = allocation;
1456 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1459 gtk_menu_item_ensure_label (menu_item);
1461 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1463 gtk_label_set_label (GTK_LABEL (GTK_BIN (menu_item)->child), label ? label : "");
1465 g_object_notify (G_OBJECT (menu_item), "label");
1469 static G_CONST_RETURN gchar *
1470 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1472 gtk_menu_item_ensure_label (menu_item);
1474 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1475 return gtk_label_get_label (GTK_LABEL (GTK_BIN (menu_item)->child));
1481 free_timeval (GTimeVal *val)
1483 g_slice_free (GTimeVal, val);
1487 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1488 gboolean remember_exact_time)
1490 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1492 if (gtk_widget_is_sensitive (menu_item->submenu) && widget->parent)
1494 gboolean take_focus;
1495 GtkMenuPositionFunc menu_position_func;
1497 take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent));
1498 gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1501 if (remember_exact_time)
1503 GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1505 g_get_current_time (popup_time);
1507 g_object_set_data_full (G_OBJECT (menu_item->submenu),
1508 "gtk-menu-exact-popup-time", popup_time,
1509 (GDestroyNotify) free_timeval);
1513 g_object_set_data (G_OBJECT (menu_item->submenu),
1514 "gtk-menu-exact-popup-time", NULL);
1517 /* gtk_menu_item_position_menu positions the submenu from the
1518 * menuitems position. If the menuitem doesn't have a window,
1519 * that doesn't work. In that case we use the default
1520 * positioning function instead which places the submenu at the
1524 menu_position_func = gtk_menu_item_position_menu;
1526 menu_position_func = NULL;
1528 gtk_menu_popup (GTK_MENU (menu_item->submenu),
1533 GTK_MENU_SHELL (widget->parent)->button,
1537 /* Enable themeing of the parent menu item depending on whether
1538 * its submenu is shown or not.
1540 gtk_widget_queue_draw (widget);
1544 gtk_menu_item_popup_timeout (gpointer data)
1546 GtkMenuItem *menu_item;
1549 menu_item = GTK_MENU_ITEM (data);
1551 parent = GTK_WIDGET (menu_item)->parent;
1553 if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) ||
1554 (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1556 gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1557 if (menu_item->timer_from_keypress && menu_item->submenu)
1558 GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1561 menu_item->timer = 0;
1567 get_popup_delay (GtkWidget *widget)
1569 if (GTK_IS_MENU_SHELL (widget->parent))
1571 return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (widget->parent));
1577 g_object_get (gtk_widget_get_settings (widget),
1578 "gtk-menu-popup-delay", &popup_delay,
1586 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1587 gboolean with_delay)
1589 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1591 if (menu_item->timer)
1593 g_source_remove (menu_item->timer);
1594 menu_item->timer = 0;
1600 gint popup_delay = get_popup_delay (widget);
1602 if (popup_delay > 0)
1604 GdkEvent *event = gtk_get_current_event ();
1606 menu_item->timer = gdk_threads_add_timeout (popup_delay,
1607 gtk_menu_item_popup_timeout,
1611 event->type != GDK_BUTTON_PRESS &&
1612 event->type != GDK_ENTER_NOTIFY)
1613 menu_item->timer_from_keypress = TRUE;
1615 menu_item->timer_from_keypress = FALSE;
1618 gdk_event_free (event);
1624 gtk_menu_item_real_popup_submenu (widget, FALSE);
1628 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1630 GtkMenuItem *menu_item;
1632 menu_item = GTK_MENU_ITEM (widget);
1634 if (menu_item->submenu)
1636 g_object_set_data (G_OBJECT (menu_item->submenu),
1637 "gtk-menu-exact-popup-time", NULL);
1639 if (menu_item->timer)
1641 g_source_remove (menu_item->timer);
1642 menu_item->timer = 0;
1645 gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1647 gtk_widget_queue_draw (widget);
1652 get_offsets (GtkMenu *menu,
1653 gint *horizontal_offset,
1654 gint *vertical_offset)
1656 gint vertical_padding;
1657 gint horizontal_padding;
1659 gtk_widget_style_get (GTK_WIDGET (menu),
1660 "horizontal-offset", horizontal_offset,
1661 "vertical-offset", vertical_offset,
1662 "horizontal-padding", &horizontal_padding,
1663 "vertical-padding", &vertical_padding,
1666 *vertical_offset -= GTK_WIDGET (menu)->style->ythickness;
1667 *vertical_offset -= vertical_padding;
1668 *horizontal_offset += horizontal_padding;
1672 gtk_menu_item_position_menu (GtkMenu *menu,
1678 GtkMenuItem *menu_item;
1680 GtkMenuItem *parent_menu_item;
1682 gint twidth, theight;
1684 GtkTextDirection direction;
1685 GdkRectangle monitor;
1687 gint horizontal_offset;
1688 gint vertical_offset;
1689 gint parent_xthickness;
1690 gint available_left, available_right;
1692 g_return_if_fail (menu != NULL);
1693 g_return_if_fail (x != NULL);
1694 g_return_if_fail (y != NULL);
1696 menu_item = GTK_MENU_ITEM (user_data);
1697 widget = GTK_WIDGET (user_data);
1702 direction = gtk_widget_get_direction (widget);
1704 twidth = GTK_WIDGET (menu)->requisition.width;
1705 theight = GTK_WIDGET (menu)->requisition.height;
1707 screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1708 monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
1709 if (monitor_num < 0)
1711 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1713 if (!gdk_window_get_origin (widget->window, &tx, &ty))
1715 g_warning ("Menu not on screen");
1719 tx += widget->allocation.x;
1720 ty += widget->allocation.y;
1722 get_offsets (menu, &horizontal_offset, &vertical_offset);
1724 available_left = tx - monitor.x;
1725 available_right = monitor.x + monitor.width - (tx + widget->allocation.width);
1727 if (GTK_IS_MENU_BAR (widget->parent))
1729 menu_item->from_menubar = TRUE;
1731 else if (GTK_IS_MENU (widget->parent))
1733 if (GTK_MENU (widget->parent)->parent_menu_item)
1734 menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item)->from_menubar;
1736 menu_item->from_menubar = FALSE;
1740 menu_item->from_menubar = FALSE;
1743 switch (menu_item->submenu_placement)
1745 case GTK_TOP_BOTTOM:
1746 if (direction == GTK_TEXT_DIR_LTR)
1747 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1750 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1751 tx += widget->allocation.width - twidth;
1753 if ((ty + widget->allocation.height + theight) <= monitor.y + monitor.height)
1754 ty += widget->allocation.height;
1755 else if ((ty - theight) >= monitor.y)
1757 else if (monitor.y + monitor.height - (ty + widget->allocation.height) > ty)
1758 ty += widget->allocation.height;
1763 case GTK_LEFT_RIGHT:
1764 if (GTK_IS_MENU (widget->parent))
1765 parent_menu_item = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item);
1767 parent_menu_item = NULL;
1769 parent_xthickness = widget->parent->style->xthickness;
1771 if (parent_menu_item && !GTK_MENU (widget->parent)->torn_off)
1773 menu_item->submenu_direction = parent_menu_item->submenu_direction;
1777 if (direction == GTK_TEXT_DIR_LTR)
1778 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1780 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1783 switch (menu_item->submenu_direction)
1785 case GTK_DIRECTION_LEFT:
1786 if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
1787 available_left >= available_right)
1788 tx -= twidth + parent_xthickness + horizontal_offset;
1791 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1792 tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1796 case GTK_DIRECTION_RIGHT:
1797 if (tx + widget->allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
1798 available_right >= available_left)
1799 tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1802 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1803 tx -= twidth + parent_xthickness + horizontal_offset;
1808 ty += vertical_offset;
1810 /* If the height of the menu doesn't fit we move it upward. */
1811 ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
1815 /* If we have negative, tx, here it is because we can't get
1816 * the menu all the way on screen. Favor the left portion.
1818 *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
1821 gtk_menu_set_monitor (menu, monitor_num);
1823 if (!gtk_widget_get_visible (menu->toplevel))
1825 gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
1826 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
1831 * gtk_menu_item_set_right_justified:
1832 * @menu_item: a #GtkMenuItem.
1833 * @right_justified: if %TRUE the menu item will appear at the
1834 * far right if added to a menu bar.
1836 * Sets whether the menu item appears justified at the right
1837 * side of a menu bar. This was traditionally done for "Help" menu
1838 * items, but is now considered a bad idea. (If the widget
1839 * layout is reversed for a right-to-left language like Hebrew
1840 * or Arabic, right-justified-menu-items appear at the left.)
1843 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
1844 gboolean right_justified)
1846 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1848 right_justified = right_justified != FALSE;
1850 if (right_justified != menu_item->right_justify)
1852 menu_item->right_justify = right_justified;
1853 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1858 * gtk_menu_item_get_right_justified:
1859 * @menu_item: a #GtkMenuItem
1861 * Gets whether the menu item appears justified at the right
1862 * side of the menu bar.
1864 * Return value: %TRUE if the menu item will appear at the
1865 * far right if added to a menu bar.
1868 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
1870 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
1872 return menu_item->right_justify;
1877 gtk_menu_item_show_all (GtkWidget *widget)
1879 GtkMenuItem *menu_item;
1881 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1883 menu_item = GTK_MENU_ITEM (widget);
1885 /* show children including submenu */
1886 if (menu_item->submenu)
1887 gtk_widget_show_all (menu_item->submenu);
1888 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1890 gtk_widget_show (widget);
1894 gtk_menu_item_hide_all (GtkWidget *widget)
1896 GtkMenuItem *menu_item;
1898 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1900 gtk_widget_hide (widget);
1902 menu_item = GTK_MENU_ITEM (widget);
1904 /* hide children including submenu */
1905 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1906 if (menu_item->submenu)
1907 gtk_widget_hide_all (menu_item->submenu);
1911 gtk_menu_item_can_activate_accel (GtkWidget *widget,
1914 /* Chain to the parent GtkMenu for further checks */
1915 return (gtk_widget_is_sensitive (widget) && gtk_widget_get_visible (widget) &&
1916 widget->parent && gtk_widget_can_activate_accel (widget->parent, signal_id));
1920 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
1923 const gchar **path_p = data;
1927 if (GTK_IS_LABEL (widget))
1929 *path_p = gtk_label_get_text (GTK_LABEL (widget));
1930 if (*path_p && (*path_p)[0] == 0)
1933 else if (GTK_IS_CONTAINER (widget))
1934 gtk_container_foreach (GTK_CONTAINER (widget),
1935 gtk_menu_item_accel_name_foreach,
1941 gtk_menu_item_parent_set (GtkWidget *widget,
1942 GtkWidget *previous_parent)
1944 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1945 GtkMenu *menu = GTK_IS_MENU (widget->parent) ? GTK_MENU (widget->parent) : NULL;
1948 _gtk_menu_item_refresh_accel_path (menu_item,
1953 if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
1954 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
1958 _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item,
1959 const gchar *prefix,
1960 GtkAccelGroup *accel_group,
1961 gboolean group_changed)
1966 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1967 g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1969 widget = GTK_WIDGET (menu_item);
1973 gtk_widget_set_accel_path (widget, NULL, NULL);
1977 path = _gtk_widget_get_accel_path (widget, NULL);
1978 if (!path) /* no active accel_path yet */
1980 path = menu_item->accel_path;
1981 if (!path && prefix)
1983 const gchar *postfix = NULL;
1986 /* try to construct one from label text */
1987 gtk_container_foreach (GTK_CONTAINER (menu_item),
1988 gtk_menu_item_accel_name_foreach,
1992 new_path = g_strconcat (prefix, "/", postfix, NULL);
1993 path = menu_item->accel_path = (char*)g_intern_string (new_path);
1998 gtk_widget_set_accel_path (widget, path, accel_group);
2000 else if (group_changed) /* reinstall accelerators */
2001 gtk_widget_set_accel_path (widget, path, accel_group);
2005 * gtk_menu_item_set_accel_path
2006 * @menu_item: a valid #GtkMenuItem
2007 * @accel_path: (allow-none): accelerator path, corresponding to this menu item's
2008 * functionality, or %NULL to unset the current path.
2010 * Set the accelerator path on @menu_item, through which runtime changes of the
2011 * menu item's accelerator caused by the user can be identified and saved to
2012 * persistant storage (see gtk_accel_map_save() on this).
2013 * To setup a default accelerator for this menu item, call
2014 * gtk_accel_map_add_entry() with the same @accel_path.
2015 * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
2016 * and gtk_menu_set_accel_path() for a more convenient variant of this function.
2018 * This function is basically a convenience wrapper that handles calling
2019 * gtk_widget_set_accel_path() with the appropriate accelerator group for
2022 * Note that you do need to set an accelerator on the parent menu with
2023 * gtk_menu_set_accel_group() for this to work.
2025 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
2026 * pass a static string, you can save some memory by interning it first with
2027 * g_intern_static_string().
2030 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2031 const gchar *accel_path)
2035 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2036 g_return_if_fail (accel_path == NULL ||
2037 (accel_path[0] == '<' && strchr (accel_path, '/')));
2039 widget = GTK_WIDGET (menu_item);
2041 /* store new path */
2042 menu_item->accel_path = (char*)g_intern_string (accel_path);
2044 /* forget accelerators associated with old path */
2045 gtk_widget_set_accel_path (widget, NULL, NULL);
2047 /* install accelerators associated with new path */
2048 if (GTK_IS_MENU (widget->parent))
2050 GtkMenu *menu = GTK_MENU (widget->parent);
2052 if (menu->accel_group)
2053 _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2061 * gtk_menu_item_get_accel_path
2062 * @menu_item: a valid #GtkMenuItem
2064 * Retrieve the accelerator path that was previously set on @menu_item.
2066 * See gtk_menu_item_set_accel_path() for details.
2068 * Returns: the accelerator path corresponding to this menu item's
2069 * functionality, or %NULL if not set
2073 G_CONST_RETURN gchar *
2074 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2076 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2078 return menu_item->accel_path;
2082 gtk_menu_item_forall (GtkContainer *container,
2083 gboolean include_internals,
2084 GtkCallback callback,
2085 gpointer callback_data)
2089 g_return_if_fail (GTK_IS_MENU_ITEM (container));
2090 g_return_if_fail (callback != NULL);
2092 bin = GTK_BIN (container);
2095 callback (bin->child, callback_data);
2099 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2101 if ((!GTK_BIN (menu_item)->child &&
2102 G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2103 GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2104 !gtk_widget_is_sensitive (menu_item) ||
2105 !gtk_widget_get_visible (menu_item))
2112 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2114 GtkWidget *accel_label;
2116 if (!GTK_BIN (menu_item)->child)
2118 accel_label = (GtkWidget *)g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2119 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2121 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2122 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
2123 GTK_WIDGET (menu_item));
2124 gtk_widget_show (accel_label);
2129 * gtk_menu_item_set_label:
2130 * @menu_item: a #GtkMenuItem
2131 * @label: the text you want to set
2133 * Sets @text on the @menu_item label
2138 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2141 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2143 GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2147 * gtk_menu_item_get_label:
2148 * @menu_item: a #GtkMenuItem
2150 * Sets @text on the @menu_item label
2152 * Returns: The text in the @menu_item label. This is the internal
2153 * string used by the label, and must not be modified.
2157 G_CONST_RETURN gchar *
2158 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2160 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2162 return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2166 * gtk_menu_item_set_use_underline:
2167 * @menu_item: a #GtkMenuItem
2168 * @setting: %TRUE if underlines in the text indicate mnemonics
2170 * If true, an underline in the text indicates the next character should be
2171 * used for the mnemonic accelerator key.
2176 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2179 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2181 gtk_menu_item_ensure_label (menu_item);
2183 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2185 gtk_label_set_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child), setting);
2187 g_object_notify (G_OBJECT (menu_item), "use-underline");
2192 * gtk_menu_item_get_use_underline:
2193 * @menu_item: a #GtkMenuItem
2195 * Checks if an underline in the text indicates the next character should be
2196 * used for the mnemonic accelerator key.
2198 * Return value: %TRUE if an embedded underline in the label indicates
2199 * the mnemonic accelerator key.
2204 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2206 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2208 gtk_menu_item_ensure_label (menu_item);
2210 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2211 return gtk_label_get_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child));
2218 #define __GTK_MENU_ITEM_C__
2219 #include "gtkaliasdef.c"