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