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