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