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/.
30 #include "gtkaccellabel.h"
32 #include "gtkmarshalers.h"
34 #include "gtkmenubar.h"
35 #include "gtkmenuprivate.h"
36 #include "gtkseparatormenuitem.h"
37 #include "gtkprivate.h"
38 #include "gtkbuildable.h"
39 #include "gtkactivatable.h"
45 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 (GtkWidget *widget);
82 static void gtk_menu_item_size_allocate (GtkWidget *widget,
83 GtkAllocation *allocation);
84 static void gtk_menu_item_realize (GtkWidget *widget);
85 static void gtk_menu_item_unrealize (GtkWidget *widget);
86 static void gtk_menu_item_map (GtkWidget *widget);
87 static void gtk_menu_item_unmap (GtkWidget *widget);
88 static gboolean gtk_menu_item_enter (GtkWidget *widget,
89 GdkEventCrossing *event);
90 static gboolean gtk_menu_item_leave (GtkWidget *widget,
91 GdkEventCrossing *event);
92 static gboolean gtk_menu_item_draw (GtkWidget *widget,
94 static void gtk_menu_item_parent_set (GtkWidget *widget,
95 GtkWidget *previous_parent);
98 static void gtk_real_menu_item_select (GtkMenuItem *item);
99 static void gtk_real_menu_item_deselect (GtkMenuItem *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_forall (GtkContainer *container,
118 gboolean include_internals,
119 GtkCallback callback,
120 gpointer callback_data);
121 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
124 static void gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
126 static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
128 static void gtk_menu_item_get_preferred_width (GtkWidget *widget,
131 static void gtk_menu_item_get_preferred_height (GtkWidget *widget,
134 static void gtk_menu_item_get_preferred_height_for_width (GtkWidget *widget,
139 static void gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface);
140 static void gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
144 static void gtk_menu_item_buildable_custom_finished(GtkBuildable *buildable,
147 const gchar *tagname,
150 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface);
151 static void gtk_menu_item_update (GtkActivatable *activatable,
153 const gchar *property_name);
154 static void gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
156 static void gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
158 static void gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
159 gboolean use_appearance);
162 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
164 static GtkBuildableIface *parent_buildable_iface;
166 G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_BIN,
167 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
168 gtk_menu_item_buildable_interface_init)
169 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
170 gtk_menu_item_activatable_interface_init))
172 #define GET_PRIVATE(object) \
173 (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate))
176 gtk_menu_item_class_init (GtkMenuItemClass *klass)
178 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
179 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
180 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
182 gobject_class->dispose = gtk_menu_item_dispose;
183 gobject_class->set_property = gtk_menu_item_set_property;
184 gobject_class->get_property = gtk_menu_item_get_property;
186 widget_class->destroy = gtk_menu_item_destroy;
187 widget_class->size_allocate = gtk_menu_item_size_allocate;
188 widget_class->draw = gtk_menu_item_draw;
189 widget_class->realize = gtk_menu_item_realize;
190 widget_class->unrealize = gtk_menu_item_unrealize;
191 widget_class->map = gtk_menu_item_map;
192 widget_class->unmap = gtk_menu_item_unmap;
193 widget_class->enter_notify_event = gtk_menu_item_enter;
194 widget_class->leave_notify_event = gtk_menu_item_leave;
195 widget_class->show_all = gtk_menu_item_show_all;
196 widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
197 widget_class->parent_set = gtk_menu_item_parent_set;
198 widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
199 widget_class->get_preferred_width = gtk_menu_item_get_preferred_width;
200 widget_class->get_preferred_height = gtk_menu_item_get_preferred_height;
201 widget_class->get_preferred_height_for_width = gtk_menu_item_get_preferred_height_for_width;
203 container_class->forall = gtk_menu_item_forall;
205 klass->activate = gtk_real_menu_item_activate;
206 klass->activate_item = gtk_real_menu_item_activate_item;
207 klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
208 klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
209 klass->set_label = gtk_real_menu_item_set_label;
210 klass->get_label = gtk_real_menu_item_get_label;
211 klass->select = gtk_real_menu_item_select;
212 klass->deselect = gtk_real_menu_item_deselect;
214 klass->hide_on_activate = TRUE;
216 menu_item_signals[ACTIVATE] =
217 g_signal_new (I_("activate"),
218 G_OBJECT_CLASS_TYPE (gobject_class),
219 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
220 G_STRUCT_OFFSET (GtkMenuItemClass, activate),
222 _gtk_marshal_VOID__VOID,
224 widget_class->activate_signal = menu_item_signals[ACTIVATE];
226 menu_item_signals[ACTIVATE_ITEM] =
227 g_signal_new (I_("activate-item"),
228 G_OBJECT_CLASS_TYPE (gobject_class),
230 G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
232 _gtk_marshal_VOID__VOID,
235 menu_item_signals[TOGGLE_SIZE_REQUEST] =
236 g_signal_new (I_("toggle-size-request"),
237 G_OBJECT_CLASS_TYPE (gobject_class),
239 G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
241 _gtk_marshal_VOID__POINTER,
245 menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
246 g_signal_new (I_("toggle-size-allocate"),
247 G_OBJECT_CLASS_TYPE (gobject_class),
249 G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
251 _gtk_marshal_VOID__INT,
255 menu_item_signals[SELECT] =
256 g_signal_new (I_("select"),
257 G_OBJECT_CLASS_TYPE (gobject_class),
259 G_STRUCT_OFFSET (GtkMenuItemClass, select),
261 _gtk_marshal_VOID__VOID,
264 menu_item_signals[DESELECT] =
265 g_signal_new (I_("deselect"),
266 G_OBJECT_CLASS_TYPE (gobject_class),
268 G_STRUCT_OFFSET (GtkMenuItemClass, deselect),
270 _gtk_marshal_VOID__VOID,
274 * GtkMenuItem:right-justified:
276 * Sets whether the menu item appears justified at the right side of a menu bar.
280 g_object_class_install_property (gobject_class,
281 PROP_RIGHT_JUSTIFIED,
282 g_param_spec_boolean ("right-justified",
283 P_("Right Justified"),
284 P_("Sets whether the menu item appears justified at the right side of a menu bar"),
286 GTK_PARAM_READWRITE));
289 * GtkMenuItem:submenu:
291 * The submenu attached to the menu item, or NULL if it has none.
295 g_object_class_install_property (gobject_class,
297 g_param_spec_object ("submenu",
299 P_("The submenu attached to the menu item, or NULL if it has none"),
301 GTK_PARAM_READWRITE));
305 * GtkMenuItem:accel-path:
307 * Sets the accelerator path of the menu item, through which runtime
308 * changes of the menu item's accelerator caused by the user can be
309 * identified and saved to persistant storage.
313 g_object_class_install_property (gobject_class,
315 g_param_spec_string ("accel-path",
317 P_("Sets the accelerator path of the menu item"),
319 GTK_PARAM_READWRITE));
324 * The text for the child label.
328 g_object_class_install_property (gobject_class,
330 g_param_spec_string ("label",
332 P_("The text for the child label"),
334 GTK_PARAM_READWRITE));
337 * GtkMenuItem:use-underline:
339 * %TRUE if underlines in the text indicate mnemonics
343 g_object_class_install_property (gobject_class,
345 g_param_spec_boolean ("use-underline",
347 P_("If set, an underline in the text indicates "
348 "the next character should be used for the "
349 "mnemonic accelerator key"),
351 GTK_PARAM_READWRITE));
353 g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
354 g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
356 gtk_widget_class_install_style_property_parser (widget_class,
357 g_param_spec_enum ("selected-shadow-type",
358 "Selected Shadow Type",
359 "Shadow type when item is selected",
360 GTK_TYPE_SHADOW_TYPE,
363 gtk_rc_property_parse_enum);
365 gtk_widget_class_install_style_property (widget_class,
366 g_param_spec_int ("horizontal-padding",
367 "Horizontal Padding",
368 "Padding to left and right of the menu item",
372 GTK_PARAM_READABLE));
374 gtk_widget_class_install_style_property (widget_class,
375 g_param_spec_int ("toggle-spacing",
377 "Space between icon and label",
381 GTK_PARAM_READABLE));
383 gtk_widget_class_install_style_property (widget_class,
384 g_param_spec_int ("arrow-spacing",
386 "Space between label and arrow",
390 GTK_PARAM_READABLE));
392 gtk_widget_class_install_style_property (widget_class,
393 g_param_spec_float ("arrow-scaling",
395 P_("Amount of space used up by arrow, relative to the menu item's font size"),
397 GTK_PARAM_READABLE));
400 * GtkMenuItem:width-chars:
402 * The minimum desired width of the menu item in characters.
406 gtk_widget_class_install_style_property (widget_class,
407 g_param_spec_int ("width-chars",
408 P_("Width in Characters"),
409 P_("The minimum desired width of the menu item in characters"),
411 GTK_PARAM_READABLE));
413 g_type_class_add_private (klass, sizeof (GtkMenuItemPrivate));
417 gtk_menu_item_init (GtkMenuItem *menu_item)
419 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
421 gtk_widget_set_has_window (GTK_WIDGET (menu_item), FALSE);
424 priv->use_action_appearance = TRUE;
426 menu_item->submenu = NULL;
427 menu_item->toggle_size = 0;
428 menu_item->accelerator_width = 0;
429 menu_item->show_submenu_indicator = FALSE;
430 if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
431 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
433 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
434 menu_item->submenu_placement = GTK_TOP_BOTTOM;
435 menu_item->right_justify = FALSE;
437 menu_item->timer = 0;
441 gtk_menu_item_new (void)
443 return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
447 gtk_menu_item_new_with_label (const gchar *label)
449 return g_object_new (GTK_TYPE_MENU_ITEM,
456 * gtk_menu_item_new_with_mnemonic:
457 * @label: The text of the button, with an underscore in front of the
459 * @returns: a new #GtkMenuItem
461 * Creates a new #GtkMenuItem containing a label. The label
462 * will be created using gtk_label_new_with_mnemonic(), so underscores
463 * in @label indicate the mnemonic for the menu item.
466 gtk_menu_item_new_with_mnemonic (const gchar *label)
468 return g_object_new (GTK_TYPE_MENU_ITEM,
469 "use-underline", TRUE,
475 gtk_menu_item_dispose (GObject *object)
477 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
478 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
482 gtk_action_disconnect_accelerator (priv->action);
483 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL);
487 G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object);
491 gtk_menu_item_set_property (GObject *object,
496 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
500 case PROP_RIGHT_JUSTIFIED:
501 gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
504 gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
506 case PROP_ACCEL_PATH:
507 gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
510 gtk_menu_item_set_label (menu_item, g_value_get_string (value));
512 case PROP_USE_UNDERLINE:
513 gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
515 case PROP_ACTIVATABLE_RELATED_ACTION:
516 gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
518 case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
519 gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
522 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
528 gtk_menu_item_get_property (GObject *object,
533 GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
534 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
538 case PROP_RIGHT_JUSTIFIED:
539 g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
542 g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
544 case PROP_ACCEL_PATH:
545 g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
548 g_value_set_string (value, gtk_menu_item_get_label (menu_item));
550 case PROP_USE_UNDERLINE:
551 g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
553 case PROP_ACTIVATABLE_RELATED_ACTION:
554 g_value_set_object (value, priv->action);
556 case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
557 g_value_set_boolean (value, priv->use_action_appearance);
560 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
566 gtk_menu_item_destroy (GtkWidget *widget)
568 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
570 if (menu_item->submenu)
571 gtk_widget_destroy (menu_item->submenu);
573 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->destroy (widget);
577 gtk_menu_item_detacher (GtkWidget *widget,
580 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
582 g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
584 menu_item->submenu = NULL;
588 get_arrow_size (GtkWidget *widget,
592 PangoContext *context;
593 PangoFontMetrics *metrics;
594 gfloat arrow_scaling;
598 gtk_widget_style_get (widget,
599 "arrow-scaling", &arrow_scaling,
602 context = gtk_widget_get_pango_context (child);
603 metrics = pango_context_get_metrics (context,
604 gtk_widget_get_style (child)->font_desc,
605 pango_context_get_language (context));
607 *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
608 pango_font_metrics_get_descent (metrics)));
610 pango_font_metrics_unref (metrics);
612 *size = *size * arrow_scaling;
617 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
622 if (GTK_IS_ACCEL_LABEL (widget))
626 w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
627 *width = MAX (*width, w);
629 else if (GTK_IS_CONTAINER (widget))
630 gtk_container_foreach (GTK_CONTAINER (widget),
631 gtk_menu_item_accel_width_foreach,
636 get_minimum_width (GtkWidget *widget)
638 PangoContext *context;
639 PangoFontMetrics *metrics;
643 context = gtk_widget_get_pango_context (widget);
644 metrics = pango_context_get_metrics (context,
645 gtk_widget_get_style (widget)->font_desc,
646 pango_context_get_language (context));
648 width = pango_font_metrics_get_approximate_char_width (metrics);
650 pango_font_metrics_unref (metrics);
652 gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
654 return PANGO_PIXELS (width_chars * width);
658 gtk_menu_item_get_preferred_width (GtkWidget *request,
662 GtkMenuItem *menu_item;
664 GtkWidget *child, *widget = GTK_WIDGET (request);
667 guint horizontal_padding;
669 GtkPackDirection pack_dir;
670 GtkPackDirection child_pack_dir;
671 gint min_width, nat_width;
673 min_width = nat_width = 0;
675 gtk_widget_style_get (widget,
676 "horizontal-padding", &horizontal_padding,
679 bin = GTK_BIN (widget);
680 menu_item = GTK_MENU_ITEM (widget);
681 parent = gtk_widget_get_parent (widget);
683 if (GTK_IS_MENU_BAR (parent))
685 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
686 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
690 pack_dir = GTK_PACK_DIRECTION_LTR;
691 child_pack_dir = GTK_PACK_DIRECTION_LTR;
694 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
696 min_width = (border_width + gtk_widget_get_style (widget)->xthickness) * 2;
698 if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
699 (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
700 min_width += 2 * horizontal_padding;
702 nat_width = min_width;
704 child = gtk_bin_get_child (bin);
706 if (child != NULL && gtk_widget_get_visible (child))
708 gint child_min, child_nat;
710 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
712 if (menu_item->submenu && menu_item->show_submenu_indicator)
717 gtk_widget_style_get (widget,
718 "arrow-spacing", &arrow_spacing,
721 get_arrow_size (widget, child, &arrow_size);
723 min_width += arrow_size;
724 min_width += arrow_spacing;
726 min_width = MAX (min_width, get_minimum_width (widget));
728 nat_width = min_width;
732 min_width += child_min;
733 nat_width += child_nat;
739 gtk_container_foreach (GTK_CONTAINER (menu_item),
740 gtk_menu_item_accel_width_foreach,
742 menu_item->accelerator_width = accel_width;
745 *minimum_size = min_width;
748 *natural_size = nat_width;
752 gtk_menu_item_get_preferred_height (GtkWidget *request,
756 GtkMenuItem *menu_item;
759 GtkWidget *child, *widget = GTK_WIDGET (request);
762 guint horizontal_padding;
764 GtkPackDirection pack_dir;
765 GtkPackDirection child_pack_dir;
766 gint min_height, nat_height;
768 min_height = nat_height = 0;
770 style = gtk_widget_get_style (widget);
772 gtk_widget_style_get (widget,
773 "horizontal-padding", &horizontal_padding,
776 bin = GTK_BIN (widget);
777 menu_item = GTK_MENU_ITEM (widget);
778 parent = gtk_widget_get_parent (widget);
780 if (GTK_IS_MENU_BAR (parent))
782 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
783 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
787 pack_dir = GTK_PACK_DIRECTION_LTR;
788 child_pack_dir = GTK_PACK_DIRECTION_LTR;
791 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
792 min_height = (border_width + style->ythickness) * 2;
794 if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
795 (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
796 min_height += 2 * horizontal_padding;
798 nat_height = min_height;
800 child = gtk_bin_get_child (bin);
802 if (child != NULL && gtk_widget_get_visible (child))
804 gint child_min, child_nat;
806 gtk_widget_get_preferred_height (child, &child_min, &child_nat);
808 min_height += child_min;
809 nat_height += child_nat;
811 if (menu_item->submenu && menu_item->show_submenu_indicator)
815 get_arrow_size (widget, child, &arrow_size);
817 min_height = MAX (min_height, arrow_size);
818 nat_height = MAX (nat_height, arrow_size);
821 else /* separator item */
823 gboolean wide_separators;
824 gint separator_height;
826 gtk_widget_style_get (widget,
827 "wide-separators", &wide_separators,
828 "separator-height", &separator_height,
832 min_height += separator_height + style->ythickness;
834 min_height += style->ythickness * 2;
836 nat_height = min_height;
840 gtk_container_foreach (GTK_CONTAINER (menu_item),
841 gtk_menu_item_accel_width_foreach,
843 menu_item->accelerator_width = accel_width;
846 *minimum_size = min_height;
849 *natural_size = nat_height;
853 gtk_menu_item_get_preferred_height_for_width (GtkWidget *request,
858 GtkMenuItem *menu_item;
861 GtkWidget *child, *widget = GTK_WIDGET (request);
863 guint horizontal_padding;
865 GtkPackDirection pack_dir;
866 GtkPackDirection child_pack_dir;
867 gint min_height, nat_height;
870 min_height = nat_height = 0;
872 style = gtk_widget_get_style (widget);
874 gtk_widget_style_get (widget,
875 "horizontal-padding", &horizontal_padding,
878 bin = GTK_BIN (widget);
879 menu_item = GTK_MENU_ITEM (widget);
880 parent = gtk_widget_get_parent (widget);
882 if (GTK_IS_MENU_BAR (parent))
884 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
885 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
889 pack_dir = GTK_PACK_DIRECTION_LTR;
890 child_pack_dir = GTK_PACK_DIRECTION_LTR;
893 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
894 min_height = (border_width + style->ythickness) * 2;
896 avail_size = for_size;
897 avail_size -= (border_width + style->xthickness) * 2;
899 if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
900 (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
901 min_height += 2 * horizontal_padding;
903 if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
904 (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
905 avail_size -= 2 * horizontal_padding;
907 nat_height = min_height;
909 child = gtk_bin_get_child (bin);
911 if (child != NULL && gtk_widget_get_visible (child))
913 gint child_min, child_nat;
916 if (menu_item->submenu && menu_item->show_submenu_indicator)
921 gtk_widget_style_get (widget,
922 "arrow-spacing", &arrow_spacing,
925 get_arrow_size (widget, child, &arrow_size);
927 avail_size -= arrow_size;
928 avail_size -= arrow_spacing;
931 gtk_widget_get_preferred_height_for_width (child,
936 min_height += child_min;
937 nat_height += child_nat;
939 if (menu_item->submenu && menu_item->show_submenu_indicator)
941 min_height = MAX (min_height, arrow_size);
942 nat_height = MAX (nat_height, arrow_size);
945 else /* separator item */
947 gboolean wide_separators;
948 gint separator_height;
950 gtk_widget_style_get (widget,
951 "wide-separators", &wide_separators,
952 "separator-height", &separator_height,
956 min_height += separator_height + style->ythickness;
958 min_height += style->ythickness * 2;
960 nat_height = min_height;
964 *minimum_size = min_height;
967 *natural_size = nat_height;
971 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
973 parent_buildable_iface = g_type_interface_peek_parent (iface);
974 iface->add_child = gtk_menu_item_buildable_add_child;
975 iface->custom_finished = gtk_menu_item_buildable_custom_finished;
979 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
984 if (type && strcmp (type, "submenu") == 0)
985 gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
988 parent_buildable_iface->add_child (buildable, builder, child, type);
993 gtk_menu_item_buildable_custom_finished (GtkBuildable *buildable,
996 const gchar *tagname,
1001 if (strcmp (tagname, "accelerator") == 0)
1003 GtkMenuShell *menu_shell;
1006 menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (GTK_WIDGET (buildable)));
1009 while (GTK_IS_MENU (menu_shell) &&
1010 (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
1011 menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (attach));
1013 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
1017 /* Fall back to something ... */
1018 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
1020 g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
1021 gtk_buildable_get_name (buildable));
1024 /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
1025 _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
1028 parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
1033 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
1035 iface->update = gtk_menu_item_update;
1036 iface->sync_action_properties = gtk_menu_item_sync_action_properties;
1040 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
1044 child = gtk_bin_get_child (GTK_BIN (menu_item));
1046 if (GTK_IS_LABEL (child))
1050 label = gtk_action_get_label (action);
1051 gtk_menu_item_set_label (menu_item, label);
1055 gboolean _gtk_menu_is_empty (GtkWidget *menu);
1058 gtk_menu_item_update (GtkActivatable *activatable,
1060 const gchar *property_name)
1062 GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1063 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1065 if (strcmp (property_name, "visible") == 0)
1066 _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1067 _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1068 else if (strcmp (property_name, "sensitive") == 0)
1069 gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1070 else if (priv->use_action_appearance)
1072 if (strcmp (property_name, "label") == 0)
1073 activatable_update_label (menu_item, action);
1078 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
1081 GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1082 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1085 if (!priv->use_action_appearance || !action)
1087 label = gtk_bin_get_child (GTK_BIN (menu_item));
1089 if (GTK_IS_ACCEL_LABEL (label))
1090 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), GTK_WIDGET (menu_item));
1096 _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1097 _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1099 gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1101 if (priv->use_action_appearance)
1103 label = gtk_bin_get_child (GTK_BIN (menu_item));
1105 /* make sure label is a label, deleting it otherwise */
1106 if (label && !GTK_IS_LABEL (label))
1108 gtk_container_remove (GTK_CONTAINER (menu_item), label);
1111 /* Make sure that menu_item has a label and that any
1112 * accelerators are set */
1113 gtk_menu_item_ensure_label (menu_item);
1114 gtk_menu_item_set_use_underline (menu_item, TRUE);
1115 /* Make label point to the menu_item's label */
1116 label = gtk_bin_get_child (GTK_BIN (menu_item));
1118 if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
1120 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), NULL);
1121 gtk_accel_label_set_accel_closure (GTK_ACCEL_LABEL (label),
1122 gtk_action_get_accel_closure (action));
1125 activatable_update_label (menu_item, action);
1130 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
1133 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1135 if (priv->action == action)
1140 gtk_action_disconnect_accelerator (priv->action);
1145 const gchar *accel_path;
1147 accel_path = gtk_action_get_accel_path (action);
1150 gtk_action_connect_accelerator (action);
1151 gtk_menu_item_set_accel_path (menu_item, accel_path);
1155 gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
1157 priv->action = action;
1161 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
1162 gboolean use_appearance)
1164 GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1166 if (priv->use_action_appearance != use_appearance)
1168 priv->use_action_appearance = use_appearance;
1170 gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
1176 * gtk_menu_item_set_submenu:
1177 * @menu_item: a #GtkMenuItem
1178 * @submenu: (allow-none): the submenu, or %NULL
1180 * Sets or replaces the menu item's submenu, or removes it when a %NULL
1181 * submenu is passed.
1184 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
1187 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1188 g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
1190 if (menu_item->submenu != submenu)
1192 if (menu_item->submenu)
1193 gtk_menu_detach (GTK_MENU (menu_item->submenu));
1197 menu_item->submenu = submenu;
1198 gtk_menu_attach_to_widget (GTK_MENU (submenu),
1199 GTK_WIDGET (menu_item),
1200 gtk_menu_item_detacher);
1203 if (gtk_widget_get_parent (GTK_WIDGET (menu_item)))
1204 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1206 g_object_notify (G_OBJECT (menu_item), "submenu");
1211 * gtk_menu_item_get_submenu:
1212 * @menu_item: a #GtkMenuItem
1214 * Gets the submenu underneath this menu item, if any.
1215 * See gtk_menu_item_set_submenu().
1217 * Return value: (transfer none): submenu for this menu item, or %NULL if none.
1220 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
1222 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
1224 return menu_item->submenu;
1227 void _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
1228 GtkSubmenuPlacement placement);
1231 _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
1232 GtkSubmenuPlacement placement)
1234 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1236 menu_item->submenu_placement = placement;
1240 gtk_menu_item_select (GtkMenuItem *menu_item)
1244 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1246 g_signal_emit (menu_item, menu_item_signals[SELECT], 0);
1248 /* Enable themeing of the parent menu item depending on whether
1249 * something is selected in its submenu
1251 parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1252 if (GTK_IS_MENU (parent))
1254 GtkMenu *menu = GTK_MENU (parent);
1256 if (menu->parent_menu_item)
1257 gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
1262 gtk_menu_item_deselect (GtkMenuItem *menu_item)
1266 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1268 g_signal_emit (menu_item, menu_item_signals[DESELECT], 0);
1270 /* Enable themeing of the parent menu item depending on whether
1271 * something is selected in its submenu
1273 parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1274 if (GTK_IS_MENU (parent))
1276 GtkMenu *menu = GTK_MENU (parent);
1278 if (menu->parent_menu_item)
1279 gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
1284 gtk_menu_item_activate (GtkMenuItem *menu_item)
1286 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1288 g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
1292 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1295 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1297 g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
1301 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1304 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1306 g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
1310 gtk_menu_item_size_allocate (GtkWidget *widget,
1311 GtkAllocation *allocation)
1313 GtkMenuItem *menu_item;
1315 GtkAllocation child_allocation;
1316 GtkTextDirection direction;
1317 GtkPackDirection pack_dir;
1318 GtkPackDirection child_pack_dir;
1322 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1323 g_return_if_fail (allocation != NULL);
1325 menu_item = GTK_MENU_ITEM (widget);
1326 bin = GTK_BIN (widget);
1328 direction = gtk_widget_get_direction (widget);
1330 parent = gtk_widget_get_parent (widget);
1331 if (GTK_IS_MENU_BAR (parent))
1333 pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
1334 child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
1338 pack_dir = GTK_PACK_DIRECTION_LTR;
1339 child_pack_dir = GTK_PACK_DIRECTION_LTR;
1342 gtk_widget_set_allocation (widget, allocation);
1344 child = gtk_bin_get_child (bin);
1347 GtkRequisition child_requisition;
1349 guint horizontal_padding;
1352 style = gtk_widget_get_style (widget);
1353 gtk_widget_style_get (widget,
1354 "horizontal-padding", &horizontal_padding,
1357 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1358 child_allocation.x = border_width + style->xthickness;
1359 child_allocation.y = border_width + style->ythickness;
1361 if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1362 (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1363 child_allocation.x += horizontal_padding;
1364 else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1365 (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1366 child_allocation.y += horizontal_padding;
1368 child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
1369 child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
1371 if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1372 child_pack_dir == GTK_PACK_DIRECTION_RTL)
1374 if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1375 child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
1376 child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
1380 if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1381 child_allocation.y += GTK_MENU_ITEM (widget)->toggle_size;
1382 child_allocation.height -= GTK_MENU_ITEM (widget)->toggle_size;
1385 child_allocation.x += allocation->x;
1386 child_allocation.y += allocation->y;
1388 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1389 if (menu_item->submenu && menu_item->show_submenu_indicator)
1391 if (direction == GTK_TEXT_DIR_RTL)
1392 child_allocation.x += child_requisition.height;
1393 child_allocation.width -= child_requisition.height;
1396 if (child_allocation.width < 1)
1397 child_allocation.width = 1;
1399 gtk_widget_size_allocate (child, &child_allocation);
1402 if (gtk_widget_get_realized (widget))
1403 gdk_window_move_resize (menu_item->event_window,
1404 allocation->x, allocation->y,
1405 allocation->width, allocation->height);
1407 if (menu_item->submenu)
1408 gtk_menu_reposition (GTK_MENU (menu_item->submenu));
1412 gtk_menu_item_realize (GtkWidget *widget)
1414 GtkAllocation allocation;
1415 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1417 GdkWindowAttr attributes;
1418 gint attributes_mask;
1420 gtk_widget_set_realized (widget, TRUE);
1422 window = gtk_widget_get_parent_window (widget);
1423 gtk_widget_set_window (widget, window);
1424 g_object_ref (window);
1426 gtk_widget_get_allocation (widget, &allocation);
1428 attributes.x = allocation.x;
1429 attributes.y = allocation.y;
1430 attributes.width = allocation.width;
1431 attributes.height = allocation.height;
1432 attributes.window_type = GDK_WINDOW_CHILD;
1433 attributes.wclass = GDK_INPUT_ONLY;
1434 attributes.event_mask = (gtk_widget_get_events (widget) |
1435 GDK_BUTTON_PRESS_MASK |
1436 GDK_BUTTON_RELEASE_MASK |
1437 GDK_ENTER_NOTIFY_MASK |
1438 GDK_LEAVE_NOTIFY_MASK |
1439 GDK_POINTER_MOTION_MASK);
1441 attributes_mask = GDK_WA_X | GDK_WA_Y;
1443 menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1444 &attributes, attributes_mask);
1445 gdk_window_set_user_data (menu_item->event_window, widget);
1447 gtk_widget_style_attach (widget);
1451 gtk_menu_item_unrealize (GtkWidget *widget)
1453 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1455 gdk_window_set_user_data (menu_item->event_window, NULL);
1456 gdk_window_destroy (menu_item->event_window);
1457 menu_item->event_window = NULL;
1459 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1463 gtk_menu_item_map (GtkWidget *widget)
1465 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1467 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1469 gdk_window_show (menu_item->event_window);
1473 gtk_menu_item_unmap (GtkWidget *widget)
1475 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1477 gdk_window_hide (menu_item->event_window);
1479 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1483 gtk_menu_item_enter (GtkWidget *widget,
1484 GdkEventCrossing *event)
1486 g_return_val_if_fail (event != NULL, FALSE);
1488 return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event);
1492 gtk_menu_item_leave (GtkWidget *widget,
1493 GdkEventCrossing *event)
1495 g_return_val_if_fail (event != NULL, FALSE);
1497 return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent*) event);
1501 gtk_menu_item_draw (GtkWidget *widget,
1504 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1505 GtkStateType state_type;
1506 GtkShadowType shadow_type, selected_shadow_type;
1510 gint x, y, w, h, width, height;
1511 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1513 state_type = gtk_widget_get_state (widget);
1514 style = gtk_widget_get_style (widget);
1515 window = gtk_widget_get_window (widget);
1516 width = gtk_widget_get_allocated_width (widget);
1517 height = gtk_widget_get_allocated_height (widget);
1521 w = width - border_width * 2;
1522 h = height - border_width * 2;
1524 child = gtk_bin_get_child (GTK_BIN (menu_item));
1526 if (child && state_type == GTK_STATE_PRELIGHT)
1528 gtk_widget_style_get (widget,
1529 "selected-shadow-type", &selected_shadow_type,
1531 gtk_paint_box (style,
1534 selected_shadow_type,
1539 if (menu_item->submenu && menu_item->show_submenu_indicator)
1541 gint arrow_x, arrow_y;
1543 guint horizontal_padding;
1544 GtkTextDirection direction;
1545 GtkArrowType arrow_type;
1547 direction = gtk_widget_get_direction (widget);
1549 gtk_widget_style_get (widget,
1550 "horizontal-padding", &horizontal_padding,
1553 get_arrow_size (widget, child, &arrow_size);
1555 shadow_type = GTK_SHADOW_OUT;
1556 if (state_type == GTK_STATE_PRELIGHT)
1557 shadow_type = GTK_SHADOW_IN;
1559 if (direction == GTK_TEXT_DIR_LTR)
1561 arrow_x = x + w - horizontal_padding - arrow_size;
1562 arrow_type = GTK_ARROW_RIGHT;
1566 arrow_x = x + horizontal_padding;
1567 arrow_type = GTK_ARROW_LEFT;
1570 arrow_y = y + (h - arrow_size) / 2;
1572 gtk_paint_arrow (style, cr,
1573 state_type, shadow_type,
1577 arrow_size, arrow_size);
1581 gboolean wide_separators;
1582 gint separator_height;
1583 guint horizontal_padding;
1585 gtk_widget_style_get (widget,
1586 "wide-separators", &wide_separators,
1587 "separator-height", &separator_height,
1588 "horizontal-padding", &horizontal_padding,
1591 if (wide_separators)
1592 gtk_paint_box (style, cr,
1593 GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
1594 widget, "hseparator",
1595 horizontal_padding + style->xthickness,
1596 (height - separator_height - style->ythickness) / 2,
1597 width - 2 * (horizontal_padding + style->xthickness),
1600 gtk_paint_hline (style, cr,
1601 GTK_STATE_NORMAL, widget, "menuitem",
1602 horizontal_padding + style->xthickness,
1603 width - horizontal_padding - style->xthickness - 1,
1604 (height - style->ythickness) / 2);
1607 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->draw (widget, cr);
1613 gtk_real_menu_item_select (GtkMenuItem *menu_item)
1615 gboolean touchscreen_mode;
1617 g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
1618 "gtk-touchscreen-mode", &touchscreen_mode,
1621 if (!touchscreen_mode &&
1622 menu_item->submenu &&
1623 (!gtk_widget_get_mapped (menu_item->submenu) ||
1624 GTK_MENU (menu_item->submenu)->tearoff_active))
1626 _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1629 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
1630 gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1634 gtk_real_menu_item_deselect (GtkMenuItem *menu_item)
1636 if (menu_item->submenu)
1637 _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1639 gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
1640 gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1644 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1645 gboolean group_cycling)
1649 parent = gtk_widget_get_parent (widget);
1651 if (GTK_IS_MENU_SHELL (parent))
1652 _gtk_menu_shell_set_keyboard_mode (GTK_MENU_SHELL (parent), TRUE);
1654 if (group_cycling &&
1656 GTK_IS_MENU_SHELL (parent) &&
1657 GTK_MENU_SHELL (parent)->active)
1659 gtk_menu_shell_select_item (GTK_MENU_SHELL (parent),
1663 g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1669 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1671 GtkMenuItemPrivate *priv;
1673 priv = GET_PRIVATE (menu_item);
1676 gtk_action_activate (priv->action);
1681 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1683 GtkMenuItemPrivate *priv;
1687 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1689 priv = GET_PRIVATE (menu_item);
1690 widget = GTK_WIDGET (menu_item);
1691 parent = gtk_widget_get_parent (widget);
1693 if (parent && GTK_IS_MENU_SHELL (parent))
1695 GtkMenuShell *menu_shell = GTK_MENU_SHELL (parent);
1697 if (menu_item->submenu == NULL)
1698 gtk_menu_shell_activate_item (menu_shell,
1702 _gtk_menu_shell_activate (menu_shell);
1704 gtk_menu_shell_select_item (menu_shell, widget);
1705 _gtk_menu_item_popup_submenu (widget, FALSE);
1707 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1713 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1716 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1722 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1725 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1727 menu_item->toggle_size = allocation;
1731 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1736 gtk_menu_item_ensure_label (menu_item);
1738 child = gtk_bin_get_child (GTK_BIN (menu_item));
1739 if (GTK_IS_LABEL (child))
1741 gtk_label_set_label (GTK_LABEL (child), label ? label : "");
1743 g_object_notify (G_OBJECT (menu_item), "label");
1747 static G_CONST_RETURN gchar *
1748 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1752 gtk_menu_item_ensure_label (menu_item);
1754 child = gtk_bin_get_child (GTK_BIN (menu_item));
1755 if (GTK_IS_LABEL (child))
1756 return gtk_label_get_label (GTK_LABEL (child));
1762 free_timeval (GTimeVal *val)
1764 g_slice_free (GTimeVal, val);
1768 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1769 gboolean remember_exact_time)
1771 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1774 parent = gtk_widget_get_parent (widget);
1776 if (gtk_widget_is_sensitive (menu_item->submenu) && parent)
1778 gboolean take_focus;
1779 GtkMenuPositionFunc menu_position_func;
1781 take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (parent));
1782 gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1785 if (remember_exact_time)
1787 GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1789 g_get_current_time (popup_time);
1791 g_object_set_data_full (G_OBJECT (menu_item->submenu),
1792 "gtk-menu-exact-popup-time", popup_time,
1793 (GDestroyNotify) free_timeval);
1797 g_object_set_data (G_OBJECT (menu_item->submenu),
1798 "gtk-menu-exact-popup-time", NULL);
1801 /* gtk_menu_item_position_menu positions the submenu from the
1802 * menuitems position. If the menuitem doesn't have a window,
1803 * that doesn't work. In that case we use the default
1804 * positioning function instead which places the submenu at the
1807 if (gtk_widget_get_window (widget))
1808 menu_position_func = gtk_menu_item_position_menu;
1810 menu_position_func = NULL;
1812 gtk_menu_popup (GTK_MENU (menu_item->submenu),
1817 GTK_MENU_SHELL (parent)->button,
1821 /* Enable themeing of the parent menu item depending on whether
1822 * its submenu is shown or not.
1824 gtk_widget_queue_draw (widget);
1828 gtk_menu_item_popup_timeout (gpointer data)
1830 GtkMenuItem *menu_item;
1833 menu_item = GTK_MENU_ITEM (data);
1835 parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1837 if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) ||
1838 (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1840 gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1841 if (menu_item->timer_from_keypress && menu_item->submenu)
1842 GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1845 menu_item->timer = 0;
1851 get_popup_delay (GtkWidget *widget)
1855 parent = gtk_widget_get_parent (widget);
1856 if (GTK_IS_MENU_SHELL (parent))
1858 return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent));
1864 g_object_get (gtk_widget_get_settings (widget),
1865 "gtk-menu-popup-delay", &popup_delay,
1873 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1874 gboolean with_delay)
1876 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1878 if (menu_item->timer)
1880 g_source_remove (menu_item->timer);
1881 menu_item->timer = 0;
1887 gint popup_delay = get_popup_delay (widget);
1889 if (popup_delay > 0)
1891 GdkEvent *event = gtk_get_current_event ();
1893 menu_item->timer = gdk_threads_add_timeout (popup_delay,
1894 gtk_menu_item_popup_timeout,
1898 event->type != GDK_BUTTON_PRESS &&
1899 event->type != GDK_ENTER_NOTIFY)
1900 menu_item->timer_from_keypress = TRUE;
1902 menu_item->timer_from_keypress = FALSE;
1905 gdk_event_free (event);
1911 gtk_menu_item_real_popup_submenu (widget, FALSE);
1915 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1917 GtkMenuItem *menu_item;
1919 menu_item = GTK_MENU_ITEM (widget);
1921 if (menu_item->submenu)
1923 g_object_set_data (G_OBJECT (menu_item->submenu),
1924 "gtk-menu-exact-popup-time", NULL);
1926 if (menu_item->timer)
1928 g_source_remove (menu_item->timer);
1929 menu_item->timer = 0;
1932 gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1934 gtk_widget_queue_draw (widget);
1939 get_offsets (GtkMenu *menu,
1940 gint *horizontal_offset,
1941 gint *vertical_offset)
1943 gint vertical_padding;
1944 gint horizontal_padding;
1946 gtk_widget_style_get (GTK_WIDGET (menu),
1947 "horizontal-offset", horizontal_offset,
1948 "vertical-offset", vertical_offset,
1949 "horizontal-padding", &horizontal_padding,
1950 "vertical-padding", &vertical_padding,
1953 *vertical_offset -= gtk_widget_get_style (GTK_WIDGET (menu))->ythickness;
1954 *vertical_offset -= vertical_padding;
1955 *horizontal_offset += horizontal_padding;
1959 gtk_menu_item_position_menu (GtkMenu *menu,
1965 GtkAllocation allocation;
1966 GtkMenuItem *menu_item;
1968 GtkMenuItem *parent_menu_item;
1969 GtkRequisition requisition;
1972 gint twidth, theight;
1974 GtkTextDirection direction;
1975 GdkRectangle monitor;
1977 gint horizontal_offset;
1978 gint vertical_offset;
1979 gint parent_xthickness;
1980 gint available_left, available_right;
1982 g_return_if_fail (menu != NULL);
1983 g_return_if_fail (x != NULL);
1984 g_return_if_fail (y != NULL);
1986 menu_item = GTK_MENU_ITEM (user_data);
1987 widget = GTK_WIDGET (user_data);
1992 direction = gtk_widget_get_direction (widget);
1994 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &requisition, NULL);
1995 twidth = requisition.width;
1996 theight = requisition.height;
1998 screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1999 monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
2000 if (monitor_num < 0)
2002 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2004 if (!gdk_window_get_origin (gtk_widget_get_window (widget),
2007 g_warning ("Menu not on screen");
2011 gtk_widget_get_allocation (widget, &allocation);
2016 get_offsets (menu, &horizontal_offset, &vertical_offset);
2018 available_left = tx - monitor.x;
2019 available_right = monitor.x + monitor.width - (tx + allocation.width);
2021 parent = gtk_widget_get_parent (widget);
2022 if (GTK_IS_MENU_BAR (parent))
2024 menu_item->from_menubar = TRUE;
2026 else if (GTK_IS_MENU (parent))
2028 if (GTK_MENU (parent)->parent_menu_item)
2029 menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (parent)->parent_menu_item)->from_menubar;
2031 menu_item->from_menubar = FALSE;
2035 menu_item->from_menubar = FALSE;
2038 switch (menu_item->submenu_placement)
2040 case GTK_TOP_BOTTOM:
2041 if (direction == GTK_TEXT_DIR_LTR)
2042 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2045 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2046 tx += allocation.width - twidth;
2048 if ((ty + allocation.height + theight) <= monitor.y + monitor.height)
2049 ty += allocation.height;
2050 else if ((ty - theight) >= monitor.y)
2052 else if (monitor.y + monitor.height - (ty + allocation.height) > ty)
2053 ty += allocation.height;
2058 case GTK_LEFT_RIGHT:
2059 if (GTK_IS_MENU (parent))
2060 parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->parent_menu_item);
2062 parent_menu_item = NULL;
2064 parent_xthickness = gtk_widget_get_style (parent)->xthickness;
2066 if (parent_menu_item && !GTK_MENU (parent)->torn_off)
2068 menu_item->submenu_direction = parent_menu_item->submenu_direction;
2072 if (direction == GTK_TEXT_DIR_LTR)
2073 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2075 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2078 switch (menu_item->submenu_direction)
2080 case GTK_DIRECTION_LEFT:
2081 if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
2082 available_left >= available_right)
2083 tx -= twidth + parent_xthickness + horizontal_offset;
2086 menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2087 tx += allocation.width + parent_xthickness + horizontal_offset;
2091 case GTK_DIRECTION_RIGHT:
2092 if (tx + allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
2093 available_right >= available_left)
2094 tx += allocation.width + parent_xthickness + horizontal_offset;
2097 menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2098 tx -= twidth + parent_xthickness + horizontal_offset;
2103 ty += vertical_offset;
2105 /* If the height of the menu doesn't fit we move it upward. */
2106 ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
2110 /* If we have negative, tx, here it is because we can't get
2111 * the menu all the way on screen. Favor the left portion.
2113 *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
2116 gtk_menu_set_monitor (menu, monitor_num);
2118 if (!gtk_widget_get_visible (menu->toplevel))
2120 gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
2121 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
2126 * gtk_menu_item_set_right_justified:
2127 * @menu_item: a #GtkMenuItem.
2128 * @right_justified: if %TRUE the menu item will appear at the
2129 * far right if added to a menu bar.
2131 * Sets whether the menu item appears justified at the right
2132 * side of a menu bar. This was traditionally done for "Help" menu
2133 * items, but is now considered a bad idea. (If the widget
2134 * layout is reversed for a right-to-left language like Hebrew
2135 * or Arabic, right-justified-menu-items appear at the left.)
2138 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
2139 gboolean right_justified)
2141 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2143 right_justified = right_justified != FALSE;
2145 if (right_justified != menu_item->right_justify)
2147 menu_item->right_justify = right_justified;
2148 gtk_widget_queue_resize (GTK_WIDGET (menu_item));
2153 * gtk_menu_item_get_right_justified:
2154 * @menu_item: a #GtkMenuItem
2156 * Gets whether the menu item appears justified at the right
2157 * side of the menu bar.
2159 * Return value: %TRUE if the menu item will appear at the
2160 * far right if added to a menu bar.
2163 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
2165 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2167 return menu_item->right_justify;
2172 gtk_menu_item_show_all (GtkWidget *widget)
2174 GtkMenuItem *menu_item;
2176 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
2178 menu_item = GTK_MENU_ITEM (widget);
2180 /* show children including submenu */
2181 if (menu_item->submenu)
2182 gtk_widget_show_all (menu_item->submenu);
2183 gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2185 gtk_widget_show (widget);
2189 gtk_menu_item_can_activate_accel (GtkWidget *widget,
2194 parent = gtk_widget_get_parent (widget);
2195 /* Chain to the parent GtkMenu for further checks */
2196 return (gtk_widget_is_sensitive (widget) && gtk_widget_get_visible (widget) &&
2197 parent && gtk_widget_can_activate_accel (parent, signal_id));
2201 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
2204 const gchar **path_p = data;
2208 if (GTK_IS_LABEL (widget))
2210 *path_p = gtk_label_get_text (GTK_LABEL (widget));
2211 if (*path_p && (*path_p)[0] == 0)
2214 else if (GTK_IS_CONTAINER (widget))
2215 gtk_container_foreach (GTK_CONTAINER (widget),
2216 gtk_menu_item_accel_name_foreach,
2222 gtk_menu_item_parent_set (GtkWidget *widget,
2223 GtkWidget *previous_parent)
2225 GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2229 parent = gtk_widget_get_parent (widget);
2230 menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
2233 _gtk_menu_item_refresh_accel_path (menu_item,
2238 if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
2239 GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
2243 _gtk_menu_item_refresh_accel_path (GtkMenuItem *menu_item,
2244 const gchar *prefix,
2245 GtkAccelGroup *accel_group,
2246 gboolean group_changed)
2251 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2252 g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
2254 widget = GTK_WIDGET (menu_item);
2258 gtk_widget_set_accel_path (widget, NULL, NULL);
2262 path = _gtk_widget_get_accel_path (widget, NULL);
2263 if (!path) /* no active accel_path yet */
2265 path = menu_item->accel_path;
2266 if (!path && prefix)
2268 const gchar *postfix = NULL;
2271 /* try to construct one from label text */
2272 gtk_container_foreach (GTK_CONTAINER (menu_item),
2273 gtk_menu_item_accel_name_foreach,
2277 new_path = g_strconcat (prefix, "/", postfix, NULL);
2278 path = menu_item->accel_path = (char*)g_intern_string (new_path);
2283 gtk_widget_set_accel_path (widget, path, accel_group);
2285 else if (group_changed) /* reinstall accelerators */
2286 gtk_widget_set_accel_path (widget, path, accel_group);
2290 * gtk_menu_item_set_accel_path
2291 * @menu_item: a valid #GtkMenuItem
2292 * @accel_path: (allow-none): accelerator path, corresponding to this menu item's
2293 * functionality, or %NULL to unset the current path.
2295 * Set the accelerator path on @menu_item, through which runtime changes of the
2296 * menu item's accelerator caused by the user can be identified and saved to
2297 * persistant storage (see gtk_accel_map_save() on this).
2298 * To setup a default accelerator for this menu item, call
2299 * gtk_accel_map_add_entry() with the same @accel_path.
2300 * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
2301 * and gtk_menu_set_accel_path() for a more convenient variant of this function.
2303 * This function is basically a convenience wrapper that handles calling
2304 * gtk_widget_set_accel_path() with the appropriate accelerator group for
2307 * Note that you do need to set an accelerator on the parent menu with
2308 * gtk_menu_set_accel_group() for this to work.
2310 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
2311 * pass a static string, you can save some memory by interning it first with
2312 * g_intern_static_string().
2315 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2316 const gchar *accel_path)
2321 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2322 g_return_if_fail (accel_path == NULL ||
2323 (accel_path[0] == '<' && strchr (accel_path, '/')));
2325 widget = GTK_WIDGET (menu_item);
2327 /* store new path */
2328 menu_item->accel_path = (char*)g_intern_string (accel_path);
2330 /* forget accelerators associated with old path */
2331 gtk_widget_set_accel_path (widget, NULL, NULL);
2333 /* install accelerators associated with new path */
2334 parent = gtk_widget_get_parent (widget);
2335 if (GTK_IS_MENU (parent))
2337 GtkMenu *menu = GTK_MENU (parent);
2339 if (menu->accel_group)
2340 _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2348 * gtk_menu_item_get_accel_path
2349 * @menu_item: a valid #GtkMenuItem
2351 * Retrieve the accelerator path that was previously set on @menu_item.
2353 * See gtk_menu_item_set_accel_path() for details.
2355 * Returns: the accelerator path corresponding to this menu item's
2356 * functionality, or %NULL if not set
2360 G_CONST_RETURN gchar *
2361 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2363 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2365 return menu_item->accel_path;
2369 gtk_menu_item_forall (GtkContainer *container,
2370 gboolean include_internals,
2371 GtkCallback callback,
2372 gpointer callback_data)
2376 g_return_if_fail (GTK_IS_MENU_ITEM (container));
2377 g_return_if_fail (callback != NULL);
2379 child = gtk_bin_get_child (GTK_BIN (container));
2381 callback (child, callback_data);
2385 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2387 if ((!gtk_bin_get_child (GTK_BIN (menu_item)) &&
2388 G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2389 GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2390 !gtk_widget_is_sensitive (menu_item) ||
2391 !gtk_widget_get_visible (menu_item))
2398 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2400 GtkWidget *accel_label;
2402 if (!gtk_bin_get_child (GTK_BIN (menu_item)))
2404 accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2405 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2407 gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2408 gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
2409 GTK_WIDGET (menu_item));
2410 gtk_widget_show (accel_label);
2415 * gtk_menu_item_set_label:
2416 * @menu_item: a #GtkMenuItem
2417 * @label: the text you want to set
2419 * Sets @text on the @menu_item label
2424 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2427 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2429 GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2433 * gtk_menu_item_get_label:
2434 * @menu_item: a #GtkMenuItem
2436 * Sets @text on the @menu_item label
2438 * Returns: The text in the @menu_item label. This is the internal
2439 * string used by the label, and must not be modified.
2443 G_CONST_RETURN gchar *
2444 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2446 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2448 return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2452 * gtk_menu_item_set_use_underline:
2453 * @menu_item: a #GtkMenuItem
2454 * @setting: %TRUE if underlines in the text indicate mnemonics
2456 * If true, an underline in the text indicates the next character should be
2457 * used for the mnemonic accelerator key.
2462 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2467 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2469 gtk_menu_item_ensure_label (menu_item);
2471 child = gtk_bin_get_child (GTK_BIN (menu_item));
2472 if (GTK_IS_LABEL (child))
2474 gtk_label_set_use_underline (GTK_LABEL (child), setting);
2476 g_object_notify (G_OBJECT (menu_item), "use-underline");
2481 * gtk_menu_item_get_use_underline:
2482 * @menu_item: a #GtkMenuItem
2484 * Checks if an underline in the text indicates the next character should be
2485 * used for the mnemonic accelerator key.
2487 * Return value: %TRUE if an embedded underline in the label indicates
2488 * the mnemonic accelerator key.
2493 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2497 g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2499 gtk_menu_item_ensure_label (menu_item);
2501 child = gtk_bin_get_child (GTK_BIN (menu_item));
2502 if (GTK_IS_LABEL (child))
2503 return gtk_label_get_use_underline (GTK_LABEL (child));