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