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