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