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