]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Add missing Since: gtk-doc markup for newly added GSEAL API. Change all
[~andy/gtk] / gtk / gtkmenuitem.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #define GTK_MENU_INTERNALS
28
29 #include <config.h>
30 #include <string.h>
31
32 #include "gtkaccellabel.h"
33 #include "gtkmain.h"
34 #include "gtkmarshalers.h"
35 #include "gtkmenu.h"
36 #include "gtkmenubar.h"
37 #include "gtkmenuitem.h"
38 #include "gtkseparatormenuitem.h"
39 #include "gtkprivate.h"
40 #include "gtkintl.h"
41 #include "gtkalias.h"
42
43 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
44
45 enum {
46   ACTIVATE,
47   ACTIVATE_ITEM,
48   TOGGLE_SIZE_REQUEST,
49   TOGGLE_SIZE_ALLOCATE,
50   LAST_SIGNAL
51 };
52
53 enum {
54   PROP_0,
55   PROP_RIGHT_JUSTIFIED,
56   PROP_SUBMENU,
57   PROP_ACCEL_PATH
58 };
59
60
61 static void gtk_menu_item_set_property   (GObject          *object,
62                                           guint             prop_id,
63                                           const GValue     *value,
64                                           GParamSpec       *pspec);
65 static void gtk_menu_item_get_property   (GObject          *object,
66                                           guint             prop_id,
67                                           GValue           *value,
68                                           GParamSpec       *pspec);
69 static void gtk_menu_item_destroy        (GtkObject        *object);
70 static void gtk_menu_item_size_request   (GtkWidget        *widget,
71                                           GtkRequisition   *requisition);
72 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
73                                           GtkAllocation    *allocation);
74 static void gtk_menu_item_realize        (GtkWidget        *widget);
75 static void gtk_menu_item_unrealize      (GtkWidget        *widget);
76 static void gtk_menu_item_map            (GtkWidget        *widget);
77 static void gtk_menu_item_unmap          (GtkWidget        *widget);
78 static void gtk_menu_item_paint          (GtkWidget        *widget,
79                                           GdkRectangle     *area);
80 static gint gtk_menu_item_expose         (GtkWidget        *widget,
81                                           GdkEventExpose   *event);
82 static void gtk_menu_item_parent_set     (GtkWidget        *widget,
83                                           GtkWidget        *previous_parent);
84
85
86 static void gtk_real_menu_item_select               (GtkItem     *item);
87 static void gtk_real_menu_item_deselect             (GtkItem     *item);
88 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
89 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
90                                                      gint        *requisition);
91 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
92                                                      gint         allocation);
93 static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
94                                                      gboolean     group_cycling);
95
96 static gint gtk_menu_item_popup_timeout  (gpointer          data);
97 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
98                                           gint             *x,
99                                           gint             *y,
100                                           gboolean         *push_in,
101                                           gpointer          user_data);
102 static void gtk_menu_item_show_all       (GtkWidget        *widget);
103 static void gtk_menu_item_hide_all       (GtkWidget        *widget);
104 static void gtk_menu_item_forall         (GtkContainer    *container,
105                                           gboolean         include_internals,
106                                           GtkCallback      callback,
107                                           gpointer         callback_data);
108 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
109                                                   guint      signal_id);
110
111
112 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
113
114 G_DEFINE_TYPE (GtkMenuItem, gtk_menu_item, GTK_TYPE_ITEM)
115
116 static void
117 gtk_menu_item_class_init (GtkMenuItemClass *klass)
118 {
119   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
120   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
121   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
122   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
123   GtkItemClass *item_class = GTK_ITEM_CLASS (klass);
124
125   gobject_class->set_property = gtk_menu_item_set_property;
126   gobject_class->get_property = gtk_menu_item_get_property;
127
128   object_class->destroy = gtk_menu_item_destroy;
129
130   widget_class->size_request = gtk_menu_item_size_request;
131   widget_class->size_allocate = gtk_menu_item_size_allocate;
132   widget_class->expose_event = gtk_menu_item_expose;
133   widget_class->realize = gtk_menu_item_realize;
134   widget_class->unrealize = gtk_menu_item_unrealize;
135   widget_class->map = gtk_menu_item_map;
136   widget_class->unmap = gtk_menu_item_unmap;
137   widget_class->show_all = gtk_menu_item_show_all;
138   widget_class->hide_all = gtk_menu_item_hide_all;
139   widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
140   widget_class->parent_set = gtk_menu_item_parent_set;
141   widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
142   
143   container_class->forall = gtk_menu_item_forall;
144
145   item_class->select = gtk_real_menu_item_select;
146   item_class->deselect = gtk_real_menu_item_deselect;
147
148   klass->activate = NULL;
149   klass->activate_item = gtk_real_menu_item_activate_item;
150   klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
151   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
152
153   klass->hide_on_activate = TRUE;
154
155   menu_item_signals[ACTIVATE] =
156     g_signal_new (I_("activate"),
157                   G_OBJECT_CLASS_TYPE (gobject_class),
158                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
159                   G_STRUCT_OFFSET (GtkMenuItemClass, activate),
160                   NULL, NULL,
161                   _gtk_marshal_VOID__VOID,
162                   G_TYPE_NONE, 0);
163   widget_class->activate_signal = menu_item_signals[ACTIVATE];
164
165   menu_item_signals[ACTIVATE_ITEM] =
166     g_signal_new (I_("activate_item"),
167                   G_OBJECT_CLASS_TYPE (gobject_class),
168                   G_SIGNAL_RUN_FIRST,
169                   G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
170                   NULL, NULL,
171                   _gtk_marshal_VOID__VOID,
172                   G_TYPE_NONE, 0);
173
174   menu_item_signals[TOGGLE_SIZE_REQUEST] =
175     g_signal_new (I_("toggle_size_request"),
176                   G_OBJECT_CLASS_TYPE (gobject_class),
177                   G_SIGNAL_RUN_FIRST,
178                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
179                   NULL, NULL,
180                   _gtk_marshal_VOID__POINTER,
181                   G_TYPE_NONE, 1,
182                   G_TYPE_POINTER);
183
184   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
185     g_signal_new (I_("toggle_size_allocate"),
186                   G_OBJECT_CLASS_TYPE (gobject_class),
187                   G_SIGNAL_RUN_FIRST,
188                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
189                   NULL, NULL,
190                   _gtk_marshal_NONE__INT,
191                   G_TYPE_NONE, 1,
192                   G_TYPE_INT);
193
194   /**
195    * GtkMenuItem:right-justified:
196    *
197    * Sets whether the menu item appears justified at the right side of a menu bar.
198    *
199    * Since: 2.14
200    **/
201   g_object_class_install_property (gobject_class,
202                                    PROP_RIGHT_JUSTIFIED,
203                                    g_param_spec_boolean ("right-justified",
204                                                          P_("Right Justified"),
205                                                          P_("Sets whether the menu item appears justified at the right side of a menu bar"),
206                                                          FALSE,
207                                                          GTK_PARAM_READWRITE));
208
209   /**
210    * GtkMenuItem:submenu:
211    *
212    * The submenu attached to the menu item, or NULL if it has none.
213    *
214    * Since: 2.12
215    **/
216   g_object_class_install_property (gobject_class,
217                                    PROP_SUBMENU,
218                                    g_param_spec_object ("submenu",
219                                                         P_("Submenu"),
220                                                         P_("The submenu attached to the menu item, or NULL if it has none"),
221                                                         GTK_TYPE_MENU,
222                                                         GTK_PARAM_READWRITE));
223
224   /**
225    * GtkMenuItem:accel-path:
226    *
227    * Sets the accelerator path of the menu item, through which runtime
228    * changes of the menu item's accelerator caused by the user can be
229    * identified and saved to persistant storage.
230    *
231    * Since: 2.14
232    **/
233   g_object_class_install_property (gobject_class,
234                                    PROP_ACCEL_PATH,
235                                    g_param_spec_string ("accel-path",
236                                                         P_("Accel Path"),
237                                                         P_("Sets the accelerator path of the menu item"),
238                                                         NULL,
239                                                         GTK_PARAM_READWRITE));
240
241   gtk_widget_class_install_style_property_parser (widget_class,
242                                                   g_param_spec_enum ("selected-shadow-type",
243                                                                      "Selected Shadow Type",
244                                                                      "Shadow type when item is selected",
245                                                                      GTK_TYPE_SHADOW_TYPE,
246                                                                      GTK_SHADOW_NONE,
247                                                                      GTK_PARAM_READABLE),
248                                                   gtk_rc_property_parse_enum);
249
250   gtk_widget_class_install_style_property (widget_class,
251                                            g_param_spec_int ("horizontal-padding",
252                                                              "Horizontal Padding",
253                                                              "Padding to left and right of the menu item",
254                                                              0,
255                                                              G_MAXINT,
256                                                              3,
257                                                              GTK_PARAM_READABLE));
258
259   gtk_widget_class_install_style_property (widget_class,
260                                            g_param_spec_int ("toggle-spacing",
261                                                              "Icon Spacing",
262                                                              "Space between icon and label",
263                                                              0,
264                                                              G_MAXINT,
265                                                              5,
266                                                              GTK_PARAM_READABLE));
267
268   gtk_widget_class_install_style_property (widget_class,
269                                            g_param_spec_int ("arrow-spacing",
270                                                              "Arrow Spacing",
271                                                              "Space between label and arrow",
272                                                              0,
273                                                              G_MAXINT,
274                                                              10,
275                                                              GTK_PARAM_READABLE));
276
277   gtk_widget_class_install_style_property (widget_class,
278                                            g_param_spec_float ("arrow-scaling",
279                                                                P_("Arrow Scaling"),
280                                                                P_("Amount of space used up by arrow, relative to the menu item's font size"),
281                                                                0.0, 2.0, 0.8,
282                                                                GTK_PARAM_READABLE));
283 }
284
285 static void
286 gtk_menu_item_init (GtkMenuItem *menu_item)
287 {
288   GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
289   
290   menu_item->submenu = NULL;
291   menu_item->toggle_size = 0;
292   menu_item->accelerator_width = 0;
293   menu_item->show_submenu_indicator = FALSE;
294   if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
295     menu_item->submenu_direction = GTK_DIRECTION_LEFT;
296   else
297     menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
298   menu_item->submenu_placement = GTK_TOP_BOTTOM;
299   menu_item->right_justify = FALSE;
300
301   menu_item->timer = 0;
302 }
303
304 GtkWidget*
305 gtk_menu_item_new (void)
306 {
307   return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
308 }
309
310 GtkWidget*
311 gtk_menu_item_new_with_label (const gchar *label)
312 {
313   GtkWidget *menu_item;
314   GtkWidget *accel_label;
315
316   menu_item = gtk_menu_item_new ();
317   accel_label = gtk_accel_label_new (label);
318   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
319
320   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
321   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
322   gtk_widget_show (accel_label);
323
324   return menu_item;
325 }
326
327
328 /**
329  * gtk_menu_item_new_with_mnemonic:
330  * @label: The text of the button, with an underscore in front of the
331  *         mnemonic character
332  * @returns: a new #GtkMenuItem
333  *
334  * Creates a new #GtkMenuItem containing a label. The label
335  * will be created using gtk_label_new_with_mnemonic(), so underscores
336  * in @label indicate the mnemonic for the menu item.
337  **/
338 GtkWidget*
339 gtk_menu_item_new_with_mnemonic (const gchar *label)
340 {
341   GtkWidget *menu_item;
342   GtkWidget *accel_label;
343
344   menu_item = gtk_menu_item_new ();
345   accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
346   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
347   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
348
349   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
350   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
351   gtk_widget_show (accel_label);
352
353   return menu_item;
354 }
355
356 static void 
357 gtk_menu_item_set_property (GObject      *object,
358                             guint         prop_id,
359                             const GValue *value,
360                             GParamSpec   *pspec)
361 {
362   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
363   
364   switch (prop_id)
365     {
366     case PROP_RIGHT_JUSTIFIED:
367       gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
368       break;
369     case PROP_SUBMENU:
370       gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
371       break;
372     case PROP_ACCEL_PATH:
373       gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
374       break;
375
376     default:
377       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378       break;
379     }
380 }
381
382 static void 
383 gtk_menu_item_get_property (GObject    *object,
384                             guint       prop_id,
385                             GValue     *value,
386                             GParamSpec *pspec)
387 {
388   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
389   
390   switch (prop_id)
391     {
392     case PROP_RIGHT_JUSTIFIED:
393       g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
394       break;
395     case PROP_SUBMENU:
396       g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
397       break;
398     case PROP_ACCEL_PATH:
399       g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
400       break;
401
402     default:
403       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
404       break;
405     }
406 }
407
408 static void
409 gtk_menu_item_destroy (GtkObject *object)
410 {
411   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
412
413   if (menu_item->submenu)
414     gtk_widget_destroy (menu_item->submenu);
415
416   GTK_OBJECT_CLASS (gtk_menu_item_parent_class)->destroy (object);
417 }
418
419 static void
420 gtk_menu_item_detacher (GtkWidget *widget,
421                         GtkMenu   *menu)
422 {
423   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
424
425   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
426
427   menu_item->submenu = NULL;
428 }
429
430 /**
431  * gtk_menu_item_set_submenu:
432  * @menu_item: a #GtkMenuItem
433  * @submenu: the submenu, or %NULL
434  *
435  * Sets or replaces the menu item's submenu, or removes it when a %NULL
436  * submenu is passed.
437  **/
438 void
439 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
440                            GtkWidget   *submenu)
441 {
442   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
443   g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
444   
445   if (menu_item->submenu != submenu)
446     {
447       if (menu_item->submenu)
448         gtk_menu_detach (GTK_MENU (menu_item->submenu));
449
450       if (submenu)
451         {
452           menu_item->submenu = submenu;
453           gtk_menu_attach_to_widget (GTK_MENU (submenu),
454                                      GTK_WIDGET (menu_item),
455                                      gtk_menu_item_detacher);
456         }
457
458       if (GTK_WIDGET (menu_item)->parent)
459         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
460
461       g_object_notify (G_OBJECT (menu_item), "submenu");
462     }
463 }
464
465 /**
466  * gtk_menu_item_get_submenu:
467  * @menu_item: a #GtkMenuItem
468  *
469  * Gets the submenu underneath this menu item, if any. See
470  * gtk_menu_item_set_submenu().
471  *
472  * Return value: submenu for this menu item, or %NULL if none.
473  **/
474 GtkWidget *
475 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
476 {
477   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
478
479   return menu_item->submenu;
480 }
481
482 /**
483  * gtk_menu_item_remove_submenu:
484  * @menu_item: a #GtkMenuItem
485  *
486  * Removes the widget's submenu.
487  *
488  * Deprecated: 2.12: gtk_menu_item_remove_submenu() is deprecated and
489  *                   should not be used in newly written code. Use
490  *                   gtk_menu_item_set_submenu() instead.
491  **/
492 void
493 gtk_menu_item_remove_submenu (GtkMenuItem *menu_item)
494 {
495   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
496
497   gtk_menu_item_set_submenu (menu_item, NULL);
498 }
499
500 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
501                                    GtkSubmenuPlacement  placement);
502
503 void
504 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
505                              GtkSubmenuPlacement  placement)
506 {
507   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
508
509   menu_item->submenu_placement = placement;
510 }
511
512 void
513 gtk_menu_item_select (GtkMenuItem *menu_item)
514 {
515   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
516
517   gtk_item_select (GTK_ITEM (menu_item));
518
519   /* Enable themeing of the parent menu item depending on whether
520    * something is selected in its submenu
521    */
522   if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
523     {
524       GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
525
526       if (menu->parent_menu_item)
527         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
528     }
529 }
530
531 void
532 gtk_menu_item_deselect (GtkMenuItem *menu_item)
533 {
534   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
535
536   gtk_item_deselect (GTK_ITEM (menu_item));
537
538   /* Enable themeing of the parent menu item depending on whether
539    * something is selected in its submenu
540    */
541   if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
542     {
543       GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
544
545       if (menu->parent_menu_item)
546         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
547     }
548 }
549
550 void
551 gtk_menu_item_activate (GtkMenuItem *menu_item)
552 {
553   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
554   
555   g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
556 }
557
558 void
559 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
560                                    gint        *requisition)
561 {
562   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
563
564   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
565 }
566
567 void
568 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
569                                     gint         allocation)
570 {
571   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
572
573   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
574 }
575
576 static void
577 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
578                                    gpointer data)
579 {
580   guint *width = data;
581
582   if (GTK_IS_ACCEL_LABEL (widget))
583     {
584       guint w;
585
586       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
587       *width = MAX (*width, w);
588     }
589   else if (GTK_IS_CONTAINER (widget))
590     gtk_container_foreach (GTK_CONTAINER (widget),
591                            gtk_menu_item_accel_width_foreach,
592                            data);
593 }
594
595 static gint
596 get_minimum_width (GtkWidget *widget)
597 {
598   PangoContext *context;
599   PangoFontMetrics *metrics;
600   gint height;
601
602   context = gtk_widget_get_pango_context (widget);
603   metrics = pango_context_get_metrics (context,
604                                        widget->style->font_desc,
605                                        pango_context_get_language (context));
606
607   height = pango_font_metrics_get_ascent (metrics) +
608       pango_font_metrics_get_descent (metrics);
609   
610   pango_font_metrics_unref (metrics);
611
612   return PANGO_PIXELS (7 * height);
613 }
614
615 static void
616 gtk_menu_item_size_request (GtkWidget      *widget,
617                             GtkRequisition *requisition)
618 {
619   GtkMenuItem *menu_item;
620   GtkBin *bin;
621   guint accel_width;
622   guint horizontal_padding;
623   GtkPackDirection pack_dir;
624   GtkPackDirection child_pack_dir;
625
626   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
627   g_return_if_fail (requisition != NULL);
628
629   gtk_widget_style_get (widget,
630                         "horizontal-padding", &horizontal_padding,
631                         NULL);
632   
633   bin = GTK_BIN (widget);
634   menu_item = GTK_MENU_ITEM (widget);
635
636   if (GTK_IS_MENU_BAR (widget->parent))
637     {
638       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
639       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
640     }
641   else
642     {
643       pack_dir = GTK_PACK_DIRECTION_LTR;
644       child_pack_dir = GTK_PACK_DIRECTION_LTR;
645     }
646
647   requisition->width = (GTK_CONTAINER (widget)->border_width +
648                         widget->style->xthickness) * 2;
649   requisition->height = (GTK_CONTAINER (widget)->border_width +
650                          widget->style->ythickness) * 2;
651
652   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
653       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
654     requisition->width += 2 * horizontal_padding;
655   else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
656       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
657     requisition->height += 2 * horizontal_padding;
658
659   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
660     {
661       GtkRequisition child_requisition;
662       
663       gtk_widget_size_request (bin->child, &child_requisition);
664
665       requisition->width += child_requisition.width;
666       requisition->height += child_requisition.height;
667
668       if (menu_item->submenu && menu_item->show_submenu_indicator)
669         {
670           guint arrow_spacing;
671           
672           gtk_widget_style_get (widget,
673                                 "arrow-spacing", &arrow_spacing,
674                                 NULL);
675
676           requisition->width += child_requisition.height;
677           requisition->width += arrow_spacing;
678
679           requisition->width = MAX (requisition->width, get_minimum_width (widget));
680         }
681     }
682   else /* separator item */
683     {
684       gboolean wide_separators;
685       gint     separator_height;
686
687       gtk_widget_style_get (widget,
688                             "wide-separators",  &wide_separators,
689                             "separator-height", &separator_height,
690                             NULL);
691
692       if (wide_separators)
693         requisition->height += separator_height + widget->style->ythickness;
694       else
695         requisition->height += widget->style->ythickness * 2;
696     }
697
698   accel_width = 0;
699   gtk_container_foreach (GTK_CONTAINER (menu_item),
700                          gtk_menu_item_accel_width_foreach,
701                          &accel_width);
702   menu_item->accelerator_width = accel_width;
703 }
704
705 static void
706 gtk_menu_item_size_allocate (GtkWidget     *widget,
707                              GtkAllocation *allocation)
708 {
709   GtkMenuItem *menu_item;
710   GtkBin *bin;
711   GtkAllocation child_allocation;
712   GtkTextDirection direction;
713   GtkPackDirection pack_dir;
714   GtkPackDirection child_pack_dir;
715
716   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
717   g_return_if_fail (allocation != NULL);
718
719   menu_item = GTK_MENU_ITEM (widget);
720   bin = GTK_BIN (widget);
721   
722   direction = gtk_widget_get_direction (widget);
723
724   if (GTK_IS_MENU_BAR (widget->parent))
725     {
726       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (widget->parent));
727       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (widget->parent));
728     }
729   else
730     {
731       pack_dir = GTK_PACK_DIRECTION_LTR;
732       child_pack_dir = GTK_PACK_DIRECTION_LTR;
733     }
734     
735   widget->allocation = *allocation;
736
737   if (bin->child)
738     {
739       GtkRequisition child_requisition;
740       guint horizontal_padding;
741
742       gtk_widget_style_get (widget,
743                             "horizontal-padding", &horizontal_padding,
744                             NULL);
745
746       child_allocation.x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
747       child_allocation.y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
748
749       if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
750           (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
751         child_allocation.x += horizontal_padding;
752       else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
753                (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
754         child_allocation.y += horizontal_padding;
755       
756       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
757       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
758
759       if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
760           child_pack_dir == GTK_PACK_DIRECTION_RTL)
761         {
762           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
763             child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
764           child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
765         }
766       else
767         {
768           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
769             child_allocation.y += GTK_MENU_ITEM (widget)->toggle_size;
770           child_allocation.height -= GTK_MENU_ITEM (widget)->toggle_size;
771         }
772
773       child_allocation.x += widget->allocation.x;
774       child_allocation.y += widget->allocation.y;
775
776       gtk_widget_get_child_requisition (bin->child, &child_requisition);
777       if (menu_item->submenu && menu_item->show_submenu_indicator) 
778         {
779           if (direction == GTK_TEXT_DIR_RTL)
780             child_allocation.x += child_requisition.height;
781           child_allocation.width -= child_requisition.height;
782         }
783       
784       if (child_allocation.width < 1)
785         child_allocation.width = 1;
786
787       gtk_widget_size_allocate (bin->child, &child_allocation);
788     }
789
790   if (GTK_WIDGET_REALIZED (widget))
791     gdk_window_move_resize (menu_item->event_window,
792                             allocation->x, allocation->y,
793                             allocation->width, allocation->height);
794
795   if (menu_item->submenu)
796     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
797 }
798
799 static void
800 gtk_menu_item_realize (GtkWidget *widget)
801 {
802   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
803   GdkWindowAttr attributes;
804   gint attributes_mask;
805
806   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
807
808   widget->window = gtk_widget_get_parent_window (widget);
809   g_object_ref (widget->window);
810   
811   attributes.x = widget->allocation.x;
812   attributes.y = widget->allocation.y;
813   attributes.width = widget->allocation.width;
814   attributes.height = widget->allocation.height;
815   attributes.window_type = GDK_WINDOW_CHILD;
816   attributes.wclass = GDK_INPUT_ONLY;
817   attributes.event_mask = (gtk_widget_get_events (widget) |
818                            GDK_BUTTON_PRESS_MASK |
819                            GDK_BUTTON_RELEASE_MASK |
820                            GDK_ENTER_NOTIFY_MASK |
821                            GDK_LEAVE_NOTIFY_MASK |
822                            GDK_POINTER_MOTION_MASK);
823
824   attributes_mask = GDK_WA_X | GDK_WA_Y;
825   menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
826   gdk_window_set_user_data (menu_item->event_window, widget);
827
828   widget->style = gtk_style_attach (widget->style, widget->window);
829 }
830
831 static void
832 gtk_menu_item_unrealize (GtkWidget *widget)
833 {
834   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
835
836   gdk_window_set_user_data (menu_item->event_window, NULL);
837   gdk_window_destroy (menu_item->event_window);
838   menu_item->event_window = NULL;
839   
840   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize)
841     (* GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize) (widget);
842 }
843
844 static void
845 gtk_menu_item_map (GtkWidget *widget)
846 {
847   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
848   
849   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
850
851   gdk_window_show (menu_item->event_window);
852 }
853
854 static void
855 gtk_menu_item_unmap (GtkWidget *widget)
856 {
857   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
858     
859   gdk_window_hide (menu_item->event_window);
860
861   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
862 }
863
864 static void
865 gtk_menu_item_paint (GtkWidget    *widget,
866                      GdkRectangle *area)
867 {
868   GtkMenuItem *menu_item;
869   GtkStateType state_type;
870   GtkShadowType shadow_type, selected_shadow_type;
871   gint width, height;
872   gint x, y;
873   gint border_width = GTK_CONTAINER (widget)->border_width;
874
875   if (GTK_WIDGET_DRAWABLE (widget))
876     {
877       menu_item = GTK_MENU_ITEM (widget);
878
879       state_type = widget->state;
880       
881       x = widget->allocation.x + border_width;
882       y = widget->allocation.y + border_width;
883       width = widget->allocation.width - border_width * 2;
884       height = widget->allocation.height - border_width * 2;
885       
886       if ((state_type == GTK_STATE_PRELIGHT) &&
887           (GTK_BIN (menu_item)->child))
888         {
889           gtk_widget_style_get (widget,
890                                 "selected-shadow-type", &selected_shadow_type,
891                                 NULL);
892           gtk_paint_box (widget->style,
893                          widget->window,
894                          GTK_STATE_PRELIGHT,
895                          selected_shadow_type,
896                          area, widget, "menuitem",
897                          x, y, width, height);
898         }
899   
900       if (menu_item->submenu && menu_item->show_submenu_indicator)
901         {
902           gint arrow_x, arrow_y;
903           gint arrow_size;
904           gint arrow_extent;
905           guint horizontal_padding;
906           gfloat arrow_scaling;
907           GtkTextDirection direction;
908           GtkArrowType arrow_type;
909           PangoContext *context;
910           PangoFontMetrics *metrics;
911
912           direction = gtk_widget_get_direction (widget);
913       
914           gtk_widget_style_get (widget,
915                                 "horizontal-padding", &horizontal_padding,
916                                 "arrow-scaling", &arrow_scaling,
917                                 NULL);
918           
919           context = gtk_widget_get_pango_context (GTK_BIN (menu_item)->child);
920           metrics = pango_context_get_metrics (context, 
921                                                GTK_WIDGET (GTK_BIN (menu_item)->child)->style->font_desc,
922                                                pango_context_get_language (context));
923
924           arrow_size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
925                                       pango_font_metrics_get_descent (metrics)));
926
927           pango_font_metrics_unref (metrics);
928
929           arrow_extent = arrow_size * arrow_scaling;
930
931           shadow_type = GTK_SHADOW_OUT;
932           if (state_type == GTK_STATE_PRELIGHT)
933             shadow_type = GTK_SHADOW_IN;
934
935           if (direction == GTK_TEXT_DIR_LTR)
936             {
937               arrow_x = x + width - horizontal_padding - arrow_extent;
938               arrow_type = GTK_ARROW_RIGHT;
939             }
940           else
941             {
942               arrow_x = x + horizontal_padding;
943               arrow_type = GTK_ARROW_LEFT;
944             }
945
946           arrow_y = y + (height - arrow_extent) / 2;
947
948           gtk_paint_arrow (widget->style, widget->window,
949                            state_type, shadow_type, 
950                            area, widget, "menuitem", 
951                            arrow_type, TRUE,
952                            arrow_x, arrow_y,
953                            arrow_extent, arrow_extent);
954         }
955       else if (!GTK_BIN (menu_item)->child)
956         {
957           gboolean wide_separators;
958           gint     separator_height;
959           guint    horizontal_padding;
960
961           gtk_widget_style_get (widget,
962                                 "wide-separators",    &wide_separators,
963                                 "separator-height",   &separator_height,
964                                 "horizontal-padding", &horizontal_padding,
965                                 NULL);
966
967           if (wide_separators)
968             gtk_paint_box (widget->style, widget->window,
969                            GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
970                            area, widget, "hseparator",
971                            widget->allocation.x + horizontal_padding + widget->style->xthickness,
972                            widget->allocation.y + (widget->allocation.height -
973                                                    separator_height -
974                                                    widget->style->ythickness) / 2,
975                            widget->allocation.width -
976                            2 * (horizontal_padding + widget->style->xthickness),
977                            separator_height);
978           else
979             gtk_paint_hline (widget->style, widget->window,
980                              GTK_STATE_NORMAL, area, widget, "menuitem",
981                              widget->allocation.x + horizontal_padding + widget->style->xthickness,
982                              widget->allocation.x + widget->allocation.width - horizontal_padding - widget->style->xthickness - 1,
983                              widget->allocation.y + (widget->allocation.height -
984                                                      widget->style->ythickness) / 2);
985         }
986     }
987 }
988
989 static gint
990 gtk_menu_item_expose (GtkWidget      *widget,
991                       GdkEventExpose *event)
992 {
993   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
994   g_return_val_if_fail (event != NULL, FALSE);
995
996   if (GTK_WIDGET_DRAWABLE (widget))
997     {
998       gtk_menu_item_paint (widget, &event->area);
999
1000       (* GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->expose_event) (widget, event);
1001     }
1002
1003   return FALSE;
1004 }
1005
1006 static void
1007 gtk_real_menu_item_select (GtkItem *item)
1008 {
1009   GtkMenuItem *menu_item;
1010   gboolean touchscreen_mode;
1011
1012   g_return_if_fail (GTK_IS_MENU_ITEM (item));
1013
1014   menu_item = GTK_MENU_ITEM (item);
1015
1016   g_object_get (gtk_widget_get_settings (GTK_WIDGET (item)),
1017                 "gtk-touchscreen-mode", &touchscreen_mode,
1018                 NULL);
1019
1020   if (!touchscreen_mode &&
1021       menu_item->submenu &&
1022       (!GTK_WIDGET_MAPPED (menu_item->submenu) ||
1023        GTK_MENU (menu_item->submenu)->tearoff_active))
1024     {
1025       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1026     }
1027
1028   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
1029   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1030 }
1031
1032 static void
1033 gtk_real_menu_item_deselect (GtkItem *item)
1034 {
1035   GtkMenuItem *menu_item;
1036
1037   g_return_if_fail (GTK_IS_MENU_ITEM (item));
1038
1039   menu_item = GTK_MENU_ITEM (item);
1040
1041   if (menu_item->submenu)
1042     _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1043
1044   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
1045   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1046 }
1047
1048 static gboolean
1049 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1050                                  gboolean   group_cycling)
1051 {
1052   if (group_cycling &&
1053       widget->parent &&
1054       GTK_IS_MENU_SHELL (widget->parent) &&
1055       GTK_MENU_SHELL (widget->parent)->active)
1056     {
1057       gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
1058                                   widget);
1059     }
1060   else
1061     g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1062   
1063   return TRUE;
1064 }
1065
1066 static void
1067 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1068 {
1069   GtkWidget *widget;
1070
1071   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1072
1073   widget = GTK_WIDGET (menu_item);
1074   
1075   if (widget->parent &&
1076       GTK_IS_MENU_SHELL (widget->parent))
1077     {
1078       if (menu_item->submenu == NULL)
1079         gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
1080                                       widget, TRUE);
1081       else
1082         {
1083           GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
1084
1085           _gtk_menu_shell_activate (menu_shell);
1086
1087           gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
1088           _gtk_menu_item_popup_submenu (widget, FALSE);
1089
1090           gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1091         }
1092     }
1093 }
1094
1095 static void
1096 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1097                                         gint        *requisition)
1098 {
1099   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1100
1101   *requisition = 0;
1102 }
1103
1104 static void
1105 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1106                                          gint         allocation)
1107 {
1108   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1109
1110   menu_item->toggle_size = allocation;
1111 }
1112
1113 static void
1114 free_timeval (GTimeVal *val)
1115 {
1116   g_slice_free (GTimeVal, val);
1117 }
1118
1119 static void
1120 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1121                                   gboolean   remember_exact_time)
1122 {
1123   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1124
1125   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
1126     {
1127       gboolean take_focus;
1128
1129       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent));
1130       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1131                                      take_focus);
1132
1133       if (remember_exact_time)
1134         {
1135           GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1136
1137           g_get_current_time (popup_time);
1138
1139           g_object_set_data_full (G_OBJECT (menu_item->submenu),
1140                                   "gtk-menu-exact-popup-time", popup_time,
1141                                   (GDestroyNotify) free_timeval);
1142         }
1143       else
1144         {
1145           g_object_set_data (G_OBJECT (menu_item->submenu),
1146                              "gtk-menu-exact-popup-time", NULL);
1147         }
1148
1149       gtk_menu_popup (GTK_MENU (menu_item->submenu),
1150                       widget->parent,
1151                       widget,
1152                       gtk_menu_item_position_menu,
1153                       menu_item,
1154                       GTK_MENU_SHELL (widget->parent)->button,
1155                       0);
1156     }
1157
1158   /* Enable themeing of the parent menu item depending on whether
1159    * its submenu is shown or not.
1160    */
1161   gtk_widget_queue_draw (widget);
1162 }
1163
1164 static gint
1165 gtk_menu_item_popup_timeout (gpointer data)
1166 {
1167   GtkMenuItem *menu_item;
1168   GtkWidget *parent;
1169   
1170   menu_item = GTK_MENU_ITEM (data);
1171
1172   parent = GTK_WIDGET (menu_item)->parent;
1173
1174   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) || 
1175       (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1176     {
1177       gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1178       if (menu_item->timer_from_keypress && menu_item->submenu)
1179         GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1180     }
1181
1182   menu_item->timer = 0;
1183
1184   return FALSE;  
1185 }
1186
1187 static gint
1188 get_popup_delay (GtkWidget *widget)
1189 {
1190   if (GTK_IS_MENU_SHELL (widget->parent))
1191     {
1192       return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (widget->parent));
1193     }
1194   else
1195     {
1196       gint popup_delay;
1197
1198       g_object_get (gtk_widget_get_settings (widget),
1199                     "gtk-menu-popup-delay", &popup_delay,
1200                     NULL);
1201
1202       return popup_delay;
1203     }
1204 }
1205
1206 void
1207 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1208                               gboolean   with_delay)
1209 {
1210   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1211
1212   if (menu_item->timer)
1213     {
1214       g_source_remove (menu_item->timer);
1215       menu_item->timer = 0;
1216       with_delay = FALSE;
1217     }
1218
1219   if (with_delay)
1220     {
1221       gint popup_delay = get_popup_delay (widget);
1222
1223       if (popup_delay > 0)
1224         {
1225           GdkEvent *event = gtk_get_current_event ();
1226
1227           menu_item->timer = gdk_threads_add_timeout (popup_delay,
1228                                                       gtk_menu_item_popup_timeout,
1229                                                       menu_item);
1230
1231           if (event &&
1232               event->type != GDK_BUTTON_PRESS &&
1233               event->type != GDK_ENTER_NOTIFY)
1234             menu_item->timer_from_keypress = TRUE;
1235           else
1236             menu_item->timer_from_keypress = FALSE;
1237
1238           if (event)
1239             gdk_event_free (event);
1240
1241           return;
1242         }
1243     }
1244
1245   gtk_menu_item_real_popup_submenu (widget, FALSE);
1246 }
1247
1248 void
1249 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1250 {
1251   GtkMenuItem *menu_item;
1252
1253   menu_item = GTK_MENU_ITEM (widget);
1254
1255   if (menu_item->submenu)
1256     {
1257       g_object_set_data (G_OBJECT (menu_item->submenu),
1258                          "gtk-menu-exact-popup-time", NULL);
1259
1260       if (menu_item->timer)
1261         {
1262           g_source_remove (menu_item->timer);
1263           menu_item->timer = 0;
1264         }
1265       else
1266         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1267
1268       gtk_widget_queue_draw (widget);
1269     }
1270 }
1271
1272 static void
1273 get_offsets (GtkMenu *menu,
1274              gint    *horizontal_offset,
1275              gint    *vertical_offset)
1276 {
1277   gint vertical_padding;
1278   gint horizontal_padding;
1279   
1280   gtk_widget_style_get (GTK_WIDGET (menu),
1281                         "horizontal-offset", horizontal_offset,
1282                         "vertical-offset", vertical_offset,
1283                         "horizontal-padding", &horizontal_padding,
1284                         "vertical-padding", &vertical_padding,
1285                         NULL);
1286
1287   *vertical_offset -= GTK_WIDGET (menu)->style->ythickness;
1288   *vertical_offset -= vertical_padding;
1289   *horizontal_offset += horizontal_padding;
1290 }
1291
1292 static void
1293 gtk_menu_item_position_menu (GtkMenu  *menu,
1294                              gint     *x,
1295                              gint     *y,
1296                              gboolean *push_in,
1297                              gpointer  user_data)
1298 {
1299   GtkMenuItem *menu_item;
1300   GtkWidget *widget;
1301   GtkMenuItem *parent_menu_item;
1302   GdkScreen *screen;
1303   gint twidth, theight;
1304   gint tx, ty;
1305   GtkTextDirection direction;
1306   GdkRectangle monitor;
1307   gint monitor_num;
1308   gint horizontal_offset;
1309   gint vertical_offset;
1310   gint parent_xthickness;
1311   gint available_left, available_right;
1312
1313   g_return_if_fail (menu != NULL);
1314   g_return_if_fail (x != NULL);
1315   g_return_if_fail (y != NULL);
1316
1317   menu_item = GTK_MENU_ITEM (user_data);
1318   widget = GTK_WIDGET (user_data);
1319
1320   if (push_in)
1321     *push_in = FALSE;
1322
1323   direction = gtk_widget_get_direction (widget);
1324
1325   twidth = GTK_WIDGET (menu)->requisition.width;
1326   theight = GTK_WIDGET (menu)->requisition.height;
1327
1328   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1329   monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
1330   if (monitor_num < 0)
1331     monitor_num = 0;
1332   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1333
1334   if (!gdk_window_get_origin (widget->window, &tx, &ty))
1335     {
1336       g_warning ("Menu not on screen");
1337       return;
1338     }
1339
1340   tx += widget->allocation.x;
1341   ty += widget->allocation.y;
1342
1343   get_offsets (menu, &horizontal_offset, &vertical_offset);
1344
1345   available_left = tx - monitor.x;
1346   available_right = monitor.x + monitor.width - (tx + widget->allocation.width);
1347
1348   if (GTK_IS_MENU_BAR (widget->parent))
1349     {
1350       menu_item->from_menubar = TRUE;
1351     }
1352   else if (GTK_IS_MENU (widget->parent))
1353     {
1354       if (GTK_MENU (widget->parent)->parent_menu_item)
1355         menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item)->from_menubar;
1356       else
1357         menu_item->from_menubar = FALSE;
1358     }
1359   else
1360     {
1361       menu_item->from_menubar = FALSE;
1362     }
1363   
1364   switch (menu_item->submenu_placement)
1365     {
1366     case GTK_TOP_BOTTOM:
1367       if (direction == GTK_TEXT_DIR_LTR)
1368         menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1369       else 
1370         {
1371           menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1372           tx += widget->allocation.width - twidth;
1373         }
1374       if ((ty + widget->allocation.height + theight) <= monitor.y + monitor.height)
1375         ty += widget->allocation.height;
1376       else if ((ty - theight) >= monitor.y)
1377         ty -= theight;
1378       else if (monitor.y + monitor.height - (ty + widget->allocation.height) > ty)
1379         ty += widget->allocation.height;
1380       else
1381         ty -= theight;
1382       break;
1383
1384     case GTK_LEFT_RIGHT:
1385       if (GTK_IS_MENU (widget->parent))
1386         parent_menu_item = GTK_MENU_ITEM (GTK_MENU (widget->parent)->parent_menu_item);
1387       else
1388         parent_menu_item = NULL;
1389       
1390       parent_xthickness = widget->parent->style->xthickness;
1391
1392       if (parent_menu_item && !GTK_MENU (widget->parent)->torn_off)
1393         {
1394           menu_item->submenu_direction = parent_menu_item->submenu_direction;
1395         }
1396       else
1397         {
1398           if (direction == GTK_TEXT_DIR_LTR)
1399             menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1400           else
1401             menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1402         }
1403
1404       switch (menu_item->submenu_direction)
1405         {
1406         case GTK_DIRECTION_LEFT:
1407           if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
1408               available_left >= available_right)
1409             tx -= twidth + parent_xthickness + horizontal_offset;
1410           else
1411             {
1412               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
1413               tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1414             }
1415           break;
1416
1417         case GTK_DIRECTION_RIGHT:
1418           if (tx + widget->allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
1419               available_right >= available_left)
1420             tx += widget->allocation.width + parent_xthickness + horizontal_offset;
1421           else
1422             {
1423               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
1424               tx -= twidth + parent_xthickness + horizontal_offset;
1425             }
1426           break;
1427         }
1428
1429       ty += vertical_offset;
1430       
1431       /* If the height of the menu doesn't fit we move it upward. */
1432       ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
1433       break;
1434     }
1435
1436   /* If we have negative, tx, here it is because we can't get
1437    * the menu all the way on screen. Favor the left portion.
1438    */
1439   *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
1440   *y = ty;
1441
1442   gtk_menu_set_monitor (menu, monitor_num);
1443
1444   if (!GTK_WIDGET_VISIBLE (menu->toplevel))
1445     {
1446       gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
1447                                 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
1448     }
1449 }
1450
1451 /**
1452  * gtk_menu_item_set_right_justified:
1453  * @menu_item: a #GtkMenuItem.
1454  * @right_justified: if %TRUE the menu item will appear at the 
1455  *   far right if added to a menu bar.
1456  * 
1457  * Sets whether the menu item appears justified at the right
1458  * side of a menu bar. This was traditionally done for "Help" menu
1459  * items, but is now considered a bad idea. (If the widget
1460  * layout is reversed for a right-to-left language like Hebrew
1461  * or Arabic, right-justified-menu-items appear at the left.)
1462  **/
1463 void
1464 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
1465                                    gboolean     right_justified)
1466 {
1467   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1468
1469   right_justified = right_justified != FALSE;
1470
1471   if (right_justified != menu_item->right_justify)
1472     {
1473       menu_item->right_justify = right_justified;
1474       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1475     }
1476 }
1477
1478 /**
1479  * gtk_menu_item_get_right_justified:
1480  * @menu_item: a #GtkMenuItem
1481  * 
1482  * Gets whether the menu item appears justified at the right
1483  * side of the menu bar.
1484  * 
1485  * Return value: %TRUE if the menu item will appear at the
1486  *   far right if added to a menu bar.
1487  **/
1488 gboolean
1489 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
1490 {
1491   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
1492   
1493   return menu_item->right_justify;
1494 }
1495
1496
1497 static void
1498 gtk_menu_item_show_all (GtkWidget *widget)
1499 {
1500   GtkMenuItem *menu_item;
1501
1502   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1503
1504   menu_item = GTK_MENU_ITEM (widget);
1505
1506   /* show children including submenu */
1507   if (menu_item->submenu)
1508     gtk_widget_show_all (menu_item->submenu);
1509   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1510
1511   gtk_widget_show (widget);
1512 }
1513
1514 static void
1515 gtk_menu_item_hide_all (GtkWidget *widget)
1516 {
1517   GtkMenuItem *menu_item;
1518
1519   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1520
1521   gtk_widget_hide (widget);
1522
1523   menu_item = GTK_MENU_ITEM (widget);
1524
1525   /* hide children including submenu */
1526   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1527   if (menu_item->submenu)
1528     gtk_widget_hide_all (menu_item->submenu);
1529 }
1530
1531 static gboolean
1532 gtk_menu_item_can_activate_accel (GtkWidget *widget,
1533                                   guint      signal_id)
1534 {
1535   /* Chain to the parent GtkMenu for further checks */
1536   return (GTK_WIDGET_IS_SENSITIVE (widget) && GTK_WIDGET_VISIBLE (widget) &&
1537           widget->parent && gtk_widget_can_activate_accel (widget->parent, signal_id));
1538 }
1539
1540 static void
1541 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
1542                                   gpointer data)
1543 {
1544   const gchar **path_p = data;
1545
1546   if (!*path_p)
1547     {
1548       if (GTK_IS_LABEL (widget))
1549         {
1550           *path_p = gtk_label_get_text (GTK_LABEL (widget));
1551           if (*path_p && (*path_p)[0] == 0)
1552             *path_p = NULL;
1553         }
1554       else if (GTK_IS_CONTAINER (widget))
1555         gtk_container_foreach (GTK_CONTAINER (widget),
1556                                gtk_menu_item_accel_name_foreach,
1557                                data);
1558     }
1559 }
1560
1561 static void
1562 gtk_menu_item_parent_set (GtkWidget *widget,
1563                           GtkWidget *previous_parent)
1564 {
1565   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1566   GtkMenu *menu = GTK_IS_MENU (widget->parent) ? GTK_MENU (widget->parent) : NULL;
1567
1568   if (menu)
1569     _gtk_menu_item_refresh_accel_path (menu_item,
1570                                        menu->accel_path,
1571                                        menu->accel_group,
1572                                        TRUE);
1573
1574   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
1575     GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
1576 }
1577
1578 void
1579 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
1580                                    const gchar   *prefix,
1581                                    GtkAccelGroup *accel_group,
1582                                    gboolean       group_changed)
1583 {
1584   const gchar *path;
1585   GtkWidget *widget;
1586
1587   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1588   g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
1589
1590   widget = GTK_WIDGET (menu_item);
1591
1592   if (!accel_group)
1593     {
1594       gtk_widget_set_accel_path (widget, NULL, NULL);
1595       return;
1596     }
1597
1598   path = _gtk_widget_get_accel_path (widget, NULL);
1599   if (!path)                                    /* no active accel_path yet */
1600     {
1601       path = menu_item->accel_path;
1602       if (!path && prefix)
1603         {
1604           const gchar *postfix = NULL;
1605           gchar *new_path;
1606
1607           /* try to construct one from label text */
1608           gtk_container_foreach (GTK_CONTAINER (menu_item),
1609                                  gtk_menu_item_accel_name_foreach,
1610                                  &postfix);
1611           if (postfix)
1612             {
1613               new_path = g_strconcat (prefix, "/", postfix, NULL);
1614               path = menu_item->accel_path = (char*)g_intern_string (new_path);
1615               g_free (new_path);
1616             }
1617         }
1618       if (path)
1619         gtk_widget_set_accel_path (widget, path, accel_group);
1620     }
1621   else if (group_changed)                       /* reinstall accelerators */
1622     gtk_widget_set_accel_path (widget, path, accel_group);
1623 }
1624
1625 /**
1626  * gtk_menu_item_set_accel_path
1627  * @menu_item:  a valid #GtkMenuItem
1628  * @accel_path: accelerator path, corresponding to this menu item's
1629  *              functionality, or %NULL to unset the current path.
1630  *
1631  * Set the accelerator path on @menu_item, through which runtime changes of the
1632  * menu item's accelerator caused by the user can be identified and saved to
1633  * persistant storage (see gtk_accel_map_save() on this).
1634  * To setup a default accelerator for this menu item, call
1635  * gtk_accel_map_add_entry() with the same @accel_path.
1636  * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
1637  * and gtk_menu_set_accel_path() for a more convenient variant of this function.
1638  *
1639  * This function is basically a convenience wrapper that handles calling
1640  * gtk_widget_set_accel_path() with the appropriate accelerator group for
1641  * the menu item.
1642  *
1643  * Note that you do need to set an accelerator on the parent menu with
1644  * gtk_menu_set_accel_group() for this to work.
1645  *
1646  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
1647  * pass a static string, you can save some memory by interning it first with 
1648  * g_intern_static_string().
1649  */
1650 void
1651 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
1652                               const gchar *accel_path)
1653 {
1654   GtkWidget *widget;
1655
1656   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1657   g_return_if_fail (accel_path == NULL ||
1658                     (accel_path[0] == '<' && strchr (accel_path, '/')));
1659
1660   widget = GTK_WIDGET (menu_item);
1661
1662   /* store new path */
1663   menu_item->accel_path = (char*)g_intern_string (accel_path);
1664
1665   /* forget accelerators associated with old path */
1666   gtk_widget_set_accel_path (widget, NULL, NULL);
1667
1668   /* install accelerators associated with new path */
1669   if (GTK_IS_MENU (widget->parent))
1670     {
1671       GtkMenu *menu = GTK_MENU (widget->parent);
1672
1673       if (menu->accel_group)
1674         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
1675                                            NULL,
1676                                            menu->accel_group,
1677                                            FALSE);
1678     }
1679 }
1680
1681 /**
1682  * gtk_menu_item_get_accel_path
1683  * @menu_item:  a valid #GtkMenuItem
1684  *
1685  * Retrieve the accelerator path that was previously set on @menu_item.
1686  *
1687  * See gtk_menu_item_set_accel_path() for details.
1688  *
1689  * Returns: the accelerator path corresponding to this menu item's
1690  *              functionality, or %NULL if not set
1691  *
1692  * Since: 2.14
1693  */
1694 G_CONST_RETURN gchar *
1695 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
1696 {
1697   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1698
1699   return menu_item->accel_path;
1700 }
1701
1702 static void
1703 gtk_menu_item_forall (GtkContainer *container,
1704                       gboolean      include_internals,
1705                       GtkCallback   callback,
1706                       gpointer      callback_data)
1707 {
1708   GtkBin *bin;
1709
1710   g_return_if_fail (GTK_IS_MENU_ITEM (container));
1711   g_return_if_fail (callback != NULL);
1712
1713   bin = GTK_BIN (container);
1714
1715   if (bin->child)
1716     callback (bin->child, callback_data);
1717 }
1718
1719 gboolean
1720 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
1721 {
1722   if ((!GTK_BIN (menu_item)->child &&
1723        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
1724       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
1725       !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
1726       !GTK_WIDGET_VISIBLE (menu_item))
1727     return FALSE;
1728
1729   return TRUE;
1730 }
1731
1732 #define __GTK_MENU_ITEM_C__
1733 #include "gtkaliasdef.c"