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