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