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