]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
af9d65ebfe0beb16260a7269dcb217c387047735
[~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 #define GTK_MENU_INTERNALS
28
29 #include <config.h>
30 #include <string.h> /* memset */
31 #include "gdk/gdkkeysyms.h"
32 #include "gtkalias.h"
33 #include "gtkaccellabel.h"
34 #include "gtkaccelmap.h"
35 #include "gtkbindings.h"
36 #include "gtklabel.h"
37 #include "gtkmain.h"
38 #include "gtkmarshalers.h"
39 #include "gtkmenu.h"
40 #include "gtkmenuitem.h"
41 #include "gtktearoffmenuitem.h"
42 #include "gtkwindow.h"
43 #include "gtkhbox.h"
44 #include "gtkvscrollbar.h"
45 #include "gtksettings.h"
46 #include "gtkintl.h"
47
48
49 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_GET_CLASS (w)
50
51 #define DEFAULT_POPUP_DELAY    225
52 #define DEFAULT_POPDOWN_DELAY  1000
53
54 #define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
55                                          * extends below the submenu
56                                          */
57
58 #define MENU_SCROLL_STEP1 8
59 #define MENU_SCROLL_STEP2 15
60 #define MENU_SCROLL_ARROW_HEIGHT 16
61 #define MENU_SCROLL_FAST_ZONE 8
62 #define MENU_SCROLL_TIMEOUT1 50
63 #define MENU_SCROLL_TIMEOUT2 20
64
65 #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
66 #define ATTACHED_MENUS "gtk-attached-menus"
67
68 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
69 typedef struct _GtkMenuPrivate          GtkMenuPrivate;
70
71 struct _GtkMenuAttachData
72 {
73   GtkWidget *attach_widget;
74   GtkMenuDetachFunc detacher;
75 };
76
77 struct _GtkMenuPrivate 
78 {
79   gboolean have_position;
80   gint x;
81   gint y;
82
83   /* info used for the table */
84   guint *heights;
85   gint heights_length;
86
87   gint monitor_num;
88
89   /* Cached layout information */
90   gboolean have_layout;
91   gint n_rows;
92   gint n_columns;
93 };
94
95 typedef struct
96 {
97   gint left_attach;
98   gint right_attach;
99   gint top_attach;
100   gint bottom_attach;
101   gint effective_left_attach;
102   gint effective_right_attach;
103   gint effective_top_attach;
104   gint effective_bottom_attach;
105 } AttachInfo;
106
107 enum {
108   MOVE_SCROLL,
109   LAST_SIGNAL
110 };
111
112 enum {
113   PROP_0,
114   PROP_TEAROFF_STATE,
115   PROP_TEAROFF_TITLE
116 };
117
118 enum {
119   CHILD_PROP_0,
120   CHILD_PROP_LEFT_ATTACH,
121   CHILD_PROP_RIGHT_ATTACH,
122   CHILD_PROP_TOP_ATTACH,
123   CHILD_PROP_BOTTOM_ATTACH
124 };
125
126 static void     gtk_menu_class_init        (GtkMenuClass     *klass);
127 static void     gtk_menu_init              (GtkMenu          *menu);
128 static void     gtk_menu_set_property      (GObject          *object,
129                                             guint             prop_id,
130                                             const GValue     *value,
131                                             GParamSpec       *pspec);
132 static void     gtk_menu_get_property      (GObject          *object,
133                                             guint             prop_id,
134                                             GValue           *value,
135                                             GParamSpec       *pspec);
136 static void     gtk_menu_set_child_property(GtkContainer     *container,
137                                             GtkWidget        *child,
138                                             guint             property_id,
139                                             const GValue     *value,
140                                             GParamSpec       *pspec);
141 static void     gtk_menu_get_child_property(GtkContainer     *container,
142                                             GtkWidget        *child,
143                                             guint             property_id,
144                                             GValue           *value,
145                                             GParamSpec       *pspec);
146 static void     gtk_menu_destroy           (GtkObject        *object);
147 static void     gtk_menu_finalize          (GObject          *object);
148 static void     gtk_menu_realize           (GtkWidget        *widget);
149 static void     gtk_menu_unrealize         (GtkWidget        *widget);
150 static void     gtk_menu_size_request      (GtkWidget        *widget,
151                                             GtkRequisition   *requisition);
152 static void     gtk_menu_size_allocate     (GtkWidget        *widget,
153                                             GtkAllocation    *allocation);
154 static void     gtk_menu_paint             (GtkWidget        *widget,
155                                             GdkEventExpose   *expose);
156 static void     gtk_menu_show              (GtkWidget        *widget);
157 static gboolean gtk_menu_expose            (GtkWidget        *widget,
158                                             GdkEventExpose   *event);
159 static gboolean gtk_menu_key_press         (GtkWidget        *widget,
160                                             GdkEventKey      *event);
161 static gboolean gtk_menu_scroll            (GtkWidget        *widget,
162                                             GdkEventScroll   *event);
163 static gboolean gtk_menu_button_press      (GtkWidget        *widget,
164                                             GdkEventButton   *event);
165 static gboolean gtk_menu_button_release    (GtkWidget        *widget,
166                                             GdkEventButton   *event);
167 static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
168                                             GdkEventMotion   *event);
169 static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
170                                             GdkEventCrossing *event);
171 static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
172                                             GdkEventCrossing *event);
173 static void     gtk_menu_scroll_to         (GtkMenu          *menu,
174                                             gint              offset);
175
176 static void     gtk_menu_stop_scrolling        (GtkMenu  *menu);
177 static void     gtk_menu_remove_scroll_timeout (GtkMenu  *menu);
178 static gboolean gtk_menu_scroll_timeout        (gpointer  data);
179
180 static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
181                                               GtkWidget       *menu_item);
182 static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
183                                             GtkWidget        *menu_item);
184 static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
185                                             GtkWidget        *child,
186                                             gint              position);
187 static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
188                                             GtkMenu          *menu);
189 static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
190                                             gboolean         enter);
191 static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
192                                             gint             width);
193 static void     gtk_menu_style_set         (GtkWidget        *widget,
194                                             GtkStyle         *previous_style);
195 static gboolean gtk_menu_focus             (GtkWidget        *widget,
196                                             GtkDirectionType direction);
197 static gint     gtk_menu_get_popup_delay   (GtkMenuShell     *menu_shell);
198 static void     gtk_menu_move_current      (GtkMenuShell     *menu_shell,
199                                             GtkMenuDirectionType direction);
200 static void     gtk_menu_real_move_scroll  (GtkMenu          *menu,
201                                             GtkScrollType     type);
202
203 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
204 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
205 static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
206                                                         gint              event_x,
207                                                         gint              event_y);
208 static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
209                                                         GtkMenuItem      *menu_item,
210                                                         GdkEventCrossing *event);
211  
212 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
213 static void gtk_menu_show_all       (GtkWidget         *widget);
214 static void gtk_menu_hide_all       (GtkWidget         *widget);
215 static void gtk_menu_position       (GtkMenu           *menu);
216 static void gtk_menu_reparent       (GtkMenu           *menu, 
217                                      GtkWidget         *new_parent, 
218                                      gboolean           unrealize);
219 static void gtk_menu_remove         (GtkContainer      *menu,
220                                      GtkWidget         *widget);
221
222 static void gtk_menu_update_title   (GtkMenu           *menu);
223
224 static void       menu_grab_transfer_window_destroy (GtkMenu *menu);
225 static GdkWindow *menu_grab_transfer_window_get     (GtkMenu *menu);
226
227 static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget,
228                                                   guint      signal_id);
229 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
230                                            gboolean group_changed);
231
232 static GtkMenuShellClass *parent_class = NULL;
233 static const gchar       *attach_data_key = "gtk-menu-attach-data";
234
235 static guint menu_signals[LAST_SIGNAL] = { 0 };
236
237 static void
238 gtk_menu_free_private (gpointer data)
239 {
240   GtkMenuPrivate *priv = (GtkMenuPrivate *)data;
241
242   g_free (priv->heights);
243
244   g_free (priv);
245 }
246
247 static GtkMenuPrivate *
248 gtk_menu_get_private (GtkMenu *menu)
249 {
250   GtkMenuPrivate *private;
251   static GQuark private_quark = 0;
252
253   if (!private_quark)
254     private_quark = g_quark_from_static_string ("gtk-menu-private");
255
256   private = g_object_get_qdata (G_OBJECT (menu), private_quark);
257
258   if (!private)
259     {
260       private = g_new0 (GtkMenuPrivate, 1);
261       private->have_position = FALSE;
262       
263       g_object_set_qdata_full (G_OBJECT (menu), private_quark,
264                                private, gtk_menu_free_private);
265     }
266
267   return private;
268 }
269
270 GType
271 gtk_menu_get_type (void)
272 {
273   static GType menu_type = 0;
274   
275   if (!menu_type)
276     {
277       static const GTypeInfo menu_info =
278       {
279         sizeof (GtkMenuClass),
280         NULL,           /* base_init */
281         NULL,           /* base_finalize */
282         (GClassInitFunc) gtk_menu_class_init,
283         NULL,           /* class_finalize */
284         NULL,           /* class_data */
285         sizeof (GtkMenu),
286         0,              /* n_preallocs */
287         (GInstanceInitFunc) gtk_menu_init,
288       };
289       
290       menu_type = g_type_register_static (GTK_TYPE_MENU_SHELL, "GtkMenu",
291                                           &menu_info, 0);
292     }
293   
294   return menu_type;
295 }
296
297 static void
298 menu_queue_resize (GtkMenu *menu)
299 {
300   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
301
302   priv->have_layout = FALSE;
303   gtk_widget_queue_resize (GTK_WIDGET (menu));
304 }
305
306 static AttachInfo *
307 get_attach_info (GtkWidget *child)
308 {
309   GObject *object = G_OBJECT (child);
310   AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
311
312   if (!ai)
313     {
314       ai = g_new0 (AttachInfo, 1);
315       g_object_set_data_full (object, ATTACH_INFO_KEY, ai, g_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   parent_class = g_type_class_peek_parent (class);
473   
474   gobject_class->finalize = gtk_menu_finalize;
475   gobject_class->set_property = gtk_menu_set_property;
476   gobject_class->get_property = gtk_menu_get_property;
477
478   object_class->destroy = gtk_menu_destroy;
479   
480   widget_class->realize = gtk_menu_realize;
481   widget_class->unrealize = gtk_menu_unrealize;
482   widget_class->size_request = gtk_menu_size_request;
483   widget_class->size_allocate = gtk_menu_size_allocate;
484   widget_class->show = gtk_menu_show;
485   widget_class->expose_event = gtk_menu_expose;
486   widget_class->scroll_event = gtk_menu_scroll;
487   widget_class->key_press_event = gtk_menu_key_press;
488   widget_class->button_press_event = gtk_menu_button_press;
489   widget_class->button_release_event = gtk_menu_button_release;
490   widget_class->motion_notify_event = gtk_menu_motion_notify;
491   widget_class->show_all = gtk_menu_show_all;
492   widget_class->hide_all = gtk_menu_hide_all;
493   widget_class->enter_notify_event = gtk_menu_enter_notify;
494   widget_class->leave_notify_event = gtk_menu_leave_notify;
495   widget_class->motion_notify_event = gtk_menu_motion_notify;
496   widget_class->style_set = gtk_menu_style_set;
497   widget_class->focus = gtk_menu_focus;
498   widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
499
500   container_class->remove = gtk_menu_remove;
501   container_class->get_child_property = gtk_menu_get_child_property;
502   container_class->set_child_property = gtk_menu_set_child_property;
503   
504   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
505   menu_shell_class->deactivate = gtk_menu_deactivate;
506   menu_shell_class->select_item = gtk_menu_select_item;
507   menu_shell_class->insert = gtk_menu_real_insert;
508   menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
509   menu_shell_class->move_current = gtk_menu_move_current;
510
511   menu_signals[MOVE_SCROLL] =
512     _gtk_binding_signal_new ("move_scroll",
513                              G_OBJECT_CLASS_TYPE (object_class),
514                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
515                              G_CALLBACK (gtk_menu_real_move_scroll),
516                              NULL, NULL,
517                              _gtk_marshal_VOID__ENUM,
518                              G_TYPE_NONE, 1,
519                              GTK_TYPE_SCROLL_TYPE);
520   
521   g_object_class_install_property (gobject_class,
522                                    PROP_TEAROFF_TITLE,
523                                    g_param_spec_string ("tearoff-title",
524                                                         P_("Tearoff Title"),
525                                                         P_("A title that may be displayed by the window manager when this menu is torn-off"),
526                                                         "",
527                                                         G_PARAM_READWRITE));
528
529   /**
530    * GtkMenu:tearoff-state:
531    *
532    * A boolean that indicates whether the menu is torn-off.
533    *
534    * Since: 2.6
535    **/
536   g_object_class_install_property (gobject_class,
537                                    PROP_TEAROFF_STATE,
538                                    g_param_spec_boolean ("tearoff-state",
539                                                          P_("Tearoff State"),
540                                                          P_("A boolean that indicates whether the menu is torn-off"),
541                                                          FALSE,
542                                                          G_PARAM_READWRITE));
543
544   gtk_widget_class_install_style_property (widget_class,
545                                            g_param_spec_int ("vertical-padding",
546                                                              P_("Vertical Padding"),
547                                                              P_("Extra space at the top and bottom of the menu"),
548                                                              0,
549                                                              G_MAXINT,
550                                                              1,
551                                                              G_PARAM_READABLE));
552
553   gtk_widget_class_install_style_property (widget_class,
554                                            g_param_spec_int ("vertical-offset",
555                                                              P_("Vertical Offset"),
556                                                              P_("When the menu is a submenu, position it this number of pixels offset vertically"),
557                                                              G_MININT,
558                                                              G_MAXINT,
559                                                              0,
560                                                              G_PARAM_READABLE));
561
562   gtk_widget_class_install_style_property (widget_class,
563                                            g_param_spec_int ("horizontal-offset",
564                                                              P_("Horizontal Offset"),
565                                                              P_("When the menu is a submenu, position it this number of pixels offset horizontally"),
566                                                              G_MININT,
567                                                              G_MAXINT,
568                                                              -2,
569                                                              G_PARAM_READABLE));
570
571
572  gtk_container_class_install_child_property (container_class,
573                                              CHILD_PROP_LEFT_ATTACH,
574                                               g_param_spec_int ("left_attach",
575                                                                P_("Left Attach"),
576                                                                P_("The column number to attach the left side of the child to"),
577                                                                 -1, INT_MAX, -1,
578                                                                G_PARAM_READWRITE));
579
580  gtk_container_class_install_child_property (container_class,
581                                              CHILD_PROP_RIGHT_ATTACH,
582                                               g_param_spec_int ("right_attach",
583                                                                P_("Right Attach"),
584                                                                P_("The column number to attach the right side of the child to"),
585                                                                 -1, INT_MAX, -1,
586                                                                G_PARAM_READWRITE));
587
588  gtk_container_class_install_child_property (container_class,
589                                              CHILD_PROP_TOP_ATTACH,
590                                               g_param_spec_int ("top_attach",
591                                                                P_("Top Attach"),
592                                                                P_("The row number to attach the top of the child to"),
593                                                                 -1, INT_MAX, -1,
594                                                                G_PARAM_READWRITE));
595
596  gtk_container_class_install_child_property (container_class,
597                                              CHILD_PROP_BOTTOM_ATTACH,
598                                               g_param_spec_int ("bottom_attach",
599                                                                P_("Bottom Attach"),
600                                                                P_("The row number to attach the bottom of the child to"),
601                                                                 -1, INT_MAX, -1,
602                                                                G_PARAM_READWRITE));
603
604   binding_set = gtk_binding_set_by_class (class);
605   gtk_binding_entry_add_signal (binding_set,
606                                 GDK_Up, 0,
607                                 "move_current", 1,
608                                 GTK_TYPE_MENU_DIRECTION_TYPE,
609                                 GTK_MENU_DIR_PREV);
610   gtk_binding_entry_add_signal (binding_set,
611                                 GDK_KP_Up, 0,
612                                 "move_current", 1,
613                                 GTK_TYPE_MENU_DIRECTION_TYPE,
614                                 GTK_MENU_DIR_PREV);
615   gtk_binding_entry_add_signal (binding_set,
616                                 GDK_Down, 0,
617                                 "move_current", 1,
618                                 GTK_TYPE_MENU_DIRECTION_TYPE,
619                                 GTK_MENU_DIR_NEXT);
620   gtk_binding_entry_add_signal (binding_set,
621                                 GDK_KP_Down, 0,
622                                 "move_current", 1,
623                                 GTK_TYPE_MENU_DIRECTION_TYPE,
624                                 GTK_MENU_DIR_NEXT);
625   gtk_binding_entry_add_signal (binding_set,
626                                 GDK_Left, 0,
627                                 "move_current", 1,
628                                 GTK_TYPE_MENU_DIRECTION_TYPE,
629                                 GTK_MENU_DIR_PARENT);
630   gtk_binding_entry_add_signal (binding_set,
631                                 GDK_KP_Left, 0,
632                                 "move_current", 1,
633                                 GTK_TYPE_MENU_DIRECTION_TYPE,
634                                 GTK_MENU_DIR_PARENT);
635   gtk_binding_entry_add_signal (binding_set,
636                                 GDK_Right, 0,
637                                 "move_current", 1,
638                                 GTK_TYPE_MENU_DIRECTION_TYPE,
639                                 GTK_MENU_DIR_CHILD);
640   gtk_binding_entry_add_signal (binding_set,
641                                 GDK_KP_Right, 0,
642                                 "move_current", 1,
643                                 GTK_TYPE_MENU_DIRECTION_TYPE,
644                                 GTK_MENU_DIR_CHILD);
645   gtk_binding_entry_add_signal (binding_set,
646                                 GDK_Home, 0,
647                                 "move_scroll", 1,
648                                 GTK_TYPE_SCROLL_TYPE,
649                                 GTK_SCROLL_START);
650   gtk_binding_entry_add_signal (binding_set,
651                                 GDK_KP_Home, 0,
652                                 "move_scroll", 1,
653                                 GTK_TYPE_SCROLL_TYPE,
654                                 GTK_SCROLL_START);
655   gtk_binding_entry_add_signal (binding_set,
656                                 GDK_End, 0,
657                                 "move_scroll", 1,
658                                 GTK_TYPE_SCROLL_TYPE,
659                                 GTK_SCROLL_END);
660   gtk_binding_entry_add_signal (binding_set,
661                                 GDK_KP_End, 0,
662                                 "move_scroll", 1,
663                                 GTK_TYPE_SCROLL_TYPE,
664                                 GTK_SCROLL_END);
665   gtk_binding_entry_add_signal (binding_set,
666                                 GDK_Page_Up, 0,
667                                 "move_scroll", 1,
668                                 GTK_TYPE_SCROLL_TYPE,
669                                 GTK_SCROLL_PAGE_UP);
670   gtk_binding_entry_add_signal (binding_set,
671                                 GDK_KP_Page_Up, 0,
672                                 "move_scroll", 1,
673                                 GTK_TYPE_SCROLL_TYPE,
674                                 GTK_SCROLL_PAGE_UP);
675   gtk_binding_entry_add_signal (binding_set,
676                                 GDK_Page_Down, 0,
677                                 "move_scroll", 1,
678                                 GTK_TYPE_SCROLL_TYPE,
679                                 GTK_SCROLL_PAGE_DOWN);
680   gtk_binding_entry_add_signal (binding_set,
681                                 GDK_KP_Page_Down, 0,
682                                 "move_scroll", 1,
683                                 GTK_TYPE_SCROLL_TYPE,
684                                 GTK_SCROLL_PAGE_DOWN);
685
686   gtk_settings_install_property (g_param_spec_boolean ("gtk-can-change-accels",
687                                                        P_("Can change accelerators"),
688                                                        P_("Whether menu accelerators can be changed by pressing a key over the menu item"),
689                                                        FALSE,
690                                                        G_PARAM_READWRITE));
691
692   gtk_settings_install_property (g_param_spec_int ("gtk-menu-popup-delay",
693                                                    P_("Delay before submenus appear"),
694                                                    P_("Minimum time the pointer must stay over a menu item before the submenu appear"),
695                                                    0,
696                                                    G_MAXINT,
697                                                    DEFAULT_POPUP_DELAY,
698                                                    G_PARAM_READWRITE));
699
700   gtk_settings_install_property (g_param_spec_int ("gtk-menu-popdown-delay",
701                                                    P_("Delay before hiding a submenu"),
702                                                    P_("The time before hiding a submenu when the pointer is moving towards the submenu"),
703                                                    0,
704                                                    G_MAXINT,
705                                                    DEFAULT_POPDOWN_DELAY,
706                                                    G_PARAM_READWRITE));
707                                                    
708 }
709
710
711 static void 
712 gtk_menu_set_property (GObject      *object,
713                        guint         prop_id,
714                        const GValue *value,
715                        GParamSpec   *pspec)
716 {
717   GtkMenu *menu;
718   
719   menu = GTK_MENU (object);
720   
721   switch (prop_id)
722     {
723     case PROP_TEAROFF_STATE:
724       gtk_menu_set_tearoff_state (menu, g_value_get_boolean (value));
725       break;
726     case PROP_TEAROFF_TITLE:
727       gtk_menu_set_title (menu, g_value_get_string (value));
728       break;      
729     default:
730       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
731       break;
732     }
733 }
734
735 static void 
736 gtk_menu_get_property (GObject     *object,
737                        guint        prop_id,
738                        GValue      *value,
739                        GParamSpec  *pspec)
740 {
741   GtkMenu *menu;
742   
743   menu = GTK_MENU (object);
744   
745   switch (prop_id)
746     {
747     case PROP_TEAROFF_STATE:
748       g_value_set_boolean (value, gtk_menu_get_tearoff_state (menu));
749       break;
750     case PROP_TEAROFF_TITLE:
751       g_value_set_string (value, gtk_menu_get_title (menu));
752       break;
753     default:
754       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
755       break;
756     }
757 }
758
759 static void
760 gtk_menu_set_child_property (GtkContainer *container,
761                              GtkWidget    *child,
762                              guint         property_id,
763                              const GValue *value,
764                              GParamSpec   *pspec)
765 {
766   GtkMenu *menu = GTK_MENU (container);
767   AttachInfo *ai = get_attach_info (child);
768
769   switch (property_id)
770     {
771     case CHILD_PROP_LEFT_ATTACH:
772       ai->left_attach = g_value_get_int (value);
773       break;
774     case CHILD_PROP_RIGHT_ATTACH:
775       ai->right_attach = g_value_get_int (value);
776       break;
777     case CHILD_PROP_TOP_ATTACH:
778       ai->top_attach = g_value_get_int (value); 
779       break;
780     case CHILD_PROP_BOTTOM_ATTACH:
781       ai->bottom_attach = g_value_get_int (value);
782       break;
783
784     default:
785       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
786       return;
787     }
788
789   menu_queue_resize (menu);
790 }
791
792 static void
793 gtk_menu_get_child_property (GtkContainer *container,
794                              GtkWidget    *child,
795                              guint         property_id,
796                              GValue       *value,
797                              GParamSpec   *pspec)
798 {
799   AttachInfo *ai = get_attach_info (child);
800
801   switch (property_id)
802     {
803     case CHILD_PROP_LEFT_ATTACH:
804       g_value_set_int (value, ai->left_attach);
805       break;
806     case CHILD_PROP_RIGHT_ATTACH:
807       g_value_set_int (value, ai->right_attach);
808       break;
809     case CHILD_PROP_TOP_ATTACH:
810       g_value_set_int (value, ai->top_attach);
811       break;
812     case CHILD_PROP_BOTTOM_ATTACH:
813       g_value_set_int (value, ai->bottom_attach);
814       break;
815       
816     default:
817       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
818       return;
819     }
820 }
821
822 static gboolean
823 gtk_menu_window_event (GtkWidget *window,
824                        GdkEvent  *event,
825                        GtkWidget *menu)
826 {
827   gboolean handled = FALSE;
828
829   g_object_ref (window);
830   g_object_ref (menu);
831
832   switch (event->type)
833     {
834     case GDK_KEY_PRESS:
835     case GDK_KEY_RELEASE:
836       handled = gtk_widget_event (menu, event);
837       break;
838     default:
839       break;
840     }
841
842   g_object_unref (window);
843   g_object_unref (menu);
844
845   return handled;
846 }
847
848 static void
849 gtk_menu_window_size_request (GtkWidget      *window,
850                               GtkRequisition *requisition,
851                               GtkMenu        *menu)
852 {
853   GtkMenuPrivate *private = gtk_menu_get_private (menu);
854
855   if (private->have_position)
856     {
857       GdkScreen *screen = gtk_widget_get_screen (window);
858       GdkRectangle monitor;
859       
860       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
861
862       if (private->y + requisition->height > monitor.y + monitor.height)
863         requisition->height = monitor.y + monitor.height - private->y;
864
865       if (private->y < monitor.y)
866         requisition->height -= monitor.y - private->y;
867     }
868 }
869
870 static void
871 gtk_menu_init (GtkMenu *menu)
872 {
873   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
874
875   menu->parent_menu_item = NULL;
876   menu->old_active_menu_item = NULL;
877   menu->accel_group = NULL;
878   menu->position_func = NULL;
879   menu->position_func_data = NULL;
880   menu->toggle_size = 0;
881
882   menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
883                                                    "type", GTK_WINDOW_POPUP,
884                                                    "child", menu,
885                                                    NULL),
886                                      "signal::event", gtk_menu_window_event, menu,
887                                      "signal::size_request", gtk_menu_window_size_request, menu,
888                                      "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
889                                      NULL);
890   gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE);
891   gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
892
893   /* Refloat the menu, so that reference counting for the menu isn't
894    * affected by it being a child of the toplevel
895    */
896   GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
897   menu->needs_destruction_ref_count = TRUE;
898
899   menu->view_window = NULL;
900   menu->bin_window = NULL;
901
902   menu->scroll_offset = 0;
903   menu->scroll_step  = 0;
904   menu->timeout_id = 0;
905   menu->scroll_fast = FALSE;
906   
907   menu->tearoff_window = NULL;
908   menu->tearoff_hbox = NULL;
909   menu->torn_off = FALSE;
910   menu->tearoff_active = FALSE;
911   menu->tearoff_adjustment = NULL;
912   menu->tearoff_scrollbar = NULL;
913
914   menu->upper_arrow_visible = FALSE;
915   menu->lower_arrow_visible = FALSE;
916   menu->upper_arrow_prelight = FALSE;
917   menu->lower_arrow_prelight = FALSE;
918
919   priv->have_layout = FALSE;
920 }
921
922 static void
923 gtk_menu_destroy (GtkObject *object)
924 {
925   GtkMenu *menu;
926   GtkMenuAttachData *data;
927   GtkMenuPrivate *priv;
928
929   g_return_if_fail (GTK_IS_MENU (object));
930
931   menu = GTK_MENU (object);
932
933   gtk_menu_stop_scrolling (menu);
934   
935   data = g_object_get_data (G_OBJECT (object), attach_data_key);
936   if (data)
937     gtk_menu_detach (menu);
938   
939   gtk_menu_stop_navigating_submenu (menu);
940
941   if (menu->old_active_menu_item)
942     {
943       g_object_unref (menu->old_active_menu_item);
944       menu->old_active_menu_item = NULL;
945     }
946
947   /* Add back the reference count for being a child */
948   if (menu->needs_destruction_ref_count)
949     {
950       menu->needs_destruction_ref_count = FALSE;
951       g_object_ref (object);
952     }
953   
954   if (menu->accel_group)
955     {
956       g_object_unref (menu->accel_group);
957       menu->accel_group = NULL;
958     }
959
960   if (menu->toplevel)
961     gtk_widget_destroy (menu->toplevel);
962   if (menu->tearoff_window)
963     gtk_widget_destroy (menu->tearoff_window);
964
965   priv = gtk_menu_get_private (menu);
966
967   GTK_OBJECT_CLASS (parent_class)->destroy (object);
968 }
969
970 static void
971 gtk_menu_finalize (GObject *object)
972 {
973   GtkMenu *menu = GTK_MENU (object);
974
975   g_free (menu->accel_path);
976   
977   G_OBJECT_CLASS (parent_class)->finalize (object);
978 }
979
980 static void
981 menu_change_screen (GtkMenu   *menu,
982                     GdkScreen *new_screen)
983 {
984   GtkMenuPrivate *private = gtk_menu_get_private (menu);
985
986   if (gtk_widget_has_screen (GTK_WIDGET (menu)))
987     {
988       if (new_screen == gtk_widget_get_screen (GTK_WIDGET (menu)))
989         return;
990     }
991
992   if (menu->torn_off)
993     {
994       gtk_window_set_screen (GTK_WINDOW (menu->tearoff_window), new_screen);
995       gtk_menu_position (menu);
996     }
997
998   gtk_window_set_screen (GTK_WINDOW (menu->toplevel), new_screen);
999   private->monitor_num = -1;
1000 }
1001
1002 static void
1003 attach_widget_screen_changed (GtkWidget *attach_widget,
1004                               GdkScreen *previous_screen,
1005                               GtkMenu   *menu)
1006 {
1007   if (gtk_widget_has_screen (attach_widget) &&
1008       !g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
1009     {
1010       menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
1011     }
1012 }
1013
1014 void
1015 gtk_menu_attach_to_widget (GtkMenu             *menu,
1016                            GtkWidget           *attach_widget,
1017                            GtkMenuDetachFunc    detacher)
1018 {
1019   GtkMenuAttachData *data;
1020   GList *list;
1021   
1022   g_return_if_fail (GTK_IS_MENU (menu));
1023   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
1024   g_return_if_fail (detacher != NULL);
1025   
1026   /* keep this function in sync with gtk_widget_set_parent()
1027    */
1028   
1029   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1030   if (data)
1031     {
1032       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
1033                  g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)));
1034      return;
1035     }
1036   
1037   g_object_ref (menu);
1038   gtk_object_sink (GTK_OBJECT (menu));
1039   
1040   data = g_new (GtkMenuAttachData, 1);
1041   data->attach_widget = attach_widget;
1042   
1043   g_signal_connect (attach_widget, "screen_changed",
1044                     G_CALLBACK (attach_widget_screen_changed), menu);
1045   attach_widget_screen_changed (attach_widget, NULL, menu);
1046   
1047   data->detacher = detacher;
1048   g_object_set_data (G_OBJECT (menu), attach_data_key, data);
1049   list = g_object_steal_data (G_OBJECT (attach_widget), ATTACHED_MENUS);
1050   if (!g_list_find (list, menu))
1051     {
1052       list = g_list_prepend (list, menu);
1053     }
1054   g_object_set_data_full (G_OBJECT (attach_widget), ATTACHED_MENUS, list, (GtkDestroyNotify) g_list_free);
1055   
1056   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
1057     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
1058   
1059   /* we don't need to set the style here, since
1060    * we are a toplevel widget.
1061    */
1062
1063   /* Fallback title for menu comes from attach widget */
1064   gtk_menu_update_title (menu);
1065 }
1066
1067 GtkWidget*
1068 gtk_menu_get_attach_widget (GtkMenu *menu)
1069 {
1070   GtkMenuAttachData *data;
1071   
1072   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1073   
1074   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1075   if (data)
1076     return data->attach_widget;
1077   return NULL;
1078 }
1079
1080 void
1081 gtk_menu_detach (GtkMenu *menu)
1082 {
1083   GtkMenuAttachData *data;
1084   GList *list;
1085   
1086   g_return_if_fail (GTK_IS_MENU (menu));
1087   
1088   /* keep this function in sync with gtk_widget_unparent()
1089    */
1090   data = g_object_get_data (G_OBJECT (menu), attach_data_key);
1091   if (!data)
1092     {
1093       g_warning ("gtk_menu_detach(): menu is not attached");
1094       return;
1095     }
1096   g_object_set_data (G_OBJECT (menu), attach_data_key, NULL);
1097   
1098   g_signal_handlers_disconnect_by_func (data->attach_widget,
1099                                         (gpointer) attach_widget_screen_changed,
1100                                         menu);
1101
1102   data->detacher (data->attach_widget, menu);
1103   list = g_object_steal_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS);
1104   list = g_list_remove (list, menu);
1105   if (list)
1106     g_object_set_data_full (G_OBJECT (data->attach_widget), ATTACHED_MENUS, list, (GtkDestroyNotify) g_list_free);
1107   else
1108     g_object_set_data (G_OBJECT (data->attach_widget), ATTACHED_MENUS, NULL);
1109   
1110   if (GTK_WIDGET_REALIZED (menu))
1111     gtk_widget_unrealize (GTK_WIDGET (menu));
1112   
1113   g_free (data);
1114   
1115   /* Fallback title for menu comes from attach widget */
1116   gtk_menu_update_title (menu);
1117
1118   g_object_unref (menu);
1119 }
1120
1121 static void
1122 gtk_menu_remove (GtkContainer *container,
1123                  GtkWidget    *widget)
1124 {
1125   GtkMenu *menu;
1126   GtkMenuPrivate *priv;
1127
1128   g_return_if_fail (GTK_IS_MENU (container));
1129   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
1130
1131   menu = GTK_MENU (container);
1132   priv = gtk_menu_get_private (menu);
1133
1134   /* Clear out old_active_menu_item if it matches the item we are removing
1135    */
1136   if (menu->old_active_menu_item == widget)
1137     {
1138       g_object_unref (menu->old_active_menu_item);
1139       menu->old_active_menu_item = NULL;
1140     }
1141
1142   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
1143   g_object_set_data (G_OBJECT (widget), ATTACH_INFO_KEY, NULL);
1144
1145   menu_queue_resize (menu);
1146 }
1147
1148 GtkWidget*
1149 gtk_menu_new (void)
1150 {
1151   return g_object_new (GTK_TYPE_MENU, NULL);
1152 }
1153
1154 static void
1155 gtk_menu_real_insert (GtkMenuShell *menu_shell,
1156                       GtkWidget    *child,
1157                       gint          position)
1158 {
1159   GtkMenu *menu = GTK_MENU (menu_shell);
1160   AttachInfo *ai = get_attach_info (child);
1161
1162   ai->left_attach = -1;
1163   ai->right_attach = -1;
1164   ai->top_attach = -1;
1165   ai->bottom_attach = -1;
1166
1167   if (GTK_WIDGET_REALIZED (menu_shell))
1168     gtk_widget_set_parent_window (child, menu->bin_window);
1169
1170   GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
1171
1172   menu_queue_resize (menu);
1173 }
1174
1175 static void
1176 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
1177 {
1178   GtkWidget *widget;
1179   gint width, height;
1180
1181   widget = GTK_WIDGET (menu);
1182
1183   if (menu->torn_off)
1184     {
1185       GdkPixmap *pixmap;
1186       GdkGC *gc;
1187       GdkGCValues gc_values;
1188
1189       menu->tearoff_active = FALSE;
1190       menu->saved_scroll_offset = menu->scroll_offset;
1191       
1192       gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1193       gc = gdk_gc_new_with_values (widget->window,
1194                                    &gc_values, GDK_GC_SUBWINDOW);
1195       
1196       gdk_drawable_get_size (menu->tearoff_window->window, &width, &height);
1197       
1198       pixmap = gdk_pixmap_new (menu->tearoff_window->window,
1199                                width,
1200                                height,
1201                                -1);
1202
1203       gdk_draw_drawable (pixmap, gc,
1204                          menu->tearoff_window->window,
1205                          0, 0, 0, 0, -1, -1);
1206       g_object_unref (gc);
1207
1208       gtk_widget_set_size_request (menu->tearoff_window,
1209                                    width,
1210                                    height);
1211
1212       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
1213       g_object_unref (pixmap);
1214     }
1215 }
1216
1217 static gboolean
1218 popup_grab_on_window (GdkWindow *window,
1219                       guint32    activate_time)
1220 {
1221   if ((gdk_pointer_grab (window, TRUE,
1222                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1223                          GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
1224                          GDK_POINTER_MOTION_MASK,
1225                          NULL, NULL, activate_time) == 0))
1226     {
1227       if (gdk_keyboard_grab (window, TRUE,
1228                              activate_time) == 0)
1229         return TRUE;
1230       else
1231         {
1232           gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
1233                                       activate_time);
1234           return FALSE;
1235         }
1236     }
1237
1238   return FALSE;
1239 }
1240
1241 /**
1242  * gtk_menu_popup:
1243  * @menu: a #GtkMenu.
1244  * @parent_menu_shell: the menu shell containing the triggering menu item, or %NULL
1245  * @parent_menu_item: the menu item whose activation triggered the popup, or %NULL
1246  * @func: a user supplied function used to position the menu, or %NULL
1247  * @data: user supplied data to be passed to @func.
1248  * @button: the mouse button which was pressed to initiate the event.
1249  * @activate_time: the time at which the activation event occurred.
1250  *
1251  * Displays a menu and makes it available for selection.  Applications can use
1252  * this function to display context-sensitive menus, and will typically supply
1253  * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
1254  * parameters. The default menu positioning function will position the menu
1255  * at the current mouse cursor position.
1256  *
1257  * The @button parameter should be the mouse button pressed to initiate
1258  * the menu popup. If the menu popup was initiated by something other than
1259  * a mouse button press, such as a mouse button release or a keypress,
1260  * @button should be 0.
1261  *
1262  * The @activate_time parameter should be the time stamp of the event that
1263  * initiated the popup. If such an event is not available, use
1264  * gtk_get_current_event_time() instead.
1265  *
1266  */
1267 void
1268 gtk_menu_popup (GtkMenu             *menu,
1269                 GtkWidget           *parent_menu_shell,
1270                 GtkWidget           *parent_menu_item,
1271                 GtkMenuPositionFunc  func,
1272                 gpointer             data,
1273                 guint                button,
1274                 guint32              activate_time)
1275 {
1276   GtkWidget *widget;
1277   GtkWidget *xgrab_shell;
1278   GtkWidget *parent;
1279   GdkEvent *current_event;
1280   GtkMenuShell *menu_shell;
1281
1282   g_return_if_fail (GTK_IS_MENU (menu));
1283   
1284   widget = GTK_WIDGET (menu);
1285   menu_shell = GTK_MENU_SHELL (menu);
1286   
1287   menu_shell->parent_menu_shell = parent_menu_shell;
1288   
1289   /* Find the last viewable ancestor, and make an X grab on it
1290    */
1291   parent = GTK_WIDGET (menu);
1292   xgrab_shell = NULL;
1293   while (parent)
1294     {
1295       gboolean viewable = TRUE;
1296       GtkWidget *tmp = parent;
1297       
1298       while (tmp)
1299         {
1300           if (!GTK_WIDGET_MAPPED (tmp))
1301             {
1302               viewable = FALSE;
1303               break;
1304             }
1305           tmp = tmp->parent;
1306         }
1307       
1308       if (viewable)
1309         xgrab_shell = parent;
1310       
1311       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1312     }
1313
1314   /* We want to receive events generated when we map the menu; unfortunately,
1315    * since there is probably already an implicit grab in place from the
1316    * button that the user used to pop up the menu, we won't receive then --
1317    * in particular, the EnterNotify when the menu pops up under the pointer.
1318    *
1319    * If we are grabbing on a parent menu shell, no problem; just grab on
1320    * that menu shell first before popping up the window with owner_events = TRUE.
1321    *
1322    * When grabbing on the menu itself, things get more convuluted - we
1323    * we do an explicit grab on a specially created window with
1324    * owner_events = TRUE, which we override further down with a grab
1325    * on the menu. (We can't grab on the menu until it is mapped; we
1326    * probably could just leave the grab on the other window, with a
1327    * little reorganization of the code in gtkmenu*).
1328    */
1329   if (xgrab_shell && xgrab_shell != widget)
1330     {
1331       if (popup_grab_on_window (xgrab_shell->window, activate_time))
1332         GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
1333     }
1334   else
1335     {
1336       GdkWindow *transfer_window;
1337
1338       xgrab_shell = widget;
1339       transfer_window = menu_grab_transfer_window_get (menu);
1340       if (popup_grab_on_window (transfer_window, activate_time))
1341         GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
1342     }
1343
1344   if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
1345     {
1346       /* We failed to make our pointer/keyboard grab. Rather than leaving the user
1347        * with a stuck up window, we just abort here. Presumably the user will
1348        * try again.
1349        */
1350       menu_shell->parent_menu_shell = NULL;
1351       menu_grab_transfer_window_destroy (menu);
1352       return;
1353     }
1354
1355   menu_shell->active = TRUE;
1356   menu_shell->button = button;
1357
1358   /* If we are popping up the menu from something other than, a button
1359    * press then, as a heuristic, we ignore enter events for the menu
1360    * until we get a MOTION_NOTIFY.  
1361    */
1362
1363   current_event = gtk_get_current_event ();
1364   if (current_event)
1365     {
1366       if ((current_event->type != GDK_BUTTON_PRESS) &&
1367           (current_event->type != GDK_ENTER_NOTIFY))
1368         menu_shell->ignore_enter = TRUE;
1369
1370       gdk_event_free (current_event);
1371     }
1372   else
1373     menu_shell->ignore_enter = TRUE;
1374
1375   if (menu->torn_off)
1376     {
1377       gtk_menu_tearoff_bg_copy (menu);
1378
1379       gtk_menu_reparent (menu, menu->toplevel, FALSE);
1380     }
1381  
1382   if (parent_menu_shell) 
1383     {
1384       GtkWidget *toplevel;
1385
1386       toplevel = gtk_widget_get_toplevel (parent_menu_shell);
1387       if (GTK_IS_WINDOW (toplevel))
1388         gtk_window_group_add_window (_gtk_window_get_group (GTK_WINDOW (toplevel)), 
1389                                      GTK_WINDOW (menu->toplevel));
1390     }
1391   
1392   menu->parent_menu_item = parent_menu_item;
1393   menu->position_func = func;
1394   menu->position_func_data = data;
1395   menu_shell->activate_time = activate_time;
1396
1397   /* We need to show the menu here rather in the init function because
1398    * code expects to be able to tell if the menu is onscreen by
1399    * looking at the GTK_WIDGET_VISIBLE (menu)
1400    */
1401   gtk_widget_show (GTK_WIDGET (menu));
1402
1403   /* Position the menu, possibly changing the size request
1404    */
1405   gtk_menu_position (menu);
1406
1407   /* Compute the size of the toplevel and realize it so we
1408    * can scroll correctly.
1409    */
1410   {
1411     GtkRequisition tmp_request;
1412     GtkAllocation tmp_allocation = { 0, };
1413
1414     gtk_widget_size_request (menu->toplevel, &tmp_request);
1415     
1416     tmp_allocation.width = tmp_request.width;
1417     tmp_allocation.height = tmp_request.height;
1418
1419     gtk_widget_size_allocate (menu->toplevel, &tmp_allocation);
1420     
1421     gtk_widget_realize (GTK_WIDGET (menu));
1422   }
1423
1424   gtk_menu_scroll_to (menu, menu->scroll_offset);
1425
1426   /* Once everything is set up correctly, map the toplevel window on
1427      the screen.
1428    */
1429   gtk_widget_show (menu->toplevel);
1430
1431   if (xgrab_shell == widget)
1432     popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
1433   gtk_grab_add (GTK_WIDGET (menu));
1434 }
1435
1436 void
1437 gtk_menu_popdown (GtkMenu *menu)
1438 {
1439   GtkMenuPrivate *private;
1440   GtkMenuShell *menu_shell;
1441
1442   g_return_if_fail (GTK_IS_MENU (menu));
1443   
1444   menu_shell = GTK_MENU_SHELL (menu);
1445   private = gtk_menu_get_private (menu);
1446   
1447   menu_shell->parent_menu_shell = NULL;
1448   menu_shell->active = FALSE;
1449   menu_shell->ignore_enter = FALSE;
1450
1451   private->have_position = FALSE;
1452
1453   gtk_menu_stop_scrolling (menu);
1454   
1455   gtk_menu_stop_navigating_submenu (menu);
1456   
1457   if (menu_shell->active_menu_item)
1458     {
1459       if (menu->old_active_menu_item)
1460         g_object_unref (menu->old_active_menu_item);
1461       menu->old_active_menu_item = menu_shell->active_menu_item;
1462       g_object_ref (menu->old_active_menu_item);
1463     }
1464
1465   gtk_menu_shell_deselect (menu_shell);
1466   
1467   /* The X Grab, if present, will automatically be removed when we hide
1468    * the window */
1469   gtk_widget_hide (menu->toplevel);
1470   gtk_window_group_add_window (_gtk_window_get_group (NULL), GTK_WINDOW (menu->toplevel));
1471
1472   if (menu->torn_off)
1473     {
1474       gtk_widget_set_size_request (menu->tearoff_window, -1, -1);
1475       
1476       if (GTK_BIN (menu->toplevel)->child) 
1477         {
1478           gtk_menu_reparent (menu, menu->tearoff_hbox, TRUE);
1479         } 
1480       else
1481         {
1482           /* We popped up the menu from the tearoff, so we need to 
1483            * release the grab - we aren't actually hiding the menu.
1484            */
1485           if (menu_shell->have_xgrab)
1486             {
1487               GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
1488               
1489               gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
1490               gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
1491             }
1492         }
1493
1494       /* gtk_menu_popdown is called each time a menu item is selected from
1495        * a torn off menu. Only scroll back to the saved position if the
1496        * non-tearoff menu was popped down.
1497        */
1498       if (!menu->tearoff_active)
1499         gtk_menu_scroll_to (menu, menu->saved_scroll_offset);
1500       menu->tearoff_active = TRUE;
1501     }
1502   else
1503     gtk_widget_hide (GTK_WIDGET (menu));
1504
1505   menu_shell->have_xgrab = FALSE;
1506   gtk_grab_remove (GTK_WIDGET (menu));
1507
1508   menu_grab_transfer_window_destroy (menu);
1509 }
1510
1511 GtkWidget*
1512 gtk_menu_get_active (GtkMenu *menu)
1513 {
1514   GtkWidget *child;
1515   GList *children;
1516   
1517   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1518   
1519   if (!menu->old_active_menu_item)
1520     {
1521       child = NULL;
1522       children = GTK_MENU_SHELL (menu)->children;
1523       
1524       while (children)
1525         {
1526           child = children->data;
1527           children = children->next;
1528           
1529           if (GTK_BIN (child)->child)
1530             break;
1531           child = NULL;
1532         }
1533       
1534       menu->old_active_menu_item = child;
1535       if (menu->old_active_menu_item)
1536         g_object_ref (menu->old_active_menu_item);
1537     }
1538   
1539   return menu->old_active_menu_item;
1540 }
1541
1542 void
1543 gtk_menu_set_active (GtkMenu *menu,
1544                      guint    index)
1545 {
1546   GtkWidget *child;
1547   GList *tmp_list;
1548   
1549   g_return_if_fail (GTK_IS_MENU (menu));
1550   
1551   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
1552   if (tmp_list)
1553     {
1554       child = tmp_list->data;
1555       if (GTK_BIN (child)->child)
1556         {
1557           if (menu->old_active_menu_item)
1558             g_object_unref (menu->old_active_menu_item);
1559           menu->old_active_menu_item = child;
1560           g_object_ref (menu->old_active_menu_item);
1561         }
1562     }
1563 }
1564
1565 void
1566 gtk_menu_set_accel_group (GtkMenu       *menu,
1567                           GtkAccelGroup *accel_group)
1568 {
1569   g_return_if_fail (GTK_IS_MENU (menu));
1570   
1571   if (menu->accel_group != accel_group)
1572     {
1573       if (menu->accel_group)
1574         g_object_unref (menu->accel_group);
1575       menu->accel_group = accel_group;
1576       if (menu->accel_group)
1577         g_object_ref (menu->accel_group);
1578       _gtk_menu_refresh_accel_paths (menu, TRUE);
1579     }
1580 }
1581
1582 GtkAccelGroup*
1583 gtk_menu_get_accel_group (GtkMenu *menu)
1584 {
1585   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1586
1587   return menu->accel_group;
1588 }
1589
1590 static gboolean
1591 gtk_menu_real_can_activate_accel (GtkWidget *widget,
1592                                   guint      signal_id)
1593 {
1594   /* Menu items chain here to figure whether they can activate their
1595    * accelerators.  Unlike ordinary widgets, menus allow accel
1596    * activation even if invisible since that's the usual case for
1597    * submenus/popup-menus. however, the state of the attach widget
1598    * affects the "activeness" of the menu.
1599    */
1600   GtkWidget *awidget = gtk_menu_get_attach_widget (GTK_MENU (widget));
1601
1602   if (awidget)
1603     return gtk_widget_can_activate_accel (awidget, signal_id);
1604   else
1605     return GTK_WIDGET_IS_SENSITIVE (widget);
1606 }
1607
1608 /**
1609  * gtk_menu_set_accel_path
1610  * @menu:       a valid #GtkMenu
1611  * @accel_path: a valid accelerator path
1612  *
1613  * Sets an accelerator path for this menu from which accelerator paths
1614  * for its immediate children, its menu items, can be constructed.
1615  * The main purpose of this function is to spare the programmer the
1616  * inconvenience of having to call gtk_menu_item_set_accel_path() on
1617  * each menu item that should support runtime user changable accelerators.
1618  * Instead, by just calling gtk_menu_set_accel_path() on their parent,
1619  * each menu item of this menu, that contains a label describing its purpose,
1620  * automatically gets an accel path assigned. For example, a menu containing
1621  * menu items "New" and "Exit", will, after 
1622  * <literal>gtk_menu_set_accel_path (menu, "&lt;Gnumeric-Sheet&gt;/File");</literal>
1623  * has been called, assign its items the accel paths:
1624  * <literal>"&lt;Gnumeric-Sheet&gt;/File/New"</literal> and <literal>"&lt;Gnumeric-Sheet&gt;/File/Exit"</literal>.
1625  * Assigning accel paths to menu items then enables the user to change
1626  * their accelerators at runtime. More details about accelerator paths
1627  * and their default setups can be found at gtk_accel_map_add_entry().
1628  */
1629 void
1630 gtk_menu_set_accel_path (GtkMenu     *menu,
1631                          const gchar *accel_path)
1632 {
1633   g_return_if_fail (GTK_IS_MENU (menu));
1634   if (accel_path)
1635     g_return_if_fail (accel_path[0] == '<' && strchr (accel_path, '/')); /* simplistic check */
1636
1637   g_free (menu->accel_path);
1638   menu->accel_path = g_strdup (accel_path);
1639   if (menu->accel_path)
1640     _gtk_menu_refresh_accel_paths (menu, FALSE);
1641 }
1642
1643 typedef struct {
1644   GtkMenu *menu;
1645   gboolean group_changed;
1646 } AccelPropagation;
1647
1648 static void
1649 refresh_accel_paths_foreach (GtkWidget *widget,
1650                              gpointer   data)
1651 {
1652   AccelPropagation *prop = data;
1653
1654   if (GTK_IS_MENU_ITEM (widget))        /* should always be true */
1655     _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
1656                                        prop->menu->accel_path,
1657                                        prop->menu->accel_group,
1658                                        prop->group_changed);
1659 }
1660
1661 static void
1662 _gtk_menu_refresh_accel_paths (GtkMenu *menu,
1663                                gboolean group_changed)
1664 {
1665   g_return_if_fail (GTK_IS_MENU (menu));
1666       
1667   if (menu->accel_path && menu->accel_group)
1668     {
1669       AccelPropagation prop;
1670
1671       prop.menu = menu;
1672       prop.group_changed = group_changed;
1673       gtk_container_foreach (GTK_CONTAINER (menu),
1674                              refresh_accel_paths_foreach,
1675                              &prop);
1676     }
1677 }
1678
1679 void
1680 gtk_menu_reposition (GtkMenu *menu)
1681 {
1682   g_return_if_fail (GTK_IS_MENU (menu));
1683
1684   if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
1685     gtk_menu_position (menu);
1686 }
1687
1688 static void
1689 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
1690                             GtkMenu       *menu)
1691 {
1692   g_return_if_fail (GTK_IS_MENU (menu));
1693
1694   if (adjustment->value != menu->scroll_offset)
1695     gtk_menu_scroll_to (menu, adjustment->value);
1696 }
1697
1698 static void
1699 gtk_menu_set_tearoff_hints (GtkMenu *menu,
1700                             gint     width)
1701 {
1702   GdkGeometry geometry_hints;
1703   
1704   if (!menu->tearoff_window)
1705     return;
1706
1707   if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1708     {
1709       gtk_widget_size_request (menu->tearoff_scrollbar, NULL);
1710       width += menu->tearoff_scrollbar->requisition.width;
1711     }
1712
1713   geometry_hints.min_width = width;
1714   geometry_hints.max_width = width;
1715     
1716   geometry_hints.min_height = 0;
1717   geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height + 10;
1718
1719   gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
1720                                  NULL,
1721                                  &geometry_hints,
1722                                  GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
1723 }
1724
1725 static void
1726 gtk_menu_update_title (GtkMenu *menu)
1727 {
1728   if (menu->tearoff_window)
1729     {
1730       const gchar *title;
1731       GtkWidget *attach_widget;
1732
1733       title = gtk_menu_get_title (menu);
1734       if (!title)
1735         {
1736           attach_widget = gtk_menu_get_attach_widget (menu);
1737           if (GTK_IS_MENU_ITEM (attach_widget))
1738             {
1739               GtkWidget *child = GTK_BIN (attach_widget)->child;
1740               if (GTK_IS_LABEL (child))
1741                 title = gtk_label_get_text (GTK_LABEL (child));
1742             }
1743         }
1744       
1745       if (title)
1746         gtk_window_set_title (GTK_WINDOW (menu->tearoff_window), title);
1747     }
1748 }
1749
1750 static GtkWidget*
1751 gtk_menu_get_toplevel (GtkWidget *menu)
1752 {
1753   GtkWidget *attach, *toplevel;
1754
1755   attach = gtk_menu_get_attach_widget (GTK_MENU (menu));
1756
1757   if (GTK_IS_MENU_ITEM (attach))
1758     attach = attach->parent;
1759
1760   if (GTK_IS_MENU (attach))
1761     return gtk_menu_get_toplevel (attach);
1762   else if (GTK_IS_WIDGET (attach))
1763     {
1764       toplevel = gtk_widget_get_toplevel (attach);
1765       if (GTK_WIDGET_TOPLEVEL (toplevel)) 
1766         return toplevel;
1767     }
1768
1769   return NULL;
1770 }
1771
1772 static void
1773 tearoff_window_destroyed (GtkWidget *widget,
1774                           GtkMenu   *menu)
1775 {
1776   gtk_menu_set_tearoff_state (menu, FALSE);
1777 }
1778
1779 void       
1780 gtk_menu_set_tearoff_state (GtkMenu  *menu,
1781                             gboolean  torn_off)
1782 {
1783   gint width, height;
1784   
1785   g_return_if_fail (GTK_IS_MENU (menu));
1786
1787   if (menu->torn_off != torn_off)
1788     {
1789       menu->torn_off = torn_off;
1790       menu->tearoff_active = torn_off;
1791       
1792       if (menu->torn_off)
1793         {
1794           if (GTK_WIDGET_VISIBLE (menu))
1795             gtk_menu_popdown (menu);
1796
1797           if (!menu->tearoff_window)
1798             {
1799               GtkWidget *toplevel;
1800
1801               menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
1802                                                      "type", GTK_WINDOW_TOPLEVEL,
1803                                                      "screen", gtk_widget_get_screen (menu->toplevel),
1804                                                      "app_paintable", TRUE,
1805                                                      NULL);
1806
1807               
1808               gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
1809                                         GDK_WINDOW_TYPE_HINT_MENU);
1810               gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0);
1811               g_signal_connect (menu->tearoff_window, "destroy",
1812                                 G_CALLBACK (tearoff_window_destroyed), menu);
1813               g_signal_connect (menu->tearoff_window, "event",
1814                                 G_CALLBACK (gtk_menu_window_event), menu);
1815
1816               gtk_menu_update_title (menu);
1817
1818               gtk_widget_realize (menu->tearoff_window);
1819
1820               toplevel = gtk_menu_get_toplevel (GTK_WIDGET (menu));
1821               if (toplevel != NULL)
1822                 gtk_window_set_transient_for (GTK_WINDOW (menu->tearoff_window),
1823                                               GTK_WINDOW (toplevel));
1824               
1825               menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
1826               gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
1827
1828               gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
1829               menu->tearoff_adjustment =
1830                 GTK_ADJUSTMENT (gtk_adjustment_new (0,
1831                                                     0,
1832                                                     GTK_WIDGET (menu)->requisition.height,
1833                                                     MENU_SCROLL_STEP2,
1834                                                     height/2,
1835                                                     height));
1836               g_object_connect (menu->tearoff_adjustment,
1837                                 "signal::value_changed", gtk_menu_scrollbar_changed, menu,
1838                                 NULL);
1839               menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
1840
1841               gtk_box_pack_end (GTK_BOX (menu->tearoff_hbox),
1842                                 menu->tearoff_scrollbar,
1843                                 FALSE, FALSE, 0);
1844               
1845               if (menu->tearoff_adjustment->upper > height)
1846                 gtk_widget_show (menu->tearoff_scrollbar);
1847               
1848               gtk_widget_show (menu->tearoff_hbox);
1849             }
1850           
1851           gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
1852
1853           gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, NULL);
1854
1855           /* Update menu->requisition
1856            */
1857           gtk_widget_size_request (GTK_WIDGET (menu), NULL);
1858   
1859           gtk_menu_set_tearoff_hints (menu, width);
1860             
1861           gtk_widget_realize (menu->tearoff_window);
1862           gtk_menu_position (menu);
1863           
1864           gtk_widget_show (GTK_WIDGET (menu));
1865           gtk_widget_show (menu->tearoff_window);
1866
1867           gtk_menu_scroll_to (menu, 0);
1868
1869         }
1870       else
1871         {
1872           gtk_widget_hide (GTK_WIDGET (menu));
1873           gtk_widget_hide (menu->tearoff_window);
1874           if (GTK_IS_CONTAINER (menu->toplevel))
1875             gtk_menu_reparent (menu, menu->toplevel, FALSE);
1876           gtk_widget_destroy (menu->tearoff_window);
1877           
1878           menu->tearoff_window = NULL;
1879           menu->tearoff_hbox = NULL;
1880           menu->tearoff_scrollbar = NULL;
1881           menu->tearoff_adjustment = NULL;
1882         }
1883
1884       g_object_notify (G_OBJECT (menu), "tearoff_state");
1885     }
1886 }
1887
1888 /**
1889  * gtk_menu_get_tearoff_state:
1890  * @menu: a #GtkMenu
1891  *
1892  * Returns whether the menu is torn off. See
1893  * gtk_menu_set_tearoff_state ().
1894  *
1895  * Return value: %TRUE if the menu is currently torn off.
1896  **/
1897 gboolean
1898 gtk_menu_get_tearoff_state (GtkMenu *menu)
1899 {
1900   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
1901
1902   return menu->torn_off;
1903 }
1904
1905 /**
1906  * gtk_menu_set_title:
1907  * @menu: a #GtkMenu
1908  * @title: a string containing the title for the menu.
1909  * 
1910  * Sets the title string for the menu.  The title is displayed when the menu
1911  * is shown as a tearoff menu.
1912  **/
1913 void       
1914 gtk_menu_set_title (GtkMenu     *menu,
1915                     const gchar *title)
1916 {
1917   g_return_if_fail (GTK_IS_MENU (menu));
1918
1919   if (title)
1920     g_object_set_data_full (G_OBJECT (menu), "gtk-menu-title",
1921                             g_strdup (title), (GtkDestroyNotify) g_free);
1922   else
1923     g_object_set_data (G_OBJECT (menu), "gtk-menu-title", NULL);
1924     
1925   gtk_menu_update_title (menu);
1926   g_object_notify (G_OBJECT (menu), "tearoff_title");
1927 }
1928
1929 /**
1930  * gtk_menu_get_title:
1931  * @menu: a #GtkMenu
1932  *
1933  * Returns the title of the menu. See gtk_menu_set_title().
1934  *
1935  * Return value: the title of the menu, or %NULL if the menu has no
1936  * title set on it. This string is owned by the widget and should
1937  * not be modified or freed.
1938  **/
1939 G_CONST_RETURN gchar *
1940 gtk_menu_get_title (GtkMenu *menu)
1941 {
1942   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1943
1944   return g_object_get_data (G_OBJECT (menu), "gtk-menu-title");
1945 }
1946
1947 void
1948 gtk_menu_reorder_child (GtkMenu   *menu,
1949                         GtkWidget *child,
1950                         gint       position)
1951 {
1952   GtkMenuShell *menu_shell;
1953
1954   g_return_if_fail (GTK_IS_MENU (menu));
1955   g_return_if_fail (GTK_IS_MENU_ITEM (child));
1956
1957   menu_shell = GTK_MENU_SHELL (menu);
1958
1959   if (g_list_find (menu_shell->children, child))
1960     {   
1961       menu_shell->children = g_list_remove (menu_shell->children, child);
1962       menu_shell->children = g_list_insert (menu_shell->children, child, position);
1963
1964       menu_queue_resize (menu);
1965     }   
1966 }
1967
1968 static void
1969 gtk_menu_style_set (GtkWidget *widget,
1970                     GtkStyle  *previous_style)
1971 {
1972   if (GTK_WIDGET_REALIZED (widget))
1973     {
1974       GtkMenu *menu = GTK_MENU (widget);
1975       
1976       gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
1977       gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
1978       gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1979     }
1980 }
1981
1982 static void
1983 gtk_menu_realize (GtkWidget *widget)
1984 {
1985   GdkWindowAttr attributes;
1986   gint attributes_mask;
1987   gint border_width;
1988   GtkMenu *menu;
1989   GtkWidget *child;
1990   GList *children;
1991   guint vertical_padding;
1992   
1993   g_return_if_fail (GTK_IS_MENU (widget));
1994
1995   menu = GTK_MENU (widget);
1996   
1997   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1998   
1999   attributes.window_type = GDK_WINDOW_CHILD;
2000   attributes.x = widget->allocation.x;
2001   attributes.y = widget->allocation.y;
2002   attributes.width = widget->allocation.width;
2003   attributes.height = widget->allocation.height;
2004   attributes.wclass = GDK_INPUT_OUTPUT;
2005   attributes.visual = gtk_widget_get_visual (widget);
2006   attributes.colormap = gtk_widget_get_colormap (widget);
2007   
2008   attributes.event_mask = gtk_widget_get_events (widget);
2009
2010   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
2011                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
2012   
2013   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
2014   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
2015   gdk_window_set_user_data (widget->window, widget);
2016   
2017   border_width = GTK_CONTAINER (widget)->border_width;
2018
2019   gtk_widget_style_get (GTK_WIDGET (menu),
2020                         "vertical-padding", &vertical_padding,
2021                         NULL);
2022   
2023   attributes.x = border_width + widget->style->xthickness;
2024   attributes.y = border_width + widget->style->ythickness + vertical_padding;
2025   attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
2026   attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
2027
2028   if (menu->upper_arrow_visible)
2029     {
2030       attributes.y += MENU_SCROLL_ARROW_HEIGHT;
2031       attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
2032     }
2033   if (menu->lower_arrow_visible)
2034     attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
2035
2036   menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
2037   gdk_window_set_user_data (menu->view_window, menu);
2038
2039   attributes.x = 0;
2040   attributes.y = 0;
2041   attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness + vertical_padding) * 2);
2042   
2043   menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
2044   gdk_window_set_user_data (menu->bin_window, menu);
2045
2046   children = GTK_MENU_SHELL (menu)->children;
2047   while (children)
2048     {
2049       child = children->data;
2050       children = children->next;
2051           
2052       gtk_widget_set_parent_window (child, menu->bin_window);
2053     }
2054   
2055   widget->style = gtk_style_attach (widget->style, widget->window);
2056   gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
2057   gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
2058   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
2059
2060   if (GTK_MENU_SHELL (widget)->active_menu_item)
2061     gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
2062                                   GTK_MENU_SHELL (widget)->active_menu_item);
2063
2064   gdk_window_show (menu->bin_window);
2065   gdk_window_show (menu->view_window);
2066 }
2067
2068 static gboolean 
2069 gtk_menu_focus (GtkWidget       *widget,
2070                 GtkDirectionType direction)
2071 {
2072   /*
2073    * A menu or its menu items cannot have focus
2074    */
2075   return FALSE;
2076 }
2077
2078 /* See notes in gtk_menu_popup() for information about the "grab transfer window"
2079  */
2080 static GdkWindow *
2081 menu_grab_transfer_window_get (GtkMenu *menu)
2082 {
2083   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2084   if (!window)
2085     {
2086       GdkWindowAttr attributes;
2087       gint attributes_mask;
2088       
2089       attributes.x = -100;
2090       attributes.y = -100;
2091       attributes.width = 10;
2092       attributes.height = 10;
2093       attributes.window_type = GDK_WINDOW_TEMP;
2094       attributes.wclass = GDK_INPUT_ONLY;
2095       attributes.override_redirect = TRUE;
2096       attributes.event_mask = 0;
2097
2098       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
2099       
2100       window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
2101                                &attributes, attributes_mask);
2102       gdk_window_set_user_data (window, menu);
2103
2104       gdk_window_show (window);
2105
2106       g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", window);
2107     }
2108
2109   return window;
2110 }
2111
2112 static void
2113 menu_grab_transfer_window_destroy (GtkMenu *menu)
2114 {
2115   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
2116   if (window)
2117     {
2118       gdk_window_set_user_data (window, NULL);
2119       gdk_window_destroy (window);
2120       g_object_set_data (G_OBJECT (menu), "gtk-menu-transfer-window", NULL);
2121     }
2122 }
2123
2124 static void
2125 gtk_menu_unrealize (GtkWidget *widget)
2126 {
2127   GtkMenu *menu;
2128
2129   g_return_if_fail (GTK_IS_MENU (widget));
2130
2131   menu = GTK_MENU (widget);
2132
2133   menu_grab_transfer_window_destroy (menu);
2134
2135   gdk_window_set_user_data (menu->view_window, NULL);
2136   gdk_window_destroy (menu->view_window);
2137   menu->view_window = NULL;
2138
2139   gdk_window_set_user_data (menu->bin_window, NULL);
2140   gdk_window_destroy (menu->bin_window);
2141   menu->bin_window = NULL;
2142
2143   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2144 }
2145
2146 static void
2147 gtk_menu_size_request (GtkWidget      *widget,
2148                        GtkRequisition *requisition)
2149 {
2150   gint i;
2151   GtkMenu *menu;
2152   GtkMenuShell *menu_shell;
2153   GtkWidget *child;
2154   GList *children;
2155   guint max_toggle_size;
2156   guint max_accel_width;
2157   guint vertical_padding;
2158   GtkRequisition child_requisition;
2159   GtkMenuPrivate *priv;
2160   
2161   g_return_if_fail (GTK_IS_MENU (widget));
2162   g_return_if_fail (requisition != NULL);
2163   
2164   menu = GTK_MENU (widget);
2165   menu_shell = GTK_MENU_SHELL (widget);
2166   priv = gtk_menu_get_private (menu);
2167   
2168   requisition->width = 0;
2169   requisition->height = 0;
2170   
2171   max_toggle_size = 0;
2172   max_accel_width = 0;
2173   
2174   g_free (priv->heights);
2175   priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
2176   priv->heights_length = gtk_menu_get_n_rows (menu);
2177
2178   children = menu_shell->children;
2179   while (children)
2180     {
2181       gint part;
2182       gint toggle_size;
2183       guint l, r, t, b;
2184
2185       child = children->data;
2186       children = children->next;
2187       
2188       if (! GTK_WIDGET_VISIBLE (child))
2189         continue;
2190
2191       get_effective_child_attach (child, &l, &r, &t, &b);
2192
2193       /* It's important to size_request the child
2194        * before doing the toggle size request, in
2195        * case the toggle size request depends on the size
2196        * request of a child of the child (e.g. for ImageMenuItem)
2197        */
2198
2199        GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
2200        gtk_widget_size_request (child, &child_requisition);
2201
2202        gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
2203        max_toggle_size = MAX (max_toggle_size, toggle_size);
2204        max_accel_width = MAX (max_accel_width,
2205                               GTK_MENU_ITEM (child)->accelerator_width);
2206
2207        part = child_requisition.width / (r - l);
2208        requisition->width = MAX (requisition->width, part);
2209
2210        part = MAX (child_requisition.height, toggle_size) / (b - t);
2211        priv->heights[t] = MAX (priv->heights[t], part);
2212     }
2213
2214   for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2215     requisition->height += priv->heights[i];
2216
2217   requisition->width += max_toggle_size + max_accel_width;
2218   requisition->width *= gtk_menu_get_n_columns (menu);
2219   requisition->width += (GTK_CONTAINER (menu)->border_width +
2220                          widget->style->xthickness) * 2;
2221
2222   gtk_widget_style_get (GTK_WIDGET (menu),
2223                         "vertical-padding", &vertical_padding,
2224                         NULL);
2225   requisition->height += (GTK_CONTAINER (menu)->border_width + vertical_padding +
2226                           widget->style->ythickness) * 2;
2227   
2228   menu->toggle_size = max_toggle_size;
2229
2230   /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
2231    */
2232   if (menu->tearoff_active)
2233     gtk_menu_set_tearoff_hints (menu, requisition->width);
2234 }
2235
2236 static void
2237 gtk_menu_size_allocate (GtkWidget     *widget,
2238                         GtkAllocation *allocation)
2239 {
2240   GtkMenu *menu;
2241   GtkMenuShell *menu_shell;
2242   GtkWidget *child;
2243   GtkAllocation child_allocation;
2244   GtkRequisition child_requisition;
2245   GtkMenuPrivate *priv;
2246   GList *children;
2247   gint x, y;
2248   gint width, height;
2249   guint vertical_padding;
2250
2251   g_return_if_fail (GTK_IS_MENU (widget));
2252   g_return_if_fail (allocation != NULL);
2253   
2254   menu = GTK_MENU (widget);
2255   menu_shell = GTK_MENU_SHELL (widget);
2256   priv = gtk_menu_get_private (menu);
2257
2258   widget->allocation = *allocation;
2259   gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition);
2260
2261   gtk_widget_style_get (GTK_WIDGET (menu),
2262                         "vertical-padding", &vertical_padding,
2263                         NULL);
2264   
2265   x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness;
2266   y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness + vertical_padding;
2267
2268   width = MAX (1, allocation->width - x * 2);
2269   height = MAX (1, allocation->height - y * 2);
2270
2271   child_requisition.width -= x * 2;
2272   child_requisition.height -= y * 2;
2273
2274   if (menu_shell->active)
2275     gtk_menu_scroll_to (menu, menu->scroll_offset);
2276   
2277   if (menu->upper_arrow_visible && !menu->tearoff_active)
2278     {
2279       y += MENU_SCROLL_ARROW_HEIGHT;
2280       height -= MENU_SCROLL_ARROW_HEIGHT;
2281     }
2282   
2283   if (menu->lower_arrow_visible && !menu->tearoff_active)
2284     height -= MENU_SCROLL_ARROW_HEIGHT;
2285   
2286   if (GTK_WIDGET_REALIZED (widget))
2287     {
2288       gdk_window_move_resize (widget->window,
2289                               allocation->x, allocation->y,
2290                               allocation->width, allocation->height);
2291
2292       gdk_window_move_resize (menu->view_window,
2293                               x,
2294                               y,
2295                               width,
2296                               height);
2297     }
2298
2299   if (menu_shell->children)
2300     {
2301       gint base_width = width / gtk_menu_get_n_columns (menu);
2302
2303       children = menu_shell->children;
2304       while (children)
2305         {
2306           child = children->data;
2307           children = children->next;
2308
2309           if (GTK_WIDGET_VISIBLE (child))
2310             {
2311               gint i;
2312               guint l, r, t, b;
2313
2314               get_effective_child_attach (child, &l, &r, &t, &b);
2315
2316               if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
2317                 {
2318                   guint tmp;
2319                   tmp = gtk_menu_get_n_columns (menu) - l;
2320                   l = gtk_menu_get_n_columns (menu) - r;
2321                   r = tmp;
2322                 }
2323
2324               child_allocation.width = (r - l) * base_width;
2325               child_allocation.height = 0;
2326               child_allocation.x = l * base_width;
2327               child_allocation.y = 0;
2328
2329               for (i = 0; i < b; i++)
2330                 {
2331                   if (i < t)
2332                     child_allocation.y += priv->heights[i];
2333                   else
2334                     child_allocation.height += priv->heights[i];
2335                 }
2336
2337               gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
2338                                                   menu->toggle_size);
2339
2340               gtk_widget_size_allocate (child, &child_allocation);
2341               gtk_widget_queue_draw (child);
2342             }
2343         }
2344       
2345       /* Resize the item window */
2346       if (GTK_WIDGET_REALIZED (widget))
2347         {
2348           gint i;
2349           gint width, height;
2350
2351           height = 0;
2352           for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
2353             height += priv->heights[i];
2354
2355           width = gtk_menu_get_n_columns (menu) * base_width;
2356           gdk_window_resize (menu->bin_window, width, height);
2357         }
2358
2359       if (menu->tearoff_active)
2360         {
2361           if (allocation->height >= widget->requisition.height)
2362             {
2363               if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
2364                 {
2365                   gtk_widget_hide (menu->tearoff_scrollbar);
2366                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2367
2368                   gtk_menu_scroll_to (menu, 0);
2369                 }
2370             }
2371           else
2372             {
2373               menu->tearoff_adjustment->upper = widget->requisition.height;
2374               menu->tearoff_adjustment->page_size = allocation->height;
2375               
2376               if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size >
2377                   menu->tearoff_adjustment->upper)
2378                 {
2379                   gint value;
2380                   value = menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size;
2381                   if (value < 0)
2382                     value = 0;
2383                   gtk_menu_scroll_to (menu, value);
2384                 }
2385               
2386               gtk_adjustment_changed (menu->tearoff_adjustment);
2387               
2388               if (!GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
2389                 {
2390                   gtk_widget_show (menu->tearoff_scrollbar);
2391                   gtk_menu_set_tearoff_hints (menu, allocation->width);
2392                 }
2393             }
2394         }
2395     }
2396 }
2397
2398 static void
2399 gtk_menu_paint (GtkWidget      *widget,
2400                 GdkEventExpose *event)
2401 {
2402   GtkMenu *menu;
2403   gint width, height;
2404   gint border_x, border_y;
2405   guint vertical_padding;
2406   
2407   g_return_if_fail (GTK_IS_MENU (widget));
2408
2409   menu = GTK_MENU (widget);
2410
2411   gtk_widget_style_get (GTK_WIDGET (menu),
2412                         "vertical-padding", &vertical_padding,
2413                         NULL);
2414   
2415   border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
2416   border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
2417   gdk_drawable_get_size (widget->window, &width, &height);
2418
2419   if (event->window == widget->window)
2420     {
2421       gint arrow_space = MENU_SCROLL_ARROW_HEIGHT - 2 * widget->style->ythickness;
2422       gint arrow_size = 0.7 * arrow_space;
2423         
2424       gtk_paint_box (widget->style,
2425                      widget->window,
2426                      GTK_STATE_NORMAL,
2427                      GTK_SHADOW_OUT,
2428                      NULL, widget, "menu",
2429                      0, 0, -1, -1);
2430       if (menu->upper_arrow_visible && !menu->tearoff_active)
2431         {
2432           gtk_paint_box (widget->style,
2433                          widget->window,
2434                          menu->upper_arrow_prelight ?
2435                          GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
2436                          GTK_SHADOW_OUT,
2437                          NULL, widget, "menu",
2438                          border_x,
2439                          border_y,
2440                          width - 2 * border_x,
2441                          MENU_SCROLL_ARROW_HEIGHT);
2442           
2443           gtk_paint_arrow (widget->style,
2444                            widget->window,
2445                            menu->upper_arrow_prelight ?
2446                            GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
2447                            GTK_SHADOW_OUT,
2448                            NULL, widget, "menu_scroll_arrow_up",
2449                            GTK_ARROW_UP,
2450                            TRUE,
2451                            (width - arrow_size ) / 2,
2452                            border_y + widget->style->ythickness + (arrow_space - arrow_size)/2,
2453                            arrow_size, arrow_size);
2454         }
2455   
2456       if (menu->lower_arrow_visible && !menu->tearoff_active)
2457         {
2458           gtk_paint_box (widget->style,
2459                          widget->window,
2460                          menu->lower_arrow_prelight ?
2461                          GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
2462                          GTK_SHADOW_OUT,
2463                          NULL, widget, "menu",
2464                          border_x,
2465                          height - border_y - MENU_SCROLL_ARROW_HEIGHT,
2466                          width - 2*border_x,
2467                          MENU_SCROLL_ARROW_HEIGHT);
2468           
2469           gtk_paint_arrow (widget->style,
2470                            widget->window,
2471                            menu->lower_arrow_prelight ?
2472                            GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
2473                            GTK_SHADOW_OUT,
2474                            NULL, widget, "menu_scroll_arrow_down",
2475                            GTK_ARROW_DOWN,
2476                            TRUE,
2477                            (width - arrow_size) / 2,
2478                            height - border_y - MENU_SCROLL_ARROW_HEIGHT +
2479                               widget->style->ythickness + (arrow_space - arrow_size)/2,
2480                            arrow_size, arrow_size);
2481         }
2482     }
2483 }
2484
2485 static gboolean
2486 gtk_menu_expose (GtkWidget      *widget,
2487                  GdkEventExpose *event)
2488 {
2489   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
2490   g_return_val_if_fail (event != NULL, FALSE);
2491
2492   if (GTK_WIDGET_DRAWABLE (widget))
2493     {
2494       gtk_menu_paint (widget, event);
2495       
2496       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2497     }
2498   
2499   return FALSE;
2500 }
2501
2502 static void
2503 gtk_menu_show (GtkWidget *widget)
2504 {
2505   GtkMenu *menu = GTK_MENU (widget);
2506
2507   _gtk_menu_refresh_accel_paths (menu, FALSE);
2508
2509   GTK_WIDGET_CLASS (parent_class)->show (widget);
2510 }
2511
2512 static gboolean
2513 gtk_menu_button_press (GtkWidget      *widget,
2514                          GdkEventButton *event)
2515 {
2516   /* Don't pop down the menu for releases over scroll arrows
2517    */
2518   if (GTK_IS_MENU (widget))
2519     {
2520       GtkMenu *menu = GTK_MENU (widget);
2521
2522       if (menu->upper_arrow_prelight ||  menu->lower_arrow_prelight)
2523         return TRUE;
2524     }
2525
2526   return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
2527 }
2528
2529 static gboolean
2530 gtk_menu_button_release (GtkWidget      *widget,
2531                          GdkEventButton *event)
2532 {
2533   /* Don't pop down the menu for releases over scroll arrows
2534    */
2535   if (GTK_IS_MENU (widget))
2536     {
2537       GtkMenu *menu = GTK_MENU (widget);
2538
2539       if (menu->upper_arrow_prelight ||  menu->lower_arrow_prelight)
2540         return TRUE;
2541     }
2542
2543   return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
2544 }
2545
2546 static const gchar *
2547 get_accel_path (GtkWidget *menu_item,
2548                 gboolean  *locked)
2549 {
2550   const gchar *path;
2551   GtkWidget *label;
2552   GClosure *accel_closure;
2553   GtkAccelGroup *accel_group;    
2554
2555   path = _gtk_widget_get_accel_path (menu_item, locked);
2556   if (!path)
2557     {
2558       path = GTK_MENU_ITEM (menu_item)->accel_path;
2559       
2560       if (locked)
2561         {
2562           *locked = TRUE;
2563
2564           label = GTK_BIN (menu_item)->child;
2565           
2566           if (GTK_IS_ACCEL_LABEL (label))
2567             {
2568               g_object_get (label, 
2569                             "accel_closure", &accel_closure, 
2570                             NULL);
2571               if (accel_closure)
2572                 {
2573                   accel_group = gtk_accel_group_from_accel_closure (accel_closure);
2574                   
2575                   *locked = accel_group->lock_count > 0;
2576                 }
2577             }
2578         }
2579     }
2580
2581   return path;
2582 }
2583
2584 static gboolean
2585 gtk_menu_key_press (GtkWidget   *widget,
2586                     GdkEventKey *event)
2587 {
2588   GtkMenuShell *menu_shell;
2589   GtkMenu *menu;
2590   gboolean delete = FALSE;
2591   gboolean can_change_accels;
2592   gchar *accel = NULL;
2593   guint accel_key, accel_mods;
2594   GdkModifierType consumed_modifiers;
2595   GdkDisplay *display;
2596   
2597   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
2598   g_return_val_if_fail (event != NULL, FALSE);
2599       
2600   menu_shell = GTK_MENU_SHELL (widget);
2601   menu = GTK_MENU (widget);
2602   
2603   gtk_menu_stop_navigating_submenu (menu);
2604
2605   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
2606     return TRUE;
2607
2608   display = gtk_widget_get_display (widget);
2609     
2610   g_object_get (gtk_widget_get_settings (widget),
2611                 "gtk-menu-bar-accel", &accel,
2612                 "gtk-can-change-accels", &can_change_accels,
2613                 NULL);
2614
2615   if (accel)
2616     {
2617       guint keyval = 0;
2618       GdkModifierType mods = 0;
2619       gboolean handled = FALSE;
2620       
2621       gtk_accelerator_parse (accel, &keyval, &mods);
2622
2623       if (keyval == 0)
2624         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
2625
2626       /* FIXME this is wrong, needs to be in the global accel resolution
2627        * thing, to properly consider i18n etc., but that probably requires
2628        * AccelGroup changes etc.
2629        */
2630       if (event->keyval == keyval &&
2631           (mods & event->state) == mods)
2632         gtk_menu_shell_cancel (menu_shell);
2633
2634       g_free (accel);
2635
2636       if (handled)
2637         return TRUE;
2638     }
2639   
2640   switch (event->keyval)
2641     {
2642     case GDK_Delete:
2643     case GDK_KP_Delete:
2644     case GDK_BackSpace:
2645       delete = TRUE;
2646       break;
2647     default:
2648       break;
2649     }
2650
2651   /* Figure out what modifiers went into determining the key symbol */
2652   gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
2653                                        event->hardware_keycode, event->state, event->group,
2654                                        NULL, NULL, NULL, &consumed_modifiers);
2655
2656   accel_key = gdk_keyval_to_lower (event->keyval);
2657   accel_mods = event->state & gtk_accelerator_get_default_mod_mask () & ~consumed_modifiers;
2658
2659   /* If lowercasing affects the keysym, then we need to include SHIFT in the modifiers,
2660    * We re-upper case when we match against the keyval, but display and save in caseless form.
2661    */
2662   if (accel_key != event->keyval)
2663     accel_mods |= GDK_SHIFT_MASK;
2664   
2665   /* Modify the accelerators */
2666   if (can_change_accels &&
2667       menu_shell->active_menu_item &&
2668       GTK_BIN (menu_shell->active_menu_item)->child &&                  /* no separators */
2669       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&  /* no submenus */
2670       (delete || gtk_accelerator_valid (accel_key, accel_mods)))
2671     {
2672       GtkWidget *menu_item = menu_shell->active_menu_item;
2673       gboolean locked, replace_accels = TRUE;
2674       const gchar *path;
2675
2676       path = get_accel_path (menu_item, &locked);
2677       if (!path || locked)
2678         {
2679           /* can't change accelerators on menu_items without paths
2680            * (basically, those items are accelerator-locked).
2681            */
2682           /* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
2683           gdk_display_beep (display);
2684         }
2685       else
2686         {
2687           gboolean changed;
2688
2689           /* For the keys that act to delete the current setting, we delete
2690            * the current setting if there is one, otherwise, we set the
2691            * key as the accelerator.
2692            */
2693           if (delete)
2694             {
2695               GtkAccelKey key;
2696               
2697               if (gtk_accel_map_lookup_entry (path, &key) &&
2698                   (key.accel_key || key.accel_mods))
2699                 {
2700                   accel_key = 0;
2701                   accel_mods = 0;
2702                 }
2703             }
2704           changed = gtk_accel_map_change_entry (path, accel_key, accel_mods, replace_accels);
2705
2706           if (!changed)
2707             {
2708               /* we failed, probably because this key is in use and
2709                * locked already
2710                */
2711               /* g_print("failed to change\n"); */
2712               gdk_display_beep (display);
2713             }
2714         }
2715     }
2716   
2717   return TRUE;
2718 }
2719
2720 static gboolean
2721 gtk_menu_motion_notify  (GtkWidget         *widget,
2722                          GdkEventMotion    *event)
2723 {
2724   GtkWidget *menu_item;
2725   GtkMenu *menu;
2726   GtkMenuShell *menu_shell;
2727
2728   gboolean need_enter;
2729
2730   if (GTK_IS_MENU (widget))
2731     gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
2732
2733   /* We received the event for one of two reasons:
2734    *
2735    * a) We are the active menu, and did gtk_grab_add()
2736    * b) The widget is a child of ours, and the event was propagated
2737    *
2738    * Since for computation of navigation regions, we want the menu which
2739    * is the parent of the menu item, for a), we need to find that menu,
2740    * which may be different from 'widget'.
2741    */
2742   menu_item = gtk_get_event_widget ((GdkEvent*) event);
2743   if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) ||
2744       !GTK_IS_MENU (menu_item->parent))
2745     return FALSE;
2746
2747   menu_shell = GTK_MENU_SHELL (menu_item->parent);
2748   menu = GTK_MENU (menu_shell);
2749   
2750   need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
2751
2752   /* Check to see if we are within an active submenu's navigation region
2753    */
2754   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
2755     return TRUE; 
2756
2757   /* Make sure we pop down if we enter a non-selectable menu item, so we
2758    * don't show a submenu when the cursor is outside the stay-up triangle.
2759    */
2760   if (!_gtk_menu_item_is_selectable (menu_item))
2761     {
2762       gtk_menu_shell_deselect (menu_shell);
2763       return FALSE;
2764     }
2765
2766   if (need_enter)
2767     {
2768       /* The menu is now sensitive to enter events on its items, but
2769        * was previously sensitive.  So we fake an enter event.
2770        */
2771       gint width, height;
2772       
2773       menu_shell->ignore_enter = FALSE; 
2774       
2775       gdk_drawable_get_size (event->window, &width, &height);
2776       if (event->x >= 0 && event->x < width &&
2777           event->y >= 0 && event->y < height)
2778         {
2779           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
2780           gboolean result;
2781
2782           send_event->crossing.window = g_object_ref (event->window);
2783           send_event->crossing.time = event->time;
2784           send_event->crossing.send_event = TRUE;
2785           send_event->crossing.x_root = event->x_root;
2786           send_event->crossing.y_root = event->y_root;
2787           send_event->crossing.x = event->x;
2788           send_event->crossing.y = event->y;
2789
2790           /* We send the event to 'widget', the currently active menu,
2791            * instead of 'menu', the menu that the pointer is in. This
2792            * will ensure that the event will be ignored unless the
2793            * menuitem is a child of the active menu or some parent
2794            * menu of the active menu.
2795            */
2796           result = gtk_widget_event (widget, send_event);
2797           gdk_event_free (send_event);
2798
2799           return result;
2800         }
2801     }
2802
2803   return FALSE;
2804 }
2805
2806 static void
2807 gtk_menu_scroll_by (GtkMenu *menu, 
2808                     gint     step)
2809 {
2810   GtkWidget *widget;
2811   gint offset;
2812   gint view_width, view_height;
2813
2814   widget = GTK_WIDGET (menu);
2815   offset = menu->scroll_offset + step;
2816
2817   /* If we scroll upward and the non-visible top part
2818    * is smaller than the scroll arrow it would be
2819    * pretty stupid to show the arrow and taking more
2820    * screen space than just scrolling to the top.
2821    */
2822   if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
2823     offset = 0;
2824
2825   /* Don't scroll over the top if we weren't before: */
2826   if ((menu->scroll_offset >= 0) && (offset < 0))
2827     offset = 0;
2828
2829   gdk_drawable_get_size (widget->window, &view_width, &view_height);
2830
2831   /* Don't scroll past the bottom if we weren't before: */
2832   if (menu->scroll_offset > 0)
2833     view_height -= MENU_SCROLL_ARROW_HEIGHT;
2834   
2835   if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
2836       (offset + view_height > widget->requisition.height))
2837     offset = widget->requisition.height - view_height;
2838
2839   if (offset != menu->scroll_offset)
2840     gtk_menu_scroll_to (menu, offset);
2841 }
2842
2843 Static gboolean
2844 gtk_menu_scroll_timeout (gpointer  data)
2845 {
2846   GtkMenu *menu;
2847
2848   GDK_THREADS_ENTER ();
2849
2850   menu = GTK_MENU (data);
2851   gtk_menu_scroll_by (menu, menu->scroll_step);
2852
2853   GDK_THREADS_LEAVE ();
2854
2855   return TRUE;
2856 }
2857
2858 static gboolean
2859 gtk_menu_scroll (GtkWidget      *widget,
2860                  GdkEventScroll *event)
2861 {
2862   GtkMenu *menu = GTK_MENU (widget);
2863
2864   switch (event->direction)
2865     {
2866     case GDK_SCROLL_RIGHT:
2867     case GDK_SCROLL_DOWN:
2868       gtk_menu_scroll_by (menu, MENU_SCROLL_STEP2);
2869       break;
2870     case GDK_SCROLL_LEFT:
2871     case GDK_SCROLL_UP:
2872       gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
2873       break;
2874     }
2875
2876   return TRUE;
2877 }
2878
2879 static void
2880 gtk_menu_handle_scrolling (GtkMenu *menu, gboolean enter)
2881 {
2882   GtkMenuShell *menu_shell;
2883   gint width, height;
2884   gint x, y;
2885   gint border;
2886   GdkRectangle rect;
2887   gboolean in_arrow;
2888   gboolean scroll_fast = FALSE;
2889   guint vertical_padding;
2890
2891   menu_shell = GTK_MENU_SHELL (menu);
2892
2893   gdk_window_get_pointer (GTK_WIDGET (menu)->window, &x, &y, NULL);
2894   gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
2895
2896   gtk_widget_style_get (GTK_WIDGET (menu),
2897                         "vertical-padding", &vertical_padding,
2898                         NULL);
2899   
2900   border = GTK_CONTAINER (menu)->border_width +
2901     GTK_WIDGET (menu)->style->ythickness + vertical_padding;
2902
2903   if (menu->upper_arrow_visible && !menu->tearoff_active)
2904     {
2905       rect.x = 0;
2906       rect.y = 0;
2907       rect.width = width;
2908       rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
2909       
2910       in_arrow = FALSE;
2911       if ((x >= rect.x) && (x < rect.x + rect.width) &&
2912           (y >= rect.y) && (y < rect.y + rect.height))
2913         {
2914           in_arrow = TRUE;
2915           scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
2916         }
2917         
2918       if (enter && in_arrow &&
2919           (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast))
2920         {
2921           menu->upper_arrow_prelight = TRUE;
2922           menu->scroll_fast = scroll_fast;
2923           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2924           
2925           /* Deselect the active item so that any submenus are poped down */
2926           gtk_menu_shell_deselect (menu_shell);
2927
2928           gtk_menu_remove_scroll_timeout (menu);
2929           menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1;
2930           menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
2931                                             gtk_menu_scroll_timeout,
2932                                             menu);
2933         }
2934       else if (!enter && !in_arrow && menu->upper_arrow_prelight)
2935         {
2936           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2937           
2938           gtk_menu_stop_scrolling (menu);
2939         }
2940     }
2941   
2942   if (menu->lower_arrow_visible && !menu->tearoff_active)
2943     {
2944       rect.x = 0;
2945       rect.y = height - border - MENU_SCROLL_ARROW_HEIGHT;
2946       rect.width = width;
2947       rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
2948
2949       in_arrow = FALSE;
2950       if ((x >= rect.x) && (x < rect.x + rect.width) &&
2951           (y >= rect.y) && (y < rect.y + rect.height))
2952         {
2953           in_arrow = TRUE;
2954           scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
2955         }
2956
2957       if (enter && in_arrow &&
2958           (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast))
2959         {
2960           menu->lower_arrow_prelight = TRUE;
2961           menu->scroll_fast = scroll_fast;
2962           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2963
2964           /* Deselect the active item so that any submenus are poped down */
2965           gtk_menu_shell_deselect (menu_shell);
2966
2967           gtk_menu_remove_scroll_timeout (menu);
2968           menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1;
2969           menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
2970                                             gtk_menu_scroll_timeout,
2971                                             menu);
2972         }
2973       else if (!enter && !in_arrow && menu->lower_arrow_prelight)
2974         {
2975           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
2976           
2977           gtk_menu_stop_scrolling (menu);
2978         }
2979     }
2980 }
2981
2982 static gboolean
2983 gtk_menu_enter_notify (GtkWidget        *widget,
2984                        GdkEventCrossing *event)
2985 {
2986   GtkWidget *menu_item;
2987
2988   menu_item = gtk_get_event_widget ((GdkEvent*) event);
2989   if (widget && GTK_IS_MENU (widget))
2990     {
2991       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
2992
2993       if (!menu_shell->ignore_enter)
2994         gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
2995     }
2996   
2997   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
2998    * will not correspond to the event widget's parent.  Check to see
2999    * if we are in the parent's navigation region.
3000    */
3001   if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
3002       gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
3003     return TRUE;
3004
3005   return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event); 
3006 }
3007
3008 static gboolean
3009 gtk_menu_leave_notify (GtkWidget        *widget,
3010                        GdkEventCrossing *event)
3011 {
3012   GtkMenuShell *menu_shell;
3013   GtkMenu *menu;
3014   GtkMenuItem *menu_item;
3015   GtkWidget *event_widget;
3016
3017   menu = GTK_MENU (widget);
3018   menu_shell = GTK_MENU_SHELL (widget); 
3019   
3020   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
3021     return TRUE; 
3022
3023   gtk_menu_handle_scrolling (menu, FALSE);
3024   
3025   event_widget = gtk_get_event_widget ((GdkEvent*) event);
3026   
3027   if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
3028     return TRUE;
3029   
3030   menu_item = GTK_MENU_ITEM (event_widget); 
3031   
3032   /* Here we check to see if we're leaving an active menu item with a submenu, 
3033    * in which case we enter submenu navigation mode. 
3034    */
3035   if (menu_shell->active_menu_item != NULL
3036       && menu_item->submenu != NULL
3037       && menu_item->submenu_placement == GTK_LEFT_RIGHT)
3038     {
3039       if (GTK_MENU_SHELL (menu_item->submenu)->active)
3040         {
3041           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
3042           return TRUE;
3043         }
3044       else if (menu_item == GTK_MENU_ITEM (menu_shell->active_menu_item))
3045         {
3046           /* We are leaving an active menu item with nonactive submenu.
3047            * Deselect it so we don't surprise the user with by popping
3048            * up a submenu _after_ he left the item.
3049            */
3050           gtk_menu_shell_deselect (menu_shell);
3051           return TRUE;
3052         }
3053     }
3054   
3055   return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event); 
3056 }
3057
3058 static void 
3059 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
3060 {
3061   if (menu->navigation_region) 
3062     {
3063       gdk_region_destroy (menu->navigation_region);
3064       menu->navigation_region = NULL;
3065     }
3066   
3067   if (menu->navigation_timeout)
3068     {
3069       g_source_remove (menu->navigation_timeout);
3070       menu->navigation_timeout = 0;
3071     }
3072 }
3073
3074 /* When the timeout is elapsed, the navigation region is destroyed
3075  * and the menuitem under the pointer (if any) is selected.
3076  */
3077 static gboolean
3078 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
3079 {
3080   GtkMenu *menu = user_data;
3081   GdkWindow *child_window;
3082
3083   GDK_THREADS_ENTER ();
3084
3085   gtk_menu_stop_navigating_submenu (menu);
3086   
3087   if (GTK_WIDGET_REALIZED (menu))
3088     {
3089       child_window = gdk_window_get_pointer (menu->bin_window, NULL, NULL, NULL);
3090
3091       if (child_window)
3092         {
3093           GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
3094
3095           send_event->crossing.window = g_object_ref (child_window);
3096           send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
3097           send_event->crossing.send_event = TRUE;
3098
3099           GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
3100
3101           gdk_event_free (send_event);
3102         }
3103     }
3104
3105   GDK_THREADS_LEAVE ();
3106
3107   return FALSE; 
3108 }
3109
3110 static gboolean
3111 gtk_menu_navigating_submenu (GtkMenu *menu,
3112                              gint     event_x,
3113                              gint     event_y)
3114 {
3115   if (menu->navigation_region)
3116     {
3117       if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
3118         return TRUE;
3119       else
3120         {
3121           gtk_menu_stop_navigating_submenu (menu);
3122           return FALSE;
3123         }
3124     }
3125   return FALSE;
3126 }
3127
3128 #undef DRAW_STAY_UP_TRIANGLE
3129
3130 #ifdef DRAW_STAY_UP_TRIANGLE
3131
3132 static void
3133 draw_stay_up_triangle (GdkWindow *window,
3134                        GdkRegion *region)
3135 {
3136   /* Draw ugly color all over the stay-up triangle */
3137   GdkColor ugly_color = { 0, 50000, 10000, 10000 };
3138   GdkGCValues gc_values;
3139   GdkGC *ugly_gc;
3140   GdkRectangle clipbox;
3141
3142   gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
3143   ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW);
3144   gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
3145   gdk_gc_set_clip_region (ugly_gc, region);
3146
3147   gdk_region_get_clipbox (region, &clipbox);
3148   
3149   gdk_draw_rectangle (window,
3150                      ugly_gc,
3151                      TRUE,
3152                      clipbox.x, clipbox.y,
3153                      clipbox.width, clipbox.height);
3154   
3155   g_object_unref (ugly_gc);
3156 }
3157 #endif
3158
3159 static GdkRegion *
3160 flip_region (GdkRegion *region,
3161              gboolean   flip_x,
3162              gboolean   flip_y)
3163 {
3164   gint n_rectangles;
3165   GdkRectangle *rectangles;
3166   GdkRectangle clipbox;
3167   GdkRegion *new_region;
3168   gint i;
3169
3170   new_region = gdk_region_new ();
3171   
3172   gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
3173   gdk_region_get_clipbox (region, &clipbox);
3174
3175   for (i = 0; i < n_rectangles; ++i)
3176     {
3177       GdkRectangle rect = rectangles[i];
3178
3179       if (flip_y)
3180         rect.y -= 2 * (rect.y - clipbox.y) + rect.height;
3181
3182       if (flip_x)
3183         rect.x -= 2 * (rect.x - clipbox.x) + rect.width;
3184
3185       gdk_region_union_with_rect (new_region, &rect);
3186     }
3187
3188   g_free (rectangles);
3189
3190   return new_region;
3191 }
3192
3193 static void
3194 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
3195                                         GtkMenuItem      *menu_item,
3196                                         GdkEventCrossing *event)
3197 {
3198   gint submenu_left = 0;
3199   gint submenu_right = 0;
3200   gint submenu_top = 0;
3201   gint submenu_bottom = 0;
3202   gint width = 0;
3203   gint height = 0;
3204   GdkPoint point[3];
3205   GtkWidget *event_widget;
3206
3207   g_return_if_fail (menu_item->submenu != NULL);
3208   g_return_if_fail (event != NULL);
3209   
3210   event_widget = gtk_get_event_widget ((GdkEvent*) event);
3211   
3212   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
3213   gdk_drawable_get_size (menu_item->submenu->window, &width, &height);
3214   
3215   submenu_right = submenu_left + width;
3216   submenu_bottom = submenu_top + height;
3217   
3218   gdk_drawable_get_size (event_widget->window, &width, &height);
3219   
3220   if (event->x >= 0 && event->x < width)
3221     {
3222       gint popdown_delay;
3223       gboolean flip_y = FALSE;
3224       gboolean flip_x = FALSE;
3225       
3226       gtk_menu_stop_navigating_submenu (menu);
3227
3228       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
3229         {
3230           /* right */
3231           point[0].x = event->x_root;
3232           point[1].x = submenu_left;
3233         }
3234       else
3235         {
3236           /* left */
3237           point[0].x = event->x_root + 1;
3238           point[1].x = 2 * (event->x_root + 1) - submenu_right;
3239
3240           flip_x = TRUE;
3241         }
3242
3243       if (event->y < 0)
3244         {
3245           /* top */
3246           point[0].y = event->y_root + 1;
3247           point[1].y = 2 * (event->y_root + 1) - submenu_top + NAVIGATION_REGION_OVERSHOOT;
3248
3249           if (point[0].y >= point[1].y - NAVIGATION_REGION_OVERSHOOT)
3250             return;
3251
3252           flip_y = TRUE;
3253         }
3254       else
3255         {
3256           /* bottom */
3257           point[0].y = event->y_root;
3258           point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT;
3259
3260           if (point[0].y >= submenu_bottom)
3261             return;
3262         }
3263
3264       point[2].x = point[1].x;
3265       point[2].y = point[0].y;
3266
3267       menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
3268
3269       if (flip_x || flip_y)
3270         {
3271           GdkRegion *new_region = flip_region (menu->navigation_region, flip_x, flip_y);
3272           gdk_region_destroy (menu->navigation_region);
3273           menu->navigation_region = new_region;
3274         }
3275
3276       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
3277                     "gtk-menu-popdown-delay", &popdown_delay,
3278                     NULL);
3279
3280       menu->navigation_timeout = g_timeout_add (popdown_delay,
3281                                                 gtk_menu_stop_navigating_submenu_cb, menu);
3282
3283 #ifdef DRAW_STAY_UP_TRIANGLE
3284       draw_stay_up_triangle (gdk_get_default_root_window(),
3285                              menu->navigation_region);
3286 #endif
3287     }
3288 }
3289
3290 static void
3291 gtk_menu_deactivate (GtkMenuShell *menu_shell)
3292 {
3293   GtkWidget *parent;
3294   
3295   g_return_if_fail (GTK_IS_MENU (menu_shell));
3296   
3297   parent = menu_shell->parent_menu_shell;
3298   
3299   menu_shell->activate_time = 0;
3300   gtk_menu_popdown (GTK_MENU (menu_shell));
3301   
3302   if (parent)
3303     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
3304 }
3305
3306 static void
3307 gtk_menu_position (GtkMenu *menu)
3308 {
3309   GtkWidget *widget;
3310   GtkRequisition requisition;
3311   GtkMenuPrivate *private;
3312   gint x, y;
3313   gint scroll_offset;
3314   gint menu_height;
3315   gboolean push_in;
3316   GdkScreen *screen;
3317   GdkScreen *pointer_screen;
3318   GdkRectangle monitor;
3319
3320   g_return_if_fail (GTK_IS_MENU (menu));
3321
3322   widget = GTK_WIDGET (menu);
3323
3324   screen = gtk_widget_get_screen (widget);
3325   gdk_display_get_pointer (gdk_screen_get_display (screen),
3326                            &pointer_screen, &x, &y, NULL);
3327
3328   /* We need the requisition to figure out the right place to
3329    * popup the menu. In fact, we always need to ask here, since
3330    * if a size_request was queued while we weren't popped up,
3331    * the requisition won't have been recomputed yet.
3332    */
3333   gtk_widget_size_request (widget, &requisition);
3334
3335   if (pointer_screen != screen)
3336     {
3337       /* Pointer is on a different screen; roughly center the
3338        * menu on the screen. If someone was using multiscreen
3339        * + Xinerama together they'd probably want something
3340        * fancier; but that is likely to be vanishingly rare.
3341        */
3342       x = MAX (0, (gdk_screen_get_width (screen) - requisition.width) / 2);
3343       y = MAX (0, (gdk_screen_get_height (screen) - requisition.height) / 2);
3344     }
3345
3346   private = gtk_menu_get_private (menu);
3347   private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
3348
3349   push_in = FALSE;
3350   
3351   if (menu->position_func)
3352     {
3353       (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data);
3354       if (private->monitor_num < 0) 
3355         private->monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
3356
3357       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
3358     }
3359   else
3360     {
3361       gint space_left, space_right, space_above, space_below;
3362       gint needed_width;
3363       gint needed_height;
3364       gint xthickness = widget->style->xthickness;
3365       gint ythickness = widget->style->ythickness;
3366       gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
3367
3368       /* The placement of popup menus horizontally works like this (with
3369        * RTL in parentheses)
3370        *
3371        * - If there is enough room to the right (left) of the mouse cursor,
3372        *   position the menu there.
3373        * 
3374        * - Otherwise, if if there is enough room to the left (right) of the 
3375        *   mouse cursor, position the menu there.
3376        * 
3377        * - Otherwise if the menu is smaller than the monitor, position it
3378        *   on the side of the mouse cursor that has the most space available
3379        *
3380        * - Otherwise (if there is simply not enough room for the menu on the
3381        *   monitor), position it as far left (right) as possible.
3382        *
3383        * Positioning in the vertical direction is similar: first try below
3384        * mouse cursor, then above.
3385        */
3386       gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor);
3387
3388       space_left = x - monitor.x;
3389       space_right = monitor.x + monitor.width - x - 1;
3390       space_above = y - monitor.y;
3391       space_below = monitor.y + monitor.height - y - 1;
3392
3393       /* position horizontally */
3394
3395       /* the amount of space we need to position the menu. Note the
3396        * menu is offset "xthickness" pixels 
3397        */
3398       needed_width = requisition.width - xthickness;
3399
3400       if (needed_width <= space_left ||
3401           needed_width <= space_right)
3402         {
3403           if ((rtl  && needed_width <= space_left) ||
3404               (!rtl && needed_width >  space_right))
3405             {
3406               /* position left */
3407               x = x + xthickness - requisition.width + 1;
3408             }
3409           else
3410             {
3411               /* position right */
3412               x = x - xthickness;
3413             }
3414
3415           /* x is clamped on-screen further down */
3416         }
3417       else if (requisition.width <= monitor.width)
3418         {
3419           /* the menu is too big to fit on either side of the mouse
3420            * cursor, but smaller than the monitor. Position it on
3421            * the side that has the most space
3422            */
3423           if (space_left > space_right)
3424             {
3425               /* left justify */
3426               x = monitor.x;
3427             }
3428           else
3429             {
3430               /* right justify */
3431               x = monitor.x + monitor.width - requisition.width;
3432             }
3433         }
3434       else /* menu is simply too big for the monitor */
3435         {
3436           if (rtl)
3437             {
3438               /* right justify */
3439               x = monitor.x + monitor.width - requisition.width;
3440             }
3441           else
3442             {
3443               /* left justify */
3444               x = monitor.x;
3445             }
3446         }
3447
3448       /* Position vertically. The algorithm is the same as above, but
3449        * simpler because we don't have to take RTL into account.
3450        */
3451       needed_height = requisition.height - ythickness;
3452
3453       if (needed_height <= space_above ||
3454           needed_height <= space_below)
3455         {
3456           if (needed_height <= space_below)
3457             y = y - ythickness;
3458           else
3459             y = y + ythickness - requisition.height + 1;
3460           
3461           y = CLAMP (y, monitor.y,
3462                      monitor.y + monitor.height - requisition.height);
3463         }
3464       else if (needed_height > space_below && needed_height > space_above)
3465         {
3466           if (space_below >= space_above)
3467             y = monitor.y + monitor.height - requisition.height;
3468           else
3469             y = monitor.y;
3470         }
3471       else
3472         {
3473           y = monitor.y;
3474         }
3475     }
3476
3477   scroll_offset = 0;
3478
3479   if (push_in)
3480     {
3481       menu_height = GTK_WIDGET (menu)->requisition.height;
3482
3483       if (y + menu_height > monitor.y + monitor.height)
3484         {
3485           scroll_offset -= y + menu_height - (monitor.y + monitor.height);
3486           y = (monitor.y + monitor.height) - menu_height;
3487         }
3488   
3489       if (y < monitor.y)
3490         {
3491           scroll_offset += monitor.y - y;
3492           y = monitor.y;
3493         }
3494     }
3495
3496   /* FIXME: should this be done in the various position_funcs ? */
3497   x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
3498  
3499   if (GTK_MENU_SHELL (menu)->active)
3500     {
3501       private->have_position = TRUE;
3502       private->x = x;
3503       private->y = y;
3504     }
3505   
3506   if (y + requisition.height > monitor.y + monitor.height)
3507     requisition.height = (monitor.y + monitor.height) - y;
3508   
3509   if (y < monitor.y)
3510     {
3511       scroll_offset += monitor.y - y;
3512       requisition.height -= monitor.y - y;
3513       y = monitor.y;
3514     }
3515
3516   if (scroll_offset > 0)
3517     scroll_offset += MENU_SCROLL_ARROW_HEIGHT;
3518   
3519   gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->active ? menu->toplevel : menu->tearoff_window), 
3520                    x, y);
3521
3522   if (!GTK_MENU_SHELL (menu)->active)
3523     {
3524       gtk_window_resize (GTK_WINDOW (menu->tearoff_window),
3525                          requisition.width, requisition.height);
3526     }
3527   
3528   menu->scroll_offset = scroll_offset;
3529 }
3530
3531 static void
3532 gtk_menu_remove_scroll_timeout (GtkMenu *menu)
3533 {
3534   if (menu->timeout_id)
3535     {
3536       g_source_remove (menu->timeout_id);
3537       menu->timeout_id = 0;
3538     }
3539 }
3540
3541 static void
3542 gtk_menu_stop_scrolling (GtkMenu *menu)
3543 {
3544   gtk_menu_remove_scroll_timeout (menu);
3545
3546   menu->upper_arrow_prelight = FALSE;
3547   menu->lower_arrow_prelight = FALSE;
3548 }
3549
3550 static void
3551 gtk_menu_scroll_to (GtkMenu *menu,
3552                     gint    offset)
3553 {
3554   GtkWidget *widget;
3555   gint x, y;
3556   gint view_width, view_height;
3557   gint border_width;
3558   gboolean last_visible;
3559   gint menu_height;
3560   guint vertical_padding;
3561
3562   widget = GTK_WIDGET (menu);
3563
3564   if (menu->tearoff_active &&
3565       menu->tearoff_adjustment &&
3566       (menu->tearoff_adjustment->value != offset))
3567     {
3568       menu->tearoff_adjustment->value =
3569         CLAMP (offset,
3570                0, menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size);
3571       gtk_adjustment_value_changed (menu->tearoff_adjustment);
3572     }
3573   
3574   /* Move/resize the viewport according to arrows: */
3575   view_width = widget->allocation.width;
3576   view_height = widget->allocation.height;
3577
3578   gtk_widget_style_get (GTK_WIDGET (menu),
3579                         "vertical-padding", &vertical_padding,
3580                         NULL);
3581   
3582   border_width = GTK_CONTAINER (menu)->border_width;
3583   view_width -= (border_width + widget->style->xthickness) * 2;
3584   view_height -= (border_width + widget->style->ythickness + vertical_padding) * 2;
3585   menu_height = widget->requisition.height -
3586       (border_width + widget->style->ythickness + vertical_padding) * 2;
3587
3588   x = border_width + widget->style->xthickness;
3589   y = border_width + widget->style->ythickness + vertical_padding;
3590
3591   if (!menu->tearoff_active)
3592     {
3593       last_visible = menu->upper_arrow_visible;
3594       menu->upper_arrow_visible = offset > 0;
3595       
3596       if (menu->upper_arrow_visible)
3597         view_height -= MENU_SCROLL_ARROW_HEIGHT;
3598       
3599       if ( (last_visible != menu->upper_arrow_visible) &&
3600            !menu->upper_arrow_visible)
3601         {
3602           menu->upper_arrow_prelight = FALSE;
3603           
3604           /* If we hid the upper arrow, possibly remove timeout */
3605           if (menu->scroll_step < 0)
3606             {
3607               gtk_menu_stop_scrolling (menu);
3608               gtk_widget_queue_draw (GTK_WIDGET (menu));
3609             }
3610         }
3611
3612       last_visible = menu->lower_arrow_visible;
3613       menu->lower_arrow_visible = offset < menu_height - view_height;
3614       
3615       if (menu->lower_arrow_visible)
3616         view_height -= MENU_SCROLL_ARROW_HEIGHT;
3617       
3618       if ( (last_visible != menu->lower_arrow_visible) &&
3619            !menu->lower_arrow_visible)
3620         {
3621           menu->lower_arrow_prelight = FALSE;
3622           
3623           /* If we hid the lower arrow, possibly remove timeout */
3624           if (menu->scroll_step > 0)
3625             {
3626               gtk_menu_stop_scrolling (menu);
3627               gtk_widget_queue_draw (GTK_WIDGET (menu));
3628             }
3629         }
3630       
3631       if (menu->upper_arrow_visible)
3632         y += MENU_SCROLL_ARROW_HEIGHT;
3633     }
3634
3635   /* Scroll the menu: */
3636   if (GTK_WIDGET_REALIZED (menu))
3637     gdk_window_move (menu->bin_window, 0, -offset);
3638
3639   if (GTK_WIDGET_REALIZED (menu))
3640     gdk_window_move_resize (menu->view_window,
3641                             x,
3642                             y,
3643                             view_width,
3644                             view_height);
3645
3646   menu->scroll_offset = offset;
3647 }
3648
3649 static gboolean
3650 compute_child_offset (GtkMenu   *menu,
3651                       GtkWidget *menu_item,
3652                       gint      *offset,
3653                       gint      *height,
3654                       gboolean  *is_last_child)
3655 {
3656   GtkMenuPrivate *priv = gtk_menu_get_private (menu);
3657   guint item_top_attach;
3658   guint item_bottom_attach;
3659   gint child_offset = 0;
3660   gint i;
3661
3662   get_effective_child_attach (menu_item, NULL, NULL,
3663                               &item_top_attach, &item_bottom_attach);
3664
3665   /* there is a possibility that we get called before _size_request, so
3666    * check the height table for safety.
3667    */
3668   if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
3669     return FALSE;
3670
3671   /* when we have a row with only invisible children, it's height will
3672    * be zero, so there's no need to check WIDGET_VISIBLE here
3673    */
3674   for (i = 0; i < item_top_attach; i++)
3675     child_offset += priv->heights[i];
3676
3677   if (is_last_child)
3678     *is_last_child = (item_bottom_attach == gtk_menu_get_n_rows (menu));
3679   if (offset)
3680     *offset = child_offset;
3681   if (height)
3682     *height = priv->heights[item_top_attach];
3683
3684   return TRUE;
3685 }
3686
3687 static void
3688 gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
3689                               GtkWidget       *menu_item)
3690 {
3691   GtkMenu *menu;
3692   gint child_offset, child_height;
3693   gint width, height;
3694   gint y;
3695   gint arrow_height;
3696   gboolean last_child = 0;
3697   
3698   menu = GTK_MENU (menu_shell);
3699
3700   /* We need to check if the selected item fully visible.
3701    * If not we need to scroll the menu so that it becomes fully
3702    * visible.
3703    */
3704
3705   if (compute_child_offset (menu, menu_item,
3706                             &child_offset, &child_height, &last_child))
3707     {
3708       guint vertical_padding;
3709       
3710       y = menu->scroll_offset;
3711       gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
3712
3713       gtk_widget_style_get (GTK_WIDGET (menu),
3714                             "vertical-padding", &vertical_padding,
3715                             NULL);
3716                             
3717       height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding;
3718       
3719       if (child_offset < y)
3720         {
3721           /* Ignore the enter event we might get if the pointer is on the menu
3722            */
3723           menu_shell->ignore_enter = TRUE;
3724           gtk_menu_scroll_to (menu, child_offset);
3725         }
3726       else
3727         {
3728           arrow_height = 0;
3729           if (menu->upper_arrow_visible && !menu->tearoff_active)
3730             arrow_height += MENU_SCROLL_ARROW_HEIGHT;
3731           if (menu->lower_arrow_visible && !menu->tearoff_active)
3732             arrow_height += MENU_SCROLL_ARROW_HEIGHT;
3733           
3734           if (child_offset + child_height > y + height - arrow_height)
3735             {
3736               arrow_height = 0;
3737               if (!last_child && !menu->tearoff_active)
3738                 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
3739               
3740               y = child_offset + child_height - height + arrow_height;
3741               if ((y > 0) && !menu->tearoff_active)
3742                 {
3743                   /* Need upper arrow */
3744                   arrow_height += MENU_SCROLL_ARROW_HEIGHT;
3745                   y = child_offset + child_height - height + arrow_height;
3746                 }
3747               /* Ignore the enter event we might get if the pointer is on the menu
3748                */
3749               menu_shell->ignore_enter = TRUE;
3750               gtk_menu_scroll_to (menu, y);
3751             }
3752         }    
3753       
3754     }
3755 }
3756
3757 static void
3758 gtk_menu_select_item (GtkMenuShell  *menu_shell,
3759                       GtkWidget     *menu_item)
3760 {
3761   GtkMenu *menu = GTK_MENU (menu_shell);
3762
3763   if (GTK_WIDGET_REALIZED (GTK_WIDGET (menu)))
3764     gtk_menu_scroll_item_visible (menu_shell, menu_item);
3765
3766   GTK_MENU_SHELL_CLASS (parent_class)->select_item (menu_shell, menu_item);
3767 }
3768
3769
3770 /* Reparent the menu, taking care of the refcounting
3771  *
3772  * If unrealize is true we force a unrealize while reparenting the parent.
3773  * This can help eliminate flicker in some cases.
3774  *
3775  * What happens is that when the menu is unrealized and then re-realized,
3776  * the allocations are as follows:
3777  *
3778  *  parent - 1x1 at (0,0) 
3779  *  child1 - 100x20 at (0,0)
3780  *  child2 - 100x20 at (0,20)
3781  *  child3 - 100x20 at (0,40)
3782  *
3783  * That is, the parent is small but the children are full sized. Then,
3784  * when the queued_resize gets processed, the parent gets resized to
3785  * full size. 
3786  *
3787  * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
3788  * contains the following logic:
3789  * 
3790  * - if a move or resize operation on a window would change the clip 
3791  *   region on the children, then before the window is resized
3792  *   the background for children is temporarily set to None, the
3793  *   move/resize done, and the background for the children restored.
3794  *
3795  * So, at the point where the parent is resized to final size, the
3796  * background for the children is temporarily None, and thus they
3797  * are not cleared to the background color and the previous background
3798  * (the image of the menu) is left in place.
3799  */
3800 static void 
3801 gtk_menu_reparent (GtkMenu      *menu, 
3802                    GtkWidget    *new_parent, 
3803                    gboolean      unrealize)
3804 {
3805   GtkObject *object = GTK_OBJECT (menu);
3806   GtkWidget *widget = GTK_WIDGET (menu);
3807   gboolean was_floating = GTK_OBJECT_FLOATING (object);
3808
3809   g_object_ref (object);
3810   gtk_object_sink (object);
3811
3812   if (unrealize)
3813     {
3814       g_object_ref (object);
3815       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
3816       gtk_container_add (GTK_CONTAINER (new_parent), widget);
3817       g_object_unref (object);
3818     }
3819   else
3820     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
3821   
3822   if (was_floating)
3823     GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
3824   else
3825     g_object_unref (object);
3826 }
3827
3828 static void
3829 gtk_menu_show_all (GtkWidget *widget)
3830 {
3831   /* Show children, but not self. */
3832   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
3833 }
3834
3835
3836 static void
3837 gtk_menu_hide_all (GtkWidget *widget)
3838 {
3839   /* Hide children, but not self. */
3840   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
3841 }
3842
3843 /**
3844  * gtk_menu_set_screen:
3845  * @menu: a #GtkMenu.
3846  * @screen: a #GdkScreen, or %NULL if the screen should be
3847  *          determined by the widget the menu is attached to.
3848  *
3849  * Sets the #GdkScreen on which the menu will be displayed.
3850  * 
3851  * Since: 2.2
3852  **/
3853 void
3854 gtk_menu_set_screen (GtkMenu   *menu, 
3855                      GdkScreen *screen)
3856 {
3857   g_return_if_fail (GTK_IS_MENU (menu));
3858   g_return_if_fail (!screen || GDK_IS_SCREEN (screen));
3859
3860   g_object_set_data (G_OBJECT (menu), "gtk-menu-explicit-screen", screen);
3861
3862   if (screen)
3863     {
3864       menu_change_screen (menu, screen);
3865     }
3866   else
3867     {
3868       GtkWidget *attach_widget = gtk_menu_get_attach_widget (menu);
3869       if (attach_widget)
3870         attach_widget_screen_changed (attach_widget, NULL, menu);
3871     }
3872 }
3873
3874 /**
3875  * gtk_menu_attach:
3876  * @menu: a #GtkMenu.
3877  * @child: a #GtkMenuItem.
3878  * @left_attach: The column number to attach the left side of the item to.
3879  * @right_attach: The column number to attach the right side of the item to.
3880  * @top_attach: The row number to attach the top of the item to.
3881  * @bottom_attach: The row number to attach the bottom of the item to.
3882  *
3883  * Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
3884  * an item will occupy is specified by @left_attach, @right_attach,
3885  * @top_attach and @bottom_attach. These each represent the leftmost,
3886  * rightmost, uppermost and lower column and row numbers of the table.
3887  * (Columns and rows are indexed from zero).
3888  *
3889  * Note that this function is not related to gtk_menu_detach().
3890  *
3891  * Since: 2.4
3892  **/
3893 void
3894 gtk_menu_attach (GtkMenu   *menu,
3895                  GtkWidget *child,
3896                  guint      left_attach,
3897                  guint      right_attach,
3898                  guint      top_attach,
3899                  guint      bottom_attach)
3900 {
3901   GtkMenuShell *menu_shell;
3902   
3903   g_return_if_fail (GTK_IS_MENU (menu));
3904   g_return_if_fail (GTK_IS_MENU_ITEM (child));
3905   g_return_if_fail (child->parent == NULL || 
3906                     child->parent == GTK_WIDGET (menu));
3907   g_return_if_fail (left_attach < right_attach);
3908   g_return_if_fail (top_attach < bottom_attach);
3909
3910   menu_shell = GTK_MENU_SHELL (menu);
3911   
3912   if (!child->parent)
3913     {
3914       AttachInfo *ai = get_attach_info (child);
3915       
3916       ai->left_attach = left_attach;
3917       ai->right_attach = right_attach;
3918       ai->top_attach = top_attach;
3919       ai->bottom_attach = bottom_attach;
3920       
3921       menu_shell->children = g_list_append (menu_shell->children, child);
3922
3923       gtk_widget_set_parent (child, GTK_WIDGET (menu));
3924
3925       menu_queue_resize (menu);
3926     }
3927   else
3928     {
3929       gtk_container_child_set (GTK_CONTAINER (child->parent), child,
3930                                "left_attach",   left_attach,
3931                                "right_attach",  right_attach,
3932                                "top_attach",    top_attach,
3933                                "bottom_attach", bottom_attach,
3934                                NULL);
3935     }
3936 }
3937
3938 static gint
3939 gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
3940 {
3941   gint popup_delay;
3942
3943   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
3944                 "gtk-menu-popup-delay", &popup_delay,
3945                 NULL);
3946
3947   return popup_delay;
3948 }
3949
3950 static GtkWidget *
3951 find_child_containing (GtkMenuShell *menu_shell,
3952                        int           left,
3953                        int           right,
3954                        int           top,
3955                        int           bottom)
3956 {
3957   GList *list;
3958
3959   /* find a child which includes the area given by
3960    * left, right, top, bottom.
3961    */
3962
3963   for (list = menu_shell->children; list; list = list->next)
3964     {
3965       guint l, r, t, b;
3966
3967       if (!_gtk_menu_item_is_selectable (list->data))
3968         continue;
3969
3970       get_effective_child_attach (list->data, &l, &r, &t, &b);
3971
3972       if (l <= left && right <= r
3973           && t <= top && bottom <= b)
3974         return GTK_WIDGET (list->data);
3975     }
3976
3977   return NULL;
3978 }
3979
3980 static void
3981 gtk_menu_move_current (GtkMenuShell *menu_shell,
3982                        GtkMenuDirectionType direction)
3983 {
3984   GtkMenu *menu = GTK_MENU (menu_shell);
3985
3986   /* use special table menu key bindings */
3987   if (menu_shell->active_menu_item && gtk_menu_get_n_columns (menu) > 1)
3988     {
3989       int i;
3990       guint l, r, t, b;
3991       gboolean rtl = (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL);
3992       GtkWidget *match = NULL;
3993
3994       get_effective_child_attach (menu_shell->active_menu_item, &l, &r, &t, &b);
3995
3996       if (direction == GTK_MENU_DIR_NEXT)
3997         {
3998           for (i = b; i < gtk_menu_get_n_rows (menu); i++)
3999             {
4000               match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
4001               if (match)
4002                 break;
4003             }
4004
4005           if (!match)
4006             {
4007               /* wrap around */
4008               for (i = 0; i < t; i++)
4009                 {
4010                   match = find_child_containing (menu_shell,
4011                                                  l, l + 1, i, i + 1);
4012                   if (match)
4013                     break;
4014                 }
4015             }
4016         }
4017       else if (direction == GTK_MENU_DIR_PREV)
4018         {
4019           for (i = t; i > 0; i--)
4020             {
4021               match = find_child_containing (menu_shell, l, l + 1, i - 1, i);
4022               if (match)
4023                 break;
4024             }
4025
4026           if (!match)
4027             {
4028               /* wrap around */
4029               for (i = gtk_menu_get_n_rows (menu); i > b; i--)
4030                 {
4031                   match = find_child_containing (menu_shell,
4032                                                  l, l + 1, i - 1, i);
4033                   if (match)
4034                     break;
4035                 }
4036             }
4037         }
4038       else if ((!rtl && direction == GTK_MENU_DIR_PARENT)
4039                || (rtl && direction == GTK_MENU_DIR_CHILD))
4040         {
4041           /* we go one left if possible */
4042           if (l > 0)
4043             match = find_child_containing (menu_shell, l - 1, l, t, t + 1);
4044
4045           if (!match)
4046             {
4047               GtkWidget *parent = menu_shell->parent_menu_shell;
4048
4049               if (!parent
4050                   || g_list_length (GTK_MENU_SHELL (parent)->children) <= 1)
4051                 match = menu_shell->active_menu_item;
4052             }
4053         }
4054       else if ((!rtl && direction == GTK_MENU_DIR_CHILD)
4055                || (rtl && direction == GTK_MENU_DIR_PARENT))
4056         {
4057           /* we go one right if possible */
4058           if (r < gtk_menu_get_n_columns (menu))
4059             match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
4060
4061           if (!match)
4062             {
4063               GtkWidget *parent = menu_shell->parent_menu_shell;
4064
4065               if (! GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
4066                   (!parent ||
4067                    g_list_length (GTK_MENU_SHELL (parent)->children) <= 1))
4068                 match = menu_shell->active_menu_item;
4069             }
4070         }
4071
4072       if (match)
4073         {
4074           gtk_menu_shell_select_item (menu_shell, match);
4075           return;
4076         }
4077     }
4078
4079   GTK_MENU_SHELL_CLASS (parent_class)->move_current (menu_shell, direction);
4080 }
4081
4082 static gint
4083 get_visible_size (GtkMenu *menu)
4084 {
4085   GtkWidget *widget = GTK_WIDGET (menu);
4086   GtkContainer *container = GTK_CONTAINER (menu);
4087   
4088   gint menu_height = (widget->allocation.height
4089                       - 2 * (container->border_width
4090                              + widget->style->ythickness));
4091   
4092   if (menu->upper_arrow_visible && !menu->tearoff_active)
4093     menu_height -= MENU_SCROLL_ARROW_HEIGHT;
4094   if (menu->lower_arrow_visible && !menu->tearoff_active)
4095     menu_height -= MENU_SCROLL_ARROW_HEIGHT;
4096
4097   return menu_height;
4098 }
4099
4100 /* Find the sensitive on-screen child containing @y, or if none,
4101  * the nearest selectable onscreen child. (%NULL if none)
4102  */
4103 static GtkWidget *
4104 child_at (GtkMenu *menu,
4105           gint     y)
4106 {
4107   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
4108   GtkWidget *child = NULL;
4109   gint child_offset = 0;
4110   GList *children;
4111   gint menu_height;
4112   gint lower, upper;            /* Onscreen bounds */
4113
4114   menu_height = get_visible_size (menu);
4115   lower = menu->scroll_offset;
4116   upper = menu->scroll_offset + menu_height;
4117   
4118   for (children = menu_shell->children; children; children = children->next)
4119     {
4120       if (GTK_WIDGET_VISIBLE (children->data))
4121         {
4122           GtkRequisition child_requisition;
4123
4124           gtk_widget_size_request (children->data, &child_requisition);
4125
4126           if (_gtk_menu_item_is_selectable (children->data) &&
4127               child_offset >= lower &&
4128               child_offset + child_requisition.height <= upper)
4129             {
4130               child = children->data;
4131               
4132               if (child_offset + child_requisition.height > y &&
4133                   !GTK_IS_TEAROFF_MENU_ITEM (child))
4134                 return child;
4135             }
4136       
4137           child_offset += child_requisition.height;
4138         }
4139     }
4140
4141   return child;
4142 }
4143
4144 static gint
4145 get_menu_height (GtkMenu *menu)
4146 {
4147   gint height;
4148   GtkWidget *widget = GTK_WIDGET (menu);
4149
4150   height = widget->requisition.height;
4151   height -= (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2;
4152
4153   if (menu->upper_arrow_visible && !menu->tearoff_active)
4154     height -= MENU_SCROLL_ARROW_HEIGHT;
4155
4156   if (menu->lower_arrow_visible && !menu->tearoff_active)
4157     height -= MENU_SCROLL_ARROW_HEIGHT;
4158
4159   return height;
4160 }
4161
4162 static void
4163 gtk_menu_real_move_scroll (GtkMenu       *menu,
4164                            GtkScrollType  type)
4165 {
4166   gint page_size = get_visible_size (menu);
4167   gint end_position = get_menu_height (menu);
4168   GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
4169   
4170   switch (type)
4171     {
4172     case GTK_SCROLL_PAGE_UP:
4173     case GTK_SCROLL_PAGE_DOWN:
4174       {
4175         gint old_offset;
4176         gint new_offset;
4177         gint child_offset = 0;
4178         gboolean old_upper_arrow_visible;
4179         gint step;
4180
4181         if (type == GTK_SCROLL_PAGE_UP)
4182           step = - page_size;
4183         else
4184           step = page_size;
4185
4186         if (menu_shell->active_menu_item)
4187           {
4188             gint child_height;
4189             
4190             compute_child_offset (menu, menu_shell->active_menu_item,
4191                                   &child_offset, &child_height, NULL);
4192             child_offset += child_height / 2;
4193           }
4194
4195         menu_shell->ignore_enter = TRUE;
4196         old_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
4197         old_offset = menu->scroll_offset;
4198
4199         new_offset = menu->scroll_offset + step;
4200         new_offset = CLAMP (new_offset, 0, end_position - page_size);
4201
4202         gtk_menu_scroll_to (menu, new_offset);
4203         
4204         if (menu_shell->active_menu_item)
4205           {
4206             GtkWidget *new_child;
4207             gboolean new_upper_arrow_visible = menu->upper_arrow_visible && !menu->tearoff_active;
4208
4209             if (menu->scroll_offset != old_offset)
4210               step = menu->scroll_offset - old_offset;
4211
4212             step -= (new_upper_arrow_visible - old_upper_arrow_visible) * MENU_SCROLL_ARROW_HEIGHT;
4213
4214             new_child = child_at (menu, child_offset + step);
4215             if (new_child)
4216               gtk_menu_shell_select_item (menu_shell, new_child);
4217           }
4218       }
4219       break;
4220     case GTK_SCROLL_START:
4221       /* Ignore the enter event we might get if the pointer is on the menu
4222        */
4223       menu_shell->ignore_enter = TRUE;
4224       gtk_menu_scroll_to (menu, 0);
4225       gtk_menu_shell_select_first (menu_shell, TRUE);
4226       break;
4227     case GTK_SCROLL_END:
4228       /* Ignore the enter event we might get if the pointer is on the menu
4229        */
4230       menu_shell->ignore_enter = TRUE;
4231       gtk_menu_scroll_to (menu, end_position - page_size);
4232       _gtk_menu_shell_select_last (menu_shell, TRUE);
4233       break;
4234     default:
4235       break;
4236     }
4237 }
4238
4239
4240 /**
4241  * gtk_menu_set_monitor:
4242  * @menu: a #GtkMenu
4243  * @monitor_num: the number of the monitor on which the menu should
4244  *    be popped up
4245  * 
4246  * Informs GTK+ on which monitor a menu should be popped up. 
4247  * See gdk_screen_get_monitor_geometry().
4248  *
4249  * This function should be called from a #GtkMenuPositionFunc if the
4250  * menu should not appear on the same monitor as the pointer. This 
4251  * information can't be reliably inferred from the coordinates returned
4252  * by a #GtkMenuPositionFunc, since, for very long menus, these coordinates 
4253  * may extend beyond the monitor boundaries or even the screen boundaries. 
4254  *
4255  * Since: 2.4
4256  **/
4257 void
4258 gtk_menu_set_monitor (GtkMenu *menu,
4259                       gint     monitor_num)
4260 {
4261   GtkMenuPrivate *priv;
4262   g_return_if_fail (GTK_IS_MENU (menu));
4263
4264   priv = gtk_menu_get_private (menu);
4265   
4266   priv->monitor_num = monitor_num;
4267 }
4268
4269 /**
4270  * gtk_menu_get_for_attach_widget:
4271  * @widget: a #GtkWidget
4272  *
4273  * Returns a list of the menus which are attached to this widget.
4274  * This list is owned by GTK+ and must not be modified.
4275  *
4276  * Return value: the list of menus attached to his widget.
4277  *
4278  * Since: 2.6
4279  **/
4280 GList*
4281 gtk_menu_get_for_attach_widget (GtkWidget *widget)
4282 {
4283   GList *list;
4284   
4285   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
4286   
4287   list = g_object_get_data (G_OBJECT (widget), ATTACHED_MENUS);
4288   return list;
4289 }
4290