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