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