]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Don't forget to set the client window on the slave
[~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 #define GTK_MENU_INTERNALS
28
29 #include "config.h"
30 #include <string.h>
31
32 #include "gtkaccellabel.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkmenu.h"
36 #include "gtkmenubar.h"
37 #include "gtkseparatormenuitem.h"
38 #include "gtkprivate.h"
39 #include "gtkbuildable.h"
40 #include "gtkactivatable.h"
41 #include "gtkintl.h"
42 #include "gtkalias.h"
43
44
45 typedef struct {
46   GtkAction *action;
47   gboolean   use_action_appearance;
48 } GtkMenuItemPrivate;
49
50 enum {
51   ACTIVATE,
52   ACTIVATE_ITEM,
53   TOGGLE_SIZE_REQUEST,
54   TOGGLE_SIZE_ALLOCATE,
55   LAST_SIGNAL
56 };
57
58 enum {
59   PROP_0,
60   PROP_RIGHT_JUSTIFIED,
61   PROP_SUBMENU,
62   PROP_ACCEL_PATH,
63   PROP_LABEL,
64   PROP_USE_UNDERLINE,
65
66   /* activatable properties */
67   PROP_ACTIVATABLE_RELATED_ACTION,
68   PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
69 };
70
71
72 static void gtk_menu_item_dispose        (GObject          *object);
73 static void gtk_menu_item_set_property   (GObject          *object,
74                                           guint             prop_id,
75                                           const GValue     *value,
76                                           GParamSpec       *pspec);
77 static void gtk_menu_item_get_property   (GObject          *object,
78                                           guint             prop_id,
79                                           GValue           *value,
80                                           GParamSpec       *pspec);
81 static void gtk_menu_item_destroy        (GtkObject        *object);
82 static void gtk_menu_item_size_request   (GtkWidget        *widget,
83                                           GtkRequisition   *requisition);
84 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
85                                           GtkAllocation    *allocation);
86 static void gtk_menu_item_realize        (GtkWidget        *widget);
87 static void gtk_menu_item_unrealize      (GtkWidget        *widget);
88 static void gtk_menu_item_map            (GtkWidget        *widget);
89 static void gtk_menu_item_unmap          (GtkWidget        *widget);
90 static void gtk_menu_item_paint          (GtkWidget        *widget,
91                                           GdkRectangle     *area);
92 static gint gtk_menu_item_expose         (GtkWidget        *widget,
93                                           GdkEventExpose   *event);
94 static void gtk_menu_item_parent_set     (GtkWidget        *widget,
95                                           GtkWidget        *previous_parent);
96
97
98 static void gtk_real_menu_item_select               (GtkItem     *item);
99 static void gtk_real_menu_item_deselect             (GtkItem     *item);
100 static void gtk_real_menu_item_activate             (GtkMenuItem *item);
101 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
102 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
103                                                      gint        *requisition);
104 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
105                                                      gint         allocation);
106 static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
107                                                      gboolean     group_cycling);
108
109 static void gtk_menu_item_ensure_label   (GtkMenuItem      *menu_item);
110 static gint gtk_menu_item_popup_timeout  (gpointer          data);
111 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
112                                           gint             *x,
113                                           gint             *y,
114                                           gboolean         *push_in,
115                                           gpointer          user_data);
116 static void gtk_menu_item_show_all       (GtkWidget        *widget);
117 static void gtk_menu_item_hide_all       (GtkWidget        *widget);
118 static void gtk_menu_item_forall         (GtkContainer    *container,
119                                           gboolean         include_internals,
120                                           GtkCallback      callback,
121                                           gpointer         callback_data);
122 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
123                                                   guint      signal_id);
124
125 static void gtk_real_menu_item_set_label (GtkMenuItem     *menu_item,
126                                           const gchar     *label);
127 static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
128
129
130 static void gtk_menu_item_buildable_interface_init (GtkBuildableIface   *iface);
131 static void gtk_menu_item_buildable_add_child      (GtkBuildable        *buildable,
132                                                     GtkBuilder          *builder,
133                                                     GObject             *child,
134                                                     const gchar         *type);
135 static void gtk_menu_item_buildable_custom_finished(GtkBuildable        *buildable,
136                                                     GtkBuilder          *builder,
137                                                     GObject             *child,
138                                                     const gchar         *tagname,
139                                                     gpointer             user_data);
140
141 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
142 static void gtk_menu_item_update                     (GtkActivatable       *activatable,
143                                                       GtkAction            *action,
144                                                       const gchar          *property_name);
145 static void gtk_menu_item_sync_action_properties     (GtkActivatable       *activatable,
146                                                       GtkAction            *action);
147 static void gtk_menu_item_set_related_action         (GtkMenuItem          *menu_item, 
148                                                       GtkAction            *action);
149 static void gtk_menu_item_set_use_action_appearance  (GtkMenuItem          *menu_item, 
150                                                       gboolean              use_appearance);
151
152
153 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
154
155 static GtkBuildableIface *parent_buildable_iface;
156
157 G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM,
158                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
159                                                 gtk_menu_item_buildable_interface_init)
160                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
161                                                 gtk_menu_item_activatable_interface_init))
162
163 #define GET_PRIVATE(object)  \
164   (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate))
165
166 static void
167 gtk_menu_item_class_init (GtkMenuItemClass *klass)
168 {
169   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
170   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
171   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
172   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
173   GtkItemClass *item_class = GTK_ITEM_CLASS (klass);
174
175   gobject_class->dispose      = gtk_menu_item_dispose;
176   gobject_class->set_property = gtk_menu_item_set_property;
177   gobject_class->get_property = gtk_menu_item_get_property;
178
179   object_class->destroy = gtk_menu_item_destroy;
180
181   widget_class->size_request = gtk_menu_item_size_request;
182   widget_class->size_allocate = gtk_menu_item_size_allocate;
183   widget_class->expose_event = gtk_menu_item_expose;
184   widget_class->realize = gtk_menu_item_realize;
185   widget_class->unrealize = gtk_menu_item_unrealize;
186   widget_class->map = gtk_menu_item_map;
187   widget_class->unmap = gtk_menu_item_unmap;
188   widget_class->show_all = gtk_menu_item_show_all;
189   widget_class->hide_all = gtk_menu_item_hide_all;
190   widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
191   widget_class->parent_set = gtk_menu_item_parent_set;
192   widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
193   
194   container_class->forall = gtk_menu_item_forall;
195
196   item_class->select      = gtk_real_menu_item_select;
197   item_class->deselect    = gtk_real_menu_item_deselect;
198
199   klass->activate             = gtk_real_menu_item_activate;
200   klass->activate_item        = gtk_real_menu_item_activate_item;
201   klass->toggle_size_request  = gtk_real_menu_item_toggle_size_request;
202   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
203   klass->set_label            = gtk_real_menu_item_set_label;
204   klass->get_label            = gtk_real_menu_item_get_label;
205
206   klass->hide_on_activate = TRUE;
207
208   menu_item_signals[ACTIVATE] =
209     g_signal_new (I_("activate"),
210                   G_OBJECT_CLASS_TYPE (gobject_class),
211                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
212                   G_STRUCT_OFFSET (GtkMenuItemClass, activate),
213                   NULL, NULL,
214                   _gtk_marshal_VOID__VOID,
215                   G_TYPE_NONE, 0);
216   widget_class->activate_signal = menu_item_signals[ACTIVATE];
217
218   menu_item_signals[ACTIVATE_ITEM] =
219     g_signal_new (I_("activate-item"),
220                   G_OBJECT_CLASS_TYPE (gobject_class),
221                   G_SIGNAL_RUN_FIRST,
222                   G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
223                   NULL, NULL,
224                   _gtk_marshal_VOID__VOID,
225                   G_TYPE_NONE, 0);
226
227   menu_item_signals[TOGGLE_SIZE_REQUEST] =
228     g_signal_new (I_("toggle-size-request"),
229                   G_OBJECT_CLASS_TYPE (gobject_class),
230                   G_SIGNAL_RUN_FIRST,
231                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
232                   NULL, NULL,
233                   _gtk_marshal_VOID__POINTER,
234                   G_TYPE_NONE, 1,
235                   G_TYPE_POINTER);
236
237   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
238     g_signal_new (I_("toggle-size-allocate"),
239                   G_OBJECT_CLASS_TYPE (gobject_class),
240                   G_SIGNAL_RUN_FIRST,
241                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
242                   NULL, NULL,
243                   _gtk_marshal_VOID__INT,
244                   G_TYPE_NONE, 1,
245                   G_TYPE_INT);
246
247   /**
248    * GtkMenuItem:right-justified:
249    *
250    * Sets whether the menu item appears justified at the right side of a menu bar.
251    *
252    * Since: 2.14
253    **/
254   g_object_class_install_property (gobject_class,
255                                    PROP_RIGHT_JUSTIFIED,
256                                    g_param_spec_boolean ("right-justified",
257                                                          P_("Right Justified"),
258                                                          P_("Sets whether the menu item appears justified at the right side of a menu bar"),
259                                                          FALSE,
260                                                          GTK_PARAM_READWRITE));
261
262   /**
263    * GtkMenuItem:submenu:
264    *
265    * The submenu attached to the menu item, or NULL if it has none.
266    *
267    * Since: 2.12
268    **/
269   g_object_class_install_property (gobject_class,
270                                    PROP_SUBMENU,
271                                    g_param_spec_object ("submenu",
272                                                         P_("Submenu"),
273                                                         P_("The submenu attached to the menu item, or NULL if it has none"),
274                                                         GTK_TYPE_MENU,
275                                                         GTK_PARAM_READWRITE));
276   
277
278   /**
279    * GtkMenuItem:accel-path:
280    *
281    * Sets the accelerator path of the menu item, through which runtime
282    * changes of the menu item's accelerator caused by the user can be
283    * identified and saved to persistant storage.
284    *
285    * Since: 2.14
286    **/
287   g_object_class_install_property (gobject_class,
288                                    PROP_ACCEL_PATH,
289                                    g_param_spec_string ("accel-path",
290                                                         P_("Accel Path"),
291                                                         P_("Sets the accelerator path of the menu item"),
292                                                         NULL,
293                                                         GTK_PARAM_READWRITE));
294
295   /**
296    * GtkMenuItem:label:
297    *
298    * The text for the child label.
299    *
300    * Since: 2.16
301    **/
302   g_object_class_install_property (gobject_class,
303                                    PROP_LABEL,
304                                    g_param_spec_string ("label",
305                                                         P_("Label"),
306                                                         P_("The text for the child label"),
307                                                         "",
308                                                         GTK_PARAM_READWRITE));
309
310   /**
311    * GtkMenuItem:use-underline:
312    *
313    * %TRUE if underlines in the text indicate mnemonics  
314    *
315    * Since: 2.16
316    **/
317   g_object_class_install_property (gobject_class,
318                                    PROP_USE_UNDERLINE,
319                                    g_param_spec_boolean ("use-underline",
320                                                          P_("Use underline"),
321                                                          P_("If set, an underline in the text indicates "
322                                                             "the next character should be used for the "
323                                                             "mnemonic accelerator key"),
324                                                          FALSE,
325                                                          GTK_PARAM_READWRITE));
326
327   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
328   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
329
330   gtk_widget_class_install_style_property_parser (widget_class,
331                                                   g_param_spec_enum ("selected-shadow-type",
332                                                                      "Selected Shadow Type",
333                                                                      "Shadow type when item is selected",
334                                                                      GTK_TYPE_SHADOW_TYPE,
335                                                                      GTK_SHADOW_NONE,
336                                                                      GTK_PARAM_READABLE),
337                                                   gtk_rc_property_parse_enum);
338
339   gtk_widget_class_install_style_property (widget_class,
340                                            g_param_spec_int ("horizontal-padding",
341                                                              "Horizontal Padding",
342                                                              "Padding to left and right of the menu item",
343                                                              0,
344                                                              G_MAXINT,
345                                                              3,
346                                                              GTK_PARAM_READABLE));
347
348   gtk_widget_class_install_style_property (widget_class,
349                                            g_param_spec_int ("toggle-spacing",
350                                                              "Icon Spacing",
351                                                              "Space between icon and label",
352                                                              0,
353                                                              G_MAXINT,
354                                                              5,
355                                                              GTK_PARAM_READABLE));
356
357   gtk_widget_class_install_style_property (widget_class,
358                                            g_param_spec_int ("arrow-spacing",
359                                                              "Arrow Spacing",
360                                                              "Space between label and arrow",
361                                                              0,
362                                                              G_MAXINT,
363                                                              10,
364                                                              GTK_PARAM_READABLE));
365
366   gtk_widget_class_install_style_property (widget_class,
367                                            g_param_spec_float ("arrow-scaling",
368                                                                P_("Arrow Scaling"),
369                                                                P_("Amount of space used up by arrow, relative to the menu item's font size"),
370                                                                0.0, 2.0, 0.8,
371                                                                GTK_PARAM_READABLE));
372
373   /**
374    * GtkMenuItem:width-chars:
375    *
376    * The minimum desired width of the menu item in characters.
377    *
378    * Since: 2.14
379    **/
380   gtk_widget_class_install_style_property (widget_class,
381                                            g_param_spec_int ("width-chars",
382                                                              P_("Width in Characters"),
383                                                              P_("The minimum desired width of the menu item in characters"),
384                                                              0, G_MAXINT, 12,
385                                                              GTK_PARAM_READABLE));
386
387   g_type_class_add_private (object_class, sizeof (GtkMenuItemPrivate));
388 }
389
390 static void
391 gtk_menu_item_init (GtkMenuItem *menu_item)
392 {
393   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
394
395   GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
396
397   priv->action = NULL;
398   priv->use_action_appearance = TRUE;
399   
400   menu_item->submenu = NULL;
401   menu_item->toggle_size = 0;
402   menu_item->accelerator_width = 0;
403   menu_item->show_submenu_indicator = FALSE;
404   if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
405     menu_item->submenu_direction = GTK_DIRECTION_LEFT;
406   else
407     menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
408   menu_item->submenu_placement = GTK_TOP_BOTTOM;
409   menu_item->right_justify = FALSE;
410
411   menu_item->timer = 0;
412 }
413
414 GtkWidget*
415 gtk_menu_item_new (void)
416 {
417   return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
418 }
419
420 GtkWidget*
421 gtk_menu_item_new_with_label (const gchar *label)
422 {
423   return g_object_new (GTK_TYPE_MENU_ITEM, 
424                        "label", label,
425                        NULL);
426 }
427
428
429 /**
430  * gtk_menu_item_new_with_mnemonic:
431  * @label: The text of the button, with an underscore in front of the
432  *         mnemonic character
433  * @returns: a new #GtkMenuItem
434  *
435  * Creates a new #GtkMenuItem containing a label. The label
436  * will be created using gtk_label_new_with_mnemonic(), so underscores
437  * in @label indicate the mnemonic for the menu item.
438  **/
439 GtkWidget*
440 gtk_menu_item_new_with_mnemonic (const gchar *label)
441 {
442   return g_object_new (GTK_TYPE_MENU_ITEM, 
443                        "use-underline", TRUE,
444                        "label", label,
445                        NULL);
446 }
447
448 static void
449 gtk_menu_item_dispose (GObject *object)
450 {
451   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
452   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
453
454   if (priv->action)
455     {
456       gtk_action_disconnect_accelerator (priv->action);
457       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL);
458       
459       priv->action = NULL;
460     }
461   G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object);
462 }
463
464 static void 
465 gtk_menu_item_set_property (GObject      *object,
466                             guint         prop_id,
467                             const GValue *value,
468                             GParamSpec   *pspec)
469 {
470   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
471   
472   switch (prop_id)
473     {
474     case PROP_RIGHT_JUSTIFIED:
475       gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
476       break;
477     case PROP_SUBMENU:
478       gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
479       break;
480     case PROP_ACCEL_PATH:
481       gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
482       break;
483     case PROP_LABEL:
484       gtk_menu_item_set_label (menu_item, g_value_get_string (value));
485       break;
486     case PROP_USE_UNDERLINE:
487       gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
488       break;
489     case PROP_ACTIVATABLE_RELATED_ACTION:
490       gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
491       break;
492     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
493       gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
494       break;
495     default:
496       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
497       break;
498     }
499 }
500
501 static void 
502 gtk_menu_item_get_property (GObject    *object,
503                             guint       prop_id,
504                             GValue     *value,
505                             GParamSpec *pspec)
506 {
507   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
508   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
509   
510   switch (prop_id)
511     {
512     case PROP_RIGHT_JUSTIFIED:
513       g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
514       break;
515     case PROP_SUBMENU:
516       g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
517       break;
518     case PROP_ACCEL_PATH:
519       g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
520       break;
521     case PROP_LABEL:
522       g_value_set_string (value, gtk_menu_item_get_label (menu_item));
523       break;
524     case PROP_USE_UNDERLINE:
525       g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
526       break;
527     case PROP_ACTIVATABLE_RELATED_ACTION:
528       g_value_set_object (value, priv->action);
529       break;
530     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
531       g_value_set_boolean (value, priv->use_action_appearance);
532       break;
533     default:
534       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
535       break;
536     }
537 }
538
539 static void
540 gtk_menu_item_destroy (GtkObject *object)
541 {
542   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
543
544   if (menu_item->submenu)
545     gtk_widget_destroy (menu_item->submenu);
546
547   GTK_OBJECT_CLASS (gtk_menu_item_parent_class)->destroy (object);
548 }
549
550 static void
551 gtk_menu_item_detacher (GtkWidget *widget,
552                         GtkMenu   *menu)
553 {
554   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
555
556   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
557
558   menu_item->submenu = NULL;
559 }
560
561 static void
562 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
563 {
564   parent_buildable_iface = g_type_interface_peek_parent (iface);
565   iface->add_child = gtk_menu_item_buildable_add_child;
566   iface->custom_finished = gtk_menu_item_buildable_custom_finished;
567 }
568
569 static void 
570 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
571                                    GtkBuilder   *builder,
572                                    GObject      *child,
573                                    const gchar  *type)
574 {
575   if (type && strcmp (type, "submenu") == 0)
576         gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
577                                    GTK_WIDGET (child));
578   else
579     parent_buildable_iface->add_child (buildable, builder, child, type);
580 }
581
582
583 static void 
584 gtk_menu_item_buildable_custom_finished (GtkBuildable        *buildable,
585                                          GtkBuilder          *builder,
586                                          GObject             *child,
587                                          const gchar         *tagname,
588                                          gpointer             user_data)
589 {
590   GtkWidget *toplevel;
591
592   if (strcmp (tagname, "accelerator") == 0)
593     {
594       GtkMenuShell *menu_shell = (GtkMenuShell *) GTK_WIDGET (buildable)->parent;
595       GtkWidget *attach;
596
597       if (menu_shell)
598         {
599           while (GTK_IS_MENU (menu_shell) &&
600                  (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
601             menu_shell = (GtkMenuShell *)attach->parent;
602           
603           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
604         }
605       else
606         {
607           /* Fall back to something ... */
608           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
609
610           g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
611                      gtk_buildable_get_name (buildable));
612         }
613
614       /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
615       _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
616     }
617   else
618     parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
619 }
620
621
622 static void
623 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
624 {
625   iface->update = gtk_menu_item_update;
626   iface->sync_action_properties = gtk_menu_item_sync_action_properties;
627 }
628
629 static void
630 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
631 {
632   GtkWidget *child = GTK_BIN (menu_item)->child;
633
634   if (GTK_IS_LABEL (child))
635     {
636       const gchar *label;
637
638       label = gtk_action_get_label (action);
639       gtk_label_set_label (GTK_LABEL (child), label ? label : "");
640     }
641 }
642
643 gboolean _gtk_menu_is_empty (GtkWidget *menu);
644
645 static void
646 gtk_menu_item_update (GtkActivatable *activatable,
647                       GtkAction      *action,
648                       const gchar    *property_name)
649 {
650   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
651   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
652
653   if (strcmp (property_name, "visible") == 0)
654     _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item), 
655                                    _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
656   else if (strcmp (property_name, "sensitive") == 0)
657     gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
658   else if (priv->use_action_appearance)
659     {
660       if (strcmp (property_name, "label") == 0)
661         activatable_update_label (menu_item, action);
662     }
663 }
664
665 static void
666 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
667                                       GtkAction      *action)
668 {
669   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
670   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
671
672   if (!action)
673     return;
674
675   _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
676                                  _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
677
678   gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
679
680   if (priv->use_action_appearance)
681     {
682       GtkWidget *label = GTK_BIN (menu_item)->child;
683
684       /* make sure label is a label */
685       if (label && !GTK_IS_LABEL (label))
686         {
687           gtk_container_remove (GTK_CONTAINER (menu_item), label);
688           label = NULL;
689         }
690
691       if (!label)
692         label = g_object_new (GTK_TYPE_ACCEL_LABEL,
693                               "use-underline", TRUE,
694                               "xalign", 0.0,
695                               "visible", TRUE,
696                               "parent", menu_item,
697                               NULL);
698
699       if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
700         g_object_set (label,
701                       "accel-closure", gtk_action_get_accel_closure (action),
702                       NULL);
703
704       activatable_update_label (menu_item, action);
705     }
706 }
707
708 static void
709 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
710                                   GtkAction   *action)
711 {
712     GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
713
714     if (priv->action == action)
715       return;
716
717     if (priv->action)
718       {
719         gtk_action_disconnect_accelerator (priv->action);
720       }
721
722     if (action)
723       {
724         const gchar *accel_path;
725         
726         accel_path = gtk_action_get_accel_path (action);
727         if (accel_path)
728           {
729             gtk_action_connect_accelerator (action);
730             gtk_menu_item_set_accel_path (menu_item, accel_path);
731           }
732       }
733
734     gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
735
736     priv->action = action;
737 }
738
739 static void
740 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
741                                          gboolean     use_appearance)
742 {
743     GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
744
745     if (priv->use_action_appearance != use_appearance)
746       {
747         priv->use_action_appearance = use_appearance;
748
749         gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
750       }
751 }
752
753
754 /**
755  * gtk_menu_item_set_submenu:
756  * @menu_item: a #GtkMenuItem
757  * @submenu: the submenu, or %NULL
758  *
759  * Sets or replaces the menu item's submenu, or removes it when a %NULL
760  * submenu is passed.
761  **/
762 void
763 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
764                            GtkWidget   *submenu)
765 {
766   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
767   g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
768   
769   if (menu_item->submenu != submenu)
770     {
771       if (menu_item->submenu)
772         gtk_menu_detach (GTK_MENU (menu_item->submenu));
773
774       if (submenu)
775         {
776           menu_item->submenu = submenu;
777           gtk_menu_attach_to_widget (GTK_MENU (submenu),
778                                      GTK_WIDGET (menu_item),
779                                      gtk_menu_item_detacher);
780         }
781
782       if (GTK_WIDGET (menu_item)->parent)
783         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
784
785       g_object_notify (G_OBJECT (menu_item), "submenu");
786     }
787 }
788
789 /**
790  * gtk_menu_item_get_submenu:
791  * @menu_item: a #GtkMenuItem
792  *
793  * Gets the submenu underneath this menu item, if any. See
794  * gtk_menu_item_set_submenu().
795  *
796  * Return value: submenu for this menu item, or %NULL if none.
797  **/
798 GtkWidget *
799 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
800 {
801   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
802
803   return menu_item->submenu;
804 }
805
806 /**
807  * gtk_menu_item_remove_submenu:
808  * @menu_item: a #GtkMenuItem
809  *
810  * Removes the widget's submenu.
811  *
812  * Deprecated: 2.12: gtk_menu_item_remove_submenu() is deprecated and
813  *                   should not be used in newly written code. Use
814  *                   gtk_menu_item_set_submenu() instead.
815  **/
816 void
817 gtk_menu_item_remove_submenu (GtkMenuItem *menu_item)
818 {
819   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
820
821   gtk_menu_item_set_submenu (menu_item, NULL);
822 }
823
824 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
825                                    GtkSubmenuPlacement  placement);
826
827 void
828 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
829                              GtkSubmenuPlacement  placement)
830 {
831   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
832
833   menu_item->submenu_placement = placement;
834 }
835
836 void
837 gtk_menu_item_select (GtkMenuItem *menu_item)
838 {
839   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
840
841   gtk_item_select (GTK_ITEM (menu_item));
842
843   /* Enable themeing of the parent menu item depending on whether
844    * something is selected in its submenu
845    */
846   if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
847     {
848       GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
849
850       if (menu->parent_menu_item)
851         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
852     }
853 }
854
855 void
856 gtk_menu_item_deselect (GtkMenuItem *menu_item)
857 {
858   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
859
860   gtk_item_deselect (GTK_ITEM (menu_item));
861
862   /* Enable themeing of the parent menu item depending on whether
863    * something is selected in its submenu
864    */
865   if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
866     {
867       GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
868
869       if (menu->parent_menu_item)
870         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
871     }
872 }
873
874 void
875 gtk_menu_item_activate (GtkMenuItem *menu_item)
876 {
877   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
878
879   g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
880 }
881
882 void
883 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
884                                    gint        *requisition)
885 {
886   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
887
888   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
889 }
890
891 void
892 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
893                                     gint         allocation)
894 {
895   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
896
897   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
898 }
899
900 static void
901 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
902                                    gpointer data)
903 {
904   guint *width = data;
905
906   if (GTK_IS_ACCEL_LABEL (widget))
907     {
908       guint w;
909
910       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
911       *width = MAX (*width, w);
912     }
913   else if (GTK_IS_CONTAINER (widget))
914     gtk_container_foreach (GTK_CONTAINER (widget),
915                            gtk_menu_item_accel_width_foreach,
916                            data);
917 }
918
919 static gint
920 get_minimum_width (GtkWidget *widget)
921 {
922   PangoContext *context;
923   PangoFontMetrics *metrics;
924   gint width;
925   gint width_chars;
926
927   context = gtk_widget_get_pango_context (widget);
928   metrics = pango_context_get_metrics (context,
929                                        widget->style->font_desc,
930                                        pango_context_get_language (context));
931
932   width = pango_font_metrics_get_approximate_char_width (metrics);
933
934   pango_font_metrics_unref (metrics);
935
936   gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
937
938   return PANGO_PIXELS (width_chars * width);
939 }
940
941 static void
942 gtk_menu_item_size_request (GtkWidget      *widget,
943                             GtkRequisition *requisition)
944 {
945   GtkMenuItem *menu_item;
946   GtkBin *bin;
947   guint accel_width;
948   guint horizontal_padding;
949   GtkPackDirection pack_dir;
950   GtkPackDirection child_pack_dir;
951
952   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
953   g_return_if_fail (requisition != NULL);
954
955   gtk_widget_style_get (widget,
956                         "horizontal-padding", &horizontal_padding,
957                         NULL);
958   
959   bin = GTK_BIN (widget);
960   menu_item = GTK_MENU_ITEM (widget);
961
962   if (GTK_IS_MENU_BAR (widget->parent))
963     {
964       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
965       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
966     }
967   else
968     {
969       pack_dir = GTK_PACK_DIRECTION_LTR;
970       child_pack_dir = GTK_PACK_DIRECTION_LTR;
971     }
972
973   requisition->width = (GTK_CONTAINER (widget)->border_width +
974                         widget->style->xthickness) * 2;
975   requisition->height = (GTK_CONTAINER (widget)->border_width +
976                          widget->style->ythickness) * 2;
977
978   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
979       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
980     requisition->width += 2 * horizontal_padding;
981   else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
982       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
983     requisition->height += 2 * horizontal_padding;
984
985   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
986     {
987       GtkRequisition child_requisition;
988       
989       gtk_widget_size_request (bin->child, &child_requisition);
990
991       requisition->width += child_requisition.width;
992       requisition->height += child_requisition.height;
993
994       if (menu_item->submenu && menu_item->show_submenu_indicator)
995         {
996           guint arrow_spacing;
997           
998           gtk_widget_style_get (widget,
999                                 "arrow-spacing", &arrow_spacing,
1000                                 NULL);
1001
1002           requisition->width += child_requisition.height;
1003           requisition->width += arrow_spacing;
1004
1005           requisition->width = MAX (requisition->width, get_minimum_width (widget));
1006         }
1007     }
1008   else /* separator item */
1009     {
1010       gboolean wide_separators;
1011       gint     separator_height;
1012
1013       gtk_widget_style_get (widget,
1014                             "wide-separators",  &wide_separators,
1015                             "separator-height", &separator_height,
1016                             NULL);
1017
1018       if (wide_separators)
1019         requisition->height += separator_height + widget->style->ythickness;
1020       else
1021         requisition->height += widget->style->ythickness * 2;
1022     }
1023
1024   accel_width = 0;
1025   gtk_container_foreach (GTK_CONTAINER (menu_item),
1026                          gtk_menu_item_accel_width_foreach,
1027                          &accel_width);
1028   menu_item->accelerator_width = accel_width;
1029 }
1030
1031 static void
1032 gtk_menu_item_size_allocate (GtkWidget     *widget,
1033                              GtkAllocation *allocation)
1034 {
1035   GtkMenuItem *menu_item;
1036   GtkBin *bin;
1037   GtkAllocation child_allocation;
1038   GtkTextDirection direction;
1039   GtkPackDirection pack_dir;
1040   GtkPackDirection child_pack_dir;
1041
1042   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1043   g_return_if_fail (allocation != NULL);
1044
1045   menu_item = GTK_MENU_ITEM (widget);
1046   bin = GTK_BIN (widget);
1047   
1048   direction = gtk_widget_get_direction (widget);
1049
1050   if (GTK_IS_MENU_BAR (widget->parent))
1051     {
1052       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
1053       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
1054     }
1055   else
1056     {
1057       pack_dir = GTK_PACK_DIRECTION_LTR;
1058       child_pack_dir = GTK_PACK_DIRECTION_LTR;
1059     }
1060     
1061   widget->allocation = *allocation;
1062
1063   if (bin->child)
1064     {
1065       GtkRequisition child_requisition;
1066       guint horizontal_padding;
1067
1068       gtk_widget_style_get (widget,
1069                             "horizontal-padding", &horizontal_padding,
1070                             NULL);
1071
1072       child_allocation.x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
1073       child_allocation.y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
1074
1075       if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1076           (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1077         child_allocation.x += horizontal_padding;
1078       else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1079                (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1080         child_allocation.y += horizontal_padding;
1081       
1082       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
1083       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
1084
1085       if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1086           child_pack_dir == GTK_PACK_DIRECTION_RTL)
1087         {
1088           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1089             child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
1090           child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
1091         }
1092       else
1093         {
1094           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1095             child_allocation.y += GTK_MENU_ITEM (widget)->toggle_size;
1096           child_allocation.height -= GTK_MENU_ITEM (widget)->toggle_size;
1097         }
1098
1099       child_allocation.x += widget->allocation.x;
1100       child_allocation.y += widget->allocation.y;
1101
1102       gtk_widget_get_child_requisition (bin->child, &child_requisition);
1103       if (menu_item->submenu && menu_item->show_submenu_indicator) 
1104         {
1105           if (direction == GTK_TEXT_DIR_RTL)
1106             child_allocation.x += child_requisition.height;
1107           child_allocation.width -= child_requisition.height;
1108         }
1109       
1110       if (child_allocation.width < 1)
1111         child_allocation.width = 1;
1112
1113       gtk_widget_size_allocate (bin->child, &child_allocation);
1114     }
1115
1116   if (GTK_WIDGET_REALIZED (widget))
1117     gdk_window_move_resize (menu_item->event_window,
1118                             allocation->x, allocation->y,
1119                             allocation->width, allocation->height);
1120
1121   if (menu_item->submenu)
1122     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
1123 }
1124
1125 static void
1126 gtk_menu_item_realize (GtkWidget *widget)
1127 {
1128   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1129   GdkWindowAttr attributes;
1130   gint attributes_mask;
1131
1132   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1133
1134   widget->window = gtk_widget_get_parent_window (widget);
1135   g_object_ref (widget->window);
1136   
1137   attributes.x = widget->allocation.x;
1138   attributes.y = widget->allocation.y;
1139   attributes.width = widget->allocation.width;
1140   attributes.height = widget->allocation.height;
1141   attributes.window_type = GDK_WINDOW_CHILD;
1142   attributes.wclass = GDK_INPUT_ONLY;
1143   attributes.event_mask = (gtk_widget_get_events (widget) |
1144                            GDK_BUTTON_PRESS_MASK |
1145                            GDK_BUTTON_RELEASE_MASK |
1146                            GDK_ENTER_NOTIFY_MASK |
1147                            GDK_LEAVE_NOTIFY_MASK |
1148                            GDK_POINTER_MOTION_MASK);
1149
1150   attributes_mask = GDK_WA_X | GDK_WA_Y;
1151   menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1152   gdk_window_set_user_data (menu_item->event_window, widget);
1153
1154   widget->style = gtk_style_attach (widget->style, widget->window);
1155 }
1156
1157 static void
1158 gtk_menu_item_unrealize (GtkWidget *widget)
1159 {
1160   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1161
1162   gdk_window_set_user_data (menu_item->event_window, NULL);
1163   gdk_window_destroy (menu_item->event_window);
1164   menu_item->event_window = NULL;
1165
1166   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1167 }
1168
1169 static void
1170 gtk_menu_item_map (GtkWidget *widget)
1171 {
1172   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1173   
1174   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1175
1176   gdk_window_show (menu_item->event_window);
1177 }
1178
1179 static void
1180 gtk_menu_item_unmap (GtkWidget *widget)
1181 {
1182   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1183     
1184   gdk_window_hide (menu_item->event_window);
1185
1186   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1187 }
1188
1189 static void
1190 gtk_menu_item_paint (GtkWidget    *widget,
1191                      GdkRectangle *area)
1192 {
1193   GtkMenuItem *menu_item;
1194   GtkStateType state_type;
1195   GtkShadowType shadow_type, selected_shadow_type;
1196   gint width, height;
1197   gint x, y;
1198   gint border_width = GTK_CONTAINER (widget)->border_width;
1199
1200   if (GTK_WIDGET_DRAWABLE (widget))
1201     {
1202       menu_item = GTK_MENU_ITEM (widget);
1203
1204       state_type = widget->state;
1205       
1206       x = widget->allocation.x + border_width;
1207       y = widget->allocation.y + border_width;
1208       width = widget->allocation.width - border_width * 2;
1209       height = widget->allocation.height - border_width * 2;
1210       
1211       if ((state_type == GTK_STATE_PRELIGHT) &&
1212           (GTK_BIN (menu_item)->child))
1213         {
1214           gtk_widget_style_get (widget,
1215                                 "selected-shadow-type", &selected_shadow_type,
1216                                 NULL);
1217           gtk_paint_box (widget->style,
1218                          widget->window,
1219                          GTK_STATE_PRELIGHT,
1220                          selected_shadow_type,
1221                          area, widget, "menuitem",
1222                          x, y, width, height);
1223         }
1224   
1225       if (menu_item->submenu && menu_item->show_submenu_indicator)
1226         {
1227           gint arrow_x, arrow_y;
1228           gint arrow_size;
1229           gint arrow_extent;
1230           guint horizontal_padding;
1231           gfloat arrow_scaling;
1232           GtkTextDirection direction;
1233           GtkArrowType arrow_type;
1234           PangoContext *context;
1235           PangoFontMetrics *metrics;
1236
1237           direction = gtk_widget_get_direction (widget);
1238       
1239           gtk_widget_style_get (widget,
1240                                 "horizontal-padding", &horizontal_padding,
1241                                 "arrow-scaling", &arrow_scaling,
1242                                 NULL);
1243           
1244           context = gtk_widget_get_pango_context (GTK_BIN (menu_item)->child);
1245           metrics = pango_context_get_metrics (context, 
1246                                                GTK_WIDGET (GTK_BIN (menu_item)->child)->style->font_desc,
1247                                                pango_context_get_language (context));
1248
1249           arrow_size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1250                                       pango_font_metrics_get_descent (metrics)));
1251
1252           pango_font_metrics_unref (metrics);
1253
1254           arrow_extent = arrow_size * arrow_scaling;
1255
1256           shadow_type = GTK_SHADOW_OUT;
1257           if (state_type == GTK_STATE_PRELIGHT)
1258             shadow_type = GTK_SHADOW_IN;
1259
1260           if (direction == GTK_TEXT_DIR_LTR)
1261             {
1262               arrow_x = x + width - horizontal_padding - arrow_extent;
1263               arrow_type = GTK_ARROW_RIGHT;
1264             }
1265           else
1266             {
1267               arrow_x = x + horizontal_padding;
1268               arrow_type = GTK_ARROW_LEFT;
1269             }
1270
1271           arrow_y = y + (height - arrow_extent) / 2;
1272
1273           gtk_paint_arrow (widget->style, widget->window,
1274                            state_type, shadow_type, 
1275                            area, widget, "menuitem", 
1276                            arrow_type, TRUE,
1277                            arrow_x, arrow_y,
1278                            arrow_extent, arrow_extent);
1279         }
1280       else if (!GTK_BIN (menu_item)->child)
1281         {
1282           gboolean wide_separators;
1283           gint     separator_height;
1284           guint    horizontal_padding;
1285
1286           gtk_widget_style_get (widget,
1287                                 "wide-separators",    &wide_separators,
1288                                 "separator-height",   &separator_height,
1289                                 "horizontal-padding", &horizontal_padding,
1290                                 NULL);
1291
1292           if (wide_separators)
1293             gtk_paint_box (widget->style, widget->window,
1294                            GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
1295                            area, widget, "hseparator",
1296                            widget->allocation.x + horizontal_padding + widget->style->xthickness,
1297                            widget->allocation.y + (widget->allocation.height -
1298                                                    separator_height -
1299                                                    widget->style->ythickness) / 2,
1300                            widget->allocation.width -
1301                            2 * (horizontal_padding + widget->style->xthickness),
1302                            separator_height);
1303           else
1304             gtk_paint_hline (widget->style, widget->window,
1305                              GTK_STATE_NORMAL, area, widget, "menuitem",
1306                              widget->allocation.x + horizontal_padding + widget->style->xthickness,
1307                              widget->allocation.x + widget->allocation.width - horizontal_padding - widget->style->xthickness - 1,
1308                              widget->allocation.y + (widget->allocation.height -
1309                                                      widget->style->ythickness) / 2);
1310         }
1311     }
1312 }
1313
1314 static gint
1315 gtk_menu_item_expose (GtkWidget      *widget,
1316                       GdkEventExpose *event)
1317 {
1318   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
1319   g_return_val_if_fail (event != NULL, FALSE);
1320
1321   if (GTK_WIDGET_DRAWABLE (widget))
1322     {
1323       gtk_menu_item_paint (widget, &event->area);
1324
1325       GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->expose_event (widget, event);
1326     }
1327
1328   return FALSE;
1329 }
1330
1331 static void
1332 gtk_real_menu_item_select (GtkItem *item)
1333 {
1334   GtkMenuItem *menu_item;
1335   gboolean touchscreen_mode;
1336
1337   g_return_if_fail (GTK_IS_MENU_ITEM (item));
1338
1339   menu_item = GTK_MENU_ITEM (item);
1340
1341   g_object_get (gtk_widget_get_settings (GTK_WIDGET (item)),
1342                 "gtk-touchscreen-mode", &touchscreen_mode,
1343                 NULL);
1344
1345   if (!touchscreen_mode &&
1346       menu_item->submenu &&
1347       (!GTK_WIDGET_MAPPED (menu_item->submenu) ||
1348        GTK_MENU (menu_item->submenu)->tearoff_active))
1349     {
1350       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1351     }
1352
1353   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
1354   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1355 }
1356
1357 static void
1358 gtk_real_menu_item_deselect (GtkItem *item)
1359 {
1360   GtkMenuItem *menu_item;
1361
1362   g_return_if_fail (GTK_IS_MENU_ITEM (item));
1363
1364   menu_item = GTK_MENU_ITEM (item);
1365
1366   if (menu_item->submenu)
1367     _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1368
1369   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
1370   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1371 }
1372
1373 static gboolean
1374 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1375                                  gboolean   group_cycling)
1376 {
1377   if (group_cycling &&
1378       widget->parent &&
1379       GTK_IS_MENU_SHELL (widget->parent) &&
1380       GTK_MENU_SHELL (widget->parent)->active)
1381     {
1382       gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
1383                                   widget);
1384     }
1385   else
1386     g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1387   
1388   return TRUE;
1389 }
1390
1391 static void 
1392 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1393 {
1394   GtkMenuItemPrivate *priv;
1395
1396   priv = GET_PRIVATE (menu_item);
1397
1398   if (priv->action)
1399     gtk_action_activate (priv->action);
1400 }
1401
1402
1403 static void
1404 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1405 {
1406   GtkMenuItemPrivate *priv;
1407   GtkWidget *widget;
1408
1409   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1410
1411   priv   = GET_PRIVATE (menu_item);
1412   widget = GTK_WIDGET (menu_item);
1413   
1414   if (widget->parent &&
1415       GTK_IS_MENU_SHELL (widget->parent))
1416     {
1417       if (menu_item->submenu == NULL)
1418         gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
1419                                       widget, TRUE);
1420       else
1421         {
1422           GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
1423
1424           _gtk_menu_shell_activate (menu_shell);
1425
1426           gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
1427           _gtk_menu_item_popup_submenu (widget, FALSE);
1428
1429           gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1430         }
1431     }
1432 }
1433
1434 static void
1435 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1436                                         gint        *requisition)
1437 {
1438   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1439
1440   *requisition = 0;
1441 }
1442
1443 static void
1444 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1445                                          gint         allocation)
1446 {
1447   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1448
1449   menu_item->toggle_size = allocation;
1450 }
1451
1452 static void
1453 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1454                               const gchar *label)
1455 {
1456   gtk_menu_item_ensure_label (menu_item);
1457
1458   if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1459     {
1460       gtk_label_set_label (GTK_LABEL (GTK_BIN (menu_item)->child), label ? label : "");
1461       
1462       g_object_notify (G_OBJECT (menu_item), "label");
1463     }
1464 }
1465
1466 static G_CONST_RETURN gchar *
1467 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1468 {
1469   gtk_menu_item_ensure_label (menu_item);
1470
1471   if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
1472     return gtk_label_get_label (GTK_LABEL (GTK_BIN (menu_item)->child));
1473
1474   return NULL;
1475 }
1476
1477 static void
1478 free_timeval (GTimeVal *val)
1479 {
1480   g_slice_free (GTimeVal, val);
1481 }
1482
1483 static void
1484 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1485                                   gboolean   remember_exact_time)
1486 {
1487   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1488
1489   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu) && widget->parent)
1490     {
1491       gboolean take_focus;
1492       GtkMenuPositionFunc menu_position_func;
1493
1494       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent));
1495       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1496                                      take_focus);
1497
1498       if (remember_exact_time)
1499         {
1500           GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1501
1502           g_get_current_time (popup_time);
1503
1504           g_object_set_data_full (G_OBJECT (menu_item->submenu),
1505                                   "gtk-menu-exact-popup-time", popup_time,
1506                                   (GDestroyNotify) free_timeval);
1507         }
1508       else
1509         {
1510           g_object_set_data (G_OBJECT (menu_item->submenu),
1511                              "gtk-menu-exact-popup-time", NULL);
1512         }
1513
1514       /* gtk_menu_item_position_menu positions the submenu from the
1515        * menuitems position. If the menuitem doesn't have a window,
1516        * that doesn't work. In that case we use the default
1517        * positioning function instead which places the submenu at the
1518        * mouse cursor.
1519        */
1520       if (widget->window)
1521         menu_position_func = gtk_menu_item_position_menu;
1522       else
1523         menu_position_func = NULL;
1524
1525       gtk_menu_popup (GTK_MENU (menu_item->submenu),
1526                       widget->parent,
1527                       widget,
1528                       menu_position_func,
1529                       menu_item,
1530                       GTK_MENU_SHELL (widget->parent)->button,
1531                       0);
1532     }
1533
1534   /* Enable themeing of the parent menu item depending on whether
1535    * its submenu is shown or not.
1536    */
1537   gtk_widget_queue_draw (widget);
1538 }
1539
1540 static gint
1541 gtk_menu_item_popup_timeout (gpointer data)
1542 {
1543   GtkMenuItem *menu_item;
1544   GtkWidget *parent;
1545   
1546   menu_item = GTK_MENU_ITEM (data);
1547
1548   parent = GTK_WIDGET (menu_item)->parent;
1549
1550   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) || 
1551       (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1552     {
1553       gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1554       if (menu_item->timer_from_keypress && menu_item->submenu)
1555         GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1556     }
1557
1558   menu_item->timer = 0;
1559
1560   return FALSE;  
1561 }
1562
1563 static gint
1564 get_popup_delay (GtkWidget *widget)
1565 {
1566   if (GTK_IS_MENU_SHELL (widget->parent))
1567     {
1568       return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (widget->parent));
1569     }
1570   else
1571     {
1572       gint popup_delay;
1573
1574       g_object_get (gtk_widget_get_settings (widget),
1575                     "gtk-menu-popup-delay", &popup_delay,
1576                     NULL);
1577
1578       return popup_delay;
1579     }
1580 }
1581
1582 void
1583 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1584                               gboolean   with_delay)
1585 {
1586   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1587
1588   if (menu_item->timer)
1589     {
1590       g_source_remove (menu_item->timer);
1591       menu_item->timer = 0;
1592       with_delay = FALSE;
1593     }
1594
1595   if (with_delay)
1596     {
1597       gint popup_delay = get_popup_delay (widget);
1598
1599       if (popup_delay > 0)
1600         {
1601           GdkEvent *event = gtk_get_current_event ();
1602
1603           menu_item->timer = gdk_threads_add_timeout (popup_delay,
1604                                                       gtk_menu_item_popup_timeout,
1605                                                       menu_item);
1606
1607           if (event &&
1608               event->type != GDK_BUTTON_PRESS &&
1609               event->type != GDK_ENTER_NOTIFY)
1610             menu_item->timer_from_keypress = TRUE;
1611           else
1612             menu_item->timer_from_keypress = FALSE;
1613
1614           if (event)
1615             gdk_event_free (event);
1616
1617           return;
1618         }
1619     }
1620
1621   gtk_menu_item_real_popup_submenu (widget, FALSE);
1622 }
1623
1624 void
1625 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1626 {
1627   GtkMenuItem *menu_item;
1628
1629   menu_item = GTK_MENU_ITEM (widget);
1630
1631   if (menu_item->submenu)
1632     {
1633       g_object_set_data (G_OBJECT (menu_item->submenu),
1634                          "gtk-menu-exact-popup-time", NULL);
1635
1636       if (menu_item->timer)
1637         {
1638           g_source_remove (menu_item->timer);
1639           menu_item->timer = 0;
1640         }
1641       else
1642         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1643
1644       gtk_widget_queue_draw (widget);
1645     }
1646 }
1647
1648 static void
1649 get_offsets (GtkMenu *menu,
1650              gint    *horizontal_offset,
1651              gint    *vertical_offset)
1652 {
1653   gint vertical_padding;
1654   gint horizontal_padding;
1655   
1656   gtk_widget_style_get (GTK_WIDGET (menu),
1657                         "horizontal-offset", horizontal_offset,
1658                         "vertical-offset", vertical_offset,
1659                         "horizontal-padding", &horizontal_padding,
1660                         "vertical-padding", &vertical_padding,
1661                         NULL);
1662
1663   *vertical_offset -= GTK_WIDGET (menu)->style->ythickness;
1664   *vertical_offset -= vertical_padding;
1665   *horizontal_offset += horizontal_padding;
1666 }
1667
1668 static void
1669 gtk_menu_item_position_menu (GtkMenu  *menu,
1670                              gint     *x,
1671                              gint     *y,
1672                              gboolean *push_in,
1673                              gpointer  user_data)
1674 {
1675   GtkMenuItem *menu_item;
1676   GtkWidget *widget;
1677   GtkMenuItem *parent_menu_item;
1678   GdkScreen *screen;
1679   gint twidth, theight;
1680   gint tx, ty;
1681   GtkTextDirection direction;
1682   GdkRectangle monitor;
1683   gint monitor_num;
1684   gint horizontal_offset;
1685   gint vertical_offset;
1686   gint parent_xthickness;
1687   gint available_left, available_right;
1688
1689   g_return_if_fail (menu != NULL);
1690   g_return_if_fail (x != NULL);
1691   g_return_if_fail (y != NULL);
1692
1693   menu_item = GTK_MENU_ITEM (user_data);
1694   widget = GTK_WIDGET (user_data);
1695
1696   if (push_in)
1697     *push_in = FALSE;
1698
1699   direction = gtk_widget_get_direction (widget);
1700
1701   twidth = GTK_WIDGET (menu)->requisition.width;
1702   theight = GTK_WIDGET (menu)->requisition.height;
1703
1704   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1705   monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
1706   if (monitor_num < 0)
1707     monitor_num = 0;
1708   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1709
1710   if (!gdk_window_get_origin (widget->window, &tx, &ty))
1711     {
1712       g_warning ("Menu not on screen");
1713       return;
1714     }
1715
1716   tx += widget->allocation.x;
1717   ty += widget->allocation.y;
1718
1719   get_offsets (menu, &horizontal_offset, &vertical_offset);
1720
1721   available_left = tx - monitor.x;
1722   available_right = monitor.x + monitor.width - (tx + widget->allocation.width);
1723
1724   if (GTK_IS_MENU_BAR (widget->parent))
1725     {
1726       menu_item->from_menubar = TRUE;
1727     }
1728   else if (GTK_IS_MENU (widget->parent))
1729     {
1730       if (GTK_MENU (widget->parent)->parent_menu_item)
1731         menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item)->from_menubar;
1732       else
1733         menu_item->from_menubar = FALSE;
1734     }
1735   else
1736     {
1737       menu_item->from_menubar = FALSE;
1738     }
1739   
1740   switch (menu_item->submenu_placement)
1741     {
1742     case GTK_TOP_BOTTOM:
1743       if (direction == GTK_TEXT_DIR_LTR)
1744         menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1745       else 
1746         {
1747           menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1748           tx += widget->allocation.width - twidth;
1749         }
1750       if ((ty + widget->allocation.height + theight) <= monitor.y + monitor.height)
1751         ty += widget->allocation.height;
1752       else if ((ty - theight) >= monitor.y)
1753         ty -= theight;
1754       else if (monitor.y + monitor.height - (ty + widget->allocation.height) > ty)
1755         ty += widget->allocation.height;
1756       else
1757         ty -= theight;
1758       break;
1759
1760     case GTK_LEFT_RIGHT:
1761       if (GTK_IS_MENU (widget->parent))
1762         parent_menu_item = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item);
1763       else
1764         parent_menu_item = NULL;
1765       
1766       parent_xthickness = widget->parent->style->xthickness;
1767
1768       if (parent_menu_item && !GTK_MENU (widget->parent)->torn_off)
1769         {
1770           menu_item->submenu_direction = parent_menu_item->submenu_direction;
1771         }
1772       else
1773         {
1774           if (direction == GTK_TEXT_DIR_LTR)
1775             menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1776           else
1777             menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1778         }
1779
1780       switch (menu_item->submenu_direction)
1781         {
1782         case GTK_DIRECTION_LEFT:
1783           if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
1784               available_left >= available_right)
1785             tx -= twidth + parent_xthickness + horizontal_offset;
1786           else
1787             {
1788               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1789               tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1790             }
1791           break;
1792
1793         case GTK_DIRECTION_RIGHT:
1794           if (tx + widget->allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
1795               available_right >= available_left)
1796             tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1797           else
1798             {
1799               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1800               tx -= twidth + parent_xthickness + horizontal_offset;
1801             }
1802           break;
1803         }
1804
1805       ty += vertical_offset;
1806       
1807       /* If the height of the menu doesn't fit we move it upward. */
1808       ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
1809       break;
1810     }
1811
1812   /* If we have negative, tx, here it is because we can't get
1813    * the menu all the way on screen. Favor the left portion.
1814    */
1815   *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
1816   *y = ty;
1817
1818   gtk_menu_set_monitor (menu, monitor_num);
1819
1820   if (!GTK_WIDGET_VISIBLE (menu->toplevel))
1821     {
1822       gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
1823                                 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
1824     }
1825 }
1826
1827 /**
1828  * gtk_menu_item_set_right_justified:
1829  * @menu_item: a #GtkMenuItem.
1830  * @right_justified: if %TRUE the menu item will appear at the 
1831  *   far right if added to a menu bar.
1832  * 
1833  * Sets whether the menu item appears justified at the right
1834  * side of a menu bar. This was traditionally done for "Help" menu
1835  * items, but is now considered a bad idea. (If the widget
1836  * layout is reversed for a right-to-left language like Hebrew
1837  * or Arabic, right-justified-menu-items appear at the left.)
1838  **/
1839 void
1840 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
1841                                    gboolean     right_justified)
1842 {
1843   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1844
1845   right_justified = right_justified != FALSE;
1846
1847   if (right_justified != menu_item->right_justify)
1848     {
1849       menu_item->right_justify = right_justified;
1850       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1851     }
1852 }
1853
1854 /**
1855  * gtk_menu_item_get_right_justified:
1856  * @menu_item: a #GtkMenuItem
1857  * 
1858  * Gets whether the menu item appears justified at the right
1859  * side of the menu bar.
1860  * 
1861  * Return value: %TRUE if the menu item will appear at the
1862  *   far right if added to a menu bar.
1863  **/
1864 gboolean
1865 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
1866 {
1867   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
1868   
1869   return menu_item->right_justify;
1870 }
1871
1872
1873 static void
1874 gtk_menu_item_show_all (GtkWidget *widget)
1875 {
1876   GtkMenuItem *menu_item;
1877
1878   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1879
1880   menu_item = GTK_MENU_ITEM (widget);
1881
1882   /* show children including submenu */
1883   if (menu_item->submenu)
1884     gtk_widget_show_all (menu_item->submenu);
1885   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1886
1887   gtk_widget_show (widget);
1888 }
1889
1890 static void
1891 gtk_menu_item_hide_all (GtkWidget *widget)
1892 {
1893   GtkMenuItem *menu_item;
1894
1895   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1896
1897   gtk_widget_hide (widget);
1898
1899   menu_item = GTK_MENU_ITEM (widget);
1900
1901   /* hide children including submenu */
1902   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1903   if (menu_item->submenu)
1904     gtk_widget_hide_all (menu_item->submenu);
1905 }
1906
1907 static gboolean
1908 gtk_menu_item_can_activate_accel (GtkWidget *widget,
1909                                   guint      signal_id)
1910 {
1911   /* Chain to the parent GtkMenu for further checks */
1912   return (GTK_WIDGET_IS_SENSITIVE (widget) && GTK_WIDGET_VISIBLE (widget) &&
1913           widget->parent && gtk_widget_can_activate_accel (widget->parent, signal_id));
1914 }
1915
1916 static void
1917 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
1918                                   gpointer data)
1919 {
1920   const gchar **path_p = data;
1921
1922   if (!*path_p)
1923     {
1924       if (GTK_IS_LABEL (widget))
1925         {
1926           *path_p = gtk_label_get_text (GTK_LABEL (widget));
1927           if (*path_p && (*path_p)[0] == 0)
1928             *path_p = NULL;
1929         }
1930       else if (GTK_IS_CONTAINER (widget))
1931         gtk_container_foreach (GTK_CONTAINER (widget),
1932                                gtk_menu_item_accel_name_foreach,
1933                                data);
1934     }
1935 }
1936
1937 static void
1938 gtk_menu_item_parent_set (GtkWidget *widget,
1939                           GtkWidget *previous_parent)
1940 {
1941   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1942   GtkMenu *menu = GTK_IS_MENU (widget->parent) ? GTK_MENU (widget->parent) : NULL;
1943
1944   if (menu)
1945     _gtk_menu_item_refresh_accel_path (menu_item,
1946                                        menu->accel_path,
1947                                        menu->accel_group,
1948                                        TRUE);
1949
1950   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
1951     GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
1952 }
1953
1954 void
1955 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
1956                                    const gchar   *prefix,
1957                                    GtkAccelGroup *accel_group,
1958                                    gboolean       group_changed)
1959 {
1960   const gchar *path;
1961   GtkWidget *widget;
1962
1963   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1964   g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1965
1966   widget = GTK_WIDGET (menu_item);
1967
1968   if (!accel_group)
1969     {
1970       gtk_widget_set_accel_path (widget, NULL, NULL);
1971       return;
1972     }
1973
1974   path = _gtk_widget_get_accel_path (widget, NULL);
1975   if (!path)                                    /* no active accel_path yet */
1976     {
1977       path = menu_item->accel_path;
1978       if (!path && prefix)
1979         {
1980           const gchar *postfix = NULL;
1981           gchar *new_path;
1982
1983           /* try to construct one from label text */
1984           gtk_container_foreach (GTK_CONTAINER (menu_item),
1985                                  gtk_menu_item_accel_name_foreach,
1986                                  &postfix);
1987           if (postfix)
1988             {
1989               new_path = g_strconcat (prefix, "/", postfix, NULL);
1990               path = menu_item->accel_path = (char*)g_intern_string (new_path);
1991               g_free (new_path);
1992             }
1993         }
1994       if (path)
1995         gtk_widget_set_accel_path (widget, path, accel_group);
1996     }
1997   else if (group_changed)                       /* reinstall accelerators */
1998     gtk_widget_set_accel_path (widget, path, accel_group);
1999 }
2000
2001 /**
2002  * gtk_menu_item_set_accel_path
2003  * @menu_item:  a valid #GtkMenuItem
2004  * @accel_path: accelerator path, corresponding to this menu item's
2005  *              functionality, or %NULL to unset the current path.
2006  *
2007  * Set the accelerator path on @menu_item, through which runtime changes of the
2008  * menu item's accelerator caused by the user can be identified and saved to
2009  * persistant storage (see gtk_accel_map_save() on this).
2010  * To setup a default accelerator for this menu item, call
2011  * gtk_accel_map_add_entry() with the same @accel_path.
2012  * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
2013  * and gtk_menu_set_accel_path() for a more convenient variant of this function.
2014  *
2015  * This function is basically a convenience wrapper that handles calling
2016  * gtk_widget_set_accel_path() with the appropriate accelerator group for
2017  * the menu item.
2018  *
2019  * Note that you do need to set an accelerator on the parent menu with
2020  * gtk_menu_set_accel_group() for this to work.
2021  *
2022  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
2023  * pass a static string, you can save some memory by interning it first with 
2024  * g_intern_static_string().
2025  */
2026 void
2027 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2028                               const gchar *accel_path)
2029 {
2030   GtkWidget *widget;
2031
2032   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2033   g_return_if_fail (accel_path == NULL ||
2034                     (accel_path[0] == '<' && strchr (accel_path, '/')));
2035
2036   widget = GTK_WIDGET (menu_item);
2037
2038   /* store new path */
2039   menu_item->accel_path = (char*)g_intern_string (accel_path);
2040
2041   /* forget accelerators associated with old path */
2042   gtk_widget_set_accel_path (widget, NULL, NULL);
2043
2044   /* install accelerators associated with new path */
2045   if (GTK_IS_MENU (widget->parent))
2046     {
2047       GtkMenu *menu = GTK_MENU (widget->parent);
2048
2049       if (menu->accel_group)
2050         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2051                                            NULL,
2052                                            menu->accel_group,
2053                                            FALSE);
2054     }
2055 }
2056
2057 /**
2058  * gtk_menu_item_get_accel_path
2059  * @menu_item:  a valid #GtkMenuItem
2060  *
2061  * Retrieve the accelerator path that was previously set on @menu_item.
2062  *
2063  * See gtk_menu_item_set_accel_path() for details.
2064  *
2065  * Returns: the accelerator path corresponding to this menu item's
2066  *              functionality, or %NULL if not set
2067  *
2068  * Since: 2.14
2069  */
2070 G_CONST_RETURN gchar *
2071 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2072 {
2073   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2074
2075   return menu_item->accel_path;
2076 }
2077
2078 static void
2079 gtk_menu_item_forall (GtkContainer *container,
2080                       gboolean      include_internals,
2081                       GtkCallback   callback,
2082                       gpointer      callback_data)
2083 {
2084   GtkBin *bin;
2085
2086   g_return_if_fail (GTK_IS_MENU_ITEM (container));
2087   g_return_if_fail (callback != NULL);
2088
2089   bin = GTK_BIN (container);
2090
2091   if (bin->child)
2092     callback (bin->child, callback_data);
2093 }
2094
2095 gboolean
2096 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2097 {
2098   if ((!GTK_BIN (menu_item)->child &&
2099        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2100       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2101       !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
2102       !GTK_WIDGET_VISIBLE (menu_item))
2103     return FALSE;
2104
2105   return TRUE;
2106 }
2107
2108 static void
2109 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2110 {
2111   GtkWidget *accel_label;
2112
2113   if (!GTK_BIN (menu_item)->child)
2114     {
2115       accel_label = (GtkWidget *)g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2116       gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2117
2118       gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2119       gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), 
2120                                         GTK_WIDGET (menu_item));
2121       gtk_widget_show (accel_label);
2122     }
2123 }
2124
2125 /**
2126  * gtk_menu_item_set_label:
2127  * @menu_item: a #GtkMenuItem
2128  * @label: the text you want to set
2129  *
2130  * Sets @text on the @menu_item label
2131  *
2132  * Since: 2.16
2133  **/
2134 void
2135 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2136                          const gchar *label)
2137 {
2138   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2139
2140   GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2141 }
2142
2143 /**
2144  * gtk_menu_item_get_label:
2145  * @menu_item: a #GtkMenuItem
2146  *
2147  * Sets @text on the @menu_item label
2148  *
2149  * Returns: The text in the @menu_item label. This is the internal
2150  *   string used by the label, and must not be modified.
2151  *
2152  * Since: 2.16
2153  **/
2154 G_CONST_RETURN gchar *
2155 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2156 {
2157   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2158
2159   return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2160 }
2161
2162 /**
2163  * gtk_menu_item_set_use_underline:
2164  * @menu_item: a #GtkMenuItem
2165  * @setting: %TRUE if underlines in the text indicate mnemonics  
2166  *
2167  * If true, an underline in the text indicates the next character should be
2168  * used for the mnemonic accelerator key.
2169  *
2170  * Since: 2.16
2171  **/
2172 void
2173 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2174                                  gboolean     setting)
2175 {
2176   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2177
2178   gtk_menu_item_ensure_label (menu_item);
2179
2180   if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2181     {
2182       gtk_label_set_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child), setting);
2183
2184       g_object_notify (G_OBJECT (menu_item), "use-underline");
2185     }
2186 }
2187
2188 /**
2189  * gtk_menu_item_get_use_underline:
2190  * @menu_item: a #GtkMenuItem
2191  *
2192  * Checks if an underline in the text indicates the next character should be
2193  * used for the mnemonic accelerator key.
2194  *
2195  * Return value: %TRUE if an embedded underline in the label indicates
2196  *               the mnemonic accelerator key.
2197  *
2198  * Since: 2.16
2199  **/
2200 gboolean
2201 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2202 {
2203   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2204
2205   gtk_menu_item_ensure_label (menu_item);
2206   
2207   if (GTK_IS_LABEL (GTK_BIN (menu_item)->child))
2208     return gtk_label_get_use_underline (GTK_LABEL (GTK_BIN (menu_item)->child));
2209
2210   return FALSE;
2211 }
2212
2213
2214
2215 #define __GTK_MENU_ITEM_C__
2216 #include "gtkaliasdef.c"