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: 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_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_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_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 (group_cycling &&
1379 GTK_IS_MENU_SHELL (widget->parent) &&
1380 GTK_MENU_SHELL (widget->parent)->active)
1382 gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
1386 g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1392 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1394 GtkMenuItemPrivate *priv;
1396 priv = GET_PRIVATE (menu_item);
1399 gtk_action_activate (priv->action);
1404 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1406 GtkMenuItemPrivate *priv;
1409 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1411 priv = GET_PRIVATE (menu_item);
1412 widget = GTK_WIDGET (menu_item);
1414 if (widget->parent &&
1415 GTK_IS_MENU_SHELL (widget->parent))
1417 if (menu_item->submenu == NULL)
1418 gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
1422 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
1424 _gtk_menu_shell_activate (menu_shell);
1426 gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
1427 _gtk_menu_item_popup_submenu (widget, FALSE);
1429 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1435 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1438 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1444 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1447 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1449 menu_item->toggle_size = allocation;
1453 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1456 gtk_menu_item_ensure_label (menu_item);
1458 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1460 gtk_label_set_label (GTK_LABEL (GTK_BIN (menu_item)->child), label ? label : "");
1462 g_object_notify (G_OBJECT (menu_item), "label");
1466 static G_CONST_RETURN gchar *
1467 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1469 gtk_menu_item_ensure_label (menu_item);
1471 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1472 return gtk_label_get_label (GTK_LABEL (GTK_BIN (menu_item)->child));
1478 free_timeval (GTimeVal *val)
1480 g_slice_free (GTimeVal, val);
1484 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1485 gboolean remember_exact_time)
1487 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1489 if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu) && widget->parent)
1491 gboolean take_focus;
1492 GtkMenuPositionFunc menu_position_func;
1494 take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent));
1495 gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1498 if (remember_exact_time)
1500 GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1502 g_get_current_time (popup_time);
1504 g_object_set_data_full (G_OBJECT (menu_item->submenu),
1505 "gtk-menu-exact-popup-time", popup_time,
1506 (GDestroyNotify) free_timeval);
1510 g_object_set_data (G_OBJECT (menu_item->submenu),
1511 "gtk-menu-exact-popup-time", NULL);
1514 /* gtk_menu_item_position_menu positions the submenu from the
1515 * menuitems position. If the menuitem doesn't have a window,
1516 * that doesn't work. In that case we use the default
1517 * positioning function instead which places the submenu at the
1521 menu_position_func = gtk_menu_item_position_menu;
1523 menu_position_func = NULL;
1525 gtk_menu_popup (GTK_MENU (menu_item->submenu),
1530 GTK_MENU_SHELL (widget->parent)->button,
1534 /* Enable themeing of the parent menu item depending on whether
1535 * its submenu is shown or not.
1537 gtk_widget_queue_draw (widget);
1541 gtk_menu_item_popup_timeout (gpointer data)
1543 GtkMenuItem *menu_item;
1546 menu_item = GTK_MENU_ITEM (data);
1548 parent = GTK_WIDGET (menu_item)->parent;
1550 if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) ||
1551 (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1553 gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1554 if (menu_item->timer_from_keypress && menu_item->submenu)
1555 GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1558 menu_item->timer = 0;
1564 get_popup_delay (GtkWidget *widget)
1566 if (GTK_IS_MENU_SHELL (widget->parent))
1568 return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (widget->parent));
1574 g_object_get (gtk_widget_get_settings (widget),
1575 "gtk-menu-popup-delay", &popup_delay,
1583 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1584 gboolean with_delay)
1586 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1588 if (menu_item->timer)
1590 g_source_remove (menu_item->timer);
1591 menu_item->timer = 0;
1597 gint popup_delay = get_popup_delay (widget);
1599 if (popup_delay > 0)
1601 GdkEvent *event = gtk_get_current_event ();
1603 menu_item->timer = gdk_threads_add_timeout (popup_delay,
1604 gtk_menu_item_popup_timeout,
1608 event->type != GDK_BUTTON_PRESS &&
1609 event->type != GDK_ENTER_NOTIFY)
1610 menu_item->timer_from_keypress = TRUE;
1612 menu_item->timer_from_keypress = FALSE;
1615 gdk_event_free (event);
1621 gtk_menu_item_real_popup_submenu (widget, FALSE);
1625 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1627 GtkMenuItem *menu_item;
1629 menu_item = GTK_MENU_ITEM (widget);
1631 if (menu_item->submenu)
1633 g_object_set_data (G_OBJECT (menu_item->submenu),
1634 "gtk-menu-exact-popup-time", NULL);
1636 if (menu_item->timer)
1638 g_source_remove (menu_item->timer);
1639 menu_item->timer = 0;
1642 gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1644 gtk_widget_queue_draw (widget);
1649 get_offsets (GtkMenu *menu,
1650 gint *horizontal_offset,
1651 gint *vertical_offset)
1653 gint vertical_padding;
1654 gint horizontal_padding;
1656 gtk_widget_style_get (GTK_WIDGET (menu),
1657 "horizontal-offset", horizontal_offset,
1658 "vertical-offset", vertical_offset,
1659 "horizontal-padding", &horizontal_padding,
1660 "vertical-padding", &vertical_padding,
1663 *vertical_offset -= GTK_WIDGET (menu)->style->ythickness;
1664 *vertical_offset -= vertical_padding;
1665 *horizontal_offset += horizontal_padding;
1669 gtk_menu_item_position_menu (GtkMenu *menu,
1675 GtkMenuItem *menu_item;
1677 GtkMenuItem *parent_menu_item;
1679 gint twidth, theight;
1681 GtkTextDirection direction;
1682 GdkRectangle monitor;
1684 gint horizontal_offset;
1685 gint vertical_offset;
1686 gint parent_xthickness;
1687 gint available_left, available_right;
1689 g_return_if_fail (menu != NULL);
1690 g_return_if_fail (x != NULL);
1691 g_return_if_fail (y != NULL);
1693 menu_item = GTK_MENU_ITEM (user_data);
1694 widget = GTK_WIDGET (user_data);
1699 direction = gtk_widget_get_direction (widget);
1701 twidth = GTK_WIDGET (menu)->requisition.width;
1702 theight = GTK_WIDGET (menu)->requisition.height;
1704 screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1705 monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
1706 if (monitor_num < 0)
1708 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1710 if (!gdk_window_get_origin (widget->window, &tx, &ty))
1712 g_warning ("Menu not on screen");
1716 tx += widget->allocation.x;
1717 ty += widget->allocation.y;
1719 get_offsets (menu, &horizontal_offset, &vertical_offset);
1721 available_left = tx - monitor.x;
1722 available_right = monitor.x + monitor.width - (tx + widget->allocation.width);
1724 if (GTK_IS_MENU_BAR (widget->parent))
1726 menu_item->from_menubar = TRUE;
1728 else if (GTK_IS_MENU (widget->parent))
1730 if (GTK_MENU (widget->parent)->parent_menu_item)
1731 menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item)->from_menubar;
1733 menu_item->from_menubar = FALSE;
1737 menu_item->from_menubar = FALSE;
1740 switch (menu_item->submenu_placement)
1742 case GTK_TOP_BOTTOM:
1743 if (direction == GTK_TEXT_DIR_LTR)
1744 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1747 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1748 tx += widget->allocation.width - twidth;
1750 if ((ty + widget->allocation.height + theight) <= monitor.y + monitor.height)
1751 ty += widget->allocation.height;
1752 else if ((ty - theight) >= monitor.y)
1754 else if (monitor.y + monitor.height - (ty + widget->allocation.height) > ty)
1755 ty += widget->allocation.height;
1760 case GTK_LEFT_RIGHT:
1761 if (GTK_IS_MENU (widget->parent))
1762 parent_menu_item = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item);
1764 parent_menu_item = NULL;
1766 parent_xthickness = widget->parent->style->xthickness;
1768 if (parent_menu_item && !GTK_MENU (widget->parent)->torn_off)
1770 menu_item->submenu_direction = parent_menu_item->submenu_direction;
1774 if (direction == GTK_TEXT_DIR_LTR)
1775 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1777 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1780 switch (menu_item->submenu_direction)
1782 case GTK_DIRECTION_LEFT:
1783 if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
1784 available_left >= available_right)
1785 tx -= twidth + parent_xthickness + horizontal_offset;
1788 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1789 tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1793 case GTK_DIRECTION_RIGHT:
1794 if (tx + widget->allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
1795 available_right >= available_left)
1796 tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1799 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1800 tx -= twidth + parent_xthickness + horizontal_offset;
1805 ty += vertical_offset;
1807 /* If the height of the menu doesn't fit we move it upward. */
1808 ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
1812 /* If we have negative, tx, here it is because we can't get
1813 * the menu all the way on screen. Favor the left portion.
1815 *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
1818 gtk_menu_set_monitor (menu, monitor_num);
1820 if (!GTK_WIDGET_VISIBLE (menu->toplevel))
1822 gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
1823 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
1828 * gtk_menu_item_set_right_justified:
1829 * @menu_item: a #GtkMenuItem.
1830 * @right_justified: if %TRUE the menu item will appear at the
1831 * far right if added to a menu bar.
1833 * Sets whether the menu item appears justified at the right
1834 * side of a menu bar. This was traditionally done for "Help" menu
1835 * items, but is now considered a bad idea. (If the widget
1836 * layout is reversed for a right-to-left language like Hebrew
1837 * or Arabic, right-justified-menu-items appear at the left.)
1840 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
1841 gboolean right_justified)
1843 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1845 right_justified = right_justified != FALSE;
1847 if (right_justified != menu_item->right_justify)
1849 menu_item->right_justify = right_justified;
1850 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1855 * gtk_menu_item_get_right_justified:
1856 * @menu_item: a #GtkMenuItem
1858 * Gets whether the menu item appears justified at the right
1859 * side of the menu bar.
1861 * Return value: %TRUE if the menu item will appear at the
1862 * far right if added to a menu bar.
1865 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
1867 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
1869 return menu_item->right_justify;
1874 gtk_menu_item_show_all (GtkWidget *widget)
1876 GtkMenuItem *menu_item;
1878 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1880 menu_item = GTK_MENU_ITEM (widget);
1882 /* show children including submenu */
1883 if (menu_item->submenu)
1884 gtk_widget_show_all (menu_item->submenu);
1885 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1887 gtk_widget_show (widget);
1891 gtk_menu_item_hide_all (GtkWidget *widget)
1893 GtkMenuItem *menu_item;
1895 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1897 gtk_widget_hide (widget);
1899 menu_item = GTK_MENU_ITEM (widget);
1901 /* hide children including submenu */
1902 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1903 if (menu_item->submenu)
1904 gtk_widget_hide_all (menu_item->submenu);
1908 gtk_menu_item_can_activate_accel (GtkWidget *widget,
1911 /* Chain to the parent GtkMenu for further checks */
1912 return (GTK_WIDGET_IS_SENSITIVE (widget) && GTK_WIDGET_VISIBLE (widget) &&
1913 widget->parent && gtk_widget_can_activate_accel (widget->parent, signal_id));
1917 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
1920 const gchar **path_p = data;
1924 if (GTK_IS_LABEL (widget))
1926 *path_p = gtk_label_get_text (GTK_LABEL (widget));
1927 if (*path_p && (*path_p)[0] == 0)
1930 else if (GTK_IS_CONTAINER (widget))
1931 gtk_container_foreach (GTK_CONTAINER (widget),
1932 gtk_menu_item_accel_name_foreach,
1938 gtk_menu_item_parent_set (GtkWidget *widget,
1939 GtkWidget *previous_parent)
1941 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1942 GtkMenu *menu = GTK_IS_MENU (widget->parent) ? GTK_MENU (widget->parent) : NULL;
1945 _gtk_menu_item_refresh_accel_path (menu_item,
1950 if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
1951 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
1955 _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item,
1956 const gchar *prefix,
1957 GtkAccelGroup *accel_group,
1958 gboolean group_changed)
1963 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1964 g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1966 widget = GTK_WIDGET (menu_item);
1970 gtk_widget_set_accel_path (widget, NULL, NULL);
1974 path = _gtk_widget_get_accel_path (widget, NULL);
1975 if (!path) /* no active accel_path yet */
1977 path = menu_item->accel_path;
1978 if (!path && prefix)
1980 const gchar *postfix = NULL;
1983 /* try to construct one from label text */
1984 gtk_container_foreach (GTK_CONTAINER (menu_item),
1985 gtk_menu_item_accel_name_foreach,
1989 new_path = g_strconcat (prefix, "/", postfix, NULL);
1990 path = menu_item->accel_path = (char*)g_intern_string (new_path);
1995 gtk_widget_set_accel_path (widget, path, accel_group);
1997 else if (group_changed) /* reinstall accelerators */
1998 gtk_widget_set_accel_path (widget, path, accel_group);
2002 * gtk_menu_item_set_accel_path
2003 * @menu_item: a valid #GtkMenuItem
2004 * @accel_path: accelerator path, corresponding to this menu item's
2005 * functionality, or %NULL to unset the current path.
2007 * Set the accelerator path on @menu_item, through which runtime changes of the
2008 * menu item's accelerator caused by the user can be identified and saved to
2009 * persistant storage (see gtk_accel_map_save() on this).
2010 * To setup a default accelerator for this menu item, call
2011 * gtk_accel_map_add_entry() with the same @accel_path.
2012 * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
2013 * and gtk_menu_set_accel_path() for a more convenient variant of this function.
2015 * This function is basically a convenience wrapper that handles calling
2016 * gtk_widget_set_accel_path() with the appropriate accelerator group for
2019 * Note that you do need to set an accelerator on the parent menu with
2020 * gtk_menu_set_accel_group() for this to work.
2022 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
2023 * pass a static string, you can save some memory by interning it first with
2024 * g_intern_static_string().
2027 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2028 const gchar *accel_path)
2032 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2033 g_return_if_fail (accel_path == NULL ||
2034 (accel_path[0] == '<' && strchr (accel_path, '/')));
2036 widget = GTK_WIDGET (menu_item);
2038 /* store new path */
2039 menu_item->accel_path = (char*)g_intern_string (accel_path);
2041 /* forget accelerators associated with old path */
2042 gtk_widget_set_accel_path (widget, NULL, NULL);
2044 /* install accelerators associated with new path */
2045 if (GTK_IS_MENU (widget->parent))
2047 GtkMenu *menu = GTK_MENU (widget->parent);
2049 if (menu->accel_group)
2050 _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2058 * gtk_menu_item_get_accel_path
2059 * @menu_item: a valid #GtkMenuItem
2061 * Retrieve the accelerator path that was previously set on @menu_item.
2063 * See gtk_menu_item_set_accel_path() for details.
2065 * Returns: the accelerator path corresponding to this menu item's
2066 * functionality, or %NULL if not set
2070 G_CONST_RETURN gchar *
2071 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2073 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2075 return menu_item->accel_path;
2079 gtk_menu_item_forall (GtkContainer *container,
2080 gboolean include_internals,
2081 GtkCallback callback,
2082 gpointer callback_data)
2086 g_return_if_fail (GTK_IS_MENU_ITEM (container));
2087 g_return_if_fail (callback != NULL);
2089 bin = GTK_BIN (container);
2092 callback (bin->child, callback_data);
2096 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2098 if ((!GTK_BIN (menu_item)->child &&
2099 G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2100 GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2101 !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
2102 !GTK_WIDGET_VISIBLE (menu_item))
2109 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2111 GtkWidget *accel_label;
2113 if (!GTK_BIN (menu_item)->child)
2115 accel_label = (GtkWidget *)g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2116 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2118 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2119 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
2120 GTK_WIDGET (menu_item));
2121 gtk_widget_show (accel_label);
2126 * gtk_menu_item_set_label:
2127 * @menu_item: a #GtkMenuItem
2128 * @label: the text you want to set
2130 * Sets @text on the @menu_item label
2135 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2138 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2140 GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2144 * gtk_menu_item_get_label:
2145 * @menu_item: a #GtkMenuItem
2147 * Sets @text on the @menu_item label
2149 * Returns: The text in the @menu_item label. This is the internal
2150 * string used by the label, and must not be modified.
2154 G_CONST_RETURN gchar *
2155 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2157 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2159 return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2163 * gtk_menu_item_set_use_underline:
2164 * @menu_item: a #GtkMenuItem
2165 * @setting: %TRUE if underlines in the text indicate mnemonics
2167 * If true, an underline in the text indicates the next character should be
2168 * used for the mnemonic accelerator key.
2173 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2176 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2178 gtk_menu_item_ensure_label (menu_item);
2180 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2182 gtk_label_set_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child), setting);
2184 g_object_notify (G_OBJECT (menu_item), "use-underline");
2189 * gtk_menu_item_get_use_underline:
2190 * @menu_item: a #GtkMenuItem
2192 * Checks if an underline in the text indicates the next character should be
2193 * used for the mnemonic accelerator key.
2195 * Return value: %TRUE if an embedded underline in the label indicates
2196 * the mnemonic accelerator key.
2201 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2203 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2205 gtk_menu_item_ensure_label (menu_item);
2207 if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2208 return gtk_label_get_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child));
2215 #define __GTK_MENU_ITEM_C__
2216 #include "gtkaliasdef.c"