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