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