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