]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Don't use *DISABLE_DEPRECATED guards
[~andy/gtk] / gtk / gtkmenuitem.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
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/.
25  */
26
27 #include "config.h"
28
29 #include <string.h>
30
31 #include "gtkaccellabel.h"
32 #include "gtkmain.h"
33 #include "gtkmarshalers.h"
34 #include "gtkmenuprivate.h"
35 #include "gtkmenushellprivate.h"
36 #include "gtkmenuitemprivate.h"
37 #include "gtkmenubar.h"
38 #include "gtkmenuprivate.h"
39 #include "gtkseparatormenuitem.h"
40 #include "gtkprivate.h"
41 #include "gtkbuildable.h"
42 #include "gtkactivatable.h"
43 #include "gtkwidgetprivate.h"
44 #include "gtkintl.h"
45 #include "gtktypebuiltins.h"
46 #include "a11y/gtkmenuitemaccessible.h"
47
48
49 /**
50  * SECTION:gtkmenuitem
51  * @Short_description: The widget used for item in menus
52  * @Title: GtkMenuItem
53  * @See_also: #GtkBin, #GtkMenuShell
54  *
55  * The #GtkMenuItem widget and the derived widgets are the only valid
56  * childs for menus. Their function is to correctly handle highlighting,
57  * alignment, events and submenus.
58  *
59  * As it derives from #GtkBin it can hold any valid child widget, altough
60  * only a few are really useful.
61  *
62  * <refsect2 id="GtkMenuItem-BUILDER-UI">
63  * <title>GtkMenuItem as GtkBuildable</title>
64  * The GtkMenuItem implementation of the GtkBuildable interface
65  * supports adding a submenu by specifying "submenu" as the "type"
66  * attribute of a &lt;child&gt; element.
67  * <example>
68  * <title>A UI definition fragment with submenus</title>
69  * <programlisting><![CDATA[
70  * <object class="GtkMenuItem">
71  *   <child type="submenu">
72  *     <object class="GtkMenu"/>
73  *   </child>
74  * </object>
75  * ]]></programlisting>
76  * </example>
77  * </refsect2>
78  */
79
80
81 enum {
82   ACTIVATE,
83   ACTIVATE_ITEM,
84   TOGGLE_SIZE_REQUEST,
85   TOGGLE_SIZE_ALLOCATE,
86   SELECT,
87   DESELECT,
88   LAST_SIGNAL
89 };
90
91 enum {
92   PROP_0,
93   PROP_RIGHT_JUSTIFIED,
94   PROP_SUBMENU,
95   PROP_ACCEL_PATH,
96   PROP_LABEL,
97   PROP_USE_UNDERLINE,
98
99   /* activatable properties */
100   PROP_ACTIVATABLE_RELATED_ACTION,
101   PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
102 };
103
104
105 static void gtk_menu_item_dispose        (GObject          *object);
106 static void gtk_menu_item_set_property   (GObject          *object,
107                                           guint             prop_id,
108                                           const GValue     *value,
109                                           GParamSpec       *pspec);
110 static void gtk_menu_item_get_property   (GObject          *object,
111                                           guint             prop_id,
112                                           GValue           *value,
113                                           GParamSpec       *pspec);
114 static void gtk_menu_item_destroy        (GtkWidget        *widget);
115 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
116                                           GtkAllocation    *allocation);
117 static void gtk_menu_item_realize        (GtkWidget        *widget);
118 static void gtk_menu_item_unrealize      (GtkWidget        *widget);
119 static void gtk_menu_item_map            (GtkWidget        *widget);
120 static void gtk_menu_item_unmap          (GtkWidget        *widget);
121 static gboolean gtk_menu_item_enter      (GtkWidget        *widget,
122                                           GdkEventCrossing *event);
123 static gboolean gtk_menu_item_leave      (GtkWidget        *widget,
124                                           GdkEventCrossing *event);
125 static gboolean gtk_menu_item_draw       (GtkWidget        *widget,
126                                           cairo_t          *cr);
127 static void gtk_menu_item_parent_set     (GtkWidget        *widget,
128                                           GtkWidget        *previous_parent);
129
130
131 static void gtk_real_menu_item_select               (GtkMenuItem *item);
132 static void gtk_real_menu_item_deselect             (GtkMenuItem *item);
133 static void gtk_real_menu_item_activate             (GtkMenuItem *item);
134 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
135 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
136                                                      gint        *requisition);
137 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
138                                                      gint         allocation);
139 static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
140                                                      gboolean     group_cycling);
141
142 static void gtk_menu_item_ensure_label   (GtkMenuItem      *menu_item);
143 static gint gtk_menu_item_popup_timeout  (gpointer          data);
144 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
145                                           gint             *x,
146                                           gint             *y,
147                                           gboolean         *push_in,
148                                           gpointer          user_data);
149 static void gtk_menu_item_show_all       (GtkWidget        *widget);
150 static void gtk_menu_item_forall         (GtkContainer    *container,
151                                           gboolean         include_internals,
152                                           GtkCallback      callback,
153                                           gpointer         callback_data);
154 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
155                                                   guint      signal_id);
156
157 static void gtk_real_menu_item_set_label (GtkMenuItem     *menu_item,
158                                           const gchar     *label);
159 static const gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
160
161 static void gtk_menu_item_get_preferred_width            (GtkWidget           *widget,
162                                                           gint                *minimum_size,
163                                                           gint                *natural_size);
164 static void gtk_menu_item_get_preferred_height           (GtkWidget           *widget,
165                                                           gint                *minimum_size,
166                                                           gint                *natural_size);
167 static void gtk_menu_item_get_preferred_height_for_width (GtkWidget           *widget,
168                                                           gint                 for_size,
169                                                           gint                *minimum_size,
170                                                           gint                *natural_size);
171
172 static void gtk_menu_item_buildable_interface_init (GtkBuildableIface   *iface);
173 static void gtk_menu_item_buildable_add_child      (GtkBuildable        *buildable,
174                                                     GtkBuilder          *builder,
175                                                     GObject             *child,
176                                                     const gchar         *type);
177 static void gtk_menu_item_buildable_custom_finished(GtkBuildable        *buildable,
178                                                     GtkBuilder          *builder,
179                                                     GObject             *child,
180                                                     const gchar         *tagname,
181                                                     gpointer             user_data);
182
183 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
184 static void gtk_menu_item_update                     (GtkActivatable       *activatable,
185                                                       GtkAction            *action,
186                                                       const gchar          *property_name);
187 static void gtk_menu_item_sync_action_properties     (GtkActivatable       *activatable,
188                                                       GtkAction            *action);
189 static void gtk_menu_item_set_related_action         (GtkMenuItem          *menu_item, 
190                                                       GtkAction            *action);
191 static void gtk_menu_item_set_use_action_appearance  (GtkMenuItem          *menu_item, 
192                                                       gboolean              use_appearance);
193
194 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
195
196 static GtkBuildableIface *parent_buildable_iface;
197
198 G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_BIN,
199                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
200                                                 gtk_menu_item_buildable_interface_init)
201                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
202                                                 gtk_menu_item_activatable_interface_init))
203
204 static void
205 gtk_menu_item_class_init (GtkMenuItemClass *klass)
206 {
207   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
208   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
209   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
210
211   gobject_class->dispose = gtk_menu_item_dispose;
212   gobject_class->set_property = gtk_menu_item_set_property;
213   gobject_class->get_property = gtk_menu_item_get_property;
214
215   widget_class->destroy = gtk_menu_item_destroy;
216   widget_class->size_allocate = gtk_menu_item_size_allocate;
217   widget_class->draw = gtk_menu_item_draw;
218   widget_class->realize = gtk_menu_item_realize;
219   widget_class->unrealize = gtk_menu_item_unrealize;
220   widget_class->map = gtk_menu_item_map;
221   widget_class->unmap = gtk_menu_item_unmap;
222   widget_class->enter_notify_event = gtk_menu_item_enter;
223   widget_class->leave_notify_event = gtk_menu_item_leave;
224   widget_class->show_all = gtk_menu_item_show_all;
225   widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
226   widget_class->parent_set = gtk_menu_item_parent_set;
227   widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
228   widget_class->get_preferred_width = gtk_menu_item_get_preferred_width;
229   widget_class->get_preferred_height = gtk_menu_item_get_preferred_height;
230   widget_class->get_preferred_height_for_width = gtk_menu_item_get_preferred_height_for_width;
231
232   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ITEM_ACCESSIBLE);
233
234   container_class->forall = gtk_menu_item_forall;
235
236   klass->activate = gtk_real_menu_item_activate;
237   klass->activate_item = gtk_real_menu_item_activate_item;
238   klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
239   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
240   klass->set_label = gtk_real_menu_item_set_label;
241   klass->get_label = gtk_real_menu_item_get_label;
242   klass->select = gtk_real_menu_item_select;
243   klass->deselect = gtk_real_menu_item_deselect;
244
245   klass->hide_on_activate = TRUE;
246
247   /**
248    * GtkMenuItem::activate:
249    * @menuitem: the object which received the signal.
250    *
251    * Emitted when the item is activated.
252    */
253   menu_item_signals[ACTIVATE] =
254     g_signal_new (I_("activate"),
255                   G_OBJECT_CLASS_TYPE (gobject_class),
256                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
257                   G_STRUCT_OFFSET (GtkMenuItemClass, activate),
258                   NULL, NULL,
259                   _gtk_marshal_VOID__VOID,
260                   G_TYPE_NONE, 0);
261   widget_class->activate_signal = menu_item_signals[ACTIVATE];
262
263   /**
264    * GtkMenuItem::activate-item:
265    * @menuitem: the object which received the signal.
266    *
267    * Emitted when the item is activated, but also if the menu item has a
268    * submenu. For normal applications, the relevant signal is
269    * #GtkMenuItem::activate.
270    */
271   menu_item_signals[ACTIVATE_ITEM] =
272     g_signal_new (I_("activate-item"),
273                   G_OBJECT_CLASS_TYPE (gobject_class),
274                   G_SIGNAL_RUN_FIRST,
275                   G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
276                   NULL, NULL,
277                   _gtk_marshal_VOID__VOID,
278                   G_TYPE_NONE, 0);
279
280   menu_item_signals[TOGGLE_SIZE_REQUEST] =
281     g_signal_new (I_("toggle-size-request"),
282                   G_OBJECT_CLASS_TYPE (gobject_class),
283                   G_SIGNAL_RUN_FIRST,
284                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
285                   NULL, NULL,
286                   _gtk_marshal_VOID__POINTER,
287                   G_TYPE_NONE, 1,
288                   G_TYPE_POINTER);
289
290   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
291     g_signal_new (I_("toggle-size-allocate"),
292                   G_OBJECT_CLASS_TYPE (gobject_class),
293                   G_SIGNAL_RUN_FIRST,
294                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
295                   NULL, NULL,
296                   _gtk_marshal_VOID__INT,
297                   G_TYPE_NONE, 1,
298                   G_TYPE_INT);
299
300   menu_item_signals[SELECT] =
301     g_signal_new (I_("select"),
302                   G_OBJECT_CLASS_TYPE (gobject_class),
303                   G_SIGNAL_RUN_FIRST,
304                   G_STRUCT_OFFSET (GtkMenuItemClass, select),
305                   NULL, NULL,
306                   _gtk_marshal_VOID__VOID,
307                   G_TYPE_NONE, 0);
308
309   menu_item_signals[DESELECT] =
310     g_signal_new (I_("deselect"),
311                   G_OBJECT_CLASS_TYPE (gobject_class),
312                   G_SIGNAL_RUN_FIRST,
313                   G_STRUCT_OFFSET (GtkMenuItemClass, deselect),
314                   NULL, NULL,
315                   _gtk_marshal_VOID__VOID,
316                   G_TYPE_NONE, 0);
317
318   /**
319    * GtkMenuItem:right-justified:
320    *
321    * Sets whether the menu item appears justified
322    * at the right side of a menu bar.
323    *
324    * Since: 2.14
325    */
326   g_object_class_install_property (gobject_class,
327                                    PROP_RIGHT_JUSTIFIED,
328                                    g_param_spec_boolean ("right-justified",
329                                                          P_("Right Justified"),
330                                                          P_("Sets whether the menu item appears justified at the right side of a menu bar"),
331                                                          FALSE,
332                                                          GTK_PARAM_READWRITE | G_PARAM_DEPRECATED));
333
334   /**
335    * GtkMenuItem:submenu:
336    *
337    * The submenu attached to the menu item, or %NULL if it has none.
338    *
339    * Since: 2.12
340    */
341   g_object_class_install_property (gobject_class,
342                                    PROP_SUBMENU,
343                                    g_param_spec_object ("submenu",
344                                                         P_("Submenu"),
345                                                         P_("The submenu attached to the menu item, or NULL if it has none"),
346                                                         GTK_TYPE_MENU,
347                                                         GTK_PARAM_READWRITE));
348
349   /**
350    * GtkMenuItem:accel-path:
351    *
352    * Sets the accelerator path of the menu item, through which runtime
353    * changes of the menu item's accelerator caused by the user can be
354    * identified and saved to persistant storage.
355    *
356    * Since: 2.14
357    */
358   g_object_class_install_property (gobject_class,
359                                    PROP_ACCEL_PATH,
360                                    g_param_spec_string ("accel-path",
361                                                         P_("Accel Path"),
362                                                         P_("Sets the accelerator path of the menu item"),
363                                                         NULL,
364                                                         GTK_PARAM_READWRITE));
365
366   /**
367    * GtkMenuItem:label:
368    *
369    * The text for the child label.
370    *
371    * Since: 2.16
372    */
373   g_object_class_install_property (gobject_class,
374                                    PROP_LABEL,
375                                    g_param_spec_string ("label",
376                                                         P_("Label"),
377                                                         P_("The text for the child label"),
378                                                         "",
379                                                         GTK_PARAM_READWRITE));
380
381   /**
382    * GtkMenuItem:use-underline:
383    *
384    * %TRUE if underlines in the text indicate mnemonics.
385    *
386    * Since: 2.16
387    */
388   g_object_class_install_property (gobject_class,
389                                    PROP_USE_UNDERLINE,
390                                    g_param_spec_boolean ("use-underline",
391                                                          P_("Use underline"),
392                                                          P_("If set, an underline in the text indicates "
393                                                             "the next character should be used for the "
394                                                             "mnemonic accelerator key"),
395                                                          FALSE,
396                                                          GTK_PARAM_READWRITE));
397
398   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
399   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
400
401   gtk_widget_class_install_style_property_parser (widget_class,
402                                                   g_param_spec_enum ("selected-shadow-type",
403                                                                      "Selected Shadow Type",
404                                                                      "Shadow type when item is selected",
405                                                                      GTK_TYPE_SHADOW_TYPE,
406                                                                      GTK_SHADOW_NONE,
407                                                                      GTK_PARAM_READABLE),
408                                                   gtk_rc_property_parse_enum);
409
410   gtk_widget_class_install_style_property (widget_class,
411                                            g_param_spec_int ("horizontal-padding",
412                                                              "Horizontal Padding",
413                                                              "Padding to left and right of the menu item",
414                                                              0,
415                                                              G_MAXINT,
416                                                              3,
417                                                              GTK_PARAM_READABLE));
418
419   gtk_widget_class_install_style_property (widget_class,
420                                            g_param_spec_int ("toggle-spacing",
421                                                              "Icon Spacing",
422                                                              "Space between icon and label",
423                                                              0,
424                                                              G_MAXINT,
425                                                              5,
426                                                              GTK_PARAM_READABLE));
427
428   gtk_widget_class_install_style_property (widget_class,
429                                            g_param_spec_int ("arrow-spacing",
430                                                              "Arrow Spacing",
431                                                              "Space between label and arrow",
432                                                              0,
433                                                              G_MAXINT,
434                                                              10,
435                                                              GTK_PARAM_READABLE));
436
437   gtk_widget_class_install_style_property (widget_class,
438                                            g_param_spec_float ("arrow-scaling",
439                                                                P_("Arrow Scaling"),
440                                                                P_("Amount of space used up by arrow, relative to the menu item's font size"),
441                                                                0.0, 2.0, 0.8,
442                                                                GTK_PARAM_READABLE));
443
444   /**
445    * GtkMenuItem:width-chars:
446    *
447    * The minimum desired width of the menu item in characters.
448    *
449    * Since: 2.14
450    */
451   gtk_widget_class_install_style_property (widget_class,
452                                            g_param_spec_int ("width-chars",
453                                                              P_("Width in Characters"),
454                                                              P_("The minimum desired width of the menu item in characters"),
455                                                              0, G_MAXINT, 12,
456                                                              GTK_PARAM_READABLE));
457
458   g_type_class_add_private (klass, sizeof (GtkMenuItemPrivate));
459 }
460
461 static void
462 gtk_menu_item_init (GtkMenuItem *menu_item)
463 {
464   GtkStyleContext *context;
465   GtkMenuItemPrivate *priv;
466
467   priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_item,
468                                       GTK_TYPE_MENU_ITEM,
469                                       GtkMenuItemPrivate);
470   menu_item->priv = priv;
471
472   gtk_widget_set_has_window (GTK_WIDGET (menu_item), FALSE);
473
474   priv->action = NULL;
475   priv->use_action_appearance = TRUE;
476   
477   menu_item->priv->submenu = NULL;
478   menu_item->priv->toggle_size = 0;
479   menu_item->priv->accelerator_width = 0;
480   if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
481     priv->submenu_direction = GTK_DIRECTION_LEFT;
482   else
483     priv->submenu_direction = GTK_DIRECTION_RIGHT;
484   priv->submenu_placement = GTK_TOP_BOTTOM;
485   priv->right_justify = FALSE;
486   priv->use_action_appearance = TRUE;
487   priv->timer = 0;
488   priv->action = NULL;
489
490   context = gtk_widget_get_style_context (GTK_WIDGET (menu_item));
491   gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUITEM);
492 }
493
494 /**
495  * gtk_menu_item_new:
496  *
497  * Creates a new #GtkMenuItem.
498  *
499  * Returns: the newly created #GtkMenuItem
500  */
501 GtkWidget*
502 gtk_menu_item_new (void)
503 {
504   return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
505 }
506
507 /**
508  * gtk_menu_item_new_with_label:
509  * @label: the text for the label
510  *
511  * Creates a new #GtkMenuItem whose child is a #GtkLabel.
512  *
513  * Returns: the newly created #GtkMenuItem
514  */
515 GtkWidget*
516 gtk_menu_item_new_with_label (const gchar *label)
517 {
518   return g_object_new (GTK_TYPE_MENU_ITEM,
519                        "label", label,
520                        NULL);
521 }
522
523
524 /**
525  * gtk_menu_item_new_with_mnemonic:
526  * @label: The text of the button, with an underscore in front of the
527  *     mnemonic character
528  *
529  * Creates a new #GtkMenuItem containing a label.
530  *
531  * The label will be created using gtk_label_new_with_mnemonic(),
532  * so underscores in @label indicate the mnemonic for the menu item.
533  *
534  * Returns: a new #GtkMenuItem
535  */
536 GtkWidget*
537 gtk_menu_item_new_with_mnemonic (const gchar *label)
538 {
539   return g_object_new (GTK_TYPE_MENU_ITEM,
540                        "use-underline", TRUE,
541                        "label", label,
542                        NULL);
543 }
544
545 static void
546 gtk_menu_item_dispose (GObject *object)
547 {
548   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
549   GtkMenuItemPrivate *priv = menu_item->priv;
550
551   if (priv->action)
552     {
553       gtk_action_disconnect_accelerator (priv->action);
554       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL);
555       priv->action = NULL;
556     }
557   G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object);
558 }
559
560 static void
561 gtk_menu_item_set_property (GObject      *object,
562                             guint         prop_id,
563                             const GValue *value,
564                             GParamSpec   *pspec)
565 {
566   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
567
568   switch (prop_id)
569     {
570     case PROP_RIGHT_JUSTIFIED:
571       gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
572       break;
573     case PROP_SUBMENU:
574       gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
575       break;
576     case PROP_ACCEL_PATH:
577       gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
578       break;
579     case PROP_LABEL:
580       gtk_menu_item_set_label (menu_item, g_value_get_string (value));
581       break;
582     case PROP_USE_UNDERLINE:
583       gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
584       break;
585     case PROP_ACTIVATABLE_RELATED_ACTION:
586       gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
587       break;
588     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
589       gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
590       break;
591     default:
592       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
593       break;
594     }
595 }
596
597 static void
598 gtk_menu_item_get_property (GObject    *object,
599                             guint       prop_id,
600                             GValue     *value,
601                             GParamSpec *pspec)
602 {
603   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
604   GtkMenuItemPrivate *priv = menu_item->priv;
605
606   switch (prop_id)
607     {
608     case PROP_RIGHT_JUSTIFIED:
609       g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
610       break;
611     case PROP_SUBMENU:
612       g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
613       break;
614     case PROP_ACCEL_PATH:
615       g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
616       break;
617     case PROP_LABEL:
618       g_value_set_string (value, gtk_menu_item_get_label (menu_item));
619       break;
620     case PROP_USE_UNDERLINE:
621       g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
622       break;
623     case PROP_ACTIVATABLE_RELATED_ACTION:
624       g_value_set_object (value, priv->action);
625       break;
626     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
627       g_value_set_boolean (value, priv->use_action_appearance);
628       break;
629     default:
630       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
631       break;
632     }
633 }
634
635 static void
636 gtk_menu_item_destroy (GtkWidget *widget)
637 {
638   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
639   GtkMenuItemPrivate *priv = menu_item->priv;
640
641   if (priv->submenu)
642     gtk_widget_destroy (priv->submenu);
643
644   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->destroy (widget);
645 }
646
647 static void
648 gtk_menu_item_detacher (GtkWidget *widget,
649                         GtkMenu   *menu)
650 {
651   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
652   GtkMenuItemPrivate *priv = menu_item->priv;
653
654   g_return_if_fail (priv->submenu == (GtkWidget*) menu);
655
656   priv->submenu = NULL;
657 }
658
659 static void
660 get_arrow_size (GtkWidget *widget,
661                 GtkWidget *child,
662                 gint      *size)
663 {
664   GtkStyleContext  *style_context;
665   GtkStateFlags     state;
666   PangoContext     *context;
667   PangoFontMetrics *metrics;
668   gfloat            arrow_scaling;
669
670   g_assert (size);
671
672   gtk_widget_style_get (widget,
673                         "arrow-scaling", &arrow_scaling,
674                         NULL);
675
676   context = gtk_widget_get_pango_context (child);
677   style_context = gtk_widget_get_style_context (child);
678   state = gtk_widget_get_state_flags (child);
679
680   metrics = pango_context_get_metrics (context,
681                                        gtk_style_context_get_font (style_context, state),
682                                        pango_context_get_language (context));
683
684   *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
685                          pango_font_metrics_get_descent (metrics)));
686
687   pango_font_metrics_unref (metrics);
688
689   *size = *size * arrow_scaling;
690 }
691
692
693 static void
694 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
695                                    gpointer   data)
696 {
697   guint *width = data;
698
699   if (GTK_IS_ACCEL_LABEL (widget))
700     {
701       guint w;
702
703       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
704       *width = MAX (*width, w);
705     }
706   else if (GTK_IS_CONTAINER (widget))
707     gtk_container_foreach (GTK_CONTAINER (widget),
708                            gtk_menu_item_accel_width_foreach,
709                            data);
710 }
711
712 static gint
713 get_minimum_width (GtkWidget *widget)
714 {
715   GtkStyleContext *style_context;
716   GtkStateFlags state;
717   PangoContext *context;
718   PangoFontMetrics *metrics;
719   gint width;
720   gint width_chars;
721
722   context = gtk_widget_get_pango_context (widget);
723   style_context = gtk_widget_get_style_context (widget);
724   state = gtk_widget_get_state_flags (widget);
725
726   metrics = pango_context_get_metrics (context,
727                                        gtk_style_context_get_font (style_context, state),
728                                        pango_context_get_language (context));
729
730   width = pango_font_metrics_get_approximate_char_width (metrics);
731
732   pango_font_metrics_unref (metrics);
733
734   gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
735
736   return PANGO_PIXELS (width_chars * width);
737 }
738
739 static void
740 gtk_menu_item_get_preferred_width (GtkWidget *widget,
741                                    gint      *minimum_size,
742                                    gint      *natural_size)
743 {
744   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
745   GtkMenuItemPrivate *priv = menu_item->priv;
746   GtkBin *bin;
747   GtkWidget *child;
748   GtkWidget *parent;
749   guint accel_width;
750   guint horizontal_padding;
751   guint border_width;
752   GtkPackDirection pack_dir;
753   GtkPackDirection child_pack_dir;
754   gint  min_width, nat_width;
755   GtkStyleContext *context;
756   GtkStateFlags state;
757   GtkBorder padding;
758
759   min_width = nat_width = 0;
760
761   gtk_widget_style_get (widget,
762                         "horizontal-padding", &horizontal_padding,
763                         NULL);
764
765   bin = GTK_BIN (widget);
766   parent = gtk_widget_get_parent (widget);
767
768   if (GTK_IS_MENU_BAR (parent))
769     {
770       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
771       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
772     }
773   else
774     {
775       pack_dir = GTK_PACK_DIRECTION_LTR;
776       child_pack_dir = GTK_PACK_DIRECTION_LTR;
777     }
778
779   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
780
781   context = gtk_widget_get_style_context (widget);
782   state = gtk_widget_get_state_flags (widget);
783   gtk_style_context_get_padding (context, state, &padding);
784
785   min_width = (border_width * 2) + padding.left + padding.right;
786
787   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
788       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
789     min_width += 2 * horizontal_padding;
790
791   nat_width = min_width;
792           
793   child = gtk_bin_get_child (bin);
794
795   if (child != NULL && gtk_widget_get_visible (child))
796     {
797       GtkMenuItemPrivate *priv = menu_item->priv;
798       gint child_min, child_nat;
799
800       gtk_widget_get_preferred_width (child, &child_min, &child_nat);
801
802       if ((menu_item->priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
803         {
804           guint arrow_spacing;
805           gint  arrow_size;
806           
807           gtk_widget_style_get (widget,
808                                 "arrow-spacing", &arrow_spacing,
809                                 NULL);
810
811           get_arrow_size (widget, child, &arrow_size);
812
813           gtk_widget_style_get (widget,
814                                 "arrow-spacing", &arrow_spacing,
815                                 NULL);
816
817           get_arrow_size (widget, child, &arrow_size);
818
819           min_width += arrow_size;
820           min_width += arrow_spacing;
821
822           min_width = MAX (min_width, get_minimum_width (widget));
823           nat_width = min_width;
824         }
825
826       min_width += child_min;
827       nat_width += child_nat;
828     }
829
830   accel_width = 0;
831   gtk_container_foreach (GTK_CONTAINER (menu_item),
832                          gtk_menu_item_accel_width_foreach,
833                          &accel_width);
834   priv->accelerator_width = accel_width;
835
836   if (minimum_size)
837     *minimum_size = min_width;
838
839   if (natural_size)
840     *natural_size = nat_width;
841 }
842
843 static void
844 gtk_menu_item_get_preferred_height (GtkWidget *widget,
845                                     gint      *minimum_size,
846                                     gint      *natural_size)
847 {
848   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
849   GtkMenuItemPrivate *priv = menu_item->priv;
850   GtkBin *bin;
851   GtkStyleContext *context;
852   GtkStateFlags state;
853   GtkBorder padding;
854   GtkWidget *child;
855   GtkWidget *parent;
856   guint accel_width;
857   guint horizontal_padding;
858   guint border_width;
859   GtkPackDirection pack_dir;
860   GtkPackDirection child_pack_dir;
861   gint  min_height, nat_height;
862
863   min_height = nat_height = 0;
864
865   context = gtk_widget_get_style_context (widget);
866   state = gtk_widget_get_state_flags (widget);
867   gtk_style_context_get_padding (context, state, &padding);
868
869   gtk_widget_style_get (widget,
870                         "horizontal-padding", &horizontal_padding,
871                         NULL);
872
873   bin = GTK_BIN (widget);
874   parent = gtk_widget_get_parent (widget);
875
876   if (GTK_IS_MENU_BAR (parent))
877     {
878       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
879       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
880     }
881   else
882     {
883       pack_dir = GTK_PACK_DIRECTION_LTR;
884       child_pack_dir = GTK_PACK_DIRECTION_LTR;
885     }
886
887   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
888   min_height = (border_width * 2) + padding.top + padding.bottom;
889
890   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
891       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
892     min_height += 2 * horizontal_padding;
893
894   nat_height = min_height;
895
896   child = gtk_bin_get_child (bin);
897
898   if (child != NULL && gtk_widget_get_visible (child))
899     {
900       GtkMenuItemPrivate *priv = menu_item->priv;
901       gint child_min, child_nat;
902
903       gtk_widget_get_preferred_height (child, &child_min, &child_nat);
904
905       min_height += child_min;
906       nat_height += child_nat;
907
908       if ((menu_item->priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
909         {
910           gint  arrow_size;
911
912           get_arrow_size (widget, child, &arrow_size);
913
914           min_height = MAX (min_height, arrow_size);
915           nat_height = MAX (nat_height, arrow_size);
916         }
917     }
918   else /* separator item */
919     {
920       gboolean wide_separators;
921       gint     separator_height;
922
923       gtk_widget_style_get (widget,
924                             "wide-separators",  &wide_separators,
925                             "separator-height", &separator_height,
926                             NULL);
927
928       if (wide_separators)
929         min_height += separator_height + padding.top;
930       else
931         min_height += padding.top + padding.bottom;
932
933       nat_height = min_height;
934     }
935
936   accel_width = 0;
937   gtk_container_foreach (GTK_CONTAINER (menu_item),
938                          gtk_menu_item_accel_width_foreach,
939                          &accel_width);
940   priv->accelerator_width = accel_width;
941
942   if (minimum_size)
943     *minimum_size = min_height;
944
945   if (natural_size)
946     *natural_size = nat_height;
947 }
948
949 static void
950 gtk_menu_item_get_preferred_height_for_width (GtkWidget *widget,
951                                               gint       for_size,
952                                               gint      *minimum_size,
953                                               gint      *natural_size)
954 {
955   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
956   GtkMenuItemPrivate *priv = menu_item->priv;
957   GtkBin *bin;
958   GtkStyleContext *context;
959   GtkStateFlags state;
960   GtkBorder padding;
961   GtkWidget *child;
962   GtkWidget *parent;
963   guint horizontal_padding;
964   guint border_width;
965   GtkPackDirection pack_dir;
966   GtkPackDirection child_pack_dir;
967   gint  min_height, nat_height;
968   gint  avail_size;
969
970   min_height = nat_height = 0;
971
972   context = gtk_widget_get_style_context (widget);
973   state = gtk_widget_get_state_flags (widget);
974   gtk_style_context_get_padding (context, state, &padding);
975
976   gtk_widget_style_get (widget,
977                         "horizontal-padding", &horizontal_padding,
978                         NULL);
979
980   bin = GTK_BIN (widget);
981   parent = gtk_widget_get_parent (widget);
982
983   if (GTK_IS_MENU_BAR (parent))
984     {
985       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
986       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
987     }
988   else
989     {
990       pack_dir = GTK_PACK_DIRECTION_LTR;
991       child_pack_dir = GTK_PACK_DIRECTION_LTR;
992     }
993
994   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
995   min_height   = (border_width * 2) + padding.top + padding.bottom;
996
997   avail_size   = for_size;
998   avail_size  -= (border_width * 2) + padding.left + padding.right;
999
1000   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1001       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1002     min_height += 2 * horizontal_padding;
1003
1004   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1005       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1006     avail_size -= 2 * horizontal_padding;
1007
1008   nat_height = min_height;
1009
1010   child = gtk_bin_get_child (bin);
1011
1012   if (child != NULL && gtk_widget_get_visible (child))
1013     {
1014       gint child_min, child_nat;
1015       gint arrow_size = 0;
1016       
1017       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1018         {
1019           guint arrow_spacing;
1020
1021           gtk_widget_style_get (widget,
1022                                 "arrow-spacing", &arrow_spacing,
1023                                 NULL);
1024
1025           get_arrow_size (widget, child, &arrow_size);
1026
1027           avail_size -= arrow_size;
1028           avail_size -= arrow_spacing;
1029         }
1030
1031       gtk_widget_get_preferred_height_for_width (child,
1032                                                  avail_size,
1033                                                  &child_min,
1034                                                  &child_nat);
1035
1036       min_height += child_min;
1037       nat_height += child_nat;
1038
1039       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1040         {
1041           min_height = MAX (min_height, arrow_size);
1042           nat_height = MAX (nat_height, arrow_size);
1043         }
1044     }
1045   else /* separator item */
1046     {
1047       gboolean wide_separators;
1048       gint     separator_height;
1049
1050       gtk_widget_style_get (widget,
1051                             "wide-separators",  &wide_separators,
1052                             "separator-height", &separator_height,
1053                             NULL);
1054
1055       if (wide_separators)
1056         min_height += separator_height + padding.top;
1057       else
1058         min_height += padding.top + padding.bottom;
1059
1060       nat_height = min_height;
1061     }
1062
1063   if (minimum_size)
1064     *minimum_size = min_height;
1065
1066   if (natural_size)
1067     *natural_size = nat_height;
1068 }
1069
1070 static void
1071 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
1072 {
1073   parent_buildable_iface = g_type_interface_peek_parent (iface);
1074   iface->add_child = gtk_menu_item_buildable_add_child;
1075   iface->custom_finished = gtk_menu_item_buildable_custom_finished;
1076 }
1077
1078 static void
1079 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
1080                                    GtkBuilder   *builder,
1081                                    GObject      *child,
1082                                    const gchar  *type)
1083 {
1084   if (type && strcmp (type, "submenu") == 0)
1085         gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
1086                                    GTK_WIDGET (child));
1087   else
1088     parent_buildable_iface->add_child (buildable, builder, child, type);
1089 }
1090
1091
1092 static void
1093 gtk_menu_item_buildable_custom_finished (GtkBuildable *buildable,
1094                                          GtkBuilder   *builder,
1095                                          GObject      *child,
1096                                          const gchar  *tagname,
1097                                          gpointer      user_data)
1098 {
1099   GtkWidget *toplevel;
1100
1101   if (strcmp (tagname, "accelerator") == 0)
1102     {
1103       GtkMenuShell *menu_shell;
1104       GtkWidget *attach;
1105
1106       menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (GTK_WIDGET (buildable)));
1107       if (menu_shell)
1108         {
1109           while (GTK_IS_MENU (menu_shell) &&
1110                  (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
1111             menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (attach));
1112
1113           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
1114         }
1115       else
1116         {
1117           /* Fall back to something ... */
1118           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
1119
1120           g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
1121                      gtk_buildable_get_name (buildable));
1122         }
1123
1124       /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
1125       _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
1126     }
1127   else
1128     parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
1129 }
1130
1131
1132 static void
1133 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
1134 {
1135   iface->update = gtk_menu_item_update;
1136   iface->sync_action_properties = gtk_menu_item_sync_action_properties;
1137 }
1138
1139 static void
1140 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
1141 {
1142   GtkWidget *child;
1143
1144   child = gtk_bin_get_child (GTK_BIN (menu_item));
1145
1146   if (GTK_IS_LABEL (child))
1147     {
1148       const gchar *label;
1149
1150       label = gtk_action_get_label (action);
1151       gtk_menu_item_set_label (menu_item, label);
1152     }
1153 }
1154
1155 gboolean _gtk_menu_is_empty (GtkWidget *menu);
1156
1157 static void
1158 gtk_menu_item_update (GtkActivatable *activatable,
1159                       GtkAction      *action,
1160                       const gchar    *property_name)
1161 {
1162   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1163   GtkMenuItemPrivate *priv = menu_item->priv;
1164
1165   if (strcmp (property_name, "visible") == 0)
1166     _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1167                                    _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1168   else if (strcmp (property_name, "sensitive") == 0)
1169     gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1170   else if (priv->use_action_appearance)
1171     {
1172       if (strcmp (property_name, "label") == 0)
1173         activatable_update_label (menu_item, action);
1174     }
1175 }
1176
1177 static void
1178 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
1179                                       GtkAction      *action)
1180 {
1181   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1182   GtkMenuItemPrivate *priv = menu_item->priv;
1183   GtkWidget *label;
1184
1185   if (!priv->use_action_appearance || !action)
1186     {
1187       label = gtk_bin_get_child (GTK_BIN (menu_item));
1188
1189       if (GTK_IS_ACCEL_LABEL (label))
1190         gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), GTK_WIDGET (menu_item));
1191     }
1192
1193   if (!action)
1194     return;
1195
1196   _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1197                                  _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1198
1199   gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1200
1201   if (priv->use_action_appearance)
1202     {
1203       label = gtk_bin_get_child (GTK_BIN (menu_item));
1204
1205       /* make sure label is a label, deleting it otherwise */
1206       if (label && !GTK_IS_LABEL (label))
1207         {
1208           gtk_container_remove (GTK_CONTAINER (menu_item), label);
1209           label = NULL;
1210         }
1211       /* Make sure that menu_item has a label and that any
1212        * accelerators are set */
1213       gtk_menu_item_ensure_label (menu_item);
1214       gtk_menu_item_set_use_underline (menu_item, TRUE);
1215       /* Make label point to the menu_item's label */
1216       label = gtk_bin_get_child (GTK_BIN (menu_item));
1217
1218       if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
1219         {
1220           gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), NULL);
1221           gtk_accel_label_set_accel_closure (GTK_ACCEL_LABEL (label),
1222                                              gtk_action_get_accel_closure (action));
1223         }
1224
1225       activatable_update_label (menu_item, action);
1226     }
1227 }
1228
1229 static void
1230 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
1231                                   GtkAction   *action)
1232 {
1233     GtkMenuItemPrivate *priv = menu_item->priv;
1234
1235     if (priv->action == action)
1236       return;
1237
1238     if (priv->action)
1239       {
1240         gtk_action_disconnect_accelerator (priv->action);
1241       }
1242
1243     if (action)
1244       {
1245         const gchar *accel_path;
1246
1247         accel_path = gtk_action_get_accel_path (action);
1248         if (accel_path)
1249           {
1250             gtk_action_connect_accelerator (action);
1251             gtk_menu_item_set_accel_path (menu_item, accel_path);
1252           }
1253       }
1254
1255     gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
1256
1257     priv->action = action;
1258 }
1259
1260 static void
1261 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
1262                                          gboolean     use_appearance)
1263 {
1264     GtkMenuItemPrivate *priv = menu_item->priv;
1265
1266     if (priv->use_action_appearance != use_appearance)
1267       {
1268         priv->use_action_appearance = use_appearance;
1269
1270         gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
1271       }
1272 }
1273
1274
1275 /**
1276  * gtk_menu_item_set_submenu:
1277  * @menu_item: a #GtkMenuItem
1278  * @submenu: (allow-none): the submenu, or %NULL
1279  *
1280  * Sets or replaces the menu item's submenu, or removes it when a %NULL
1281  * submenu is passed.
1282  */
1283 void
1284 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
1285                            GtkWidget   *submenu)
1286 {
1287   GtkMenuItemPrivate *priv = menu_item->priv;
1288
1289   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1290   g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
1291
1292   if (priv->submenu != submenu)
1293     {
1294       if (priv->submenu)
1295         gtk_menu_detach (GTK_MENU (priv->submenu));
1296
1297       if (submenu)
1298         {
1299           priv->submenu = submenu;
1300           gtk_menu_attach_to_widget (GTK_MENU (submenu),
1301                                      GTK_WIDGET (menu_item),
1302                                      gtk_menu_item_detacher);
1303         }
1304
1305       if (gtk_widget_get_parent (GTK_WIDGET (menu_item)))
1306         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1307
1308       g_object_notify (G_OBJECT (menu_item), "submenu");
1309     }
1310 }
1311
1312 /**
1313  * gtk_menu_item_get_submenu:
1314  * @menu_item: a #GtkMenuItem
1315  *
1316  * Gets the submenu underneath this menu item, if any.
1317  * See gtk_menu_item_set_submenu().
1318  *
1319  * Return value: (transfer none): submenu for this menu item, or %NULL if none
1320  */
1321 GtkWidget *
1322 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
1323 {
1324   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
1325
1326   return menu_item->priv->submenu;
1327 }
1328
1329 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1330                                    GtkSubmenuPlacement  placement);
1331
1332 void
1333 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1334                              GtkSubmenuPlacement  placement)
1335 {
1336   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1337
1338   menu_item->priv->submenu_placement = placement;
1339 }
1340
1341 void
1342 gtk_menu_item_select (GtkMenuItem *menu_item)
1343 {
1344   GtkWidget *parent;
1345
1346   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1347
1348   g_signal_emit (menu_item, menu_item_signals[SELECT], 0);
1349
1350   /* Enable themeing of the parent menu item depending on whether
1351    * something is selected in its submenu
1352    */
1353   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1354   if (GTK_IS_MENU (parent))
1355     {
1356       GtkMenu *menu = GTK_MENU (parent);
1357
1358       if (menu->priv->parent_menu_item)
1359         gtk_widget_queue_draw (GTK_WIDGET (menu->priv->parent_menu_item));
1360     }
1361 }
1362
1363 /**
1364  * gtk_menu_item_deselect:
1365  * @menu_item: the menu item
1366  *
1367  * Emits the #GtkMenuItem::deselect signal on the given item. Behaves
1368  * exactly like #gtk_item_deselect.
1369  */
1370 void
1371 gtk_menu_item_deselect (GtkMenuItem *menu_item)
1372 {
1373   GtkWidget *parent;
1374
1375   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1376
1377   g_signal_emit (menu_item, menu_item_signals[DESELECT], 0);
1378
1379   /* Enable themeing of the parent menu item depending on whether
1380    * something is selected in its submenu
1381    */
1382   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1383   if (GTK_IS_MENU (parent))
1384     {
1385       GtkMenu *menu = GTK_MENU (parent);
1386
1387       if (menu->priv->parent_menu_item)
1388         gtk_widget_queue_draw (GTK_WIDGET (menu->priv->parent_menu_item));
1389     }
1390 }
1391
1392 /**
1393  * gtk_menu_item_activate:
1394  * @menu_item: the menu item
1395  *
1396  * Emits the #GtkMenuItem::activate signal on the given item
1397  */
1398 void
1399 gtk_menu_item_activate (GtkMenuItem *menu_item)
1400 {
1401   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1402
1403   g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
1404 }
1405
1406 /**
1407  * gtk_menu_item_toggle_size_request:
1408  * @menu_item: the menu item
1409  * @requisition: the requisition to use as signal data.
1410  *
1411  * Emits the #GtkMenuItem::toggle-size-request signal on the given item.
1412  */
1413 void
1414 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1415                                    gint        *requisition)
1416 {
1417   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1418
1419   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
1420 }
1421
1422 /**
1423  * gtk_menu_item_toggle_size_allocate:
1424  * @menu_item: the menu item.
1425  * @allocation: the allocation to use as signal data.
1426  *
1427  * Emits the #GtkMenuItem::toggle-size-allocate signal on the given item.
1428  */
1429 void
1430 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1431                                     gint         allocation)
1432 {
1433   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1434
1435   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
1436 }
1437
1438 static void
1439 gtk_menu_item_size_allocate (GtkWidget     *widget,
1440                              GtkAllocation *allocation)
1441 {
1442   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1443   GtkMenuItemPrivate *priv = menu_item->priv;
1444   GtkBin *bin;
1445   GtkAllocation child_allocation;
1446   GtkTextDirection direction;
1447   GtkPackDirection pack_dir;
1448   GtkPackDirection child_pack_dir;
1449   GtkWidget *child;
1450   GtkWidget *parent;
1451
1452   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1453   g_return_if_fail (allocation != NULL);
1454
1455   bin = GTK_BIN (widget);
1456
1457   direction = gtk_widget_get_direction (widget);
1458
1459   parent = gtk_widget_get_parent (widget);
1460   if (GTK_IS_MENU_BAR (parent))
1461     {
1462       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
1463       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
1464     }
1465   else
1466     {
1467       pack_dir = GTK_PACK_DIRECTION_LTR;
1468       child_pack_dir = GTK_PACK_DIRECTION_LTR;
1469     }
1470
1471   gtk_widget_set_allocation (widget, allocation);
1472
1473   child = gtk_bin_get_child (bin);
1474   if (child)
1475     {
1476       GtkStyleContext *context;
1477       GtkStateFlags state;
1478       GtkBorder padding;
1479       guint horizontal_padding;
1480       guint border_width;
1481
1482       context = gtk_widget_get_style_context (widget);
1483       state = gtk_widget_get_state_flags (widget);
1484       gtk_style_context_get_padding (context, state, &padding);
1485
1486       gtk_widget_style_get (widget,
1487                             "horizontal-padding", &horizontal_padding,
1488                             NULL);
1489
1490       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1491       child_allocation.x = border_width + padding.left;
1492       child_allocation.y = border_width + padding.top;
1493
1494       child_allocation.width = allocation->width - (border_width * 2) -
1495         padding.left - padding.right;
1496       child_allocation.height = allocation->height - (border_width * 2) -
1497         padding.top - padding.bottom;
1498
1499       if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1500           (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1501         {
1502           child_allocation.x += horizontal_padding;
1503           child_allocation.width -= 2 * horizontal_padding;
1504         }
1505       else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1506                (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1507         {
1508           child_allocation.y += horizontal_padding;
1509           child_allocation.height -= 2 * horizontal_padding;
1510         }
1511
1512       if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1513           child_pack_dir == GTK_PACK_DIRECTION_RTL)
1514         {
1515           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1516             child_allocation.x += priv->toggle_size;
1517           child_allocation.width -= priv->toggle_size;
1518         }
1519       else
1520         {
1521           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1522             child_allocation.y += priv->toggle_size;
1523           child_allocation.height -= priv->toggle_size;
1524         }
1525
1526       child_allocation.x += allocation->x;
1527       child_allocation.y += allocation->y;
1528
1529       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1530         {
1531           guint arrow_spacing;
1532           gint  arrow_size;
1533
1534           gtk_widget_style_get (widget,
1535                                 "arrow-spacing", &arrow_spacing,
1536                                 NULL);
1537
1538           get_arrow_size (widget, child, &arrow_size);
1539
1540           if (direction == GTK_TEXT_DIR_RTL)
1541             child_allocation.x += arrow_size + arrow_spacing;
1542           child_allocation.width -= arrow_size + arrow_spacing;
1543         }
1544       
1545       if (child_allocation.width < 1)
1546         child_allocation.width = 1;
1547
1548       gtk_widget_size_allocate (child, &child_allocation);
1549     }
1550
1551   if (gtk_widget_get_realized (widget))
1552     gdk_window_move_resize (priv->event_window,
1553                             allocation->x, allocation->y,
1554                             allocation->width, allocation->height);
1555
1556   if (priv->submenu)
1557     gtk_menu_reposition (GTK_MENU (priv->submenu));
1558 }
1559
1560 static void
1561 gtk_menu_item_realize (GtkWidget *widget)
1562 {
1563   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1564   GtkMenuItemPrivate *priv = menu_item->priv;
1565   GtkAllocation allocation;
1566   GdkWindow *window;
1567   GdkWindowAttr attributes;
1568   gint attributes_mask;
1569
1570   gtk_widget_set_realized (widget, TRUE);
1571
1572   window = gtk_widget_get_parent_window (widget);
1573   gtk_widget_set_window (widget, window);
1574   g_object_ref (window);
1575
1576   gtk_widget_get_allocation (widget, &allocation);
1577
1578   attributes.x = allocation.x;
1579   attributes.y = allocation.y;
1580   attributes.width = allocation.width;
1581   attributes.height = allocation.height;
1582   attributes.window_type = GDK_WINDOW_CHILD;
1583   attributes.wclass = GDK_INPUT_ONLY;
1584   attributes.event_mask = (gtk_widget_get_events (widget) |
1585                            GDK_BUTTON_PRESS_MASK |
1586                            GDK_BUTTON_RELEASE_MASK |
1587                            GDK_ENTER_NOTIFY_MASK |
1588                            GDK_LEAVE_NOTIFY_MASK |
1589                            GDK_POINTER_MOTION_MASK);
1590
1591   attributes_mask = GDK_WA_X | GDK_WA_Y;
1592
1593   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1594                                        &attributes, attributes_mask);
1595   gdk_window_set_user_data (priv->event_window, widget);
1596 }
1597
1598 static void
1599 gtk_menu_item_unrealize (GtkWidget *widget)
1600 {
1601   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1602   GtkMenuItemPrivate *priv = menu_item->priv;
1603
1604   gdk_window_set_user_data (priv->event_window, NULL);
1605   gdk_window_destroy (priv->event_window);
1606   priv->event_window = NULL;
1607
1608   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1609 }
1610
1611 static void
1612 gtk_menu_item_map (GtkWidget *widget)
1613 {
1614   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1615   GtkMenuItemPrivate *priv = menu_item->priv;
1616
1617   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1618
1619   gdk_window_show (priv->event_window);
1620 }
1621
1622 static void
1623 gtk_menu_item_unmap (GtkWidget *widget)
1624 {
1625   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1626   GtkMenuItemPrivate *priv = menu_item->priv;
1627
1628   gdk_window_hide (priv->event_window);
1629
1630   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1631 }
1632
1633 static gboolean
1634 gtk_menu_item_enter (GtkWidget        *widget,
1635                      GdkEventCrossing *event)
1636 {
1637   g_return_val_if_fail (event != NULL, FALSE);
1638
1639   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event);
1640 }
1641
1642 static gboolean
1643 gtk_menu_item_leave (GtkWidget        *widget,
1644                      GdkEventCrossing *event)
1645 {
1646   g_return_val_if_fail (event != NULL, FALSE);
1647
1648   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent*) event);
1649 }
1650
1651 static gboolean
1652 gtk_menu_item_draw (GtkWidget *widget,
1653                     cairo_t   *cr)
1654 {
1655   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1656   GtkMenuItemPrivate *priv = menu_item->priv;
1657   GtkStateFlags state;
1658   GtkStyleContext *context;
1659   GtkBorder padding;
1660   GtkWidget *child, *parent;
1661   gint x, y, w, h, width, height;
1662   guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1663
1664   state = gtk_widget_get_state_flags (widget);
1665   context = gtk_widget_get_style_context (widget);
1666   width = gtk_widget_get_allocated_width (widget);
1667   height = gtk_widget_get_allocated_height (widget);
1668
1669   x = border_width;
1670   y = border_width;
1671   w = width - border_width * 2;
1672   h = height - border_width * 2;
1673
1674   child = gtk_bin_get_child (GTK_BIN (menu_item));
1675   parent = gtk_widget_get_parent (widget);
1676
1677   gtk_style_context_save (context);
1678   gtk_style_context_set_state (context, state);
1679
1680   gtk_style_context_get_padding (context, state, &padding);
1681
1682   if (GTK_IS_MENU_BAR (parent))
1683     gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR);
1684
1685   if (child && (state & GTK_STATE_FLAG_PRELIGHT))
1686     {
1687       gtk_render_background (context, cr, x, y, w, h);
1688       gtk_render_frame (context, cr, x, y, w, h);
1689     }
1690
1691   if (priv->submenu && !GTK_IS_MENU_BAR (parent))
1692     {
1693       gint arrow_x, arrow_y;
1694       gint arrow_size;
1695       guint horizontal_padding;
1696       GtkTextDirection direction;
1697       gdouble angle;
1698
1699       direction = gtk_widget_get_direction (widget);
1700
1701       gtk_widget_style_get (widget,
1702                             "horizontal-padding", &horizontal_padding,
1703                             NULL);
1704
1705       get_arrow_size (widget, child, &arrow_size);
1706
1707       if (direction == GTK_TEXT_DIR_LTR)
1708         {
1709           arrow_x = x + w - horizontal_padding - arrow_size;
1710           angle = G_PI / 2;
1711         }
1712       else
1713         {
1714           arrow_x = x + horizontal_padding;
1715           angle = (3 * G_PI) / 2;
1716         }
1717
1718       arrow_y = y + (h - arrow_size) / 2;
1719
1720       gtk_render_arrow (context, cr, angle, arrow_x, arrow_y, arrow_size);
1721     }
1722   else if (!child)
1723     {
1724       gboolean wide_separators;
1725       gint     separator_height;
1726       guint    horizontal_padding;
1727
1728       gtk_widget_style_get (widget,
1729                             "wide-separators",    &wide_separators,
1730                             "separator-height",   &separator_height,
1731                             "horizontal-padding", &horizontal_padding,
1732                             NULL);
1733       if (wide_separators)
1734         gtk_render_frame (context, cr,
1735                           horizontal_padding + padding.left,
1736                           (height - separator_height - padding.top) / 2,
1737                           width - (2 * horizontal_padding) - padding.left - padding.right,
1738                           separator_height);
1739       else
1740         gtk_render_line (context, cr,
1741                          horizontal_padding + padding.left,
1742                          (height - padding.top) / 2,
1743                          width - horizontal_padding - padding.right - 1,
1744                          (height - padding.top) / 2);
1745     }
1746
1747   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->draw (widget, cr);
1748
1749   gtk_style_context_restore (context);
1750
1751   return FALSE;
1752 }
1753
1754 /**
1755  * gtk_menu_item_select:
1756  * @menu_item: the menu item
1757  *
1758  * Emits the #GtkMenuItem::select signal on the given item. Behaves
1759  * exactly like #gtk_item_select.
1760  */
1761 static void
1762 gtk_real_menu_item_select (GtkMenuItem *menu_item)
1763 {
1764   GtkMenuItemPrivate *priv = menu_item->priv;
1765   gboolean touchscreen_mode;
1766
1767   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
1768                 "gtk-touchscreen-mode", &touchscreen_mode,
1769                 NULL);
1770
1771   if (!touchscreen_mode && priv->submenu &&
1772       (!gtk_widget_get_mapped (priv->submenu) ||
1773        GTK_MENU (priv->submenu)->priv->tearoff_active))
1774     {
1775       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1776     }
1777
1778   gtk_widget_set_state_flags (GTK_WIDGET (menu_item),
1779                               GTK_STATE_FLAG_PRELIGHT, FALSE);
1780   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1781 }
1782
1783 static void
1784 gtk_real_menu_item_deselect (GtkMenuItem *menu_item)
1785 {
1786   GtkMenuItemPrivate *priv = menu_item->priv;
1787
1788   if (priv->submenu)
1789     _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1790
1791   gtk_widget_unset_state_flags (GTK_WIDGET (menu_item),
1792                                 GTK_STATE_FLAG_PRELIGHT);
1793   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1794 }
1795
1796 static gboolean
1797 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1798                                  gboolean   group_cycling)
1799 {
1800   GtkWidget *parent;
1801
1802   parent = gtk_widget_get_parent (widget);
1803
1804   if (GTK_IS_MENU_SHELL (parent))
1805     _gtk_menu_shell_set_keyboard_mode (GTK_MENU_SHELL (parent), TRUE);
1806
1807   if (group_cycling &&
1808       parent &&
1809       GTK_IS_MENU_SHELL (parent) &&
1810       GTK_MENU_SHELL (parent)->priv->active)
1811     {
1812       gtk_menu_shell_select_item (GTK_MENU_SHELL (parent), widget);
1813     }
1814   else
1815     g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1816
1817   return TRUE;
1818 }
1819
1820 static void
1821 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1822 {
1823   GtkMenuItemPrivate *priv = menu_item->priv;
1824
1825   if (priv->action)
1826     gtk_action_activate (priv->action);
1827 }
1828
1829
1830 static void
1831 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1832 {
1833   GtkMenuItemPrivate *priv = menu_item->priv;
1834   GtkWidget *parent;
1835   GtkWidget *widget;
1836
1837   widget = GTK_WIDGET (menu_item);
1838   parent = gtk_widget_get_parent (widget);
1839
1840   if (parent && GTK_IS_MENU_SHELL (parent))
1841     {
1842       GtkMenuShell *menu_shell = GTK_MENU_SHELL (parent);
1843
1844       if (priv->submenu == NULL)
1845         gtk_menu_shell_activate_item (menu_shell, widget, TRUE);
1846       else
1847         {
1848           gtk_menu_shell_select_item (menu_shell, widget);
1849           _gtk_menu_item_popup_submenu (widget, FALSE);
1850
1851           gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->submenu), TRUE);
1852         }
1853     }
1854 }
1855
1856 static void
1857 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1858                                         gint        *requisition)
1859 {
1860   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1861
1862   *requisition = 0;
1863 }
1864
1865 static void
1866 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1867                                          gint         allocation)
1868 {
1869   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1870
1871   menu_item->priv->toggle_size = allocation;
1872 }
1873
1874 static void
1875 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1876                               const gchar *label)
1877 {
1878   GtkWidget *child;
1879
1880   gtk_menu_item_ensure_label (menu_item);
1881
1882   child = gtk_bin_get_child (GTK_BIN (menu_item));
1883   if (GTK_IS_LABEL (child))
1884     {
1885       gtk_label_set_label (GTK_LABEL (child), label ? label : "");
1886
1887       g_object_notify (G_OBJECT (menu_item), "label");
1888     }
1889 }
1890
1891 static const gchar *
1892 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1893 {
1894   GtkWidget *child;
1895
1896   gtk_menu_item_ensure_label (menu_item);
1897
1898   child = gtk_bin_get_child (GTK_BIN (menu_item));
1899   if (GTK_IS_LABEL (child))
1900     return gtk_label_get_label (GTK_LABEL (child));
1901
1902   return NULL;
1903 }
1904
1905 static void
1906 free_timeval (GTimeVal *val)
1907 {
1908   g_slice_free (GTimeVal, val);
1909 }
1910
1911 static void
1912 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1913                                   gboolean   remember_exact_time)
1914 {
1915   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1916   GtkMenuItemPrivate *priv = menu_item->priv;
1917   GtkWidget *parent;
1918
1919   parent = gtk_widget_get_parent (widget);
1920
1921   if (gtk_widget_is_sensitive (priv->submenu) && parent)
1922     {
1923       gboolean take_focus;
1924       GtkMenuPositionFunc menu_position_func;
1925
1926       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (parent));
1927       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->submenu), take_focus);
1928
1929       if (remember_exact_time)
1930         {
1931           GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1932
1933           g_get_current_time (popup_time);
1934
1935           g_object_set_data_full (G_OBJECT (priv->submenu),
1936                                   "gtk-menu-exact-popup-time", popup_time,
1937                                   (GDestroyNotify) free_timeval);
1938         }
1939       else
1940         {
1941           g_object_set_data (G_OBJECT (priv->submenu),
1942                              "gtk-menu-exact-popup-time", NULL);
1943         }
1944
1945       /* gtk_menu_item_position_menu positions the submenu from the
1946        * menuitems position. If the menuitem doesn't have a window,
1947        * that doesn't work. In that case we use the default
1948        * positioning function instead which places the submenu at the
1949        * mouse cursor.
1950        */
1951       if (gtk_widget_get_window (widget))
1952         menu_position_func = gtk_menu_item_position_menu;
1953       else
1954         menu_position_func = NULL;
1955
1956       gtk_menu_popup (GTK_MENU (priv->submenu),
1957                       parent,
1958                       widget,
1959                       menu_position_func,
1960                       menu_item,
1961                       GTK_MENU_SHELL (parent)->priv->button,
1962                       0);
1963     }
1964
1965   /* Enable themeing of the parent menu item depending on whether
1966    * its submenu is shown or not.
1967    */
1968   gtk_widget_queue_draw (widget);
1969 }
1970
1971 static gint
1972 gtk_menu_item_popup_timeout (gpointer data)
1973 {
1974   GtkMenuItem *menu_item = GTK_MENU_ITEM (data);
1975   GtkMenuItemPrivate *priv = menu_item->priv;
1976   GtkWidget *parent;
1977
1978   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1979
1980   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->priv->active) ||
1981       (GTK_IS_MENU (parent) && GTK_MENU (parent)->priv->torn_off))
1982     {
1983       gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1984       if (priv->timer_from_keypress && priv->submenu)
1985         GTK_MENU_SHELL (priv->submenu)->priv->ignore_enter = TRUE;
1986     }
1987
1988   priv->timer = 0;
1989
1990   return FALSE;
1991 }
1992
1993 static gint
1994 get_popup_delay (GtkWidget *widget)
1995 {
1996   GtkWidget *parent;
1997
1998   parent = gtk_widget_get_parent (widget);
1999   if (GTK_IS_MENU_SHELL (parent))
2000     {
2001       return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent));
2002     }
2003   else
2004     {
2005       gint popup_delay;
2006
2007       g_object_get (gtk_widget_get_settings (widget),
2008                     "gtk-menu-popup-delay", &popup_delay,
2009                     NULL);
2010
2011       return popup_delay;
2012     }
2013 }
2014
2015 void
2016 _gtk_menu_item_popup_submenu (GtkWidget *widget,
2017                               gboolean   with_delay)
2018 {
2019   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2020   GtkMenuItemPrivate *priv = menu_item->priv;
2021
2022   if (priv->timer)
2023     {
2024       g_source_remove (priv->timer);
2025       priv->timer = 0;
2026       with_delay = FALSE;
2027     }
2028
2029   if (with_delay)
2030     {
2031       gint popup_delay = get_popup_delay (widget);
2032
2033       if (popup_delay > 0)
2034         {
2035           GdkEvent *event = gtk_get_current_event ();
2036
2037           priv->timer = gdk_threads_add_timeout (popup_delay,
2038                                                  gtk_menu_item_popup_timeout,
2039                                                  menu_item);
2040
2041           if (event &&
2042               event->type != GDK_BUTTON_PRESS &&
2043               event->type != GDK_ENTER_NOTIFY)
2044             priv->timer_from_keypress = TRUE;
2045           else
2046             priv->timer_from_keypress = FALSE;
2047
2048           if (event)
2049             gdk_event_free (event);
2050
2051           return;
2052         }
2053     }
2054
2055   gtk_menu_item_real_popup_submenu (widget, FALSE);
2056 }
2057
2058 void
2059 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
2060 {
2061   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2062   GtkMenuItemPrivate *priv = menu_item->priv;
2063
2064   if (priv->submenu)
2065     {
2066       g_object_set_data (G_OBJECT (priv->submenu),
2067                          "gtk-menu-exact-popup-time", NULL);
2068
2069       if (priv->timer)
2070         {
2071           g_source_remove (priv->timer);
2072           priv->timer = 0;
2073         }
2074       else
2075         gtk_menu_popdown (GTK_MENU (priv->submenu));
2076
2077       gtk_widget_queue_draw (widget);
2078     }
2079 }
2080
2081 static void
2082 get_offsets (GtkMenu *menu,
2083              gint    *horizontal_offset,
2084              gint    *vertical_offset)
2085 {
2086   gint vertical_padding;
2087   gint horizontal_padding;
2088   GtkStyleContext *context;
2089   GtkStateFlags state;
2090   GtkBorder padding;
2091
2092   gtk_widget_style_get (GTK_WIDGET (menu),
2093                         "horizontal-offset", horizontal_offset,
2094                         "vertical-offset", vertical_offset,
2095                         "horizontal-padding", &horizontal_padding,
2096                         "vertical-padding", &vertical_padding,
2097                         NULL);
2098
2099   context = gtk_widget_get_style_context (GTK_WIDGET (menu));
2100   state = gtk_widget_get_state_flags (GTK_WIDGET (menu));
2101   gtk_style_context_get_padding (context, state, &padding);
2102
2103   *vertical_offset -= padding.top;
2104   *vertical_offset -= vertical_padding;
2105   *horizontal_offset += horizontal_padding;
2106 }
2107
2108 static void
2109 gtk_menu_item_position_menu (GtkMenu  *menu,
2110                              gint     *x,
2111                              gint     *y,
2112                              gboolean *push_in,
2113                              gpointer  user_data)
2114 {
2115   GtkMenuItem *menu_item = GTK_MENU_ITEM (user_data);
2116   GtkMenuItemPrivate *priv = menu_item->priv;
2117   GtkAllocation allocation;
2118   GtkWidget *widget;
2119   GtkMenuItem *parent_menu_item;
2120   GtkWidget *parent;
2121   GdkScreen *screen;
2122   gint twidth, theight;
2123   gint tx, ty;
2124   GtkTextDirection direction;
2125   GdkRectangle monitor;
2126   gint monitor_num;
2127   gint horizontal_offset;
2128   gint vertical_offset;
2129   gint available_left, available_right;
2130   GtkStyleContext *context;
2131   GtkStateFlags state;
2132   GtkBorder parent_padding;
2133
2134   g_return_if_fail (menu != NULL);
2135   g_return_if_fail (x != NULL);
2136   g_return_if_fail (y != NULL);
2137
2138   widget = GTK_WIDGET (user_data);
2139
2140   if (push_in)
2141     *push_in = FALSE;
2142
2143   direction = gtk_widget_get_direction (widget);
2144
2145   twidth = gtk_widget_get_allocated_width (GTK_WIDGET (menu));
2146   theight = gtk_widget_get_allocated_height (GTK_WIDGET (menu));
2147
2148   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
2149   monitor_num = gdk_screen_get_monitor_at_window (screen, priv->event_window);
2150   if (monitor_num < 0)
2151     monitor_num = 0;
2152   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2153
2154   if (!gdk_window_get_origin (gtk_widget_get_window (widget), &tx, &ty))
2155     {
2156       g_warning ("Menu not on screen");
2157       return;
2158     }
2159
2160   gtk_widget_get_allocation (widget, &allocation);
2161
2162   tx += allocation.x;
2163   ty += allocation.y;
2164
2165   get_offsets (menu, &horizontal_offset, &vertical_offset);
2166
2167   available_left = tx - monitor.x;
2168   available_right = monitor.x + monitor.width - (tx + allocation.width);
2169
2170   parent = gtk_widget_get_parent (widget);
2171   if (GTK_IS_MENU_BAR (parent))
2172     {
2173       priv->from_menubar = TRUE;
2174     }
2175   else if (GTK_IS_MENU (parent))
2176     {
2177       if (GTK_MENU (parent)->priv->parent_menu_item)
2178         priv->from_menubar = GTK_MENU_ITEM (GTK_MENU (parent)->priv->parent_menu_item)->priv->from_menubar;
2179       else
2180         priv->from_menubar = FALSE;
2181     }
2182   else
2183     {
2184       priv->from_menubar = FALSE;
2185     }
2186
2187   switch (priv->submenu_placement)
2188     {
2189     case GTK_TOP_BOTTOM:
2190       if (direction == GTK_TEXT_DIR_LTR)
2191         priv->submenu_direction = GTK_DIRECTION_RIGHT;
2192       else
2193         {
2194           priv->submenu_direction = GTK_DIRECTION_LEFT;
2195           tx += allocation.width - twidth;
2196         }
2197       if ((ty + allocation.height + theight) <= monitor.y + monitor.height)
2198         ty += allocation.height;
2199       else if ((ty - theight) >= monitor.y)
2200         ty -= theight;
2201       else if (monitor.y + monitor.height - (ty + allocation.height) > ty)
2202         ty += allocation.height;
2203       else
2204         ty -= theight;
2205       break;
2206
2207     case GTK_LEFT_RIGHT:
2208       if (GTK_IS_MENU (parent))
2209         parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->priv->parent_menu_item);
2210       else
2211         parent_menu_item = NULL;
2212
2213       context = gtk_widget_get_style_context (parent);
2214       state = gtk_widget_get_state_flags (parent);
2215       gtk_style_context_get_padding (context, state, &parent_padding);
2216
2217       if (parent_menu_item && !GTK_MENU (parent)->priv->torn_off)
2218         {
2219           priv->submenu_direction = parent_menu_item->priv->submenu_direction;
2220         }
2221       else
2222         {
2223           if (direction == GTK_TEXT_DIR_LTR)
2224             priv->submenu_direction = GTK_DIRECTION_RIGHT;
2225           else
2226             priv->submenu_direction = GTK_DIRECTION_LEFT;
2227         }
2228
2229       switch (priv->submenu_direction)
2230         {
2231         case GTK_DIRECTION_LEFT:
2232           if (tx - twidth - parent_padding.left - horizontal_offset >= monitor.x ||
2233               available_left >= available_right)
2234             tx -= twidth + parent_padding.left + horizontal_offset;
2235           else
2236             {
2237               priv->submenu_direction = GTK_DIRECTION_RIGHT;
2238               tx += allocation.width + parent_padding.right + horizontal_offset;
2239             }
2240           break;
2241
2242         case GTK_DIRECTION_RIGHT:
2243           if (tx + allocation.width + parent_padding.right + horizontal_offset + twidth <= monitor.x + monitor.width ||
2244               available_right >= available_left)
2245             tx += allocation.width + parent_padding.right + horizontal_offset;
2246           else
2247             {
2248               priv->submenu_direction = GTK_DIRECTION_LEFT;
2249               tx -= twidth + parent_padding.left + horizontal_offset;
2250             }
2251           break;
2252         }
2253
2254       ty += vertical_offset;
2255
2256       /* If the height of the menu doesn't fit we move it upward. */
2257       ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
2258       break;
2259     }
2260
2261   /* If we have negative, tx, here it is because we can't get
2262    * the menu all the way on screen. Favor the left portion.
2263    */
2264   *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
2265   *y = ty;
2266
2267   gtk_menu_set_monitor (menu, monitor_num);
2268
2269   if (!gtk_widget_get_visible (menu->priv->toplevel))
2270     {
2271       gtk_window_set_type_hint (GTK_WINDOW (menu->priv->toplevel), priv->from_menubar?
2272                                 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
2273     }
2274 }
2275
2276 /**
2277  * gtk_menu_item_set_right_justified:
2278  * @menu_item: a #GtkMenuItem.
2279  * @right_justified: if %TRUE the menu item will appear at the
2280  *   far right if added to a menu bar
2281  *
2282  * Sets whether the menu item appears justified at the right
2283  * side of a menu bar. This was traditionally done for "Help"
2284  * menu items, but is now considered a bad idea. (If the widget
2285  * layout is reversed for a right-to-left language like Hebrew
2286  * or Arabic, right-justified-menu-items appear at the left.)
2287  *
2288  * Deprecated: 3.2: If you insist on using it, use
2289  *   gtk_widget_set_hexpand() and gtk_widget_set_halign().
2290  **/
2291 void
2292 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
2293                                    gboolean     right_justified)
2294 {
2295   GtkMenuItemPrivate *priv = menu_item->priv;
2296
2297   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2298
2299   right_justified = right_justified != FALSE;
2300
2301   if (priv->right_justify != right_justified)
2302     {
2303       priv->right_justify = right_justified;
2304       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
2305     }
2306 }
2307
2308 /**
2309  * gtk_menu_item_get_right_justified:
2310  * @menu_item: a #GtkMenuItem
2311  *
2312  * Gets whether the menu item appears justified at the right
2313  * side of the menu bar.
2314  *
2315  * Return value: %TRUE if the menu item will appear at the
2316  *   far right if added to a menu bar.
2317  *
2318  * Deprecated: 3.2: See gtk_menu_item_set_right_justified()
2319  **/
2320 gboolean
2321 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
2322 {
2323   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2324
2325   return menu_item->priv->right_justify;
2326 }
2327
2328
2329 static void
2330 gtk_menu_item_show_all (GtkWidget *widget)
2331 {
2332   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2333   GtkMenuItemPrivate *priv = menu_item->priv;
2334
2335   /* show children including submenu */
2336   if (priv->submenu)
2337     gtk_widget_show_all (priv->submenu);
2338   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2339
2340   gtk_widget_show (widget);
2341 }
2342
2343 static gboolean
2344 gtk_menu_item_can_activate_accel (GtkWidget *widget,
2345                                   guint      signal_id)
2346 {
2347   GtkWidget *parent;
2348
2349   parent = gtk_widget_get_parent (widget);
2350
2351   /* Chain to the parent GtkMenu for further checks */
2352   return (gtk_widget_is_sensitive (widget) && gtk_widget_get_visible (widget) &&
2353           parent && gtk_widget_can_activate_accel (parent, signal_id));
2354 }
2355
2356 static void
2357 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
2358                                   gpointer   data)
2359 {
2360   const gchar **path_p = data;
2361
2362   if (!*path_p)
2363     {
2364       if (GTK_IS_LABEL (widget))
2365         {
2366           *path_p = gtk_label_get_text (GTK_LABEL (widget));
2367           if (*path_p && (*path_p)[0] == 0)
2368             *path_p = NULL;
2369         }
2370       else if (GTK_IS_CONTAINER (widget))
2371         gtk_container_foreach (GTK_CONTAINER (widget),
2372                                gtk_menu_item_accel_name_foreach,
2373                                data);
2374     }
2375 }
2376
2377 static void
2378 gtk_menu_item_parent_set (GtkWidget *widget,
2379                           GtkWidget *previous_parent)
2380 {
2381   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2382   GtkMenu *menu;
2383   GtkWidget *parent;
2384
2385   parent = gtk_widget_get_parent (widget);
2386   menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
2387
2388   if (menu)
2389     _gtk_menu_item_refresh_accel_path (menu_item,
2390                                        menu->priv->accel_path,
2391                                        menu->priv->accel_group,
2392                                        TRUE);
2393
2394   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
2395     GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
2396 }
2397
2398 void
2399 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
2400                                    const gchar   *prefix,
2401                                    GtkAccelGroup *accel_group,
2402                                    gboolean       group_changed)
2403 {
2404   GtkMenuItemPrivate *priv = menu_item->priv;
2405   const gchar *path;
2406   GtkWidget *widget;
2407
2408   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2409   g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
2410
2411   widget = GTK_WIDGET (menu_item);
2412
2413   if (!accel_group)
2414     {
2415       gtk_widget_set_accel_path (widget, NULL, NULL);
2416       return;
2417     }
2418
2419   path = _gtk_widget_get_accel_path (widget, NULL);
2420   if (!path)  /* no active accel_path yet */
2421     {
2422       path = priv->accel_path;
2423       if (!path && prefix)
2424         {
2425           const gchar *postfix = NULL;
2426           gchar *new_path;
2427
2428           /* try to construct one from label text */
2429           gtk_container_foreach (GTK_CONTAINER (menu_item),
2430                                  gtk_menu_item_accel_name_foreach,
2431                                  &postfix);
2432           if (postfix)
2433             {
2434               new_path = g_strconcat (prefix, "/", postfix, NULL);
2435               path = priv->accel_path = (char*)g_intern_string (new_path);
2436               g_free (new_path);
2437             }
2438         }
2439       if (path)
2440         gtk_widget_set_accel_path (widget, path, accel_group);
2441     }
2442   else if (group_changed)    /* reinstall accelerators */
2443     gtk_widget_set_accel_path (widget, path, accel_group);
2444 }
2445
2446 /**
2447  * gtk_menu_item_set_accel_path
2448  * @menu_item:  a valid #GtkMenuItem
2449  * @accel_path: (allow-none): accelerator path, corresponding to this menu
2450  *     item's functionality, or %NULL to unset the current path.
2451  *
2452  * Set the accelerator path on @menu_item, through which runtime
2453  * changes of the menu item's accelerator caused by the user can be
2454  * identified and saved to persistent storage (see gtk_accel_map_save()
2455  * on this). To set up a default accelerator for this menu item, call
2456  * gtk_accel_map_add_entry() with the same @accel_path. See also
2457  * gtk_accel_map_add_entry() on the specifics of accelerator paths,
2458  * and gtk_menu_set_accel_path() for a more convenient variant of
2459  * this function.
2460  *
2461  * This function is basically a convenience wrapper that handles
2462  * calling gtk_widget_set_accel_path() with the appropriate accelerator
2463  * group for the menu item.
2464  *
2465  * Note that you do need to set an accelerator on the parent menu with
2466  * gtk_menu_set_accel_group() for this to work.
2467  *
2468  * Note that @accel_path string will be stored in a #GQuark.
2469  * Therefore, if you pass a static string, you can save some memory
2470  * by interning it first with g_intern_static_string().
2471  */
2472 void
2473 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2474                               const gchar *accel_path)
2475 {
2476   GtkMenuItemPrivate *priv = menu_item->priv;
2477   GtkWidget *parent;
2478   GtkWidget *widget;
2479
2480   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2481   g_return_if_fail (accel_path == NULL ||
2482                     (accel_path[0] == '<' && strchr (accel_path, '/')));
2483
2484   widget = GTK_WIDGET (menu_item);
2485
2486   /* store new path */
2487   priv->accel_path = (char*)g_intern_string (accel_path);
2488
2489   /* forget accelerators associated with old path */
2490   gtk_widget_set_accel_path (widget, NULL, NULL);
2491
2492   /* install accelerators associated with new path */
2493   parent = gtk_widget_get_parent (widget);
2494   if (GTK_IS_MENU (parent))
2495     {
2496       GtkMenu *menu = GTK_MENU (parent);
2497
2498       if (menu->priv->accel_group)
2499         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2500                                            NULL,
2501                                            menu->priv->accel_group,
2502                                            FALSE);
2503     }
2504 }
2505
2506 /**
2507  * gtk_menu_item_get_accel_path
2508  * @menu_item:  a valid #GtkMenuItem
2509  *
2510  * Retrieve the accelerator path that was previously set on @menu_item.
2511  *
2512  * See gtk_menu_item_set_accel_path() for details.
2513  *
2514  * Returns: the accelerator path corresponding to this menu
2515  *     item's functionality, or %NULL if not set
2516  *
2517  * Since: 2.14
2518  */
2519 const gchar *
2520 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2521 {
2522   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2523
2524   return menu_item->priv->accel_path;
2525 }
2526
2527 static void
2528 gtk_menu_item_forall (GtkContainer *container,
2529                       gboolean      include_internals,
2530                       GtkCallback   callback,
2531                       gpointer      callback_data)
2532 {
2533   GtkWidget *child;
2534
2535   g_return_if_fail (GTK_IS_MENU_ITEM (container));
2536   g_return_if_fail (callback != NULL);
2537
2538   child = gtk_bin_get_child (GTK_BIN (container));
2539   if (child)
2540     callback (child, callback_data);
2541 }
2542
2543 gboolean
2544 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2545 {
2546   if ((!gtk_bin_get_child (GTK_BIN (menu_item)) &&
2547        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2548       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2549       !gtk_widget_is_sensitive (menu_item) ||
2550       !gtk_widget_get_visible (menu_item))
2551     return FALSE;
2552
2553   return TRUE;
2554 }
2555
2556 static void
2557 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2558 {
2559   GtkWidget *accel_label;
2560
2561   if (!gtk_bin_get_child (GTK_BIN (menu_item)))
2562     {
2563       accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2564       gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2565
2566       gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2567       gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
2568                                         GTK_WIDGET (menu_item));
2569       gtk_widget_show (accel_label);
2570     }
2571 }
2572
2573 /**
2574  * gtk_menu_item_set_label:
2575  * @menu_item: a #GtkMenuItem
2576  * @label: the text you want to set
2577  *
2578  * Sets @text on the @menu_item label
2579  *
2580  * Since: 2.16
2581  */
2582 void
2583 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2584                          const gchar *label)
2585 {
2586   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2587
2588   GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2589 }
2590
2591 /**
2592  * gtk_menu_item_get_label:
2593  * @menu_item: a #GtkMenuItem
2594  *
2595  * Sets @text on the @menu_item label
2596  *
2597  * Returns: The text in the @menu_item label. This is the internal
2598  *   string used by the label, and must not be modified.
2599  *
2600  * Since: 2.16
2601  */
2602 const gchar *
2603 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2604 {
2605   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2606
2607   return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2608 }
2609
2610 /**
2611  * gtk_menu_item_set_use_underline:
2612  * @menu_item: a #GtkMenuItem
2613  * @setting: %TRUE if underlines in the text indicate mnemonics
2614  *
2615  * If true, an underline in the text indicates the next character
2616  * should be used for the mnemonic accelerator key.
2617  *
2618  * Since: 2.16
2619  */
2620 void
2621 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2622                                  gboolean     setting)
2623 {
2624   GtkWidget *child;
2625
2626   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2627
2628   gtk_menu_item_ensure_label (menu_item);
2629
2630   child = gtk_bin_get_child (GTK_BIN (menu_item));
2631   if (GTK_IS_LABEL (child))
2632     {
2633       gtk_label_set_use_underline (GTK_LABEL (child), setting);
2634
2635       g_object_notify (G_OBJECT (menu_item), "use-underline");
2636     }
2637 }
2638
2639 /**
2640  * gtk_menu_item_get_use_underline:
2641  * @menu_item: a #GtkMenuItem
2642  *
2643  * Checks if an underline in the text indicates the next character
2644  * should be used for the mnemonic accelerator key.
2645  *
2646  * Return value: %TRUE if an embedded underline in the label
2647  *     indicates the mnemonic accelerator key.
2648  *
2649  * Since: 2.16
2650  */
2651 gboolean
2652 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2653 {
2654   GtkWidget *child;
2655
2656   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2657
2658   gtk_menu_item_ensure_label (menu_item);
2659
2660   child = gtk_bin_get_child (GTK_BIN (menu_item));
2661   if (GTK_IS_LABEL (child))
2662     return gtk_label_get_use_underline (GTK_LABEL (child));
2663
2664   return FALSE;
2665 }
2666
2667 /**
2668  * gtk_menu_item_set_reserve_indicator:
2669  * @menu_item: a #GtkMenuItem
2670  * @reserve: the new value
2671  *
2672  * Sets whether the @menu_item should reserve space for
2673  * the submenu indicator, regardless if it actually has
2674  * a submenu or not.
2675  *
2676  * There should be little need for applications to call
2677  * this functions.
2678  *
2679  * Since: 3.0
2680  */
2681 void
2682 gtk_menu_item_set_reserve_indicator (GtkMenuItem *menu_item,
2683                                      gboolean     reserve)
2684 {
2685   GtkMenuItemPrivate *priv;
2686
2687   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2688
2689   priv = menu_item->priv;
2690
2691   if (priv->reserve_indicator != reserve)
2692     {
2693       priv->reserve_indicator = reserve;
2694       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
2695     }
2696 }
2697
2698 /**
2699  * gtk_menu_item_get_reserve_indicator:
2700  * @menu_item: a #GtkMenuItem
2701  *
2702  * Returns whether the @menu_item reserves space for
2703  * the submenu indicator, regardless if it has a submenu
2704  * or not.
2705  *
2706  * Returns: %TRUE if @menu_item always reserves space for the
2707  *     submenu indicator
2708  *
2709  * Since: 3.0
2710  */
2711 gboolean
2712 gtk_menu_item_get_reserve_indicator (GtkMenuItem *menu_item)
2713 {
2714   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2715
2716   return menu_item->priv->reserve_indicator;
2717 }