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