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