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