]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Remove gtk_widget_hide_all()
[~andy/gtk] / gtk / gtkmenuitem.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28 #include <string.h>
29
30 #include "gtkaccellabel.h"
31 #include "gtkmain.h"
32 #include "gtkmarshalers.h"
33 #include "gtkmenu.h"
34 #include "gtkmenubar.h"
35 #include "gtkmenuprivate.h"
36 #include "gtkseparatormenuitem.h"
37 #include "gtkprivate.h"
38 #include "gtkbuildable.h"
39 #include "gtkactivatable.h"
40 #include "gtkintl.h"
41
42
43 typedef struct {
44   GtkAction *action;
45   gboolean   use_action_appearance;
46 } GtkMenuItemPrivate;
47
48 enum {
49   ACTIVATE,
50   ACTIVATE_ITEM,
51   TOGGLE_SIZE_REQUEST,
52   TOGGLE_SIZE_ALLOCATE,
53   SELECT,
54   DESELECT,
55   LAST_SIGNAL
56 };
57
58 enum {
59   PROP_0,
60   PROP_RIGHT_JUSTIFIED,
61   PROP_SUBMENU,
62   PROP_ACCEL_PATH,
63   PROP_LABEL,
64   PROP_USE_UNDERLINE,
65
66   /* activatable properties */
67   PROP_ACTIVATABLE_RELATED_ACTION,
68   PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
69 };
70
71
72 static void gtk_menu_item_dispose        (GObject          *object);
73 static void gtk_menu_item_set_property   (GObject          *object,
74                                           guint             prop_id,
75                                           const GValue     *value,
76                                           GParamSpec       *pspec);
77 static void gtk_menu_item_get_property   (GObject          *object,
78                                           guint             prop_id,
79                                           GValue           *value,
80                                           GParamSpec       *pspec);
81 static void gtk_menu_item_destroy        (GtkWidget        *widget);
82 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
83                                           GtkAllocation    *allocation);
84 static void gtk_menu_item_realize        (GtkWidget        *widget);
85 static void gtk_menu_item_unrealize      (GtkWidget        *widget);
86 static void gtk_menu_item_map            (GtkWidget        *widget);
87 static void gtk_menu_item_unmap          (GtkWidget        *widget);
88 static gboolean gtk_menu_item_enter      (GtkWidget        *widget,
89                                           GdkEventCrossing *event);
90 static gboolean gtk_menu_item_leave      (GtkWidget        *widget,
91                                           GdkEventCrossing *event);
92 static gboolean gtk_menu_item_draw       (GtkWidget        *widget,
93                                           cairo_t          *cr);
94 static void gtk_menu_item_parent_set     (GtkWidget        *widget,
95                                           GtkWidget        *previous_parent);
96
97
98 static void gtk_real_menu_item_select               (GtkMenuItem *item);
99 static void gtk_real_menu_item_deselect             (GtkMenuItem *item);
100 static void gtk_real_menu_item_activate             (GtkMenuItem *item);
101 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
102 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
103                                                      gint        *requisition);
104 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
105                                                      gint         allocation);
106 static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
107                                                      gboolean     group_cycling);
108
109 static void gtk_menu_item_ensure_label   (GtkMenuItem      *menu_item);
110 static gint gtk_menu_item_popup_timeout  (gpointer          data);
111 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
112                                           gint             *x,
113                                           gint             *y,
114                                           gboolean         *push_in,
115                                           gpointer          user_data);
116 static void gtk_menu_item_show_all       (GtkWidget        *widget);
117 static void gtk_menu_item_forall         (GtkContainer    *container,
118                                           gboolean         include_internals,
119                                           GtkCallback      callback,
120                                           gpointer         callback_data);
121 static gboolean gtk_menu_item_can_activate_accel (GtkWidget *widget,
122                                                   guint      signal_id);
123
124 static void gtk_real_menu_item_set_label (GtkMenuItem     *menu_item,
125                                           const gchar     *label);
126 static G_CONST_RETURN gchar * gtk_real_menu_item_get_label (GtkMenuItem *menu_item);
127
128 static void gtk_menu_item_get_preferred_width            (GtkWidget           *widget,
129                                                           gint                *minimum_size,
130                                                           gint                *natural_size);
131 static void gtk_menu_item_get_preferred_height           (GtkWidget           *widget,
132                                                           gint                *minimum_size,
133                                                           gint                *natural_size);
134 static void gtk_menu_item_get_preferred_height_for_width (GtkWidget           *widget,
135                                                           gint                 for_size,
136                                                           gint                *minimum_size,
137                                                           gint                *natural_size);
138
139 static void gtk_menu_item_buildable_interface_init (GtkBuildableIface   *iface);
140 static void gtk_menu_item_buildable_add_child      (GtkBuildable        *buildable,
141                                                     GtkBuilder          *builder,
142                                                     GObject             *child,
143                                                     const gchar         *type);
144 static void gtk_menu_item_buildable_custom_finished(GtkBuildable        *buildable,
145                                                     GtkBuilder          *builder,
146                                                     GObject             *child,
147                                                     const gchar         *tagname,
148                                                     gpointer             user_data);
149
150 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
151 static void gtk_menu_item_update                     (GtkActivatable       *activatable,
152                                                       GtkAction            *action,
153                                                       const gchar          *property_name);
154 static void gtk_menu_item_sync_action_properties     (GtkActivatable       *activatable,
155                                                       GtkAction            *action);
156 static void gtk_menu_item_set_related_action         (GtkMenuItem          *menu_item, 
157                                                       GtkAction            *action);
158 static void gtk_menu_item_set_use_action_appearance  (GtkMenuItem          *menu_item, 
159                                                       gboolean              use_appearance);
160
161
162 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
163
164 static GtkBuildableIface *parent_buildable_iface;
165
166 G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_BIN,
167                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
168                                                 gtk_menu_item_buildable_interface_init)
169                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
170                                                 gtk_menu_item_activatable_interface_init))
171
172 #define GET_PRIVATE(object)  \
173   (G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_MENU_ITEM, GtkMenuItemPrivate))
174
175 static void
176 gtk_menu_item_class_init (GtkMenuItemClass *klass)
177 {
178   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
179   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
180   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
181
182   gobject_class->dispose      = gtk_menu_item_dispose;
183   gobject_class->set_property = gtk_menu_item_set_property;
184   gobject_class->get_property = gtk_menu_item_get_property;
185
186   widget_class->destroy = gtk_menu_item_destroy;
187   widget_class->size_allocate = gtk_menu_item_size_allocate;
188   widget_class->draw = gtk_menu_item_draw;
189   widget_class->realize = gtk_menu_item_realize;
190   widget_class->unrealize = gtk_menu_item_unrealize;
191   widget_class->map = gtk_menu_item_map;
192   widget_class->unmap = gtk_menu_item_unmap;
193   widget_class->enter_notify_event = gtk_menu_item_enter;
194   widget_class->leave_notify_event = gtk_menu_item_leave;
195   widget_class->show_all = gtk_menu_item_show_all;
196   widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
197   widget_class->parent_set = gtk_menu_item_parent_set;
198   widget_class->can_activate_accel = gtk_menu_item_can_activate_accel;
199   widget_class->get_preferred_width = gtk_menu_item_get_preferred_width;
200   widget_class->get_preferred_height = gtk_menu_item_get_preferred_height;
201   widget_class->get_preferred_height_for_width = gtk_menu_item_get_preferred_height_for_width;
202   
203   container_class->forall = gtk_menu_item_forall;
204
205   klass->activate             = gtk_real_menu_item_activate;
206   klass->activate_item        = gtk_real_menu_item_activate_item;
207   klass->toggle_size_request  = gtk_real_menu_item_toggle_size_request;
208   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
209   klass->set_label            = gtk_real_menu_item_set_label;
210   klass->get_label            = gtk_real_menu_item_get_label;
211   klass->select               = gtk_real_menu_item_select;
212   klass->deselect             = gtk_real_menu_item_deselect;
213
214   klass->hide_on_activate = TRUE;
215
216   menu_item_signals[ACTIVATE] =
217     g_signal_new (I_("activate"),
218                   G_OBJECT_CLASS_TYPE (gobject_class),
219                   G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
220                   G_STRUCT_OFFSET (GtkMenuItemClass, activate),
221                   NULL, NULL,
222                   _gtk_marshal_VOID__VOID,
223                   G_TYPE_NONE, 0);
224   widget_class->activate_signal = menu_item_signals[ACTIVATE];
225
226   menu_item_signals[ACTIVATE_ITEM] =
227     g_signal_new (I_("activate-item"),
228                   G_OBJECT_CLASS_TYPE (gobject_class),
229                   G_SIGNAL_RUN_FIRST,
230                   G_STRUCT_OFFSET (GtkMenuItemClass, activate_item),
231                   NULL, NULL,
232                   _gtk_marshal_VOID__VOID,
233                   G_TYPE_NONE, 0);
234
235   menu_item_signals[TOGGLE_SIZE_REQUEST] =
236     g_signal_new (I_("toggle-size-request"),
237                   G_OBJECT_CLASS_TYPE (gobject_class),
238                   G_SIGNAL_RUN_FIRST,
239                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_request),
240                   NULL, NULL,
241                   _gtk_marshal_VOID__POINTER,
242                   G_TYPE_NONE, 1,
243                   G_TYPE_POINTER);
244
245   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
246     g_signal_new (I_("toggle-size-allocate"),
247                   G_OBJECT_CLASS_TYPE (gobject_class),
248                   G_SIGNAL_RUN_FIRST,
249                   G_STRUCT_OFFSET (GtkMenuItemClass, toggle_size_allocate),
250                   NULL, NULL,
251                   _gtk_marshal_VOID__INT,
252                   G_TYPE_NONE, 1,
253                   G_TYPE_INT);
254
255   menu_item_signals[SELECT] =
256     g_signal_new (I_("select"),
257                   G_OBJECT_CLASS_TYPE (gobject_class),
258                   G_SIGNAL_RUN_FIRST,
259                   G_STRUCT_OFFSET (GtkMenuItemClass, select),
260                   NULL, NULL,
261                   _gtk_marshal_VOID__VOID,
262                   G_TYPE_NONE, 0);
263
264   menu_item_signals[DESELECT] =
265     g_signal_new (I_("deselect"),
266                   G_OBJECT_CLASS_TYPE (gobject_class),
267                   G_SIGNAL_RUN_FIRST,
268                   G_STRUCT_OFFSET (GtkMenuItemClass, deselect),
269                   NULL, NULL,
270                   _gtk_marshal_VOID__VOID,
271                   G_TYPE_NONE, 0);
272
273   /**
274    * GtkMenuItem:right-justified:
275    *
276    * Sets whether the menu item appears justified at the right side of a menu bar.
277    *
278    * Since: 2.14
279    **/
280   g_object_class_install_property (gobject_class,
281                                    PROP_RIGHT_JUSTIFIED,
282                                    g_param_spec_boolean ("right-justified",
283                                                          P_("Right Justified"),
284                                                          P_("Sets whether the menu item appears justified at the right side of a menu bar"),
285                                                          FALSE,
286                                                          GTK_PARAM_READWRITE));
287
288   /**
289    * GtkMenuItem:submenu:
290    *
291    * The submenu attached to the menu item, or NULL if it has none.
292    *
293    * Since: 2.12
294    **/
295   g_object_class_install_property (gobject_class,
296                                    PROP_SUBMENU,
297                                    g_param_spec_object ("submenu",
298                                                         P_("Submenu"),
299                                                         P_("The submenu attached to the menu item, or NULL if it has none"),
300                                                         GTK_TYPE_MENU,
301                                                         GTK_PARAM_READWRITE));
302   
303
304   /**
305    * GtkMenuItem:accel-path:
306    *
307    * Sets the accelerator path of the menu item, through which runtime
308    * changes of the menu item's accelerator caused by the user can be
309    * identified and saved to persistant storage.
310    *
311    * Since: 2.14
312    **/
313   g_object_class_install_property (gobject_class,
314                                    PROP_ACCEL_PATH,
315                                    g_param_spec_string ("accel-path",
316                                                         P_("Accel Path"),
317                                                         P_("Sets the accelerator path of the menu item"),
318                                                         NULL,
319                                                         GTK_PARAM_READWRITE));
320
321   /**
322    * GtkMenuItem:label:
323    *
324    * The text for the child label.
325    *
326    * Since: 2.16
327    **/
328   g_object_class_install_property (gobject_class,
329                                    PROP_LABEL,
330                                    g_param_spec_string ("label",
331                                                         P_("Label"),
332                                                         P_("The text for the child label"),
333                                                         "",
334                                                         GTK_PARAM_READWRITE));
335
336   /**
337    * GtkMenuItem:use-underline:
338    *
339    * %TRUE if underlines in the text indicate mnemonics  
340    *
341    * Since: 2.16
342    **/
343   g_object_class_install_property (gobject_class,
344                                    PROP_USE_UNDERLINE,
345                                    g_param_spec_boolean ("use-underline",
346                                                          P_("Use underline"),
347                                                          P_("If set, an underline in the text indicates "
348                                                             "the next character should be used for the "
349                                                             "mnemonic accelerator key"),
350                                                          FALSE,
351                                                          GTK_PARAM_READWRITE));
352
353   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
354   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
355
356   gtk_widget_class_install_style_property_parser (widget_class,
357                                                   g_param_spec_enum ("selected-shadow-type",
358                                                                      "Selected Shadow Type",
359                                                                      "Shadow type when item is selected",
360                                                                      GTK_TYPE_SHADOW_TYPE,
361                                                                      GTK_SHADOW_NONE,
362                                                                      GTK_PARAM_READABLE),
363                                                   gtk_rc_property_parse_enum);
364
365   gtk_widget_class_install_style_property (widget_class,
366                                            g_param_spec_int ("horizontal-padding",
367                                                              "Horizontal Padding",
368                                                              "Padding to left and right of the menu item",
369                                                              0,
370                                                              G_MAXINT,
371                                                              3,
372                                                              GTK_PARAM_READABLE));
373
374   gtk_widget_class_install_style_property (widget_class,
375                                            g_param_spec_int ("toggle-spacing",
376                                                              "Icon Spacing",
377                                                              "Space between icon and label",
378                                                              0,
379                                                              G_MAXINT,
380                                                              5,
381                                                              GTK_PARAM_READABLE));
382
383   gtk_widget_class_install_style_property (widget_class,
384                                            g_param_spec_int ("arrow-spacing",
385                                                              "Arrow Spacing",
386                                                              "Space between label and arrow",
387                                                              0,
388                                                              G_MAXINT,
389                                                              10,
390                                                              GTK_PARAM_READABLE));
391
392   gtk_widget_class_install_style_property (widget_class,
393                                            g_param_spec_float ("arrow-scaling",
394                                                                P_("Arrow Scaling"),
395                                                                P_("Amount of space used up by arrow, relative to the menu item's font size"),
396                                                                0.0, 2.0, 0.8,
397                                                                GTK_PARAM_READABLE));
398
399   /**
400    * GtkMenuItem:width-chars:
401    *
402    * The minimum desired width of the menu item in characters.
403    *
404    * Since: 2.14
405    **/
406   gtk_widget_class_install_style_property (widget_class,
407                                            g_param_spec_int ("width-chars",
408                                                              P_("Width in Characters"),
409                                                              P_("The minimum desired width of the menu item in characters"),
410                                                              0, G_MAXINT, 12,
411                                                              GTK_PARAM_READABLE));
412
413   g_type_class_add_private (klass, sizeof (GtkMenuItemPrivate));
414 }
415
416 static void
417 gtk_menu_item_init (GtkMenuItem *menu_item)
418 {
419   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
420
421   gtk_widget_set_has_window (GTK_WIDGET (menu_item), FALSE);
422
423   priv->action = NULL;
424   priv->use_action_appearance = TRUE;
425   
426   menu_item->submenu = NULL;
427   menu_item->toggle_size = 0;
428   menu_item->accelerator_width = 0;
429   menu_item->show_submenu_indicator = FALSE;
430   if (gtk_widget_get_direction (GTK_WIDGET (menu_item)) == GTK_TEXT_DIR_RTL)
431     menu_item->submenu_direction = GTK_DIRECTION_LEFT;
432   else
433     menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
434   menu_item->submenu_placement = GTK_TOP_BOTTOM;
435   menu_item->right_justify = FALSE;
436
437   menu_item->timer = 0;
438 }
439
440 GtkWidget*
441 gtk_menu_item_new (void)
442 {
443   return g_object_new (GTK_TYPE_MENU_ITEM, NULL);
444 }
445
446 GtkWidget*
447 gtk_menu_item_new_with_label (const gchar *label)
448 {
449   return g_object_new (GTK_TYPE_MENU_ITEM, 
450                        "label", label,
451                        NULL);
452 }
453
454
455 /**
456  * gtk_menu_item_new_with_mnemonic:
457  * @label: The text of the button, with an underscore in front of the
458  *         mnemonic character
459  * @returns: a new #GtkMenuItem
460  *
461  * Creates a new #GtkMenuItem containing a label. The label
462  * will be created using gtk_label_new_with_mnemonic(), so underscores
463  * in @label indicate the mnemonic for the menu item.
464  **/
465 GtkWidget*
466 gtk_menu_item_new_with_mnemonic (const gchar *label)
467 {
468   return g_object_new (GTK_TYPE_MENU_ITEM, 
469                        "use-underline", TRUE,
470                        "label", label,
471                        NULL);
472 }
473
474 static void
475 gtk_menu_item_dispose (GObject *object)
476 {
477   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
478   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
479
480   if (priv->action)
481     {
482       gtk_action_disconnect_accelerator (priv->action);
483       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), NULL);
484       
485       priv->action = NULL;
486     }
487   G_OBJECT_CLASS (gtk_menu_item_parent_class)->dispose (object);
488 }
489
490 static void 
491 gtk_menu_item_set_property (GObject      *object,
492                             guint         prop_id,
493                             const GValue *value,
494                             GParamSpec   *pspec)
495 {
496   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
497   
498   switch (prop_id)
499     {
500     case PROP_RIGHT_JUSTIFIED:
501       gtk_menu_item_set_right_justified (menu_item, g_value_get_boolean (value));
502       break;
503     case PROP_SUBMENU:
504       gtk_menu_item_set_submenu (menu_item, g_value_get_object (value));
505       break;
506     case PROP_ACCEL_PATH:
507       gtk_menu_item_set_accel_path (menu_item, g_value_get_string (value));
508       break;
509     case PROP_LABEL:
510       gtk_menu_item_set_label (menu_item, g_value_get_string (value));
511       break;
512     case PROP_USE_UNDERLINE:
513       gtk_menu_item_set_use_underline (menu_item, g_value_get_boolean (value));
514       break;
515     case PROP_ACTIVATABLE_RELATED_ACTION:
516       gtk_menu_item_set_related_action (menu_item, g_value_get_object (value));
517       break;
518     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
519       gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
520       break;
521     default:
522       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
523       break;
524     }
525 }
526
527 static void 
528 gtk_menu_item_get_property (GObject    *object,
529                             guint       prop_id,
530                             GValue     *value,
531                             GParamSpec *pspec)
532 {
533   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
534   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
535   
536   switch (prop_id)
537     {
538     case PROP_RIGHT_JUSTIFIED:
539       g_value_set_boolean (value, gtk_menu_item_get_right_justified (menu_item));
540       break;
541     case PROP_SUBMENU:
542       g_value_set_object (value, gtk_menu_item_get_submenu (menu_item));
543       break;
544     case PROP_ACCEL_PATH:
545       g_value_set_string (value, gtk_menu_item_get_accel_path (menu_item));
546       break;
547     case PROP_LABEL:
548       g_value_set_string (value, gtk_menu_item_get_label (menu_item));
549       break;
550     case PROP_USE_UNDERLINE:
551       g_value_set_boolean (value, gtk_menu_item_get_use_underline (menu_item));
552       break;
553     case PROP_ACTIVATABLE_RELATED_ACTION:
554       g_value_set_object (value, priv->action);
555       break;
556     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
557       g_value_set_boolean (value, priv->use_action_appearance);
558       break;
559     default:
560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561       break;
562     }
563 }
564
565 static void
566 gtk_menu_item_destroy (GtkWidget *widget)
567 {
568   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
569
570   if (menu_item->submenu)
571     gtk_widget_destroy (menu_item->submenu);
572
573   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->destroy (widget);
574 }
575
576 static void
577 gtk_menu_item_detacher (GtkWidget *widget,
578                         GtkMenu   *menu)
579 {
580   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
581
582   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
583
584   menu_item->submenu = NULL;
585 }
586
587 static void
588 get_arrow_size (GtkWidget *widget,
589                 GtkWidget *child,
590                 gint      *size)
591 {
592   PangoContext     *context;
593   PangoFontMetrics *metrics;
594   gfloat            arrow_scaling;
595           
596   g_assert (size);
597
598   gtk_widget_style_get (widget,
599                         "arrow-scaling", &arrow_scaling,
600                         NULL);
601           
602   context = gtk_widget_get_pango_context (child);
603   metrics = pango_context_get_metrics (context, 
604                                        gtk_widget_get_style (child)->font_desc,
605                                        pango_context_get_language (context));
606   
607   *size = (PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
608                          pango_font_metrics_get_descent (metrics)));
609   
610   pango_font_metrics_unref (metrics);
611   
612   *size = *size * arrow_scaling;
613 }
614
615
616 static void
617 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
618                                    gpointer data)
619 {
620   guint *width = data;
621
622   if (GTK_IS_ACCEL_LABEL (widget))
623     {
624       guint w;
625
626       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
627       *width = MAX (*width, w);
628     }
629   else if (GTK_IS_CONTAINER (widget))
630     gtk_container_foreach (GTK_CONTAINER (widget),
631                            gtk_menu_item_accel_width_foreach,
632                            data);
633 }
634
635 static gint
636 get_minimum_width (GtkWidget *widget)
637 {
638   PangoContext *context;
639   PangoFontMetrics *metrics;
640   gint width;
641   gint width_chars;
642
643   context = gtk_widget_get_pango_context (widget);
644   metrics = pango_context_get_metrics (context,
645                                        gtk_widget_get_style (widget)->font_desc,
646                                        pango_context_get_language (context));
647
648   width = pango_font_metrics_get_approximate_char_width (metrics);
649
650   pango_font_metrics_unref (metrics);
651
652   gtk_widget_style_get (widget, "width-chars", &width_chars, NULL);
653
654   return PANGO_PIXELS (width_chars * width);
655 }
656
657 static void 
658 gtk_menu_item_get_preferred_width (GtkWidget *request,
659                                    gint      *minimum_size,
660                                    gint      *natural_size)
661 {
662   GtkMenuItem *menu_item;
663   GtkBin *bin;
664   GtkWidget *child, *widget = GTK_WIDGET (request);
665   GtkWidget *parent;
666   guint accel_width;
667   guint horizontal_padding;
668   guint border_width;
669   GtkPackDirection pack_dir;
670   GtkPackDirection child_pack_dir;
671   gint  min_width, nat_width;
672
673   min_width = nat_width = 0;
674
675   gtk_widget_style_get (widget,
676                         "horizontal-padding", &horizontal_padding,
677                         NULL);
678   
679   bin = GTK_BIN (widget);
680   menu_item = GTK_MENU_ITEM (widget);
681   parent = gtk_widget_get_parent (widget);
682
683   if (GTK_IS_MENU_BAR (parent))
684     {
685       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
686       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
687     }
688   else
689     {
690       pack_dir = GTK_PACK_DIRECTION_LTR;
691       child_pack_dir = GTK_PACK_DIRECTION_LTR;
692     }
693
694   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
695
696   min_width = (border_width + gtk_widget_get_style (widget)->xthickness) * 2;
697
698   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
699       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
700     min_width += 2 * horizontal_padding;
701
702   nat_width = min_width;
703
704   child = gtk_bin_get_child (bin);
705
706   if (child != NULL && gtk_widget_get_visible (child))
707     {
708       gint child_min, child_nat;
709
710       gtk_widget_get_preferred_width (child, &child_min, &child_nat);
711
712       if (menu_item->submenu && menu_item->show_submenu_indicator)
713         {
714           guint arrow_spacing;
715           gint  arrow_size;
716           
717           gtk_widget_style_get (widget,
718                                 "arrow-spacing", &arrow_spacing,
719                                 NULL);
720
721           get_arrow_size (widget, child, &arrow_size);
722
723           min_width += arrow_size;
724           min_width += arrow_spacing;
725
726           min_width = MAX (min_width, get_minimum_width (widget));
727
728           nat_width = min_width;
729         }
730
731
732       min_width += child_min;
733       nat_width += child_nat;
734
735
736     }
737
738   accel_width = 0;
739   gtk_container_foreach (GTK_CONTAINER (menu_item),
740                          gtk_menu_item_accel_width_foreach,
741                          &accel_width);
742   menu_item->accelerator_width = accel_width;
743
744   if (minimum_size)
745     *minimum_size = min_width;
746
747   if (natural_size)
748     *natural_size = nat_width;
749 }
750
751 static void 
752 gtk_menu_item_get_preferred_height (GtkWidget *request,
753                                     gint      *minimum_size,
754                                     gint      *natural_size)
755 {
756   GtkMenuItem *menu_item;
757   GtkBin *bin;
758   GtkStyle *style;
759   GtkWidget *child, *widget = GTK_WIDGET (request);
760   GtkWidget *parent;
761   guint accel_width;
762   guint horizontal_padding;
763   guint border_width;
764   GtkPackDirection pack_dir;
765   GtkPackDirection child_pack_dir;
766   gint  min_height, nat_height;
767
768   min_height = nat_height = 0;
769
770   style = gtk_widget_get_style (widget);
771
772   gtk_widget_style_get (widget,
773                         "horizontal-padding", &horizontal_padding,
774                         NULL);
775   
776   bin = GTK_BIN (widget);
777   menu_item = GTK_MENU_ITEM (widget);
778   parent = gtk_widget_get_parent (widget);
779
780   if (GTK_IS_MENU_BAR (parent))
781     {
782       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
783       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
784     }
785   else
786     {
787       pack_dir = GTK_PACK_DIRECTION_LTR;
788       child_pack_dir = GTK_PACK_DIRECTION_LTR;
789     }
790
791   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
792   min_height = (border_width + style->ythickness) * 2;
793
794   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
795       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
796     min_height += 2 * horizontal_padding;
797
798   nat_height = min_height;
799
800   child = gtk_bin_get_child (bin);
801
802   if (child != NULL && gtk_widget_get_visible (child))
803     {
804       gint child_min, child_nat;
805       
806       gtk_widget_get_preferred_height (child, &child_min, &child_nat);
807
808       min_height += child_min;
809       nat_height += child_nat;
810
811       if (menu_item->submenu && menu_item->show_submenu_indicator)
812         {
813           gint  arrow_size;
814
815           get_arrow_size (widget, child, &arrow_size);
816
817           min_height = MAX (min_height, arrow_size);
818           nat_height = MAX (nat_height, arrow_size);
819         }
820     }
821   else /* separator item */
822     {
823       gboolean wide_separators;
824       gint     separator_height;
825
826       gtk_widget_style_get (widget,
827                             "wide-separators",  &wide_separators,
828                             "separator-height", &separator_height,
829                             NULL);
830
831       if (wide_separators)
832         min_height += separator_height + style->ythickness;
833       else
834         min_height += style->ythickness * 2;
835
836       nat_height = min_height;
837     }
838
839   accel_width = 0;
840   gtk_container_foreach (GTK_CONTAINER (menu_item),
841                          gtk_menu_item_accel_width_foreach,
842                          &accel_width);
843   menu_item->accelerator_width = accel_width;
844
845   if (minimum_size)
846     *minimum_size = min_height;
847
848   if (natural_size)
849     *natural_size = nat_height;
850 }
851
852 static void
853 gtk_menu_item_get_preferred_height_for_width (GtkWidget *request,
854                                               gint       for_size,
855                                               gint      *minimum_size,
856                                               gint      *natural_size)
857 {
858   GtkMenuItem *menu_item;
859   GtkBin *bin;
860   GtkStyle *style;
861   GtkWidget *child, *widget = GTK_WIDGET (request);
862   GtkWidget *parent;
863   guint horizontal_padding;
864   guint border_width;
865   GtkPackDirection pack_dir;
866   GtkPackDirection child_pack_dir;
867   gint  min_height, nat_height;
868   gint  avail_size;
869
870   min_height = nat_height = 0;
871
872   style = gtk_widget_get_style (widget);
873
874   gtk_widget_style_get (widget,
875                         "horizontal-padding", &horizontal_padding,
876                         NULL);
877   
878   bin = GTK_BIN (widget);
879   menu_item = GTK_MENU_ITEM (widget);
880   parent = gtk_widget_get_parent (widget);
881
882   if (GTK_IS_MENU_BAR (parent))
883     {
884       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
885       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
886     }
887   else
888     {
889       pack_dir = GTK_PACK_DIRECTION_LTR;
890       child_pack_dir = GTK_PACK_DIRECTION_LTR;
891     }
892
893   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
894   min_height   = (border_width + style->ythickness) * 2;
895
896   avail_size   = for_size;
897   avail_size  -= (border_width + style->xthickness) * 2;
898
899   if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
900       (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
901     min_height += 2 * horizontal_padding;
902
903   if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
904       (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
905     avail_size -= 2 * horizontal_padding;
906
907   nat_height = min_height;
908
909   child = gtk_bin_get_child (bin);
910
911   if (child != NULL && gtk_widget_get_visible (child))
912     {
913       gint child_min, child_nat;
914       gint arrow_size = 0;
915       
916       if (menu_item->submenu && menu_item->show_submenu_indicator)
917         {
918
919           guint arrow_spacing;
920           
921           gtk_widget_style_get (widget,
922                                 "arrow-spacing", &arrow_spacing,
923                                 NULL);
924
925           get_arrow_size (widget, child, &arrow_size);
926
927           avail_size -= arrow_size;
928           avail_size -= arrow_spacing;
929         }
930
931       gtk_widget_get_preferred_height_for_width (child,
932                                                  avail_size,
933                                                  &child_min,
934                                                  &child_nat);
935
936       min_height += child_min;
937       nat_height += child_nat;
938
939       if (menu_item->submenu && menu_item->show_submenu_indicator)
940         {
941           min_height = MAX (min_height, arrow_size);
942           nat_height = MAX (nat_height, arrow_size);
943         }
944     }
945   else /* separator item */
946     {
947       gboolean wide_separators;
948       gint     separator_height;
949
950       gtk_widget_style_get (widget,
951                             "wide-separators",  &wide_separators,
952                             "separator-height", &separator_height,
953                             NULL);
954
955       if (wide_separators)
956         min_height += separator_height + style->ythickness;
957       else
958         min_height += style->ythickness * 2;
959
960       nat_height = min_height;
961     }
962
963   if (minimum_size)
964     *minimum_size = min_height;
965
966   if (natural_size)
967     *natural_size = nat_height;
968 }
969
970 static void
971 gtk_menu_item_buildable_interface_init (GtkBuildableIface *iface)
972 {
973   parent_buildable_iface = g_type_interface_peek_parent (iface);
974   iface->add_child = gtk_menu_item_buildable_add_child;
975   iface->custom_finished = gtk_menu_item_buildable_custom_finished;
976 }
977
978 static void
979 gtk_menu_item_buildable_add_child (GtkBuildable *buildable,
980                                    GtkBuilder   *builder,
981                                    GObject      *child,
982                                    const gchar  *type)
983 {
984   if (type && strcmp (type, "submenu") == 0)
985         gtk_menu_item_set_submenu (GTK_MENU_ITEM (buildable),
986                                    GTK_WIDGET (child));
987   else
988     parent_buildable_iface->add_child (buildable, builder, child, type);
989 }
990
991
992 static void 
993 gtk_menu_item_buildable_custom_finished (GtkBuildable        *buildable,
994                                          GtkBuilder          *builder,
995                                          GObject             *child,
996                                          const gchar         *tagname,
997                                          gpointer             user_data)
998 {
999   GtkWidget *toplevel;
1000
1001   if (strcmp (tagname, "accelerator") == 0)
1002     {
1003       GtkMenuShell *menu_shell;
1004       GtkWidget *attach;
1005
1006       menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (GTK_WIDGET (buildable)));
1007       if (menu_shell)
1008         {
1009           while (GTK_IS_MENU (menu_shell) &&
1010                  (attach = gtk_menu_get_attach_widget (GTK_MENU (menu_shell))) != NULL)
1011             menu_shell = GTK_MENU_SHELL (gtk_widget_get_parent (attach));
1012           
1013           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
1014         }
1015       else
1016         {
1017           /* Fall back to something ... */
1018           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (buildable));
1019
1020           g_warning ("found a GtkMenuItem '%s' without a parent GtkMenuShell, assigned accelerators wont work.",
1021                      gtk_buildable_get_name (buildable));
1022         }
1023
1024       /* Feed the correct toplevel to the GtkWidget accelerator parsing code */
1025       _gtk_widget_buildable_finish_accelerator (GTK_WIDGET (buildable), toplevel, user_data);
1026     }
1027   else
1028     parent_buildable_iface->custom_finished (buildable, builder, child, tagname, user_data);
1029 }
1030
1031
1032 static void
1033 gtk_menu_item_activatable_interface_init (GtkActivatableIface *iface)
1034 {
1035   iface->update = gtk_menu_item_update;
1036   iface->sync_action_properties = gtk_menu_item_sync_action_properties;
1037 }
1038
1039 static void
1040 activatable_update_label (GtkMenuItem *menu_item, GtkAction *action)
1041 {
1042   GtkWidget *child;
1043
1044   child = gtk_bin_get_child (GTK_BIN (menu_item));
1045
1046   if (GTK_IS_LABEL (child))
1047     {
1048       const gchar *label;
1049
1050       label = gtk_action_get_label (action);
1051       gtk_menu_item_set_label (menu_item, label);
1052     }
1053 }
1054
1055 gboolean _gtk_menu_is_empty (GtkWidget *menu);
1056
1057 static void
1058 gtk_menu_item_update (GtkActivatable *activatable,
1059                       GtkAction      *action,
1060                       const gchar    *property_name)
1061 {
1062   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1063   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1064
1065   if (strcmp (property_name, "visible") == 0)
1066     _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item), 
1067                                    _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1068   else if (strcmp (property_name, "sensitive") == 0)
1069     gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1070   else if (priv->use_action_appearance)
1071     {
1072       if (strcmp (property_name, "label") == 0)
1073         activatable_update_label (menu_item, action);
1074     }
1075 }
1076
1077 static void
1078 gtk_menu_item_sync_action_properties (GtkActivatable *activatable,
1079                                       GtkAction      *action)
1080 {
1081   GtkMenuItem *menu_item = GTK_MENU_ITEM (activatable);
1082   GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1083   GtkWidget *label;
1084
1085   if (!priv->use_action_appearance || !action)
1086     {
1087       label = gtk_bin_get_child (GTK_BIN (menu_item));
1088
1089       if (GTK_IS_ACCEL_LABEL (label))
1090         gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), GTK_WIDGET (menu_item));
1091     }
1092
1093   if (!action)
1094     return;
1095
1096   _gtk_action_sync_menu_visible (action, GTK_WIDGET (menu_item),
1097                                  _gtk_menu_is_empty (gtk_menu_item_get_submenu (menu_item)));
1098
1099   gtk_widget_set_sensitive (GTK_WIDGET (menu_item), gtk_action_is_sensitive (action));
1100
1101   if (priv->use_action_appearance)
1102     {
1103       label = gtk_bin_get_child (GTK_BIN (menu_item));
1104
1105       /* make sure label is a label, deleting it otherwise */
1106       if (label && !GTK_IS_LABEL (label))
1107         {
1108           gtk_container_remove (GTK_CONTAINER (menu_item), label);
1109           label = NULL;
1110         }
1111       /* Make sure that menu_item has a label and that any
1112        * accelerators are set */
1113       gtk_menu_item_ensure_label (menu_item);
1114       gtk_menu_item_set_use_underline (menu_item, TRUE);
1115       /* Make label point to the menu_item's label */
1116       label = gtk_bin_get_child (GTK_BIN (menu_item));
1117
1118       if (GTK_IS_ACCEL_LABEL (label) && gtk_action_get_accel_path (action))
1119         {
1120           gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), NULL);
1121           gtk_accel_label_set_accel_closure (GTK_ACCEL_LABEL (label),
1122                                              gtk_action_get_accel_closure (action));
1123         }
1124
1125       activatable_update_label (menu_item, action);
1126     }
1127 }
1128
1129 static void
1130 gtk_menu_item_set_related_action (GtkMenuItem *menu_item,
1131                                   GtkAction   *action)
1132 {
1133     GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1134
1135     if (priv->action == action)
1136       return;
1137
1138     if (priv->action)
1139       {
1140         gtk_action_disconnect_accelerator (priv->action);
1141       }
1142
1143     if (action)
1144       {
1145         const gchar *accel_path;
1146         
1147         accel_path = gtk_action_get_accel_path (action);
1148         if (accel_path)
1149           {
1150             gtk_action_connect_accelerator (action);
1151             gtk_menu_item_set_accel_path (menu_item, accel_path);
1152           }
1153       }
1154
1155     gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (menu_item), action);
1156
1157     priv->action = action;
1158 }
1159
1160 static void
1161 gtk_menu_item_set_use_action_appearance (GtkMenuItem *menu_item,
1162                                          gboolean     use_appearance)
1163 {
1164     GtkMenuItemPrivate *priv = GET_PRIVATE (menu_item);
1165
1166     if (priv->use_action_appearance != use_appearance)
1167       {
1168         priv->use_action_appearance = use_appearance;
1169
1170         gtk_activatable_sync_action_properties (GTK_ACTIVATABLE (menu_item), priv->action);
1171       }
1172 }
1173
1174
1175 /**
1176  * gtk_menu_item_set_submenu:
1177  * @menu_item: a #GtkMenuItem
1178  * @submenu: (allow-none): the submenu, or %NULL
1179  *
1180  * Sets or replaces the menu item's submenu, or removes it when a %NULL
1181  * submenu is passed.
1182  **/
1183 void
1184 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
1185                            GtkWidget   *submenu)
1186 {
1187   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1188   g_return_if_fail (submenu == NULL || GTK_IS_MENU (submenu));
1189   
1190   if (menu_item->submenu != submenu)
1191     {
1192       if (menu_item->submenu)
1193         gtk_menu_detach (GTK_MENU (menu_item->submenu));
1194
1195       if (submenu)
1196         {
1197           menu_item->submenu = submenu;
1198           gtk_menu_attach_to_widget (GTK_MENU (submenu),
1199                                      GTK_WIDGET (menu_item),
1200                                      gtk_menu_item_detacher);
1201         }
1202
1203       if (gtk_widget_get_parent (GTK_WIDGET (menu_item)))
1204         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
1205
1206       g_object_notify (G_OBJECT (menu_item), "submenu");
1207     }
1208 }
1209
1210 /**
1211  * gtk_menu_item_get_submenu:
1212  * @menu_item: a #GtkMenuItem
1213  *
1214  * Gets the submenu underneath this menu item, if any.
1215  * See gtk_menu_item_set_submenu().
1216  *
1217  * Return value: (transfer none): submenu for this menu item, or %NULL if none.
1218  **/
1219 GtkWidget *
1220 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
1221 {
1222   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
1223
1224   return menu_item->submenu;
1225 }
1226
1227 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1228                                    GtkSubmenuPlacement  placement);
1229
1230 void
1231 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1232                              GtkSubmenuPlacement  placement)
1233 {
1234   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1235
1236   menu_item->submenu_placement = placement;
1237 }
1238
1239 void
1240 gtk_menu_item_select (GtkMenuItem *menu_item)
1241 {
1242   GtkWidget *parent;
1243
1244   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1245
1246   g_signal_emit (menu_item, menu_item_signals[SELECT], 0);
1247
1248   /* Enable themeing of the parent menu item depending on whether
1249    * something is selected in its submenu
1250    */
1251   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1252   if (GTK_IS_MENU (parent))
1253     {
1254       GtkMenu *menu = GTK_MENU (parent);
1255
1256       if (menu->parent_menu_item)
1257         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
1258     }
1259 }
1260
1261 void
1262 gtk_menu_item_deselect (GtkMenuItem *menu_item)
1263 {
1264   GtkWidget *parent;
1265
1266   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1267
1268   g_signal_emit (menu_item, menu_item_signals[DESELECT], 0);
1269
1270   /* Enable themeing of the parent menu item depending on whether
1271    * something is selected in its submenu
1272    */
1273   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1274   if (GTK_IS_MENU (parent))
1275     {
1276       GtkMenu *menu = GTK_MENU (parent);
1277
1278       if (menu->parent_menu_item)
1279         gtk_widget_queue_draw (GTK_WIDGET (menu->parent_menu_item));
1280     }
1281 }
1282
1283 void
1284 gtk_menu_item_activate (GtkMenuItem *menu_item)
1285 {
1286   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1287
1288   g_signal_emit (menu_item, menu_item_signals[ACTIVATE], 0);
1289 }
1290
1291 void
1292 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1293                                    gint        *requisition)
1294 {
1295   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1296
1297   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_REQUEST], 0, requisition);
1298 }
1299
1300 void
1301 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1302                                     gint         allocation)
1303 {
1304   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1305
1306   g_signal_emit (menu_item, menu_item_signals[TOGGLE_SIZE_ALLOCATE], 0, allocation);
1307 }
1308
1309 static void
1310 gtk_menu_item_size_allocate (GtkWidget     *widget,
1311                              GtkAllocation *allocation)
1312 {
1313   GtkMenuItem *menu_item;
1314   GtkBin *bin;
1315   GtkAllocation child_allocation;
1316   GtkTextDirection direction;
1317   GtkPackDirection pack_dir;
1318   GtkPackDirection child_pack_dir;
1319   GtkWidget *child;
1320   GtkWidget *parent;
1321
1322   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1323   g_return_if_fail (allocation != NULL);
1324
1325   menu_item = GTK_MENU_ITEM (widget);
1326   bin = GTK_BIN (widget);
1327   
1328   direction = gtk_widget_get_direction (widget);
1329
1330   parent = gtk_widget_get_parent (widget);
1331   if (GTK_IS_MENU_BAR (parent))
1332     {
1333       pack_dir = gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (parent));
1334       child_pack_dir = gtk_menu_bar_get_child_pack_direction (GTK_MENU_BAR (parent));
1335     }
1336   else
1337     {
1338       pack_dir = GTK_PACK_DIRECTION_LTR;
1339       child_pack_dir = GTK_PACK_DIRECTION_LTR;
1340     }
1341
1342   gtk_widget_set_allocation (widget, allocation);
1343
1344   child = gtk_bin_get_child (bin);
1345   if (child)
1346     {
1347       GtkRequisition child_requisition;
1348       GtkStyle *style;
1349       guint horizontal_padding;
1350       guint border_width;
1351
1352       style = gtk_widget_get_style (widget);
1353       gtk_widget_style_get (widget,
1354                             "horizontal-padding", &horizontal_padding,
1355                             NULL);
1356
1357       border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1358       child_allocation.x = border_width + style->xthickness;
1359       child_allocation.y = border_width + style->ythickness;
1360
1361       if ((pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) &&
1362           (child_pack_dir == GTK_PACK_DIRECTION_LTR || child_pack_dir == GTK_PACK_DIRECTION_RTL))
1363         child_allocation.x += horizontal_padding;
1364       else if ((pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT) &&
1365                (child_pack_dir == GTK_PACK_DIRECTION_TTB || child_pack_dir == GTK_PACK_DIRECTION_BTT))
1366         child_allocation.y += horizontal_padding;
1367
1368       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
1369       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
1370
1371       if (child_pack_dir == GTK_PACK_DIRECTION_LTR ||
1372           child_pack_dir == GTK_PACK_DIRECTION_RTL)
1373         {
1374           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_RTL))
1375             child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
1376           child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
1377         }
1378       else
1379         {
1380           if ((direction == GTK_TEXT_DIR_LTR) == (child_pack_dir != GTK_PACK_DIRECTION_BTT))
1381             child_allocation.y += GTK_MENU_ITEM (widget)->toggle_size;
1382           child_allocation.height -= GTK_MENU_ITEM (widget)->toggle_size;
1383         }
1384
1385       child_allocation.x += allocation->x;
1386       child_allocation.y += allocation->y;
1387
1388       gtk_widget_get_preferred_size (child, &child_requisition, NULL);
1389       if (menu_item->submenu && menu_item->show_submenu_indicator) 
1390         {
1391           if (direction == GTK_TEXT_DIR_RTL)
1392             child_allocation.x += child_requisition.height;
1393           child_allocation.width -= child_requisition.height;
1394         }
1395       
1396       if (child_allocation.width < 1)
1397         child_allocation.width = 1;
1398
1399       gtk_widget_size_allocate (child, &child_allocation);
1400     }
1401
1402   if (gtk_widget_get_realized (widget))
1403     gdk_window_move_resize (menu_item->event_window,
1404                             allocation->x, allocation->y,
1405                             allocation->width, allocation->height);
1406
1407   if (menu_item->submenu)
1408     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
1409 }
1410
1411 static void
1412 gtk_menu_item_realize (GtkWidget *widget)
1413 {
1414   GtkAllocation allocation;
1415   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1416   GdkWindow *window;
1417   GdkWindowAttr attributes;
1418   gint attributes_mask;
1419
1420   gtk_widget_set_realized (widget, TRUE);
1421
1422   window = gtk_widget_get_parent_window (widget);
1423   gtk_widget_set_window (widget, window);
1424   g_object_ref (window);
1425
1426   gtk_widget_get_allocation (widget, &allocation);
1427
1428   attributes.x = allocation.x;
1429   attributes.y = allocation.y;
1430   attributes.width = allocation.width;
1431   attributes.height = allocation.height;
1432   attributes.window_type = GDK_WINDOW_CHILD;
1433   attributes.wclass = GDK_INPUT_ONLY;
1434   attributes.event_mask = (gtk_widget_get_events (widget) |
1435                            GDK_BUTTON_PRESS_MASK |
1436                            GDK_BUTTON_RELEASE_MASK |
1437                            GDK_ENTER_NOTIFY_MASK |
1438                            GDK_LEAVE_NOTIFY_MASK |
1439                            GDK_POINTER_MOTION_MASK);
1440
1441   attributes_mask = GDK_WA_X | GDK_WA_Y;
1442
1443   menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1444                                             &attributes, attributes_mask);
1445   gdk_window_set_user_data (menu_item->event_window, widget);
1446
1447   gtk_widget_style_attach (widget);
1448 }
1449
1450 static void
1451 gtk_menu_item_unrealize (GtkWidget *widget)
1452 {
1453   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1454
1455   gdk_window_set_user_data (menu_item->event_window, NULL);
1456   gdk_window_destroy (menu_item->event_window);
1457   menu_item->event_window = NULL;
1458
1459   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unrealize (widget);
1460 }
1461
1462 static void
1463 gtk_menu_item_map (GtkWidget *widget)
1464 {
1465   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1466   
1467   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->map (widget);
1468
1469   gdk_window_show (menu_item->event_window);
1470 }
1471
1472 static void
1473 gtk_menu_item_unmap (GtkWidget *widget)
1474 {
1475   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1476     
1477   gdk_window_hide (menu_item->event_window);
1478
1479   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->unmap (widget);
1480 }
1481
1482 static gboolean
1483 gtk_menu_item_enter (GtkWidget        *widget,
1484                      GdkEventCrossing *event)
1485 {
1486   g_return_val_if_fail (event != NULL, FALSE);
1487
1488   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent *) event);
1489 }
1490
1491 static gboolean
1492 gtk_menu_item_leave (GtkWidget        *widget,
1493                      GdkEventCrossing *event)
1494 {
1495   g_return_val_if_fail (event != NULL, FALSE);
1496
1497   return gtk_widget_event (gtk_widget_get_parent (widget), (GdkEvent*) event);
1498 }
1499
1500 static gboolean
1501 gtk_menu_item_draw (GtkWidget *widget,
1502                     cairo_t   *cr)
1503 {
1504   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1505   GtkStateType state_type;
1506   GtkShadowType shadow_type, selected_shadow_type;
1507   GtkStyle *style;
1508   GtkWidget *child;
1509   GdkWindow *window;
1510   gint x, y, w, h, width, height;
1511   guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1512
1513   state_type = gtk_widget_get_state (widget);
1514   style = gtk_widget_get_style (widget);
1515   window = gtk_widget_get_window (widget);
1516   width = gtk_widget_get_allocated_width (widget);
1517   height = gtk_widget_get_allocated_height (widget);
1518
1519   x = border_width;
1520   y = border_width;
1521   w = width - border_width * 2;
1522   h = height - border_width * 2;
1523
1524   child = gtk_bin_get_child (GTK_BIN (menu_item));
1525   
1526   if (child && state_type == GTK_STATE_PRELIGHT)
1527     {
1528       gtk_widget_style_get (widget,
1529                             "selected-shadow-type", &selected_shadow_type,
1530                             NULL);
1531       gtk_paint_box (style,
1532                      cr,
1533                      GTK_STATE_PRELIGHT,
1534                      selected_shadow_type,
1535                      widget, "menuitem",
1536                      x, y, w, h);
1537     }
1538
1539   if (menu_item->submenu && menu_item->show_submenu_indicator)
1540     {
1541       gint arrow_x, arrow_y;
1542       gint arrow_size;
1543       guint horizontal_padding;
1544       GtkTextDirection direction;
1545       GtkArrowType arrow_type;
1546
1547       direction = gtk_widget_get_direction (widget);
1548   
1549       gtk_widget_style_get (widget,
1550                             "horizontal-padding", &horizontal_padding,
1551                             NULL);
1552
1553       get_arrow_size (widget, child, &arrow_size);
1554
1555       shadow_type = GTK_SHADOW_OUT;
1556       if (state_type == GTK_STATE_PRELIGHT)
1557         shadow_type = GTK_SHADOW_IN;
1558
1559       if (direction == GTK_TEXT_DIR_LTR)
1560         {
1561           arrow_x = x + w - horizontal_padding - arrow_size;
1562           arrow_type = GTK_ARROW_RIGHT;
1563         }
1564       else
1565         {
1566           arrow_x = x + horizontal_padding;
1567           arrow_type = GTK_ARROW_LEFT;
1568         }
1569
1570       arrow_y = y + (h - arrow_size) / 2;
1571
1572       gtk_paint_arrow (style, cr,
1573                        state_type, shadow_type, 
1574                        widget, "menuitem", 
1575                        arrow_type, TRUE,
1576                        arrow_x, arrow_y,
1577                        arrow_size, arrow_size);
1578     }
1579   else if (!child)
1580     {
1581       gboolean wide_separators;
1582       gint     separator_height;
1583       guint    horizontal_padding;
1584
1585       gtk_widget_style_get (widget,
1586                             "wide-separators",    &wide_separators,
1587                             "separator-height",   &separator_height,
1588                             "horizontal-padding", &horizontal_padding,
1589                             NULL);
1590
1591       if (wide_separators)
1592         gtk_paint_box (style, cr,
1593                        GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT,
1594                        widget, "hseparator",
1595                        horizontal_padding + style->xthickness,
1596                        (height - separator_height - style->ythickness) / 2,
1597                        width - 2 * (horizontal_padding + style->xthickness),
1598                        separator_height);
1599       else
1600         gtk_paint_hline (style, cr,
1601                          GTK_STATE_NORMAL, widget, "menuitem",
1602                          horizontal_padding + style->xthickness,
1603                          width - horizontal_padding - style->xthickness - 1,
1604                          (height - style->ythickness) / 2);
1605     }
1606
1607   GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->draw (widget, cr);
1608
1609   return FALSE;
1610 }
1611
1612 static void
1613 gtk_real_menu_item_select (GtkMenuItem *menu_item)
1614 {
1615   gboolean touchscreen_mode;
1616
1617   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_item)),
1618                 "gtk-touchscreen-mode", &touchscreen_mode,
1619                 NULL);
1620
1621   if (!touchscreen_mode &&
1622       menu_item->submenu &&
1623       (!gtk_widget_get_mapped (menu_item->submenu) ||
1624        GTK_MENU (menu_item->submenu)->tearoff_active))
1625     {
1626       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1627     }
1628
1629   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
1630   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1631 }
1632
1633 static void
1634 gtk_real_menu_item_deselect (GtkMenuItem *menu_item)
1635 {
1636   if (menu_item->submenu)
1637     _gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
1638
1639   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
1640   gtk_widget_queue_draw (GTK_WIDGET (menu_item));
1641 }
1642
1643 static gboolean
1644 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
1645                                  gboolean   group_cycling)
1646 {
1647   GtkWidget *parent;
1648
1649   parent = gtk_widget_get_parent (widget);
1650
1651   if (GTK_IS_MENU_SHELL (parent))
1652     _gtk_menu_shell_set_keyboard_mode (GTK_MENU_SHELL (parent), TRUE);
1653
1654   if (group_cycling &&
1655       parent &&
1656       GTK_IS_MENU_SHELL (parent) &&
1657       GTK_MENU_SHELL (parent)->active)
1658     {
1659       gtk_menu_shell_select_item (GTK_MENU_SHELL (parent),
1660                                   widget);
1661     }
1662   else
1663     g_signal_emit (widget, menu_item_signals[ACTIVATE_ITEM], 0);
1664   
1665   return TRUE;
1666 }
1667
1668 static void 
1669 gtk_real_menu_item_activate (GtkMenuItem *menu_item)
1670 {
1671   GtkMenuItemPrivate *priv;
1672
1673   priv = GET_PRIVATE (menu_item);
1674
1675   if (priv->action)
1676     gtk_action_activate (priv->action);
1677 }
1678
1679
1680 static void
1681 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
1682 {
1683   GtkMenuItemPrivate *priv;
1684   GtkWidget *parent;
1685   GtkWidget *widget;
1686
1687   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1688
1689   priv   = GET_PRIVATE (menu_item);
1690   widget = GTK_WIDGET (menu_item);
1691   parent = gtk_widget_get_parent (widget);
1692
1693   if (parent && GTK_IS_MENU_SHELL (parent))
1694     {
1695       GtkMenuShell *menu_shell = GTK_MENU_SHELL (parent);
1696
1697       if (menu_item->submenu == NULL)
1698         gtk_menu_shell_activate_item (menu_shell,
1699                                       widget, TRUE);
1700       else
1701         {
1702           _gtk_menu_shell_activate (menu_shell);
1703
1704           gtk_menu_shell_select_item (menu_shell, widget);
1705           _gtk_menu_item_popup_submenu (widget, FALSE);
1706
1707           gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1708         }
1709     }
1710 }
1711
1712 static void
1713 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
1714                                         gint        *requisition)
1715 {
1716   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1717
1718   *requisition = 0;
1719 }
1720
1721 static void
1722 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
1723                                          gint         allocation)
1724 {
1725   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1726
1727   menu_item->toggle_size = allocation;
1728 }
1729
1730 static void
1731 gtk_real_menu_item_set_label (GtkMenuItem *menu_item,
1732                               const gchar *label)
1733 {
1734   GtkWidget *child;
1735
1736   gtk_menu_item_ensure_label (menu_item);
1737
1738   child = gtk_bin_get_child (GTK_BIN (menu_item));
1739   if (GTK_IS_LABEL (child))
1740     {
1741       gtk_label_set_label (GTK_LABEL (child), label ? label : "");
1742       
1743       g_object_notify (G_OBJECT (menu_item), "label");
1744     }
1745 }
1746
1747 static G_CONST_RETURN gchar *
1748 gtk_real_menu_item_get_label (GtkMenuItem *menu_item)
1749 {
1750   GtkWidget *child;
1751
1752   gtk_menu_item_ensure_label (menu_item);
1753
1754   child = gtk_bin_get_child (GTK_BIN (menu_item));
1755   if (GTK_IS_LABEL (child))
1756     return gtk_label_get_label (GTK_LABEL (child));
1757
1758   return NULL;
1759 }
1760
1761 static void
1762 free_timeval (GTimeVal *val)
1763 {
1764   g_slice_free (GTimeVal, val);
1765 }
1766
1767 static void
1768 gtk_menu_item_real_popup_submenu (GtkWidget *widget,
1769                                   gboolean   remember_exact_time)
1770 {
1771   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1772   GtkWidget *parent;
1773
1774   parent = gtk_widget_get_parent (widget);
1775
1776   if (gtk_widget_is_sensitive (menu_item->submenu) && parent)
1777     {
1778       gboolean take_focus;
1779       GtkMenuPositionFunc menu_position_func;
1780
1781       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (parent));
1782       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
1783                                      take_focus);
1784
1785       if (remember_exact_time)
1786         {
1787           GTimeVal *popup_time = g_slice_new0 (GTimeVal);
1788
1789           g_get_current_time (popup_time);
1790
1791           g_object_set_data_full (G_OBJECT (menu_item->submenu),
1792                                   "gtk-menu-exact-popup-time", popup_time,
1793                                   (GDestroyNotify) free_timeval);
1794         }
1795       else
1796         {
1797           g_object_set_data (G_OBJECT (menu_item->submenu),
1798                              "gtk-menu-exact-popup-time", NULL);
1799         }
1800
1801       /* gtk_menu_item_position_menu positions the submenu from the
1802        * menuitems position. If the menuitem doesn't have a window,
1803        * that doesn't work. In that case we use the default
1804        * positioning function instead which places the submenu at the
1805        * mouse cursor.
1806        */
1807       if (gtk_widget_get_window (widget))
1808         menu_position_func = gtk_menu_item_position_menu;
1809       else
1810         menu_position_func = NULL;
1811
1812       gtk_menu_popup (GTK_MENU (menu_item->submenu),
1813                       parent,
1814                       widget,
1815                       menu_position_func,
1816                       menu_item,
1817                       GTK_MENU_SHELL (parent)->button,
1818                       0);
1819     }
1820
1821   /* Enable themeing of the parent menu item depending on whether
1822    * its submenu is shown or not.
1823    */
1824   gtk_widget_queue_draw (widget);
1825 }
1826
1827 static gint
1828 gtk_menu_item_popup_timeout (gpointer data)
1829 {
1830   GtkMenuItem *menu_item;
1831   GtkWidget *parent;
1832   
1833   menu_item = GTK_MENU_ITEM (data);
1834
1835   parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
1836
1837   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->active) || 
1838       (GTK_IS_MENU (parent) && GTK_MENU (parent)->torn_off))
1839     {
1840       gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
1841       if (menu_item->timer_from_keypress && menu_item->submenu)
1842         GTK_MENU_SHELL (menu_item->submenu)->ignore_enter = TRUE;
1843     }
1844
1845   menu_item->timer = 0;
1846
1847   return FALSE;  
1848 }
1849
1850 static gint
1851 get_popup_delay (GtkWidget *widget)
1852 {
1853   GtkWidget *parent;
1854
1855   parent = gtk_widget_get_parent (widget);
1856   if (GTK_IS_MENU_SHELL (parent))
1857     {
1858       return _gtk_menu_shell_get_popup_delay (GTK_MENU_SHELL (parent));
1859     }
1860   else
1861     {
1862       gint popup_delay;
1863
1864       g_object_get (gtk_widget_get_settings (widget),
1865                     "gtk-menu-popup-delay", &popup_delay,
1866                     NULL);
1867
1868       return popup_delay;
1869     }
1870 }
1871
1872 void
1873 _gtk_menu_item_popup_submenu (GtkWidget *widget,
1874                               gboolean   with_delay)
1875 {
1876   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
1877
1878   if (menu_item->timer)
1879     {
1880       g_source_remove (menu_item->timer);
1881       menu_item->timer = 0;
1882       with_delay = FALSE;
1883     }
1884
1885   if (with_delay)
1886     {
1887       gint popup_delay = get_popup_delay (widget);
1888
1889       if (popup_delay > 0)
1890         {
1891           GdkEvent *event = gtk_get_current_event ();
1892
1893           menu_item->timer = gdk_threads_add_timeout (popup_delay,
1894                                                       gtk_menu_item_popup_timeout,
1895                                                       menu_item);
1896
1897           if (event &&
1898               event->type != GDK_BUTTON_PRESS &&
1899               event->type != GDK_ENTER_NOTIFY)
1900             menu_item->timer_from_keypress = TRUE;
1901           else
1902             menu_item->timer_from_keypress = FALSE;
1903
1904           if (event)
1905             gdk_event_free (event);
1906
1907           return;
1908         }
1909     }
1910
1911   gtk_menu_item_real_popup_submenu (widget, FALSE);
1912 }
1913
1914 void
1915 _gtk_menu_item_popdown_submenu (GtkWidget *widget)
1916 {
1917   GtkMenuItem *menu_item;
1918
1919   menu_item = GTK_MENU_ITEM (widget);
1920
1921   if (menu_item->submenu)
1922     {
1923       g_object_set_data (G_OBJECT (menu_item->submenu),
1924                          "gtk-menu-exact-popup-time", NULL);
1925
1926       if (menu_item->timer)
1927         {
1928           g_source_remove (menu_item->timer);
1929           menu_item->timer = 0;
1930         }
1931       else
1932         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
1933
1934       gtk_widget_queue_draw (widget);
1935     }
1936 }
1937
1938 static void
1939 get_offsets (GtkMenu *menu,
1940              gint    *horizontal_offset,
1941              gint    *vertical_offset)
1942 {
1943   gint vertical_padding;
1944   gint horizontal_padding;
1945   
1946   gtk_widget_style_get (GTK_WIDGET (menu),
1947                         "horizontal-offset", horizontal_offset,
1948                         "vertical-offset", vertical_offset,
1949                         "horizontal-padding", &horizontal_padding,
1950                         "vertical-padding", &vertical_padding,
1951                         NULL);
1952
1953   *vertical_offset -= gtk_widget_get_style (GTK_WIDGET (menu))->ythickness;
1954   *vertical_offset -= vertical_padding;
1955   *horizontal_offset += horizontal_padding;
1956 }
1957
1958 static void
1959 gtk_menu_item_position_menu (GtkMenu  *menu,
1960                              gint     *x,
1961                              gint     *y,
1962                              gboolean *push_in,
1963                              gpointer  user_data)
1964 {
1965   GtkAllocation allocation;
1966   GtkMenuItem *menu_item;
1967   GtkWidget *widget;
1968   GtkMenuItem *parent_menu_item;
1969   GtkRequisition requisition;
1970   GtkWidget *parent;
1971   GdkScreen *screen;
1972   gint twidth, theight;
1973   gint tx, ty;
1974   GtkTextDirection direction;
1975   GdkRectangle monitor;
1976   gint monitor_num;
1977   gint horizontal_offset;
1978   gint vertical_offset;
1979   gint parent_xthickness;
1980   gint available_left, available_right;
1981
1982   g_return_if_fail (menu != NULL);
1983   g_return_if_fail (x != NULL);
1984   g_return_if_fail (y != NULL);
1985
1986   menu_item = GTK_MENU_ITEM (user_data);
1987   widget = GTK_WIDGET (user_data);
1988
1989   if (push_in)
1990     *push_in = FALSE;
1991
1992   direction = gtk_widget_get_direction (widget);
1993
1994   gtk_widget_get_preferred_size (GTK_WIDGET (menu), &requisition, NULL);
1995   twidth = requisition.width;
1996   theight = requisition.height;
1997
1998   screen = gtk_widget_get_screen (GTK_WIDGET (menu));
1999   monitor_num = gdk_screen_get_monitor_at_window (screen, menu_item->event_window);
2000   if (monitor_num < 0)
2001     monitor_num = 0;
2002   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2003
2004   if (!gdk_window_get_origin (gtk_widget_get_window (widget),
2005                               &tx, &ty))
2006     {
2007       g_warning ("Menu not on screen");
2008       return;
2009     }
2010
2011   gtk_widget_get_allocation (widget, &allocation);
2012
2013   tx += allocation.x;
2014   ty += allocation.y;
2015
2016   get_offsets (menu, &horizontal_offset, &vertical_offset);
2017
2018   available_left = tx - monitor.x;
2019   available_right = monitor.x + monitor.width - (tx + allocation.width);
2020
2021   parent = gtk_widget_get_parent (widget);
2022   if (GTK_IS_MENU_BAR (parent))
2023     {
2024       menu_item->from_menubar = TRUE;
2025     }
2026   else if (GTK_IS_MENU (parent))
2027     {
2028       if (GTK_MENU (parent)->parent_menu_item)
2029         menu_item->from_menubar = GTK_MENU_ITEM (GTK_MENU (parent)->parent_menu_item)->from_menubar;
2030       else
2031         menu_item->from_menubar = FALSE;
2032     }
2033   else
2034     {
2035       menu_item->from_menubar = FALSE;
2036     }
2037   
2038   switch (menu_item->submenu_placement)
2039     {
2040     case GTK_TOP_BOTTOM:
2041       if (direction == GTK_TEXT_DIR_LTR)
2042         menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2043       else 
2044         {
2045           menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2046           tx += allocation.width - twidth;
2047         }
2048       if ((ty + allocation.height + theight) <= monitor.y + monitor.height)
2049         ty += allocation.height;
2050       else if ((ty - theight) >= monitor.y)
2051         ty -= theight;
2052       else if (monitor.y + monitor.height - (ty + allocation.height) > ty)
2053         ty += allocation.height;
2054       else
2055         ty -= theight;
2056       break;
2057
2058     case GTK_LEFT_RIGHT:
2059       if (GTK_IS_MENU (parent))
2060         parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->parent_menu_item);
2061       else
2062         parent_menu_item = NULL;
2063
2064       parent_xthickness = gtk_widget_get_style (parent)->xthickness;
2065
2066       if (parent_menu_item && !GTK_MENU (parent)->torn_off)
2067         {
2068           menu_item->submenu_direction = parent_menu_item->submenu_direction;
2069         }
2070       else
2071         {
2072           if (direction == GTK_TEXT_DIR_LTR)
2073             menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2074           else
2075             menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2076         }
2077
2078       switch (menu_item->submenu_direction)
2079         {
2080         case GTK_DIRECTION_LEFT:
2081           if (tx - twidth - parent_xthickness - horizontal_offset >= monitor.x ||
2082               available_left >= available_right)
2083             tx -= twidth + parent_xthickness + horizontal_offset;
2084           else
2085             {
2086               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
2087               tx += allocation.width + parent_xthickness + horizontal_offset;
2088             }
2089           break;
2090
2091         case GTK_DIRECTION_RIGHT:
2092           if (tx + allocation.width + parent_xthickness + horizontal_offset + twidth <= monitor.x + monitor.width ||
2093               available_right >= available_left)
2094             tx += allocation.width + parent_xthickness + horizontal_offset;
2095           else
2096             {
2097               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
2098               tx -= twidth + parent_xthickness + horizontal_offset;
2099             }
2100           break;
2101         }
2102
2103       ty += vertical_offset;
2104       
2105       /* If the height of the menu doesn't fit we move it upward. */
2106       ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight));
2107       break;
2108     }
2109
2110   /* If we have negative, tx, here it is because we can't get
2111    * the menu all the way on screen. Favor the left portion.
2112    */
2113   *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth));
2114   *y = ty;
2115
2116   gtk_menu_set_monitor (menu, monitor_num);
2117
2118   if (!gtk_widget_get_visible (menu->toplevel))
2119     {
2120       gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar?
2121                                 GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
2122     }
2123 }
2124
2125 /**
2126  * gtk_menu_item_set_right_justified:
2127  * @menu_item: a #GtkMenuItem.
2128  * @right_justified: if %TRUE the menu item will appear at the 
2129  *   far right if added to a menu bar.
2130  * 
2131  * Sets whether the menu item appears justified at the right
2132  * side of a menu bar. This was traditionally done for "Help" menu
2133  * items, but is now considered a bad idea. (If the widget
2134  * layout is reversed for a right-to-left language like Hebrew
2135  * or Arabic, right-justified-menu-items appear at the left.)
2136  **/
2137 void
2138 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
2139                                    gboolean     right_justified)
2140 {
2141   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2142
2143   right_justified = right_justified != FALSE;
2144
2145   if (right_justified != menu_item->right_justify)
2146     {
2147       menu_item->right_justify = right_justified;
2148       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
2149     }
2150 }
2151
2152 /**
2153  * gtk_menu_item_get_right_justified:
2154  * @menu_item: a #GtkMenuItem
2155  * 
2156  * Gets whether the menu item appears justified at the right
2157  * side of the menu bar.
2158  * 
2159  * Return value: %TRUE if the menu item will appear at the
2160  *   far right if added to a menu bar.
2161  **/
2162 gboolean
2163 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
2164 {
2165   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2166   
2167   return menu_item->right_justify;
2168 }
2169
2170
2171 static void
2172 gtk_menu_item_show_all (GtkWidget *widget)
2173 {
2174   GtkMenuItem *menu_item;
2175
2176   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
2177
2178   menu_item = GTK_MENU_ITEM (widget);
2179
2180   /* show children including submenu */
2181   if (menu_item->submenu)
2182     gtk_widget_show_all (menu_item->submenu);
2183   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2184
2185   gtk_widget_show (widget);
2186 }
2187
2188 static gboolean
2189 gtk_menu_item_can_activate_accel (GtkWidget *widget,
2190                                   guint      signal_id)
2191 {
2192   GtkWidget *parent;
2193
2194   parent = gtk_widget_get_parent (widget);
2195   /* Chain to the parent GtkMenu for further checks */
2196   return (gtk_widget_is_sensitive (widget) && gtk_widget_get_visible (widget) &&
2197           parent && gtk_widget_can_activate_accel (parent, signal_id));
2198 }
2199
2200 static void
2201 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
2202                                   gpointer data)
2203 {
2204   const gchar **path_p = data;
2205
2206   if (!*path_p)
2207     {
2208       if (GTK_IS_LABEL (widget))
2209         {
2210           *path_p = gtk_label_get_text (GTK_LABEL (widget));
2211           if (*path_p && (*path_p)[0] == 0)
2212             *path_p = NULL;
2213         }
2214       else if (GTK_IS_CONTAINER (widget))
2215         gtk_container_foreach (GTK_CONTAINER (widget),
2216                                gtk_menu_item_accel_name_foreach,
2217                                data);
2218     }
2219 }
2220
2221 static void
2222 gtk_menu_item_parent_set (GtkWidget *widget,
2223                           GtkWidget *previous_parent)
2224 {
2225   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
2226   GtkMenu *menu;
2227   GtkWidget *parent;
2228
2229   parent = gtk_widget_get_parent (widget);
2230   menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
2231
2232   if (menu)
2233     _gtk_menu_item_refresh_accel_path (menu_item,
2234                                        menu->accel_path,
2235                                        menu->accel_group,
2236                                        TRUE);
2237
2238   if (GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set)
2239     GTK_WIDGET_CLASS (gtk_menu_item_parent_class)->parent_set (widget, previous_parent);
2240 }
2241
2242 void
2243 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
2244                                    const gchar   *prefix,
2245                                    GtkAccelGroup *accel_group,
2246                                    gboolean       group_changed)
2247 {
2248   const gchar *path;
2249   GtkWidget *widget;
2250
2251   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2252   g_return_if_fail (!accel_group || GTK_IS_ACCEL_GROUP (accel_group));
2253
2254   widget = GTK_WIDGET (menu_item);
2255
2256   if (!accel_group)
2257     {
2258       gtk_widget_set_accel_path (widget, NULL, NULL);
2259       return;
2260     }
2261
2262   path = _gtk_widget_get_accel_path (widget, NULL);
2263   if (!path)                                    /* no active accel_path yet */
2264     {
2265       path = menu_item->accel_path;
2266       if (!path && prefix)
2267         {
2268           const gchar *postfix = NULL;
2269           gchar *new_path;
2270
2271           /* try to construct one from label text */
2272           gtk_container_foreach (GTK_CONTAINER (menu_item),
2273                                  gtk_menu_item_accel_name_foreach,
2274                                  &postfix);
2275           if (postfix)
2276             {
2277               new_path = g_strconcat (prefix, "/", postfix, NULL);
2278               path = menu_item->accel_path = (char*)g_intern_string (new_path);
2279               g_free (new_path);
2280             }
2281         }
2282       if (path)
2283         gtk_widget_set_accel_path (widget, path, accel_group);
2284     }
2285   else if (group_changed)                       /* reinstall accelerators */
2286     gtk_widget_set_accel_path (widget, path, accel_group);
2287 }
2288
2289 /**
2290  * gtk_menu_item_set_accel_path
2291  * @menu_item:  a valid #GtkMenuItem
2292  * @accel_path: (allow-none): accelerator path, corresponding to this menu item's
2293  *              functionality, or %NULL to unset the current path.
2294  *
2295  * Set the accelerator path on @menu_item, through which runtime changes of the
2296  * menu item's accelerator caused by the user can be identified and saved to
2297  * persistant storage (see gtk_accel_map_save() on this).
2298  * To setup a default accelerator for this menu item, call
2299  * gtk_accel_map_add_entry() with the same @accel_path.
2300  * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
2301  * and gtk_menu_set_accel_path() for a more convenient variant of this function.
2302  *
2303  * This function is basically a convenience wrapper that handles calling
2304  * gtk_widget_set_accel_path() with the appropriate accelerator group for
2305  * the menu item.
2306  *
2307  * Note that you do need to set an accelerator on the parent menu with
2308  * gtk_menu_set_accel_group() for this to work.
2309  *
2310  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
2311  * pass a static string, you can save some memory by interning it first with 
2312  * g_intern_static_string().
2313  */
2314 void
2315 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
2316                               const gchar *accel_path)
2317 {
2318   GtkWidget *parent;
2319   GtkWidget *widget;
2320
2321   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2322   g_return_if_fail (accel_path == NULL ||
2323                     (accel_path[0] == '<' && strchr (accel_path, '/')));
2324
2325   widget = GTK_WIDGET (menu_item);
2326
2327   /* store new path */
2328   menu_item->accel_path = (char*)g_intern_string (accel_path);
2329
2330   /* forget accelerators associated with old path */
2331   gtk_widget_set_accel_path (widget, NULL, NULL);
2332
2333   /* install accelerators associated with new path */
2334   parent = gtk_widget_get_parent (widget);
2335   if (GTK_IS_MENU (parent))
2336     {
2337       GtkMenu *menu = GTK_MENU (parent);
2338
2339       if (menu->accel_group)
2340         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2341                                            NULL,
2342                                            menu->accel_group,
2343                                            FALSE);
2344     }
2345 }
2346
2347 /**
2348  * gtk_menu_item_get_accel_path
2349  * @menu_item:  a valid #GtkMenuItem
2350  *
2351  * Retrieve the accelerator path that was previously set on @menu_item.
2352  *
2353  * See gtk_menu_item_set_accel_path() for details.
2354  *
2355  * Returns: the accelerator path corresponding to this menu item's
2356  *              functionality, or %NULL if not set
2357  *
2358  * Since: 2.14
2359  */
2360 G_CONST_RETURN gchar *
2361 gtk_menu_item_get_accel_path (GtkMenuItem *menu_item)
2362 {
2363   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2364
2365   return menu_item->accel_path;
2366 }
2367
2368 static void
2369 gtk_menu_item_forall (GtkContainer *container,
2370                       gboolean      include_internals,
2371                       GtkCallback   callback,
2372                       gpointer      callback_data)
2373 {
2374   GtkWidget *child;
2375
2376   g_return_if_fail (GTK_IS_MENU_ITEM (container));
2377   g_return_if_fail (callback != NULL);
2378
2379   child = gtk_bin_get_child (GTK_BIN (container));
2380   if (child)
2381     callback (child, callback_data);
2382 }
2383
2384 gboolean
2385 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
2386 {
2387   if ((!gtk_bin_get_child (GTK_BIN (menu_item)) &&
2388        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
2389       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
2390       !gtk_widget_is_sensitive (menu_item) ||
2391       !gtk_widget_get_visible (menu_item))
2392     return FALSE;
2393
2394   return TRUE;
2395 }
2396
2397 static void
2398 gtk_menu_item_ensure_label (GtkMenuItem *menu_item)
2399 {
2400   GtkWidget *accel_label;
2401
2402   if (!gtk_bin_get_child (GTK_BIN (menu_item)))
2403     {
2404       accel_label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
2405       gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
2406
2407       gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
2408       gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), 
2409                                         GTK_WIDGET (menu_item));
2410       gtk_widget_show (accel_label);
2411     }
2412 }
2413
2414 /**
2415  * gtk_menu_item_set_label:
2416  * @menu_item: a #GtkMenuItem
2417  * @label: the text you want to set
2418  *
2419  * Sets @text on the @menu_item label
2420  *
2421  * Since: 2.16
2422  **/
2423 void
2424 gtk_menu_item_set_label (GtkMenuItem *menu_item,
2425                          const gchar *label)
2426 {
2427   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2428
2429   GTK_MENU_ITEM_GET_CLASS (menu_item)->set_label (menu_item, label);
2430 }
2431
2432 /**
2433  * gtk_menu_item_get_label:
2434  * @menu_item: a #GtkMenuItem
2435  *
2436  * Sets @text on the @menu_item label
2437  *
2438  * Returns: The text in the @menu_item label. This is the internal
2439  *   string used by the label, and must not be modified.
2440  *
2441  * Since: 2.16
2442  **/
2443 G_CONST_RETURN gchar *
2444 gtk_menu_item_get_label (GtkMenuItem *menu_item)
2445 {
2446   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
2447
2448   return GTK_MENU_ITEM_GET_CLASS (menu_item)->get_label (menu_item);
2449 }
2450
2451 /**
2452  * gtk_menu_item_set_use_underline:
2453  * @menu_item: a #GtkMenuItem
2454  * @setting: %TRUE if underlines in the text indicate mnemonics  
2455  *
2456  * If true, an underline in the text indicates the next character should be
2457  * used for the mnemonic accelerator key.
2458  *
2459  * Since: 2.16
2460  **/
2461 void
2462 gtk_menu_item_set_use_underline (GtkMenuItem *menu_item,
2463                                  gboolean     setting)
2464 {
2465   GtkWidget *child;
2466
2467   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
2468
2469   gtk_menu_item_ensure_label (menu_item);
2470
2471   child = gtk_bin_get_child (GTK_BIN (menu_item));
2472   if (GTK_IS_LABEL (child))
2473     {
2474       gtk_label_set_use_underline (GTK_LABEL (child), setting);
2475
2476       g_object_notify (G_OBJECT (menu_item), "use-underline");
2477     }
2478 }
2479
2480 /**
2481  * gtk_menu_item_get_use_underline:
2482  * @menu_item: a #GtkMenuItem
2483  *
2484  * Checks if an underline in the text indicates the next character should be
2485  * used for the mnemonic accelerator key.
2486  *
2487  * Return value: %TRUE if an embedded underline in the label indicates
2488  *               the mnemonic accelerator key.
2489  *
2490  * Since: 2.16
2491  **/
2492 gboolean
2493 gtk_menu_item_get_use_underline (GtkMenuItem *menu_item)
2494 {
2495   GtkWidget *child;
2496
2497   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
2498
2499   gtk_menu_item_ensure_label (menu_item);
2500
2501   child = gtk_bin_get_child (GTK_BIN (menu_item));
2502   if (GTK_IS_LABEL (child))
2503     return gtk_label_get_use_underline (GTK_LABEL (child));
2504
2505   return FALSE;
2506 }