]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
9c9dea3a3cf77f831b0a97b055c6bf77fa2c7e62
[~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_do_set_right_justified (GtkMenuItem *menu_item,
562                                       gboolean     right_justified)
563 {
564   GtkMenuItemPrivate *priv = menu_item->priv;
565
566   right_justified = right_justified != FALSE;
567
568   if (priv->right_justify != right_justified)
569     {
570       priv->right_justify = right_justified;
571       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
572     }
573 }
574
575 static void
576 gtk_menu_item_set_property (GObject      *object,
577                             guint         prop_id,
578                             const GValue *value,
579                             GParamSpec   *pspec)
580 {
581   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
582
583   switch (prop_id)
584     {
585     case PROP_RIGHT_JUSTIFIED:
586       gtk_menu_item_do_set_right_justified (menu_item, g_value_get_boolean (value));
587       break;
588     case PROP_SUBMENU:
589       gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
590       break;
591     case PROP_ACCEL_PATH:
592       gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
593       break;
594     case PROP_LABEL:
595       gtk_menu_item_set_label (menu_item, g_value_get_string (value));
596       break;
597     case PROP_USE_UNDERLINE:
598       gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
599       break;
600     case PROP_ACTIVATABLE_RELATED_ACTION:
601       gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
602       break;
603     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
604       gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
605       break;
606     default:
607       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
608       break;
609     }
610 }
611
612 static void
613 gtk_menu_item_get_property (GObject    *object,
614                             guint       prop_id,
615                             GValue     *value,
616                             GParamSpec *pspec)
617 {
618   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
619   GtkMenuItemPrivate *priv = menu_item->priv;
620
621   switch (prop_id)
622     {
623     case PROP_RIGHT_JUSTIFIED:
624       g_value_set_boolean (value, menu_item->priv->right_justify);
625       break;
626     case PROP_SUBMENU:
627       g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
628       break;
629     case PROP_ACCEL_PATH:
630       g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
631       break;
632     case PROP_LABEL:
633       g_value_set_string (value, gtk_menu_item_get_label (menu_item));
634       break;
635     case PROP_USE_UNDERLINE:
636       g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
637       break;
638     case PROP_ACTIVATABLE_RELATED_ACTION:
639       g_value_set_object (value, priv->action);
640       break;
641     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
642       g_value_set_boolean (value, priv->use_action_appearance);
643       break;
644     default:
645       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
646       break;
647     }
648 }
649
650 static void
651 gtk_menu_item_destroy (GtkWidget *widget)
652 {
653   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
654   GtkMenuItemPrivate *priv = menu_item->priv;
655
656   if (priv->submenu)
657     gtk_widget_destroy (priv->submenu);
658
659   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->destroy (widget);
660 }
661
662 static void
663 gtk_menu_item_detacher (GtkWidget *widget,
664                         GtkMenu   *menu)
665 {
666   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
667   GtkMenuItemPrivate *priv = menu_item->priv;
668
669   g_return_if_fail (priv->submenu == (GtkWidget*) menu);
670
671   priv->submenu = NULL;
672 }
673
674 static void
675 get_arrow_size (GtkWidget *widget,
676                 GtkWidget *child,
677                 gint      *size)
678 {
679   GtkStyleContext  *style_context;
680   GtkStateFlags     state;
681   PangoContext     *context;
682   PangoFontMetrics *metrics;
683   gfloat            arrow_scaling;
684
685   g_assert (size);
686
687   gtk_widget_style_get (widget,
688                         "arrow-scaling", &arrow_scaling,
689                         NULL);
690
691   context = gtk_widget_get_pango_context (child);
692   style_context = gtk_widget_get_style_context (child);
693   state = gtk_widget_get_state_flags (child);
694
695   metrics = pango_context_get_metrics (context,
696                                        gtk_style_context_get_font (style_context, state),
697                                        pango_context_get_language (context));
698
699   *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
700                          pango_font_metrics_get_descent (metrics)));
701
702   pango_font_metrics_unref (metrics);
703
704   *size = *size * arrow_scaling;
705 }
706
707
708 static void
709 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
710                                    gpointer   data)
711 {
712   guint *width = data;
713
714   if (GTK_IS_ACCEL_LABEL (widget))
715     {
716       guint w;
717
718       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
719       *width = MAX (*width, w);
720     }
721   else if (GTK_IS_CONTAINER (widget))
722     gtk_container_foreach (GTK_CONTAINER (widget),
723                            gtk_menu_item_accel_width_foreach,
724                            data);
725 }
726
727 static gint
728 get_minimum_width (GtkWidget *widget)
729 {
730   GtkStyleContext *style_context;
731   GtkStateFlags state;
732   PangoContext *context;
733   PangoFontMetrics *metrics;
734   gint width;
735   gint width_chars;
736
737   context = gtk_widget_get_pango_context (widget);
738   style_context = gtk_widget_get_style_context (widget);
739   state = gtk_widget_get_state_flags (widget);
740
741   metrics = pango_context_get_metrics (context,
742                                        gtk_style_context_get_font (style_context, state),
743                                        pango_context_get_language (context));
744
745   width = pango_font_metrics_get_approximate_char_width (metrics);
746
747   pango_font_metrics_unref (metrics);
748
749   gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
750
751   return PANGO_PIXELS (width_chars * width);
752 }
753
754 static void
755 gtk_menu_item_get_preferred_width (GtkWidget *widget,
756                                    gint      *minimum_size,
757                                    gint      *natural_size)
758 {
759   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
760   GtkMenuItemPrivate *priv = menu_item->priv;
761   GtkBin *bin;
762   GtkWidget *child;
763   GtkWidget *parent;
764   guint accel_width;
765   guint horizontal_padding;
766   guint border_width;
767   GtkPackDirection pack_dir;
768   GtkPackDirection child_pack_dir;
769   gint  min_width, nat_width;
770   GtkStyleContext *context;
771   GtkStateFlags state;
772   GtkBorder padding;
773
774   min_width = nat_width = 0;
775
776   gtk_widget_style_get (widget,
777                         "horizontal-padding", &horizontal_padding,
778                         NULL);
779
780   bin = GTK_BIN (widget);
781   parent = gtk_widget_get_parent (widget);
782
783   if (GTK_IS_MENU_BAR (parent))
784     {
785       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
786       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
787     }
788   else
789     {
790       pack_dir = GTK_PACK_DIRECTION_LTR;
791       child_pack_dir = GTK_PACK_DIRECTION_LTR;
792     }
793
794   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
795
796   context = gtk_widget_get_style_context (widget);
797   state = gtk_widget_get_state_flags (widget);
798   gtk_style_context_get_padding (context, state, &padding);
799
800   min_width = (border_width * 2) + padding.left + padding.right;
801
802   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
803       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
804     min_width += 2 * horizontal_padding;
805
806   nat_width = min_width;
807           
808   child = gtk_bin_get_child (bin);
809
810   if (child != NULL && gtk_widget_get_visible (child))
811     {
812       GtkMenuItemPrivate *priv = menu_item->priv;
813       gint child_min, child_nat;
814
815       gtk_widget_get_preferred_width (child, &child_min, &child_nat);
816
817       if ((menu_item->priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
818         {
819           guint arrow_spacing;
820           gint  arrow_size;
821           
822           gtk_widget_style_get (widget,
823                                 "arrow-spacing", &arrow_spacing,
824                                 NULL);
825
826           get_arrow_size (widget, child, &arrow_size);
827
828           gtk_widget_style_get (widget,
829                                 "arrow-spacing", &arrow_spacing,
830                                 NULL);
831
832           get_arrow_size (widget, child, &arrow_size);
833
834           min_width += arrow_size;
835           min_width += arrow_spacing;
836
837           min_width = MAX (min_width, get_minimum_width (widget));
838           nat_width = min_width;
839         }
840
841       min_width += child_min;
842       nat_width += child_nat;
843     }
844
845   accel_width = 0;
846   gtk_container_foreach (GTK_CONTAINER (menu_item),
847                          gtk_menu_item_accel_width_foreach,
848                          &accel_width);
849   priv->accelerator_width = accel_width;
850
851   if (minimum_size)
852     *minimum_size = min_width;
853
854   if (natural_size)
855     *natural_size = nat_width;
856 }
857
858 static void
859 gtk_menu_item_get_preferred_height (GtkWidget *widget,
860                                     gint      *minimum_size,
861                                     gint      *natural_size)
862 {
863   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
864   GtkMenuItemPrivate *priv = menu_item->priv;
865   GtkBin *bin;
866   GtkStyleContext *context;
867   GtkStateFlags state;
868   GtkBorder padding;
869   GtkWidget *child;
870   GtkWidget *parent;
871   guint accel_width;
872   guint horizontal_padding;
873   guint border_width;
874   GtkPackDirection pack_dir;
875   GtkPackDirection child_pack_dir;
876   gint  min_height, nat_height;
877
878   min_height = nat_height = 0;
879
880   context = gtk_widget_get_style_context (widget);
881   state = gtk_widget_get_state_flags (widget);
882   gtk_style_context_get_padding (context, state, &padding);
883
884   gtk_widget_style_get (widget,
885                         "horizontal-padding", &horizontal_padding,
886                         NULL);
887
888   bin = GTK_BIN (widget);
889   parent = gtk_widget_get_parent (widget);
890
891   if (GTK_IS_MENU_BAR (parent))
892     {
893       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
894       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
895     }
896   else
897     {
898       pack_dir = GTK_PACK_DIRECTION_LTR;
899       child_pack_dir = GTK_PACK_DIRECTION_LTR;
900     }
901
902   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
903   min_height = (border_width * 2) + padding.top + padding.bottom;
904
905   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
906       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
907     min_height += 2 * horizontal_padding;
908
909   nat_height = min_height;
910
911   child = gtk_bin_get_child (bin);
912
913   if (child != NULL && gtk_widget_get_visible (child))
914     {
915       GtkMenuItemPrivate *priv = menu_item->priv;
916       gint child_min, child_nat;
917
918       gtk_widget_get_preferred_height (child, &child_min, &child_nat);
919
920       min_height += child_min;
921       nat_height += child_nat;
922
923       if ((menu_item->priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
924         {
925           gint  arrow_size;
926
927           get_arrow_size (widget, child, &arrow_size);
928
929           min_height = MAX (min_height, arrow_size);
930           nat_height = MAX (nat_height, arrow_size);
931         }
932     }
933   else /* separator item */
934     {
935       gboolean wide_separators;
936       gint     separator_height;
937
938       gtk_widget_style_get (widget,
939                             "wide-separators",  &wide_separators,
940                             "separator-height", &separator_height,
941                             NULL);
942
943       if (wide_separators)
944         {
945           min_height += separator_height;
946           nat_height += separator_height;
947         }
948       else
949         {
950           /* force odd, so that we can have the same space above and
951            * below the line.
952            */
953           if (min_height % 2 == 0)
954             min_height += 1;
955           if (nat_height % 2 == 0)
956             nat_height += 1;
957         }
958     }
959
960   accel_width = 0;
961   gtk_container_foreach (GTK_CONTAINER (menu_item),
962                          gtk_menu_item_accel_width_foreach,
963                          &accel_width);
964   priv->accelerator_width = accel_width;
965
966   if (minimum_size)
967     *minimum_size = min_height;
968
969   if (natural_size)
970     *natural_size = nat_height;
971 }
972
973 static void
974 gtk_menu_item_get_preferred_height_for_width (GtkWidget *widget,
975                                               gint       for_size,
976                                               gint      *minimum_size,
977                                               gint      *natural_size)
978 {
979   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
980   GtkMenuItemPrivate *priv = menu_item->priv;
981   GtkBin *bin;
982   GtkStyleContext *context;
983   GtkStateFlags state;
984   GtkBorder padding;
985   GtkWidget *child;
986   GtkWidget *parent;
987   guint horizontal_padding;
988   guint border_width;
989   GtkPackDirection pack_dir;
990   GtkPackDirection child_pack_dir;
991   gint  min_height, nat_height;
992   gint  avail_size;
993
994   min_height = nat_height = 0;
995
996   context = gtk_widget_get_style_context (widget);
997   state = gtk_widget_get_state_flags (widget);
998   gtk_style_context_get_padding (context, state, &padding);
999
1000   gtk_widget_style_get (widget,
1001                         "horizontal-padding", &horizontal_padding,
1002                         NULL);
1003
1004   bin = GTK_BIN (widget);
1005   parent = gtk_widget_get_parent (widget);
1006
1007   if (GTK_IS_MENU_BAR (parent))
1008     {
1009       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
1010       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
1011     }
1012   else
1013     {
1014       pack_dir = GTK_PACK_DIRECTION_LTR;
1015       child_pack_dir = GTK_PACK_DIRECTION_LTR;
1016     }
1017
1018   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1019   min_height   = (border_width * 2) + padding.top + padding.bottom;
1020
1021   avail_size   = for_size;
1022   avail_size  -= (border_width * 2) + padding.left + padding.right;
1023
1024   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1025       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1026     min_height += 2 * horizontal_padding;
1027
1028   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1029       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1030     avail_size -= 2 * horizontal_padding;
1031
1032   nat_height = min_height;
1033
1034   child = gtk_bin_get_child (bin);
1035
1036   if (child != NULL && gtk_widget_get_visible (child))
1037     {
1038       gint child_min, child_nat;
1039       gint arrow_size = 0;
1040       
1041       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1042         {
1043           guint arrow_spacing;
1044
1045           gtk_widget_style_get (widget,
1046                                 "arrow-spacing", &arrow_spacing,
1047                                 NULL);
1048
1049           get_arrow_size (widget, child, &arrow_size);
1050
1051           avail_size -= arrow_size;
1052           avail_size -= arrow_spacing;
1053         }
1054
1055       gtk_widget_get_preferred_height_for_width (child,
1056                                                  avail_size,
1057                                                  &child_min,
1058                                                  &child_nat);
1059
1060       min_height += child_min;
1061       nat_height += child_nat;
1062
1063       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1064         {
1065           min_height = MAX (min_height, arrow_size);
1066           nat_height = MAX (nat_height, arrow_size);
1067         }
1068     }
1069   else /* separator item */
1070     {
1071       gboolean wide_separators;
1072       gint     separator_height;
1073
1074       gtk_widget_style_get (widget,
1075                             "wide-separators",  &wide_separators,
1076                             "separator-height", &separator_height,
1077                             NULL);
1078
1079       if (wide_separators)
1080         {
1081           min_height += separator_height;
1082           nat_height += separator_height;
1083         }
1084       else
1085         {
1086           /* force odd, so that we can have the same space above and
1087            * below the line.
1088            */
1089           if (min_height % 2 == 0)
1090             min_height += 1;
1091           if (nat_height % 2 == 0)
1092             nat_height += 1;
1093         }
1094     }
1095
1096   if (minimum_size)
1097     *minimum_size = min_height;
1098
1099   if (natural_size)
1100     *natural_size = nat_height;
1101 }
1102
1103 static void
1104 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
1105 {
1106   parent_buildable_iface = g_type_interface_peek_parent (iface);
1107   iface->add_child = gtk_menu_item_buildable_add_child;
1108   iface->custom_finished = gtk_menu_item_buildable_custom_finished;
1109 }
1110
1111 static void
1112 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
1113                                    GtkBuilder   *builder,
1114                                    GObject      *child,
1115                                    const gchar  *type)
1116 {
1117   if (type && strcmp (type, "submenu") == 0)
1118         gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
1119                                    GTK_WIDGET (child));
1120   else
1121     parent_buildable_iface->add_child (buildable, builder, child, type);
1122 }
1123
1124
1125 static void
1126 gtk_menu_item_buildable_custom_finished (GtkBuildable *buildable,
1127                                          GtkBuilder   *builder,
1128                                          GObject      *child,
1129                                          const gchar  *tagname,
1130                                          gpointer      user_data)
1131 {
1132   GtkWidget *toplevel;
1133
1134   if (strcmp (tagname, "accelerator") == 0)
1135     {
1136       GtkMenuShell *menu_shell;
1137       GtkWidget *attach;
1138
1139       menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (GTK_WIDGET (buildable)));
1140       if (menu_shell)
1141         {
1142           while (GTK_IS_MENU (menu_shell) &&
1143                  (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
1144             menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (attach));
1145
1146           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
1147         }
1148       else
1149         {
1150           /* Fall back to something ... */
1151           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
1152
1153           g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
1154                      gtk_buildable_get_name (buildable));
1155         }
1156
1157       /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
1158       _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
1159     }
1160   else
1161     parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
1162 }
1163
1164
1165 static void
1166 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
1167 {
1168   iface->update = gtk_menu_item_update;
1169   iface->sync_action_properties = gtk_menu_item_sync_action_properties;
1170 }
1171
1172 static void
1173 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
1174 {
1175   GtkWidget *child;
1176
1177   child = gtk_bin_get_child (GTK_BIN (menu_item));
1178
1179   if (GTK_IS_LABEL (child))
1180     {
1181       const gchar *label;
1182
1183       label = gtk_action_get_label (action);
1184       gtk_menu_item_set_label (menu_item, label);
1185     }
1186 }
1187
1188 gboolean _gtk_menu_is_empty (GtkWidget *menu);
1189
1190 static void
1191 gtk_menu_item_update (GtkActivatable *activatable,
1192                       GtkAction      *action,
1193                       const gchar    *property_name)
1194 {
1195   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1196   GtkMenuItemPrivate *priv = menu_item->priv;
1197
1198   if (strcmp (property_name, "visible") == 0)
1199     _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1200                                    _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1201   else if (strcmp (property_name, "sensitive") == 0)
1202     gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1203   else if (priv->use_action_appearance)
1204     {
1205       if (strcmp (property_name, "label") == 0)
1206         activatable_update_label (menu_item, action);
1207     }
1208 }
1209
1210 static void
1211 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
1212                                       GtkAction      *action)
1213 {
1214   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1215   GtkMenuItemPrivate *priv = menu_item->priv;
1216   GtkWidget *label;
1217
1218   if (!priv->use_action_appearance || !action)
1219     {
1220       label = gtk_bin_get_child (GTK_BIN (menu_item));
1221
1222       if (GTK_IS_ACCEL_LABEL (label))
1223         gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), GTK_WIDGET (menu_item));
1224     }
1225
1226   if (!action)
1227     return;
1228
1229   _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1230                                  _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1231
1232   gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1233
1234   if (priv->use_action_appearance)
1235     {
1236       label = gtk_bin_get_child (GTK_BIN (menu_item));
1237
1238       /* make sure label is a label, deleting it otherwise */
1239       if (label && !GTK_IS_LABEL (label))
1240         {
1241           gtk_container_remove (GTK_CONTAINER (menu_item), label);
1242           label = NULL;
1243         }
1244       /* Make sure that menu_item has a label and that any
1245        * accelerators are set */
1246       gtk_menu_item_ensure_label (menu_item);
1247       gtk_menu_item_set_use_underline (menu_item, TRUE);
1248       /* Make label point to the menu_item's label */
1249       label = gtk_bin_get_child (GTK_BIN (menu_item));
1250
1251       if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
1252         {
1253           gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), NULL);
1254           gtk_accel_label_set_accel_closure (GTK_ACCEL_LABEL (label),
1255                                              gtk_action_get_accel_closure (action));
1256         }
1257
1258       activatable_update_label (menu_item, action);
1259     }
1260 }
1261
1262 static void
1263 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
1264                                   GtkAction   *action)
1265 {
1266     GtkMenuItemPrivate *priv = menu_item->priv;
1267
1268     if (priv->action == action)
1269       return;
1270
1271     if (priv->action)
1272       {
1273         gtk_action_disconnect_accelerator (priv->action);
1274       }
1275
1276     if (action)
1277       {
1278         const gchar *accel_path;
1279
1280         accel_path = gtk_action_get_accel_path (action);
1281         if (accel_path)
1282           {
1283             gtk_action_connect_accelerator (action);
1284             gtk_menu_item_set_accel_path (menu_item, accel_path);
1285           }
1286       }
1287
1288     gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
1289
1290     priv->action = action;
1291 }
1292
1293 static void
1294 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
1295                                          gboolean     use_appearance)
1296 {
1297     GtkMenuItemPrivate *priv = menu_item->priv;
1298
1299     if (priv->use_action_appearance != use_appearance)
1300       {
1301         priv->use_action_appearance = use_appearance;
1302
1303         gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
1304       }
1305 }
1306
1307
1308 /**
1309  * gtk_menu_item_set_submenu:
1310  * @menu_item: a #GtkMenuItem
1311  * @submenu: (allow-none): the submenu, or %NULL
1312  *
1313  * Sets or replaces the menu item's submenu, or removes it when a %NULL
1314  * submenu is passed.
1315  */
1316 void
1317 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
1318                            GtkWidget   *submenu)
1319 {
1320   GtkMenuItemPrivate *priv = menu_item->priv;
1321
1322   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1323   g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
1324
1325   if (priv->submenu != submenu)
1326     {
1327       if (priv->submenu)
1328         gtk_menu_detach (GTK_MENU (priv->submenu));
1329
1330       if (submenu)
1331         {
1332           priv->submenu = submenu;
1333           gtk_menu_attach_to_widget (GTK_MENU (submenu),
1334                                      GTK_WIDGET (menu_item),
1335                                      gtk_menu_item_detacher);
1336         }
1337
1338       if (gtk_widget_get_parent (GTK_WIDGET (menu_item)))
1339         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1340
1341       g_object_notify (G_OBJECT (menu_item), "submenu");
1342     }
1343 }
1344
1345 /**
1346  * gtk_menu_item_get_submenu:
1347  * @menu_item: a #GtkMenuItem
1348  *
1349  * Gets the submenu underneath this menu item, if any.
1350  * See gtk_menu_item_set_submenu().
1351  *
1352  * Return value: (transfer none): submenu for this menu item, or %NULL if none
1353  */
1354 GtkWidget *
1355 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
1356 {
1357   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
1358
1359   return menu_item->priv->submenu;
1360 }
1361
1362 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1363                                    GtkSubmenuPlacement  placement);
1364
1365 void
1366 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1367                              GtkSubmenuPlacement  placement)
1368 {
1369   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1370
1371   menu_item->priv->submenu_placement = placement;
1372 }
1373
1374 void
1375 gtk_menu_item_select (GtkMenuItem *menu_item)
1376 {
1377   GtkWidget *parent;
1378
1379   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1380
1381   g_signal_emit (menu_item, menu_item_signals[SELECT], 0);
1382
1383   /* Enable themeing of the parent menu item depending on whether
1384    * something is selected in its submenu
1385    */
1386   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1387   if (GTK_IS_MENU (parent))
1388     {
1389       GtkMenu *menu = GTK_MENU (parent);
1390
1391       if (menu->priv->parent_menu_item)
1392         gtk_widget_queue_draw (GTK_WIDGET (menu->priv->parent_menu_item));
1393     }
1394 }
1395
1396 /**
1397  * gtk_menu_item_deselect:
1398  * @menu_item: the menu item
1399  *
1400  * Emits the #GtkMenuItem::deselect signal on the given item. Behaves
1401  * exactly like #gtk_item_deselect.
1402  */
1403 void
1404 gtk_menu_item_deselect (GtkMenuItem *menu_item)
1405 {
1406   GtkWidget *parent;
1407
1408   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1409
1410   g_signal_emit (menu_item, menu_item_signals[DESELECT], 0);
1411
1412   /* Enable themeing of the parent menu item depending on whether
1413    * something is selected in its submenu
1414    */
1415   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1416   if (GTK_IS_MENU (parent))
1417     {
1418       GtkMenu *menu = GTK_MENU (parent);
1419
1420       if (menu->priv->parent_menu_item)
1421         gtk_widget_queue_draw (GTK_WIDGET (menu->priv->parent_menu_item));
1422     }
1423 }
1424
1425 /**
1426  * gtk_menu_item_activate:
1427  * @menu_item: the menu item
1428  *
1429  * Emits the #GtkMenuItem::activate signal on the given item
1430  */
1431 void
1432 gtk_menu_item_activate (GtkMenuItem *menu_item)
1433 {
1434   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1435
1436   g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
1437 }
1438
1439 /**
1440  * gtk_menu_item_toggle_size_request:
1441  * @menu_item: the menu item
1442  * @requisition: the requisition to use as signal data.
1443  *
1444  * Emits the #GtkMenuItem::toggle-size-request signal on the given item.
1445  */
1446 void
1447 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1448                                    gint        *requisition)
1449 {
1450   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1451
1452   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
1453 }
1454
1455 /**
1456  * gtk_menu_item_toggle_size_allocate:
1457  * @menu_item: the menu item.
1458  * @allocation: the allocation to use as signal data.
1459  *
1460  * Emits the #GtkMenuItem::toggle-size-allocate signal on the given item.
1461  */
1462 void
1463 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1464                                     gint         allocation)
1465 {
1466   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1467
1468   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
1469 }
1470
1471 static void
1472 gtk_menu_item_size_allocate (GtkWidget     *widget,
1473                              GtkAllocation *allocation)
1474 {
1475   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1476   GtkMenuItemPrivate *priv = menu_item->priv;
1477   GtkBin *bin;
1478   GtkAllocation child_allocation;
1479   GtkTextDirection direction;
1480   GtkPackDirection pack_dir;
1481   GtkPackDirection child_pack_dir;
1482   GtkWidget *child;
1483   GtkWidget *parent;
1484
1485   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1486   g_return_if_fail (allocation != NULL);
1487
1488   bin = GTK_BIN (widget);
1489
1490   direction = gtk_widget_get_direction (widget);
1491
1492   parent = gtk_widget_get_parent (widget);
1493   if (GTK_IS_MENU_BAR (parent))
1494     {
1495       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
1496       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
1497     }
1498   else
1499     {
1500       pack_dir = GTK_PACK_DIRECTION_LTR;
1501       child_pack_dir = GTK_PACK_DIRECTION_LTR;
1502     }
1503
1504   gtk_widget_set_allocation (widget, allocation);
1505
1506   child = gtk_bin_get_child (bin);
1507   if (child)
1508     {
1509       GtkStyleContext *context;
1510       GtkStateFlags state;
1511       GtkBorder padding;
1512       guint horizontal_padding;
1513       guint border_width;
1514
1515       context = gtk_widget_get_style_context (widget);
1516       state = gtk_widget_get_state_flags (widget);
1517       gtk_style_context_get_padding (context, state, &padding);
1518
1519       gtk_widget_style_get (widget,
1520                             "horizontal-padding", &horizontal_padding,
1521                             NULL);
1522
1523       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1524       child_allocation.x = border_width + padding.left;
1525       child_allocation.y = border_width + padding.top;
1526
1527       child_allocation.width = allocation->width - (border_width * 2) -
1528         padding.left - padding.right;
1529       child_allocation.height = allocation->height - (border_width * 2) -
1530         padding.top - padding.bottom;
1531
1532       if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1533           (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1534         {
1535           child_allocation.x += horizontal_padding;
1536           child_allocation.width -= 2 * horizontal_padding;
1537         }
1538       else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1539                (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1540         {
1541           child_allocation.y += horizontal_padding;
1542           child_allocation.height -= 2 * horizontal_padding;
1543         }
1544
1545       if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1546           child_pack_dir == GTK_PACK_DIRECTION_RTL)
1547         {
1548           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1549             child_allocation.x += priv->toggle_size;
1550           child_allocation.width -= priv->toggle_size;
1551         }
1552       else
1553         {
1554           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1555             child_allocation.y += priv->toggle_size;
1556           child_allocation.height -= priv->toggle_size;
1557         }
1558
1559       child_allocation.x += allocation->x;
1560       child_allocation.y += allocation->y;
1561
1562       if ((priv->submenu && !GTK_IS_MENU_BAR (parent)) || priv->reserve_indicator)
1563         {
1564           guint arrow_spacing;
1565           gint  arrow_size;
1566
1567           gtk_widget_style_get (widget,
1568                                 "arrow-spacing", &arrow_spacing,
1569                                 NULL);
1570
1571           get_arrow_size (widget, child, &arrow_size);
1572
1573           if (direction == GTK_TEXT_DIR_RTL)
1574             child_allocation.x += arrow_size + arrow_spacing;
1575           child_allocation.width -= arrow_size + arrow_spacing;
1576         }
1577       
1578       if (child_allocation.width < 1)
1579         child_allocation.width = 1;
1580
1581       gtk_widget_size_allocate (child, &child_allocation);
1582     }
1583
1584   if (gtk_widget_get_realized (widget))
1585     gdk_window_move_resize (priv->event_window,
1586                             allocation->x, allocation->y,
1587                             allocation->width, allocation->height);
1588
1589   if (priv->submenu)
1590     gtk_menu_reposition (GTK_MENU (priv->submenu));
1591 }
1592
1593 static void
1594 gtk_menu_item_realize (GtkWidget *widget)
1595 {
1596   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1597   GtkMenuItemPrivate *priv = menu_item->priv;
1598   GtkAllocation allocation;
1599   GdkWindow *window;
1600   GdkWindowAttr attributes;
1601   gint attributes_mask;
1602
1603   gtk_widget_set_realized (widget, TRUE);
1604
1605   window = gtk_widget_get_parent_window (widget);
1606   gtk_widget_set_window (widget, window);
1607   g_object_ref (window);
1608
1609   gtk_widget_get_allocation (widget, &allocation);
1610
1611   attributes.x = allocation.x;
1612   attributes.y = allocation.y;
1613   attributes.width = allocation.width;
1614   attributes.height = allocation.height;
1615   attributes.window_type = GDK_WINDOW_CHILD;
1616   attributes.wclass = GDK_INPUT_ONLY;
1617   attributes.event_mask = (gtk_widget_get_events (widget) |
1618                            GDK_BUTTON_PRESS_MASK |
1619                            GDK_BUTTON_RELEASE_MASK |
1620                            GDK_ENTER_NOTIFY_MASK |
1621                            GDK_LEAVE_NOTIFY_MASK |
1622                            GDK_POINTER_MOTION_MASK);
1623
1624   attributes_mask = GDK_WA_X | GDK_WA_Y;
1625
1626   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1627                                        &attributes, attributes_mask);
1628   gdk_window_set_user_data (priv->event_window, widget);
1629 }
1630
1631 static void
1632 gtk_menu_item_unrealize (GtkWidget *widget)
1633 {
1634   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1635   GtkMenuItemPrivate *priv = menu_item->priv;
1636
1637   gdk_window_set_user_data (priv->event_window, NULL);
1638   gdk_window_destroy (priv->event_window);
1639   priv->event_window = NULL;
1640
1641   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1642 }
1643
1644 static void
1645 gtk_menu_item_map (GtkWidget *widget)
1646 {
1647   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1648   GtkMenuItemPrivate *priv = menu_item->priv;
1649
1650   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1651
1652   gdk_window_show (priv->event_window);
1653 }
1654
1655 static void
1656 gtk_menu_item_unmap (GtkWidget *widget)
1657 {
1658   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1659   GtkMenuItemPrivate *priv = menu_item->priv;
1660
1661   gdk_window_hide (priv->event_window);
1662
1663   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1664 }
1665
1666 static gboolean
1667 gtk_menu_item_enter (GtkWidget        *widget,
1668                      GdkEventCrossing *event)
1669 {
1670   g_return_val_if_fail (event != NULL, FALSE);
1671
1672   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event);
1673 }
1674
1675 static gboolean
1676 gtk_menu_item_leave (GtkWidget        *widget,
1677                      GdkEventCrossing *event)
1678 {
1679   g_return_val_if_fail (event != NULL, FALSE);
1680
1681   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent*) event);
1682 }
1683
1684 static gboolean
1685 gtk_menu_item_draw (GtkWidget *widget,
1686                     cairo_t   *cr)
1687 {
1688   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1689   GtkMenuItemPrivate *priv = menu_item->priv;
1690   GtkStateFlags state;
1691   GtkStyleContext *context;
1692   GtkBorder padding;
1693   GtkWidget *child, *parent;
1694   gint x, y, w, h, width, height;
1695   guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1696
1697   state = gtk_widget_get_state_flags (widget);
1698   context = gtk_widget_get_style_context (widget);
1699   width = gtk_widget_get_allocated_width (widget);
1700   height = gtk_widget_get_allocated_height (widget);
1701
1702   x = border_width;
1703   y = border_width;
1704   w = width - border_width * 2;
1705   h = height - border_width * 2;
1706
1707   child = gtk_bin_get_child (GTK_BIN (menu_item));
1708   parent = gtk_widget_get_parent (widget);
1709
1710   gtk_style_context_save (context);
1711   gtk_style_context_set_state (context, state);
1712
1713   gtk_style_context_get_padding (context, state, &padding);
1714
1715   if (GTK_IS_MENU_BAR (parent))
1716     gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR);
1717
1718   if (child && (state & GTK_STATE_FLAG_PRELIGHT))
1719     {
1720       gtk_render_background (context, cr, x, y, w, h);
1721       gtk_render_frame (context, cr, x, y, w, h);
1722     }
1723
1724   if (priv->submenu && !GTK_IS_MENU_BAR (parent))
1725     {
1726       gint arrow_x, arrow_y;
1727       gint arrow_size;
1728       guint horizontal_padding;
1729       GtkTextDirection direction;
1730       gdouble angle;
1731
1732       direction = gtk_widget_get_direction (widget);
1733
1734       gtk_widget_style_get (widget,
1735                             "horizontal-padding", &horizontal_padding,
1736                             NULL);
1737
1738       get_arrow_size (widget, child, &arrow_size);
1739
1740       if (direction == GTK_TEXT_DIR_LTR)
1741         {
1742           arrow_x = x + w - horizontal_padding - arrow_size;
1743           angle = G_PI / 2;
1744         }
1745       else
1746         {
1747           arrow_x = x + horizontal_padding;
1748           angle = (3 * G_PI) / 2;
1749         }
1750
1751       arrow_y = y + (h - arrow_size) / 2;
1752
1753       gtk_render_arrow (context, cr, angle, arrow_x, arrow_y, arrow_size);
1754     }
1755   else if (!child)
1756     {
1757       gboolean wide_separators;
1758       gint     separator_height;
1759       guint    horizontal_padding;
1760
1761       gtk_widget_style_get (widget,
1762                             "wide-separators",    &wide_separators,
1763                             "separator-height",   &separator_height,
1764                             "horizontal-padding", &horizontal_padding,
1765                             NULL);
1766       if (wide_separators)
1767         gtk_render_frame (context, cr,
1768                           x + horizontal_padding + padding.left,
1769                           y + padding.top,
1770                           w - (2 * horizontal_padding) - padding.left - padding.right,
1771                           separator_height);
1772       else
1773         gtk_render_line (context, cr,
1774                          x + horizontal_padding + padding.left,
1775                          y + padding.top,
1776                          x + w - horizontal_padding - padding.right - 1,
1777                          y + padding.top);
1778     }
1779
1780   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->draw (widget, cr);
1781
1782   gtk_style_context_restore (context);
1783
1784   return FALSE;
1785 }
1786
1787 /**
1788  * gtk_menu_item_select:
1789  * @menu_item: the menu item
1790  *
1791  * Emits the #GtkMenuItem::select signal on the given item. Behaves
1792  * exactly like #gtk_item_select.
1793  */
1794 static void
1795 gtk_real_menu_item_select (GtkMenuItem *menu_item)
1796 {
1797   GtkMenuItemPrivate *priv = menu_item->priv;
1798   gboolean touchscreen_mode;
1799
1800   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
1801                 "gtk-touchscreen-mode", &touchscreen_mode,
1802                 NULL);
1803
1804   if (!touchscreen_mode && priv->submenu &&
1805       (!gtk_widget_get_mapped (priv->submenu) ||
1806        GTK_MENU (priv->submenu)->priv->tearoff_active))
1807     {
1808       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1809     }
1810
1811   gtk_widget_set_state_flags (GTK_WIDGET (menu_item),
1812                               GTK_STATE_FLAG_PRELIGHT, FALSE);
1813   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1814 }
1815
1816 static void
1817 gtk_real_menu_item_deselect (GtkMenuItem *menu_item)
1818 {
1819   GtkMenuItemPrivate *priv = menu_item->priv;
1820
1821   if (priv->submenu)
1822     _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1823
1824   gtk_widget_unset_state_flags (GTK_WIDGET (menu_item),
1825                                 GTK_STATE_FLAG_PRELIGHT);
1826   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1827 }
1828
1829 static gboolean
1830 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1831                                  gboolean   group_cycling)
1832 {
1833   GtkWidget *parent;
1834
1835   parent = gtk_widget_get_parent (widget);
1836
1837   if (GTK_IS_MENU_SHELL (parent))
1838     _gtk_menu_shell_set_keyboard_mode (GTK_MENU_SHELL (parent), TRUE);
1839
1840   if (group_cycling &&
1841       parent &&
1842       GTK_IS_MENU_SHELL (parent) &&
1843       GTK_MENU_SHELL (parent)->priv->active)
1844     {
1845       gtk_menu_shell_select_item (GTK_MENU_SHELL (parent), widget);
1846     }
1847   else
1848     g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1849
1850   return TRUE;
1851 }
1852
1853 static void
1854 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1855 {
1856   GtkMenuItemPrivate *priv = menu_item->priv;
1857
1858   if (priv->action)
1859     gtk_action_activate (priv->action);
1860 }
1861
1862
1863 static void
1864 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1865 {
1866   GtkMenuItemPrivate *priv = menu_item->priv;
1867   GtkWidget *parent;
1868   GtkWidget *widget;
1869
1870   widget = GTK_WIDGET (menu_item);
1871   parent = gtk_widget_get_parent (widget);
1872
1873   if (parent && GTK_IS_MENU_SHELL (parent))
1874     {
1875       GtkMenuShell *menu_shell = GTK_MENU_SHELL (parent);
1876
1877       if (priv->submenu == NULL)
1878         gtk_menu_shell_activate_item (menu_shell, widget, TRUE);
1879       else
1880         {
1881           gtk_menu_shell_select_item (menu_shell, widget);
1882           _gtk_menu_item_popup_submenu (widget, FALSE);
1883
1884           gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->submenu), TRUE);
1885         }
1886     }
1887 }
1888
1889 static void
1890 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1891                                         gint        *requisition)
1892 {
1893   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1894
1895   *requisition = 0;
1896 }
1897
1898 static void
1899 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1900                                          gint         allocation)
1901 {
1902   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1903
1904   menu_item->priv->toggle_size = allocation;
1905 }
1906
1907 static void
1908 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1909                               const gchar *label)
1910 {
1911   GtkWidget *child;
1912
1913   gtk_menu_item_ensure_label (menu_item);
1914
1915   child = gtk_bin_get_child (GTK_BIN (menu_item));
1916   if (GTK_IS_LABEL (child))
1917     {
1918       gtk_label_set_label (GTK_LABEL (child), label ? label : "");
1919
1920       g_object_notify (G_OBJECT (menu_item), "label");
1921     }
1922 }
1923
1924 static const gchar *
1925 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1926 {
1927   GtkWidget *child;
1928
1929   gtk_menu_item_ensure_label (menu_item);
1930
1931   child = gtk_bin_get_child (GTK_BIN (menu_item));
1932   if (GTK_IS_LABEL (child))
1933     return gtk_label_get_label (GTK_LABEL (child));
1934
1935   return NULL;
1936 }
1937
1938 static void
1939 free_timeval (GTimeVal *val)
1940 {
1941   g_slice_free (GTimeVal, val);
1942 }
1943
1944 static void
1945 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1946                                   gboolean   remember_exact_time)
1947 {
1948   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1949   GtkMenuItemPrivate *priv = menu_item->priv;
1950   GtkWidget *parent;
1951
1952   parent = gtk_widget_get_parent (widget);
1953
1954   if (gtk_widget_is_sensitive (priv->submenu) && parent)
1955     {
1956       gboolean take_focus;
1957       GtkMenuPositionFunc menu_position_func;
1958
1959       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (parent));
1960       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->submenu), take_focus);
1961
1962       if (remember_exact_time)
1963         {
1964           GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1965
1966           g_get_current_time (popup_time);
1967
1968           g_object_set_data_full (G_OBJECT (priv->submenu),
1969                                   "gtk-menu-exact-popup-time", popup_time,
1970                                   (GDestroyNotify) free_timeval);
1971         }
1972       else
1973         {
1974           g_object_set_data (G_OBJECT (priv->submenu),
1975                              "gtk-menu-exact-popup-time", NULL);
1976         }
1977
1978       /* gtk_menu_item_position_menu positions the submenu from the
1979        * menuitems position. If the menuitem doesn't have a window,
1980        * that doesn't work. In that case we use the default
1981        * positioning function instead which places the submenu at the
1982        * mouse cursor.
1983        */
1984       if (gtk_widget_get_window (widget))
1985         menu_position_func = gtk_menu_item_position_menu;
1986       else
1987         menu_position_func = NULL;
1988
1989       gtk_menu_popup (GTK_MENU (priv->submenu),
1990                       parent,
1991                       widget,
1992                       menu_position_func,
1993                       menu_item,
1994                       GTK_MENU_SHELL (parent)->priv->button,
1995                       0);
1996     }
1997
1998   /* Enable themeing of the parent menu item depending on whether
1999    * its submenu is shown or not.
2000    */
2001   gtk_widget_queue_draw (widget);
2002 }
2003
2004 static gint
2005 gtk_menu_item_popup_timeout (gpointer data)
2006 {
2007   GtkMenuItem *menu_item = GTK_MENU_ITEM (data);
2008   GtkMenuItemPrivate *priv = menu_item->priv;
2009   GtkWidget *parent;
2010
2011   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
2012
2013   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->priv->active) ||
2014       (GTK_IS_MENU (parent) && GTK_MENU (parent)->priv->torn_off))
2015     {
2016       gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
2017       if (priv->timer_from_keypress && priv->submenu)
2018         GTK_MENU_SHELL (priv->submenu)->priv->ignore_enter = TRUE;
2019     }
2020
2021   priv->timer = 0;
2022
2023   return FALSE;
2024 }
2025
2026 static gint
2027 get_popup_delay (GtkWidget *widget)
2028 {
2029   GtkWidget *parent;
2030
2031   parent = gtk_widget_get_parent (widget);
2032   if (GTK_IS_MENU_SHELL (parent))
2033     {
2034       return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent));
2035     }
2036   else
2037     {
2038       gint popup_delay;
2039
2040       g_object_get (gtk_widget_get_settings (widget),
2041                     "gtk-menu-popup-delay", &popup_delay,
2042                     NULL);
2043
2044       return popup_delay;
2045     }
2046 }
2047
2048 void
2049 _gtk_menu_item_popup_submenu (GtkWidget *widget,
2050                               gboolean   with_delay)
2051 {
2052   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2053   GtkMenuItemPrivate *priv = menu_item->priv;
2054
2055   if (priv->timer)
2056     {
2057       g_source_remove (priv->timer);
2058       priv->timer = 0;
2059       with_delay = FALSE;
2060     }
2061
2062   if (with_delay)
2063     {
2064       gint popup_delay = get_popup_delay (widget);
2065
2066       if (popup_delay > 0)
2067         {
2068           GdkEvent *event = gtk_get_current_event ();
2069
2070           priv->timer = gdk_threads_add_timeout (popup_delay,
2071                                                  gtk_menu_item_popup_timeout,
2072                                                  menu_item);
2073
2074           if (event &&
2075               event->type != GDK_BUTTON_PRESS &&
2076               event->type != GDK_ENTER_NOTIFY)
2077             priv->timer_from_keypress = TRUE;
2078           else
2079             priv->timer_from_keypress = FALSE;
2080
2081           if (event)
2082             gdk_event_free (event);
2083
2084           return;
2085         }
2086     }
2087
2088   gtk_menu_item_real_popup_submenu (widget, FALSE);
2089 }
2090
2091 void
2092 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
2093 {
2094   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2095   GtkMenuItemPrivate *priv = menu_item->priv;
2096
2097   if (priv->submenu)
2098     {
2099       g_object_set_data (G_OBJECT (priv->submenu),
2100                          "gtk-menu-exact-popup-time", NULL);
2101
2102       if (priv->timer)
2103         {
2104           g_source_remove (priv->timer);
2105           priv->timer = 0;
2106         }
2107       else
2108         gtk_menu_popdown (GTK_MENU (priv->submenu));
2109
2110       gtk_widget_queue_draw (widget);
2111     }
2112 }
2113
2114 static void
2115 get_offsets (GtkMenu *menu,
2116              gint    *horizontal_offset,
2117              gint    *vertical_offset)
2118 {
2119   gint vertical_padding;
2120   gint horizontal_padding;
2121   GtkStyleContext *context;
2122   GtkStateFlags state;
2123   GtkBorder padding;
2124
2125   gtk_widget_style_get (GTK_WIDGET (menu),
2126                         "horizontal-offset", horizontal_offset,
2127                         "vertical-offset", vertical_offset,
2128                         "horizontal-padding", &horizontal_padding,
2129                         "vertical-padding", &vertical_padding,
2130                         NULL);
2131
2132   context = gtk_widget_get_style_context (GTK_WIDGET (menu));
2133   state = gtk_widget_get_state_flags (GTK_WIDGET (menu));
2134   gtk_style_context_get_padding (context, state, &padding);
2135
2136   *vertical_offset -= padding.top;
2137   *vertical_offset -= vertical_padding;
2138   *horizontal_offset += horizontal_padding;
2139 }
2140
2141 static void
2142 gtk_menu_item_position_menu (GtkMenu  *menu,
2143                              gint     *x,
2144                              gint     *y,
2145                              gboolean *push_in,
2146                              gpointer  user_data)
2147 {
2148   GtkMenuItem *menu_item = GTK_MENU_ITEM (user_data);
2149   GtkMenuItemPrivate *priv = menu_item->priv;
2150   GtkAllocation allocation;
2151   GtkWidget *widget;
2152   GtkMenuItem *parent_menu_item;
2153   GtkWidget *parent;
2154   GdkScreen *screen;
2155   gint twidth, theight;
2156   gint tx, ty;
2157   GtkTextDirection direction;
2158   GdkRectangle monitor;
2159   gint monitor_num;
2160   gint horizontal_offset;
2161   gint vertical_offset;
2162   gint available_left, available_right;
2163   GtkStyleContext *context;
2164   GtkStateFlags state;
2165   GtkBorder parent_padding;
2166
2167   g_return_if_fail (menu != NULL);
2168   g_return_if_fail (x != NULL);
2169   g_return_if_fail (y != NULL);
2170
2171   widget = GTK_WIDGET (user_data);
2172
2173   if (push_in)
2174     *push_in = FALSE;
2175
2176   direction = gtk_widget_get_direction (widget);
2177
2178   twidth = gtk_widget_get_allocated_width (GTK_WIDGET (menu));
2179   theight = gtk_widget_get_allocated_height (GTK_WIDGET (menu));
2180
2181   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
2182   monitor_num = gdk_screen_get_monitor_at_window (screen, priv->event_window);
2183   if (monitor_num < 0)
2184     monitor_num = 0;
2185   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2186
2187   if (!gdk_window_get_origin (gtk_widget_get_window (widget), &tx, &ty))
2188     {
2189       g_warning ("Menu not on screen");
2190       return;
2191     }
2192
2193   gtk_widget_get_allocation (widget, &allocation);
2194
2195   tx += allocation.x;
2196   ty += allocation.y;
2197
2198   get_offsets (menu, &horizontal_offset, &vertical_offset);
2199
2200   available_left = tx - monitor.x;
2201   available_right = monitor.x + monitor.width - (tx + allocation.width);
2202
2203   parent = gtk_widget_get_parent (widget);
2204   priv->from_menubar = GTK_IS_MENU_BAR (parent);
2205
2206   switch (priv->submenu_placement)
2207     {
2208     case GTK_TOP_BOTTOM:
2209       if (direction == GTK_TEXT_DIR_LTR)
2210         priv->submenu_direction = GTK_DIRECTION_RIGHT;
2211       else
2212         {
2213           priv->submenu_direction = GTK_DIRECTION_LEFT;
2214           tx += allocation.width - twidth;
2215         }
2216       if ((ty + allocation.height + theight) <= monitor.y + monitor.height)
2217         ty += allocation.height;
2218       else if ((ty - theight) >= monitor.y)
2219         ty -= theight;
2220       else if (monitor.y + monitor.height - (ty + allocation.height) > ty)
2221         ty += allocation.height;
2222       else
2223         ty -= theight;
2224       break;
2225
2226     case GTK_LEFT_RIGHT:
2227       if (GTK_IS_MENU (parent))
2228         parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->priv->parent_menu_item);
2229       else
2230         parent_menu_item = NULL;
2231
2232       context = gtk_widget_get_style_context (parent);
2233       state = gtk_widget_get_state_flags (parent);
2234       gtk_style_context_get_padding (context, state, &parent_padding);
2235
2236       if (parent_menu_item && !GTK_MENU (parent)->priv->torn_off)
2237         {
2238           priv->submenu_direction = parent_menu_item->priv->submenu_direction;
2239         }
2240       else
2241         {
2242           if (direction == GTK_TEXT_DIR_LTR)
2243             priv->submenu_direction = GTK_DIRECTION_RIGHT;
2244           else
2245             priv->submenu_direction = GTK_DIRECTION_LEFT;
2246         }
2247
2248       switch (priv->submenu_direction)
2249         {
2250         case GTK_DIRECTION_LEFT:
2251           if (tx - twidth - parent_padding.left - horizontal_offset >= monitor.x ||
2252               available_left >= available_right)
2253             tx -= twidth + parent_padding.left + horizontal_offset;
2254           else
2255             {
2256               priv->submenu_direction = GTK_DIRECTION_RIGHT;
2257               tx += allocation.width + parent_padding.right + horizontal_offset;
2258             }
2259           break;
2260
2261         case GTK_DIRECTION_RIGHT:
2262           if (tx + allocation.width + parent_padding.right + horizontal_offset + twidth <= monitor.x + monitor.width ||
2263               available_right >= available_left)
2264             tx += allocation.width + parent_padding.right + horizontal_offset;
2265           else
2266             {
2267               priv->submenu_direction = GTK_DIRECTION_LEFT;
2268               tx -= twidth + parent_padding.left + horizontal_offset;
2269             }
2270           break;
2271         }
2272
2273       ty += vertical_offset;
2274
2275       /* If the height of the menu doesn't fit we move it upward. */
2276       ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
2277       break;
2278     }
2279
2280   /* If we have negative, tx, here it is because we can't get
2281    * the menu all the way on screen. Favor the left portion.
2282    */
2283   *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
2284   *y = ty;
2285
2286   gtk_menu_set_monitor (menu, monitor_num);
2287
2288   if (!gtk_widget_get_visible (menu->priv->toplevel))
2289     {
2290       gtk_window_set_type_hint (GTK_WINDOW (menu->priv->toplevel), priv->from_menubar?
2291                                 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
2292     }
2293 }
2294
2295 /**
2296  * gtk_menu_item_set_right_justified:
2297  * @menu_item: a #GtkMenuItem.
2298  * @right_justified: if %TRUE the menu item will appear at the
2299  *   far right if added to a menu bar
2300  *
2301  * Sets whether the menu item appears justified at the right
2302  * side of a menu bar. This was traditionally done for "Help"
2303  * menu items, but is now considered a bad idea. (If the widget
2304  * layout is reversed for a right-to-left language like Hebrew
2305  * or Arabic, right-justified-menu-items appear at the left.)
2306  *
2307  * Deprecated: 3.2: If you insist on using it, use
2308  *   gtk_widget_set_hexpand() and gtk_widget_set_halign().
2309  **/
2310 void
2311 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
2312                                    gboolean     right_justified)
2313 {
2314   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2315
2316   gtk_menu_item_do_set_right_justified (menu_item, right_justified);
2317 }
2318
2319 /**
2320  * gtk_menu_item_get_right_justified:
2321  * @menu_item: a #GtkMenuItem
2322  *
2323  * Gets whether the menu item appears justified at the right
2324  * side of the menu bar.
2325  *
2326  * Return value: %TRUE if the menu item will appear at the
2327  *   far right if added to a menu bar.
2328  *
2329  * Deprecated: 3.2: See gtk_menu_item_set_right_justified()
2330  **/
2331 gboolean
2332 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
2333 {
2334   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2335
2336   return menu_item->priv->right_justify;
2337 }
2338
2339
2340 static void
2341 gtk_menu_item_show_all (GtkWidget *widget)
2342 {
2343   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2344   GtkMenuItemPrivate *priv = menu_item->priv;
2345
2346   /* show children including submenu */
2347   if (priv->submenu)
2348     gtk_widget_show_all (priv->submenu);
2349   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2350
2351   gtk_widget_show (widget);
2352 }
2353
2354 static gboolean
2355 gtk_menu_item_can_activate_accel (GtkWidget *widget,
2356                                   guint      signal_id)
2357 {
2358   GtkWidget *parent;
2359
2360   parent = gtk_widget_get_parent (widget);
2361
2362   /* Chain to the parent GtkMenu for further checks */
2363   return (gtk_widget_is_sensitive (widget) && gtk_widget_get_visible (widget) &&
2364           parent && gtk_widget_can_activate_accel (parent, signal_id));
2365 }
2366
2367 static void
2368 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
2369                                   gpointer   data)
2370 {
2371   const gchar **path_p = data;
2372
2373   if (!*path_p)
2374     {
2375       if (GTK_IS_LABEL (widget))
2376         {
2377           *path_p = gtk_label_get_text (GTK_LABEL (widget));
2378           if (*path_p && (*path_p)[0] == 0)
2379             *path_p = NULL;
2380         }
2381       else if (GTK_IS_CONTAINER (widget))
2382         gtk_container_foreach (GTK_CONTAINER (widget),
2383                                gtk_menu_item_accel_name_foreach,
2384                                data);
2385     }
2386 }
2387
2388 static void
2389 gtk_menu_item_parent_set (GtkWidget *widget,
2390                           GtkWidget *previous_parent)
2391 {
2392   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2393   GtkMenu *menu;
2394   GtkWidget *parent;
2395
2396   parent = gtk_widget_get_parent (widget);
2397   menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
2398
2399   if (menu)
2400     _gtk_menu_item_refresh_accel_path (menu_item,
2401                                        menu->priv->accel_path,
2402                                        menu->priv->accel_group,
2403                                        TRUE);
2404
2405   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
2406     GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
2407 }
2408
2409 void
2410 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
2411                                    const gchar   *prefix,
2412                                    GtkAccelGroup *accel_group,
2413                                    gboolean       group_changed)
2414 {
2415   GtkMenuItemPrivate *priv = menu_item->priv;
2416   const gchar *path;
2417   GtkWidget *widget;
2418
2419   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2420   g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
2421
2422   widget = GTK_WIDGET (menu_item);
2423
2424   if (!accel_group)
2425     {
2426       gtk_widget_set_accel_path (widget, NULL, NULL);
2427       return;
2428     }
2429
2430   path = _gtk_widget_get_accel_path (widget, NULL);
2431   if (!path)  /* no active accel_path yet */
2432     {
2433       path = priv->accel_path;
2434       if (!path && prefix)
2435         {
2436           const gchar *postfix = NULL;
2437           gchar *new_path;
2438
2439           /* try to construct one from label text */
2440           gtk_container_foreach (GTK_CONTAINER (menu_item),
2441                                  gtk_menu_item_accel_name_foreach,
2442                                  &postfix);
2443           if (postfix)
2444             {
2445               new_path = g_strconcat (prefix, "/", postfix, NULL);
2446               path = priv->accel_path = (char*)g_intern_string (new_path);
2447               g_free (new_path);
2448             }
2449         }
2450       if (path)
2451         gtk_widget_set_accel_path (widget, path, accel_group);
2452     }
2453   else if (group_changed)    /* reinstall accelerators */
2454     gtk_widget_set_accel_path (widget, path, accel_group);
2455 }
2456
2457 /**
2458  * gtk_menu_item_set_accel_path
2459  * @menu_item:  a valid #GtkMenuItem
2460  * @accel_path: (allow-none): accelerator path, corresponding to this menu
2461  *     item's functionality, or %NULL to unset the current path.
2462  *
2463  * Set the accelerator path on @menu_item, through which runtime
2464  * changes of the menu item's accelerator caused by the user can be
2465  * identified and saved to persistent storage (see gtk_accel_map_save()
2466  * on this). To set up a default accelerator for this menu item, call
2467  * gtk_accel_map_add_entry() with the same @accel_path. See also
2468  * gtk_accel_map_add_entry() on the specifics of accelerator paths,
2469  * and gtk_menu_set_accel_path() for a more convenient variant of
2470  * this function.
2471  *
2472  * This function is basically a convenience wrapper that handles
2473  * calling gtk_widget_set_accel_path() with the appropriate accelerator
2474  * group for the menu item.
2475  *
2476  * Note that you do need to set an accelerator on the parent menu with
2477  * gtk_menu_set_accel_group() for this to work.
2478  *
2479  * Note that @accel_path string will be stored in a #GQuark.
2480  * Therefore, if you pass a static string, you can save some memory
2481  * by interning it first with g_intern_static_string().
2482  */
2483 void
2484 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2485                               const gchar *accel_path)
2486 {
2487   GtkMenuItemPrivate *priv = menu_item->priv;
2488   GtkWidget *parent;
2489   GtkWidget *widget;
2490
2491   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2492   g_return_if_fail (accel_path == NULL ||
2493                     (accel_path[0] == '<' && strchr (accel_path, '/')));
2494
2495   widget = GTK_WIDGET (menu_item);
2496
2497   /* store new path */
2498   priv->accel_path = (char*)g_intern_string (accel_path);
2499
2500   /* forget accelerators associated with old path */
2501   gtk_widget_set_accel_path (widget, NULL, NULL);
2502
2503   /* install accelerators associated with new path */
2504   parent = gtk_widget_get_parent (widget);
2505   if (GTK_IS_MENU (parent))
2506     {
2507       GtkMenu *menu = GTK_MENU (parent);
2508
2509       if (menu->priv->accel_group)
2510         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2511                                            NULL,
2512                                            menu->priv->accel_group,
2513                                            FALSE);
2514     }
2515 }
2516
2517 /**
2518  * gtk_menu_item_get_accel_path
2519  * @menu_item:  a valid #GtkMenuItem
2520  *
2521  * Retrieve the accelerator path that was previously set on @menu_item.
2522  *
2523  * See gtk_menu_item_set_accel_path() for details.
2524  *
2525  * Returns: the accelerator path corresponding to this menu
2526  *     item's functionality, or %NULL if not set
2527  *
2528  * Since: 2.14
2529  */
2530 const gchar *
2531 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2532 {
2533   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2534
2535   return menu_item->priv->accel_path;
2536 }
2537
2538 static void
2539 gtk_menu_item_forall (GtkContainer *container,
2540                       gboolean      include_internals,
2541                       GtkCallback   callback,
2542                       gpointer      callback_data)
2543 {
2544   GtkWidget *child;
2545
2546   g_return_if_fail (GTK_IS_MENU_ITEM (container));
2547   g_return_if_fail (callback != NULL);
2548
2549   child = gtk_bin_get_child (GTK_BIN (container));
2550   if (child)
2551     callback (child, callback_data);
2552 }
2553
2554 gboolean
2555 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2556 {
2557   if ((!gtk_bin_get_child (GTK_BIN (menu_item)) &&
2558        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2559       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2560       !gtk_widget_is_sensitive (menu_item) ||
2561       !gtk_widget_get_visible (menu_item))
2562     return FALSE;
2563
2564   return TRUE;
2565 }
2566
2567 static void
2568 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2569 {
2570   GtkWidget *accel_label;
2571
2572   if (!gtk_bin_get_child (GTK_BIN (menu_item)))
2573     {
2574       accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2575       gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2576
2577       gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2578       gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label),
2579                                         GTK_WIDGET (menu_item));
2580       gtk_widget_show (accel_label);
2581     }
2582 }
2583
2584 /**
2585  * gtk_menu_item_set_label:
2586  * @menu_item: a #GtkMenuItem
2587  * @label: the text you want to set
2588  *
2589  * Sets @text on the @menu_item label
2590  *
2591  * Since: 2.16
2592  */
2593 void
2594 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2595                          const gchar *label)
2596 {
2597   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2598
2599   GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2600 }
2601
2602 /**
2603  * gtk_menu_item_get_label:
2604  * @menu_item: a #GtkMenuItem
2605  *
2606  * Sets @text on the @menu_item label
2607  *
2608  * Returns: The text in the @menu_item label. This is the internal
2609  *   string used by the label, and must not be modified.
2610  *
2611  * Since: 2.16
2612  */
2613 const gchar *
2614 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2615 {
2616   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2617
2618   return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2619 }
2620
2621 /**
2622  * gtk_menu_item_set_use_underline:
2623  * @menu_item: a #GtkMenuItem
2624  * @setting: %TRUE if underlines in the text indicate mnemonics
2625  *
2626  * If true, an underline in the text indicates the next character
2627  * should be used for the mnemonic accelerator key.
2628  *
2629  * Since: 2.16
2630  */
2631 void
2632 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2633                                  gboolean     setting)
2634 {
2635   GtkWidget *child;
2636
2637   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2638
2639   gtk_menu_item_ensure_label (menu_item);
2640
2641   child = gtk_bin_get_child (GTK_BIN (menu_item));
2642   if (GTK_IS_LABEL (child))
2643     {
2644       gtk_label_set_use_underline (GTK_LABEL (child), setting);
2645
2646       g_object_notify (G_OBJECT (menu_item), "use-underline");
2647     }
2648 }
2649
2650 /**
2651  * gtk_menu_item_get_use_underline:
2652  * @menu_item: a #GtkMenuItem
2653  *
2654  * Checks if an underline in the text indicates the next character
2655  * should be used for the mnemonic accelerator key.
2656  *
2657  * Return value: %TRUE if an embedded underline in the label
2658  *     indicates the mnemonic accelerator key.
2659  *
2660  * Since: 2.16
2661  */
2662 gboolean
2663 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2664 {
2665   GtkWidget *child;
2666
2667   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2668
2669   gtk_menu_item_ensure_label (menu_item);
2670
2671   child = gtk_bin_get_child (GTK_BIN (menu_item));
2672   if (GTK_IS_LABEL (child))
2673     return gtk_label_get_use_underline (GTK_LABEL (child));
2674
2675   return FALSE;
2676 }
2677
2678 /**
2679  * gtk_menu_item_set_reserve_indicator:
2680  * @menu_item: a #GtkMenuItem
2681  * @reserve: the new value
2682  *
2683  * Sets whether the @menu_item should reserve space for
2684  * the submenu indicator, regardless if it actually has
2685  * a submenu or not.
2686  *
2687  * There should be little need for applications to call
2688  * this functions.
2689  *
2690  * Since: 3.0
2691  */
2692 void
2693 gtk_menu_item_set_reserve_indicator (GtkMenuItem *menu_item,
2694                                      gboolean     reserve)
2695 {
2696   GtkMenuItemPrivate *priv;
2697
2698   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2699
2700   priv = menu_item->priv;
2701
2702   if (priv->reserve_indicator != reserve)
2703     {
2704       priv->reserve_indicator = reserve;
2705       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
2706     }
2707 }
2708
2709 /**
2710  * gtk_menu_item_get_reserve_indicator:
2711  * @menu_item: a #GtkMenuItem
2712  *
2713  * Returns whether the @menu_item reserves space for
2714  * the submenu indicator, regardless if it has a submenu
2715  * or not.
2716  *
2717  * Returns: %TRUE if @menu_item always reserves space for the
2718  *     submenu indicator
2719  *
2720  * Since: 3.0
2721  */
2722 gboolean
2723 gtk_menu_item_get_reserve_indicator (GtkMenuItem *menu_item)
2724 {
2725   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2726
2727   return menu_item->priv->reserve_indicator;
2728 }