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