]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
menu: deprecate horizontal-padding and vertical-padding
[~andy/gtk] / gtk / gtkmenu.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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24
25 /**
26  * SECTION:gtkmenu
27  * @Short_description: A menu widget
28  * @Title: GtkMenu
29  *
30  * A #GtkMenu is a #GtkMenuShell that implements a drop down menu
31  * consisting of a list of #GtkMenuItem objects which can be navigated
32  * and activated by the user to perform application functions.
33  *
34  * A #GtkMenu is most commonly dropped down by activating a
35  * #GtkMenuItem in a #GtkMenuBar or popped up by activating a
36  * #GtkMenuItem in another #GtkMenu.
37  *
38  * A #GtkMenu can also be popped up by activating a #GtkComboBox.
39  * Other composite widgets such as the #GtkNotebook can pop up a
40  * #GtkMenu as well.
41  *
42  * Applications can display a #GtkMenu as a popup menu by calling the 
43  * gtk_menu_popup() function.  The example below shows how an application
44  * can pop up a menu when the 3rd mouse button is pressed.  
45  *
46  * <example>
47  * <title>Connecting the popup signal handler.</title>
48  * <programlisting>
49  *   /<!---->* connect our handler which will popup the menu *<!---->/
50  *   g_signal_connect_swapped (window, "button_press_event",
51  *      G_CALLBACK (my_popup_handler), menu);
52  * </programlisting>
53  * </example>
54  *
55  * <example>
56  * <title>Signal handler which displays a popup menu.</title>
57  * <programlisting>
58  * static gint
59  * my_popup_handler (GtkWidget *widget, GdkEvent *event)
60  * {
61  *   GtkMenu *menu;
62  *   GdkEventButton *event_button;
63  *
64  *   g_return_val_if_fail (widget != NULL, FALSE);
65  *   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
66  *   g_return_val_if_fail (event != NULL, FALSE);
67  *
68  *   /<!---->* The "widget" is the menu that was supplied when 
69  *    * g_signal_connect_swapped() was called.
70  *    *<!---->/
71  *   menu = GTK_MENU (widget);
72  *
73  *   if (event->type == GDK_BUTTON_PRESS)
74  *     {
75  *       event_button = (GdkEventButton *) event;
76  *       if (event_button->button == GDK_BUTTON_SECONDARY)
77  *         {
78  *           gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 
79  *                           event_button->button, event_button->time);
80  *           return TRUE;
81  *         }
82  *     }
83  * 
84  *   return FALSE;
85  * }
86  * </programlisting>
87  * </example>
88  */
89
90 #include "config.h"
91
92 #include <string.h>
93
94 #include  <gobject/gvaluecollector.h>
95
96 #include "gtkaccellabel.h"
97 #include "gtkaccelmap.h"
98 #include "gtkadjustment.h"
99 #include "gtkbindings.h"
100 #include "gtkcheckmenuitem.h"
101 #include "gtkmain.h"
102 #include "gtkmarshalers.h"
103 #include "gtkmenuprivate.h"
104 #include "gtkmenuitemprivate.h"
105 #include "gtkmenushellprivate.h"
106 #include "gtkwindow.h"
107 #include "gtkbox.h"
108 #include "gtkscrollbar.h"
109 #include "gtksettings.h"
110 #include "gtkprivate.h"
111 #include "gtkwidgetpath.h"
112 #include "gtkwidgetprivate.h"
113 #include "gtkdnd.h"
114 #include "gtkintl.h"
115 #include "gtktypebuiltins.h"
116 #include "gtkwidgetprivate.h"
117
118 #include "deprecated/gtktearoffmenuitem.h"
119
120
121 #include "a11y/gtkmenuaccessible.h"
122
123 #define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
124                                          * extends below the submenu
125                                          */
126
127 #define MENU_SCROLL_STEP1      8
128 #define MENU_SCROLL_STEP2     15
129 #define MENU_SCROLL_FAST_ZONE  8
130 #define MENU_SCROLL_TIMEOUT1  50
131 #define MENU_SCROLL_TIMEOUT2  20
132
133 #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
134 #define ATTACHED_MENUS "gtk-attached-menus"
135
136 typedef struct _GtkMenuAttachData  GtkMenuAttachData;
137 typedef struct _GtkMenuPopdownData GtkMenuPopdownData;
138
139 struct _GtkMenuAttachData
140 {
141   GtkWidget *attach_widget;
142   GtkMenuDetachFunc detacher;
143 };
144
145 struct _GtkMenuPopdownData
146 {
147   GtkMenu *menu;
148   GdkDevice *device;
149 };
150
151 typedef struct
152 {
153   gint left_attach;
154   gint right_attach;
155   gint top_attach;
156   gint bottom_attach;
157   gint effective_left_attach;
158   gint effective_right_attach;
159   gint effective_top_attach;
160   gint effective_bottom_attach;
161 } AttachInfo;
162
163 enum {
164   MOVE_SCROLL,
165   LAST_SIGNAL
166 };
167
168 enum {
169   PROP_0,
170   PROP_ACTIVE,
171   PROP_ACCEL_GROUP,
172   PROP_ACCEL_PATH,
173   PROP_ATTACH_WIDGET,
174   PROP_TEAROFF_STATE,
175   PROP_TEAROFF_TITLE,
176   PROP_MONITOR,
177   PROP_RESERVE_TOGGLE_SIZE
178 };
179
180 enum {
181   CHILD_PROP_0,
182   CHILD_PROP_LEFT_ATTACH,
183   CHILD_PROP_RIGHT_ATTACH,
184   CHILD_PROP_TOP_ATTACH,
185   CHILD_PROP_BOTTOM_ATTACH
186 };
187
188 static void     gtk_menu_set_property      (GObject          *object,
189                                             guint             prop_id,
190                                             const GValue     *value,
191                                             GParamSpec       *pspec);
192 static void     gtk_menu_get_property      (GObject          *object,
193                                             guint             prop_id,
194                                             GValue           *value,
195                                             GParamSpec       *pspec);
196 static void     gtk_menu_set_child_property(GtkContainer     *container,
197                                             GtkWidget        *child,
198                                             guint             property_id,
199                                             const GValue     *value,
200                                             GParamSpec       *pspec);
201 static void     gtk_menu_get_child_property(GtkContainer     *container,
202                                             GtkWidget        *child,
203                                             guint             property_id,
204                                             GValue           *value,
205                                             GParamSpec       *pspec);
206 static void     gtk_menu_destroy           (GtkWidget        *widget);
207 static void     gtk_menu_realize           (GtkWidget        *widget);
208 static void     gtk_menu_unrealize         (GtkWidget        *widget);
209 static void     gtk_menu_size_allocate     (GtkWidget        *widget,
210                                             GtkAllocation    *allocation);
211 static void     gtk_menu_show              (GtkWidget        *widget);
212 static gboolean gtk_menu_draw              (GtkWidget        *widget,
213                                             cairo_t          *cr);
214 static gboolean gtk_menu_key_press         (GtkWidget        *widget,
215                                             GdkEventKey      *event);
216 static gboolean gtk_menu_scroll            (GtkWidget        *widget,
217                                             GdkEventScroll   *event);
218 static gboolean gtk_menu_button_press      (GtkWidget        *widget,
219                                             GdkEventButton   *event);
220 static gboolean gtk_menu_button_release    (GtkWidget        *widget,
221                                             GdkEventButton   *event);
222 static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
223                                             GdkEventMotion   *event);
224 static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
225                                             GdkEventCrossing *event);
226 static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
227                                             GdkEventCrossing *event);
228 static void     gtk_menu_scroll_to         (GtkMenu          *menu,
229                                             gint              offset);
230 static void     gtk_menu_grab_notify       (GtkWidget        *widget,
231                                             gboolean          was_grabbed);
232 static gboolean gtk_menu_captured_event    (GtkWidget        *widget,
233                                             GdkEvent         *event);
234
235
236 static void     gtk_menu_stop_scrolling         (GtkMenu  *menu);
237 static void     gtk_menu_remove_scroll_timeout  (GtkMenu  *menu);
238 static gboolean gtk_menu_scroll_timeout         (gpointer  data);
239
240 static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
241                                               GtkWidget       *menu_item);
242 static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
243                                             GtkWidget        *menu_item);
244 static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
245                                             GtkWidget        *child,
246                                             gint              position);
247 static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
248                                             GtkMenu          *menu);
249 static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
250                                             gint              event_x,
251                                             gint              event_y,
252                                             gboolean          enter,
253                                             gboolean          motion);
254 static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
255                                             gint             width);
256 static void     gtk_menu_style_updated     (GtkWidget        *widget);
257 static gboolean gtk_menu_focus             (GtkWidget        *widget,
258                                             GtkDirectionType direction);
259 static gint     gtk_menu_get_popup_delay   (GtkMenuShell     *menu_shell);
260 static void     gtk_menu_move_current      (GtkMenuShell     *menu_shell,
261                                             GtkMenuDirectionType direction);
262 static void     gtk_menu_real_move_scroll  (GtkMenu          *menu,
263                                             GtkScrollType     type);
264
265 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
266 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
267 static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
268                                                         gint              event_x,
269                                                         gint              event_y);
270 static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
271                                                         GtkMenuItem      *menu_item,
272                                                         GdkEventCrossing *event);
273  
274 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
275 static void gtk_menu_show_all       (GtkWidget         *widget);
276 static void gtk_menu_position       (GtkMenu           *menu,
277                                      gboolean           set_scroll_offset);
278 static void gtk_menu_reparent       (GtkMenu           *menu,
279                                      GtkWidget         *new_parent,
280                                      gboolean           unrealize);
281 static void gtk_menu_remove         (GtkContainer      *menu,
282                                      GtkWidget         *widget);
283
284 static void gtk_menu_update_title   (GtkMenu           *menu);
285
286 static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
287 static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);
288
289 static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
290                                                   guint      signal_id);
291 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
292                                            gboolean group_changed);
293
294 static void gtk_menu_get_preferred_width            (GtkWidget           *widget,
295                                                      gint                *minimum_size,
296                                                      gint                *natural_size);
297 static void gtk_menu_get_preferred_height           (GtkWidget           *widget,
298                                                      gint                *minimum_size,
299                                                      gint                *natural_size);
300 static void gtk_menu_get_preferred_height_for_width (GtkWidget           *widget,
301                                                      gint                 for_size,
302                                                      gint                *minimum_size,
303                                                      gint                *natural_size);
304
305
306 static const gchar attach_data_key[] = "gtk-menu-attach-data";
307
308 static guint menu_signals[LAST_SIGNAL] = { 0 };
309
310 G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL)
311
312 static void
313 menu_queue_resize (GtkMenu *menu)
314 {
315   GtkMenuPrivate *priv = menu->priv;
316
317   priv->have_layout = FALSE;
318   gtk_widget_queue_resize (GTK_WIDGET (menu));
319 }
320
321 static void
322 attach_info_free (AttachInfo *info)
323 {
324   g_slice_free (AttachInfo, info);
325 }
326
327 static AttachInfo *
328 get_attach_info (GtkWidget *child)
329 {
330   GObject *object = G_OBJECT (child);
331   AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
332
333   if (!ai)
334     {
335       ai = g_slice_new0 (AttachInfo);
336       g_object_set_data_full (object, I_(ATTACH_INFO_KEY), ai,
337                               (GDestroyNotify) attach_info_free);
338     }
339
340   return ai;
341 }
342
343 static gboolean
344 is_grid_attached (AttachInfo *ai)
345 {
346   return (ai->left_attach >= 0 &&
347           ai->right_attach >= 0 &&
348           ai->top_attach >= 0 &&
349           ai->bottom_attach >= 0);
350 }
351
352 static void
353 menu_ensure_layout (GtkMenu *menu)
354 {
355   GtkMenuPrivate *priv = menu->priv;
356
357   if (!priv->have_layout)
358     {
359       GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
360       GList *l;
361       gchar *row_occupied;
362       gint current_row;
363       gint max_right_attach;
364       gint max_bottom_attach;
365
366       /* Find extents of gridded portion
367        */
368       max_right_attach = 1;
369       max_bottom_attach = 0;
370
371       for (l = menu_shell->priv->children; l; l = l->next)
372         {
373           GtkWidget *child = l->data;
374           AttachInfo *ai = get_attach_info (child);
375
376           if (is_grid_attached (ai))
377             {
378               max_bottom_attach = MAX (max_bottom_attach, ai->bottom_attach);
379               max_right_attach = MAX (max_right_attach, ai->right_attach);
380             }
381         }
382
383       /* Find empty rows */
384       row_occupied = g_malloc0 (max_bottom_attach);
385
386       for (l = menu_shell->priv->children; l; l = l->next)
387         {
388           GtkWidget *child = l->data;
389           AttachInfo *ai = get_attach_info (child);
390
391           if (is_grid_attached (ai))
392             {
393               gint i;
394
395               for (i = ai->top_attach; i < ai->bottom_attach; i++)
396                 row_occupied[i] = TRUE;
397             }
398         }
399
400       /* Lay non-grid-items out in those rows
401        */
402       current_row = 0;
403       for (l = menu_shell->priv->children; l; l = l->next)
404         {
405           GtkWidget *child = l->data;
406           AttachInfo *ai = get_attach_info (child);
407
408           if (!is_grid_attached (ai))
409             {
410               while (current_row < max_bottom_attach && row_occupied[current_row])
411                 current_row++;
412
413               ai->effective_left_attach = 0;
414               ai->effective_right_attach = max_right_attach;
415               ai->effective_top_attach = current_row;
416               ai->effective_bottom_attach = current_row + 1;
417
418               current_row++;
419             }
420           else
421             {
422               ai->effective_left_attach = ai->left_attach;
423               ai->effective_right_attach = ai->right_attach;
424               ai->effective_top_attach = ai->top_attach;
425               ai->effective_bottom_attach = ai->bottom_attach;
426             }
427         }
428
429       g_free (row_occupied);
430
431       priv->n_rows = MAX (current_row, max_bottom_attach);
432       priv->n_columns = max_right_attach;
433       priv->have_layout = TRUE;
434     }
435 }
436
437
438 static gint
439 gtk_menu_get_n_columns (GtkMenu *menu)
440 {
441   GtkMenuPrivate *priv = menu->priv;
442
443   menu_ensure_layout (menu);
444
445   return priv->n_columns;
446 }
447
448 static gint
449 gtk_menu_get_n_rows (GtkMenu *menu)
450 {
451   GtkMenuPrivate *priv = menu->priv;
452
453   menu_ensure_layout (menu);
454
455   return priv->n_rows;
456 }
457
458 static void
459 get_effective_child_attach (GtkWidget *child,
460                             int       *l,
461                             int       *r,
462                             int       *t,
463                             int       *b)
464 {
465   GtkMenu *menu = GTK_MENU (gtk_widget_get_parent (child));
466   AttachInfo *ai;
467   
468   menu_ensure_layout (menu);
469
470   ai = get_attach_info (child);
471
472   if (l)
473     *l = ai->effective_left_attach;
474   if (r)
475     *r = ai->effective_right_attach;
476   if (t)
477     *t = ai->effective_top_attach;
478   if (b)
479     *b = ai->effective_bottom_attach;
480
481 }
482
483 static void
484 gtk_menu_class_init (GtkMenuClass *class)
485 {
486   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
487   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
488   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
489   GtkMenuShellClass *menu_shell_class = GTK_MENU_SHELL_CLASS (class);
490   GtkBindingSet *binding_set;
491   
492   gobject_class->set_property = gtk_menu_set_property;
493   gobject_class->get_property = gtk_menu_get_property;
494
495   widget_class->destroy = gtk_menu_destroy;
496   widget_class->realize = gtk_menu_realize;
497   widget_class->unrealize = gtk_menu_unrealize;
498   widget_class->size_allocate = gtk_menu_size_allocate;
499   widget_class->show = gtk_menu_show;
500   widget_class->draw = gtk_menu_draw;
501   widget_class->scroll_event = gtk_menu_scroll;
502   widget_class->key_press_event = gtk_menu_key_press;
503   widget_class->button_press_event = gtk_menu_button_press;
504   widget_class->button_release_event = gtk_menu_button_release;
505   widget_class->motion_notify_event = gtk_menu_motion_notify;
506   widget_class->show_all = gtk_menu_show_all;
507   widget_class->enter_notify_event = gtk_menu_enter_notify;
508   widget_class->leave_notify_event = gtk_menu_leave_notify;
509   widget_class->style_updated = gtk_menu_style_updated;
510   widget_class->focus = gtk_menu_focus;
511   widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
512   widget_class->grab_notify = gtk_menu_grab_notify;
513   widget_class->get_preferred_width = gtk_menu_get_preferred_width;
514   widget_class->get_preferred_height = gtk_menu_get_preferred_height;
515   widget_class->get_preferred_height_for_width = gtk_menu_get_preferred_height_for_width;
516
517   container_class->remove = gtk_menu_remove;
518   container_class->get_child_property = gtk_menu_get_child_property;
519   container_class->set_child_property = gtk_menu_set_child_property;
520   
521   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
522   menu_shell_class->deactivate = gtk_menu_deactivate;
523   menu_shell_class->select_item = gtk_menu_select_item;
524   menu_shell_class->insert = gtk_menu_real_insert;
525   menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
526   menu_shell_class->move_current = gtk_menu_move_current;
527
528   /**
529    * GtkMenu::move-scroll:
530    * @menu: a #GtkMenu
531    * @scroll_type: a #GtkScrollType
532    */
533   menu_signals[MOVE_SCROLL] =
534     g_signal_new_class_handler (I_("move-scroll"),
535                                 G_OBJECT_CLASS_TYPE (gobject_class),
536                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
537                                 G_CALLBACK (gtk_menu_real_move_scroll),
538                                 NULL, NULL,
539                                 _gtk_marshal_VOID__ENUM,
540                                 G_TYPE_NONE, 1,
541                                 GTK_TYPE_SCROLL_TYPE);
542
543   /**
544    * GtkMenu:active:
545    *
546    * The index of the currently selected menu item, or -1 if no
547    * menu item is selected.
548    *
549    * Since: 2.14
550    **/
551   g_object_class_install_property (gobject_class,
552                                    PROP_ACTIVE,
553                                    g_param_spec_int ("active",
554                                                      P_("Active"),
555                                                      P_("The currently selected menu item"),
556                                                      -1, G_MAXINT, -1,
557                                                      GTK_PARAM_READWRITE));
558
559   /**
560    * GtkMenu:accel-group:
561    *
562    * The accel group holding accelerators for the menu.
563    *
564    * Since: 2.14
565    **/
566   g_object_class_install_property (gobject_class,
567                                    PROP_ACCEL_GROUP,
568                                    g_param_spec_object ("accel-group",
569                                                         P_("Accel Group"),
570                                                         P_("The accel group holding accelerators for the menu"),
571                                                         GTK_TYPE_ACCEL_GROUP,
572                                                         GTK_PARAM_READWRITE));
573
574   /**
575    * GtkMenu:accel-path:
576    *
577    * An accel path used to conveniently construct accel paths of child items.
578    *
579    * Since: 2.14
580    **/
581   g_object_class_install_property (gobject_class,
582                                    PROP_ACCEL_PATH,
583                                    g_param_spec_string ("accel-path",
584                                                         P_("Accel Path"),
585                                                         P_("An accel path used to conveniently construct accel paths of child items"),
586                                                         NULL,
587                                                         GTK_PARAM_READWRITE));
588
589   /**
590    * GtkMenu:attach-widget:
591    *
592    * The widget the menu is attached to. Setting this property attaches
593    * the menu without a #GtkMenuDetachFunc. If you need to use a detacher,
594    * use gtk_menu_attach_to_widget() directly.
595    *
596    * Since: 2.14
597    **/
598   g_object_class_install_property (gobject_class,
599                                    PROP_ATTACH_WIDGET,
600                                    g_param_spec_object ("attach-widget",
601                                                         P_("Attach Widget"),
602                                                         P_("The widget the menu is attached to"),
603                                                         GTK_TYPE_WIDGET,
604                                                         GTK_PARAM_READWRITE));
605
606   g_object_class_install_property (gobject_class,
607                                    PROP_TEAROFF_TITLE,
608                                    g_param_spec_string ("tearoff-title",
609                                                         P_("Tearoff Title"),
610                                                         P_("A title that may be displayed by the window manager when this menu is torn-off"),
611                                                         NULL,
612                                                         GTK_PARAM_READWRITE));
613
614   /**
615    * GtkMenu:tearoff-state:
616    *
617    * A boolean that indicates whether the menu is torn-off.
618    *
619    * Since: 2.6
620    **/
621   g_object_class_install_property (gobject_class,
622                                    PROP_TEAROFF_STATE,
623                                    g_param_spec_boolean ("tearoff-state",
624                                                          P_("Tearoff State"),
625                                                          P_("A boolean that indicates whether the menu is torn-off"),
626                                                          FALSE,
627                                                          GTK_PARAM_READWRITE));
628
629   /**
630    * GtkMenu:monitor:
631    *
632    * The monitor the menu will be popped up on.
633    *
634    * Since: 2.14
635    **/
636   g_object_class_install_property (gobject_class,
637                                    PROP_MONITOR,
638                                    g_param_spec_int ("monitor",
639                                                      P_("Monitor"),
640                                                      P_("The monitor the menu will be popped up on"),
641                                                      -1, G_MAXINT, -1,
642                                                      GTK_PARAM_READWRITE));
643
644   /**
645    * GtkMenu:reserve-toggle-size:
646    *
647    * A boolean that indicates whether the menu reserves space for
648    * toggles and icons, regardless of their actual presence.
649    *
650    * This property should only be changed from its default value
651    * for special-purposes such as tabular menus. Regular menus that
652    * are connected to a menu bar or context menus should reserve
653    * toggle space for consistency.
654    *
655    * Since: 2.18
656    */
657   g_object_class_install_property (gobject_class,
658                                    PROP_RESERVE_TOGGLE_SIZE,
659                                    g_param_spec_boolean ("reserve-toggle-size",
660                                                          P_("Reserve Toggle Size"),
661                                                          P_("A boolean that indicates whether the menu reserves space for toggles and icons"),
662                                                          TRUE,
663                                                          GTK_PARAM_READWRITE));
664
665   /**
666    * GtkMenu:horizontal-padding:
667    *
668    * Extra space at the left and right edges of the menu.
669    *
670    * Deprecated: 3.8: use the standard padding CSS property (through objects
671    *   like #GtkStyleContext and #GtkCssProvider); the value of this style
672    *   property is ignored.
673    */
674   gtk_widget_class_install_style_property (widget_class,
675                                            g_param_spec_int ("horizontal-padding",
676                                                              P_("Horizontal Padding"),
677                                                              P_("Extra space at the left and right edges of the menu"),
678                                                              0,
679                                                              G_MAXINT,
680                                                              0,
681                                                              GTK_PARAM_READABLE |
682                                                              G_PARAM_DEPRECATED));
683
684   /**
685    * GtkMenu:vertical-padding:
686    *
687    * Extra space at the top and bottom of the menu.
688    *
689    * Deprecated: 3.8: use the standard padding CSS property (through objects
690    *   like #GtkStyleContext and #GtkCssProvider); the value of this style
691    *   property is ignored.
692    */
693   gtk_widget_class_install_style_property (widget_class,
694                                            g_param_spec_int ("vertical-padding",
695                                                              P_("Vertical Padding"),
696                                                              P_("Extra space at the top and bottom of the menu"),
697                                                              0,
698                                                              G_MAXINT,
699                                                              1,
700                                                              GTK_PARAM_READABLE |
701                                                              G_PARAM_DEPRECATED));
702
703   gtk_widget_class_install_style_property (widget_class,
704                                            g_param_spec_int ("vertical-offset",
705                                                              P_("Vertical Offset"),
706                                                              P_("When the menu is a submenu, position it this number of pixels offset vertically"),
707                                                              G_MININT,
708                                                              G_MAXINT,
709                                                              0,
710                                                              GTK_PARAM_READABLE));
711
712   gtk_widget_class_install_style_property (widget_class,
713                                            g_param_spec_int ("horizontal-offset",
714                                                              P_("Horizontal Offset"),
715                                                              P_("When the menu is a submenu, position it this number of pixels offset horizontally"),
716                                                              G_MININT,
717                                                              G_MAXINT,
718                                                              -2,
719                                                              GTK_PARAM_READABLE));
720
721   gtk_widget_class_install_style_property (widget_class,
722                                            g_param_spec_boolean ("double-arrows",
723                                                                  P_("Double Arrows"),
724                                                                  P_("When scrolling, always show both arrows."),
725                                                                  TRUE,
726                                                                  GTK_PARAM_READABLE));
727
728   /**
729    * GtkMenu:arrow-placement:
730    *
731    * Indicates where scroll arrows should be placed.
732    *
733    * Since: 2.16
734    **/
735   gtk_widget_class_install_style_property (widget_class,
736                                            g_param_spec_enum ("arrow-placement",
737                                                               P_("Arrow Placement"),
738                                                               P_("Indicates where scroll arrows should be placed"),
739                                                               GTK_TYPE_ARROW_PLACEMENT,
740                                                               GTK_ARROWS_BOTH,
741                                                               GTK_PARAM_READABLE));
742
743  gtk_container_class_install_child_property (container_class,
744                                              CHILD_PROP_LEFT_ATTACH,
745                                               g_param_spec_int ("left-attach",
746                                                                P_("Left Attach"),
747                                                                P_("The column number to attach the left side of the child to"),
748                                                                 -1, INT_MAX, -1,
749                                                                GTK_PARAM_READWRITE));
750
751  gtk_container_class_install_child_property (container_class,
752                                              CHILD_PROP_RIGHT_ATTACH,
753                                               g_param_spec_int ("right-attach",
754                                                                P_("Right Attach"),
755                                                                P_("The column number to attach the right side of the child to"),
756                                                                 -1, INT_MAX, -1,
757                                                                GTK_PARAM_READWRITE));
758
759  gtk_container_class_install_child_property (container_class,
760                                              CHILD_PROP_TOP_ATTACH,
761                                               g_param_spec_int ("top-attach",
762                                                                P_("Top Attach"),
763                                                                P_("The row number to attach the top of the child to"),
764                                                                 -1, INT_MAX, -1,
765                                                                GTK_PARAM_READWRITE));
766
767  gtk_container_class_install_child_property (container_class,
768                                              CHILD_PROP_BOTTOM_ATTACH,
769                                               g_param_spec_int ("bottom-attach",
770                                                                P_("Bottom Attach"),
771                                                                P_("The row number to attach the bottom of the child to"),
772                                                                 -1, INT_MAX, -1,
773                                                                GTK_PARAM_READWRITE));
774
775  /**
776   * GtkMenu:arrow-scaling:
777   *
778   * Arbitrary constant to scale down the size of the scroll arrow.
779   *
780   * Since: 2.16
781   */
782   gtk_widget_class_install_style_property (widget_class,
783                                            g_param_spec_float ("arrow-scaling",
784                                                                P_("Arrow Scaling"),
785                                                                P_("Arbitrary constant to scale down the size of the scroll arrow"),
786                                                                0.0, 1.0, 0.7,
787                                                                GTK_PARAM_READABLE));
788
789   binding_set = gtk_binding_set_by_class (class);
790   gtk_binding_entry_add_signal (binding_set,
791                                 GDK_KEY_Up, 0,
792                                 I_("move-current"), 1,
793                                 GTK_TYPE_MENU_DIRECTION_TYPE,
794                                 GTK_MENU_DIR_PREV);
795   gtk_binding_entry_add_signal (binding_set,
796                                 GDK_KEY_KP_Up, 0,
797                                 "move-current", 1,
798                                 GTK_TYPE_MENU_DIRECTION_TYPE,
799                                 GTK_MENU_DIR_PREV);
800   gtk_binding_entry_add_signal (binding_set,
801                                 GDK_KEY_Down, 0,
802                                 "move-current", 1,
803                                 GTK_TYPE_MENU_DIRECTION_TYPE,
804                                 GTK_MENU_DIR_NEXT);
805   gtk_binding_entry_add_signal (binding_set,
806                                 GDK_KEY_KP_Down, 0,
807                                 "move-current", 1,
808                                 GTK_TYPE_MENU_DIRECTION_TYPE,
809                                 GTK_MENU_DIR_NEXT);
810   gtk_binding_entry_add_signal (binding_set,
811                                 GDK_KEY_Left, 0,
812                                 "move-current", 1,
813                                 GTK_TYPE_MENU_DIRECTION_TYPE,
814                                 GTK_MENU_DIR_PARENT);
815   gtk_binding_entry_add_signal (binding_set,
816                                 GDK_KEY_KP_Left, 0,
817                                 "move-current", 1,
818                                 GTK_TYPE_MENU_DIRECTION_TYPE,
819                                 GTK_MENU_DIR_PARENT);
820   gtk_binding_entry_add_signal (binding_set,
821                                 GDK_KEY_Right, 0,
822                                 "move-current", 1,
823                                 GTK_TYPE_MENU_DIRECTION_TYPE,
824                                 GTK_MENU_DIR_CHILD);
825   gtk_binding_entry_add_signal (binding_set,
826                                 GDK_KEY_KP_Right, 0,
827                                 "move-current", 1,
828                                 GTK_TYPE_MENU_DIRECTION_TYPE,
829                                 GTK_MENU_DIR_CHILD);
830   gtk_binding_entry_add_signal (binding_set,
831                                 GDK_KEY_Home, 0,
832                                 "move-scroll", 1,
833                                 GTK_TYPE_SCROLL_TYPE,
834                                 GTK_SCROLL_START);
835   gtk_binding_entry_add_signal (binding_set,
836                                 GDK_KEY_KP_Home, 0,
837                                 "move-scroll", 1,
838                                 GTK_TYPE_SCROLL_TYPE,
839                                 GTK_SCROLL_START);
840   gtk_binding_entry_add_signal (binding_set,
841                                 GDK_KEY_End, 0,
842                                 "move-scroll", 1,
843                                 GTK_TYPE_SCROLL_TYPE,
844                                 GTK_SCROLL_END);
845   gtk_binding_entry_add_signal (binding_set,
846                                 GDK_KEY_KP_End, 0,
847                                 "move-scroll", 1,
848                                 GTK_TYPE_SCROLL_TYPE,
849                                 GTK_SCROLL_END);
850   gtk_binding_entry_add_signal (binding_set,
851                                 GDK_KEY_Page_Up, 0,
852                                 "move-scroll", 1,
853                                 GTK_TYPE_SCROLL_TYPE,
854                                 GTK_SCROLL_PAGE_UP);
855   gtk_binding_entry_add_signal (binding_set,
856                                 GDK_KEY_KP_Page_Up, 0,
857                                 "move-scroll", 1,
858                                 GTK_TYPE_SCROLL_TYPE,
859                                 GTK_SCROLL_PAGE_UP);
860   gtk_binding_entry_add_signal (binding_set,
861                                 GDK_KEY_Page_Down, 0,
862                                 "move-scroll", 1,
863                                 GTK_TYPE_SCROLL_TYPE,
864                                 GTK_SCROLL_PAGE_DOWN);
865   gtk_binding_entry_add_signal (binding_set,
866                                 GDK_KEY_KP_Page_Down, 0,
867                                 "move-scroll", 1,
868                                 GTK_TYPE_SCROLL_TYPE,
869                                 GTK_SCROLL_PAGE_DOWN);
870
871   g_type_class_add_private (gobject_class, sizeof (GtkMenuPrivate));
872
873   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_ACCESSIBLE);
874 }
875
876
877 static void
878 gtk_menu_set_property (GObject      *object,
879                        guint         prop_id,
880                        const GValue *value,
881                        GParamSpec   *pspec)
882 {
883   GtkMenu *menu = GTK_MENU (object);
884
885   switch (prop_id)
886     {
887     case PROP_ACTIVE:
888       gtk_menu_set_active (menu, g_value_get_int (value));
889       break;
890     case PROP_ACCEL_GROUP:
891       gtk_menu_set_accel_group (menu, g_value_get_object (value));
892       break;
893     case PROP_ACCEL_PATH:
894       gtk_menu_set_accel_path (menu, g_value_get_string (value));
895       break;
896     case PROP_ATTACH_WIDGET:
897       {
898         GtkWidget *widget;
899
900         widget = gtk_menu_get_attach_widget (menu);
901         if (widget)
902           gtk_menu_detach (menu);
903
904         widget = (GtkWidget*) g_value_get_object (value); 
905         if (widget)
906           gtk_menu_attach_to_widget (menu, widget, NULL);
907       }
908       break;
909     case PROP_TEAROFF_STATE:
910       gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
911       break;
912     case PROP_TEAROFF_TITLE:
913       gtk_menu_set_title (menu, g_value_get_string (value));
914       break;
915     case PROP_MONITOR:
916       gtk_menu_set_monitor (menu, g_value_get_int (value));
917       break;
918     case PROP_RESERVE_TOGGLE_SIZE:
919       gtk_menu_set_reserve_toggle_size (menu, g_value_get_boolean (value));
920       break;
921     default:
922       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
923       break;
924     }
925 }
926
927 static void
928 gtk_menu_get_property (GObject     *object,
929                        guint        prop_id,
930                        GValue      *value,
931                        GParamSpec  *pspec)
932 {
933   GtkMenu *menu = GTK_MENU (object);
934
935   switch (prop_id)
936     {
937     case PROP_ACTIVE:
938       g_value_set_int (value, g_list_index (GTK_MENU_SHELL (menu)->priv->children, gtk_menu_get_active (menu)));
939       break;
940     case PROP_ACCEL_GROUP:
941       g_value_set_object (value, gtk_menu_get_accel_group (menu));
942       break;
943     case PROP_ACCEL_PATH:
944       g_value_set_string (value, gtk_menu_get_accel_path (menu));
945       break;
946     case PROP_ATTACH_WIDGET:
947       g_value_set_object (value, gtk_menu_get_attach_widget (menu));
948       break;
949     case PROP_TEAROFF_STATE:
950       g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
951       break;
952     case PROP_TEAROFF_TITLE:
953       g_value_set_string (value, gtk_menu_get_title (menu));
954       break;
955     case PROP_MONITOR:
956       g_value_set_int (value, gtk_menu_get_monitor (menu));
957       break;
958     case PROP_RESERVE_TOGGLE_SIZE:
959       g_value_set_boolean (value, gtk_menu_get_reserve_toggle_size (menu));
960       break;
961     default:
962       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
963       break;
964     }
965 }
966
967 static void
968 gtk_menu_set_child_property (GtkContainer *container,
969                              GtkWidget    *child,
970                              guint         property_id,
971                              const GValue *value,
972                              GParamSpec   *pspec)
973 {
974   GtkMenu *menu = GTK_MENU (container);
975   AttachInfo *ai = get_attach_info (child);
976
977   switch (property_id)
978     {
979     case CHILD_PROP_LEFT_ATTACH:
980       ai->left_attach = g_value_get_int (value);
981       break;
982     case CHILD_PROP_RIGHT_ATTACH:
983       ai->right_attach = g_value_get_int (value);
984       break;
985     case CHILD_PROP_TOP_ATTACH:
986       ai->top_attach = g_value_get_int (value);
987       break;
988     case CHILD_PROP_BOTTOM_ATTACH:
989       ai->bottom_attach = g_value_get_int (value);
990       break;
991
992     default:
993       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
994       return;
995     }
996
997   menu_queue_resize (menu);
998 }
999
1000 static void
1001 gtk_menu_get_child_property (GtkContainer *container,
1002                              GtkWidget    *child,
1003                              guint         property_id,
1004                              GValue       *value,
1005                              GParamSpec   *pspec)
1006 {
1007   AttachInfo *ai = get_attach_info (child);
1008
1009   switch (property_id)
1010     {
1011     case CHILD_PROP_LEFT_ATTACH:
1012       g_value_set_int (value, ai->left_attach);
1013       break;
1014     case CHILD_PROP_RIGHT_ATTACH:
1015       g_value_set_int (value, ai->right_attach);
1016       break;
1017     case CHILD_PROP_TOP_ATTACH:
1018       g_value_set_int (value, ai->top_attach);
1019       break;
1020     case CHILD_PROP_BOTTOM_ATTACH:
1021       g_value_set_int (value, ai->bottom_attach);
1022       break;
1023       
1024     default:
1025       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
1026       return;
1027     }
1028 }
1029
1030 static gboolean
1031 gtk_menu_window_event (GtkWidget *window,
1032                        GdkEvent  *event,
1033                        GtkWidget *menu)
1034 {
1035   gboolean handled = FALSE;
1036
1037   g_object_ref (window);
1038   g_object_ref (menu);
1039
1040   switch (event->type)
1041     {
1042     case GDK_KEY_PRESS:
1043     case GDK_KEY_RELEASE:
1044       handled = gtk_widget_event (menu, event);
1045       break;
1046     case GDK_WINDOW_STATE:
1047       /* Window for the menu has been closed by the display server or by GDK.
1048        * Update the internal state as if the user had clicked outside the
1049        * menu
1050        */
1051       if (event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN &&
1052           event->window_state.changed_mask & GDK_WINDOW_STATE_WITHDRAWN)
1053         gtk_menu_shell_deactivate (GTK_MENU_SHELL(menu));
1054       break;
1055     default:
1056       break;
1057     }
1058
1059   g_object_unref (window);
1060   g_object_unref (menu);
1061
1062   return handled;
1063 }
1064
1065 static void
1066 gtk_menu_init (GtkMenu *menu)
1067 {
1068   GtkMenuPrivate *priv;
1069   GtkStyleContext *context;
1070
1071   priv = G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate);
1072
1073   menu->priv = priv;
1074
1075   priv->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
1076                                                    "type", GTK_WINDOW_POPUP,
1077                                                    "child", menu,
1078                                                    NULL),
1079                                      "signal::event", gtk_menu_window_event, menu,
1080                                      "signal::destroy", gtk_widget_destroyed, &priv->toplevel,
1081                                      NULL);
1082   gtk_window_set_resizable (GTK_WINDOW (priv->toplevel), FALSE);
1083   gtk_window_set_mnemonic_modifier (GTK_WINDOW (priv->toplevel), 0);
1084
1085   /* Refloat the menu, so that reference counting for the menu isn't
1086    * affected by it being a child of the toplevel
1087    */
1088   g_object_force_floating (G_OBJECT (menu));
1089   priv->needs_destruction_ref = TRUE;
1090
1091   priv->monitor_num = -1;
1092   priv->drag_start_y = -1;
1093
1094   context = gtk_widget_get_style_context (GTK_WIDGET (menu));
1095   gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
1096
1097   _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event);
1098 }
1099
1100 static void
1101 gtk_menu_destroy (GtkWidget *widget)
1102 {
1103   GtkMenu *menu = GTK_MENU (widget);
1104   GtkMenuPrivate *priv = menu->priv;
1105   GtkMenuAttachData *data;
1106
1107   gtk_menu_remove_scroll_timeout (menu);
1108
1109   data = g_object_get_data (G_OBJECT (widget), attach_data_key);
1110   if (data)
1111     gtk_menu_detach (menu);
1112
1113   gtk_menu_stop_navigating_submenu (menu);
1114
1115   if (priv->old_active_menu_item)
1116     g_clear_object (&priv->old_active_menu_item);
1117
1118   /* Add back the reference count for being a child */
1119   if (priv->needs_destruction_ref)
1120     {
1121       priv->needs_destruction_ref = FALSE;
1122       g_object_ref (widget);
1123     }
1124
1125   if (priv->accel_group)
1126     g_clear_object (&priv->accel_group);
1127
1128   if (priv->toplevel)
1129     gtk_widget_destroy (priv->toplevel);
1130
1131   if (priv->tearoff_window)
1132     gtk_widget_destroy (priv->tearoff_window);
1133
1134   if (priv->heights)
1135     {
1136       g_free (priv->heights);
1137       priv->heights = NULL;
1138     }
1139
1140   if (priv->title)
1141     {
1142       g_free (priv->title);
1143       priv->title = NULL;
1144     }
1145
1146   if (priv->position_func_data_destroy)
1147     {
1148       priv->position_func_data_destroy (priv->position_func_data);
1149       priv->position_func_data = NULL;
1150       priv->position_func_data_destroy = NULL;
1151     }
1152
1153   GTK_WIDGET_CLASS (gtk_menu_parent_class)->destroy (widget);
1154 }
1155
1156 static void
1157 menu_change_screen (GtkMenu   *menu,
1158                     GdkScreen *new_screen)
1159 {
1160   GtkMenuPrivate *priv = menu->priv;
1161
1162   if (gtk_widget_has_screen (GTK_WIDGET (menu)))
1163     {
1164       if (new_screen == gtk_widget_get_screen (GTK_WIDGET (menu)))
1165         return;
1166     }
1167
1168   if (priv->torn_off)
1169     {
1170       gtk_window_set_screen (GTK_WINDOW (priv->tearoff_window), new_screen);
1171       gtk_menu_position (menu, TRUE);
1172     }
1173
1174   gtk_window_set_screen (GTK_WINDOW (priv->toplevel), new_screen);
1175   priv->monitor_num = -1;
1176 }
1177
1178 static void
1179 attach_widget_screen_changed (GtkWidget *attach_widget,
1180                               GdkScreen *previous_screen,
1181                               GtkMenu   *menu)
1182 {
1183   if (gtk_widget_has_screen (attach_widget) &&
1184       !g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1185     menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
1186 }
1187
1188 /**
1189  * gtk_menu_attach_to_widget:
1190  * @menu: a #GtkMenu
1191  * @attach_widget: the #GtkWidget that the menu will be attached to
1192  * @detacher: (scope async)(allow-none): the user supplied callback function
1193  *             that will be called when the menu calls gtk_menu_detach()
1194  *
1195  * Attaches the menu to the widget and provides a callback function
1196  * that will be invoked when the menu calls gtk_menu_detach() during
1197  * its destruction.
1198  */
1199 void
1200 gtk_menu_attach_to_widget (GtkMenu           *menu,
1201                            GtkWidget         *attach_widget,
1202                            GtkMenuDetachFunc  detacher)
1203 {
1204   GtkMenuAttachData *data;
1205   GList *list;
1206
1207   g_return_if_fail (GTK_IS_MENU (menu));
1208   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
1209
1210   /* keep this function in sync with gtk_widget_set_parent() */
1211   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1212   if (data)
1213     {
1214       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
1215                  g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)));
1216      return;
1217     }
1218
1219   g_object_ref_sink (menu);
1220
1221   data = g_slice_new (GtkMenuAttachData);
1222   data->attach_widget = attach_widget;
1223
1224   g_signal_connect (attach_widget, "screen-changed",
1225                     G_CALLBACK (attach_widget_screen_changed), menu);
1226   attach_widget_screen_changed (attach_widget, NULL, menu);
1227
1228   data->detacher = detacher;
1229   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), data);
1230   list = g_object_steal_data (G_OBJECT (attach_widget), ATTACHED_MENUS);
1231   if (!g_list_find (list, menu))
1232     list = g_list_prepend (list, menu);
1233
1234   g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
1235                           (GDestroyNotify) g_list_free);
1236
1237   if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0)
1238     gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE);
1239
1240   /* Attach the widget to the toplevel window. */
1241   gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget);
1242
1243   _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
1244
1245   /* Fallback title for menu comes from attach widget */
1246   gtk_menu_update_title (menu);
1247
1248   g_object_notify (G_OBJECT (menu), "attach-widget");
1249 }
1250
1251 /**
1252  * gtk_menu_get_attach_widget:
1253  * @menu: a #GtkMenu
1254  *
1255  * Returns the #GtkWidget that the menu is attached to.
1256  *
1257  * Returns: (transfer none): the #GtkWidget that the menu is attached to
1258  */
1259 GtkWidget*
1260 gtk_menu_get_attach_widget (GtkMenu *menu)
1261 {
1262   GtkMenuAttachData *data;
1263
1264   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1265
1266   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1267   if (data)
1268     return data->attach_widget;
1269   return NULL;
1270 }
1271
1272 /**
1273  * gtk_menu_detach:
1274  * @menu: a #GtkMenu
1275  *
1276  * Detaches the menu from the widget to which it had been attached.
1277  * This function will call the callback function, @detacher, provided
1278  * when the gtk_menu_attach_to_widget() function was called.
1279  */
1280 void
1281 gtk_menu_detach (GtkMenu *menu)
1282 {
1283   GtkMenuAttachData *data;
1284   GList *list;
1285
1286   g_return_if_fail (GTK_IS_MENU (menu));
1287
1288   /* keep this function in sync with gtk_widget_unparent() */
1289   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1290   if (!data)
1291     {
1292       g_warning ("gtk_menu_detach(): menu is not attached");
1293       return;
1294     }
1295   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), NULL);
1296
1297   /* Detach the toplevel window. */
1298   gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), NULL);
1299
1300   g_signal_handlers_disconnect_by_func (data->attach_widget,
1301                                         (gpointer) attach_widget_screen_changed,
1302                                         menu);
1303
1304   if (data->detacher)
1305     data->detacher (data->attach_widget, menu);
1306   list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
1307   list = g_list_remove (list, menu);
1308   if (list)
1309     g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list,
1310                             (GDestroyNotify) g_list_free);
1311   else
1312     g_object_set_data (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), NULL);
1313
1314   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
1315     gtk_widget_unrealize (GTK_WIDGET (menu));
1316
1317   g_slice_free (GtkMenuAttachData, data);
1318
1319   _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
1320
1321   /* Fallback title for menu comes from attach widget */
1322   gtk_menu_update_title (menu);
1323
1324   g_object_notify (G_OBJECT (menu), "attach-widget");
1325   g_object_unref (menu);
1326 }
1327
1328 static void
1329 gtk_menu_remove (GtkContainer *container,
1330                  GtkWidget    *widget)
1331 {
1332   GtkMenu *menu = GTK_MENU (container);
1333   GtkMenuPrivate *priv = menu->priv;
1334
1335   /* Clear out old_active_menu_item if it matches the item we are removing */
1336   if (priv->old_active_menu_item == widget)
1337     g_clear_object (&priv->old_active_menu_item);
1338
1339   GTK_CONTAINER_CLASS (gtk_menu_parent_class)->remove (container, widget);
1340
1341   g_object_set_data (G_OBJECT (widget), I_(ATTACH_INFO_KEY), NULL);
1342
1343   menu_queue_resize (menu);
1344 }
1345
1346 /**
1347  * gtk_menu_new:
1348  *
1349  * Creates a new #GtkMenu
1350  *
1351  * Returns: a new #GtkMenu
1352  */
1353 GtkWidget*
1354 gtk_menu_new (void)
1355 {
1356   return g_object_new (GTK_TYPE_MENU, NULL);
1357 }
1358
1359 static void
1360 gtk_menu_real_insert (GtkMenuShell *menu_shell,
1361                       GtkWidget    *child,
1362                       gint          position)
1363 {
1364   GtkMenu *menu = GTK_MENU (menu_shell);
1365   GtkMenuPrivate *priv = menu->priv;
1366   AttachInfo *ai = get_attach_info (child);
1367
1368   ai->left_attach = -1;
1369   ai->right_attach = -1;
1370   ai->top_attach = -1;
1371   ai->bottom_attach = -1;
1372
1373   if (gtk_widget_get_realized (GTK_WIDGET (menu_shell)))
1374     gtk_widget_set_parent_window (child, priv->bin_window);
1375
1376   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->insert (menu_shell, child, position);
1377
1378   menu_queue_resize (menu);
1379 }
1380
1381 static void
1382 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
1383 {
1384   GtkMenuPrivate *priv = menu->priv;
1385   gint width, height;
1386
1387   if (priv->torn_off)
1388     {
1389       GdkWindow *window;
1390       cairo_surface_t *surface;
1391       cairo_pattern_t *pattern;
1392       cairo_t *cr;
1393
1394       priv->tearoff_active = FALSE;
1395       priv->saved_scroll_offset = priv->scroll_offset;
1396
1397       window = gtk_widget_get_window (priv->tearoff_window);
1398       width = gdk_window_get_width (window);
1399       height = gdk_window_get_height (window);
1400
1401       surface = gdk_window_create_similar_surface (window,
1402                                                    CAIRO_CONTENT_COLOR,
1403                                                    width,
1404                                                    height);
1405
1406       cr = cairo_create (surface);
1407       gdk_cairo_set_source_window (cr, window, 0, 0);
1408       cairo_paint (cr);
1409       cairo_destroy (cr);
1410
1411       gtk_widget_set_size_request (priv->tearoff_window, width, height);
1412
1413       pattern = cairo_pattern_create_for_surface (surface);
1414       gdk_window_set_background_pattern (window, pattern);
1415
1416       cairo_pattern_destroy (pattern);
1417       cairo_surface_destroy (surface);
1418     }
1419 }
1420
1421 static gboolean
1422 popup_grab_on_window (GdkWindow *window,
1423                       GdkDevice *keyboard,
1424                       GdkDevice *pointer,
1425                       guint32    activate_time)
1426 {
1427   if (keyboard &&
1428       gdk_device_grab (keyboard, window,
1429                        GDK_OWNERSHIP_WINDOW, TRUE,
1430                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1431                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1432     return FALSE;
1433
1434   if (pointer &&
1435       gdk_device_grab (pointer, window,
1436                        GDK_OWNERSHIP_WINDOW, TRUE,
1437                        GDK_SMOOTH_SCROLL_MASK |
1438                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1439                        GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
1440                        GDK_POINTER_MOTION_MASK,
1441                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1442     {
1443       if (keyboard)
1444         gdk_device_ungrab (keyboard, activate_time);
1445
1446       return FALSE;
1447     }
1448
1449   return TRUE;
1450 }
1451
1452 /**
1453  * gtk_menu_popup_for_device:
1454  * @menu: a #GtkMenu
1455  * @device: (allow-none): a #GdkDevice
1456  * @parent_menu_shell: (allow-none): the menu shell containing the triggering
1457  *     menu item, or %NULL
1458  * @parent_menu_item: (allow-none): the menu item whose activation triggered
1459  *     the popup, or %NULL
1460  * @func: (allow-none): a user supplied function used to position the menu,
1461  *     or %NULL
1462  * @data: (allow-none): user supplied data to be passed to @func
1463  * @destroy: (allow-none): destroy notify for @data
1464  * @button: the mouse button which was pressed to initiate the event
1465  * @activate_time: the time at which the activation event occurred
1466  *
1467  * Displays a menu and makes it available for selection.
1468  *
1469  * Applications can use this function to display context-sensitive menus,
1470  * and will typically supply %NULL for the @parent_menu_shell,
1471  * @parent_menu_item, @func, @data and @destroy parameters. The default
1472  * menu positioning function will position the menu at the current position
1473  * of @device (or its corresponding pointer).
1474  *
1475  * The @button parameter should be the mouse button pressed to initiate
1476  * the menu popup. If the menu popup was initiated by something other than
1477  * a mouse button press, such as a mouse button release or a keypress,
1478  * @button should be 0.
1479  *
1480  * The @activate_time parameter is used to conflict-resolve initiation of
1481  * concurrent requests for mouse/keyboard grab requests. To function
1482  * properly, this needs to be the time stamp of the user event (such as
1483  * a mouse click or key press) that caused the initiation of the popup.
1484  * Only if no such event is available, gtk_get_current_event_time() can
1485  * be used instead.
1486  *
1487  * Since: 3.0
1488  */
1489 void
1490 gtk_menu_popup_for_device (GtkMenu             *menu,
1491                            GdkDevice           *device,
1492                            GtkWidget           *parent_menu_shell,
1493                            GtkWidget           *parent_menu_item,
1494                            GtkMenuPositionFunc  func,
1495                            gpointer             data,
1496                            GDestroyNotify       destroy,
1497                            guint                button,
1498                            guint32              activate_time)
1499 {
1500   GtkMenuPrivate *priv = menu->priv;
1501   GtkWidget *widget;
1502   GtkWidget *xgrab_shell;
1503   GtkWidget *parent;
1504   GdkEvent *current_event;
1505   GtkMenuShell *menu_shell;
1506   gboolean grab_keyboard;
1507   GtkWidget *parent_toplevel;
1508   GdkDevice *keyboard, *pointer, *source_device = NULL;
1509
1510   g_return_if_fail (GTK_IS_MENU (menu));
1511   g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
1512
1513   if (device == NULL)
1514     device = gtk_get_current_event_device ();
1515
1516   if (device == NULL)
1517     {
1518       GdkDisplay *display;
1519       GdkDeviceManager *device_manager;
1520       GList *devices;
1521
1522       display = gtk_widget_get_display (GTK_WIDGET (menu));
1523       device_manager = gdk_display_get_device_manager (display);
1524       devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
1525
1526       device = devices->data;
1527
1528       g_list_free (devices);
1529     }
1530
1531   widget = GTK_WIDGET (menu);
1532   menu_shell = GTK_MENU_SHELL (menu);
1533
1534   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1535     {
1536       keyboard = device;
1537       pointer = gdk_device_get_associated_device (device);
1538     }
1539   else
1540     {
1541       pointer = device;
1542       keyboard = gdk_device_get_associated_device (device);
1543     }
1544
1545   menu_shell->priv->parent_menu_shell = parent_menu_shell;
1546
1547   priv->seen_item_enter = FALSE;
1548
1549   /* Find the last viewable ancestor, and make an X grab on it
1550    */
1551   parent = GTK_WIDGET (menu);
1552   xgrab_shell = NULL;
1553   while (parent)
1554     {
1555       gboolean viewable = TRUE;
1556       GtkWidget *tmp = parent;
1557
1558       while (tmp)
1559         {
1560           if (!gtk_widget_get_mapped (tmp))
1561             {
1562               viewable = FALSE;
1563               break;
1564             }
1565           tmp = gtk_widget_get_parent (tmp);
1566         }
1567
1568       if (viewable)
1569         xgrab_shell = parent;
1570
1571       parent = GTK_MENU_SHELL (parent)->priv->parent_menu_shell;
1572     }
1573
1574   /* We want to receive events generated when we map the menu;
1575    * unfortunately, since there is probably already an implicit
1576    * grab in place from the button that the user used to pop up
1577    * the menu, we won't receive then -- in particular, the EnterNotify
1578    * when the menu pops up under the pointer.
1579    *
1580    * If we are grabbing on a parent menu shell, no problem; just grab
1581    * on that menu shell first before popping up the window with
1582    * owner_events = TRUE.
1583    *
1584    * When grabbing on the menu itself, things get more convoluted --
1585    * we do an explicit grab on a specially created window with
1586    * owner_events = TRUE, which we override further down with a
1587    * grab on the menu. (We can't grab on the menu until it is mapped;
1588    * we probably could just leave the grab on the other window,
1589    * with a little reorganization of the code in gtkmenu*).
1590    */
1591   grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
1592   gtk_window_set_accept_focus (GTK_WINDOW (priv->toplevel), grab_keyboard);
1593
1594   if (!grab_keyboard)
1595     keyboard = NULL;
1596
1597   if (xgrab_shell && xgrab_shell != widget)
1598     {
1599       if (popup_grab_on_window (gtk_widget_get_window (xgrab_shell), keyboard, pointer, activate_time))
1600         {
1601           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1602           GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab = TRUE;
1603         }
1604     }
1605   else
1606     {
1607       GdkWindow *transfer_window;
1608
1609       xgrab_shell = widget;
1610       transfer_window = menu_grab_transfer_window_get (menu);
1611       if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
1612         {
1613           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1614           GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab = TRUE;
1615         }
1616     }
1617
1618   if (!GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab)
1619     {
1620       /* We failed to make our pointer/keyboard grab.
1621        * Rather than leaving the user with a stuck up window,
1622        * we just abort here. Presumably the user will try again.
1623        */
1624       menu_shell->priv->parent_menu_shell = NULL;
1625       menu_grab_transfer_window_destroy (menu);
1626       return;
1627     }
1628
1629   _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (menu), pointer);
1630   menu_shell->priv->active = TRUE;
1631   menu_shell->priv->button = button;
1632
1633   /* If we are popping up the menu from something other than, a button
1634    * press then, as a heuristic, we ignore enter events for the menu
1635    * until we get a MOTION_NOTIFY.
1636    */
1637
1638   current_event = gtk_get_current_event ();
1639   if (current_event)
1640     {
1641       if ((current_event->type != GDK_BUTTON_PRESS) &&
1642           (current_event->type != GDK_ENTER_NOTIFY))
1643         menu_shell->priv->ignore_enter = TRUE;
1644
1645       source_device = gdk_event_get_source_device (current_event);
1646       gdk_event_free (current_event);
1647     }
1648   else
1649     menu_shell->priv->ignore_enter = TRUE;
1650
1651   if (priv->torn_off)
1652     {
1653       gtk_menu_tearoff_bg_copy (menu);
1654
1655       gtk_menu_reparent (menu, priv->toplevel, FALSE);
1656     }
1657
1658   parent_toplevel = NULL;
1659   if (parent_menu_shell)
1660     parent_toplevel = gtk_widget_get_toplevel (parent_menu_shell);
1661   else if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1662     {
1663       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
1664       if (attach_widget)
1665         parent_toplevel = gtk_widget_get_toplevel (attach_widget);
1666     }
1667
1668   /* Set transient for to get the right window group and parent */
1669   if (GTK_IS_WINDOW (parent_toplevel))
1670     gtk_window_set_transient_for (GTK_WINDOW (priv->toplevel),
1671                                   GTK_WINDOW (parent_toplevel));
1672
1673   priv->parent_menu_item = parent_menu_item;
1674   priv->position_func = func;
1675   priv->position_func_data = data;
1676   priv->position_func_data_destroy = destroy;
1677   menu_shell->priv->activate_time = activate_time;
1678
1679   /* We need to show the menu here rather in the init function
1680    * because code expects to be able to tell if the menu is onscreen
1681    * by looking at gtk_widget_get_visible (menu)
1682    */
1683   gtk_widget_show (GTK_WIDGET (menu));
1684
1685   /* Position the menu, possibly changing the size request
1686    */
1687   gtk_menu_position (menu, TRUE);
1688
1689   /* Compute the size of the toplevel and realize it so we
1690    * can scroll correctly.
1691    */
1692   if (!gtk_widget_get_realized (GTK_WIDGET (menu)))
1693   {
1694     GtkRequisition tmp_request;
1695     GtkAllocation tmp_allocation = { 0, };
1696
1697     /* Instead of trusting the menu position function to queue a
1698      * resize when the menu goes out of bounds, invalidate the cached
1699      * size here.
1700      */
1701     gtk_widget_queue_resize (GTK_WIDGET (menu));
1702     gtk_widget_get_preferred_size (priv->toplevel, &tmp_request, NULL);
1703
1704     tmp_allocation.width = tmp_request.width;
1705     tmp_allocation.height = tmp_request.height;
1706
1707     gtk_widget_size_allocate (priv->toplevel, &tmp_allocation);
1708
1709     gtk_widget_realize (priv->toplevel);
1710   }
1711
1712   gtk_menu_scroll_to (menu, priv->scroll_offset);
1713
1714   /* if no item is selected, select the first one */
1715   if (!menu_shell->priv->active_menu_item &&
1716       source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
1717     gtk_menu_shell_select_first (menu_shell, TRUE);
1718
1719   /* Once everything is set up correctly, map the toplevel */
1720   gtk_widget_show (priv->toplevel);
1721
1722   if (xgrab_shell == widget)
1723     popup_grab_on_window (gtk_widget_get_window (widget), keyboard, pointer, activate_time); /* Should always succeed */
1724
1725   gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE);
1726
1727   if (parent_menu_shell)
1728     {
1729       gboolean keyboard_mode;
1730
1731       keyboard_mode = _gtk_menu_shell_get_keyboard_mode (GTK_MENU_SHELL (parent_menu_shell));
1732       _gtk_menu_shell_set_keyboard_mode (menu_shell, keyboard_mode);
1733     }
1734   else if (menu_shell->priv->button == 0) /* a keynav-activated context menu */
1735     _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE);
1736
1737   _gtk_menu_shell_update_mnemonics (menu_shell);
1738 }
1739
1740 /**
1741  * gtk_menu_popup:
1742  * @menu: a #GtkMenu
1743  * @parent_menu_shell: (allow-none): the menu shell containing the
1744  *     triggering menu item, or %NULL
1745  * @parent_menu_item: (allow-none): the menu item whose activation
1746  *     triggered the popup, or %NULL
1747  * @func: (scope async) (allow-none): a user supplied function used to position
1748  *     the menu, or %NULL
1749  * @data: user supplied data to be passed to @func.
1750  * @button: the mouse button which was pressed to initiate the event.
1751  * @activate_time: the time at which the activation event occurred.
1752  *
1753  * Displays a menu and makes it available for selection.
1754  *
1755  * Applications can use this function to display context-sensitive
1756  * menus, and will typically supply %NULL for the @parent_menu_shell,
1757  * @parent_menu_item, @func and @data parameters. The default menu
1758  * positioning function will position the menu at the current mouse
1759  * cursor position.
1760  *
1761  * The @button parameter should be the mouse button pressed to initiate
1762  * the menu popup. If the menu popup was initiated by something other
1763  * than a mouse button press, such as a mouse button release or a keypress,
1764  * @button should be 0.
1765  *
1766  * The @activate_time parameter is used to conflict-resolve initiation
1767  * of concurrent requests for mouse/keyboard grab requests. To function
1768  * properly, this needs to be the timestamp of the user event (such as
1769  * a mouse click or key press) that caused the initiation of the popup.
1770  * Only if no such event is available, gtk_get_current_event_time() can
1771  * be used instead.
1772  */
1773 void
1774 gtk_menu_popup (GtkMenu             *menu,
1775                 GtkWidget           *parent_menu_shell,
1776                 GtkWidget           *parent_menu_item,
1777                 GtkMenuPositionFunc  func,
1778                 gpointer             data,
1779                 guint                button,
1780                 guint32              activate_time)
1781 {
1782   g_return_if_fail (GTK_IS_MENU (menu));
1783
1784   gtk_menu_popup_for_device (menu,
1785                              NULL,
1786                              parent_menu_shell,
1787                              parent_menu_item,
1788                              func, data, NULL,
1789                              button, activate_time);
1790 }
1791
1792 /**
1793  * gtk_menu_popdown:
1794  * @menu: a #GtkMenu
1795  *
1796  * Removes the menu from the screen.
1797  */
1798 void
1799 gtk_menu_popdown (GtkMenu *menu)
1800 {
1801   GtkMenuPrivate *priv;
1802   GtkMenuShell *menu_shell;
1803   GdkDevice *pointer;
1804
1805   g_return_if_fail (GTK_IS_MENU (menu));
1806
1807   menu_shell = GTK_MENU_SHELL (menu);
1808   priv = menu->priv;
1809
1810   menu_shell->priv->parent_menu_shell = NULL;
1811   menu_shell->priv->active = FALSE;
1812   menu_shell->priv->ignore_enter = FALSE;
1813
1814   priv->have_position = FALSE;
1815
1816   gtk_menu_stop_scrolling (menu);
1817   gtk_menu_stop_navigating_submenu (menu);
1818
1819   if (menu_shell->priv->active_menu_item)
1820     {
1821       if (priv->old_active_menu_item)
1822         g_object_unref (priv->old_active_menu_item);
1823       priv->old_active_menu_item = menu_shell->priv->active_menu_item;
1824       g_object_ref (priv->old_active_menu_item);
1825     }
1826
1827   gtk_menu_shell_deselect (menu_shell);
1828
1829   /* The X Grab, if present, will automatically be removed
1830    * when we hide the window
1831    */
1832   if (priv->toplevel)
1833     {
1834       gtk_widget_hide (priv->toplevel);
1835       gtk_window_set_transient_for (GTK_WINDOW (priv->toplevel), NULL);
1836     }
1837
1838   pointer = _gtk_menu_shell_get_grab_device (menu_shell);
1839
1840   if (priv->torn_off)
1841     {
1842       gtk_widget_set_size_request (priv->tearoff_window, -1, -1);
1843
1844       if (gtk_bin_get_child (GTK_BIN (priv->toplevel)))
1845         {
1846           gtk_menu_reparent (menu, priv->tearoff_hbox, TRUE);
1847         }
1848       else
1849         {
1850           /* We popped up the menu from the tearoff, so we need to
1851            * release the grab - we aren't actually hiding the menu.
1852            */
1853           if (menu_shell->priv->have_xgrab && pointer)
1854             {
1855               GdkDevice *keyboard;
1856
1857               gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
1858               keyboard = gdk_device_get_associated_device (pointer);
1859
1860               if (keyboard)
1861                 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
1862             }
1863         }
1864
1865       /* gtk_menu_popdown is called each time a menu item is selected from
1866        * a torn off menu. Only scroll back to the saved position if the
1867        * non-tearoff menu was popped down.
1868        */
1869       if (!priv->tearoff_active)
1870         gtk_menu_scroll_to (menu, priv->saved_scroll_offset);
1871       priv->tearoff_active = TRUE;
1872     }
1873   else
1874     gtk_widget_hide (GTK_WIDGET (menu));
1875
1876   menu_shell->priv->have_xgrab = FALSE;
1877
1878   if (pointer)
1879     gtk_device_grab_remove (GTK_WIDGET (menu), pointer);
1880
1881   _gtk_menu_shell_set_grab_device (menu_shell, NULL);
1882
1883   menu_grab_transfer_window_destroy (menu);
1884 }
1885
1886 /**
1887  * gtk_menu_get_active:
1888  * @menu: a #GtkMenu
1889  *
1890  * Returns the selected menu item from the menu.  This is used by the
1891  * #GtkComboBox.
1892  *
1893  * Returns: (transfer none): the #GtkMenuItem that was last selected
1894  *          in the menu.  If a selection has not yet been made, the
1895  *          first menu item is selected.
1896  */
1897 GtkWidget*
1898 gtk_menu_get_active (GtkMenu *menu)
1899 {
1900   GtkMenuPrivate *priv = menu->priv;
1901   GtkWidget *child;
1902   GList *children;
1903
1904   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1905
1906   if (!priv->old_active_menu_item)
1907     {
1908       child = NULL;
1909       children = GTK_MENU_SHELL (menu)->priv->children;
1910
1911       while (children)
1912         {
1913           child = children->data;
1914           children = children->next;
1915
1916           if (gtk_bin_get_child (GTK_BIN (child)))
1917             break;
1918           child = NULL;
1919         }
1920
1921       priv->old_active_menu_item = child;
1922       if (priv->old_active_menu_item)
1923         g_object_ref (priv->old_active_menu_item);
1924     }
1925
1926   return priv->old_active_menu_item;
1927 }
1928
1929 /**
1930  * gtk_menu_set_active:
1931  * @menu: a #GtkMenu
1932  * @index: the index of the menu item to select.  Iindex values are
1933  *         from 0 to n-1
1934  *
1935  * Selects the specified menu item within the menu.  This is used by
1936  * the #GtkComboBox and should not be used by anyone else.
1937  */
1938 void
1939 gtk_menu_set_active (GtkMenu *menu,
1940                      guint    index)
1941 {
1942   GtkMenuPrivate *priv = menu->priv;
1943   GtkWidget *child;
1944   GList *tmp_list;
1945
1946   g_return_if_fail (GTK_IS_MENU (menu));
1947
1948   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->priv->children, index);
1949   if (tmp_list)
1950     {
1951       child = tmp_list->data;
1952       if (gtk_bin_get_child (GTK_BIN (child)))
1953         {
1954           if (priv->old_active_menu_item)
1955             g_object_unref (priv->old_active_menu_item);
1956           priv->old_active_menu_item = child;
1957           g_object_ref (priv->old_active_menu_item);
1958         }
1959     }
1960 }
1961
1962 /**
1963  * gtk_menu_set_accel_group:
1964  * @menu: a #GtkMenu
1965  * @accel_group: (allow-none): the #GtkAccelGroup to be associated
1966  *               with the menu.
1967  *
1968  * Set the #GtkAccelGroup which holds global accelerators for the
1969  * menu.  This accelerator group needs to also be added to all windows
1970  * that this menu is being used in with gtk_window_add_accel_group(),
1971  * in order for those windows to support all the accelerators
1972  * contained in this group.
1973  */
1974 void
1975 gtk_menu_set_accel_group (GtkMenu       *menu,
1976                           GtkAccelGroup *accel_group)
1977 {
1978   GtkMenuPrivate *priv = menu->priv;
1979   g_return_if_fail (GTK_IS_MENU (menu));
1980
1981   if (priv->accel_group != accel_group)
1982     {
1983       if (priv->accel_group)
1984         g_object_unref (priv->accel_group);
1985       priv->accel_group = accel_group;
1986       if (priv->accel_group)
1987         g_object_ref (priv->accel_group);
1988       _gtk_menu_refresh_accel_paths (menu, TRUE);
1989     }
1990 }
1991
1992 /**
1993  * gtk_menu_get_accel_group:
1994  * @menu: a #GtkMenu
1995  *
1996  * Gets the #GtkAccelGroup which holds global accelerators for the
1997  * menu. See gtk_menu_set_accel_group().
1998  *
1999  * Returns: (transfer none): the #GtkAccelGroup associated with the menu
2000  */
2001 GtkAccelGroup*
2002 gtk_menu_get_accel_group (GtkMenu *menu)
2003 {
2004   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2005
2006   return menu->priv->accel_group;
2007 }
2008
2009 static gboolean
2010 gtk_menu_real_can_activate_accel (GtkWidget *widget,
2011                                   guint      signal_id)
2012 {
2013   /* Menu items chain here to figure whether they can activate their
2014    * accelerators.  Unlike ordinary widgets, menus allow accel
2015    * activation even if invisible since that's the usual case for
2016    * submenus/popup-menus. however, the state of the attach widget
2017    * affects the "activeness" of the menu.
2018    */
2019   GtkWidget *awidget = gtk_menu_get_attach_widget (GTK_MENU (widget));
2020
2021   if (awidget)
2022     return gtk_widget_can_activate_accel (awidget, signal_id);
2023   else
2024     return gtk_widget_is_sensitive (widget);
2025 }
2026
2027 /**
2028  * gtk_menu_set_accel_path:
2029  * @menu:       a valid #GtkMenu
2030  * @accel_path: (allow-none): a valid accelerator path
2031  *
2032  * Sets an accelerator path for this menu from which accelerator paths
2033  * for its immediate children, its menu items, can be constructed.
2034  * The main purpose of this function is to spare the programmer the
2035  * inconvenience of having to call gtk_menu_item_set_accel_path() on
2036  * each menu item that should support runtime user changable accelerators.
2037  * Instead, by just calling gtk_menu_set_accel_path() on their parent,
2038  * each menu item of this menu, that contains a label describing its
2039  * purpose, automatically gets an accel path assigned.
2040  *
2041  * For example, a menu containing menu items "New" and "Exit", will, after
2042  * <literal>gtk_menu_set_accel_path (menu, "&lt;Gnumeric-Sheet&gt;/File");</literal>
2043  * has been called, assign its items the accel paths:
2044  * <literal>"&lt;Gnumeric-Sheet&gt;/File/New"</literal> and <literal>"&lt;Gnumeric-Sheet&gt;/File/Exit"</literal>.
2045  *
2046  * Assigning accel paths to menu items then enables the user to change
2047  * their accelerators at runtime. More details about accelerator paths
2048  * and their default setups can be found at gtk_accel_map_add_entry().
2049  *
2050  * Note that @accel_path string will be stored in a #GQuark. Therefore,
2051  * if you pass a static string, you can save some memory by interning
2052  * it first with g_intern_static_string().
2053  */
2054 void
2055 gtk_menu_set_accel_path (GtkMenu     *menu,
2056                          const gchar *accel_path)
2057 {
2058   GtkMenuPrivate *priv = menu->priv;
2059   g_return_if_fail (GTK_IS_MENU (menu));
2060
2061   if (accel_path)
2062     g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
2063
2064   /* FIXME: accel_path should be defined as const gchar* */
2065   priv->accel_path = (gchar*)g_intern_string (accel_path);
2066   if (priv->accel_path)
2067     _gtk_menu_refresh_accel_paths (menu, FALSE);
2068 }
2069
2070 /**
2071  * gtk_menu_get_accel_path:
2072  * @menu: a valid #GtkMenu
2073  *
2074  * Retrieves the accelerator path set on the menu.
2075  *
2076  * Returns: the accelerator path set on the menu.
2077  *
2078  * Since: 2.14
2079  */
2080 const gchar*
2081 gtk_menu_get_accel_path (GtkMenu *menu)
2082 {
2083   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2084
2085   return menu->priv->accel_path;
2086 }
2087
2088 typedef struct {
2089   GtkMenu *menu;
2090   gboolean group_changed;
2091 } AccelPropagation;
2092
2093 static void
2094 refresh_accel_paths_foreach (GtkWidget *widget,
2095                              gpointer   data)
2096 {
2097   GtkMenuPrivate *priv;
2098   AccelPropagation *prop = data;
2099
2100   if (GTK_IS_MENU_ITEM (widget))  /* should always be true */
2101     {
2102       priv = prop->menu->priv;
2103       _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2104                                          priv->accel_path,
2105                                          priv->accel_group,
2106                                          prop->group_changed);
2107     }
2108 }
2109
2110 static void
2111 _gtk_menu_refresh_accel_paths (GtkMenu  *menu,
2112                                gboolean  group_changed)
2113 {
2114   GtkMenuPrivate *priv = menu->priv;
2115   g_return_if_fail (GTK_IS_MENU (menu));
2116
2117   if (priv->accel_path && priv->accel_group)
2118     {
2119       AccelPropagation prop;
2120
2121       prop.menu = menu;
2122       prop.group_changed = group_changed;
2123       gtk_container_foreach (GTK_CONTAINER (menu),
2124                              refresh_accel_paths_foreach,
2125                              &prop);
2126     }
2127 }
2128
2129 /**
2130  * gtk_menu_reposition:
2131  * @menu: a #GtkMenu
2132  *
2133  * Repositions the menu according to its position function.
2134  */
2135 void
2136 gtk_menu_reposition (GtkMenu *menu)
2137 {
2138   g_return_if_fail (GTK_IS_MENU (menu));
2139
2140   if (!menu->priv->torn_off && gtk_widget_is_drawable (GTK_WIDGET (menu)))
2141     gtk_menu_position (menu, FALSE);
2142 }
2143
2144 static void
2145 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
2146                             GtkMenu       *menu)
2147 {
2148   double value;
2149
2150   value = gtk_adjustment_get_value (adjustment);
2151   if (menu->priv->scroll_offset != value)
2152     gtk_menu_scroll_to (menu, value);
2153 }
2154
2155 static void
2156 gtk_menu_set_tearoff_hints (GtkMenu *menu,
2157                             gint     width)
2158 {
2159   GtkMenuPrivate *priv = menu->priv;
2160   GdkGeometry geometry_hints;
2161
2162   if (!priv->tearoff_window)
2163     return;
2164
2165   if (gtk_widget_get_visible (priv->tearoff_scrollbar))
2166     {
2167       GtkRequisition requisition;
2168
2169       gtk_widget_get_preferred_size (priv->tearoff_scrollbar,
2170                                      &requisition, NULL);
2171       width += requisition.width;
2172     }
2173
2174   geometry_hints.min_width = width;
2175   geometry_hints.max_width = width;
2176
2177   geometry_hints.min_height = 0;
2178   geometry_hints.max_height = priv->requested_height;
2179
2180   gtk_window_set_geometry_hints (GTK_WINDOW (priv->tearoff_window),
2181                                  NULL,
2182                                  &geometry_hints,
2183                                  GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
2184 }
2185
2186 static void
2187 gtk_menu_update_title (GtkMenu *menu)
2188 {
2189   GtkMenuPrivate *priv = menu->priv;
2190
2191   if (priv->tearoff_window)
2192     {
2193       const gchar *title;
2194       GtkWidget *attach_widget;
2195
2196       title = gtk_menu_get_title (menu);
2197       if (!title)
2198         {
2199           attach_widget = gtk_menu_get_attach_widget (menu);
2200           if (GTK_IS_MENU_ITEM (attach_widget))
2201             {
2202               GtkWidget *child = gtk_bin_get_child (GTK_BIN (attach_widget));
2203               if (GTK_IS_LABEL (child))
2204                 title = gtk_label_get_text (GTK_LABEL (child));
2205             }
2206         }
2207
2208       if (title)
2209         gtk_window_set_title (GTK_WINDOW (priv->tearoff_window), title);
2210     }
2211 }
2212
2213 static GtkWidget*
2214 gtk_menu_get_toplevel (GtkWidget *menu)
2215 {
2216   GtkWidget *attach, *toplevel;
2217
2218   attach = gtk_menu_get_attach_widget (GTK_MENU (menu));
2219
2220   if (GTK_IS_MENU_ITEM (attach))
2221     attach = gtk_widget_get_parent (attach);
2222
2223   if (GTK_IS_MENU (attach))
2224     return gtk_menu_get_toplevel (attach);
2225   else if (GTK_IS_WIDGET (attach))
2226     {
2227       toplevel = gtk_widget_get_toplevel (attach);
2228       if (gtk_widget_is_toplevel (toplevel))
2229         return toplevel;
2230     }
2231
2232   return NULL;
2233 }
2234
2235 static void
2236 tearoff_window_destroyed (GtkWidget *widget,
2237                           GtkMenu   *menu)
2238 {
2239   gtk_menu_set_tearoff_state (menu, FALSE);
2240 }
2241
2242 /**
2243  * gtk_menu_set_tearoff_state:
2244  * @menu: a #GtkMenu
2245  * @torn_off: If %TRUE, menu is displayed as a tearoff menu.
2246  *
2247  * Changes the tearoff state of the menu.  A menu is normally
2248  * displayed as drop down menu which persists as long as the menu is
2249  * active.  It can also be displayed as a tearoff menu which persists
2250  * until it is closed or reattached.
2251  */
2252 void
2253 gtk_menu_set_tearoff_state (GtkMenu  *menu,
2254                             gboolean  torn_off)
2255 {
2256   GtkMenuPrivate *priv = menu->priv;
2257   gint height;
2258
2259   g_return_if_fail (GTK_IS_MENU (menu));
2260
2261   if (priv->torn_off != torn_off)
2262     {
2263       priv->torn_off = torn_off;
2264       priv->tearoff_active = torn_off;
2265
2266       if (priv->torn_off)
2267         {
2268           if (gtk_widget_get_visible (GTK_WIDGET (menu)))
2269             gtk_menu_popdown (menu);
2270
2271           if (!priv->tearoff_window)
2272             {
2273               GtkWidget *toplevel;
2274
2275               priv->tearoff_window = g_object_new (GTK_TYPE_WINDOW,
2276                                                    "type", GTK_WINDOW_TOPLEVEL,
2277                                                    "screen", gtk_widget_get_screen (priv->toplevel),
2278                                                    "app-paintable", TRUE,
2279                                                    NULL);
2280
2281               gtk_window_set_type_hint (GTK_WINDOW (priv->tearoff_window),
2282                                         GDK_WINDOW_TYPE_HINT_MENU);
2283               gtk_window_set_mnemonic_modifier (GTK_WINDOW (priv->tearoff_window), 0);
2284               g_signal_connect (priv->tearoff_window, "destroy",
2285                                 G_CALLBACK (tearoff_window_destroyed), menu);
2286               g_signal_connect (priv->tearoff_window, "event",
2287                                 G_CALLBACK (gtk_menu_window_event), menu);
2288
2289               gtk_menu_update_title (menu);
2290
2291               gtk_widget_realize (priv->tearoff_window);
2292
2293               toplevel = gtk_menu_get_toplevel (GTK_WIDGET (menu));
2294               if (toplevel != NULL)
2295                 gtk_window_set_transient_for (GTK_WINDOW (priv->tearoff_window),
2296                                               GTK_WINDOW (toplevel));
2297
2298               priv->tearoff_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2299               gtk_container_add (GTK_CONTAINER (priv->tearoff_window),
2300                                  priv->tearoff_hbox);
2301
2302               height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (menu)));
2303               priv->tearoff_adjustment = gtk_adjustment_new (0,
2304                                                              0, priv->requested_height,
2305                                                              MENU_SCROLL_STEP2,
2306                                                              height/2,
2307                                                              height);
2308               g_object_connect (priv->tearoff_adjustment,
2309                                 "signal::value-changed", gtk_menu_scrollbar_changed, menu,
2310                                 NULL);
2311               priv->tearoff_scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, priv->tearoff_adjustment);
2312
2313               gtk_box_pack_end (GTK_BOX (priv->tearoff_hbox),
2314                                 priv->tearoff_scrollbar,
2315                                 FALSE, FALSE, 0);
2316
2317               if (gtk_adjustment_get_upper (priv->tearoff_adjustment) > height)
2318                 gtk_widget_show (priv->tearoff_scrollbar);
2319
2320               gtk_widget_show (priv->tearoff_hbox);
2321             }
2322
2323           gtk_menu_reparent (menu, priv->tearoff_hbox, FALSE);
2324
2325           /* Update menu->requisition */
2326           gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, NULL);
2327
2328           gtk_menu_set_tearoff_hints (menu, gdk_window_get_width (gtk_widget_get_window (GTK_WIDGET (menu))));
2329
2330           gtk_widget_realize (priv->tearoff_window);
2331           gtk_menu_position (menu, TRUE);
2332
2333           gtk_widget_show (GTK_WIDGET (menu));
2334           gtk_widget_show (priv->tearoff_window);
2335
2336           gtk_menu_scroll_to (menu, 0);
2337
2338         }
2339       else
2340         {
2341           gtk_widget_hide (GTK_WIDGET (menu));
2342           gtk_widget_hide (priv->tearoff_window);
2343           if (GTK_IS_CONTAINER (priv->toplevel))
2344             gtk_menu_reparent (menu, priv->toplevel, FALSE);
2345           gtk_widget_destroy (priv->tearoff_window);
2346
2347           priv->tearoff_window = NULL;
2348           priv->tearoff_hbox = NULL;
2349           priv->tearoff_scrollbar = NULL;
2350           priv->tearoff_adjustment = NULL;
2351         }
2352
2353       g_object_notify (G_OBJECT (menu), "tearoff-state");
2354     }
2355 }
2356
2357 /**
2358  * gtk_menu_get_tearoff_state:
2359  * @menu: a #GtkMenu
2360  *
2361  * Returns whether the menu is torn off.
2362  * See gtk_menu_set_tearoff_state().
2363  *
2364  * Return value: %TRUE if the menu is currently torn off.
2365  */
2366 gboolean
2367 gtk_menu_get_tearoff_state (GtkMenu *menu)
2368 {
2369   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
2370
2371   return menu->priv->torn_off;
2372 }
2373
2374 /**
2375  * gtk_menu_set_title:
2376  * @menu: a #GtkMenu
2377  * @title: a string containing the title for the menu
2378  *
2379  * Sets the title string for the menu.
2380  *
2381  * The title is displayed when the menu is shown as a tearoff
2382  * menu. If @title is %NULL, the menu will see if it is attached
2383  * to a parent menu item, and if so it will try to use the same
2384  * text as that menu item's label.
2385  */
2386 void
2387 gtk_menu_set_title (GtkMenu     *menu,
2388                     const gchar *title)
2389 {
2390   GtkMenuPrivate *priv = menu->priv;
2391   char *old_title;
2392
2393   g_return_if_fail (GTK_IS_MENU (menu));
2394
2395   old_title = priv->title;
2396   priv->title = g_strdup (title);
2397   g_free (old_title);
2398
2399   gtk_menu_update_title (menu);
2400   g_object_notify (G_OBJECT (menu), "tearoff-title");
2401 }
2402
2403 /**
2404  * gtk_menu_get_title:
2405  * @menu: a #GtkMenu
2406  *
2407  * Returns the title of the menu. See gtk_menu_set_title().
2408  *
2409  * Return value: the title of the menu, or %NULL if the menu
2410  *     has no title set on it. This string is owned by GTK+
2411  *     and should not be modified or freed.
2412  **/
2413 const gchar *
2414 gtk_menu_get_title (GtkMenu *menu)
2415 {
2416   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2417
2418   return menu->priv->title;
2419 }
2420
2421 /**
2422  * gtk_menu_reorder_child:
2423  * @menu: a #GtkMenu
2424  * @child: the #GtkMenuItem to move
2425  * @position: the new position to place @child.
2426  *     Positions are numbered from 0 to n - 1
2427  *
2428  * Moves @child to a new @position in the list of @menu
2429  * children.
2430  */
2431 void
2432 gtk_menu_reorder_child (GtkMenu   *menu,
2433                         GtkWidget *child,
2434                         gint       position)
2435 {
2436   GtkMenuShell *menu_shell;
2437
2438   g_return_if_fail (GTK_IS_MENU (menu));
2439   g_return_if_fail (GTK_IS_MENU_ITEM (child));
2440
2441   menu_shell = GTK_MENU_SHELL (menu);
2442
2443   if (g_list_find (menu_shell->priv->children, child))
2444     {
2445       menu_shell->priv->children = g_list_remove (menu_shell->priv->children, child);
2446       menu_shell->priv->children = g_list_insert (menu_shell->priv->children, child, position);
2447
2448       menu_queue_resize (menu);
2449     }
2450 }
2451
2452 static void
2453 gtk_menu_style_updated (GtkWidget *widget)
2454 {
2455   GTK_WIDGET_CLASS (gtk_menu_parent_class)->style_updated (widget);
2456
2457   if (gtk_widget_get_realized (widget))
2458     {
2459       GtkMenu *menu = GTK_MENU (widget);
2460       GtkMenuPrivate *priv = menu->priv;
2461       GtkStyleContext *context;
2462
2463       context = gtk_widget_get_style_context (widget);
2464
2465       gtk_style_context_set_background (context, priv->bin_window);
2466       gtk_style_context_set_background (context, priv->view_window);
2467       gtk_style_context_set_background (context, gtk_widget_get_window (widget));
2468     }
2469 }
2470
2471 static void
2472 get_arrows_border (GtkMenu   *menu,
2473                    GtkBorder *border)
2474 {
2475   GtkMenuPrivate *priv = menu->priv;
2476   guint scroll_arrow_height;
2477   GtkArrowPlacement arrow_placement;
2478
2479   gtk_widget_style_get (GTK_WIDGET (menu),
2480                         "scroll-arrow-vlength", &scroll_arrow_height,
2481                         "arrow_placement", &arrow_placement,
2482                         NULL);
2483
2484   switch (arrow_placement)
2485     {
2486     case GTK_ARROWS_BOTH:
2487       border->top = priv->upper_arrow_visible ? scroll_arrow_height : 0;
2488       border->bottom = priv->lower_arrow_visible ? scroll_arrow_height : 0;
2489       break;
2490
2491     case GTK_ARROWS_START:
2492       border->top = (priv->upper_arrow_visible ||
2493                      priv->lower_arrow_visible) ? scroll_arrow_height : 0;
2494       border->bottom = 0;
2495       break;
2496
2497     case GTK_ARROWS_END:
2498       border->top = 0;
2499       border->bottom = (priv->upper_arrow_visible ||
2500                         priv->lower_arrow_visible) ? scroll_arrow_height : 0;
2501       break;
2502     }
2503
2504   border->left = border->right = 0;
2505 }
2506
2507 static void
2508 get_menu_padding (GtkWidget *widget,
2509                   GtkBorder *padding)
2510 {
2511   GtkStyleContext *context;
2512   GtkStateFlags state;
2513
2514   context = gtk_widget_get_style_context (widget);
2515   state = gtk_widget_get_state_flags (widget);
2516
2517   gtk_style_context_get_padding (context, state, padding);
2518 }
2519
2520 static void
2521 gtk_menu_realize (GtkWidget *widget)
2522 {
2523   GtkMenu *menu = GTK_MENU (widget);
2524   GtkMenuPrivate *priv = menu->priv;
2525   GtkAllocation allocation;
2526   GtkStyleContext *context;
2527   GdkWindow *window;
2528   GdkWindowAttr attributes;
2529   gint attributes_mask;
2530   gint border_width;
2531   GtkWidget *child;
2532   GList *children;
2533   GtkBorder arrow_border, padding;
2534
2535   g_return_if_fail (GTK_IS_MENU (widget));
2536
2537   gtk_widget_set_realized (widget, TRUE);
2538
2539   gtk_widget_get_allocation (widget, &allocation);
2540
2541   attributes.window_type = GDK_WINDOW_CHILD;
2542   attributes.x = allocation.x;
2543   attributes.y = allocation.y;
2544   attributes.width = allocation.width;
2545   attributes.height = allocation.height;
2546   attributes.wclass = GDK_INPUT_OUTPUT;
2547   attributes.visual = gtk_widget_get_visual (widget);
2548   attributes.event_mask = gtk_widget_get_events (widget);
2549   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2550                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
2551
2552   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2553
2554   window = gdk_window_new (gtk_widget_get_parent_window (widget),
2555                            &attributes, attributes_mask);
2556   gtk_widget_set_window (widget, window);
2557   gdk_window_set_user_data (window, widget);
2558
2559   get_menu_padding (widget, &padding);
2560   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2561   context = gtk_widget_get_style_context (widget);
2562
2563   gtk_widget_get_allocation (widget, &allocation);
2564
2565   attributes.x = border_width + padding.left;
2566   attributes.y = border_width + padding.top;
2567   attributes.width = allocation.width -
2568     (2 * border_width) - padding.left - padding.right;
2569   attributes.height = allocation.height -
2570     (2 * border_width) - padding.top - padding.bottom;
2571
2572   get_arrows_border (menu, &arrow_border);
2573   attributes.y += arrow_border.top;
2574   attributes.height -= arrow_border.top;
2575   attributes.height -= arrow_border.bottom;
2576
2577   attributes.width = MAX (1, attributes.width);
2578   attributes.height = MAX (1, attributes.height);
2579
2580   priv->view_window = gdk_window_new (window,
2581                                       &attributes, attributes_mask);
2582   gdk_window_set_user_data (priv->view_window, menu);
2583
2584   gtk_widget_get_allocation (widget, &allocation);
2585
2586   attributes.x = 0;
2587   attributes.y = 0;
2588   attributes.width = allocation.width + (2 * border_width) +
2589     padding.left + padding.right;
2590   attributes.height = priv->requested_height - (2 * border_width) +
2591     padding.top + padding.bottom;
2592
2593   attributes.width = MAX (1, attributes.width);
2594   attributes.height = MAX (1, attributes.height);
2595
2596   priv->bin_window = gdk_window_new (priv->view_window,
2597                                      &attributes, attributes_mask);
2598   gdk_window_set_user_data (priv->bin_window, menu);
2599
2600   children = GTK_MENU_SHELL (menu)->priv->children;
2601   while (children)
2602     {
2603       child = children->data;
2604       children = children->next;
2605
2606       gtk_widget_set_parent_window (child, priv->bin_window);
2607     }
2608
2609   gtk_style_context_set_background (context, priv->bin_window);
2610   gtk_style_context_set_background (context, priv->view_window);
2611   gtk_style_context_set_background (context, window);
2612
2613   if (GTK_MENU_SHELL (widget)->priv->active_menu_item)
2614     gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
2615                                   GTK_MENU_SHELL (widget)->priv->active_menu_item);
2616
2617   gdk_window_show (priv->bin_window);
2618   gdk_window_show (priv->view_window);
2619 }
2620
2621 static gboolean
2622 gtk_menu_focus (GtkWidget       *widget,
2623                 GtkDirectionType direction)
2624 {
2625   /* A menu or its menu items cannot have focus */
2626   return FALSE;
2627 }
2628
2629 /* See notes in gtk_menu_popup() for information
2630  * about the "grab transfer window"
2631  */
2632 static GdkWindow *
2633 menu_grab_transfer_window_get (GtkMenu *menu)
2634 {
2635   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2636   if (!window)
2637     {
2638       GdkWindowAttr attributes;
2639       gint attributes_mask;
2640
2641       attributes.x = -100;
2642       attributes.y = -100;
2643       attributes.width = 10;
2644       attributes.height = 10;
2645       attributes.window_type = GDK_WINDOW_TEMP;
2646       attributes.wclass = GDK_INPUT_ONLY;
2647       attributes.override_redirect = TRUE;
2648       attributes.event_mask = 0;
2649
2650       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
2651
2652       window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
2653                                &attributes, attributes_mask);
2654       gdk_window_set_user_data (window, menu);
2655
2656       gdk_window_show (window);
2657
2658       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), window);
2659     }
2660
2661   return window;
2662 }
2663
2664 static void
2665 menu_grab_transfer_window_destroy (GtkMenu *menu)
2666 {
2667   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2668   if (window)
2669     {
2670       gdk_window_set_user_data (window, NULL);
2671       gdk_window_destroy (window);
2672       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
2673     }
2674 }
2675
2676 static void
2677 gtk_menu_unrealize (GtkWidget *widget)
2678 {
2679   GtkMenu *menu = GTK_MENU (widget);
2680   GtkMenuPrivate *priv = menu->priv;
2681
2682   menu_grab_transfer_window_destroy (menu);
2683
2684   gdk_window_set_user_data (priv->view_window, NULL);
2685   gdk_window_destroy (priv->view_window);
2686   priv->view_window = NULL;
2687
2688   gdk_window_set_user_data (priv->bin_window, NULL);
2689   gdk_window_destroy (priv->bin_window);
2690   priv->bin_window = NULL;
2691
2692   GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget);
2693 }
2694
2695 static gint
2696 calculate_line_heights (GtkMenu *menu,
2697                         gint     for_width,
2698                         guint  **ret_min_heights,
2699                         guint  **ret_nat_heights)
2700 {
2701   GtkBorder       padding;
2702   GtkMenuPrivate *priv;
2703   GtkMenuShell   *menu_shell;
2704   GtkWidget      *child, *widget;
2705   GList          *children;
2706   guint           border_width;
2707   guint           n_columns;
2708   gint            n_heights;
2709   guint          *min_heights;
2710   guint          *nat_heights;
2711   gint            avail_width;
2712
2713   priv         = menu->priv;
2714   widget       = GTK_WIDGET (menu);
2715   menu_shell   = GTK_MENU_SHELL (widget);
2716
2717   min_heights  = g_new0 (guint, gtk_menu_get_n_rows (menu));
2718   nat_heights  = g_new0 (guint, gtk_menu_get_n_rows (menu));
2719   n_heights    = gtk_menu_get_n_rows (menu);
2720   n_columns    = gtk_menu_get_n_columns (menu);
2721   avail_width  = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns;
2722
2723   get_menu_padding (widget, &padding);
2724
2725   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
2726   avail_width -= (border_width) * 2 + padding.left + padding.right;
2727
2728   for (children = menu_shell->priv->children; children; children = children->next)
2729     {
2730       gint part;
2731       gint toggle_size;
2732       gint l, r, t, b;
2733       gint child_min, child_nat;
2734
2735       child = children->data;
2736
2737       if (!gtk_widget_get_visible (child))
2738         continue;
2739
2740       get_effective_child_attach (child, &l, &r, &t, &b);
2741
2742       part = avail_width / (r - l);
2743
2744       gtk_widget_get_preferred_height_for_width (child, part,
2745                                                  &child_min, &child_nat);
2746
2747       gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
2748
2749       part = MAX (child_min, toggle_size) / (b - t);
2750       min_heights[t] = MAX (min_heights[t], part);
2751
2752       part = MAX (child_nat, toggle_size) / (b - t);
2753       nat_heights[t] = MAX (nat_heights[t], part);
2754     }
2755
2756   if (ret_min_heights)
2757     *ret_min_heights = min_heights;
2758   else
2759     g_free (min_heights);
2760
2761   if (ret_nat_heights)
2762     *ret_nat_heights = nat_heights;
2763   else
2764     g_free (nat_heights);
2765
2766   return n_heights;
2767 }
2768
2769 static void
2770 gtk_menu_size_allocate (GtkWidget     *widget,
2771                         GtkAllocation *allocation)
2772 {
2773   GtkMenu *menu;
2774   GtkMenuPrivate *priv;
2775   GtkMenuShell *menu_shell;
2776   GtkWidget *child;
2777   GtkAllocation child_allocation;
2778   GList *children;
2779   gint x, y, i;
2780   gint width, height;
2781   guint border_width;
2782   GtkBorder padding;
2783
2784   g_return_if_fail (GTK_IS_MENU (widget));
2785   g_return_if_fail (allocation != NULL);
2786
2787   menu = GTK_MENU (widget);
2788   menu_shell = GTK_MENU_SHELL (widget);
2789   priv = menu->priv;
2790
2791   gtk_widget_set_allocation (widget, allocation);
2792
2793   get_menu_padding (widget, &padding);
2794   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
2795
2796   g_free (priv->heights);
2797   priv->heights_length = calculate_line_heights (menu,
2798                                                  allocation->width,
2799                                                  &priv->heights,
2800                                                  NULL);
2801
2802   /* refresh our cached height request */
2803   priv->requested_height = (2 * border_width) + padding.top + padding.bottom;
2804   for (i = 0; i < priv->heights_length; i++)
2805     priv->requested_height += priv->heights[i];
2806
2807   x = border_width + padding.left;
2808   y = border_width + padding.top;
2809   width = allocation->width - (2 * border_width) -
2810     padding.left - padding.right;
2811   height = allocation->height - (2 * border_width) -
2812     padding.top - padding.bottom;
2813
2814   if (menu_shell->priv->active)
2815     gtk_menu_scroll_to (menu, priv->scroll_offset);
2816
2817   if (!priv->tearoff_active)
2818     {
2819       GtkBorder arrow_border;
2820
2821       get_arrows_border (menu, &arrow_border);
2822       y += arrow_border.top;
2823       height -= arrow_border.top;
2824       height -= arrow_border.bottom;
2825     }
2826
2827   width = MAX (1, width);
2828   height = MAX (1, height);
2829
2830   if (gtk_widget_get_realized (widget))
2831     {
2832       gdk_window_move_resize (gtk_widget_get_window (widget),
2833                               allocation->x, allocation->y,
2834                               allocation->width, allocation->height);
2835
2836       gdk_window_move_resize (priv->view_window, x, y, width, height);
2837     }
2838
2839   if (menu_shell->priv->children)
2840     {
2841       gint base_width = width / gtk_menu_get_n_columns (menu);
2842
2843       children = menu_shell->priv->children;
2844       while (children)
2845         {
2846           child = children->data;
2847           children = children->next;
2848
2849           if (gtk_widget_get_visible (child))
2850             {
2851               gint i;
2852               gint l, r, t, b;
2853
2854               get_effective_child_attach (child, &l, &r, &t, &b);
2855
2856               if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
2857                 {
2858                   guint tmp;
2859                   tmp = gtk_menu_get_n_columns (menu) - l;
2860                   l = gtk_menu_get_n_columns (menu) - r;
2861                   r = tmp;
2862                 }
2863
2864               child_allocation.width = (r - l) * base_width;
2865               child_allocation.height = 0;
2866               child_allocation.x = l * base_width;
2867               child_allocation.y = 0;
2868
2869               for (i = 0; i < b; i++)
2870                 {
2871                   if (i < t)
2872                     child_allocation.y += priv->heights[i];
2873                   else
2874                     child_allocation.height += priv->heights[i];
2875                 }
2876
2877               gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
2878                                                   priv->toggle_size);
2879
2880               gtk_widget_size_allocate (child, &child_allocation);
2881               gtk_widget_queue_draw (child);
2882             }
2883         }
2884
2885       /* Resize the item window */
2886       if (gtk_widget_get_realized (widget))
2887         {
2888           gint i;
2889           gint width, height;
2890
2891           height = 0;
2892           for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2893             height += priv->heights[i];
2894
2895           width = gtk_menu_get_n_columns (menu) * base_width;
2896           gdk_window_resize (priv->bin_window, width, height);
2897         }
2898
2899       if (priv->tearoff_active)
2900         {
2901           if (height >= priv->requested_height)
2902             {
2903               if (gtk_widget_get_visible (priv->tearoff_scrollbar))
2904                 {
2905                   gtk_widget_hide (priv->tearoff_scrollbar);
2906                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2907
2908                   gtk_menu_scroll_to (menu, 0);
2909                 }
2910             }
2911           else
2912             {
2913               gtk_adjustment_configure (priv->tearoff_adjustment,
2914                                         gtk_adjustment_get_value (priv->tearoff_adjustment),
2915                                         0,
2916                                         priv->requested_height,
2917                                         gtk_adjustment_get_step_increment (priv->tearoff_adjustment),
2918                                         gtk_adjustment_get_page_increment (priv->tearoff_adjustment),
2919                                         allocation->height);
2920
2921               if (!gtk_widget_get_visible (priv->tearoff_scrollbar))
2922                 {
2923                   gtk_widget_show (priv->tearoff_scrollbar);
2924                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2925                 }
2926             }
2927         }
2928     }
2929 }
2930
2931 static void
2932 get_arrows_visible_area (GtkMenu      *menu,
2933                          GdkRectangle *border,
2934                          GdkRectangle *upper,
2935                          GdkRectangle *lower,
2936                          gint         *arrow_space)
2937 {
2938   GtkArrowPlacement arrow_placement;
2939   GtkWidget *widget = GTK_WIDGET (menu);
2940   guint border_width;
2941   gint scroll_arrow_height;
2942   GtkBorder menu_padding;
2943
2944   gtk_widget_style_get (widget,
2945                         "scroll-arrow-vlength", &scroll_arrow_height,
2946                         "arrow-placement", &arrow_placement,
2947                         NULL);
2948
2949   get_menu_padding (widget, &menu_padding);
2950   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2951   border->x = border_width + menu_padding.left;
2952   border->y = border_width + menu_padding.top;
2953   border->width = gdk_window_get_width (gtk_widget_get_window (widget));
2954   border->height = gdk_window_get_height (gtk_widget_get_window (widget));
2955
2956   switch (arrow_placement)
2957     {
2958     case GTK_ARROWS_BOTH:
2959       upper->x = border->x;
2960       upper->y = border->y;
2961       upper->width = border->width - 2 * border->x;
2962       upper->height = scroll_arrow_height;
2963
2964       lower->x = border->x;
2965       lower->y = border->height - border->y - scroll_arrow_height;
2966       lower->width = border->width - 2 * border->x;
2967       lower->height = scroll_arrow_height;
2968       break;
2969
2970     case GTK_ARROWS_START:
2971       upper->x = border->x;
2972       upper->y = border->y;
2973       upper->width = (border->width - 2 * border->x) / 2;
2974       upper->height = scroll_arrow_height;
2975
2976       lower->x = border->x + upper->width;
2977       lower->y = border->y;
2978       lower->width = (border->width - 2 * border->x) / 2;
2979       lower->height = scroll_arrow_height;
2980       break;
2981
2982     case GTK_ARROWS_END:
2983       upper->x = border->x;
2984       upper->y = border->height - border->y - scroll_arrow_height;
2985       upper->width = (border->width - 2 * border->x) / 2;
2986       upper->height = scroll_arrow_height;
2987
2988       lower->x = border->x + upper->width;
2989       lower->y = border->height - border->y - scroll_arrow_height;
2990       lower->width = (border->width - 2 * border->x) / 2;
2991       lower->height = scroll_arrow_height;
2992       break;
2993
2994     default:
2995        g_assert_not_reached();
2996        upper->x = upper->y = upper->width = upper->height = 0;
2997        lower->x = lower->y = lower->width = lower->height = 0;
2998     }
2999
3000   *arrow_space = scroll_arrow_height - menu_padding.top - menu_padding.bottom;
3001 }
3002
3003 static gboolean
3004 gtk_menu_draw (GtkWidget *widget,
3005                cairo_t   *cr)
3006 {
3007   GtkMenu *menu;
3008   GtkMenuPrivate *priv;
3009   GtkStyleContext *context;
3010   GdkRectangle border;
3011   GdkRectangle upper;
3012   GdkRectangle lower;
3013   gint arrow_space;
3014   GtkBorder menu_padding;
3015
3016   menu = GTK_MENU (widget);
3017   priv = menu->priv;
3018   context = gtk_widget_get_style_context (widget);
3019
3020   get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
3021   get_menu_padding (widget, &menu_padding);
3022
3023   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
3024     {
3025       gfloat arrow_scaling;
3026       gint arrow_size;
3027
3028       gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
3029       arrow_size = arrow_scaling * arrow_space;
3030
3031       gtk_render_background (context, cr, 0, 0,
3032                              gtk_widget_get_allocated_width (widget),
3033                              gtk_widget_get_allocated_height (widget));
3034       gtk_render_frame (context, cr, 0, 0,
3035                         gtk_widget_get_allocated_width (widget),
3036                         gtk_widget_get_allocated_height (widget));
3037
3038       gtk_style_context_save (context);
3039       gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
3040
3041       if (priv->upper_arrow_visible && !priv->tearoff_active)
3042         {
3043           gtk_style_context_save (context);
3044           gtk_style_context_set_state (context, priv->upper_arrow_state);
3045
3046           gtk_render_background (context, cr,
3047                                  upper.x, upper.y,
3048                                  upper.width, upper.height);
3049           gtk_render_frame (context, cr,
3050                             upper.x, upper.y,
3051                             upper.width, upper.height);
3052
3053           gtk_render_arrow (context, cr, 0,
3054                             upper.x + (upper.width - arrow_size) / 2,
3055                             upper.y + menu_padding.top + (arrow_space - arrow_size) / 2,
3056                             arrow_size);
3057
3058           gtk_style_context_restore (context);
3059         }
3060
3061       if (priv->lower_arrow_visible && !priv->tearoff_active)
3062         {
3063           gtk_style_context_save (context);
3064           gtk_style_context_set_state (context, priv->lower_arrow_state);
3065
3066           gtk_render_background (context, cr,
3067                                  lower.x, lower.y,
3068                                  lower.width, lower.height);
3069           gtk_render_frame (context, cr,
3070                             lower.x, lower.y,
3071                             lower.width, lower.height);
3072
3073           gtk_render_arrow (context, cr, G_PI,
3074                             lower.x + (lower.width - arrow_size) / 2,
3075                             lower.y + menu_padding.bottom + (arrow_space - arrow_size) / 2,
3076                             arrow_size);
3077
3078           gtk_style_context_restore (context);
3079         }
3080
3081       gtk_style_context_restore (context);
3082     }
3083
3084   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
3085     {
3086       gint y = -border.y + priv->scroll_offset;
3087
3088       cairo_save (cr);
3089       gtk_cairo_transform_to_window (cr, widget, priv->bin_window);
3090
3091       if (!priv->tearoff_active)
3092         {
3093           GtkBorder arrow_border;
3094
3095           get_arrows_border (menu, &arrow_border);
3096           y -= arrow_border.top;
3097         }
3098
3099       gtk_render_background (context, cr,
3100                              - border.x, y,
3101                              border.width, border.height);
3102       gtk_render_frame (context, cr,
3103                         - border.x, y,
3104                         border.width, border.height);
3105
3106       cairo_restore (cr);
3107     }
3108
3109   GTK_WIDGET_CLASS (gtk_menu_parent_class)->draw (widget, cr);
3110
3111   return FALSE;
3112 }
3113
3114 static void
3115 gtk_menu_show (GtkWidget *widget)
3116 {
3117   GtkMenu *menu = GTK_MENU (widget);
3118
3119   _gtk_menu_refresh_accel_paths (menu, FALSE);
3120
3121   GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget);
3122 }
3123
3124
3125 static void 
3126 gtk_menu_get_preferred_width (GtkWidget *widget,
3127                               gint      *minimum_size,
3128                               gint      *natural_size)
3129 {
3130   GtkMenu        *menu;
3131   GtkMenuShell   *menu_shell;
3132   GtkMenuPrivate *priv;
3133   GtkWidget      *child;
3134   GList          *children;
3135   guint           max_toggle_size;
3136   guint           max_accel_width;
3137   guint           border_width;
3138   gint            child_min, child_nat;
3139   gint            min_width, nat_width;
3140   GtkBorder       padding;
3141
3142   menu       = GTK_MENU (widget);
3143   menu_shell = GTK_MENU_SHELL (widget);
3144   priv       = menu->priv;
3145
3146   min_width = nat_width = 0;
3147
3148   max_toggle_size = 0;
3149   max_accel_width = 0;
3150
3151   children = menu_shell->priv->children;
3152   while (children)
3153     {
3154       gint part;
3155       gint toggle_size;
3156       gint l, r, t, b;
3157
3158       child = children->data;
3159       children = children->next;
3160
3161       if (! gtk_widget_get_visible (child))
3162         continue;
3163
3164       get_effective_child_attach (child, &l, &r, &t, &b);
3165
3166       /* It's important to size_request the child
3167        * before doing the toggle size request, in
3168        * case the toggle size request depends on the size
3169        * request of a child of the child (e.g. for ImageMenuItem)
3170        */
3171        gtk_widget_get_preferred_width (child, &child_min, &child_nat);
3172
3173        gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
3174        max_toggle_size = MAX (max_toggle_size, toggle_size);
3175        max_accel_width = MAX (max_accel_width,
3176                               GTK_MENU_ITEM (child)->priv->accelerator_width);
3177
3178        part = child_min / (r - l);
3179        min_width = MAX (min_width, part);
3180
3181        part = child_nat / (r - l);
3182        nat_width = MAX (nat_width, part);
3183     }
3184
3185   /* If the menu doesn't include any images or check items
3186    * reserve the space so that all menus are consistent.
3187    * We only do this for 'ordinary' menus, not for combobox
3188    * menus or multi-column menus
3189    */
3190   if (max_toggle_size == 0 &&
3191       gtk_menu_get_n_columns (menu) == 1 &&
3192       !priv->no_toggle_size)
3193     {
3194       GtkStyleContext *context;
3195       GtkWidgetPath *check_path;
3196       guint toggle_spacing;
3197       guint indicator_size;
3198
3199       context = gtk_style_context_new ();
3200
3201       /* Create a GtkCheckMenuItem path, only to query indicator spacing */
3202       check_path = _gtk_widget_create_path (widget);
3203       gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM);
3204
3205       gtk_style_context_set_path (context, check_path);
3206       gtk_widget_path_free (check_path);
3207       gtk_style_context_set_screen (context, gtk_widget_get_screen (widget));
3208
3209       gtk_style_context_get_style (context,
3210                                    "toggle-spacing", &toggle_spacing,
3211                                    "indicator-size", &indicator_size,
3212                                    NULL);
3213
3214       max_toggle_size = indicator_size + toggle_spacing;
3215
3216       g_object_unref (context);
3217     }
3218
3219   min_width += 2 * max_toggle_size + max_accel_width;
3220   min_width *= gtk_menu_get_n_columns (menu);
3221
3222   nat_width += 2 * max_toggle_size + max_accel_width;
3223   nat_width *= gtk_menu_get_n_columns (menu);
3224
3225   get_menu_padding (widget, &padding);
3226   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
3227   min_width   += (2 * border_width) + padding.left + padding.right;
3228   nat_width   += (2 * border_width) + padding.left + padding.right;
3229
3230   priv->toggle_size = max_toggle_size;
3231   priv->accel_size  = max_accel_width;
3232
3233   if (minimum_size)
3234     *minimum_size = min_width;
3235
3236   if (natural_size)
3237     *natural_size = nat_width;
3238
3239   /* Don't resize the tearoff if it is not active,
3240    * because it won't redraw (it is only a background pixmap).
3241    */
3242   if (priv->tearoff_active)
3243     gtk_menu_set_tearoff_hints (menu, min_width);
3244 }
3245
3246 static void
3247 gtk_menu_get_preferred_height (GtkWidget *widget,
3248                                gint      *minimum_size,
3249                                gint      *natural_size)
3250 {
3251   gint min_width;
3252
3253   /* Menus are height-for-width only, just return the height
3254    * for the minimum width
3255    */
3256   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
3257   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
3258 }
3259
3260 static void
3261 gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
3262                                          gint       for_size,
3263                                          gint      *minimum_size,
3264                                          gint      *natural_size)
3265 {
3266   GtkBorder       padding;
3267   GtkMenu        *menu = GTK_MENU (widget);
3268   GtkMenuPrivate *priv = menu->priv;
3269   guint          *min_heights, *nat_heights;
3270   guint           border_width;
3271   gint            n_heights, i;
3272   gint            min_height, nat_height;
3273
3274   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
3275   get_menu_padding (widget, &padding);
3276
3277   min_height = nat_height = (2 * border_width) + padding.top + padding.bottom;
3278
3279   n_heights =
3280     calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
3281
3282   for (i = 0; i < n_heights; i++)
3283     {
3284       min_height += min_heights[i];
3285       nat_height += nat_heights[i];
3286     }
3287
3288   if (priv->have_position)
3289     {
3290       GdkScreen *screen = gtk_widget_get_screen (priv->toplevel);
3291       GdkRectangle monitor;
3292
3293       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
3294
3295       if (priv->position_y + min_height > monitor.y + monitor.height)
3296         min_height = monitor.y + monitor.height - priv->position_y;
3297
3298       if (priv->position_y + nat_height > monitor.y + monitor.height)
3299         nat_height = monitor.y + monitor.height - priv->position_y;
3300
3301       if (priv->position_y < monitor.y)
3302         {
3303           min_height -= monitor.y - priv->position_y;
3304           nat_height -= monitor.y - priv->position_y;
3305         }
3306     }
3307
3308   if (minimum_size)
3309     *minimum_size = min_height;
3310
3311   if (natural_size)
3312     *natural_size = nat_height;
3313
3314   g_free (min_heights);
3315   g_free (nat_heights);
3316 }
3317
3318 static gboolean
3319 pointer_in_menu_window (GtkWidget *widget,
3320                         gdouble    x_root,
3321                         gdouble    y_root)
3322 {
3323   GtkMenu *menu = GTK_MENU (widget);
3324   GtkMenuPrivate *priv = menu->priv;
3325   GtkAllocation allocation;
3326
3327   if (gtk_widget_get_mapped (priv->toplevel))
3328     {
3329       GtkMenuShell *menu_shell;
3330       gint          window_x, window_y;
3331
3332       gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
3333                                &window_x, &window_y);
3334
3335       gtk_widget_get_allocation (widget, &allocation);
3336       if (x_root >= window_x && x_root < window_x + allocation.width &&
3337           y_root >= window_y && y_root < window_y + allocation.height)
3338         return TRUE;
3339
3340       menu_shell = GTK_MENU_SHELL (widget);
3341
3342       if (GTK_IS_MENU (menu_shell->priv->parent_menu_shell))
3343         return pointer_in_menu_window (menu_shell->priv->parent_menu_shell,
3344                                        x_root, y_root);
3345     }
3346
3347   return FALSE;
3348 }
3349
3350 static gboolean
3351 gtk_menu_button_press (GtkWidget      *widget,
3352                        GdkEventButton *event)
3353 {
3354   GdkDevice *source_device;
3355   GtkWidget *event_widget;
3356   GtkMenu *menu;
3357
3358   if (event->type != GDK_BUTTON_PRESS)
3359     return FALSE;
3360
3361   source_device = gdk_event_get_source_device ((GdkEvent *) event);
3362   event_widget = gtk_get_event_widget ((GdkEvent *) event);
3363   menu = GTK_MENU (widget);
3364
3365   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3366    *  was clicked. The check for the event_widget being a GtkMenuShell
3367    *  works because we have the pointer grabbed on menu_shell->window
3368    *  with owner_events=TRUE, so all events that are either outside
3369    *  the menu or on its border are delivered relative to
3370    *  menu_shell->window.
3371    */
3372   if (GTK_IS_MENU_SHELL (event_widget) &&
3373       pointer_in_menu_window (widget, event->x_root, event->y_root))
3374     return TRUE;
3375
3376   if (GTK_IS_MENU_ITEM (event_widget) &&
3377       gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
3378       GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
3379       !gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
3380     menu->priv->ignore_button_release = TRUE;
3381
3382   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
3383 }
3384
3385 static gboolean
3386 gtk_menu_button_release (GtkWidget      *widget,
3387                          GdkEventButton *event)
3388 {
3389   GtkMenuPrivate *priv = GTK_MENU (widget)->priv;
3390
3391   if (priv->ignore_button_release)
3392     {
3393       priv->ignore_button_release = FALSE;
3394       return FALSE;
3395     }
3396
3397   if (event->type != GDK_BUTTON_RELEASE)
3398     return FALSE;
3399
3400   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3401    *  was clicked (see comment in button_press()).
3402    */
3403   if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
3404       pointer_in_menu_window (widget, event->x_root, event->y_root))
3405     {
3406       /*  Ugly: make sure menu_shell->button gets reset to 0 when we
3407        *  bail out early here so it is in a consistent state for the
3408        *  next button_press/button_release in GtkMenuShell.
3409        *  See bug #449371.
3410        */
3411       if (GTK_MENU_SHELL (widget)->priv->active)
3412         GTK_MENU_SHELL (widget)->priv->button = 0;
3413
3414       return TRUE;
3415     }
3416
3417   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_release_event (widget, event);
3418 }
3419
3420 static const gchar *
3421 get_accel_path (GtkWidget *menu_item,
3422                 gboolean  *locked)
3423 {
3424   const gchar *path;
3425   GtkWidget *label;
3426   GClosure *accel_closure;
3427   GtkAccelGroup *accel_group;
3428
3429   path = _gtk_widget_get_accel_path (menu_item, locked);
3430   if (!path)
3431     {
3432       path = GTK_MENU_ITEM (menu_item)->priv->accel_path;
3433
3434       if (locked)
3435         {
3436           *locked = TRUE;
3437
3438           label = gtk_bin_get_child (GTK_BIN (menu_item));
3439
3440           if (GTK_IS_ACCEL_LABEL (label))
3441             {
3442               g_object_get (label,
3443                             "accel-closure", &accel_closure,
3444                             NULL);
3445               if (accel_closure)
3446                 {
3447                   accel_group = gtk_accel_group_from_accel_closure (accel_closure);
3448
3449                   *locked = gtk_accel_group_get_is_locked (accel_group);
3450                 }
3451             }
3452         }
3453     }
3454
3455   return path;
3456 }
3457
3458 static gboolean
3459 gtk_menu_key_press (GtkWidget   *widget,
3460                     GdkEventKey *event)
3461 {
3462   GtkMenuShell *menu_shell;
3463   GtkMenu *menu;
3464   gboolean delete = FALSE;
3465   gboolean can_change_accels;
3466   gchar *accel = NULL;
3467   guint accel_key, accel_mods;
3468   GdkModifierType consumed_modifiers;
3469   GdkDisplay *display;
3470
3471   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
3472   g_return_val_if_fail (event != NULL, FALSE);
3473
3474   menu_shell = GTK_MENU_SHELL (widget);
3475   menu = GTK_MENU (widget);
3476
3477   gtk_menu_stop_navigating_submenu (menu);
3478
3479   if (GTK_WIDGET_CLASS (gtk_menu_parent_class)->key_press_event (widget, event))
3480     return TRUE;
3481
3482   display = gtk_widget_get_display (widget);
3483
3484   g_object_get (gtk_widget_get_settings (widget),
3485                 "gtk-menu-bar-accel", &accel,
3486                 "gtk-can-change-accels", &can_change_accels,
3487                 NULL);
3488
3489   if (accel && *accel)
3490     {
3491       guint keyval = 0;
3492       GdkModifierType mods = 0;
3493
3494       gtk_accelerator_parse (accel, &keyval, &mods);
3495
3496       if (keyval == 0)
3497         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
3498
3499       /* FIXME this is wrong, needs to be in the global accel resolution
3500        * thing, to properly consider i18n etc., but that probably requires
3501        * AccelGroup changes etc.
3502        */
3503       if (event->keyval == keyval && (mods & event->state) == mods)
3504         {
3505           gtk_menu_shell_cancel (menu_shell);
3506           g_free (accel);
3507           return TRUE;
3508         }
3509     }
3510
3511   g_free (accel);
3512
3513   switch (event->keyval)
3514     {
3515     case GDK_KEY_Delete:
3516     case GDK_KEY_KP_Delete:
3517     case GDK_KEY_BackSpace:
3518       delete = TRUE;
3519       break;
3520     default:
3521       break;
3522     }
3523
3524   /* Figure out what modifiers went into determining the key symbol */
3525   _gtk_translate_keyboard_accel_state (gdk_keymap_get_for_display (display),
3526                                        event->hardware_keycode,
3527                                        event->state,
3528                                        gtk_accelerator_get_default_mod_mask (),
3529                                        event->group,
3530                                        &accel_key, NULL, NULL, &consumed_modifiers);
3531
3532   accel_key = gdk_keyval_to_lower (accel_key);
3533   accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
3534
3535   /* If lowercasing affects the keysym, then we need to include SHIFT
3536    * in the modifiers, We re-upper case when we match against the
3537    * keyval, but display and save in caseless form.
3538    */
3539   if (accel_key != event->keyval)
3540     accel_mods |= GDK_SHIFT_MASK;
3541
3542   /* Modify the accelerators */
3543   if (can_change_accels &&
3544       menu_shell->priv->active_menu_item &&
3545       gtk_bin_get_child (GTK_BIN (menu_shell->priv->active_menu_item)) && /* no separators */
3546       GTK_MENU_ITEM (menu_shell->priv->active_menu_item)->priv->submenu == NULL &&  /* no submenus */
3547       (delete || gtk_accelerator_valid (accel_key, accel_mods)))
3548     {
3549       GtkWidget *menu_item = menu_shell->priv->active_menu_item;
3550       gboolean locked, replace_accels = TRUE;
3551       const gchar *path;
3552
3553       path = get_accel_path (menu_item, &locked);
3554       if (!path || locked)
3555         {
3556           /* Can't change accelerators on menu_items without paths
3557            * (basically, those items are accelerator-locked).
3558            */
3559           gtk_widget_error_bell (widget);
3560         }
3561       else
3562         {
3563           gboolean changed;
3564
3565           /* For the keys that act to delete the current setting,
3566            * we delete the current setting if there is one, otherwise,
3567            * we set the key as the accelerator.
3568            */
3569           if (delete)
3570             {
3571               GtkAccelKey key;
3572
3573               if (gtk_accel_map_lookup_entry (path, &key) &&
3574                   (key.accel_key || key.accel_mods))
3575                 {
3576                   accel_key = 0;
3577                   accel_mods = 0;
3578                 }
3579             }
3580           changed = gtk_accel_map_change_entry (path, accel_key, accel_mods, replace_accels);
3581
3582           if (!changed)
3583             {
3584               /* We failed, probably because this key is in use
3585                * and locked already.
3586                */
3587               gtk_widget_error_bell (widget);
3588             }
3589         }
3590     }
3591
3592   return TRUE;
3593 }
3594
3595 static gboolean
3596 check_threshold (GtkWidget *widget,
3597                  gint       start_x,
3598                  gint       start_y,
3599                  gint       x,
3600                  gint       y)
3601 {
3602 #define THRESHOLD 8
3603
3604   return
3605     ABS (start_x - x) > THRESHOLD ||
3606     ABS (start_y - y) > THRESHOLD;
3607 }
3608
3609 static gboolean
3610 definitely_within_item (GtkWidget *widget,
3611                         gint       x,
3612                         gint       y)
3613 {
3614   GdkWindow *window = GTK_MENU_ITEM (widget)->priv->event_window;
3615   int w, h;
3616
3617   w = gdk_window_get_width (window);
3618   h = gdk_window_get_height (window);
3619
3620   return
3621     check_threshold (widget, 0, 0, x, y) &&
3622     check_threshold (widget, w - 1, 0, x, y) &&
3623     check_threshold (widget, w - 1, h - 1, x, y) &&
3624     check_threshold (widget, 0, h - 1, x, y);
3625 }
3626
3627 static gboolean
3628 gtk_menu_has_navigation_triangle (GtkMenu *menu)
3629 {
3630   GtkMenuPrivate *priv = menu->priv;
3631
3632   return priv->navigation_height && priv->navigation_width;
3633 }
3634
3635 static gboolean
3636 gtk_menu_motion_notify (GtkWidget      *widget,
3637                         GdkEventMotion *event)
3638 {
3639   GtkWidget *menu_item;
3640   GtkMenu *menu;
3641   GtkMenuShell *menu_shell;
3642   GtkWidget *parent;
3643   GdkDevice *source_device;
3644
3645   gboolean need_enter;
3646
3647   source_device = gdk_event_get_source_device ((GdkEvent *) event);
3648
3649   if (GTK_IS_MENU (widget) &&
3650       gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
3651     {
3652       GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
3653
3654       if (priv->ignore_button_release)
3655         priv->ignore_button_release = FALSE;
3656
3657       gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root,
3658                                  TRUE, TRUE);
3659     }
3660
3661   /* We received the event for one of two reasons:
3662    *
3663    * a) We are the active menu, and did gtk_grab_add()
3664    * b) The widget is a child of ours, and the event was propagated
3665    *
3666    * Since for computation of navigation regions, we want the menu which
3667    * is the parent of the menu item, for a), we need to find that menu,
3668    * which may be different from 'widget'.
3669    */
3670   menu_item = gtk_get_event_widget ((GdkEvent*) event);
3671   parent = gtk_widget_get_parent (menu_item);
3672   if (!GTK_IS_MENU_ITEM (menu_item) ||
3673       !GTK_IS_MENU (parent))
3674     return FALSE;
3675
3676   menu_shell = GTK_MENU_SHELL (parent);
3677   menu = GTK_MENU (menu_shell);
3678
3679   if (definitely_within_item (menu_item, event->x, event->y))
3680     menu_shell->priv->activate_time = 0;
3681
3682   need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->priv->ignore_enter);
3683
3684   /* Check to see if we are within an active submenu's navigation region
3685    */
3686   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
3687     return TRUE;
3688
3689   /* Make sure we pop down if we enter a non-selectable menu item, so we
3690    * don't show a submenu when the cursor is outside the stay-up triangle.
3691    */
3692   if (!_gtk_menu_item_is_selectable (menu_item))
3693     {
3694       /* We really want to deselect, but this gives the menushell code
3695        * a chance to do some bookkeeping about the menuitem.
3696        */
3697       gtk_menu_shell_select_item (menu_shell, menu_item);
3698       return FALSE;
3699     }
3700
3701   if (need_enter)
3702     {
3703       /* The menu is now sensitive to enter events on its items, but
3704        * was previously sensitive.  So we fake an enter event.
3705        */
3706       menu_shell->priv->ignore_enter = FALSE;
3707
3708       if (event->x >= 0 && event->x < gdk_window_get_width (event->window) &&
3709           event->y >= 0 && event->y < gdk_window_get_height (event->window))
3710         {
3711           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
3712           gboolean result;
3713
3714           send_event->crossing.window = g_object_ref (event->window);
3715           send_event->crossing.time = event->time;
3716           send_event->crossing.send_event = TRUE;
3717           send_event->crossing.x_root = event->x_root;
3718           send_event->crossing.y_root = event->y_root;
3719           send_event->crossing.x = event->x;
3720           send_event->crossing.y = event->y;
3721           send_event->crossing.state = event->state;
3722           gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
3723
3724           /* We send the event to 'widget', the currently active menu,
3725            * instead of 'menu', the menu that the pointer is in. This
3726            * will ensure that the event will be ignored unless the
3727            * menuitem is a child of the active menu or some parent
3728            * menu of the active menu.
3729            */
3730           result = gtk_widget_event (widget, send_event);
3731           gdk_event_free (send_event);
3732
3733           return result;
3734         }
3735     }
3736
3737   return FALSE;
3738 }
3739
3740 static gboolean
3741 get_double_arrows (GtkMenu *menu)
3742 {
3743   GtkMenuPrivate   *priv = menu->priv;
3744   gboolean          double_arrows;
3745   GtkArrowPlacement arrow_placement;
3746
3747   gtk_widget_style_get (GTK_WIDGET (menu),
3748                         "double-arrows", &double_arrows,
3749                         "arrow-placement", &arrow_placement,
3750                         NULL);
3751
3752   if (arrow_placement != GTK_ARROWS_BOTH)
3753     return TRUE;
3754
3755   return double_arrows || (priv->initially_pushed_in &&
3756                            priv->scroll_offset != 0);
3757 }
3758
3759 static void
3760 gtk_menu_scroll_by (GtkMenu *menu,
3761                     gint     step)
3762 {
3763   GtkMenuPrivate *priv = menu->priv;
3764   GtkBorder arrow_border;
3765   GtkWidget *widget;
3766   gint offset;
3767   gint view_height;
3768   gboolean double_arrows;
3769
3770   widget = GTK_WIDGET (menu);
3771   offset = priv->scroll_offset + step;
3772
3773   get_arrows_border (menu, &arrow_border);
3774
3775   double_arrows = get_double_arrows (menu);
3776
3777   /* If we scroll upward and the non-visible top part
3778    * is smaller than the scroll arrow it would be
3779    * pretty stupid to show the arrow and taking more
3780    * screen space than just scrolling to the top.
3781    */
3782   if (!double_arrows)
3783     if ((step < 0) && (offset < arrow_border.top))
3784       offset = 0;
3785
3786   /* Don't scroll over the top if we weren't before: */
3787   if ((priv->scroll_offset >= 0) && (offset < 0))
3788     offset = 0;
3789
3790   view_height = gdk_window_get_height (gtk_widget_get_window (widget));
3791
3792   if (priv->scroll_offset == 0 &&
3793       view_height >= priv->requested_height)
3794     return;
3795
3796   /* Don't scroll past the bottom if we weren't before: */
3797   if (priv->scroll_offset > 0)
3798     view_height -= arrow_border.top;
3799
3800   /* When both arrows are always shown,
3801    * reduce view height even more.
3802    */
3803   if (double_arrows)
3804     view_height -= arrow_border.bottom;
3805
3806   if ((priv->scroll_offset + view_height <= priv->requested_height) &&
3807       (offset + view_height > priv->requested_height))
3808     offset = priv->requested_height - view_height;
3809
3810   if (offset != priv->scroll_offset)
3811     gtk_menu_scroll_to (menu, offset);
3812 }
3813
3814 static gboolean
3815 gtk_menu_scroll_timeout (gpointer data)
3816 {
3817   GtkMenu  *menu;
3818
3819   menu = GTK_MENU (data);
3820   gtk_menu_scroll_by (menu, menu->priv->scroll_step);
3821
3822   return TRUE;
3823 }
3824
3825 static gboolean
3826 gtk_menu_scroll (GtkWidget      *widget,
3827                  GdkEventScroll *event)
3828 {
3829   GtkMenu *menu = GTK_MENU (widget);
3830
3831   switch (event->direction)
3832     {
3833     case GDK_SCROLL_RIGHT:
3834     case GDK_SCROLL_DOWN:
3835       gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
3836       break;
3837     case GDK_SCROLL_LEFT:
3838     case GDK_SCROLL_UP:
3839       gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
3840       break;
3841     case GDK_SCROLL_SMOOTH:
3842       gtk_menu_scroll_by (menu, event->delta_y);
3843       break;
3844     }
3845
3846   return TRUE;
3847 }
3848
3849 static void
3850 get_arrows_sensitive_area (GtkMenu      *menu,
3851                            GdkRectangle *upper,
3852                            GdkRectangle *lower)
3853 {
3854   GtkArrowPlacement arrow_placement;
3855   GtkWidget *widget = GTK_WIDGET (menu);
3856   GdkWindow *window;
3857   gint width, height;
3858   guint border;
3859   gint win_x, win_y;
3860   gint scroll_arrow_height;
3861   GtkBorder padding;
3862
3863   window = gtk_widget_get_window (widget);
3864   width = gdk_window_get_width (window);
3865   height = gdk_window_get_height (window);
3866
3867   gtk_widget_style_get (widget,
3868                         "scroll-arrow-vlength", &scroll_arrow_height,
3869                         "arrow-placement", &arrow_placement,
3870                         NULL);
3871
3872   border = gtk_container_get_border_width (GTK_CONTAINER (menu));
3873   get_menu_padding (widget, &padding);
3874
3875   gdk_window_get_position (window, &win_x, &win_y);
3876
3877   switch (arrow_placement)
3878     {
3879     case GTK_ARROWS_BOTH:
3880       if (upper)
3881         {
3882           upper->x = win_x;
3883           upper->y = win_y;
3884           upper->width = width;
3885           upper->height = scroll_arrow_height + border + padding.top;
3886         }
3887
3888       if (lower)
3889         {
3890           lower->x = win_x;
3891           lower->y = win_y + height - border - padding.bottom - scroll_arrow_height;
3892           lower->width = width;
3893           lower->height = scroll_arrow_height + border + padding.bottom;
3894         }
3895       break;
3896
3897     case GTK_ARROWS_START:
3898       if (upper)
3899         {
3900           upper->x = win_x;
3901           upper->y = win_y;
3902           upper->width = width / 2;
3903           upper->height = scroll_arrow_height + border + padding.top;
3904         }
3905
3906       if (lower)
3907         {
3908           lower->x = win_x + width / 2;
3909           lower->y = win_y;
3910           lower->width = width / 2;
3911           lower->height = scroll_arrow_height + border + padding.bottom;
3912         }
3913       break;
3914
3915     case GTK_ARROWS_END:
3916       if (upper)
3917         {
3918           upper->x = win_x;
3919           upper->y = win_y + height - border - scroll_arrow_height;
3920           upper->width = width / 2;
3921           upper->height = scroll_arrow_height + border + padding.top;
3922         }
3923
3924       if (lower)
3925         {
3926           lower->x = win_x + width / 2;
3927           lower->y = win_y + height - border - scroll_arrow_height;
3928           lower->width = width / 2;
3929           lower->height = scroll_arrow_height + border + padding.bottom;
3930         }
3931       break;
3932     }
3933 }
3934
3935
3936 static void
3937 gtk_menu_handle_scrolling (GtkMenu *menu,
3938                            gint     x,
3939                            gint     y,
3940                            gboolean enter,
3941                            gboolean motion)
3942 {
3943   GtkMenuPrivate *priv = menu->priv;
3944   GtkMenuShell *menu_shell;
3945   GdkRectangle rect;
3946   gboolean in_arrow;
3947   gboolean scroll_fast = FALSE;
3948   gint top_x, top_y;
3949
3950   menu_shell = GTK_MENU_SHELL (menu);
3951
3952   gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
3953                            &top_x, &top_y);
3954   x -= top_x;
3955   y -= top_y;
3956
3957   /*  upper arrow handling  */
3958
3959   get_arrows_sensitive_area (menu, &rect, NULL);
3960
3961   in_arrow = FALSE;
3962   if (priv->upper_arrow_visible && !priv->tearoff_active &&
3963       (x >= rect.x) && (x < rect.x + rect.width) &&
3964       (y >= rect.y) && (y < rect.y + rect.height))
3965     {
3966       in_arrow = TRUE;
3967     }
3968
3969   if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
3970     {
3971       gboolean arrow_pressed = FALSE;
3972
3973       if (priv->upper_arrow_visible && !priv->tearoff_active)
3974         {
3975           scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
3976
3977           if (enter && in_arrow &&
3978               (!priv->upper_arrow_prelight ||
3979                priv->scroll_fast != scroll_fast))
3980             {
3981               priv->upper_arrow_prelight = TRUE;
3982               priv->scroll_fast = scroll_fast;
3983
3984               /* Deselect the active item so that
3985                * any submenus are popped down
3986                */
3987               gtk_menu_shell_deselect (menu_shell);
3988
3989               gtk_menu_remove_scroll_timeout (menu);
3990               priv->scroll_step = scroll_fast
3991                                     ? -MENU_SCROLL_STEP2
3992                                     : -MENU_SCROLL_STEP1;
3993
3994               priv->scroll_timeout =
3995                 gdk_threads_add_timeout (scroll_fast
3996                                            ? MENU_SCROLL_TIMEOUT2
3997                                            : MENU_SCROLL_TIMEOUT1,
3998                                          gtk_menu_scroll_timeout, menu);
3999             }
4000           else if (!enter && !in_arrow && priv->upper_arrow_prelight)
4001             {
4002               gtk_menu_stop_scrolling (menu);
4003             }
4004         }
4005
4006       /*  check if the button isn't insensitive before
4007        *  changing it to something else.
4008        */
4009       if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4010         {
4011           GtkStateFlags arrow_state = 0;
4012
4013           if (arrow_pressed)
4014             arrow_state |= GTK_STATE_FLAG_ACTIVE;
4015
4016           if (priv->upper_arrow_prelight)
4017             arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4018
4019           if (arrow_state != priv->upper_arrow_state)
4020             {
4021               priv->upper_arrow_state = arrow_state;
4022
4023               gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)),
4024                                           &rect, FALSE);
4025             }
4026         }
4027     }
4028
4029   /*  lower arrow handling  */
4030
4031   get_arrows_sensitive_area (menu, NULL, &rect);
4032
4033   in_arrow = FALSE;
4034   if (priv->lower_arrow_visible && !priv->tearoff_active &&
4035       (x >= rect.x) && (x < rect.x + rect.width) &&
4036       (y >= rect.y) && (y < rect.y + rect.height))
4037     {
4038       in_arrow = TRUE;
4039     }
4040
4041   if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4042     {
4043       gboolean arrow_pressed = FALSE;
4044
4045       if (priv->lower_arrow_visible && !priv->tearoff_active)
4046         {
4047           scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
4048
4049           if (enter && in_arrow &&
4050               (!priv->lower_arrow_prelight ||
4051                priv->scroll_fast != scroll_fast))
4052             {
4053               priv->lower_arrow_prelight = TRUE;
4054               priv->scroll_fast = scroll_fast;
4055
4056               /* Deselect the active item so that
4057                * any submenus are popped down
4058                */
4059               gtk_menu_shell_deselect (menu_shell);
4060
4061               gtk_menu_remove_scroll_timeout (menu);
4062               priv->scroll_step = scroll_fast
4063                                     ? MENU_SCROLL_STEP2
4064                                     : MENU_SCROLL_STEP1;
4065
4066               priv->scroll_timeout =
4067                 gdk_threads_add_timeout (scroll_fast
4068                                            ? MENU_SCROLL_TIMEOUT2
4069                                            : MENU_SCROLL_TIMEOUT1,
4070                                          gtk_menu_scroll_timeout, menu);
4071             }
4072           else if (!enter && !in_arrow && priv->lower_arrow_prelight)
4073             {
4074               gtk_menu_stop_scrolling (menu);
4075             }
4076         }
4077
4078       /*  check if the button isn't insensitive before
4079        *  changing it to something else.
4080        */
4081       if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4082         {
4083           GtkStateFlags arrow_state = 0;
4084
4085           if (arrow_pressed)
4086             arrow_state |= GTK_STATE_FLAG_ACTIVE;
4087
4088           if (priv->lower_arrow_prelight)
4089             arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4090
4091           if (arrow_state != priv->lower_arrow_state)
4092             {
4093               priv->lower_arrow_state = arrow_state;
4094
4095               gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)),
4096                                           &rect, FALSE);
4097             }
4098         }
4099     }
4100 }
4101
4102 static gboolean
4103 gtk_menu_enter_notify (GtkWidget        *widget,
4104                        GdkEventCrossing *event)
4105 {
4106   GtkWidget *menu_item;
4107   GtkWidget *parent;
4108   GdkDevice *source_device;
4109
4110   if (event->mode == GDK_CROSSING_GTK_GRAB ||
4111       event->mode == GDK_CROSSING_GTK_UNGRAB ||
4112       event->mode == GDK_CROSSING_STATE_CHANGED)
4113     return TRUE;
4114
4115   source_device = gdk_event_get_source_device ((GdkEvent *) event);
4116   menu_item = gtk_get_event_widget ((GdkEvent*) event);
4117
4118   if (GTK_IS_MENU (widget) &&
4119       gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
4120     {
4121       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
4122
4123       if (!menu_shell->priv->ignore_enter)
4124         gtk_menu_handle_scrolling (GTK_MENU (widget),
4125                                    event->x_root, event->y_root, TRUE, TRUE);
4126     }
4127
4128   if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN &&
4129       GTK_IS_MENU_ITEM (menu_item))
4130     {
4131       GtkWidget *menu = gtk_widget_get_parent (menu_item);
4132
4133       if (GTK_IS_MENU (menu))
4134         {
4135           GtkMenuPrivate *priv = (GTK_MENU (menu))->priv;
4136           GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
4137
4138           if (priv->seen_item_enter)
4139             {
4140               /* This is the second enter we see for an item
4141                * on this menu. This means a release should always
4142                * mean activate.
4143                */
4144               menu_shell->priv->activate_time = 0;
4145             }
4146           else if ((event->detail != GDK_NOTIFY_NONLINEAR &&
4147                     event->detail != GDK_NOTIFY_NONLINEAR_VIRTUAL))
4148             {
4149               if (definitely_within_item (menu_item, event->x, event->y))
4150                 {
4151                   /* This is an actual user-enter (ie. not a pop-under)
4152                    * In this case, the user must either have entered
4153                    * sufficiently far enough into the item, or he must move
4154                    * far enough away from the enter point. (see
4155                    * gtk_menu_motion_notify())
4156                    */
4157                   menu_shell->priv->activate_time = 0;
4158                 }
4159             }
4160
4161           priv->seen_item_enter = TRUE;
4162         }
4163     }
4164
4165   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
4166    * will not correspond to the event widget's parent.  Check to see
4167    * if we are in the parent's navigation region.
4168    */
4169   parent = gtk_widget_get_parent (menu_item);
4170   if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (parent) &&
4171       gtk_menu_navigating_submenu (GTK_MENU (parent),
4172                                    event->x_root, event->y_root))
4173     return TRUE;
4174
4175   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (widget, event);
4176 }
4177
4178 static gboolean
4179 gtk_menu_leave_notify (GtkWidget        *widget,
4180                        GdkEventCrossing *event)
4181 {
4182   GtkMenuShell *menu_shell;
4183   GtkMenu *menu;
4184   GtkMenuItem *menu_item;
4185   GtkWidget *event_widget;
4186   GdkDevice *source_device;
4187
4188   if (event->mode == GDK_CROSSING_GTK_GRAB ||
4189       event->mode == GDK_CROSSING_GTK_UNGRAB ||
4190       event->mode == GDK_CROSSING_STATE_CHANGED)
4191     return TRUE;
4192
4193   menu = GTK_MENU (widget);
4194   menu_shell = GTK_MENU_SHELL (widget);
4195
4196   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
4197     return TRUE;
4198
4199   source_device = gdk_event_get_source_device ((GdkEvent *) event);
4200
4201   if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
4202     gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
4203
4204   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4205
4206   if (!GTK_IS_MENU_ITEM (event_widget))
4207     return TRUE;
4208
4209   menu_item = GTK_MENU_ITEM (event_widget);
4210
4211   /* Here we check to see if we're leaving an active menu item
4212    * with a submenu, in which case we enter submenu navigation mode.
4213    */
4214   if (menu_shell->priv->active_menu_item != NULL
4215       && menu_item->priv->submenu != NULL
4216       && menu_item->priv->submenu_placement == GTK_LEFT_RIGHT)
4217     {
4218       if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active)
4219         {
4220           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
4221           return TRUE;
4222         }
4223       else if (menu_item == GTK_MENU_ITEM (menu_shell->priv->active_menu_item))
4224         {
4225           /* We are leaving an active menu item with nonactive submenu.
4226            * Deselect it so we don't surprise the user with by popping
4227            * up a submenu _after_ he left the item.
4228            */
4229           gtk_menu_shell_deselect (menu_shell);
4230           return TRUE;
4231         }
4232     }
4233
4234   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
4235 }
4236
4237 static gboolean
4238 pointer_on_menu_widget (GtkMenu *menu,
4239                         gdouble  x_root,
4240                         gdouble  y_root)
4241 {
4242   GtkMenuPrivate *priv = menu->priv;
4243   GtkAllocation allocation;
4244   gint window_x, window_y;
4245
4246   gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
4247   gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
4248                            &window_x, &window_y);
4249
4250   if (x_root >= window_x && x_root < window_x + allocation.width &&
4251       y_root >= window_y && y_root < window_y + allocation.height)
4252     return TRUE;
4253
4254   return FALSE;
4255 }
4256
4257 static gboolean
4258 gtk_menu_captured_event (GtkWidget *widget,
4259                          GdkEvent  *event)
4260 {
4261   GdkDevice *source_device;
4262   gboolean retval = FALSE;
4263   GtkMenuPrivate *priv;
4264   GtkMenu *menu;
4265   gdouble x_root, y_root;
4266   guint button;
4267   GdkModifierType state;
4268
4269   menu = GTK_MENU (widget);
4270   priv = menu->priv;
4271
4272   if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
4273     return retval;
4274
4275   source_device = gdk_event_get_source_device (event);
4276   gdk_event_get_root_coords (event, &x_root, &y_root);
4277
4278   switch (event->type)
4279     {
4280     case GDK_TOUCH_BEGIN:
4281     case GDK_BUTTON_PRESS:
4282       if ((!gdk_event_get_button (event, &button) || button == 1) &&
4283           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
4284           pointer_on_menu_widget (menu, x_root, y_root))
4285         {
4286           priv->drag_start_y = event->button.y_root;
4287           priv->initial_drag_offset = priv->scroll_offset;
4288           priv->drag_scroll_started = FALSE;
4289         }
4290       else
4291         priv->drag_start_y = -1;
4292
4293       priv->drag_already_pressed = TRUE;
4294       break;
4295     case GDK_TOUCH_END:
4296     case GDK_BUTTON_RELEASE:
4297       if (priv->drag_scroll_started)
4298         {
4299           priv->drag_scroll_started = FALSE;
4300           priv->drag_start_y = -1;
4301           priv->drag_already_pressed = FALSE;
4302           retval = TRUE;
4303         }
4304       break;
4305     case GDK_TOUCH_UPDATE:
4306     case GDK_MOTION_NOTIFY:
4307       if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) != 0) &&
4308           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
4309         {
4310           if (!priv->drag_already_pressed)
4311             {
4312               if (pointer_on_menu_widget (menu, x_root, y_root))
4313                 {
4314                   priv->drag_start_y = y_root;
4315                   priv->initial_drag_offset = priv->scroll_offset;
4316                   priv->drag_scroll_started = FALSE;
4317                 }
4318               else
4319                 priv->drag_start_y = -1;
4320
4321               priv->drag_already_pressed = TRUE;
4322             }
4323
4324           if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
4325             break;
4326
4327           if (priv->drag_scroll_started)
4328             {
4329               gint offset, view_height;
4330               GtkBorder arrow_border;
4331               gdouble y_diff;
4332
4333               y_diff = y_root - priv->drag_start_y;
4334               offset = priv->initial_drag_offset - y_diff;
4335
4336               view_height = gdk_window_get_height (gtk_widget_get_window (widget));
4337               get_arrows_border (menu, &arrow_border);
4338
4339               if (priv->upper_arrow_visible)
4340                 view_height -= arrow_border.top;
4341
4342               if (priv->lower_arrow_visible)
4343                 view_height -= arrow_border.bottom;
4344
4345               offset = CLAMP (offset,
4346                               MIN (priv->scroll_offset, 0),
4347                               MAX (priv->scroll_offset, priv->requested_height - view_height));
4348
4349               gtk_menu_scroll_to (menu, offset);
4350
4351               retval = TRUE;
4352             }
4353           else if (gtk_drag_check_threshold (widget,
4354                                              0, priv->drag_start_y,
4355                                              0, y_root))
4356             {
4357               priv->drag_scroll_started = TRUE;
4358               gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
4359               retval = TRUE;
4360             }
4361         }
4362       break;
4363     case GDK_ENTER_NOTIFY:
4364     case GDK_LEAVE_NOTIFY:
4365       if (priv->drag_scroll_started)
4366         retval = TRUE;
4367       break;
4368     default:
4369       break;
4370     }
4371
4372   return retval;
4373 }
4374
4375 static void
4376 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
4377 {
4378   GtkMenuPrivate *priv = menu->priv;
4379
4380   priv->navigation_x = 0;
4381   priv->navigation_y = 0;
4382   priv->navigation_width = 0;
4383   priv->navigation_height = 0;
4384
4385   if (priv->navigation_timeout)
4386     {
4387       g_source_remove (priv->navigation_timeout);
4388       priv->navigation_timeout = 0;
4389     }
4390 }
4391
4392 /* When the timeout is elapsed, the navigation region is destroyed
4393  * and the menuitem under the pointer (if any) is selected.
4394  */
4395 static gboolean
4396 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
4397 {
4398   GtkMenuPopdownData *popdown_data = user_data;
4399   GtkMenu *menu = popdown_data->menu;
4400   GtkMenuPrivate *priv = menu->priv;
4401   GdkWindow *child_window;
4402
4403   gtk_menu_stop_navigating_submenu (menu);
4404
4405   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
4406     {
4407       child_window = gdk_window_get_device_position (priv->bin_window,
4408                                                      popdown_data->device,
4409                                                      NULL, NULL, NULL);
4410
4411       if (child_window)
4412         {
4413           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
4414
4415           send_event->crossing.window = g_object_ref (child_window);
4416           send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
4417           send_event->crossing.send_event = TRUE;
4418           gdk_event_set_device (send_event, popdown_data->device);
4419
4420           GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
4421
4422           gdk_event_free (send_event);
4423         }
4424     }
4425
4426   return FALSE;
4427 }
4428
4429 static gboolean
4430 gtk_menu_navigating_submenu (GtkMenu *menu,
4431                              gint     event_x,
4432                              gint     event_y)
4433 {
4434   GtkMenuPrivate *priv = menu->priv;
4435   gint width, height;
4436
4437   if (!gtk_menu_has_navigation_triangle (menu))
4438     return FALSE;
4439
4440   width = priv->navigation_width;
4441   height = priv->navigation_height;
4442
4443   /* Check if x/y are in the triangle spanned by the navigation parameters */
4444
4445   /* 1) Move the coordinates so the triangle starts at 0,0 */
4446   event_x -= priv->navigation_x;
4447   event_y -= priv->navigation_y;
4448
4449   /* 2) Ensure both legs move along the positive axis */
4450   if (width < 0)
4451     {
4452       event_x = -event_x;
4453       width = -width;
4454     }
4455   if (height < 0)
4456     {
4457       event_y = -event_y;
4458       height = -height;
4459     }
4460
4461   /* 3) Check that the given coordinate is inside the triangle. The formula
4462    * is a transformed form of this formula: x/w + y/h <= 1
4463    */
4464   if (event_x >= 0 && event_y >= 0 &&
4465       event_x * height + event_y * width <= width * height)
4466     {
4467       return TRUE;
4468     }
4469   else
4470     {
4471       gtk_menu_stop_navigating_submenu (menu);
4472       return FALSE;
4473     }
4474 }
4475
4476 static void
4477 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
4478                                         GtkMenuItem      *menu_item,
4479                                         GdkEventCrossing *event)
4480 {
4481   GtkMenuPrivate *priv = menu->priv;
4482   gint submenu_left = 0;
4483   gint submenu_right = 0;
4484   gint submenu_top = 0;
4485   gint submenu_bottom = 0;
4486   gint width = 0;
4487   GtkWidget *event_widget;
4488   GtkMenuPopdownData *popdown_data;
4489   GdkWindow *window;
4490
4491   g_return_if_fail (menu_item->priv->submenu != NULL);
4492   g_return_if_fail (event != NULL);
4493
4494   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4495
4496   window = gtk_widget_get_window (menu_item->priv->submenu);
4497   gdk_window_get_origin (window, &submenu_left, &submenu_top);
4498
4499   submenu_right = submenu_left + gdk_window_get_width (window);
4500   submenu_bottom = submenu_top + gdk_window_get_height (window);
4501
4502   width = gdk_window_get_width (gtk_widget_get_window (event_widget));
4503
4504   if (event->x >= 0 && event->x < width)
4505     {
4506       gint popdown_delay;
4507
4508       gtk_menu_stop_navigating_submenu (menu);
4509
4510       /* The navigation region is the triangle closest to the x/y
4511        * location of the rectangle. This is why the width or height
4512        * can be negative.
4513        */
4514       if (menu_item->priv->submenu_direction == GTK_DIRECTION_RIGHT)
4515         {
4516           /* right */
4517           priv->navigation_x = submenu_left;
4518           priv->navigation_width = event->x_root - submenu_left;
4519         }
4520       else
4521         {
4522           /* left */
4523           priv->navigation_x = submenu_right;
4524           priv->navigation_width = event->x_root - submenu_right;
4525         }
4526
4527       if (event->y < 0)
4528         {
4529           /* top */
4530           priv->navigation_y = event->y_root;
4531           priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT;
4532
4533           if (priv->navigation_height >= 0)
4534             return;
4535         }
4536       else
4537         {
4538           /* bottom */
4539           priv->navigation_y = event->y_root;
4540           priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT;
4541
4542           if (priv->navigation_height <= 0)
4543             return;
4544         }
4545
4546       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
4547                     "gtk-menu-popdown-delay", &popdown_delay,
4548                     NULL);
4549
4550       popdown_data = g_new (GtkMenuPopdownData, 1);
4551       popdown_data->menu = menu;
4552       popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
4553
4554       priv->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
4555                                                                popdown_delay,
4556                                                                gtk_menu_stop_navigating_submenu_cb,
4557                                                                popdown_data,
4558                                                                (GDestroyNotify) g_free);
4559     }
4560 }
4561
4562 static void
4563 gtk_menu_deactivate (GtkMenuShell *menu_shell)
4564 {
4565   GtkWidget *parent;
4566
4567   g_return_if_fail (GTK_IS_MENU (menu_shell));
4568
4569   parent = menu_shell->priv->parent_menu_shell;
4570
4571   menu_shell->priv->activate_time = 0;
4572   gtk_menu_popdown (GTK_MENU (menu_shell));
4573
4574   if (parent)
4575     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
4576 }
4577
4578 static void
4579 gtk_menu_position (GtkMenu  *menu,
4580                    gboolean  set_scroll_offset)
4581 {
4582   GtkMenuPrivate *priv = menu->priv;
4583   GtkWidget *widget;
4584   GtkRequisition requisition;
4585   gint x, y;
4586   gint scroll_offset;
4587   GdkScreen *screen;
4588   GdkScreen *pointer_screen;
4589   GdkRectangle monitor;
4590   GdkDevice *pointer;
4591
4592   widget = GTK_WIDGET (menu);
4593
4594   screen = gtk_widget_get_screen (widget);
4595   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (menu));
4596   gdk_device_get_position (pointer, &pointer_screen, &x, &y);
4597
4598   /* Realize so we have the proper width and heigh to figure out
4599    * the right place to popup the menu.
4600    */
4601   gtk_widget_realize (priv->toplevel);
4602   requisition.width = gtk_widget_get_allocated_width (widget);
4603   requisition.height = gtk_widget_get_allocated_height (widget);
4604
4605   if (pointer_screen != screen)
4606     {
4607       /* Pointer is on a different screen; roughly center the
4608        * menu on the screen. If someone was using multiscreen
4609        * + Xinerama together they'd probably want something
4610        * fancier; but that is likely to be vanishingly rare.
4611        */
4612       x = MAX (0, (gdk_screen_get_width (screen) - requisition.width) / 2);
4613       y = MAX (0, (gdk_screen_get_height (screen) - requisition.height) / 2);
4614     }
4615
4616   priv->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4617   priv->initially_pushed_in = FALSE;
4618
4619   /* Set the type hint here to allow custom position functions
4620    * to set a different hint
4621    */
4622   if (!gtk_widget_get_visible (priv->toplevel))
4623     gtk_window_set_type_hint (GTK_WINDOW (priv->toplevel), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
4624
4625   if (priv->position_func)
4626     {
4627       (* priv->position_func) (menu, &x, &y, &priv->initially_pushed_in,
4628                                priv->position_func_data);
4629
4630       if (priv->monitor_num < 0)
4631         priv->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4632
4633       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
4634     }
4635   else
4636     {
4637       gint space_left, space_right, space_above, space_below;
4638       gint needed_width;
4639       gint needed_height;
4640       GtkBorder padding;
4641       gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4642
4643       get_menu_padding (widget, &padding);
4644
4645       /* The placement of popup menus horizontally works like this (with
4646        * RTL in parentheses)
4647        *
4648        * - If there is enough room to the right (left) of the mouse cursor,
4649        *   position the menu there.
4650        *
4651        * - Otherwise, if if there is enough room to the left (right) of the
4652        *   mouse cursor, position the menu there.
4653        *
4654        * - Otherwise if the menu is smaller than the monitor, position it
4655        *   on the side of the mouse cursor that has the most space available
4656        *
4657        * - Otherwise (if there is simply not enough room for the menu on the
4658        *   monitor), position it as far left (right) as possible.
4659        *
4660        * Positioning in the vertical direction is similar: first try below
4661        * mouse cursor, then above.
4662        */
4663       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
4664
4665       space_left = x - monitor.x;
4666       space_right = monitor.x + monitor.width - x - 1;
4667       space_above = y - monitor.y;
4668       space_below = monitor.y + monitor.height - y - 1;
4669
4670       /* Position horizontally. */
4671
4672       /* the amount of space we need to position the menu.
4673        * Note the menu is offset "thickness" pixels
4674        */
4675       needed_width = requisition.width - padding.left;
4676
4677       if (needed_width <= space_left ||
4678           needed_width <= space_right)
4679         {
4680           if ((rtl  && needed_width <= space_left) ||
4681               (!rtl && needed_width >  space_right))
4682             {
4683               /* position left */
4684               x = x + padding.left - requisition.width + 1;
4685             }
4686           else
4687             {
4688               /* position right */
4689               x = x - padding.right;
4690             }
4691
4692           /* x is clamped on-screen further down */
4693         }
4694       else if (requisition.width <= monitor.width)
4695         {
4696           /* the menu is too big to fit on either side of the mouse
4697            * cursor, but smaller than the monitor. Position it on
4698            * the side that has the most space
4699            */
4700           if (space_left > space_right)
4701             {
4702               /* left justify */
4703               x = monitor.x;
4704             }
4705           else
4706             {
4707               /* right justify */
4708               x = monitor.x + monitor.width - requisition.width;
4709             }
4710         }
4711       else /* menu is simply too big for the monitor */
4712         {
4713           if (rtl)
4714             {
4715               /* right justify */
4716               x = monitor.x + monitor.width - requisition.width;
4717             }
4718           else
4719             {
4720               /* left justify */
4721               x = monitor.x;
4722             }
4723         }
4724
4725       /* Position vertically.
4726        * The algorithm is the same as above, but simpler
4727        * because we don't have to take RTL into account.
4728        */
4729       needed_height = requisition.height - padding.top;
4730
4731       if (needed_height <= space_above ||
4732           needed_height <= space_below)
4733         {
4734           if (needed_height <= space_below)
4735             y = y - padding.top;
4736           else
4737             y = y + padding.bottom - requisition.height + 1;
4738
4739           y = CLAMP (y, monitor.y,
4740                      monitor.y + monitor.height - requisition.height);
4741         }
4742       else if (needed_height > space_below && needed_height > space_above)
4743         {
4744           if (space_below >= space_above)
4745             y = monitor.y + monitor.height - requisition.height;
4746           else
4747             y = monitor.y;
4748         }
4749       else
4750         {
4751           y = monitor.y;
4752         }
4753     }
4754
4755   scroll_offset = 0;
4756
4757   if (y + requisition.height > monitor.y + monitor.height)
4758     {
4759       if (priv->initially_pushed_in)
4760         scroll_offset += (monitor.y + monitor.height) - requisition.height - y;
4761       y = (monitor.y + monitor.height) - requisition.height;
4762     }
4763
4764   if (y < monitor.y)
4765     {
4766       if (priv->initially_pushed_in)
4767         scroll_offset += monitor.y - y;
4768       y = monitor.y;
4769     }
4770
4771   x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
4772
4773   if (GTK_MENU_SHELL (menu)->priv->active)
4774     {
4775       priv->have_position = TRUE;
4776       priv->position_x = x;
4777       priv->position_y = y;
4778     }
4779
4780   if (scroll_offset != 0)
4781     {
4782       GtkBorder arrow_border;
4783
4784       get_arrows_border (menu, &arrow_border);
4785       scroll_offset += arrow_border.top;
4786     }
4787
4788   gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->priv->active ? priv->toplevel : priv->tearoff_window),
4789                    x, y);
4790
4791   if (!GTK_MENU_SHELL (menu)->priv->active)
4792     {
4793       gtk_window_resize (GTK_WINDOW (priv->tearoff_window),
4794                          requisition.width, requisition.height);
4795     }
4796
4797   if (set_scroll_offset)
4798     priv->scroll_offset = scroll_offset;
4799 }
4800
4801 static void
4802 gtk_menu_remove_scroll_timeout (GtkMenu *menu)
4803 {
4804   GtkMenuPrivate *priv = menu->priv;
4805
4806   if (priv->scroll_timeout)
4807     {
4808       g_source_remove (priv->scroll_timeout);
4809       priv->scroll_timeout = 0;
4810     }
4811 }
4812
4813 static void
4814 gtk_menu_stop_scrolling (GtkMenu *menu)
4815 {
4816   GtkMenuPrivate *priv = menu->priv;
4817
4818   gtk_menu_remove_scroll_timeout (menu);
4819   priv->upper_arrow_prelight = FALSE;
4820   priv->lower_arrow_prelight = FALSE;
4821 }
4822
4823 static void
4824 gtk_menu_scroll_to (GtkMenu *menu,
4825                     gint    offset)
4826 {
4827   GtkMenuPrivate *priv = menu->priv;
4828   GtkBorder arrow_border, padding;
4829   GtkWidget *widget;
4830   gint x, y;
4831   gint view_width, view_height;
4832   gint border_width;
4833   gint menu_height;
4834   gboolean double_arrows;
4835
4836   widget = GTK_WIDGET (menu);
4837
4838   if (priv->tearoff_active && priv->tearoff_adjustment)
4839     gtk_adjustment_set_value (priv->tearoff_adjustment, offset);
4840
4841   /* Move/resize the viewport according to arrows: */
4842   view_width = gtk_widget_get_allocated_width (widget);
4843   view_height = gtk_widget_get_allocated_height (widget);
4844
4845   get_menu_padding (widget, &padding);
4846   double_arrows = get_double_arrows (menu);
4847
4848   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
4849
4850   view_width -= (2 * border_width) + padding.left + padding.right;
4851   view_height -= (2 * border_width) + padding.top + padding.bottom;
4852   menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom;
4853
4854   x = border_width + padding.left;
4855   y = border_width + padding.top;
4856
4857   if (double_arrows && !priv->tearoff_active)
4858     {
4859       if (view_height < menu_height               ||
4860           (offset > 0 && priv->scroll_offset > 0) ||
4861           (offset < 0 && priv->scroll_offset < 0))
4862         {
4863           GtkStateFlags upper_arrow_previous_state = priv->upper_arrow_state;
4864           GtkStateFlags lower_arrow_previous_state = priv->lower_arrow_state;
4865
4866           if (!priv->upper_arrow_visible || !priv->lower_arrow_visible)
4867             gtk_widget_queue_draw (GTK_WIDGET (menu));
4868
4869           priv->upper_arrow_visible = priv->lower_arrow_visible = TRUE;
4870
4871           get_arrows_border (menu, &arrow_border);
4872           y += arrow_border.top;
4873           view_height -= arrow_border.top;
4874           view_height -= arrow_border.bottom;
4875
4876           if (offset <= 0)
4877             priv->upper_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
4878           else
4879             {
4880               priv->upper_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
4881
4882               if (priv->upper_arrow_prelight)
4883                 priv->upper_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4884               else
4885                 priv->upper_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
4886             }
4887
4888           if (offset >= menu_height - view_height)
4889             priv->lower_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
4890           else
4891             {
4892               priv->lower_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
4893
4894               if (priv->lower_arrow_prelight)
4895                 priv->lower_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4896               else
4897                 priv->lower_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
4898             }
4899
4900           if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
4901               (priv->lower_arrow_state != lower_arrow_previous_state))
4902             gtk_widget_queue_draw (GTK_WIDGET (menu));
4903
4904           if ((upper_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
4905               (priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
4906             {
4907               /* At the upper border, possibly remove timeout */
4908               if (priv->scroll_step < 0)
4909                 {
4910                   gtk_menu_stop_scrolling (menu);
4911                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4912                 }
4913             }
4914
4915           if ((lower_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
4916               (priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
4917             {
4918               /* At the lower border, possibly remove timeout */
4919               if (priv->scroll_step > 0)
4920                 {
4921                   gtk_menu_stop_scrolling (menu);
4922                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4923                 }
4924             }
4925         }
4926       else if (priv->upper_arrow_visible || priv->lower_arrow_visible)
4927         {
4928           offset = 0;
4929
4930           priv->upper_arrow_visible = priv->lower_arrow_visible = FALSE;
4931           priv->upper_arrow_prelight = priv->lower_arrow_prelight = FALSE;
4932
4933           gtk_menu_stop_scrolling (menu);
4934           gtk_widget_queue_draw (GTK_WIDGET (menu));
4935         }
4936     }
4937   else if (!priv->tearoff_active)
4938     {
4939       gboolean last_visible;
4940
4941       last_visible = priv->upper_arrow_visible;
4942       priv->upper_arrow_visible = offset > 0;
4943
4944       /* upper_arrow_visible may have changed, so requery the border */
4945       get_arrows_border (menu, &arrow_border);
4946       view_height -= arrow_border.top;
4947
4948       if ((last_visible != priv->upper_arrow_visible) &&
4949           !priv->upper_arrow_visible)
4950         {
4951           priv->upper_arrow_prelight = FALSE;
4952
4953           /* If we hide the upper arrow, possibly remove timeout */
4954           if (priv->scroll_step < 0)
4955             {
4956               gtk_menu_stop_scrolling (menu);
4957               gtk_widget_queue_draw (GTK_WIDGET (menu));
4958             }
4959         }
4960
4961       last_visible = priv->lower_arrow_visible;
4962       priv->lower_arrow_visible = offset < menu_height - view_height;
4963
4964       /* lower_arrow_visible may have changed, so requery the border */
4965       get_arrows_border (menu, &arrow_border);
4966       view_height -= arrow_border.bottom;
4967
4968       if ((last_visible != priv->lower_arrow_visible) &&
4969            !priv->lower_arrow_visible)
4970         {
4971           priv->lower_arrow_prelight = FALSE;
4972
4973           /* If we hide the lower arrow, possibly remove timeout */
4974           if (priv->scroll_step > 0)
4975             {
4976               gtk_menu_stop_scrolling (menu);
4977               gtk_widget_queue_draw (GTK_WIDGET (menu));
4978             }
4979         }
4980
4981       y += arrow_border.top;
4982     }
4983
4984   /* Scroll the menu: */
4985   if (gtk_widget_get_realized (widget))
4986     {
4987       gdk_window_move (priv->bin_window, 0, -offset);
4988       gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
4989     }
4990
4991   priv->scroll_offset = offset;
4992 }
4993
4994 static gboolean
4995 compute_child_offset (GtkMenu   *menu,
4996                       GtkWidget *menu_item,
4997                       gint      *offset,
4998                       gint      *height,
4999                       gboolean  *is_last_child)
5000 {
5001   GtkMenuPrivate *priv = menu->priv;
5002   gint item_top_attach;
5003   gint item_bottom_attach;
5004   gint child_offset = 0;
5005   gint i;
5006
5007   get_effective_child_attach (menu_item, NULL, NULL,
5008                               &item_top_attach, &item_bottom_attach);
5009
5010   /* there is a possibility that we get called before _size_request,
5011    * so check the height table for safety.
5012    */
5013   if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
5014     return FALSE;
5015
5016   /* when we have a row with only invisible children, its height will
5017    * be zero, so there's no need to check WIDGET_VISIBLE here
5018    */
5019   for (i = 0; i < item_top_attach; i++)
5020     child_offset += priv->heights[i];
5021
5022   if (is_last_child)
5023     *is_last_child = (item_bottom_attach == gtk_menu_get_n_rows (menu));
5024   if (offset)
5025     *offset = child_offset;
5026   if (height)
5027     *height = priv->heights[item_top_attach];
5028
5029   return TRUE;
5030 }
5031
5032 static void
5033 gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
5034                               GtkWidget    *menu_item)
5035 {
5036   GtkMenu *menu = GTK_MENU (menu_shell);
5037   GtkMenuPrivate *priv = menu->priv;
5038   GtkWidget *widget = GTK_WIDGET (menu_shell);
5039   gint child_offset, child_height;
5040   gint height;
5041   gint y;
5042   gint arrow_height;
5043   gboolean last_child = 0;
5044
5045   /* We need to check if the selected item fully visible.
5046    * If not we need to scroll the menu so that it becomes fully
5047    * visible.
5048    */
5049   if (compute_child_offset (menu, menu_item,
5050                             &child_offset, &child_height, &last_child))
5051     {
5052       gboolean double_arrows;
5053       GtkBorder padding;
5054
5055       y = priv->scroll_offset;
5056       height = gdk_window_get_height (gtk_widget_get_window (widget));
5057
5058       double_arrows = get_double_arrows (menu);
5059       get_menu_padding (widget, &padding);
5060
5061       height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) +
5062         padding.top + padding.bottom;
5063
5064       if (child_offset < y)
5065         {
5066           /* Ignore the enter event we might get if the pointer
5067            * is on the menu
5068            */
5069           menu_shell->priv->ignore_enter = TRUE;
5070           gtk_menu_scroll_to (menu, child_offset);
5071         }
5072       else
5073         {
5074           GtkBorder arrow_border;
5075
5076           arrow_height = 0;
5077
5078           get_arrows_border (menu, &arrow_border);
5079           if (!priv->tearoff_active)
5080             arrow_height = arrow_border.top + arrow_border.bottom;
5081
5082           if (child_offset + child_height > y + height - arrow_height)
5083             {
5084               arrow_height = 0;
5085               if ((!last_child && !priv->tearoff_active) || double_arrows)
5086                 arrow_height += arrow_border.bottom;
5087
5088               y = child_offset + child_height - height + arrow_height;
5089               if (((y > 0) && !priv->tearoff_active) || double_arrows)
5090                 {
5091                   /* Need upper arrow */
5092                   arrow_height += arrow_border.top;
5093                   y = child_offset + child_height - height + arrow_height;
5094                 }
5095               /* Ignore the enter event we might get if the pointer
5096                * is on the menu
5097                */
5098               menu_shell->priv->ignore_enter = TRUE;
5099               gtk_menu_scroll_to (menu, y);
5100             }
5101         }
5102     }
5103 }
5104
5105 static void
5106 gtk_menu_select_item (GtkMenuShell *menu_shell,
5107                       GtkWidget    *menu_item)
5108 {
5109   GtkMenu *menu = GTK_MENU (menu_shell);
5110
5111   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
5112     gtk_menu_scroll_item_visible (menu_shell, menu_item);
5113
5114   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->select_item (menu_shell, menu_item);
5115 }
5116
5117
5118 /* Reparent the menu, taking care of the refcounting
5119  *
5120  * If unrealize is true we force a unrealize while reparenting the parent.
5121  * This can help eliminate flicker in some cases.
5122  *
5123  * What happens is that when the menu is unrealized and then re-realized,
5124  * the allocations are as follows:
5125  *
5126  *  parent - 1x1 at (0,0)
5127  *  child1 - 100x20 at (0,0)
5128  *  child2 - 100x20 at (0,20)
5129  *  child3 - 100x20 at (0,40)
5130  *
5131  * That is, the parent is small but the children are full sized. Then,
5132  * when the queued_resize gets processed, the parent gets resized to
5133  * full size.
5134  *
5135  * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
5136  * contains the following logic:
5137  *
5138  * - if a move or resize operation on a window would change the clip
5139  *   region on the children, then before the window is resized
5140  *   the background for children is temporarily set to None, the
5141  *   move/resize done, and the background for the children restored.
5142  *
5143  * So, at the point where the parent is resized to final size, the
5144  * background for the children is temporarily None, and thus they
5145  * are not cleared to the background color and the previous background
5146  * (the image of the menu) is left in place.
5147  */
5148 static void
5149 gtk_menu_reparent (GtkMenu   *menu,
5150                    GtkWidget *new_parent,
5151                    gboolean   unrealize)
5152 {
5153   GObject *object = G_OBJECT (menu);
5154   GtkWidget *widget = GTK_WIDGET (menu);
5155   gboolean was_floating = g_object_is_floating (object);
5156
5157   g_object_ref_sink (object);
5158
5159   if (unrealize)
5160     {
5161       g_object_ref (object);
5162       gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget);
5163       gtk_container_add (GTK_CONTAINER (new_parent), widget);
5164       g_object_unref (object);
5165     }
5166   else
5167     gtk_widget_reparent (widget, new_parent);
5168
5169   if (was_floating)
5170     g_object_force_floating (object);
5171   else
5172     g_object_unref (object);
5173 }
5174
5175 static void
5176 gtk_menu_show_all (GtkWidget *widget)
5177 {
5178   /* Show children, but not self. */
5179   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
5180 }
5181
5182 /**
5183  * gtk_menu_set_screen:
5184  * @menu: a #GtkMenu
5185  * @screen: (allow-none): a #GdkScreen, or %NULL if the screen should be
5186  *          determined by the widget the menu is attached to
5187  *
5188  * Sets the #GdkScreen on which the menu will be displayed.
5189  *
5190  * Since: 2.2
5191  */
5192 void
5193 gtk_menu_set_screen (GtkMenu   *menu,
5194                      GdkScreen *screen)
5195 {
5196   g_return_if_fail (GTK_IS_MENU (menu));
5197   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
5198
5199   g_object_set_data (G_OBJECT (menu), I_("gtk-menu-explicit-screen"), screen);
5200
5201   if (screen)
5202     {
5203       menu_change_screen (menu, screen);
5204     }
5205   else
5206     {
5207       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
5208       if (attach_widget)
5209         attach_widget_screen_changed (attach_widget, NULL, menu);
5210     }
5211 }
5212
5213 /**
5214  * gtk_menu_attach:
5215  * @menu: a #GtkMenu
5216  * @child: a #GtkMenuItem
5217  * @left_attach: The column number to attach the left side of the item to
5218  * @right_attach: The column number to attach the right side of the item to
5219  * @top_attach: The row number to attach the top of the item to
5220  * @bottom_attach: The row number to attach the bottom of the item to
5221  *
5222  * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
5223  * an item will occupy is specified by @left_attach, @right_attach,
5224  * @top_attach and @bottom_attach. These each represent the leftmost,
5225  * rightmost, uppermost and lower column and row numbers of the table.
5226  * (Columns and rows are indexed from zero).
5227  *
5228  * Note that this function is not related to gtk_menu_detach().
5229  *
5230  * Since: 2.4
5231  */
5232 void
5233 gtk_menu_attach (GtkMenu   *menu,
5234                  GtkWidget *child,
5235                  guint      left_attach,
5236                  guint      right_attach,
5237                  guint      top_attach,
5238                  guint      bottom_attach)
5239 {
5240   GtkMenuShell *menu_shell;
5241   GtkWidget *parent;
5242
5243   g_return_if_fail (GTK_IS_MENU (menu));
5244   g_return_if_fail (GTK_IS_MENU_ITEM (child));
5245   parent = gtk_widget_get_parent (child);
5246   g_return_if_fail (parent == NULL || parent == GTK_WIDGET (menu));
5247   g_return_if_fail (left_attach < right_attach);
5248   g_return_if_fail (top_attach < bottom_attach);
5249
5250   menu_shell = GTK_MENU_SHELL (menu);
5251
5252   if (!parent)
5253     {
5254       AttachInfo *ai = get_attach_info (child);
5255
5256       ai->left_attach = left_attach;
5257       ai->right_attach = right_attach;
5258       ai->top_attach = top_attach;
5259       ai->bottom_attach = bottom_attach;
5260
5261       menu_shell->priv->children = g_list_append (menu_shell->priv->children, child);
5262
5263       gtk_widget_set_parent (child, GTK_WIDGET (menu));
5264
5265       menu_queue_resize (menu);
5266     }
5267   else
5268     {
5269       gtk_container_child_set (GTK_CONTAINER (parent), child,
5270                                "left-attach",   left_attach,
5271                                "right-attach",  right_attach,
5272                                "top-attach",    top_attach,
5273                                "bottom-attach", bottom_attach,
5274                                NULL);
5275     }
5276 }
5277
5278 static gint
5279 gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
5280 {
5281   gint popup_delay;
5282
5283   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
5284                 "gtk-menu-popup-delay", &popup_delay,
5285                 NULL);
5286
5287   return popup_delay;
5288 }
5289
5290 static GtkWidget *
5291 find_child_containing (GtkMenuShell *menu_shell,
5292                        int           left,
5293                        int           right,
5294                        int           top,
5295                        int           bottom)
5296 {
5297   GList *list;
5298
5299   /* find a child which includes the area given by
5300    * left, right, top, bottom.
5301    */
5302   for (list = menu_shell->priv->children; list; list = list->next)
5303     {
5304       gint l, r, t, b;
5305
5306       if (!_gtk_menu_item_is_selectable (list->data))
5307         continue;
5308
5309       get_effective_child_attach (list->data, &l, &r, &t, &b);
5310
5311       if (l <= left && right <= r && t <= top && bottom <= b)
5312         return GTK_WIDGET (list->data);
5313     }
5314
5315   return NULL;
5316 }
5317
5318 static void
5319 gtk_menu_move_current (GtkMenuShell         *menu_shell,
5320                        GtkMenuDirectionType  direction)
5321 {
5322   GtkMenu *menu = GTK_MENU (menu_shell);
5323   gint i;
5324   gint l, r, t, b;
5325   GtkWidget *match = NULL;
5326
5327   if (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL)
5328     {
5329       switch (direction)
5330         {
5331         case GTK_MENU_DIR_CHILD:
5332           direction = GTK_MENU_DIR_PARENT;
5333           break;
5334         case GTK_MENU_DIR_PARENT:
5335           direction = GTK_MENU_DIR_CHILD;
5336           break;
5337         default: ;
5338         }
5339     }
5340
5341   /* use special table menu key bindings */
5342   if (menu_shell->priv->active_menu_item && gtk_menu_get_n_columns (menu) > 1)
5343     {
5344       get_effective_child_attach (menu_shell->priv->active_menu_item, &l, &r, &t, &b);
5345
5346       if (direction == GTK_MENU_DIR_NEXT)
5347         {
5348           for (i = b; i < gtk_menu_get_n_rows (menu); i++)
5349             {
5350               match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
5351               if (match)
5352                 break;
5353             }
5354
5355           if (!match)
5356             {
5357               /* wrap around */
5358               for (i = 0; i < t; i++)
5359                 {
5360                   match = find_child_containing (menu_shell,
5361                                                  l, l + 1, i, i + 1);
5362                   if (match)
5363                     break;
5364                 }
5365             }
5366         }
5367       else if (direction == GTK_MENU_DIR_PREV)
5368         {
5369           for (i = t; i > 0; i--)
5370             {
5371               match = find_child_containing (menu_shell,
5372                                              l, l + 1, i - 1, i);
5373               if (match)
5374                 break;
5375             }
5376
5377           if (!match)
5378             {
5379               /* wrap around */
5380               for (i = gtk_menu_get_n_rows (menu); i > b; i--)
5381                 {
5382                   match = find_child_containing (menu_shell,
5383                                                  l, l + 1, i - 1, i);
5384                   if (match)
5385                     break;
5386                 }
5387             }
5388         }
5389       else if (direction == GTK_MENU_DIR_PARENT)
5390         {
5391           /* we go one left if possible */
5392           if (l > 0)
5393             match = find_child_containing (menu_shell,
5394                                            l - 1, l, t, t + 1);
5395
5396           if (!match)
5397             {
5398               GtkWidget *parent = menu_shell->priv->parent_menu_shell;
5399
5400               if (!parent
5401                   || g_list_length (GTK_MENU_SHELL (parent)->priv->children) <= 1)
5402                 match = menu_shell->priv->active_menu_item;
5403             }
5404         }
5405       else if (direction == GTK_MENU_DIR_CHILD)
5406         {
5407           /* we go one right if possible */
5408           if (r < gtk_menu_get_n_columns (menu))
5409             match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
5410
5411           if (!match)
5412             {
5413               GtkWidget *parent = menu_shell->priv->parent_menu_shell;
5414
5415               if (! GTK_MENU_ITEM (menu_shell->priv->active_menu_item)->priv->submenu &&
5416                   (!parent ||
5417                    g_list_length (GTK_MENU_SHELL (parent)->priv->children) <= 1))
5418                 match = menu_shell->priv->active_menu_item;
5419             }
5420         }
5421
5422       if (match)
5423         {
5424           gtk_menu_shell_select_item (menu_shell, match);
5425           return;
5426         }
5427     }
5428
5429   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->move_current (menu_shell, direction);
5430 }
5431
5432 static gint
5433 get_visible_size (GtkMenu *menu)
5434 {
5435   GtkMenuPrivate *priv = menu->priv;
5436   GtkAllocation allocation;
5437   GtkWidget *widget = GTK_WIDGET (menu);
5438   GtkContainer *container = GTK_CONTAINER (menu);
5439   GtkBorder padding;
5440   gint menu_height;
5441
5442   gtk_widget_get_allocation (widget, &allocation);
5443   get_menu_padding (widget, &padding);
5444
5445   menu_height = (allocation.height -
5446                  (2 * gtk_container_get_border_width (container)) -
5447                  padding.top - padding.bottom);
5448
5449   if (!priv->tearoff_active)
5450     {
5451       GtkBorder arrow_border;
5452
5453       get_arrows_border (menu, &arrow_border);
5454       menu_height -= arrow_border.top;
5455       menu_height -= arrow_border.bottom;
5456     }
5457
5458   return menu_height;
5459 }
5460
5461 /* Find the sensitive on-screen child containing @y, or if none,
5462  * the nearest selectable onscreen child. (%NULL if none)
5463  */
5464 static GtkWidget *
5465 child_at (GtkMenu *menu,
5466           gint     y)
5467 {
5468   GtkMenuPrivate *priv = menu->priv;
5469   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5470   GtkWidget *child = NULL;
5471   gint child_offset = 0;
5472   GList *children;
5473   gint menu_height;
5474   gint lower, upper; /* Onscreen bounds */
5475
5476   menu_height = get_visible_size (menu);
5477   lower = priv->scroll_offset;
5478   upper = priv->scroll_offset + menu_height;
5479
5480   for (children = menu_shell->priv->children; children; children = children->next)
5481     {
5482       if (gtk_widget_get_visible (children->data))
5483         {
5484           GtkRequisition child_requisition;
5485
5486           gtk_widget_get_preferred_size (children->data,
5487                                          &child_requisition, NULL);
5488
5489           if (_gtk_menu_item_is_selectable (children->data) &&
5490               child_offset >= lower &&
5491               child_offset + child_requisition.height <= upper)
5492             {
5493               child = children->data;
5494
5495               if (child_offset + child_requisition.height > y &&
5496                   !GTK_IS_TEAROFF_MENU_ITEM (child))
5497                 return child;
5498             }
5499
5500           child_offset += child_requisition.height;
5501         }
5502     }
5503
5504   return child;
5505 }
5506
5507 static gint
5508 get_menu_height (GtkMenu *menu)
5509 {
5510   GtkMenuPrivate *priv = menu->priv;
5511   GtkWidget *widget = GTK_WIDGET (menu);
5512   GtkBorder padding;
5513   gint height;
5514
5515   get_menu_padding (widget, &padding);
5516
5517   height = priv->requested_height;
5518   height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) +
5519     padding.top + padding.bottom;
5520
5521   if (!priv->tearoff_active)
5522     {
5523       GtkBorder arrow_border;
5524
5525       get_arrows_border (menu, &arrow_border);
5526       height -= arrow_border.top;
5527       height -= arrow_border.bottom;
5528     }
5529
5530   return height;
5531 }
5532
5533 static void
5534 gtk_menu_real_move_scroll (GtkMenu       *menu,
5535                            GtkScrollType  type)
5536 {
5537   GtkMenuPrivate *priv = menu->priv;
5538   gint page_size = get_visible_size (menu);
5539   gint end_position = get_menu_height (menu);
5540   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5541
5542   switch (type)
5543     {
5544     case GTK_SCROLL_PAGE_UP:
5545     case GTK_SCROLL_PAGE_DOWN:
5546       {
5547         gint old_offset;
5548         gint new_offset;
5549         gint child_offset = 0;
5550         gboolean old_upper_arrow_visible;
5551         gint step;
5552
5553         if (type == GTK_SCROLL_PAGE_UP)
5554           step = - page_size;
5555         else
5556           step = page_size;
5557
5558         if (menu_shell->priv->active_menu_item)
5559           {
5560             gint child_height;
5561
5562             compute_child_offset (menu, menu_shell->priv->active_menu_item,
5563                                   &child_offset, &child_height, NULL);
5564             child_offset += child_height / 2;
5565           }
5566
5567         menu_shell->priv->ignore_enter = TRUE;
5568         old_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
5569         old_offset = priv->scroll_offset;
5570
5571         new_offset = priv->scroll_offset + step;
5572         new_offset = CLAMP (new_offset, 0, end_position - page_size);
5573
5574         gtk_menu_scroll_to (menu, new_offset);
5575
5576         if (menu_shell->priv->active_menu_item)
5577           {
5578             GtkWidget *new_child;
5579             gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
5580             GtkBorder arrow_border;
5581
5582             get_arrows_border (menu, &arrow_border);
5583
5584             if (priv->scroll_offset != old_offset)
5585               step = priv->scroll_offset - old_offset;
5586
5587             step -= (new_upper_arrow_visible - old_upper_arrow_visible) * arrow_border.top;
5588
5589             new_child = child_at (menu, child_offset + step);
5590             if (new_child)
5591               gtk_menu_shell_select_item (menu_shell, new_child);
5592           }
5593       }
5594       break;
5595     case GTK_SCROLL_START:
5596       /* Ignore the enter event we might get if the pointer is on the menu */
5597       menu_shell->priv->ignore_enter = TRUE;
5598       gtk_menu_shell_select_first (menu_shell, TRUE);
5599       break;
5600     case GTK_SCROLL_END:
5601       /* Ignore the enter event we might get if the pointer is on the menu */
5602       menu_shell->priv->ignore_enter = TRUE;
5603       _gtk_menu_shell_select_last (menu_shell, TRUE);
5604       break;
5605     default:
5606       break;
5607     }
5608 }
5609
5610 /**
5611  * gtk_menu_set_monitor:
5612  * @menu: a #GtkMenu
5613  * @monitor_num: the number of the monitor on which the menu should
5614  *    be popped up
5615  *
5616  * Informs GTK+ on which monitor a menu should be popped up.
5617  * See gdk_screen_get_monitor_geometry().
5618  *
5619  * This function should be called from a #GtkMenuPositionFunc
5620  * if the menu should not appear on the same monitor as the pointer.
5621  * This information can't be reliably inferred from the coordinates
5622  * returned by a #GtkMenuPositionFunc, since, for very long menus,
5623  * these coordinates may extend beyond the monitor boundaries or even
5624  * the screen boundaries.
5625  *
5626  * Since: 2.4
5627  */
5628 void
5629 gtk_menu_set_monitor (GtkMenu *menu,
5630                       gint     monitor_num)
5631 {
5632   GtkMenuPrivate *priv = menu->priv;
5633
5634   g_return_if_fail (GTK_IS_MENU (menu));
5635
5636   priv->monitor_num = monitor_num;
5637 }
5638
5639 /**
5640  * gtk_menu_get_monitor:
5641  * @menu: a #GtkMenu
5642  *
5643  * Retrieves the number of the monitor on which to show the menu.
5644  *
5645  * Returns: the number of the monitor on which the menu should
5646  *    be popped up or -1, if no monitor has been set
5647  *
5648  * Since: 2.14
5649  */
5650 gint
5651 gtk_menu_get_monitor (GtkMenu *menu)
5652 {
5653   g_return_val_if_fail (GTK_IS_MENU (menu), -1);
5654
5655   return menu->priv->monitor_num;
5656 }
5657
5658 /**
5659  * gtk_menu_get_for_attach_widget:
5660  * @widget: a #GtkWidget
5661  *
5662  * Returns a list of the menus which are attached to this widget.
5663  * This list is owned by GTK+ and must not be modified.
5664  *
5665  * Return value: (element-type GtkWidget) (transfer none): the list
5666  *     of menus attached to his widget.
5667  *
5668  * Since: 2.6
5669  */
5670 GList*
5671 gtk_menu_get_for_attach_widget (GtkWidget *widget)
5672 {
5673   GList *list;
5674
5675   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
5676
5677   list = g_object_get_data (G_OBJECT (widget), ATTACHED_MENUS);
5678
5679   return list;
5680 }
5681
5682 static void
5683 gtk_menu_grab_notify (GtkWidget *widget,
5684                       gboolean   was_grabbed)
5685 {
5686   GtkMenu *menu;
5687   GtkWidget *toplevel;
5688   GtkWindowGroup *group;
5689   GtkWidget *grab;
5690   GdkDevice *pointer;
5691
5692   menu = GTK_MENU (widget);
5693   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
5694
5695   if (!pointer ||
5696       !gtk_widget_device_is_shadowed (widget, pointer))
5697     return;
5698
5699   toplevel = gtk_widget_get_toplevel (widget);
5700
5701   if (!GTK_IS_WINDOW (toplevel))
5702     return;
5703
5704   group = gtk_window_get_group (GTK_WINDOW (toplevel));
5705   grab = gtk_window_group_get_current_device_grab (group, pointer);
5706
5707   if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
5708     gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
5709
5710   menu->priv->drag_scroll_started = FALSE;
5711 }
5712
5713 /**
5714  * gtk_menu_set_reserve_toggle_size:
5715  * @menu: a #GtkMenu
5716  * @reserve_toggle_size: whether to reserve size for toggles
5717  *
5718  * Sets whether the menu should reserve space for drawing toggles
5719  * or icons, regardless of their actual presence.
5720  *
5721  * Since: 2.18
5722  */
5723 void
5724 gtk_menu_set_reserve_toggle_size (GtkMenu  *menu,
5725                                   gboolean  reserve_toggle_size)
5726 {
5727   GtkMenuPrivate *priv = menu->priv;
5728   gboolean no_toggle_size;
5729
5730   g_return_if_fail (GTK_IS_MENU (menu));
5731
5732   no_toggle_size = !reserve_toggle_size;
5733
5734   if (priv->no_toggle_size != no_toggle_size)
5735     {
5736       priv->no_toggle_size = no_toggle_size;
5737
5738       g_object_notify (G_OBJECT (menu), "reserve-toggle-size");
5739     }
5740 }
5741
5742 /**
5743  * gtk_menu_get_reserve_toggle_size:
5744  * @menu: a #GtkMenu
5745  *
5746  * Returns whether the menu reserves space for toggles and
5747  * icons, regardless of their actual presence.
5748  *
5749  * Returns: Whether the menu reserves toggle space
5750  *
5751  * Since: 2.18
5752  */
5753 gboolean
5754 gtk_menu_get_reserve_toggle_size (GtkMenu *menu)
5755 {
5756   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
5757
5758   return !menu->priv->no_toggle_size;
5759 }
5760
5761 /**
5762  * gtk_menu_new_from_model:
5763  * @model: a #GMenuModel
5764  *
5765  * Creates a #GtkMenu and populates it with menu items and
5766  * submenus according to @model.
5767  *
5768  * The created menu items are connected to actions found in the
5769  * #GtkApplicationWindow to which the menu belongs - typically
5770  * by means of being attached to a widget (see gtk_menu_attach_to_widget())
5771  * that is contained within the #GtkApplicationWindows widget hierarchy.
5772  *
5773  * Returns: a new #GtkMenu
5774  *
5775  * Since: 3.4
5776  */
5777 GtkWidget *
5778 gtk_menu_new_from_model (GMenuModel *model)
5779 {
5780   GtkWidget *menu;
5781
5782   g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
5783
5784   menu = gtk_menu_new ();
5785   gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), model, NULL, TRUE);
5786
5787   return menu;
5788 }