]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
stylecontext: Do invalidation on first resize container
[~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   /* Attach the widget to the toplevel window. */
1238   gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget);
1239
1240   _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
1241
1242   /* Fallback title for menu comes from attach widget */
1243   gtk_menu_update_title (menu);
1244
1245   g_object_notify (G_OBJECT (menu), "attach-widget");
1246 }
1247
1248 /**
1249  * gtk_menu_get_attach_widget:
1250  * @menu: a #GtkMenu
1251  *
1252  * Returns the #GtkWidget that the menu is attached to.
1253  *
1254  * Returns: (transfer none): the #GtkWidget that the menu is attached to
1255  */
1256 GtkWidget*
1257 gtk_menu_get_attach_widget (GtkMenu *menu)
1258 {
1259   GtkMenuAttachData *data;
1260
1261   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1262
1263   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1264   if (data)
1265     return data->attach_widget;
1266   return NULL;
1267 }
1268
1269 /**
1270  * gtk_menu_detach:
1271  * @menu: a #GtkMenu
1272  *
1273  * Detaches the menu from the widget to which it had been attached.
1274  * This function will call the callback function, @detacher, provided
1275  * when the gtk_menu_attach_to_widget() function was called.
1276  */
1277 void
1278 gtk_menu_detach (GtkMenu *menu)
1279 {
1280   GtkMenuAttachData *data;
1281   GList *list;
1282
1283   g_return_if_fail (GTK_IS_MENU (menu));
1284
1285   /* keep this function in sync with gtk_widget_unparent() */
1286   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1287   if (!data)
1288     {
1289       g_warning ("gtk_menu_detach(): menu is not attached");
1290       return;
1291     }
1292   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), NULL);
1293
1294   /* Detach the toplevel window. */
1295   gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), NULL);
1296
1297   g_signal_handlers_disconnect_by_func (data->attach_widget,
1298                                         (gpointer) attach_widget_screen_changed,
1299                                         menu);
1300
1301   if (data->detacher)
1302     data->detacher (data->attach_widget, menu);
1303   list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
1304   list = g_list_remove (list, menu);
1305   if (list)
1306     g_object_set_data_full (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), list,
1307                             (GDestroyNotify) g_list_free);
1308   else
1309     g_object_set_data (G_OBJECT (data->attach_widget), I_(ATTACHED_MENUS), NULL);
1310
1311   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
1312     gtk_widget_unrealize (GTK_WIDGET (menu));
1313
1314   g_slice_free (GtkMenuAttachData, data);
1315
1316   _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
1317
1318   /* Fallback title for menu comes from attach widget */
1319   gtk_menu_update_title (menu);
1320
1321   g_object_notify (G_OBJECT (menu), "attach-widget");
1322   g_object_unref (menu);
1323 }
1324
1325 static void
1326 gtk_menu_remove (GtkContainer *container,
1327                  GtkWidget    *widget)
1328 {
1329   GtkMenu *menu = GTK_MENU (container);
1330   GtkMenuPrivate *priv = menu->priv;
1331
1332   /* Clear out old_active_menu_item if it matches the item we are removing */
1333   if (priv->old_active_menu_item == widget)
1334     g_clear_object (&priv->old_active_menu_item);
1335
1336   GTK_CONTAINER_CLASS (gtk_menu_parent_class)->remove (container, widget);
1337
1338   g_object_set_data (G_OBJECT (widget), I_(ATTACH_INFO_KEY), NULL);
1339
1340   menu_queue_resize (menu);
1341 }
1342
1343 /**
1344  * gtk_menu_new:
1345  *
1346  * Creates a new #GtkMenu
1347  *
1348  * Returns: a new #GtkMenu
1349  */
1350 GtkWidget*
1351 gtk_menu_new (void)
1352 {
1353   return g_object_new (GTK_TYPE_MENU, NULL);
1354 }
1355
1356 static void
1357 gtk_menu_real_insert (GtkMenuShell *menu_shell,
1358                       GtkWidget    *child,
1359                       gint          position)
1360 {
1361   GtkMenu *menu = GTK_MENU (menu_shell);
1362   GtkMenuPrivate *priv = menu->priv;
1363   AttachInfo *ai = get_attach_info (child);
1364
1365   ai->left_attach = -1;
1366   ai->right_attach = -1;
1367   ai->top_attach = -1;
1368   ai->bottom_attach = -1;
1369
1370   if (gtk_widget_get_realized (GTK_WIDGET (menu_shell)))
1371     gtk_widget_set_parent_window (child, priv->bin_window);
1372
1373   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->insert (menu_shell, child, position);
1374
1375   menu_queue_resize (menu);
1376 }
1377
1378 static void
1379 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
1380 {
1381   GtkMenuPrivate *priv = menu->priv;
1382   gint width, height;
1383
1384   if (priv->torn_off)
1385     {
1386       GdkWindow *window;
1387       cairo_surface_t *surface;
1388       cairo_pattern_t *pattern;
1389       cairo_t *cr;
1390
1391       priv->tearoff_active = FALSE;
1392       priv->saved_scroll_offset = priv->scroll_offset;
1393
1394       window = gtk_widget_get_window (priv->tearoff_window);
1395       width = gdk_window_get_width (window);
1396       height = gdk_window_get_height (window);
1397
1398       surface = gdk_window_create_similar_surface (window,
1399                                                    CAIRO_CONTENT_COLOR,
1400                                                    width,
1401                                                    height);
1402
1403       cr = cairo_create (surface);
1404       gdk_cairo_set_source_window (cr, window, 0, 0);
1405       cairo_paint (cr);
1406       cairo_destroy (cr);
1407
1408       gtk_widget_set_size_request (priv->tearoff_window, width, height);
1409
1410       pattern = cairo_pattern_create_for_surface (surface);
1411       gdk_window_set_background_pattern (window, pattern);
1412
1413       cairo_pattern_destroy (pattern);
1414       cairo_surface_destroy (surface);
1415     }
1416 }
1417
1418 static gboolean
1419 popup_grab_on_window (GdkWindow *window,
1420                       GdkDevice *keyboard,
1421                       GdkDevice *pointer,
1422                       guint32    activate_time)
1423 {
1424   if (keyboard &&
1425       gdk_device_grab (keyboard, window,
1426                        GDK_OWNERSHIP_WINDOW, TRUE,
1427                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1428                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1429     return FALSE;
1430
1431   if (pointer &&
1432       gdk_device_grab (pointer, window,
1433                        GDK_OWNERSHIP_WINDOW, TRUE,
1434                        GDK_SMOOTH_SCROLL_MASK |
1435                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1436                        GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
1437                        GDK_POINTER_MOTION_MASK,
1438                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1439     {
1440       if (keyboard)
1441         gdk_device_ungrab (keyboard, activate_time);
1442
1443       return FALSE;
1444     }
1445
1446   return TRUE;
1447 }
1448
1449 /**
1450  * gtk_menu_popup_for_device:
1451  * @menu: a #GtkMenu
1452  * @device: (allow-none): a #GdkDevice
1453  * @parent_menu_shell: (allow-none): the menu shell containing the triggering
1454  *     menu item, or %NULL
1455  * @parent_menu_item: (allow-none): the menu item whose activation triggered
1456  *     the popup, or %NULL
1457  * @func: (allow-none): a user supplied function used to position the menu,
1458  *     or %NULL
1459  * @data: (allow-none): user supplied data to be passed to @func
1460  * @destroy: (allow-none): destroy notify for @data
1461  * @button: the mouse button which was pressed to initiate the event
1462  * @activate_time: the time at which the activation event occurred
1463  *
1464  * Displays a menu and makes it available for selection.
1465  *
1466  * Applications can use this function to display context-sensitive menus,
1467  * and will typically supply %NULL for the @parent_menu_shell,
1468  * @parent_menu_item, @func, @data and @destroy parameters. The default
1469  * menu positioning function will position the menu at the current position
1470  * of @device (or its corresponding pointer).
1471  *
1472  * The @button parameter should be the mouse button pressed to initiate
1473  * the menu popup. If the menu popup was initiated by something other than
1474  * a mouse button press, such as a mouse button release or a keypress,
1475  * @button should be 0.
1476  *
1477  * The @activate_time parameter is used to conflict-resolve initiation of
1478  * concurrent requests for mouse/keyboard grab requests. To function
1479  * properly, this needs to be the time stamp of the user event (such as
1480  * a mouse click or key press) that caused the initiation of the popup.
1481  * Only if no such event is available, gtk_get_current_event_time() can
1482  * be used instead.
1483  *
1484  * Since: 3.0
1485  */
1486 void
1487 gtk_menu_popup_for_device (GtkMenu             *menu,
1488                            GdkDevice           *device,
1489                            GtkWidget           *parent_menu_shell,
1490                            GtkWidget           *parent_menu_item,
1491                            GtkMenuPositionFunc  func,
1492                            gpointer             data,
1493                            GDestroyNotify       destroy,
1494                            guint                button,
1495                            guint32              activate_time)
1496 {
1497   GtkMenuPrivate *priv = menu->priv;
1498   GtkWidget *widget;
1499   GtkWidget *xgrab_shell;
1500   GtkWidget *parent;
1501   GdkEvent *current_event;
1502   GtkMenuShell *menu_shell;
1503   gboolean grab_keyboard;
1504   GtkWidget *parent_toplevel;
1505   GdkDevice *keyboard, *pointer, *source_device = NULL;
1506
1507   g_return_if_fail (GTK_IS_MENU (menu));
1508   g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
1509
1510   if (device == NULL)
1511     device = gtk_get_current_event_device ();
1512
1513   if (device == NULL)
1514     {
1515       GdkDisplay *display;
1516       GdkDeviceManager *device_manager;
1517       GList *devices;
1518
1519       display = gtk_widget_get_display (GTK_WIDGET (menu));
1520       device_manager = gdk_display_get_device_manager (display);
1521       devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
1522
1523       device = devices->data;
1524
1525       g_list_free (devices);
1526     }
1527
1528   widget = GTK_WIDGET (menu);
1529   menu_shell = GTK_MENU_SHELL (menu);
1530
1531   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1532     {
1533       keyboard = device;
1534       pointer = gdk_device_get_associated_device (device);
1535     }
1536   else
1537     {
1538       pointer = device;
1539       keyboard = gdk_device_get_associated_device (device);
1540     }
1541
1542   menu_shell->priv->parent_menu_shell = parent_menu_shell;
1543
1544   priv->seen_item_enter = FALSE;
1545
1546   /* Find the last viewable ancestor, and make an X grab on it
1547    */
1548   parent = GTK_WIDGET (menu);
1549   xgrab_shell = NULL;
1550   while (parent)
1551     {
1552       gboolean viewable = TRUE;
1553       GtkWidget *tmp = parent;
1554
1555       while (tmp)
1556         {
1557           if (!gtk_widget_get_mapped (tmp))
1558             {
1559               viewable = FALSE;
1560               break;
1561             }
1562           tmp = gtk_widget_get_parent (tmp);
1563         }
1564
1565       if (viewable)
1566         xgrab_shell = parent;
1567
1568       parent = GTK_MENU_SHELL (parent)->priv->parent_menu_shell;
1569     }
1570
1571   /* We want to receive events generated when we map the menu;
1572    * unfortunately, since there is probably already an implicit
1573    * grab in place from the button that the user used to pop up
1574    * the menu, we won't receive then -- in particular, the EnterNotify
1575    * when the menu pops up under the pointer.
1576    *
1577    * If we are grabbing on a parent menu shell, no problem; just grab
1578    * on that menu shell first before popping up the window with
1579    * owner_events = TRUE.
1580    *
1581    * When grabbing on the menu itself, things get more convoluted --
1582    * we do an explicit grab on a specially created window with
1583    * owner_events = TRUE, which we override further down with a
1584    * grab on the menu. (We can't grab on the menu until it is mapped;
1585    * we probably could just leave the grab on the other window,
1586    * with a little reorganization of the code in gtkmenu*).
1587    */
1588   grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
1589   gtk_window_set_accept_focus (GTK_WINDOW (priv->toplevel), grab_keyboard);
1590
1591   if (!grab_keyboard)
1592     keyboard = NULL;
1593
1594   if (xgrab_shell && xgrab_shell != widget)
1595     {
1596       if (popup_grab_on_window (gtk_widget_get_window (xgrab_shell), keyboard, pointer, activate_time))
1597         {
1598           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1599           GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab = TRUE;
1600         }
1601     }
1602   else
1603     {
1604       GdkWindow *transfer_window;
1605
1606       xgrab_shell = widget;
1607       transfer_window = menu_grab_transfer_window_get (menu);
1608       if (popup_grab_on_window (transfer_window, keyboard, pointer, activate_time))
1609         {
1610           _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer);
1611           GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab = TRUE;
1612         }
1613     }
1614
1615   if (!GTK_MENU_SHELL (xgrab_shell)->priv->have_xgrab)
1616     {
1617       /* We failed to make our pointer/keyboard grab.
1618        * Rather than leaving the user with a stuck up window,
1619        * we just abort here. Presumably the user will try again.
1620        */
1621       menu_shell->priv->parent_menu_shell = NULL;
1622       menu_grab_transfer_window_destroy (menu);
1623       return;
1624     }
1625
1626   _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (menu), pointer);
1627   menu_shell->priv->active = TRUE;
1628   menu_shell->priv->button = button;
1629
1630   /* If we are popping up the menu from something other than, a button
1631    * press then, as a heuristic, we ignore enter events for the menu
1632    * until we get a MOTION_NOTIFY.
1633    */
1634
1635   current_event = gtk_get_current_event ();
1636   if (current_event)
1637     {
1638       if ((current_event->type != GDK_BUTTON_PRESS) &&
1639           (current_event->type != GDK_ENTER_NOTIFY))
1640         menu_shell->priv->ignore_enter = TRUE;
1641
1642       source_device = gdk_event_get_source_device (current_event);
1643       gdk_event_free (current_event);
1644     }
1645   else
1646     menu_shell->priv->ignore_enter = TRUE;
1647
1648   if (priv->torn_off)
1649     {
1650       gtk_menu_tearoff_bg_copy (menu);
1651
1652       gtk_menu_reparent (menu, priv->toplevel, FALSE);
1653     }
1654
1655   parent_toplevel = NULL;
1656   if (parent_menu_shell)
1657     parent_toplevel = gtk_widget_get_toplevel (parent_menu_shell);
1658   else if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1659     {
1660       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
1661       if (attach_widget)
1662         parent_toplevel = gtk_widget_get_toplevel (attach_widget);
1663     }
1664
1665   /* Set transient for to get the right window group and parent */
1666   if (GTK_IS_WINDOW (parent_toplevel))
1667     gtk_window_set_transient_for (GTK_WINDOW (priv->toplevel),
1668                                   GTK_WINDOW (parent_toplevel));
1669
1670   priv->parent_menu_item = parent_menu_item;
1671   priv->position_func = func;
1672   priv->position_func_data = data;
1673   priv->position_func_data_destroy = destroy;
1674   menu_shell->priv->activate_time = activate_time;
1675
1676   /* We need to show the menu here rather in the init function
1677    * because code expects to be able to tell if the menu is onscreen
1678    * by looking at gtk_widget_get_visible (menu)
1679    */
1680   gtk_widget_show (GTK_WIDGET (menu));
1681
1682   /* Position the menu, possibly changing the size request
1683    */
1684   gtk_menu_position (menu, TRUE);
1685
1686   /* Compute the size of the toplevel and realize it so we
1687    * can scroll correctly.
1688    */
1689   if (!gtk_widget_get_realized (GTK_WIDGET (menu)))
1690   {
1691     GtkRequisition tmp_request;
1692     GtkAllocation tmp_allocation = { 0, };
1693
1694     /* Instead of trusting the menu position function to queue a
1695      * resize when the menu goes out of bounds, invalidate the cached
1696      * size here.
1697      */
1698     gtk_widget_queue_resize (GTK_WIDGET (menu));
1699     gtk_widget_get_preferred_size (priv->toplevel, &tmp_request, NULL);
1700
1701     tmp_allocation.width = tmp_request.width;
1702     tmp_allocation.height = tmp_request.height;
1703
1704     gtk_widget_size_allocate (priv->toplevel, &tmp_allocation);
1705
1706     gtk_widget_realize (priv->toplevel);
1707   }
1708
1709   gtk_menu_scroll_to (menu, priv->scroll_offset);
1710
1711   /* if no item is selected, select the first one */
1712   if (!menu_shell->priv->active_menu_item &&
1713       source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
1714     gtk_menu_shell_select_first (menu_shell, TRUE);
1715
1716   /* Once everything is set up correctly, map the toplevel */
1717   gtk_widget_show (priv->toplevel);
1718
1719   if (xgrab_shell == widget)
1720     popup_grab_on_window (gtk_widget_get_window (widget), keyboard, pointer, activate_time); /* Should always succeed */
1721
1722   gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE);
1723
1724   if (parent_menu_shell)
1725     {
1726       gboolean keyboard_mode;
1727
1728       keyboard_mode = _gtk_menu_shell_get_keyboard_mode (GTK_MENU_SHELL (parent_menu_shell));
1729       _gtk_menu_shell_set_keyboard_mode (menu_shell, keyboard_mode);
1730     }
1731   else if (menu_shell->priv->button == 0) /* a keynav-activated context menu */
1732     _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE);
1733
1734   _gtk_menu_shell_update_mnemonics (menu_shell);
1735 }
1736
1737 /**
1738  * gtk_menu_popup:
1739  * @menu: a #GtkMenu
1740  * @parent_menu_shell: (allow-none): the menu shell containing the
1741  *     triggering menu item, or %NULL
1742  * @parent_menu_item: (allow-none): the menu item whose activation
1743  *     triggered the popup, or %NULL
1744  * @func: (scope async) (allow-none): a user supplied function used to position
1745  *     the menu, or %NULL
1746  * @data: user supplied data to be passed to @func.
1747  * @button: the mouse button which was pressed to initiate the event.
1748  * @activate_time: the time at which the activation event occurred.
1749  *
1750  * Displays a menu and makes it available for selection.
1751  *
1752  * Applications can use this function to display context-sensitive
1753  * menus, and will typically supply %NULL for the @parent_menu_shell,
1754  * @parent_menu_item, @func and @data parameters. The default menu
1755  * positioning function will position the menu at the current mouse
1756  * cursor position.
1757  *
1758  * The @button parameter should be the mouse button pressed to initiate
1759  * the menu popup. If the menu popup was initiated by something other
1760  * than a mouse button press, such as a mouse button release or a keypress,
1761  * @button should be 0.
1762  *
1763  * The @activate_time parameter is used to conflict-resolve initiation
1764  * of concurrent requests for mouse/keyboard grab requests. To function
1765  * properly, this needs to be the timestamp of the user event (such as
1766  * a mouse click or key press) that caused the initiation of the popup.
1767  * Only if no such event is available, gtk_get_current_event_time() can
1768  * be used instead.
1769  */
1770 void
1771 gtk_menu_popup (GtkMenu             *menu,
1772                 GtkWidget           *parent_menu_shell,
1773                 GtkWidget           *parent_menu_item,
1774                 GtkMenuPositionFunc  func,
1775                 gpointer             data,
1776                 guint                button,
1777                 guint32              activate_time)
1778 {
1779   g_return_if_fail (GTK_IS_MENU (menu));
1780
1781   gtk_menu_popup_for_device (menu,
1782                              NULL,
1783                              parent_menu_shell,
1784                              parent_menu_item,
1785                              func, data, NULL,
1786                              button, activate_time);
1787 }
1788
1789 /**
1790  * gtk_menu_popdown:
1791  * @menu: a #GtkMenu
1792  *
1793  * Removes the menu from the screen.
1794  */
1795 void
1796 gtk_menu_popdown (GtkMenu *menu)
1797 {
1798   GtkMenuPrivate *priv;
1799   GtkMenuShell *menu_shell;
1800   GdkDevice *pointer;
1801
1802   g_return_if_fail (GTK_IS_MENU (menu));
1803
1804   menu_shell = GTK_MENU_SHELL (menu);
1805   priv = menu->priv;
1806
1807   menu_shell->priv->parent_menu_shell = NULL;
1808   menu_shell->priv->active = FALSE;
1809   menu_shell->priv->ignore_enter = FALSE;
1810
1811   priv->have_position = FALSE;
1812
1813   gtk_menu_stop_scrolling (menu);
1814   gtk_menu_stop_navigating_submenu (menu);
1815
1816   if (menu_shell->priv->active_menu_item)
1817     {
1818       if (priv->old_active_menu_item)
1819         g_object_unref (priv->old_active_menu_item);
1820       priv->old_active_menu_item = menu_shell->priv->active_menu_item;
1821       g_object_ref (priv->old_active_menu_item);
1822     }
1823
1824   gtk_menu_shell_deselect (menu_shell);
1825
1826   /* The X Grab, if present, will automatically be removed
1827    * when we hide the window
1828    */
1829   if (priv->toplevel)
1830     {
1831       gtk_widget_hide (priv->toplevel);
1832       gtk_window_set_transient_for (GTK_WINDOW (priv->toplevel), NULL);
1833     }
1834
1835   pointer = _gtk_menu_shell_get_grab_device (menu_shell);
1836
1837   if (priv->torn_off)
1838     {
1839       gtk_widget_set_size_request (priv->tearoff_window, -1, -1);
1840
1841       if (gtk_bin_get_child (GTK_BIN (priv->toplevel)))
1842         {
1843           gtk_menu_reparent (menu, priv->tearoff_hbox, TRUE);
1844         }
1845       else
1846         {
1847           /* We popped up the menu from the tearoff, so we need to
1848            * release the grab - we aren't actually hiding the menu.
1849            */
1850           if (menu_shell->priv->have_xgrab && pointer)
1851             {
1852               GdkDevice *keyboard;
1853
1854               gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
1855               keyboard = gdk_device_get_associated_device (pointer);
1856
1857               if (keyboard)
1858                 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
1859             }
1860         }
1861
1862       /* gtk_menu_popdown is called each time a menu item is selected from
1863        * a torn off menu. Only scroll back to the saved position if the
1864        * non-tearoff menu was popped down.
1865        */
1866       if (!priv->tearoff_active)
1867         gtk_menu_scroll_to (menu, priv->saved_scroll_offset);
1868       priv->tearoff_active = TRUE;
1869     }
1870   else
1871     gtk_widget_hide (GTK_WIDGET (menu));
1872
1873   menu_shell->priv->have_xgrab = FALSE;
1874
1875   if (pointer)
1876     gtk_device_grab_remove (GTK_WIDGET (menu), pointer);
1877
1878   _gtk_menu_shell_set_grab_device (menu_shell, NULL);
1879
1880   menu_grab_transfer_window_destroy (menu);
1881 }
1882
1883 /**
1884  * gtk_menu_get_active:
1885  * @menu: a #GtkMenu
1886  *
1887  * Returns the selected menu item from the menu.  This is used by the
1888  * #GtkComboBox.
1889  *
1890  * Returns: (transfer none): the #GtkMenuItem that was last selected
1891  *          in the menu.  If a selection has not yet been made, the
1892  *          first menu item is selected.
1893  */
1894 GtkWidget*
1895 gtk_menu_get_active (GtkMenu *menu)
1896 {
1897   GtkMenuPrivate *priv = menu->priv;
1898   GtkWidget *child;
1899   GList *children;
1900
1901   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1902
1903   if (!priv->old_active_menu_item)
1904     {
1905       child = NULL;
1906       children = GTK_MENU_SHELL (menu)->priv->children;
1907
1908       while (children)
1909         {
1910           child = children->data;
1911           children = children->next;
1912
1913           if (gtk_bin_get_child (GTK_BIN (child)))
1914             break;
1915           child = NULL;
1916         }
1917
1918       priv->old_active_menu_item = child;
1919       if (priv->old_active_menu_item)
1920         g_object_ref (priv->old_active_menu_item);
1921     }
1922
1923   return priv->old_active_menu_item;
1924 }
1925
1926 /**
1927  * gtk_menu_set_active:
1928  * @menu: a #GtkMenu
1929  * @index: the index of the menu item to select.  Iindex values are
1930  *         from 0 to n-1
1931  *
1932  * Selects the specified menu item within the menu.  This is used by
1933  * the #GtkComboBox and should not be used by anyone else.
1934  */
1935 void
1936 gtk_menu_set_active (GtkMenu *menu,
1937                      guint    index)
1938 {
1939   GtkMenuPrivate *priv = menu->priv;
1940   GtkWidget *child;
1941   GList *tmp_list;
1942
1943   g_return_if_fail (GTK_IS_MENU (menu));
1944
1945   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->priv->children, index);
1946   if (tmp_list)
1947     {
1948       child = tmp_list->data;
1949       if (gtk_bin_get_child (GTK_BIN (child)))
1950         {
1951           if (priv->old_active_menu_item)
1952             g_object_unref (priv->old_active_menu_item);
1953           priv->old_active_menu_item = child;
1954           g_object_ref (priv->old_active_menu_item);
1955         }
1956     }
1957 }
1958
1959 /**
1960  * gtk_menu_set_accel_group:
1961  * @menu: a #GtkMenu
1962  * @accel_group: (allow-none): the #GtkAccelGroup to be associated
1963  *               with the menu.
1964  *
1965  * Set the #GtkAccelGroup which holds global accelerators for the
1966  * menu.  This accelerator group needs to also be added to all windows
1967  * that this menu is being used in with gtk_window_add_accel_group(),
1968  * in order for those windows to support all the accelerators
1969  * contained in this group.
1970  */
1971 void
1972 gtk_menu_set_accel_group (GtkMenu       *menu,
1973                           GtkAccelGroup *accel_group)
1974 {
1975   GtkMenuPrivate *priv = menu->priv;
1976   g_return_if_fail (GTK_IS_MENU (menu));
1977
1978   if (priv->accel_group != accel_group)
1979     {
1980       if (priv->accel_group)
1981         g_object_unref (priv->accel_group);
1982       priv->accel_group = accel_group;
1983       if (priv->accel_group)
1984         g_object_ref (priv->accel_group);
1985       _gtk_menu_refresh_accel_paths (menu, TRUE);
1986     }
1987 }
1988
1989 /**
1990  * gtk_menu_get_accel_group:
1991  * @menu: a #GtkMenu
1992  *
1993  * Gets the #GtkAccelGroup which holds global accelerators for the
1994  * menu. See gtk_menu_set_accel_group().
1995  *
1996  * Returns: (transfer none): the #GtkAccelGroup associated with the menu
1997  */
1998 GtkAccelGroup*
1999 gtk_menu_get_accel_group (GtkMenu *menu)
2000 {
2001   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2002
2003   return menu->priv->accel_group;
2004 }
2005
2006 static gboolean
2007 gtk_menu_real_can_activate_accel (GtkWidget *widget,
2008                                   guint      signal_id)
2009 {
2010   /* Menu items chain here to figure whether they can activate their
2011    * accelerators.  Unlike ordinary widgets, menus allow accel
2012    * activation even if invisible since that's the usual case for
2013    * submenus/popup-menus. however, the state of the attach widget
2014    * affects the "activeness" of the menu.
2015    */
2016   GtkWidget *awidget = gtk_menu_get_attach_widget (GTK_MENU (widget));
2017
2018   if (awidget)
2019     return gtk_widget_can_activate_accel (awidget, signal_id);
2020   else
2021     return gtk_widget_is_sensitive (widget);
2022 }
2023
2024 /**
2025  * gtk_menu_set_accel_path:
2026  * @menu:       a valid #GtkMenu
2027  * @accel_path: (allow-none): a valid accelerator path
2028  *
2029  * Sets an accelerator path for this menu from which accelerator paths
2030  * for its immediate children, its menu items, can be constructed.
2031  * The main purpose of this function is to spare the programmer the
2032  * inconvenience of having to call gtk_menu_item_set_accel_path() on
2033  * each menu item that should support runtime user changable accelerators.
2034  * Instead, by just calling gtk_menu_set_accel_path() on their parent,
2035  * each menu item of this menu, that contains a label describing its
2036  * purpose, automatically gets an accel path assigned.
2037  *
2038  * For example, a menu containing menu items "New" and "Exit", will, after
2039  * <literal>gtk_menu_set_accel_path (menu, "&lt;Gnumeric-Sheet&gt;/File");</literal>
2040  * has been called, assign its items the accel paths:
2041  * <literal>"&lt;Gnumeric-Sheet&gt;/File/New"</literal> and <literal>"&lt;Gnumeric-Sheet&gt;/File/Exit"</literal>.
2042  *
2043  * Assigning accel paths to menu items then enables the user to change
2044  * their accelerators at runtime. More details about accelerator paths
2045  * and their default setups can be found at gtk_accel_map_add_entry().
2046  *
2047  * Note that @accel_path string will be stored in a #GQuark. Therefore,
2048  * if you pass a static string, you can save some memory by interning
2049  * it first with g_intern_static_string().
2050  */
2051 void
2052 gtk_menu_set_accel_path (GtkMenu     *menu,
2053                          const gchar *accel_path)
2054 {
2055   GtkMenuPrivate *priv = menu->priv;
2056   g_return_if_fail (GTK_IS_MENU (menu));
2057
2058   if (accel_path)
2059     g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
2060
2061   /* FIXME: accel_path should be defined as const gchar* */
2062   priv->accel_path = (gchar*)g_intern_string (accel_path);
2063   if (priv->accel_path)
2064     _gtk_menu_refresh_accel_paths (menu, FALSE);
2065 }
2066
2067 /**
2068  * gtk_menu_get_accel_path:
2069  * @menu: a valid #GtkMenu
2070  *
2071  * Retrieves the accelerator path set on the menu.
2072  *
2073  * Returns: the accelerator path set on the menu.
2074  *
2075  * Since: 2.14
2076  */
2077 const gchar*
2078 gtk_menu_get_accel_path (GtkMenu *menu)
2079 {
2080   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2081
2082   return menu->priv->accel_path;
2083 }
2084
2085 typedef struct {
2086   GtkMenu *menu;
2087   gboolean group_changed;
2088 } AccelPropagation;
2089
2090 static void
2091 refresh_accel_paths_foreach (GtkWidget *widget,
2092                              gpointer   data)
2093 {
2094   GtkMenuPrivate *priv;
2095   AccelPropagation *prop = data;
2096
2097   if (GTK_IS_MENU_ITEM (widget))  /* should always be true */
2098     {
2099       priv = prop->menu->priv;
2100       _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
2101                                          priv->accel_path,
2102                                          priv->accel_group,
2103                                          prop->group_changed);
2104     }
2105 }
2106
2107 static void
2108 _gtk_menu_refresh_accel_paths (GtkMenu  *menu,
2109                                gboolean  group_changed)
2110 {
2111   GtkMenuPrivate *priv = menu->priv;
2112   g_return_if_fail (GTK_IS_MENU (menu));
2113
2114   if (priv->accel_path && priv->accel_group)
2115     {
2116       AccelPropagation prop;
2117
2118       prop.menu = menu;
2119       prop.group_changed = group_changed;
2120       gtk_container_foreach (GTK_CONTAINER (menu),
2121                              refresh_accel_paths_foreach,
2122                              &prop);
2123     }
2124 }
2125
2126 /**
2127  * gtk_menu_reposition:
2128  * @menu: a #GtkMenu
2129  *
2130  * Repositions the menu according to its position function.
2131  */
2132 void
2133 gtk_menu_reposition (GtkMenu *menu)
2134 {
2135   g_return_if_fail (GTK_IS_MENU (menu));
2136
2137   if (!menu->priv->torn_off && gtk_widget_is_drawable (GTK_WIDGET (menu)))
2138     gtk_menu_position (menu, FALSE);
2139 }
2140
2141 static void
2142 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
2143                             GtkMenu       *menu)
2144 {
2145   double value;
2146
2147   value = gtk_adjustment_get_value (adjustment);
2148   if (menu->priv->scroll_offset != value)
2149     gtk_menu_scroll_to (menu, value);
2150 }
2151
2152 static void
2153 gtk_menu_set_tearoff_hints (GtkMenu *menu,
2154                             gint     width)
2155 {
2156   GtkMenuPrivate *priv = menu->priv;
2157   GdkGeometry geometry_hints;
2158
2159   if (!priv->tearoff_window)
2160     return;
2161
2162   if (gtk_widget_get_visible (priv->tearoff_scrollbar))
2163     {
2164       GtkRequisition requisition;
2165
2166       gtk_widget_get_preferred_size (priv->tearoff_scrollbar,
2167                                      &requisition, NULL);
2168       width += requisition.width;
2169     }
2170
2171   geometry_hints.min_width = width;
2172   geometry_hints.max_width = width;
2173
2174   geometry_hints.min_height = 0;
2175   geometry_hints.max_height = priv->requested_height;
2176
2177   gtk_window_set_geometry_hints (GTK_WINDOW (priv->tearoff_window),
2178                                  NULL,
2179                                  &geometry_hints,
2180                                  GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
2181 }
2182
2183 static void
2184 gtk_menu_update_title (GtkMenu *menu)
2185 {
2186   GtkMenuPrivate *priv = menu->priv;
2187
2188   if (priv->tearoff_window)
2189     {
2190       const gchar *title;
2191       GtkWidget *attach_widget;
2192
2193       title = gtk_menu_get_title (menu);
2194       if (!title)
2195         {
2196           attach_widget = gtk_menu_get_attach_widget (menu);
2197           if (GTK_IS_MENU_ITEM (attach_widget))
2198             {
2199               GtkWidget *child = gtk_bin_get_child (GTK_BIN (attach_widget));
2200               if (GTK_IS_LABEL (child))
2201                 title = gtk_label_get_text (GTK_LABEL (child));
2202             }
2203         }
2204
2205       if (title)
2206         gtk_window_set_title (GTK_WINDOW (priv->tearoff_window), title);
2207     }
2208 }
2209
2210 static GtkWidget*
2211 gtk_menu_get_toplevel (GtkWidget *menu)
2212 {
2213   GtkWidget *attach, *toplevel;
2214
2215   attach = gtk_menu_get_attach_widget (GTK_MENU (menu));
2216
2217   if (GTK_IS_MENU_ITEM (attach))
2218     attach = gtk_widget_get_parent (attach);
2219
2220   if (GTK_IS_MENU (attach))
2221     return gtk_menu_get_toplevel (attach);
2222   else if (GTK_IS_WIDGET (attach))
2223     {
2224       toplevel = gtk_widget_get_toplevel (attach);
2225       if (gtk_widget_is_toplevel (toplevel))
2226         return toplevel;
2227     }
2228
2229   return NULL;
2230 }
2231
2232 static void
2233 tearoff_window_destroyed (GtkWidget *widget,
2234                           GtkMenu   *menu)
2235 {
2236   gtk_menu_set_tearoff_state (menu, FALSE);
2237 }
2238
2239 /**
2240  * gtk_menu_set_tearoff_state:
2241  * @menu: a #GtkMenu
2242  * @torn_off: If %TRUE, menu is displayed as a tearoff menu.
2243  *
2244  * Changes the tearoff state of the menu.  A menu is normally
2245  * displayed as drop down menu which persists as long as the menu is
2246  * active.  It can also be displayed as a tearoff menu which persists
2247  * until it is closed or reattached.
2248  */
2249 void
2250 gtk_menu_set_tearoff_state (GtkMenu  *menu,
2251                             gboolean  torn_off)
2252 {
2253   GtkMenuPrivate *priv = menu->priv;
2254   gint height;
2255
2256   g_return_if_fail (GTK_IS_MENU (menu));
2257
2258   if (priv->torn_off != torn_off)
2259     {
2260       priv->torn_off = torn_off;
2261       priv->tearoff_active = torn_off;
2262
2263       if (priv->torn_off)
2264         {
2265           if (gtk_widget_get_visible (GTK_WIDGET (menu)))
2266             gtk_menu_popdown (menu);
2267
2268           if (!priv->tearoff_window)
2269             {
2270               GtkWidget *toplevel;
2271
2272               priv->tearoff_window = g_object_new (GTK_TYPE_WINDOW,
2273                                                    "type", GTK_WINDOW_TOPLEVEL,
2274                                                    "screen", gtk_widget_get_screen (priv->toplevel),
2275                                                    "app-paintable", TRUE,
2276                                                    NULL);
2277
2278               gtk_window_set_type_hint (GTK_WINDOW (priv->tearoff_window),
2279                                         GDK_WINDOW_TYPE_HINT_MENU);
2280               gtk_window_set_mnemonic_modifier (GTK_WINDOW (priv->tearoff_window), 0);
2281               g_signal_connect (priv->tearoff_window, "destroy",
2282                                 G_CALLBACK (tearoff_window_destroyed), menu);
2283               g_signal_connect (priv->tearoff_window, "event",
2284                                 G_CALLBACK (gtk_menu_window_event), menu);
2285
2286               gtk_menu_update_title (menu);
2287
2288               gtk_widget_realize (priv->tearoff_window);
2289
2290               toplevel = gtk_menu_get_toplevel (GTK_WIDGET (menu));
2291               if (toplevel != NULL)
2292                 gtk_window_set_transient_for (GTK_WINDOW (priv->tearoff_window),
2293                                               GTK_WINDOW (toplevel));
2294
2295               priv->tearoff_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2296               gtk_container_add (GTK_CONTAINER (priv->tearoff_window),
2297                                  priv->tearoff_hbox);
2298
2299               height = gdk_window_get_height (gtk_widget_get_window (GTK_WIDGET (menu)));
2300               priv->tearoff_adjustment = gtk_adjustment_new (0,
2301                                                              0, priv->requested_height,
2302                                                              MENU_SCROLL_STEP2,
2303                                                              height/2,
2304                                                              height);
2305               g_object_connect (priv->tearoff_adjustment,
2306                                 "signal::value-changed", gtk_menu_scrollbar_changed, menu,
2307                                 NULL);
2308               priv->tearoff_scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, priv->tearoff_adjustment);
2309
2310               gtk_box_pack_end (GTK_BOX (priv->tearoff_hbox),
2311                                 priv->tearoff_scrollbar,
2312                                 FALSE, FALSE, 0);
2313
2314               if (gtk_adjustment_get_upper (priv->tearoff_adjustment) > height)
2315                 gtk_widget_show (priv->tearoff_scrollbar);
2316
2317               gtk_widget_show (priv->tearoff_hbox);
2318             }
2319
2320           gtk_menu_reparent (menu, priv->tearoff_hbox, FALSE);
2321
2322           /* Update menu->requisition */
2323           gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, NULL);
2324
2325           gtk_menu_set_tearoff_hints (menu, gdk_window_get_width (gtk_widget_get_window (GTK_WIDGET (menu))));
2326
2327           gtk_widget_realize (priv->tearoff_window);
2328           gtk_menu_position (menu, TRUE);
2329
2330           gtk_widget_show (GTK_WIDGET (menu));
2331           gtk_widget_show (priv->tearoff_window);
2332
2333           gtk_menu_scroll_to (menu, 0);
2334
2335         }
2336       else
2337         {
2338           gtk_widget_hide (GTK_WIDGET (menu));
2339           gtk_widget_hide (priv->tearoff_window);
2340           if (GTK_IS_CONTAINER (priv->toplevel))
2341             gtk_menu_reparent (menu, priv->toplevel, FALSE);
2342           gtk_widget_destroy (priv->tearoff_window);
2343
2344           priv->tearoff_window = NULL;
2345           priv->tearoff_hbox = NULL;
2346           priv->tearoff_scrollbar = NULL;
2347           priv->tearoff_adjustment = NULL;
2348         }
2349
2350       g_object_notify (G_OBJECT (menu), "tearoff-state");
2351     }
2352 }
2353
2354 /**
2355  * gtk_menu_get_tearoff_state:
2356  * @menu: a #GtkMenu
2357  *
2358  * Returns whether the menu is torn off.
2359  * See gtk_menu_set_tearoff_state().
2360  *
2361  * Return value: %TRUE if the menu is currently torn off.
2362  */
2363 gboolean
2364 gtk_menu_get_tearoff_state (GtkMenu *menu)
2365 {
2366   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
2367
2368   return menu->priv->torn_off;
2369 }
2370
2371 /**
2372  * gtk_menu_set_title:
2373  * @menu: a #GtkMenu
2374  * @title: a string containing the title for the menu
2375  *
2376  * Sets the title string for the menu.
2377  *
2378  * The title is displayed when the menu is shown as a tearoff
2379  * menu. If @title is %NULL, the menu will see if it is attached
2380  * to a parent menu item, and if so it will try to use the same
2381  * text as that menu item's label.
2382  */
2383 void
2384 gtk_menu_set_title (GtkMenu     *menu,
2385                     const gchar *title)
2386 {
2387   GtkMenuPrivate *priv = menu->priv;
2388   char *old_title;
2389
2390   g_return_if_fail (GTK_IS_MENU (menu));
2391
2392   old_title = priv->title;
2393   priv->title = g_strdup (title);
2394   g_free (old_title);
2395
2396   gtk_menu_update_title (menu);
2397   g_object_notify (G_OBJECT (menu), "tearoff-title");
2398 }
2399
2400 /**
2401  * gtk_menu_get_title:
2402  * @menu: a #GtkMenu
2403  *
2404  * Returns the title of the menu. See gtk_menu_set_title().
2405  *
2406  * Return value: the title of the menu, or %NULL if the menu
2407  *     has no title set on it. This string is owned by GTK+
2408  *     and should not be modified or freed.
2409  **/
2410 const gchar *
2411 gtk_menu_get_title (GtkMenu *menu)
2412 {
2413   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
2414
2415   return menu->priv->title;
2416 }
2417
2418 /**
2419  * gtk_menu_reorder_child:
2420  * @menu: a #GtkMenu
2421  * @child: the #GtkMenuItem to move
2422  * @position: the new position to place @child.
2423  *     Positions are numbered from 0 to n - 1
2424  *
2425  * Moves @child to a new @position in the list of @menu
2426  * children.
2427  */
2428 void
2429 gtk_menu_reorder_child (GtkMenu   *menu,
2430                         GtkWidget *child,
2431                         gint       position)
2432 {
2433   GtkMenuShell *menu_shell;
2434
2435   g_return_if_fail (GTK_IS_MENU (menu));
2436   g_return_if_fail (GTK_IS_MENU_ITEM (child));
2437
2438   menu_shell = GTK_MENU_SHELL (menu);
2439
2440   if (g_list_find (menu_shell->priv->children, child))
2441     {
2442       menu_shell->priv->children = g_list_remove (menu_shell->priv->children, child);
2443       menu_shell->priv->children = g_list_insert (menu_shell->priv->children, child, position);
2444
2445       menu_queue_resize (menu);
2446     }
2447 }
2448
2449 static void
2450 gtk_menu_style_updated (GtkWidget *widget)
2451 {
2452   GTK_WIDGET_CLASS (gtk_menu_parent_class)->style_updated (widget);
2453
2454   if (gtk_widget_get_realized (widget))
2455     {
2456       GtkMenu *menu = GTK_MENU (widget);
2457       GtkMenuPrivate *priv = menu->priv;
2458       GtkStyleContext *context;
2459
2460       context = gtk_widget_get_style_context (widget);
2461
2462       gtk_style_context_set_background (context, priv->bin_window);
2463       gtk_style_context_set_background (context, priv->view_window);
2464       gtk_style_context_set_background (context, gtk_widget_get_window (widget));
2465     }
2466 }
2467
2468 static void
2469 get_arrows_border (GtkMenu   *menu,
2470                    GtkBorder *border)
2471 {
2472   GtkMenuPrivate *priv = menu->priv;
2473   guint scroll_arrow_height;
2474   GtkArrowPlacement arrow_placement;
2475
2476   gtk_widget_style_get (GTK_WIDGET (menu),
2477                         "scroll-arrow-vlength", &scroll_arrow_height,
2478                         "arrow_placement", &arrow_placement,
2479                         NULL);
2480
2481   switch (arrow_placement)
2482     {
2483     case GTK_ARROWS_BOTH:
2484       border->top = priv->upper_arrow_visible ? scroll_arrow_height : 0;
2485       border->bottom = priv->lower_arrow_visible ? scroll_arrow_height : 0;
2486       break;
2487
2488     case GTK_ARROWS_START:
2489       border->top = (priv->upper_arrow_visible ||
2490                      priv->lower_arrow_visible) ? scroll_arrow_height : 0;
2491       border->bottom = 0;
2492       break;
2493
2494     case GTK_ARROWS_END:
2495       border->top = 0;
2496       border->bottom = (priv->upper_arrow_visible ||
2497                         priv->lower_arrow_visible) ? scroll_arrow_height : 0;
2498       break;
2499     }
2500
2501   border->left = border->right = 0;
2502 }
2503
2504 static void
2505 get_menu_padding (GtkWidget *widget,
2506                   GtkBorder *padding)
2507 {
2508   GtkStyleContext *context;
2509   GtkStateFlags state;
2510
2511   context = gtk_widget_get_style_context (widget);
2512   state = gtk_widget_get_state_flags (widget);
2513
2514   gtk_style_context_get_padding (context, state, padding);
2515 }
2516
2517 static void
2518 gtk_menu_realize (GtkWidget *widget)
2519 {
2520   GtkMenu *menu = GTK_MENU (widget);
2521   GtkMenuPrivate *priv = menu->priv;
2522   GtkAllocation allocation;
2523   GtkStyleContext *context;
2524   GdkWindow *window;
2525   GdkWindowAttr attributes;
2526   gint attributes_mask;
2527   gint border_width;
2528   GtkWidget *child;
2529   GList *children;
2530   GtkBorder arrow_border, padding;
2531
2532   g_return_if_fail (GTK_IS_MENU (widget));
2533
2534   gtk_widget_set_realized (widget, TRUE);
2535
2536   gtk_widget_get_allocation (widget, &allocation);
2537
2538   attributes.window_type = GDK_WINDOW_CHILD;
2539   attributes.x = allocation.x;
2540   attributes.y = allocation.y;
2541   attributes.width = allocation.width;
2542   attributes.height = allocation.height;
2543   attributes.wclass = GDK_INPUT_OUTPUT;
2544   attributes.visual = gtk_widget_get_visual (widget);
2545   attributes.event_mask = gtk_widget_get_events (widget);
2546   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2547                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
2548
2549   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2550
2551   window = gdk_window_new (gtk_widget_get_parent_window (widget),
2552                            &attributes, attributes_mask);
2553   gtk_widget_set_window (widget, window);
2554   gtk_widget_register_window (widget, window);
2555
2556   get_menu_padding (widget, &padding);
2557   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2558   context = gtk_widget_get_style_context (widget);
2559
2560   gtk_widget_get_allocation (widget, &allocation);
2561
2562   attributes.x = border_width + padding.left;
2563   attributes.y = border_width + padding.top;
2564   attributes.width = allocation.width -
2565     (2 * border_width) - padding.left - padding.right;
2566   attributes.height = allocation.height -
2567     (2 * border_width) - padding.top - padding.bottom;
2568
2569   get_arrows_border (menu, &arrow_border);
2570   attributes.y += arrow_border.top;
2571   attributes.height -= arrow_border.top;
2572   attributes.height -= arrow_border.bottom;
2573
2574   attributes.width = MAX (1, attributes.width);
2575   attributes.height = MAX (1, attributes.height);
2576
2577   priv->view_window = gdk_window_new (window,
2578                                       &attributes, attributes_mask);
2579   gtk_widget_register_window (widget, priv->view_window);
2580
2581   gtk_widget_get_allocation (widget, &allocation);
2582
2583   attributes.x = 0;
2584   attributes.y = 0;
2585   attributes.width = allocation.width + (2 * border_width) +
2586     padding.left + padding.right;
2587   attributes.height = priv->requested_height - (2 * border_width) +
2588     padding.top + padding.bottom;
2589
2590   attributes.width = MAX (1, attributes.width);
2591   attributes.height = MAX (1, attributes.height);
2592
2593   priv->bin_window = gdk_window_new (priv->view_window,
2594                                      &attributes, attributes_mask);
2595   gtk_widget_register_window (widget, priv->bin_window);
2596
2597   children = GTK_MENU_SHELL (menu)->priv->children;
2598   while (children)
2599     {
2600       child = children->data;
2601       children = children->next;
2602
2603       gtk_widget_set_parent_window (child, priv->bin_window);
2604     }
2605
2606   gtk_style_context_set_background (context, priv->bin_window);
2607   gtk_style_context_set_background (context, priv->view_window);
2608   gtk_style_context_set_background (context, window);
2609
2610   if (GTK_MENU_SHELL (widget)->priv->active_menu_item)
2611     gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
2612                                   GTK_MENU_SHELL (widget)->priv->active_menu_item);
2613
2614   gdk_window_show (priv->bin_window);
2615   gdk_window_show (priv->view_window);
2616 }
2617
2618 static gboolean
2619 gtk_menu_focus (GtkWidget       *widget,
2620                 GtkDirectionType direction)
2621 {
2622   /* A menu or its menu items cannot have focus */
2623   return FALSE;
2624 }
2625
2626 /* See notes in gtk_menu_popup() for information
2627  * about the "grab transfer window"
2628  */
2629 static GdkWindow *
2630 menu_grab_transfer_window_get (GtkMenu *menu)
2631 {
2632   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2633   if (!window)
2634     {
2635       GdkWindowAttr attributes;
2636       gint attributes_mask;
2637
2638       attributes.x = -100;
2639       attributes.y = -100;
2640       attributes.width = 10;
2641       attributes.height = 10;
2642       attributes.window_type = GDK_WINDOW_TEMP;
2643       attributes.wclass = GDK_INPUT_ONLY;
2644       attributes.override_redirect = TRUE;
2645       attributes.event_mask = 0;
2646
2647       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
2648
2649       window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
2650                                &attributes, attributes_mask);
2651       gtk_widget_register_window (GTK_WIDGET (menu), window);
2652
2653       gdk_window_show (window);
2654
2655       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), window);
2656     }
2657
2658   return window;
2659 }
2660
2661 static void
2662 menu_grab_transfer_window_destroy (GtkMenu *menu)
2663 {
2664   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2665   if (window)
2666     {
2667       gtk_widget_unregister_window (GTK_WIDGET (menu), window);
2668       gdk_window_destroy (window);
2669       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
2670     }
2671 }
2672
2673 static void
2674 gtk_menu_unrealize (GtkWidget *widget)
2675 {
2676   GtkMenu *menu = GTK_MENU (widget);
2677   GtkMenuPrivate *priv = menu->priv;
2678
2679   menu_grab_transfer_window_destroy (menu);
2680
2681   gtk_widget_unregister_window (widget, priv->view_window);
2682   gdk_window_destroy (priv->view_window);
2683   priv->view_window = NULL;
2684
2685   gtk_widget_unregister_window (widget, priv->bin_window);
2686   gdk_window_destroy (priv->bin_window);
2687   priv->bin_window = NULL;
2688
2689   GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget);
2690 }
2691
2692 static gint
2693 calculate_line_heights (GtkMenu *menu,
2694                         gint     for_width,
2695                         guint  **ret_min_heights,
2696                         guint  **ret_nat_heights)
2697 {
2698   GtkBorder       padding;
2699   GtkMenuPrivate *priv;
2700   GtkMenuShell   *menu_shell;
2701   GtkWidget      *child, *widget;
2702   GList          *children;
2703   guint           border_width;
2704   guint           n_columns;
2705   gint            n_heights;
2706   guint          *min_heights;
2707   guint          *nat_heights;
2708   gint            avail_width;
2709
2710   priv         = menu->priv;
2711   widget       = GTK_WIDGET (menu);
2712   menu_shell   = GTK_MENU_SHELL (widget);
2713
2714   min_heights  = g_new0 (guint, gtk_menu_get_n_rows (menu));
2715   nat_heights  = g_new0 (guint, gtk_menu_get_n_rows (menu));
2716   n_heights    = gtk_menu_get_n_rows (menu);
2717   n_columns    = gtk_menu_get_n_columns (menu);
2718   avail_width  = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns;
2719
2720   get_menu_padding (widget, &padding);
2721
2722   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
2723   avail_width -= (border_width) * 2 + padding.left + padding.right;
2724
2725   for (children = menu_shell->priv->children; children; children = children->next)
2726     {
2727       gint part;
2728       gint toggle_size;
2729       gint l, r, t, b;
2730       gint child_min, child_nat;
2731
2732       child = children->data;
2733
2734       if (!gtk_widget_get_visible (child))
2735         continue;
2736
2737       get_effective_child_attach (child, &l, &r, &t, &b);
2738
2739       part = avail_width / (r - l);
2740
2741       gtk_widget_get_preferred_height_for_width (child, part,
2742                                                  &child_min, &child_nat);
2743
2744       gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
2745
2746       part = MAX (child_min, toggle_size) / (b - t);
2747       min_heights[t] = MAX (min_heights[t], part);
2748
2749       part = MAX (child_nat, toggle_size) / (b - t);
2750       nat_heights[t] = MAX (nat_heights[t], part);
2751     }
2752
2753   if (ret_min_heights)
2754     *ret_min_heights = min_heights;
2755   else
2756     g_free (min_heights);
2757
2758   if (ret_nat_heights)
2759     *ret_nat_heights = nat_heights;
2760   else
2761     g_free (nat_heights);
2762
2763   return n_heights;
2764 }
2765
2766 static void
2767 gtk_menu_size_allocate (GtkWidget     *widget,
2768                         GtkAllocation *allocation)
2769 {
2770   GtkMenu *menu;
2771   GtkMenuPrivate *priv;
2772   GtkMenuShell *menu_shell;
2773   GtkWidget *child;
2774   GtkAllocation child_allocation;
2775   GList *children;
2776   gint x, y, i;
2777   gint width, height;
2778   guint border_width;
2779   GtkBorder padding;
2780
2781   g_return_if_fail (GTK_IS_MENU (widget));
2782   g_return_if_fail (allocation != NULL);
2783
2784   menu = GTK_MENU (widget);
2785   menu_shell = GTK_MENU_SHELL (widget);
2786   priv = menu->priv;
2787
2788   gtk_widget_set_allocation (widget, allocation);
2789
2790   get_menu_padding (widget, &padding);
2791   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
2792
2793   g_free (priv->heights);
2794   priv->heights_length = calculate_line_heights (menu,
2795                                                  allocation->width,
2796                                                  &priv->heights,
2797                                                  NULL);
2798
2799   /* refresh our cached height request */
2800   priv->requested_height = (2 * border_width) + padding.top + padding.bottom;
2801   for (i = 0; i < priv->heights_length; i++)
2802     priv->requested_height += priv->heights[i];
2803
2804   x = border_width + padding.left;
2805   y = border_width + padding.top;
2806   width = allocation->width - (2 * border_width) -
2807     padding.left - padding.right;
2808   height = allocation->height - (2 * border_width) -
2809     padding.top - padding.bottom;
2810
2811   if (menu_shell->priv->active)
2812     gtk_menu_scroll_to (menu, priv->scroll_offset);
2813
2814   if (!priv->tearoff_active)
2815     {
2816       GtkBorder arrow_border;
2817
2818       get_arrows_border (menu, &arrow_border);
2819       y += arrow_border.top;
2820       height -= arrow_border.top;
2821       height -= arrow_border.bottom;
2822     }
2823
2824   width = MAX (1, width);
2825   height = MAX (1, height);
2826
2827   if (gtk_widget_get_realized (widget))
2828     {
2829       gdk_window_move_resize (gtk_widget_get_window (widget),
2830                               allocation->x, allocation->y,
2831                               allocation->width, allocation->height);
2832
2833       gdk_window_move_resize (priv->view_window, x, y, width, height);
2834     }
2835
2836   if (menu_shell->priv->children)
2837     {
2838       gint base_width = width / gtk_menu_get_n_columns (menu);
2839
2840       children = menu_shell->priv->children;
2841       while (children)
2842         {
2843           child = children->data;
2844           children = children->next;
2845
2846           if (gtk_widget_get_visible (child))
2847             {
2848               gint i;
2849               gint l, r, t, b;
2850
2851               get_effective_child_attach (child, &l, &r, &t, &b);
2852
2853               if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
2854                 {
2855                   guint tmp;
2856                   tmp = gtk_menu_get_n_columns (menu) - l;
2857                   l = gtk_menu_get_n_columns (menu) - r;
2858                   r = tmp;
2859                 }
2860
2861               child_allocation.width = (r - l) * base_width;
2862               child_allocation.height = 0;
2863               child_allocation.x = l * base_width;
2864               child_allocation.y = 0;
2865
2866               for (i = 0; i < b; i++)
2867                 {
2868                   if (i < t)
2869                     child_allocation.y += priv->heights[i];
2870                   else
2871                     child_allocation.height += priv->heights[i];
2872                 }
2873
2874               gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
2875                                                   priv->toggle_size);
2876
2877               gtk_widget_size_allocate (child, &child_allocation);
2878               gtk_widget_queue_draw (child);
2879             }
2880         }
2881
2882       /* Resize the item window */
2883       if (gtk_widget_get_realized (widget))
2884         {
2885           gint i;
2886           gint width, height;
2887
2888           height = 0;
2889           for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2890             height += priv->heights[i];
2891
2892           width = gtk_menu_get_n_columns (menu) * base_width;
2893           gdk_window_resize (priv->bin_window, width, height);
2894         }
2895
2896       if (priv->tearoff_active)
2897         {
2898           if (height >= priv->requested_height)
2899             {
2900               if (gtk_widget_get_visible (priv->tearoff_scrollbar))
2901                 {
2902                   gtk_widget_hide (priv->tearoff_scrollbar);
2903                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2904
2905                   gtk_menu_scroll_to (menu, 0);
2906                 }
2907             }
2908           else
2909             {
2910               gtk_adjustment_configure (priv->tearoff_adjustment,
2911                                         gtk_adjustment_get_value (priv->tearoff_adjustment),
2912                                         0,
2913                                         priv->requested_height,
2914                                         gtk_adjustment_get_step_increment (priv->tearoff_adjustment),
2915                                         gtk_adjustment_get_page_increment (priv->tearoff_adjustment),
2916                                         allocation->height);
2917
2918               if (!gtk_widget_get_visible (priv->tearoff_scrollbar))
2919                 {
2920                   gtk_widget_show (priv->tearoff_scrollbar);
2921                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2922                 }
2923             }
2924         }
2925     }
2926 }
2927
2928 static void
2929 get_arrows_visible_area (GtkMenu      *menu,
2930                          GdkRectangle *border,
2931                          GdkRectangle *upper,
2932                          GdkRectangle *lower,
2933                          gint         *arrow_space)
2934 {
2935   GtkArrowPlacement arrow_placement;
2936   GtkWidget *widget = GTK_WIDGET (menu);
2937   guint border_width;
2938   gint scroll_arrow_height;
2939   GtkBorder menu_padding;
2940
2941   gtk_widget_style_get (widget,
2942                         "scroll-arrow-vlength", &scroll_arrow_height,
2943                         "arrow-placement", &arrow_placement,
2944                         NULL);
2945
2946   get_menu_padding (widget, &menu_padding);
2947   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2948   border->x = border_width + menu_padding.left;
2949   border->y = border_width + menu_padding.top;
2950   border->width = gdk_window_get_width (gtk_widget_get_window (widget));
2951   border->height = gdk_window_get_height (gtk_widget_get_window (widget));
2952
2953   switch (arrow_placement)
2954     {
2955     case GTK_ARROWS_BOTH:
2956       upper->x = border->x;
2957       upper->y = border->y;
2958       upper->width = border->width - 2 * border->x;
2959       upper->height = scroll_arrow_height;
2960
2961       lower->x = border->x;
2962       lower->y = border->height - border->y - scroll_arrow_height;
2963       lower->width = border->width - 2 * border->x;
2964       lower->height = scroll_arrow_height;
2965       break;
2966
2967     case GTK_ARROWS_START:
2968       upper->x = border->x;
2969       upper->y = border->y;
2970       upper->width = (border->width - 2 * border->x) / 2;
2971       upper->height = scroll_arrow_height;
2972
2973       lower->x = border->x + upper->width;
2974       lower->y = border->y;
2975       lower->width = (border->width - 2 * border->x) / 2;
2976       lower->height = scroll_arrow_height;
2977       break;
2978
2979     case GTK_ARROWS_END:
2980       upper->x = border->x;
2981       upper->y = border->height - border->y - scroll_arrow_height;
2982       upper->width = (border->width - 2 * border->x) / 2;
2983       upper->height = scroll_arrow_height;
2984
2985       lower->x = border->x + upper->width;
2986       lower->y = border->height - border->y - scroll_arrow_height;
2987       lower->width = (border->width - 2 * border->x) / 2;
2988       lower->height = scroll_arrow_height;
2989       break;
2990
2991     default:
2992        g_assert_not_reached();
2993        upper->x = upper->y = upper->width = upper->height = 0;
2994        lower->x = lower->y = lower->width = lower->height = 0;
2995     }
2996
2997   *arrow_space = scroll_arrow_height - menu_padding.top - menu_padding.bottom;
2998 }
2999
3000 static gboolean
3001 gtk_menu_draw (GtkWidget *widget,
3002                cairo_t   *cr)
3003 {
3004   GtkMenu *menu;
3005   GtkMenuPrivate *priv;
3006   GtkStyleContext *context;
3007   GdkRectangle border;
3008   GdkRectangle upper;
3009   GdkRectangle lower;
3010   gint arrow_space;
3011   GtkBorder menu_padding;
3012
3013   menu = GTK_MENU (widget);
3014   priv = menu->priv;
3015   context = gtk_widget_get_style_context (widget);
3016
3017   get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
3018   get_menu_padding (widget, &menu_padding);
3019
3020   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
3021     {
3022       gfloat arrow_scaling;
3023       gint arrow_size;
3024
3025       gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
3026       arrow_size = arrow_scaling * arrow_space;
3027
3028       gtk_render_background (context, cr, 0, 0,
3029                              gtk_widget_get_allocated_width (widget),
3030                              gtk_widget_get_allocated_height (widget));
3031       gtk_render_frame (context, cr, 0, 0,
3032                         gtk_widget_get_allocated_width (widget),
3033                         gtk_widget_get_allocated_height (widget));
3034
3035       gtk_style_context_save (context);
3036       gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
3037
3038       if (priv->upper_arrow_visible && !priv->tearoff_active)
3039         {
3040           gtk_style_context_save (context);
3041           gtk_style_context_set_state (context, priv->upper_arrow_state);
3042
3043           gtk_render_background (context, cr,
3044                                  upper.x, upper.y,
3045                                  upper.width, upper.height);
3046           gtk_render_frame (context, cr,
3047                             upper.x, upper.y,
3048                             upper.width, upper.height);
3049
3050           gtk_render_arrow (context, cr, 0,
3051                             upper.x + (upper.width - arrow_size) / 2,
3052                             upper.y + menu_padding.top + (arrow_space - arrow_size) / 2,
3053                             arrow_size);
3054
3055           gtk_style_context_restore (context);
3056         }
3057
3058       if (priv->lower_arrow_visible && !priv->tearoff_active)
3059         {
3060           gtk_style_context_save (context);
3061           gtk_style_context_set_state (context, priv->lower_arrow_state);
3062
3063           gtk_render_background (context, cr,
3064                                  lower.x, lower.y,
3065                                  lower.width, lower.height);
3066           gtk_render_frame (context, cr,
3067                             lower.x, lower.y,
3068                             lower.width, lower.height);
3069
3070           gtk_render_arrow (context, cr, G_PI,
3071                             lower.x + (lower.width - arrow_size) / 2,
3072                             lower.y + menu_padding.bottom + (arrow_space - arrow_size) / 2,
3073                             arrow_size);
3074
3075           gtk_style_context_restore (context);
3076         }
3077
3078       gtk_style_context_restore (context);
3079     }
3080
3081   if (gtk_cairo_should_draw_window (cr, priv->bin_window))
3082     {
3083       gint y = -border.y + priv->scroll_offset;
3084
3085       cairo_save (cr);
3086       gtk_cairo_transform_to_window (cr, widget, priv->bin_window);
3087
3088       if (!priv->tearoff_active)
3089         {
3090           GtkBorder arrow_border;
3091
3092           get_arrows_border (menu, &arrow_border);
3093           y -= arrow_border.top;
3094         }
3095
3096       gtk_render_background (context, cr,
3097                              - border.x, y,
3098                              border.width, border.height);
3099       gtk_render_frame (context, cr,
3100                         - border.x, y,
3101                         border.width, border.height);
3102
3103       cairo_restore (cr);
3104     }
3105
3106   GTK_WIDGET_CLASS (gtk_menu_parent_class)->draw (widget, cr);
3107
3108   return FALSE;
3109 }
3110
3111 static void
3112 gtk_menu_show (GtkWidget *widget)
3113 {
3114   GtkMenu *menu = GTK_MENU (widget);
3115
3116   _gtk_menu_refresh_accel_paths (menu, FALSE);
3117
3118   GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget);
3119 }
3120
3121
3122 static void 
3123 gtk_menu_get_preferred_width (GtkWidget *widget,
3124                               gint      *minimum_size,
3125                               gint      *natural_size)
3126 {
3127   GtkMenu        *menu;
3128   GtkMenuShell   *menu_shell;
3129   GtkMenuPrivate *priv;
3130   GtkWidget      *child;
3131   GList          *children;
3132   guint           max_toggle_size;
3133   guint           max_accel_width;
3134   guint           border_width;
3135   gint            child_min, child_nat;
3136   gint            min_width, nat_width;
3137   GtkBorder       padding;
3138
3139   menu       = GTK_MENU (widget);
3140   menu_shell = GTK_MENU_SHELL (widget);
3141   priv       = menu->priv;
3142
3143   min_width = nat_width = 0;
3144
3145   max_toggle_size = 0;
3146   max_accel_width = 0;
3147
3148   children = menu_shell->priv->children;
3149   while (children)
3150     {
3151       gint part;
3152       gint toggle_size;
3153       gint l, r, t, b;
3154
3155       child = children->data;
3156       children = children->next;
3157
3158       if (! gtk_widget_get_visible (child))
3159         continue;
3160
3161       get_effective_child_attach (child, &l, &r, &t, &b);
3162
3163       /* It's important to size_request the child
3164        * before doing the toggle size request, in
3165        * case the toggle size request depends on the size
3166        * request of a child of the child (e.g. for ImageMenuItem)
3167        */
3168        gtk_widget_get_preferred_width (child, &child_min, &child_nat);
3169
3170        gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
3171        max_toggle_size = MAX (max_toggle_size, toggle_size);
3172        max_accel_width = MAX (max_accel_width,
3173                               GTK_MENU_ITEM (child)->priv->accelerator_width);
3174
3175        part = child_min / (r - l);
3176        min_width = MAX (min_width, part);
3177
3178        part = child_nat / (r - l);
3179        nat_width = MAX (nat_width, part);
3180     }
3181
3182   /* If the menu doesn't include any images or check items
3183    * reserve the space so that all menus are consistent.
3184    * We only do this for 'ordinary' menus, not for combobox
3185    * menus or multi-column menus
3186    */
3187   if (max_toggle_size == 0 &&
3188       gtk_menu_get_n_columns (menu) == 1 &&
3189       !priv->no_toggle_size)
3190     {
3191       GtkStyleContext *context;
3192       GtkWidgetPath *check_path;
3193       guint toggle_spacing;
3194       guint indicator_size;
3195
3196       context = gtk_style_context_new ();
3197
3198       /* Create a GtkCheckMenuItem path, only to query indicator spacing */
3199       check_path = _gtk_widget_create_path (widget);
3200       gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM);
3201
3202       gtk_style_context_set_path (context, check_path);
3203       gtk_widget_path_free (check_path);
3204       gtk_style_context_set_screen (context, gtk_widget_get_screen (widget));
3205
3206       gtk_style_context_get_style (context,
3207                                    "toggle-spacing", &toggle_spacing,
3208                                    "indicator-size", &indicator_size,
3209                                    NULL);
3210
3211       max_toggle_size = indicator_size + toggle_spacing;
3212
3213       g_object_unref (context);
3214     }
3215
3216   min_width += 2 * max_toggle_size + max_accel_width;
3217   min_width *= gtk_menu_get_n_columns (menu);
3218
3219   nat_width += 2 * max_toggle_size + max_accel_width;
3220   nat_width *= gtk_menu_get_n_columns (menu);
3221
3222   get_menu_padding (widget, &padding);
3223   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
3224   min_width   += (2 * border_width) + padding.left + padding.right;
3225   nat_width   += (2 * border_width) + padding.left + padding.right;
3226
3227   priv->toggle_size = max_toggle_size;
3228   priv->accel_size  = max_accel_width;
3229
3230   if (minimum_size)
3231     *minimum_size = min_width;
3232
3233   if (natural_size)
3234     *natural_size = nat_width;
3235
3236   /* Don't resize the tearoff if it is not active,
3237    * because it won't redraw (it is only a background pixmap).
3238    */
3239   if (priv->tearoff_active)
3240     gtk_menu_set_tearoff_hints (menu, min_width);
3241 }
3242
3243 static void
3244 gtk_menu_get_preferred_height (GtkWidget *widget,
3245                                gint      *minimum_size,
3246                                gint      *natural_size)
3247 {
3248   gint min_width;
3249
3250   /* Menus are height-for-width only, just return the height
3251    * for the minimum width
3252    */
3253   GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
3254   GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
3255 }
3256
3257 static void
3258 gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
3259                                          gint       for_size,
3260                                          gint      *minimum_size,
3261                                          gint      *natural_size)
3262 {
3263   GtkBorder       padding;
3264   GtkMenu        *menu = GTK_MENU (widget);
3265   GtkMenuPrivate *priv = menu->priv;
3266   guint          *min_heights, *nat_heights;
3267   guint           border_width;
3268   gint            n_heights, i;
3269   gint            min_height, nat_height;
3270
3271   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
3272   get_menu_padding (widget, &padding);
3273
3274   min_height = nat_height = (2 * border_width) + padding.top + padding.bottom;
3275
3276   n_heights =
3277     calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
3278
3279   for (i = 0; i < n_heights; i++)
3280     {
3281       min_height += min_heights[i];
3282       nat_height += nat_heights[i];
3283     }
3284
3285   if (priv->have_position)
3286     {
3287       GdkScreen *screen = gtk_widget_get_screen (priv->toplevel);
3288       GdkRectangle monitor;
3289
3290       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
3291
3292       if (priv->position_y + min_height > monitor.y + monitor.height)
3293         min_height = monitor.y + monitor.height - priv->position_y;
3294
3295       if (priv->position_y + nat_height > monitor.y + monitor.height)
3296         nat_height = monitor.y + monitor.height - priv->position_y;
3297
3298       if (priv->position_y < monitor.y)
3299         {
3300           min_height -= monitor.y - priv->position_y;
3301           nat_height -= monitor.y - priv->position_y;
3302         }
3303     }
3304
3305   if (minimum_size)
3306     *minimum_size = min_height;
3307
3308   if (natural_size)
3309     *natural_size = nat_height;
3310
3311   g_free (min_heights);
3312   g_free (nat_heights);
3313 }
3314
3315 static gboolean
3316 pointer_in_menu_window (GtkWidget *widget,
3317                         gdouble    x_root,
3318                         gdouble    y_root)
3319 {
3320   GtkMenu *menu = GTK_MENU (widget);
3321   GtkMenuPrivate *priv = menu->priv;
3322   GtkAllocation allocation;
3323
3324   if (gtk_widget_get_mapped (priv->toplevel))
3325     {
3326       GtkMenuShell *menu_shell;
3327       gint          window_x, window_y;
3328
3329       gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
3330                                &window_x, &window_y);
3331
3332       gtk_widget_get_allocation (widget, &allocation);
3333       if (x_root >= window_x && x_root < window_x + allocation.width &&
3334           y_root >= window_y && y_root < window_y + allocation.height)
3335         return TRUE;
3336
3337       menu_shell = GTK_MENU_SHELL (widget);
3338
3339       if (GTK_IS_MENU (menu_shell->priv->parent_menu_shell))
3340         return pointer_in_menu_window (menu_shell->priv->parent_menu_shell,
3341                                        x_root, y_root);
3342     }
3343
3344   return FALSE;
3345 }
3346
3347 static gboolean
3348 gtk_menu_button_press (GtkWidget      *widget,
3349                        GdkEventButton *event)
3350 {
3351   GdkDevice *source_device;
3352   GtkWidget *event_widget;
3353   GtkMenu *menu;
3354
3355   if (event->type != GDK_BUTTON_PRESS)
3356     return FALSE;
3357
3358   source_device = gdk_event_get_source_device ((GdkEvent *) event);
3359   event_widget = gtk_get_event_widget ((GdkEvent *) event);
3360   menu = GTK_MENU (widget);
3361
3362   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3363    *  was clicked. The check for the event_widget being a GtkMenuShell
3364    *  works because we have the pointer grabbed on menu_shell->window
3365    *  with owner_events=TRUE, so all events that are either outside
3366    *  the menu or on its border are delivered relative to
3367    *  menu_shell->window.
3368    */
3369   if (GTK_IS_MENU_SHELL (event_widget) &&
3370       pointer_in_menu_window (widget, event->x_root, event->y_root))
3371     return TRUE;
3372
3373   if (GTK_IS_MENU_ITEM (event_widget) &&
3374       gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
3375       GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
3376       !gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
3377     menu->priv->ignore_button_release = TRUE;
3378
3379   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
3380 }
3381
3382 static gboolean
3383 gtk_menu_button_release (GtkWidget      *widget,
3384                          GdkEventButton *event)
3385 {
3386   GtkMenuPrivate *priv = GTK_MENU (widget)->priv;
3387
3388   if (priv->ignore_button_release)
3389     {
3390       priv->ignore_button_release = FALSE;
3391       return FALSE;
3392     }
3393
3394   if (event->type != GDK_BUTTON_RELEASE)
3395     return FALSE;
3396
3397   /*  Don't pass down to menu shell if a non-menuitem part of the menu
3398    *  was clicked (see comment in button_press()).
3399    */
3400   if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
3401       pointer_in_menu_window (widget, event->x_root, event->y_root))
3402     {
3403       /*  Ugly: make sure menu_shell->button gets reset to 0 when we
3404        *  bail out early here so it is in a consistent state for the
3405        *  next button_press/button_release in GtkMenuShell.
3406        *  See bug #449371.
3407        */
3408       if (GTK_MENU_SHELL (widget)->priv->active)
3409         GTK_MENU_SHELL (widget)->priv->button = 0;
3410
3411       return TRUE;
3412     }
3413
3414   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_release_event (widget, event);
3415 }
3416
3417 static const gchar *
3418 get_accel_path (GtkWidget *menu_item,
3419                 gboolean  *locked)
3420 {
3421   const gchar *path;
3422   GtkWidget *label;
3423   GClosure *accel_closure;
3424   GtkAccelGroup *accel_group;
3425
3426   path = _gtk_widget_get_accel_path (menu_item, locked);
3427   if (!path)
3428     {
3429       path = GTK_MENU_ITEM (menu_item)->priv->accel_path;
3430
3431       if (locked)
3432         {
3433           *locked = TRUE;
3434
3435           label = gtk_bin_get_child (GTK_BIN (menu_item));
3436
3437           if (GTK_IS_ACCEL_LABEL (label))
3438             {
3439               g_object_get (label,
3440                             "accel-closure", &accel_closure,
3441                             NULL);
3442               if (accel_closure)
3443                 {
3444                   accel_group = gtk_accel_group_from_accel_closure (accel_closure);
3445
3446                   *locked = gtk_accel_group_get_is_locked (accel_group);
3447                 }
3448             }
3449         }
3450     }
3451
3452   return path;
3453 }
3454
3455 static gboolean
3456 gtk_menu_key_press (GtkWidget   *widget,
3457                     GdkEventKey *event)
3458 {
3459   GtkMenuShell *menu_shell;
3460   GtkMenu *menu;
3461   gboolean delete = FALSE;
3462   gboolean can_change_accels;
3463   gchar *accel = NULL;
3464   guint accel_key, accel_mods;
3465   GdkModifierType consumed_modifiers;
3466   GdkDisplay *display;
3467
3468   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
3469   g_return_val_if_fail (event != NULL, FALSE);
3470
3471   menu_shell = GTK_MENU_SHELL (widget);
3472   menu = GTK_MENU (widget);
3473
3474   gtk_menu_stop_navigating_submenu (menu);
3475
3476   if (GTK_WIDGET_CLASS (gtk_menu_parent_class)->key_press_event (widget, event))
3477     return TRUE;
3478
3479   display = gtk_widget_get_display (widget);
3480
3481   g_object_get (gtk_widget_get_settings (widget),
3482                 "gtk-menu-bar-accel", &accel,
3483                 "gtk-can-change-accels", &can_change_accels,
3484                 NULL);
3485
3486   if (accel && *accel)
3487     {
3488       guint keyval = 0;
3489       GdkModifierType mods = 0;
3490
3491       gtk_accelerator_parse (accel, &keyval, &mods);
3492
3493       if (keyval == 0)
3494         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
3495
3496       /* FIXME this is wrong, needs to be in the global accel resolution
3497        * thing, to properly consider i18n etc., but that probably requires
3498        * AccelGroup changes etc.
3499        */
3500       if (event->keyval == keyval && (mods & event->state) == mods)
3501         {
3502           gtk_menu_shell_cancel (menu_shell);
3503           g_free (accel);
3504           return TRUE;
3505         }
3506     }
3507
3508   g_free (accel);
3509
3510   switch (event->keyval)
3511     {
3512     case GDK_KEY_Delete:
3513     case GDK_KEY_KP_Delete:
3514     case GDK_KEY_BackSpace:
3515       delete = TRUE;
3516       break;
3517     default:
3518       break;
3519     }
3520
3521   /* Figure out what modifiers went into determining the key symbol */
3522   _gtk_translate_keyboard_accel_state (gdk_keymap_get_for_display (display),
3523                                        event->hardware_keycode,
3524                                        event->state,
3525                                        gtk_accelerator_get_default_mod_mask (),
3526                                        event->group,
3527                                        &accel_key, NULL, NULL, &consumed_modifiers);
3528
3529   accel_key = gdk_keyval_to_lower (accel_key);
3530   accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
3531
3532   /* If lowercasing affects the keysym, then we need to include SHIFT
3533    * in the modifiers, We re-upper case when we match against the
3534    * keyval, but display and save in caseless form.
3535    */
3536   if (accel_key != event->keyval)
3537     accel_mods |= GDK_SHIFT_MASK;
3538
3539   /* Modify the accelerators */
3540   if (can_change_accels &&
3541       menu_shell->priv->active_menu_item &&
3542       gtk_bin_get_child (GTK_BIN (menu_shell->priv->active_menu_item)) && /* no separators */
3543       GTK_MENU_ITEM (menu_shell->priv->active_menu_item)->priv->submenu == NULL &&  /* no submenus */
3544       (delete || gtk_accelerator_valid (accel_key, accel_mods)))
3545     {
3546       GtkWidget *menu_item = menu_shell->priv->active_menu_item;
3547       gboolean locked, replace_accels = TRUE;
3548       const gchar *path;
3549
3550       path = get_accel_path (menu_item, &locked);
3551       if (!path || locked)
3552         {
3553           /* Can't change accelerators on menu_items without paths
3554            * (basically, those items are accelerator-locked).
3555            */
3556           gtk_widget_error_bell (widget);
3557         }
3558       else
3559         {
3560           gboolean changed;
3561
3562           /* For the keys that act to delete the current setting,
3563            * we delete the current setting if there is one, otherwise,
3564            * we set the key as the accelerator.
3565            */
3566           if (delete)
3567             {
3568               GtkAccelKey key;
3569
3570               if (gtk_accel_map_lookup_entry (path, &key) &&
3571                   (key.accel_key || key.accel_mods))
3572                 {
3573                   accel_key = 0;
3574                   accel_mods = 0;
3575                 }
3576             }
3577           changed = gtk_accel_map_change_entry (path, accel_key, accel_mods, replace_accels);
3578
3579           if (!changed)
3580             {
3581               /* We failed, probably because this key is in use
3582                * and locked already.
3583                */
3584               gtk_widget_error_bell (widget);
3585             }
3586         }
3587     }
3588
3589   return TRUE;
3590 }
3591
3592 static gboolean
3593 check_threshold (GtkWidget *widget,
3594                  gint       start_x,
3595                  gint       start_y,
3596                  gint       x,
3597                  gint       y)
3598 {
3599 #define THRESHOLD 8
3600
3601   return
3602     ABS (start_x - x) > THRESHOLD ||
3603     ABS (start_y - y) > THRESHOLD;
3604 }
3605
3606 static gboolean
3607 definitely_within_item (GtkWidget *widget,
3608                         gint       x,
3609                         gint       y)
3610 {
3611   GdkWindow *window = GTK_MENU_ITEM (widget)->priv->event_window;
3612   int w, h;
3613
3614   w = gdk_window_get_width (window);
3615   h = gdk_window_get_height (window);
3616
3617   return
3618     check_threshold (widget, 0, 0, x, y) &&
3619     check_threshold (widget, w - 1, 0, x, y) &&
3620     check_threshold (widget, w - 1, h - 1, x, y) &&
3621     check_threshold (widget, 0, h - 1, x, y);
3622 }
3623
3624 static gboolean
3625 gtk_menu_has_navigation_triangle (GtkMenu *menu)
3626 {
3627   GtkMenuPrivate *priv = menu->priv;
3628
3629   return priv->navigation_height && priv->navigation_width;
3630 }
3631
3632 static gboolean
3633 gtk_menu_motion_notify (GtkWidget      *widget,
3634                         GdkEventMotion *event)
3635 {
3636   GtkWidget *menu_item;
3637   GtkMenu *menu;
3638   GtkMenuShell *menu_shell;
3639   GtkWidget *parent;
3640   GdkDevice *source_device;
3641
3642   gboolean need_enter;
3643
3644   source_device = gdk_event_get_source_device ((GdkEvent *) event);
3645
3646   if (GTK_IS_MENU (widget) &&
3647       gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
3648     {
3649       GtkMenuPrivate *priv = GTK_MENU(widget)->priv;
3650
3651       if (priv->ignore_button_release)
3652         priv->ignore_button_release = FALSE;
3653
3654       gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root,
3655                                  TRUE, TRUE);
3656     }
3657
3658   /* We received the event for one of two reasons:
3659    *
3660    * a) We are the active menu, and did gtk_grab_add()
3661    * b) The widget is a child of ours, and the event was propagated
3662    *
3663    * Since for computation of navigation regions, we want the menu which
3664    * is the parent of the menu item, for a), we need to find that menu,
3665    * which may be different from 'widget'.
3666    */
3667   menu_item = gtk_get_event_widget ((GdkEvent*) event);
3668   parent = gtk_widget_get_parent (menu_item);
3669   if (!GTK_IS_MENU_ITEM (menu_item) ||
3670       !GTK_IS_MENU (parent))
3671     return FALSE;
3672
3673   menu_shell = GTK_MENU_SHELL (parent);
3674   menu = GTK_MENU (menu_shell);
3675
3676   if (definitely_within_item (menu_item, event->x, event->y))
3677     menu_shell->priv->activate_time = 0;
3678
3679   need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->priv->ignore_enter);
3680
3681   /* Check to see if we are within an active submenu's navigation region
3682    */
3683   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
3684     return TRUE;
3685
3686   /* Make sure we pop down if we enter a non-selectable menu item, so we
3687    * don't show a submenu when the cursor is outside the stay-up triangle.
3688    */
3689   if (!_gtk_menu_item_is_selectable (menu_item))
3690     {
3691       /* We really want to deselect, but this gives the menushell code
3692        * a chance to do some bookkeeping about the menuitem.
3693        */
3694       gtk_menu_shell_select_item (menu_shell, menu_item);
3695       return FALSE;
3696     }
3697
3698   if (need_enter)
3699     {
3700       /* The menu is now sensitive to enter events on its items, but
3701        * was previously sensitive.  So we fake an enter event.
3702        */
3703       menu_shell->priv->ignore_enter = FALSE;
3704
3705       if (event->x >= 0 && event->x < gdk_window_get_width (event->window) &&
3706           event->y >= 0 && event->y < gdk_window_get_height (event->window))
3707         {
3708           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
3709           gboolean result;
3710
3711           send_event->crossing.window = g_object_ref (event->window);
3712           send_event->crossing.time = event->time;
3713           send_event->crossing.send_event = TRUE;
3714           send_event->crossing.x_root = event->x_root;
3715           send_event->crossing.y_root = event->y_root;
3716           send_event->crossing.x = event->x;
3717           send_event->crossing.y = event->y;
3718           send_event->crossing.state = event->state;
3719           gdk_event_set_device (send_event, gdk_event_get_device ((GdkEvent *) event));
3720
3721           /* We send the event to 'widget', the currently active menu,
3722            * instead of 'menu', the menu that the pointer is in. This
3723            * will ensure that the event will be ignored unless the
3724            * menuitem is a child of the active menu or some parent
3725            * menu of the active menu.
3726            */
3727           result = gtk_widget_event (widget, send_event);
3728           gdk_event_free (send_event);
3729
3730           return result;
3731         }
3732     }
3733
3734   return FALSE;
3735 }
3736
3737 static gboolean
3738 get_double_arrows (GtkMenu *menu)
3739 {
3740   GtkMenuPrivate   *priv = menu->priv;
3741   gboolean          double_arrows;
3742   GtkArrowPlacement arrow_placement;
3743
3744   gtk_widget_style_get (GTK_WIDGET (menu),
3745                         "double-arrows", &double_arrows,
3746                         "arrow-placement", &arrow_placement,
3747                         NULL);
3748
3749   if (arrow_placement != GTK_ARROWS_BOTH)
3750     return TRUE;
3751
3752   return double_arrows || (priv->initially_pushed_in &&
3753                            priv->scroll_offset != 0);
3754 }
3755
3756 static void
3757 gtk_menu_scroll_by (GtkMenu *menu,
3758                     gint     step)
3759 {
3760   GtkMenuPrivate *priv = menu->priv;
3761   GtkBorder arrow_border;
3762   GtkWidget *widget;
3763   gint offset;
3764   gint view_height;
3765   gboolean double_arrows;
3766
3767   widget = GTK_WIDGET (menu);
3768   offset = priv->scroll_offset + step;
3769
3770   get_arrows_border (menu, &arrow_border);
3771
3772   double_arrows = get_double_arrows (menu);
3773
3774   /* If we scroll upward and the non-visible top part
3775    * is smaller than the scroll arrow it would be
3776    * pretty stupid to show the arrow and taking more
3777    * screen space than just scrolling to the top.
3778    */
3779   if (!double_arrows)
3780     if ((step < 0) && (offset < arrow_border.top))
3781       offset = 0;
3782
3783   /* Don't scroll over the top if we weren't before: */
3784   if ((priv->scroll_offset >= 0) && (offset < 0))
3785     offset = 0;
3786
3787   view_height = gdk_window_get_height (gtk_widget_get_window (widget));
3788
3789   if (priv->scroll_offset == 0 &&
3790       view_height >= priv->requested_height)
3791     return;
3792
3793   /* Don't scroll past the bottom if we weren't before: */
3794   if (priv->scroll_offset > 0)
3795     view_height -= arrow_border.top;
3796
3797   /* When both arrows are always shown,
3798    * reduce view height even more.
3799    */
3800   if (double_arrows)
3801     view_height -= arrow_border.bottom;
3802
3803   if ((priv->scroll_offset + view_height <= priv->requested_height) &&
3804       (offset + view_height > priv->requested_height))
3805     offset = priv->requested_height - view_height;
3806
3807   if (offset != priv->scroll_offset)
3808     gtk_menu_scroll_to (menu, offset);
3809 }
3810
3811 static gboolean
3812 gtk_menu_scroll_timeout (gpointer data)
3813 {
3814   GtkMenu  *menu;
3815
3816   menu = GTK_MENU (data);
3817   gtk_menu_scroll_by (menu, menu->priv->scroll_step);
3818
3819   return TRUE;
3820 }
3821
3822 static gboolean
3823 gtk_menu_scroll (GtkWidget      *widget,
3824                  GdkEventScroll *event)
3825 {
3826   GtkMenu *menu = GTK_MENU (widget);
3827
3828   switch (event->direction)
3829     {
3830     case GDK_SCROLL_RIGHT:
3831     case GDK_SCROLL_DOWN:
3832       gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
3833       break;
3834     case GDK_SCROLL_LEFT:
3835     case GDK_SCROLL_UP:
3836       gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
3837       break;
3838     case GDK_SCROLL_SMOOTH:
3839       gtk_menu_scroll_by (menu, event->delta_y);
3840       break;
3841     }
3842
3843   return TRUE;
3844 }
3845
3846 static void
3847 get_arrows_sensitive_area (GtkMenu      *menu,
3848                            GdkRectangle *upper,
3849                            GdkRectangle *lower)
3850 {
3851   GtkArrowPlacement arrow_placement;
3852   GtkWidget *widget = GTK_WIDGET (menu);
3853   GdkWindow *window;
3854   gint width, height;
3855   guint border;
3856   gint win_x, win_y;
3857   gint scroll_arrow_height;
3858   GtkBorder padding;
3859
3860   window = gtk_widget_get_window (widget);
3861   width = gdk_window_get_width (window);
3862   height = gdk_window_get_height (window);
3863
3864   gtk_widget_style_get (widget,
3865                         "scroll-arrow-vlength", &scroll_arrow_height,
3866                         "arrow-placement", &arrow_placement,
3867                         NULL);
3868
3869   border = gtk_container_get_border_width (GTK_CONTAINER (menu));
3870   get_menu_padding (widget, &padding);
3871
3872   gdk_window_get_position (window, &win_x, &win_y);
3873
3874   switch (arrow_placement)
3875     {
3876     case GTK_ARROWS_BOTH:
3877       if (upper)
3878         {
3879           upper->x = win_x;
3880           upper->y = win_y;
3881           upper->width = width;
3882           upper->height = scroll_arrow_height + border + padding.top;
3883         }
3884
3885       if (lower)
3886         {
3887           lower->x = win_x;
3888           lower->y = win_y + height - border - padding.bottom - scroll_arrow_height;
3889           lower->width = width;
3890           lower->height = scroll_arrow_height + border + padding.bottom;
3891         }
3892       break;
3893
3894     case GTK_ARROWS_START:
3895       if (upper)
3896         {
3897           upper->x = win_x;
3898           upper->y = win_y;
3899           upper->width = width / 2;
3900           upper->height = scroll_arrow_height + border + padding.top;
3901         }
3902
3903       if (lower)
3904         {
3905           lower->x = win_x + width / 2;
3906           lower->y = win_y;
3907           lower->width = width / 2;
3908           lower->height = scroll_arrow_height + border + padding.bottom;
3909         }
3910       break;
3911
3912     case GTK_ARROWS_END:
3913       if (upper)
3914         {
3915           upper->x = win_x;
3916           upper->y = win_y + height - border - scroll_arrow_height;
3917           upper->width = width / 2;
3918           upper->height = scroll_arrow_height + border + padding.top;
3919         }
3920
3921       if (lower)
3922         {
3923           lower->x = win_x + width / 2;
3924           lower->y = win_y + height - border - scroll_arrow_height;
3925           lower->width = width / 2;
3926           lower->height = scroll_arrow_height + border + padding.bottom;
3927         }
3928       break;
3929     }
3930 }
3931
3932
3933 static void
3934 gtk_menu_handle_scrolling (GtkMenu *menu,
3935                            gint     x,
3936                            gint     y,
3937                            gboolean enter,
3938                            gboolean motion)
3939 {
3940   GtkMenuPrivate *priv = menu->priv;
3941   GtkMenuShell *menu_shell;
3942   GdkRectangle rect;
3943   gboolean in_arrow;
3944   gboolean scroll_fast = FALSE;
3945   gint top_x, top_y;
3946
3947   menu_shell = GTK_MENU_SHELL (menu);
3948
3949   gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
3950                            &top_x, &top_y);
3951   x -= top_x;
3952   y -= top_y;
3953
3954   /*  upper arrow handling  */
3955
3956   get_arrows_sensitive_area (menu, &rect, NULL);
3957
3958   in_arrow = FALSE;
3959   if (priv->upper_arrow_visible && !priv->tearoff_active &&
3960       (x >= rect.x) && (x < rect.x + rect.width) &&
3961       (y >= rect.y) && (y < rect.y + rect.height))
3962     {
3963       in_arrow = TRUE;
3964     }
3965
3966   if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
3967     {
3968       gboolean arrow_pressed = FALSE;
3969
3970       if (priv->upper_arrow_visible && !priv->tearoff_active)
3971         {
3972           scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
3973
3974           if (enter && in_arrow &&
3975               (!priv->upper_arrow_prelight ||
3976                priv->scroll_fast != scroll_fast))
3977             {
3978               priv->upper_arrow_prelight = TRUE;
3979               priv->scroll_fast = scroll_fast;
3980
3981               /* Deselect the active item so that
3982                * any submenus are popped down
3983                */
3984               gtk_menu_shell_deselect (menu_shell);
3985
3986               gtk_menu_remove_scroll_timeout (menu);
3987               priv->scroll_step = scroll_fast
3988                                     ? -MENU_SCROLL_STEP2
3989                                     : -MENU_SCROLL_STEP1;
3990
3991               priv->scroll_timeout =
3992                 gdk_threads_add_timeout (scroll_fast
3993                                            ? MENU_SCROLL_TIMEOUT2
3994                                            : MENU_SCROLL_TIMEOUT1,
3995                                          gtk_menu_scroll_timeout, menu);
3996             }
3997           else if (!enter && !in_arrow && priv->upper_arrow_prelight)
3998             {
3999               gtk_menu_stop_scrolling (menu);
4000             }
4001         }
4002
4003       /*  check if the button isn't insensitive before
4004        *  changing it to something else.
4005        */
4006       if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4007         {
4008           GtkStateFlags arrow_state = 0;
4009
4010           if (arrow_pressed)
4011             arrow_state |= GTK_STATE_FLAG_ACTIVE;
4012
4013           if (priv->upper_arrow_prelight)
4014             arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4015
4016           if (arrow_state != priv->upper_arrow_state)
4017             {
4018               priv->upper_arrow_state = arrow_state;
4019
4020               gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)),
4021                                           &rect, FALSE);
4022             }
4023         }
4024     }
4025
4026   /*  lower arrow handling  */
4027
4028   get_arrows_sensitive_area (menu, NULL, &rect);
4029
4030   in_arrow = FALSE;
4031   if (priv->lower_arrow_visible && !priv->tearoff_active &&
4032       (x >= rect.x) && (x < rect.x + rect.width) &&
4033       (y >= rect.y) && (y < rect.y + rect.height))
4034     {
4035       in_arrow = TRUE;
4036     }
4037
4038   if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4039     {
4040       gboolean arrow_pressed = FALSE;
4041
4042       if (priv->lower_arrow_visible && !priv->tearoff_active)
4043         {
4044           scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
4045
4046           if (enter && in_arrow &&
4047               (!priv->lower_arrow_prelight ||
4048                priv->scroll_fast != scroll_fast))
4049             {
4050               priv->lower_arrow_prelight = TRUE;
4051               priv->scroll_fast = scroll_fast;
4052
4053               /* Deselect the active item so that
4054                * any submenus are popped down
4055                */
4056               gtk_menu_shell_deselect (menu_shell);
4057
4058               gtk_menu_remove_scroll_timeout (menu);
4059               priv->scroll_step = scroll_fast
4060                                     ? MENU_SCROLL_STEP2
4061                                     : MENU_SCROLL_STEP1;
4062
4063               priv->scroll_timeout =
4064                 gdk_threads_add_timeout (scroll_fast
4065                                            ? MENU_SCROLL_TIMEOUT2
4066                                            : MENU_SCROLL_TIMEOUT1,
4067                                          gtk_menu_scroll_timeout, menu);
4068             }
4069           else if (!enter && !in_arrow && priv->lower_arrow_prelight)
4070             {
4071               gtk_menu_stop_scrolling (menu);
4072             }
4073         }
4074
4075       /*  check if the button isn't insensitive before
4076        *  changing it to something else.
4077        */
4078       if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
4079         {
4080           GtkStateFlags arrow_state = 0;
4081
4082           if (arrow_pressed)
4083             arrow_state |= GTK_STATE_FLAG_ACTIVE;
4084
4085           if (priv->lower_arrow_prelight)
4086             arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4087
4088           if (arrow_state != priv->lower_arrow_state)
4089             {
4090               priv->lower_arrow_state = arrow_state;
4091
4092               gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)),
4093                                           &rect, FALSE);
4094             }
4095         }
4096     }
4097 }
4098
4099 static gboolean
4100 gtk_menu_enter_notify (GtkWidget        *widget,
4101                        GdkEventCrossing *event)
4102 {
4103   GtkWidget *menu_item;
4104   GtkWidget *parent;
4105   GdkDevice *source_device;
4106
4107   if (event->mode == GDK_CROSSING_GTK_GRAB ||
4108       event->mode == GDK_CROSSING_GTK_UNGRAB ||
4109       event->mode == GDK_CROSSING_STATE_CHANGED)
4110     return TRUE;
4111
4112   source_device = gdk_event_get_source_device ((GdkEvent *) event);
4113   menu_item = gtk_get_event_widget ((GdkEvent*) event);
4114
4115   if (GTK_IS_MENU (widget) &&
4116       gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
4117     {
4118       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
4119
4120       if (!menu_shell->priv->ignore_enter)
4121         gtk_menu_handle_scrolling (GTK_MENU (widget),
4122                                    event->x_root, event->y_root, TRUE, TRUE);
4123     }
4124
4125   if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN &&
4126       GTK_IS_MENU_ITEM (menu_item))
4127     {
4128       GtkWidget *menu = gtk_widget_get_parent (menu_item);
4129
4130       if (GTK_IS_MENU (menu))
4131         {
4132           GtkMenuPrivate *priv = (GTK_MENU (menu))->priv;
4133           GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
4134
4135           if (priv->seen_item_enter)
4136             {
4137               /* This is the second enter we see for an item
4138                * on this menu. This means a release should always
4139                * mean activate.
4140                */
4141               menu_shell->priv->activate_time = 0;
4142             }
4143           else if ((event->detail != GDK_NOTIFY_NONLINEAR &&
4144                     event->detail != GDK_NOTIFY_NONLINEAR_VIRTUAL))
4145             {
4146               if (definitely_within_item (menu_item, event->x, event->y))
4147                 {
4148                   /* This is an actual user-enter (ie. not a pop-under)
4149                    * In this case, the user must either have entered
4150                    * sufficiently far enough into the item, or he must move
4151                    * far enough away from the enter point. (see
4152                    * gtk_menu_motion_notify())
4153                    */
4154                   menu_shell->priv->activate_time = 0;
4155                 }
4156             }
4157
4158           priv->seen_item_enter = TRUE;
4159         }
4160     }
4161
4162   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
4163    * will not correspond to the event widget's parent.  Check to see
4164    * if we are in the parent's navigation region.
4165    */
4166   parent = gtk_widget_get_parent (menu_item);
4167   if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (parent) &&
4168       gtk_menu_navigating_submenu (GTK_MENU (parent),
4169                                    event->x_root, event->y_root))
4170     return TRUE;
4171
4172   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (widget, event);
4173 }
4174
4175 static gboolean
4176 gtk_menu_leave_notify (GtkWidget        *widget,
4177                        GdkEventCrossing *event)
4178 {
4179   GtkMenuShell *menu_shell;
4180   GtkMenu *menu;
4181   GtkMenuItem *menu_item;
4182   GtkWidget *event_widget;
4183   GdkDevice *source_device;
4184
4185   if (event->mode == GDK_CROSSING_GTK_GRAB ||
4186       event->mode == GDK_CROSSING_GTK_UNGRAB ||
4187       event->mode == GDK_CROSSING_STATE_CHANGED)
4188     return TRUE;
4189
4190   menu = GTK_MENU (widget);
4191   menu_shell = GTK_MENU_SHELL (widget);
4192
4193   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
4194     return TRUE;
4195
4196   source_device = gdk_event_get_source_device ((GdkEvent *) event);
4197
4198   if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN)
4199     gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
4200
4201   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4202
4203   if (!GTK_IS_MENU_ITEM (event_widget))
4204     return TRUE;
4205
4206   menu_item = GTK_MENU_ITEM (event_widget);
4207
4208   /* Here we check to see if we're leaving an active menu item
4209    * with a submenu, in which case we enter submenu navigation mode.
4210    */
4211   if (menu_shell->priv->active_menu_item != NULL
4212       && menu_item->priv->submenu != NULL
4213       && menu_item->priv->submenu_placement == GTK_LEFT_RIGHT)
4214     {
4215       if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active)
4216         {
4217           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
4218           return TRUE;
4219         }
4220       else if (menu_item == GTK_MENU_ITEM (menu_shell->priv->active_menu_item))
4221         {
4222           /* We are leaving an active menu item with nonactive submenu.
4223            * Deselect it so we don't surprise the user with by popping
4224            * up a submenu _after_ he left the item.
4225            */
4226           gtk_menu_shell_deselect (menu_shell);
4227           return TRUE;
4228         }
4229     }
4230
4231   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event);
4232 }
4233
4234 static gboolean
4235 pointer_on_menu_widget (GtkMenu *menu,
4236                         gdouble  x_root,
4237                         gdouble  y_root)
4238 {
4239   GtkMenuPrivate *priv = menu->priv;
4240   GtkAllocation allocation;
4241   gint window_x, window_y;
4242
4243   gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
4244   gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
4245                            &window_x, &window_y);
4246
4247   if (x_root >= window_x && x_root < window_x + allocation.width &&
4248       y_root >= window_y && y_root < window_y + allocation.height)
4249     return TRUE;
4250
4251   return FALSE;
4252 }
4253
4254 static gboolean
4255 gtk_menu_captured_event (GtkWidget *widget,
4256                          GdkEvent  *event)
4257 {
4258   GdkDevice *source_device;
4259   gboolean retval = FALSE;
4260   GtkMenuPrivate *priv;
4261   GtkMenu *menu;
4262   gdouble x_root, y_root;
4263   guint button;
4264   GdkModifierType state;
4265
4266   menu = GTK_MENU (widget);
4267   priv = menu->priv;
4268
4269   if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
4270     return retval;
4271
4272   source_device = gdk_event_get_source_device (event);
4273   gdk_event_get_root_coords (event, &x_root, &y_root);
4274
4275   switch (event->type)
4276     {
4277     case GDK_TOUCH_BEGIN:
4278     case GDK_BUTTON_PRESS:
4279       if ((!gdk_event_get_button (event, &button) || button == 1) &&
4280           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
4281           pointer_on_menu_widget (menu, x_root, y_root))
4282         {
4283           priv->drag_start_y = event->button.y_root;
4284           priv->initial_drag_offset = priv->scroll_offset;
4285           priv->drag_scroll_started = FALSE;
4286         }
4287       else
4288         priv->drag_start_y = -1;
4289
4290       priv->drag_already_pressed = TRUE;
4291       break;
4292     case GDK_TOUCH_END:
4293     case GDK_BUTTON_RELEASE:
4294       if (priv->drag_scroll_started)
4295         {
4296           priv->drag_scroll_started = FALSE;
4297           priv->drag_start_y = -1;
4298           priv->drag_already_pressed = FALSE;
4299           retval = TRUE;
4300         }
4301       break;
4302     case GDK_TOUCH_UPDATE:
4303     case GDK_MOTION_NOTIFY:
4304       if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) != 0) &&
4305           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
4306         {
4307           if (!priv->drag_already_pressed)
4308             {
4309               if (pointer_on_menu_widget (menu, x_root, y_root))
4310                 {
4311                   priv->drag_start_y = y_root;
4312                   priv->initial_drag_offset = priv->scroll_offset;
4313                   priv->drag_scroll_started = FALSE;
4314                 }
4315               else
4316                 priv->drag_start_y = -1;
4317
4318               priv->drag_already_pressed = TRUE;
4319             }
4320
4321           if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
4322             break;
4323
4324           if (priv->drag_scroll_started)
4325             {
4326               gint offset, view_height;
4327               GtkBorder arrow_border;
4328               gdouble y_diff;
4329
4330               y_diff = y_root - priv->drag_start_y;
4331               offset = priv->initial_drag_offset - y_diff;
4332
4333               view_height = gdk_window_get_height (gtk_widget_get_window (widget));
4334               get_arrows_border (menu, &arrow_border);
4335
4336               if (priv->upper_arrow_visible)
4337                 view_height -= arrow_border.top;
4338
4339               if (priv->lower_arrow_visible)
4340                 view_height -= arrow_border.bottom;
4341
4342               offset = CLAMP (offset,
4343                               MIN (priv->scroll_offset, 0),
4344                               MAX (priv->scroll_offset, priv->requested_height - view_height));
4345
4346               gtk_menu_scroll_to (menu, offset);
4347
4348               retval = TRUE;
4349             }
4350           else if (gtk_drag_check_threshold (widget,
4351                                              0, priv->drag_start_y,
4352                                              0, y_root))
4353             {
4354               priv->drag_scroll_started = TRUE;
4355               gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
4356               retval = TRUE;
4357             }
4358         }
4359       break;
4360     case GDK_ENTER_NOTIFY:
4361     case GDK_LEAVE_NOTIFY:
4362       if (priv->drag_scroll_started)
4363         retval = TRUE;
4364       break;
4365     default:
4366       break;
4367     }
4368
4369   return retval;
4370 }
4371
4372 static void
4373 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
4374 {
4375   GtkMenuPrivate *priv = menu->priv;
4376
4377   priv->navigation_x = 0;
4378   priv->navigation_y = 0;
4379   priv->navigation_width = 0;
4380   priv->navigation_height = 0;
4381
4382   if (priv->navigation_timeout)
4383     {
4384       g_source_remove (priv->navigation_timeout);
4385       priv->navigation_timeout = 0;
4386     }
4387 }
4388
4389 /* When the timeout is elapsed, the navigation region is destroyed
4390  * and the menuitem under the pointer (if any) is selected.
4391  */
4392 static gboolean
4393 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
4394 {
4395   GtkMenuPopdownData *popdown_data = user_data;
4396   GtkMenu *menu = popdown_data->menu;
4397   GtkMenuPrivate *priv = menu->priv;
4398   GdkWindow *child_window;
4399
4400   gtk_menu_stop_navigating_submenu (menu);
4401
4402   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
4403     {
4404       child_window = gdk_window_get_device_position (priv->bin_window,
4405                                                      popdown_data->device,
4406                                                      NULL, NULL, NULL);
4407
4408       if (child_window)
4409         {
4410           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
4411
4412           send_event->crossing.window = g_object_ref (child_window);
4413           send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
4414           send_event->crossing.send_event = TRUE;
4415           gdk_event_set_device (send_event, popdown_data->device);
4416
4417           GTK_WIDGET_CLASS (gtk_menu_parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
4418
4419           gdk_event_free (send_event);
4420         }
4421     }
4422
4423   return FALSE;
4424 }
4425
4426 static gboolean
4427 gtk_menu_navigating_submenu (GtkMenu *menu,
4428                              gint     event_x,
4429                              gint     event_y)
4430 {
4431   GtkMenuPrivate *priv = menu->priv;
4432   gint width, height;
4433
4434   if (!gtk_menu_has_navigation_triangle (menu))
4435     return FALSE;
4436
4437   width = priv->navigation_width;
4438   height = priv->navigation_height;
4439
4440   /* Check if x/y are in the triangle spanned by the navigation parameters */
4441
4442   /* 1) Move the coordinates so the triangle starts at 0,0 */
4443   event_x -= priv->navigation_x;
4444   event_y -= priv->navigation_y;
4445
4446   /* 2) Ensure both legs move along the positive axis */
4447   if (width < 0)
4448     {
4449       event_x = -event_x;
4450       width = -width;
4451     }
4452   if (height < 0)
4453     {
4454       event_y = -event_y;
4455       height = -height;
4456     }
4457
4458   /* 3) Check that the given coordinate is inside the triangle. The formula
4459    * is a transformed form of this formula: x/w + y/h <= 1
4460    */
4461   if (event_x >= 0 && event_y >= 0 &&
4462       event_x * height + event_y * width <= width * height)
4463     {
4464       return TRUE;
4465     }
4466   else
4467     {
4468       gtk_menu_stop_navigating_submenu (menu);
4469       return FALSE;
4470     }
4471 }
4472
4473 static void
4474 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
4475                                         GtkMenuItem      *menu_item,
4476                                         GdkEventCrossing *event)
4477 {
4478   GtkMenuPrivate *priv = menu->priv;
4479   gint submenu_left = 0;
4480   gint submenu_right = 0;
4481   gint submenu_top = 0;
4482   gint submenu_bottom = 0;
4483   gint width = 0;
4484   GtkWidget *event_widget;
4485   GtkMenuPopdownData *popdown_data;
4486   GdkWindow *window;
4487
4488   g_return_if_fail (menu_item->priv->submenu != NULL);
4489   g_return_if_fail (event != NULL);
4490
4491   event_widget = gtk_get_event_widget ((GdkEvent*) event);
4492
4493   window = gtk_widget_get_window (menu_item->priv->submenu);
4494   gdk_window_get_origin (window, &submenu_left, &submenu_top);
4495
4496   submenu_right = submenu_left + gdk_window_get_width (window);
4497   submenu_bottom = submenu_top + gdk_window_get_height (window);
4498
4499   width = gdk_window_get_width (gtk_widget_get_window (event_widget));
4500
4501   if (event->x >= 0 && event->x < width)
4502     {
4503       gint popdown_delay;
4504
4505       gtk_menu_stop_navigating_submenu (menu);
4506
4507       /* The navigation region is the triangle closest to the x/y
4508        * location of the rectangle. This is why the width or height
4509        * can be negative.
4510        */
4511       if (menu_item->priv->submenu_direction == GTK_DIRECTION_RIGHT)
4512         {
4513           /* right */
4514           priv->navigation_x = submenu_left;
4515           priv->navigation_width = event->x_root - submenu_left;
4516         }
4517       else
4518         {
4519           /* left */
4520           priv->navigation_x = submenu_right;
4521           priv->navigation_width = event->x_root - submenu_right;
4522         }
4523
4524       if (event->y < 0)
4525         {
4526           /* top */
4527           priv->navigation_y = event->y_root;
4528           priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT;
4529
4530           if (priv->navigation_height >= 0)
4531             return;
4532         }
4533       else
4534         {
4535           /* bottom */
4536           priv->navigation_y = event->y_root;
4537           priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT;
4538
4539           if (priv->navigation_height <= 0)
4540             return;
4541         }
4542
4543       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
4544                     "gtk-menu-popdown-delay", &popdown_delay,
4545                     NULL);
4546
4547       popdown_data = g_new (GtkMenuPopdownData, 1);
4548       popdown_data->menu = menu;
4549       popdown_data->device = gdk_event_get_device ((GdkEvent *) event);
4550
4551       priv->navigation_timeout = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT,
4552                                                                popdown_delay,
4553                                                                gtk_menu_stop_navigating_submenu_cb,
4554                                                                popdown_data,
4555                                                                (GDestroyNotify) g_free);
4556     }
4557 }
4558
4559 static void
4560 gtk_menu_deactivate (GtkMenuShell *menu_shell)
4561 {
4562   GtkWidget *parent;
4563
4564   g_return_if_fail (GTK_IS_MENU (menu_shell));
4565
4566   parent = menu_shell->priv->parent_menu_shell;
4567
4568   menu_shell->priv->activate_time = 0;
4569   gtk_menu_popdown (GTK_MENU (menu_shell));
4570
4571   if (parent)
4572     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
4573 }
4574
4575 static void
4576 gtk_menu_position (GtkMenu  *menu,
4577                    gboolean  set_scroll_offset)
4578 {
4579   GtkMenuPrivate *priv = menu->priv;
4580   GtkWidget *widget;
4581   GtkRequisition requisition;
4582   gint x, y;
4583   gint scroll_offset;
4584   GdkScreen *screen;
4585   GdkScreen *pointer_screen;
4586   GdkRectangle monitor;
4587   GdkDevice *pointer;
4588
4589   widget = GTK_WIDGET (menu);
4590
4591   screen = gtk_widget_get_screen (widget);
4592   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (menu));
4593   gdk_device_get_position (pointer, &pointer_screen, &x, &y);
4594
4595   /* Realize so we have the proper width and heigh to figure out
4596    * the right place to popup the menu.
4597    */
4598   gtk_widget_realize (priv->toplevel);
4599   requisition.width = gtk_widget_get_allocated_width (widget);
4600   requisition.height = gtk_widget_get_allocated_height (widget);
4601
4602   if (pointer_screen != screen)
4603     {
4604       /* Pointer is on a different screen; roughly center the
4605        * menu on the screen. If someone was using multiscreen
4606        * + Xinerama together they'd probably want something
4607        * fancier; but that is likely to be vanishingly rare.
4608        */
4609       x = MAX (0, (gdk_screen_get_width (screen) - requisition.width) / 2);
4610       y = MAX (0, (gdk_screen_get_height (screen) - requisition.height) / 2);
4611     }
4612
4613   priv->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4614   priv->initially_pushed_in = FALSE;
4615
4616   /* Set the type hint here to allow custom position functions
4617    * to set a different hint
4618    */
4619   if (!gtk_widget_get_visible (priv->toplevel))
4620     gtk_window_set_type_hint (GTK_WINDOW (priv->toplevel), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
4621
4622   if (priv->position_func)
4623     {
4624       (* priv->position_func) (menu, &x, &y, &priv->initially_pushed_in,
4625                                priv->position_func_data);
4626
4627       if (priv->monitor_num < 0)
4628         priv->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
4629
4630       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
4631     }
4632   else
4633     {
4634       gint space_left, space_right, space_above, space_below;
4635       gint needed_width;
4636       gint needed_height;
4637       GtkBorder padding;
4638       gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
4639
4640       get_menu_padding (widget, &padding);
4641
4642       /* The placement of popup menus horizontally works like this (with
4643        * RTL in parentheses)
4644        *
4645        * - If there is enough room to the right (left) of the mouse cursor,
4646        *   position the menu there.
4647        *
4648        * - Otherwise, if if there is enough room to the left (right) of the
4649        *   mouse cursor, position the menu there.
4650        *
4651        * - Otherwise if the menu is smaller than the monitor, position it
4652        *   on the side of the mouse cursor that has the most space available
4653        *
4654        * - Otherwise (if there is simply not enough room for the menu on the
4655        *   monitor), position it as far left (right) as possible.
4656        *
4657        * Positioning in the vertical direction is similar: first try below
4658        * mouse cursor, then above.
4659        */
4660       gdk_screen_get_monitor_workarea (screen, priv->monitor_num, &monitor);
4661
4662       space_left = x - monitor.x;
4663       space_right = monitor.x + monitor.width - x - 1;
4664       space_above = y - monitor.y;
4665       space_below = monitor.y + monitor.height - y - 1;
4666
4667       /* Position horizontally. */
4668
4669       /* the amount of space we need to position the menu.
4670        * Note the menu is offset "thickness" pixels
4671        */
4672       needed_width = requisition.width - padding.left;
4673
4674       if (needed_width <= space_left ||
4675           needed_width <= space_right)
4676         {
4677           if ((rtl  && needed_width <= space_left) ||
4678               (!rtl && needed_width >  space_right))
4679             {
4680               /* position left */
4681               x = x + padding.left - requisition.width + 1;
4682             }
4683           else
4684             {
4685               /* position right */
4686               x = x - padding.right;
4687             }
4688
4689           /* x is clamped on-screen further down */
4690         }
4691       else if (requisition.width <= monitor.width)
4692         {
4693           /* the menu is too big to fit on either side of the mouse
4694            * cursor, but smaller than the monitor. Position it on
4695            * the side that has the most space
4696            */
4697           if (space_left > space_right)
4698             {
4699               /* left justify */
4700               x = monitor.x;
4701             }
4702           else
4703             {
4704               /* right justify */
4705               x = monitor.x + monitor.width - requisition.width;
4706             }
4707         }
4708       else /* menu is simply too big for the monitor */
4709         {
4710           if (rtl)
4711             {
4712               /* right justify */
4713               x = monitor.x + monitor.width - requisition.width;
4714             }
4715           else
4716             {
4717               /* left justify */
4718               x = monitor.x;
4719             }
4720         }
4721
4722       /* Position vertically.
4723        * The algorithm is the same as above, but simpler
4724        * because we don't have to take RTL into account.
4725        */
4726       needed_height = requisition.height - padding.top;
4727
4728       if (needed_height <= space_above ||
4729           needed_height <= space_below)
4730         {
4731           if (needed_height <= space_below)
4732             y = y - padding.top;
4733           else
4734             y = y + padding.bottom - requisition.height + 1;
4735
4736           y = CLAMP (y, monitor.y,
4737                      monitor.y + monitor.height - requisition.height);
4738         }
4739       else if (needed_height > space_below && needed_height > space_above)
4740         {
4741           if (space_below >= space_above)
4742             y = monitor.y + monitor.height - requisition.height;
4743           else
4744             y = monitor.y;
4745         }
4746       else
4747         {
4748           y = monitor.y;
4749         }
4750     }
4751
4752   scroll_offset = 0;
4753
4754   if (y + requisition.height > monitor.y + monitor.height)
4755     {
4756       if (priv->initially_pushed_in)
4757         scroll_offset += (monitor.y + monitor.height) - requisition.height - y;
4758       y = (monitor.y + monitor.height) - requisition.height;
4759     }
4760
4761   if (y < monitor.y)
4762     {
4763       if (priv->initially_pushed_in)
4764         scroll_offset += monitor.y - y;
4765       y = monitor.y;
4766     }
4767
4768   x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
4769
4770   if (GTK_MENU_SHELL (menu)->priv->active)
4771     {
4772       priv->have_position = TRUE;
4773       priv->position_x = x;
4774       priv->position_y = y;
4775     }
4776
4777   if (scroll_offset != 0)
4778     {
4779       GtkBorder arrow_border;
4780
4781       get_arrows_border (menu, &arrow_border);
4782       scroll_offset += arrow_border.top;
4783     }
4784
4785   gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->priv->active ? priv->toplevel : priv->tearoff_window),
4786                    x, y);
4787
4788   if (!GTK_MENU_SHELL (menu)->priv->active)
4789     {
4790       gtk_window_resize (GTK_WINDOW (priv->tearoff_window),
4791                          requisition.width, requisition.height);
4792     }
4793
4794   if (set_scroll_offset)
4795     priv->scroll_offset = scroll_offset;
4796 }
4797
4798 static void
4799 gtk_menu_remove_scroll_timeout (GtkMenu *menu)
4800 {
4801   GtkMenuPrivate *priv = menu->priv;
4802
4803   if (priv->scroll_timeout)
4804     {
4805       g_source_remove (priv->scroll_timeout);
4806       priv->scroll_timeout = 0;
4807     }
4808 }
4809
4810 static void
4811 gtk_menu_stop_scrolling (GtkMenu *menu)
4812 {
4813   GtkMenuPrivate *priv = menu->priv;
4814
4815   gtk_menu_remove_scroll_timeout (menu);
4816   priv->upper_arrow_prelight = FALSE;
4817   priv->lower_arrow_prelight = FALSE;
4818 }
4819
4820 static void
4821 gtk_menu_scroll_to (GtkMenu *menu,
4822                     gint    offset)
4823 {
4824   GtkMenuPrivate *priv = menu->priv;
4825   GtkBorder arrow_border, padding;
4826   GtkWidget *widget;
4827   gint x, y;
4828   gint view_width, view_height;
4829   gint border_width;
4830   gint menu_height;
4831   gboolean double_arrows;
4832
4833   widget = GTK_WIDGET (menu);
4834
4835   if (priv->tearoff_active && priv->tearoff_adjustment)
4836     gtk_adjustment_set_value (priv->tearoff_adjustment, offset);
4837
4838   /* Move/resize the viewport according to arrows: */
4839   view_width = gtk_widget_get_allocated_width (widget);
4840   view_height = gtk_widget_get_allocated_height (widget);
4841
4842   get_menu_padding (widget, &padding);
4843   double_arrows = get_double_arrows (menu);
4844
4845   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
4846
4847   view_width -= (2 * border_width) + padding.left + padding.right;
4848   view_height -= (2 * border_width) + padding.top + padding.bottom;
4849   menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom;
4850
4851   x = border_width + padding.left;
4852   y = border_width + padding.top;
4853
4854   if (double_arrows && !priv->tearoff_active)
4855     {
4856       if (view_height < menu_height               ||
4857           (offset > 0 && priv->scroll_offset > 0) ||
4858           (offset < 0 && priv->scroll_offset < 0))
4859         {
4860           GtkStateFlags upper_arrow_previous_state = priv->upper_arrow_state;
4861           GtkStateFlags lower_arrow_previous_state = priv->lower_arrow_state;
4862
4863           if (!priv->upper_arrow_visible || !priv->lower_arrow_visible)
4864             gtk_widget_queue_draw (GTK_WIDGET (menu));
4865
4866           priv->upper_arrow_visible = priv->lower_arrow_visible = TRUE;
4867
4868           get_arrows_border (menu, &arrow_border);
4869           y += arrow_border.top;
4870           view_height -= arrow_border.top;
4871           view_height -= arrow_border.bottom;
4872
4873           if (offset <= 0)
4874             priv->upper_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
4875           else
4876             {
4877               priv->upper_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
4878
4879               if (priv->upper_arrow_prelight)
4880                 priv->upper_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4881               else
4882                 priv->upper_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
4883             }
4884
4885           if (offset >= menu_height - view_height)
4886             priv->lower_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
4887           else
4888             {
4889               priv->lower_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
4890
4891               if (priv->lower_arrow_prelight)
4892                 priv->lower_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
4893               else
4894                 priv->lower_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
4895             }
4896
4897           if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
4898               (priv->lower_arrow_state != lower_arrow_previous_state))
4899             gtk_widget_queue_draw (GTK_WIDGET (menu));
4900
4901           if ((upper_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
4902               (priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
4903             {
4904               /* At the upper border, possibly remove timeout */
4905               if (priv->scroll_step < 0)
4906                 {
4907                   gtk_menu_stop_scrolling (menu);
4908                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4909                 }
4910             }
4911
4912           if ((lower_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
4913               (priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
4914             {
4915               /* At the lower border, possibly remove timeout */
4916               if (priv->scroll_step > 0)
4917                 {
4918                   gtk_menu_stop_scrolling (menu);
4919                   gtk_widget_queue_draw (GTK_WIDGET (menu));
4920                 }
4921             }
4922         }
4923       else if (priv->upper_arrow_visible || priv->lower_arrow_visible)
4924         {
4925           offset = 0;
4926
4927           priv->upper_arrow_visible = priv->lower_arrow_visible = FALSE;
4928           priv->upper_arrow_prelight = priv->lower_arrow_prelight = FALSE;
4929
4930           gtk_menu_stop_scrolling (menu);
4931           gtk_widget_queue_draw (GTK_WIDGET (menu));
4932         }
4933     }
4934   else if (!priv->tearoff_active)
4935     {
4936       gboolean last_visible;
4937
4938       last_visible = priv->upper_arrow_visible;
4939       priv->upper_arrow_visible = offset > 0;
4940
4941       /* upper_arrow_visible may have changed, so requery the border */
4942       get_arrows_border (menu, &arrow_border);
4943       view_height -= arrow_border.top;
4944
4945       if ((last_visible != priv->upper_arrow_visible) &&
4946           !priv->upper_arrow_visible)
4947         {
4948           priv->upper_arrow_prelight = FALSE;
4949
4950           /* If we hide the upper arrow, possibly remove timeout */
4951           if (priv->scroll_step < 0)
4952             {
4953               gtk_menu_stop_scrolling (menu);
4954               gtk_widget_queue_draw (GTK_WIDGET (menu));
4955             }
4956         }
4957
4958       last_visible = priv->lower_arrow_visible;
4959       priv->lower_arrow_visible = offset < menu_height - view_height;
4960
4961       /* lower_arrow_visible may have changed, so requery the border */
4962       get_arrows_border (menu, &arrow_border);
4963       view_height -= arrow_border.bottom;
4964
4965       if ((last_visible != priv->lower_arrow_visible) &&
4966            !priv->lower_arrow_visible)
4967         {
4968           priv->lower_arrow_prelight = FALSE;
4969
4970           /* If we hide the lower arrow, possibly remove timeout */
4971           if (priv->scroll_step > 0)
4972             {
4973               gtk_menu_stop_scrolling (menu);
4974               gtk_widget_queue_draw (GTK_WIDGET (menu));
4975             }
4976         }
4977
4978       y += arrow_border.top;
4979     }
4980
4981   /* Scroll the menu: */
4982   if (gtk_widget_get_realized (widget))
4983     {
4984       gdk_window_move (priv->bin_window, 0, -offset);
4985       gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
4986     }
4987
4988   priv->scroll_offset = offset;
4989 }
4990
4991 static gboolean
4992 compute_child_offset (GtkMenu   *menu,
4993                       GtkWidget *menu_item,
4994                       gint      *offset,
4995                       gint      *height,
4996                       gboolean  *is_last_child)
4997 {
4998   GtkMenuPrivate *priv = menu->priv;
4999   gint item_top_attach;
5000   gint item_bottom_attach;
5001   gint child_offset = 0;
5002   gint i;
5003
5004   get_effective_child_attach (menu_item, NULL, NULL,
5005                               &item_top_attach, &item_bottom_attach);
5006
5007   /* there is a possibility that we get called before _size_request,
5008    * so check the height table for safety.
5009    */
5010   if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
5011     return FALSE;
5012
5013   /* when we have a row with only invisible children, its height will
5014    * be zero, so there's no need to check WIDGET_VISIBLE here
5015    */
5016   for (i = 0; i < item_top_attach; i++)
5017     child_offset += priv->heights[i];
5018
5019   if (is_last_child)
5020     *is_last_child = (item_bottom_attach == gtk_menu_get_n_rows (menu));
5021   if (offset)
5022     *offset = child_offset;
5023   if (height)
5024     *height = priv->heights[item_top_attach];
5025
5026   return TRUE;
5027 }
5028
5029 static void
5030 gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
5031                               GtkWidget    *menu_item)
5032 {
5033   GtkMenu *menu = GTK_MENU (menu_shell);
5034   GtkMenuPrivate *priv = menu->priv;
5035   GtkWidget *widget = GTK_WIDGET (menu_shell);
5036   gint child_offset, child_height;
5037   gint height;
5038   gint y;
5039   gint arrow_height;
5040   gboolean last_child = 0;
5041
5042   /* We need to check if the selected item fully visible.
5043    * If not we need to scroll the menu so that it becomes fully
5044    * visible.
5045    */
5046   if (compute_child_offset (menu, menu_item,
5047                             &child_offset, &child_height, &last_child))
5048     {
5049       gboolean double_arrows;
5050       GtkBorder padding;
5051
5052       y = priv->scroll_offset;
5053       height = gdk_window_get_height (gtk_widget_get_window (widget));
5054
5055       double_arrows = get_double_arrows (menu);
5056       get_menu_padding (widget, &padding);
5057
5058       height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) +
5059         padding.top + padding.bottom;
5060
5061       if (child_offset < y)
5062         {
5063           /* Ignore the enter event we might get if the pointer
5064            * is on the menu
5065            */
5066           menu_shell->priv->ignore_enter = TRUE;
5067           gtk_menu_scroll_to (menu, child_offset);
5068         }
5069       else
5070         {
5071           GtkBorder arrow_border;
5072
5073           arrow_height = 0;
5074
5075           get_arrows_border (menu, &arrow_border);
5076           if (!priv->tearoff_active)
5077             arrow_height = arrow_border.top + arrow_border.bottom;
5078
5079           if (child_offset + child_height > y + height - arrow_height)
5080             {
5081               arrow_height = 0;
5082               if ((!last_child && !priv->tearoff_active) || double_arrows)
5083                 arrow_height += arrow_border.bottom;
5084
5085               y = child_offset + child_height - height + arrow_height;
5086               if (((y > 0) && !priv->tearoff_active) || double_arrows)
5087                 {
5088                   /* Need upper arrow */
5089                   arrow_height += arrow_border.top;
5090                   y = child_offset + child_height - height + arrow_height;
5091                 }
5092               /* Ignore the enter event we might get if the pointer
5093                * is on the menu
5094                */
5095               menu_shell->priv->ignore_enter = TRUE;
5096               gtk_menu_scroll_to (menu, y);
5097             }
5098         }
5099     }
5100 }
5101
5102 static void
5103 gtk_menu_select_item (GtkMenuShell *menu_shell,
5104                       GtkWidget    *menu_item)
5105 {
5106   GtkMenu *menu = GTK_MENU (menu_shell);
5107
5108   if (gtk_widget_get_realized (GTK_WIDGET (menu)))
5109     gtk_menu_scroll_item_visible (menu_shell, menu_item);
5110
5111   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->select_item (menu_shell, menu_item);
5112 }
5113
5114
5115 /* Reparent the menu, taking care of the refcounting
5116  *
5117  * If unrealize is true we force a unrealize while reparenting the parent.
5118  * This can help eliminate flicker in some cases.
5119  *
5120  * What happens is that when the menu is unrealized and then re-realized,
5121  * the allocations are as follows:
5122  *
5123  *  parent - 1x1 at (0,0)
5124  *  child1 - 100x20 at (0,0)
5125  *  child2 - 100x20 at (0,20)
5126  *  child3 - 100x20 at (0,40)
5127  *
5128  * That is, the parent is small but the children are full sized. Then,
5129  * when the queued_resize gets processed, the parent gets resized to
5130  * full size.
5131  *
5132  * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
5133  * contains the following logic:
5134  *
5135  * - if a move or resize operation on a window would change the clip
5136  *   region on the children, then before the window is resized
5137  *   the background for children is temporarily set to None, the
5138  *   move/resize done, and the background for the children restored.
5139  *
5140  * So, at the point where the parent is resized to final size, the
5141  * background for the children is temporarily None, and thus they
5142  * are not cleared to the background color and the previous background
5143  * (the image of the menu) is left in place.
5144  */
5145 static void
5146 gtk_menu_reparent (GtkMenu   *menu,
5147                    GtkWidget *new_parent,
5148                    gboolean   unrealize)
5149 {
5150   GObject *object = G_OBJECT (menu);
5151   GtkWidget *widget = GTK_WIDGET (menu);
5152   gboolean was_floating = g_object_is_floating (object);
5153
5154   g_object_ref_sink (object);
5155
5156   if (unrealize)
5157     {
5158       g_object_ref (object);
5159       gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget);
5160       gtk_container_add (GTK_CONTAINER (new_parent), widget);
5161       g_object_unref (object);
5162     }
5163   else
5164     gtk_widget_reparent (widget, new_parent);
5165
5166   if (was_floating)
5167     g_object_force_floating (object);
5168   else
5169     g_object_unref (object);
5170 }
5171
5172 static void
5173 gtk_menu_show_all (GtkWidget *widget)
5174 {
5175   /* Show children, but not self. */
5176   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
5177 }
5178
5179 /**
5180  * gtk_menu_set_screen:
5181  * @menu: a #GtkMenu
5182  * @screen: (allow-none): a #GdkScreen, or %NULL if the screen should be
5183  *          determined by the widget the menu is attached to
5184  *
5185  * Sets the #GdkScreen on which the menu will be displayed.
5186  *
5187  * Since: 2.2
5188  */
5189 void
5190 gtk_menu_set_screen (GtkMenu   *menu,
5191                      GdkScreen *screen)
5192 {
5193   g_return_if_fail (GTK_IS_MENU (menu));
5194   g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
5195
5196   g_object_set_data (G_OBJECT (menu), I_("gtk-menu-explicit-screen"), screen);
5197
5198   if (screen)
5199     {
5200       menu_change_screen (menu, screen);
5201     }
5202   else
5203     {
5204       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
5205       if (attach_widget)
5206         attach_widget_screen_changed (attach_widget, NULL, menu);
5207     }
5208 }
5209
5210 /**
5211  * gtk_menu_attach:
5212  * @menu: a #GtkMenu
5213  * @child: a #GtkMenuItem
5214  * @left_attach: The column number to attach the left side of the item to
5215  * @right_attach: The column number to attach the right side of the item to
5216  * @top_attach: The row number to attach the top of the item to
5217  * @bottom_attach: The row number to attach the bottom of the item to
5218  *
5219  * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
5220  * an item will occupy is specified by @left_attach, @right_attach,
5221  * @top_attach and @bottom_attach. These each represent the leftmost,
5222  * rightmost, uppermost and lower column and row numbers of the table.
5223  * (Columns and rows are indexed from zero).
5224  *
5225  * Note that this function is not related to gtk_menu_detach().
5226  *
5227  * Since: 2.4
5228  */
5229 void
5230 gtk_menu_attach (GtkMenu   *menu,
5231                  GtkWidget *child,
5232                  guint      left_attach,
5233                  guint      right_attach,
5234                  guint      top_attach,
5235                  guint      bottom_attach)
5236 {
5237   GtkMenuShell *menu_shell;
5238   GtkWidget *parent;
5239
5240   g_return_if_fail (GTK_IS_MENU (menu));
5241   g_return_if_fail (GTK_IS_MENU_ITEM (child));
5242   parent = gtk_widget_get_parent (child);
5243   g_return_if_fail (parent == NULL || parent == GTK_WIDGET (menu));
5244   g_return_if_fail (left_attach < right_attach);
5245   g_return_if_fail (top_attach < bottom_attach);
5246
5247   menu_shell = GTK_MENU_SHELL (menu);
5248
5249   if (!parent)
5250     {
5251       AttachInfo *ai = get_attach_info (child);
5252
5253       ai->left_attach = left_attach;
5254       ai->right_attach = right_attach;
5255       ai->top_attach = top_attach;
5256       ai->bottom_attach = bottom_attach;
5257
5258       menu_shell->priv->children = g_list_append (menu_shell->priv->children, child);
5259
5260       gtk_widget_set_parent (child, GTK_WIDGET (menu));
5261
5262       menu_queue_resize (menu);
5263     }
5264   else
5265     {
5266       gtk_container_child_set (GTK_CONTAINER (parent), child,
5267                                "left-attach",   left_attach,
5268                                "right-attach",  right_attach,
5269                                "top-attach",    top_attach,
5270                                "bottom-attach", bottom_attach,
5271                                NULL);
5272     }
5273 }
5274
5275 static gint
5276 gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
5277 {
5278   gint popup_delay;
5279
5280   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
5281                 "gtk-menu-popup-delay", &popup_delay,
5282                 NULL);
5283
5284   return popup_delay;
5285 }
5286
5287 static GtkWidget *
5288 find_child_containing (GtkMenuShell *menu_shell,
5289                        int           left,
5290                        int           right,
5291                        int           top,
5292                        int           bottom)
5293 {
5294   GList *list;
5295
5296   /* find a child which includes the area given by
5297    * left, right, top, bottom.
5298    */
5299   for (list = menu_shell->priv->children; list; list = list->next)
5300     {
5301       gint l, r, t, b;
5302
5303       if (!_gtk_menu_item_is_selectable (list->data))
5304         continue;
5305
5306       get_effective_child_attach (list->data, &l, &r, &t, &b);
5307
5308       if (l <= left && right <= r && t <= top && bottom <= b)
5309         return GTK_WIDGET (list->data);
5310     }
5311
5312   return NULL;
5313 }
5314
5315 static void
5316 gtk_menu_move_current (GtkMenuShell         *menu_shell,
5317                        GtkMenuDirectionType  direction)
5318 {
5319   GtkMenu *menu = GTK_MENU (menu_shell);
5320   gint i;
5321   gint l, r, t, b;
5322   GtkWidget *match = NULL;
5323
5324   if (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL)
5325     {
5326       switch (direction)
5327         {
5328         case GTK_MENU_DIR_CHILD:
5329           direction = GTK_MENU_DIR_PARENT;
5330           break;
5331         case GTK_MENU_DIR_PARENT:
5332           direction = GTK_MENU_DIR_CHILD;
5333           break;
5334         default: ;
5335         }
5336     }
5337
5338   /* use special table menu key bindings */
5339   if (menu_shell->priv->active_menu_item && gtk_menu_get_n_columns (menu) > 1)
5340     {
5341       get_effective_child_attach (menu_shell->priv->active_menu_item, &l, &r, &t, &b);
5342
5343       if (direction == GTK_MENU_DIR_NEXT)
5344         {
5345           for (i = b; i < gtk_menu_get_n_rows (menu); i++)
5346             {
5347               match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
5348               if (match)
5349                 break;
5350             }
5351
5352           if (!match)
5353             {
5354               /* wrap around */
5355               for (i = 0; i < t; i++)
5356                 {
5357                   match = find_child_containing (menu_shell,
5358                                                  l, l + 1, i, i + 1);
5359                   if (match)
5360                     break;
5361                 }
5362             }
5363         }
5364       else if (direction == GTK_MENU_DIR_PREV)
5365         {
5366           for (i = t; i > 0; i--)
5367             {
5368               match = find_child_containing (menu_shell,
5369                                              l, l + 1, i - 1, i);
5370               if (match)
5371                 break;
5372             }
5373
5374           if (!match)
5375             {
5376               /* wrap around */
5377               for (i = gtk_menu_get_n_rows (menu); i > b; i--)
5378                 {
5379                   match = find_child_containing (menu_shell,
5380                                                  l, l + 1, i - 1, i);
5381                   if (match)
5382                     break;
5383                 }
5384             }
5385         }
5386       else if (direction == GTK_MENU_DIR_PARENT)
5387         {
5388           /* we go one left if possible */
5389           if (l > 0)
5390             match = find_child_containing (menu_shell,
5391                                            l - 1, l, t, t + 1);
5392
5393           if (!match)
5394             {
5395               GtkWidget *parent = menu_shell->priv->parent_menu_shell;
5396
5397               if (!parent
5398                   || g_list_length (GTK_MENU_SHELL (parent)->priv->children) <= 1)
5399                 match = menu_shell->priv->active_menu_item;
5400             }
5401         }
5402       else if (direction == GTK_MENU_DIR_CHILD)
5403         {
5404           /* we go one right if possible */
5405           if (r < gtk_menu_get_n_columns (menu))
5406             match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
5407
5408           if (!match)
5409             {
5410               GtkWidget *parent = menu_shell->priv->parent_menu_shell;
5411
5412               if (! GTK_MENU_ITEM (menu_shell->priv->active_menu_item)->priv->submenu &&
5413                   (!parent ||
5414                    g_list_length (GTK_MENU_SHELL (parent)->priv->children) <= 1))
5415                 match = menu_shell->priv->active_menu_item;
5416             }
5417         }
5418
5419       if (match)
5420         {
5421           gtk_menu_shell_select_item (menu_shell, match);
5422           return;
5423         }
5424     }
5425
5426   GTK_MENU_SHELL_CLASS (gtk_menu_parent_class)->move_current (menu_shell, direction);
5427 }
5428
5429 static gint
5430 get_visible_size (GtkMenu *menu)
5431 {
5432   GtkMenuPrivate *priv = menu->priv;
5433   GtkAllocation allocation;
5434   GtkWidget *widget = GTK_WIDGET (menu);
5435   GtkContainer *container = GTK_CONTAINER (menu);
5436   GtkBorder padding;
5437   gint menu_height;
5438
5439   gtk_widget_get_allocation (widget, &allocation);
5440   get_menu_padding (widget, &padding);
5441
5442   menu_height = (allocation.height -
5443                  (2 * gtk_container_get_border_width (container)) -
5444                  padding.top - padding.bottom);
5445
5446   if (!priv->tearoff_active)
5447     {
5448       GtkBorder arrow_border;
5449
5450       get_arrows_border (menu, &arrow_border);
5451       menu_height -= arrow_border.top;
5452       menu_height -= arrow_border.bottom;
5453     }
5454
5455   return menu_height;
5456 }
5457
5458 /* Find the sensitive on-screen child containing @y, or if none,
5459  * the nearest selectable onscreen child. (%NULL if none)
5460  */
5461 static GtkWidget *
5462 child_at (GtkMenu *menu,
5463           gint     y)
5464 {
5465   GtkMenuPrivate *priv = menu->priv;
5466   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5467   GtkWidget *child = NULL;
5468   gint child_offset = 0;
5469   GList *children;
5470   gint menu_height;
5471   gint lower, upper; /* Onscreen bounds */
5472
5473   menu_height = get_visible_size (menu);
5474   lower = priv->scroll_offset;
5475   upper = priv->scroll_offset + menu_height;
5476
5477   for (children = menu_shell->priv->children; children; children = children->next)
5478     {
5479       if (gtk_widget_get_visible (children->data))
5480         {
5481           GtkRequisition child_requisition;
5482
5483           gtk_widget_get_preferred_size (children->data,
5484                                          &child_requisition, NULL);
5485
5486           if (_gtk_menu_item_is_selectable (children->data) &&
5487               child_offset >= lower &&
5488               child_offset + child_requisition.height <= upper)
5489             {
5490               child = children->data;
5491
5492               if (child_offset + child_requisition.height > y &&
5493                   !GTK_IS_TEAROFF_MENU_ITEM (child))
5494                 return child;
5495             }
5496
5497           child_offset += child_requisition.height;
5498         }
5499     }
5500
5501   return child;
5502 }
5503
5504 static gint
5505 get_menu_height (GtkMenu *menu)
5506 {
5507   GtkMenuPrivate *priv = menu->priv;
5508   GtkWidget *widget = GTK_WIDGET (menu);
5509   GtkBorder padding;
5510   gint height;
5511
5512   get_menu_padding (widget, &padding);
5513
5514   height = priv->requested_height;
5515   height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) +
5516     padding.top + padding.bottom;
5517
5518   if (!priv->tearoff_active)
5519     {
5520       GtkBorder arrow_border;
5521
5522       get_arrows_border (menu, &arrow_border);
5523       height -= arrow_border.top;
5524       height -= arrow_border.bottom;
5525     }
5526
5527   return height;
5528 }
5529
5530 static void
5531 gtk_menu_real_move_scroll (GtkMenu       *menu,
5532                            GtkScrollType  type)
5533 {
5534   GtkMenuPrivate *priv = menu->priv;
5535   gint page_size = get_visible_size (menu);
5536   gint end_position = get_menu_height (menu);
5537   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
5538
5539   switch (type)
5540     {
5541     case GTK_SCROLL_PAGE_UP:
5542     case GTK_SCROLL_PAGE_DOWN:
5543       {
5544         gint old_offset;
5545         gint new_offset;
5546         gint child_offset = 0;
5547         gboolean old_upper_arrow_visible;
5548         gint step;
5549
5550         if (type == GTK_SCROLL_PAGE_UP)
5551           step = - page_size;
5552         else
5553           step = page_size;
5554
5555         if (menu_shell->priv->active_menu_item)
5556           {
5557             gint child_height;
5558
5559             compute_child_offset (menu, menu_shell->priv->active_menu_item,
5560                                   &child_offset, &child_height, NULL);
5561             child_offset += child_height / 2;
5562           }
5563
5564         menu_shell->priv->ignore_enter = TRUE;
5565         old_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
5566         old_offset = priv->scroll_offset;
5567
5568         new_offset = priv->scroll_offset + step;
5569         new_offset = CLAMP (new_offset, 0, end_position - page_size);
5570
5571         gtk_menu_scroll_to (menu, new_offset);
5572
5573         if (menu_shell->priv->active_menu_item)
5574           {
5575             GtkWidget *new_child;
5576             gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
5577             GtkBorder arrow_border;
5578
5579             get_arrows_border (menu, &arrow_border);
5580
5581             if (priv->scroll_offset != old_offset)
5582               step = priv->scroll_offset - old_offset;
5583
5584             step -= (new_upper_arrow_visible - old_upper_arrow_visible) * arrow_border.top;
5585
5586             new_child = child_at (menu, child_offset + step);
5587             if (new_child)
5588               gtk_menu_shell_select_item (menu_shell, new_child);
5589           }
5590       }
5591       break;
5592     case GTK_SCROLL_START:
5593       /* Ignore the enter event we might get if the pointer is on the menu */
5594       menu_shell->priv->ignore_enter = TRUE;
5595       gtk_menu_shell_select_first (menu_shell, TRUE);
5596       break;
5597     case GTK_SCROLL_END:
5598       /* Ignore the enter event we might get if the pointer is on the menu */
5599       menu_shell->priv->ignore_enter = TRUE;
5600       _gtk_menu_shell_select_last (menu_shell, TRUE);
5601       break;
5602     default:
5603       break;
5604     }
5605 }
5606
5607 /**
5608  * gtk_menu_set_monitor:
5609  * @menu: a #GtkMenu
5610  * @monitor_num: the number of the monitor on which the menu should
5611  *    be popped up
5612  *
5613  * Informs GTK+ on which monitor a menu should be popped up.
5614  * See gdk_screen_get_monitor_geometry().
5615  *
5616  * This function should be called from a #GtkMenuPositionFunc
5617  * if the menu should not appear on the same monitor as the pointer.
5618  * This information can't be reliably inferred from the coordinates
5619  * returned by a #GtkMenuPositionFunc, since, for very long menus,
5620  * these coordinates may extend beyond the monitor boundaries or even
5621  * the screen boundaries.
5622  *
5623  * Since: 2.4
5624  */
5625 void
5626 gtk_menu_set_monitor (GtkMenu *menu,
5627                       gint     monitor_num)
5628 {
5629   GtkMenuPrivate *priv = menu->priv;
5630
5631   g_return_if_fail (GTK_IS_MENU (menu));
5632
5633   priv->monitor_num = monitor_num;
5634 }
5635
5636 /**
5637  * gtk_menu_get_monitor:
5638  * @menu: a #GtkMenu
5639  *
5640  * Retrieves the number of the monitor on which to show the menu.
5641  *
5642  * Returns: the number of the monitor on which the menu should
5643  *    be popped up or -1, if no monitor has been set
5644  *
5645  * Since: 2.14
5646  */
5647 gint
5648 gtk_menu_get_monitor (GtkMenu *menu)
5649 {
5650   g_return_val_if_fail (GTK_IS_MENU (menu), -1);
5651
5652   return menu->priv->monitor_num;
5653 }
5654
5655 /**
5656  * gtk_menu_get_for_attach_widget:
5657  * @widget: a #GtkWidget
5658  *
5659  * Returns a list of the menus which are attached to this widget.
5660  * This list is owned by GTK+ and must not be modified.
5661  *
5662  * Return value: (element-type GtkWidget) (transfer none): the list
5663  *     of menus attached to his widget.
5664  *
5665  * Since: 2.6
5666  */
5667 GList*
5668 gtk_menu_get_for_attach_widget (GtkWidget *widget)
5669 {
5670   GList *list;
5671
5672   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
5673
5674   list = g_object_get_data (G_OBJECT (widget), ATTACHED_MENUS);
5675
5676   return list;
5677 }
5678
5679 static void
5680 gtk_menu_grab_notify (GtkWidget *widget,
5681                       gboolean   was_grabbed)
5682 {
5683   GtkMenu *menu;
5684   GtkWidget *toplevel;
5685   GtkWindowGroup *group;
5686   GtkWidget *grab;
5687   GdkDevice *pointer;
5688
5689   menu = GTK_MENU (widget);
5690   pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget));
5691
5692   if (!pointer ||
5693       !gtk_widget_device_is_shadowed (widget, pointer))
5694     return;
5695
5696   toplevel = gtk_widget_get_toplevel (widget);
5697
5698   if (!GTK_IS_WINDOW (toplevel))
5699     return;
5700
5701   group = gtk_window_get_group (GTK_WINDOW (toplevel));
5702   grab = gtk_window_group_get_current_device_grab (group, pointer);
5703
5704   if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab))
5705     gtk_menu_shell_cancel (GTK_MENU_SHELL (widget));
5706
5707   menu->priv->drag_scroll_started = FALSE;
5708 }
5709
5710 /**
5711  * gtk_menu_set_reserve_toggle_size:
5712  * @menu: a #GtkMenu
5713  * @reserve_toggle_size: whether to reserve size for toggles
5714  *
5715  * Sets whether the menu should reserve space for drawing toggles
5716  * or icons, regardless of their actual presence.
5717  *
5718  * Since: 2.18
5719  */
5720 void
5721 gtk_menu_set_reserve_toggle_size (GtkMenu  *menu,
5722                                   gboolean  reserve_toggle_size)
5723 {
5724   GtkMenuPrivate *priv = menu->priv;
5725   gboolean no_toggle_size;
5726
5727   g_return_if_fail (GTK_IS_MENU (menu));
5728
5729   no_toggle_size = !reserve_toggle_size;
5730
5731   if (priv->no_toggle_size != no_toggle_size)
5732     {
5733       priv->no_toggle_size = no_toggle_size;
5734
5735       g_object_notify (G_OBJECT (menu), "reserve-toggle-size");
5736     }
5737 }
5738
5739 /**
5740  * gtk_menu_get_reserve_toggle_size:
5741  * @menu: a #GtkMenu
5742  *
5743  * Returns whether the menu reserves space for toggles and
5744  * icons, regardless of their actual presence.
5745  *
5746  * Returns: Whether the menu reserves toggle space
5747  *
5748  * Since: 2.18
5749  */
5750 gboolean
5751 gtk_menu_get_reserve_toggle_size (GtkMenu *menu)
5752 {
5753   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
5754
5755   return !menu->priv->no_toggle_size;
5756 }
5757
5758 /**
5759  * gtk_menu_new_from_model:
5760  * @model: a #GMenuModel
5761  *
5762  * Creates a #GtkMenu and populates it with menu items and
5763  * submenus according to @model.
5764  *
5765  * The created menu items are connected to actions found in the
5766  * #GtkApplicationWindow to which the menu belongs - typically
5767  * by means of being attached to a widget (see gtk_menu_attach_to_widget())
5768  * that is contained within the #GtkApplicationWindows widget hierarchy.
5769  *
5770  * Returns: a new #GtkMenu
5771  *
5772  * Since: 3.4
5773  */
5774 GtkWidget *
5775 gtk_menu_new_from_model (GMenuModel *model)
5776 {
5777   GtkWidget *menu;
5778
5779   g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
5780
5781   menu = gtk_menu_new ();
5782   gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), model, NULL, TRUE);
5783
5784   return menu;
5785 }