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