]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
Break compatibility and make gtk_image_new() take no arguments. The
[~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 #include <ctype.h>
28 #include <string.h> /* memset */
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkbindings.h"
31 #include "gtklabel.h"
32 #include "gtkmain.h"
33 #include "gtkmenu.h"
34 #include "gtkmenuitem.h"
35 #include "gtksignal.h"
36 #include "gtkwindow.h"
37 #include "gtkhbox.h"
38 #include "gtkvscrollbar.h"
39 #include "gtksettings.h"
40 #include "gtkintl.h"
41
42
43 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_GET_CLASS (w)
44 #define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
45
46 #define SUBMENU_NAV_REGION_PADDING 2
47 #define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
48
49 #define MENU_SCROLL_STEP 10
50 #define MENU_SCROLL_ARROW_HEIGHT 16
51 #define MENU_SCROLL_FAST_ZONE 4
52 #define MENU_SCROLL_TIMEOUT1 150
53 #define MENU_SCROLL_TIMEOUT2 50
54
55 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
56
57 struct _GtkMenuAttachData
58 {
59   GtkWidget *attach_widget;
60   GtkMenuDetachFunc detacher;
61 };
62
63 enum {
64   PROP_0,
65   PROP_TEAROFF_TITLE
66 };
67
68 static void     gtk_menu_class_init        (GtkMenuClass     *klass);
69 static void     gtk_menu_init              (GtkMenu          *menu);
70 static void     gtk_menu_set_property      (GObject      *object,
71                                             guint         prop_id,
72                                             const GValue *value,
73                                             GParamSpec   *pspec);
74 static void     gtk_menu_get_property      (GObject     *object,
75                                             guint        prop_id,
76                                             GValue      *value,
77                                             GParamSpec  *pspec);
78 static void     gtk_menu_destroy           (GtkObject        *object);
79 static void     gtk_menu_realize           (GtkWidget        *widget);
80 static void     gtk_menu_unrealize         (GtkWidget        *widget);
81 static void     gtk_menu_size_request      (GtkWidget        *widget,
82                                             GtkRequisition   *requisition);
83 static void     gtk_menu_size_allocate     (GtkWidget        *widget,
84                                             GtkAllocation    *allocation);
85 static void     gtk_menu_paint             (GtkWidget        *widget);
86 static gboolean gtk_menu_expose            (GtkWidget        *widget,
87                                             GdkEventExpose   *event);
88 static gboolean gtk_menu_key_press         (GtkWidget        *widget,
89                                             GdkEventKey      *event);
90 static gboolean gtk_menu_motion_notify     (GtkWidget        *widget,
91                                             GdkEventMotion   *event);
92 static gboolean gtk_menu_enter_notify      (GtkWidget        *widget,
93                                             GdkEventCrossing *event);
94 static gboolean gtk_menu_leave_notify      (GtkWidget        *widget,
95                                             GdkEventCrossing *event);
96 static void     gtk_menu_scroll_to         (GtkMenu          *menu,
97                                             gint              offset);
98 static void     gtk_menu_stop_scrolling    (GtkMenu          *menu);
99 static gboolean gtk_menu_scroll_timeout    (gpointer          data);
100 static void     gtk_menu_select_item       (GtkMenuShell     *menu_shell,
101                                             GtkWidget        *menu_item);
102 static void     gtk_menu_real_insert       (GtkMenuShell     *menu_shell,
103                                             GtkWidget        *child,
104                                             gint              position);
105 static void     gtk_menu_scrollbar_changed (GtkAdjustment    *adjustment,
106                                             GtkMenu          *menu);
107 static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
108                                             gboolean         enter);
109 static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
110                                             gint             width);
111
112 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
113 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
114 static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
115                                                         gint              event_x,
116                                                         gint              event_y);
117 static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
118                                                         GtkMenuItem      *menu_item,
119                                                         GdkEventCrossing *event);
120  
121 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
122 static void gtk_menu_show_all       (GtkWidget         *widget);
123 static void gtk_menu_hide_all       (GtkWidget         *widget);
124 static void gtk_menu_position       (GtkMenu           *menu);
125 static void gtk_menu_reparent       (GtkMenu           *menu, 
126                                      GtkWidget         *new_parent, 
127                                      gboolean           unrealize);
128 static void gtk_menu_remove         (GtkContainer      *menu,
129                                      GtkWidget         *widget);
130
131 static GtkMenuShellClass *parent_class = NULL;
132 static const gchar       *attach_data_key = "gtk-menu-attach-data";
133
134 GtkType
135 gtk_menu_get_type (void)
136 {
137   static GtkType menu_type = 0;
138   
139   if (!menu_type)
140     {
141       static const GtkTypeInfo menu_info =
142       {
143         "GtkMenu",
144         sizeof (GtkMenu),
145         sizeof (GtkMenuClass),
146         (GtkClassInitFunc) gtk_menu_class_init,
147         (GtkObjectInitFunc) gtk_menu_init,
148         /* reserved_1 */ NULL,
149         /* reserved_2 */ NULL,
150         (GtkClassInitFunc) NULL,
151       };
152       
153       menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
154     }
155   
156   return menu_type;
157 }
158
159 static void
160 gtk_menu_class_init (GtkMenuClass *class)
161 {
162   GObjectClass *gobject_class;
163   GtkObjectClass *object_class;
164   GtkWidgetClass *widget_class;
165   GtkContainerClass *container_class;
166   GtkMenuShellClass *menu_shell_class;
167
168   GtkBindingSet *binding_set;
169   
170   gobject_class = (GObjectClass*) class;
171   object_class = (GtkObjectClass*) class;
172   widget_class = (GtkWidgetClass*) class;
173   container_class = (GtkContainerClass*) class;
174   menu_shell_class = (GtkMenuShellClass*) class;
175   parent_class = gtk_type_class (gtk_menu_shell_get_type ());
176   
177   gobject_class->set_property = gtk_menu_set_property;
178   gobject_class->get_property = gtk_menu_get_property;
179
180   g_object_class_install_property (gobject_class,
181                                    PROP_TEAROFF_TITLE,
182                                    g_param_spec_string ("tearoff-title",
183                                                         _("Tearoff Title"),
184                                                         _("A title that may be displayed by the window manager when this menu is torn-off."),
185                                                         "",
186                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
187   object_class->destroy = gtk_menu_destroy;
188   
189   widget_class->realize = gtk_menu_realize;
190   widget_class->unrealize = gtk_menu_unrealize;
191   widget_class->size_request = gtk_menu_size_request;
192   widget_class->size_allocate = gtk_menu_size_allocate;
193   widget_class->expose_event = gtk_menu_expose;
194   widget_class->key_press_event = gtk_menu_key_press;
195   widget_class->motion_notify_event = gtk_menu_motion_notify;
196   widget_class->show_all = gtk_menu_show_all;
197   widget_class->hide_all = gtk_menu_hide_all;
198   widget_class->enter_notify_event = gtk_menu_enter_notify;
199   widget_class->leave_notify_event = gtk_menu_leave_notify;
200
201   container_class->remove = gtk_menu_remove;
202   
203   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
204   menu_shell_class->deactivate = gtk_menu_deactivate;
205   menu_shell_class->select_item = gtk_menu_select_item;
206   menu_shell_class->insert = gtk_menu_real_insert;
207
208   binding_set = gtk_binding_set_by_class (class);
209   gtk_binding_entry_add_signal (binding_set,
210                                 GDK_Up, 0,
211                                 "move_current", 1,
212                                 GTK_TYPE_MENU_DIRECTION_TYPE,
213                                 GTK_MENU_DIR_PREV);
214   gtk_binding_entry_add_signal (binding_set,
215                                 GDK_KP_Up, 0,
216                                 "move_current", 1,
217                                 GTK_TYPE_MENU_DIRECTION_TYPE,
218                                 GTK_MENU_DIR_PREV);
219   gtk_binding_entry_add_signal (binding_set,
220                                 GDK_Down, 0,
221                                 "move_current", 1,
222                                 GTK_TYPE_MENU_DIRECTION_TYPE,
223                                 GTK_MENU_DIR_NEXT);
224   gtk_binding_entry_add_signal (binding_set,
225                                 GDK_KP_Down, 0,
226                                 "move_current", 1,
227                                 GTK_TYPE_MENU_DIRECTION_TYPE,
228                                 GTK_MENU_DIR_NEXT);
229   gtk_binding_entry_add_signal (binding_set,
230                                 GDK_Left, 0,
231                                 "move_current", 1,
232                                 GTK_TYPE_MENU_DIRECTION_TYPE,
233                                 GTK_MENU_DIR_PARENT);
234   gtk_binding_entry_add_signal (binding_set,
235                                 GDK_KP_Left, 0,
236                                 "move_current", 1,
237                                 GTK_TYPE_MENU_DIRECTION_TYPE,
238                                 GTK_MENU_DIR_PARENT);
239   gtk_binding_entry_add_signal (binding_set,
240                                 GDK_Right, 0,
241                                 "move_current", 1,
242                                 GTK_TYPE_MENU_DIRECTION_TYPE,
243                                 GTK_MENU_DIR_CHILD);
244   gtk_binding_entry_add_signal (binding_set,
245                                 GDK_KP_Right, 0,
246                                 "move_current", 1,
247                                 GTK_TYPE_MENU_DIRECTION_TYPE,
248                                 GTK_MENU_DIR_CHILD);
249 }
250
251
252 static void 
253 gtk_menu_set_property (GObject      *object,
254                        guint         prop_id,
255                        const GValue *value,
256                        GParamSpec   *pspec)
257 {
258   GtkMenu *menu;
259   
260   menu = GTK_MENU (object);
261   
262   switch (prop_id)
263     {
264     case PROP_TEAROFF_TITLE:
265       gtk_menu_set_title (menu, g_value_get_string (value));
266       break;      
267     default:
268       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269       break;
270     }
271 }
272
273 static void 
274 gtk_menu_get_property (GObject     *object,
275                        guint        prop_id,
276                        GValue      *value,
277                        GParamSpec  *pspec)
278 {
279   GtkMenu *menu;
280   
281   menu = GTK_MENU (object);
282   
283   switch (prop_id)
284     {
285     case PROP_TEAROFF_TITLE:
286       g_value_set_string (value, gtk_object_get_data (GTK_OBJECT (menu), 
287                                                       "gtk-menu-title"));
288       break;
289     default:
290       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
291       break;
292     }
293 }
294
295 static gboolean
296 gtk_menu_window_event (GtkWidget *window,
297                        GdkEvent  *event,
298                        GtkWidget *menu)
299 {
300   gboolean handled = FALSE;
301
302   gtk_widget_ref (window);
303   gtk_widget_ref (menu);
304
305   switch (event->type)
306     {
307     case GDK_KEY_PRESS:
308     case GDK_KEY_RELEASE:
309       handled = gtk_widget_event (menu, event);
310       break;
311     default:
312       break;
313     }
314
315   gtk_widget_unref (window);
316   gtk_widget_unref (menu);
317
318   return handled;
319 }
320
321 static void
322 gtk_menu_init (GtkMenu *menu)
323 {
324   menu->parent_menu_item = NULL;
325   menu->old_active_menu_item = NULL;
326   menu->accel_group = NULL;
327   menu->position_func = NULL;
328   menu->position_func_data = NULL;
329   menu->toggle_size = 0;
330
331   menu->toplevel = g_object_connect (gtk_widget_new (GTK_TYPE_WINDOW,
332                                                      "type", GTK_WINDOW_POPUP,
333                                                      "child", menu,
334                                                      NULL),
335                                      "signal::event", gtk_menu_window_event, menu,
336                                      "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
337                                      NULL);
338   gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
339                          FALSE, FALSE, TRUE);
340   gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
341
342   /* Refloat the menu, so that reference counting for the menu isn't
343    * affected by it being a child of the toplevel
344    */
345   GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
346   menu->needs_destruction_ref_count = TRUE;
347
348   menu->view_window = NULL;
349   menu->bin_window = NULL;
350
351   menu->scroll_offset = 0;
352   menu->scroll_step  = 0;
353   menu->timeout_id = 0;
354   menu->scroll_fast = FALSE;
355   
356   menu->tearoff_window = NULL;
357   menu->tearoff_hbox = NULL;
358   menu->torn_off = FALSE;
359   menu->tearoff_active = FALSE;
360   menu->tearoff_adjustment = NULL;
361   menu->tearoff_scrollbar = NULL;
362
363   menu->upper_arrow_visible = FALSE;
364   menu->lower_arrow_visible = FALSE;
365   menu->upper_arrow_prelight = FALSE;
366   menu->lower_arrow_prelight = FALSE;
367   
368   MENU_NEEDS_RESIZE (menu) = TRUE;
369 }
370
371 static void
372 gtk_menu_destroy (GtkObject *object)
373 {
374   GtkMenu *menu;
375   GtkMenuAttachData *data;
376
377   g_return_if_fail (GTK_IS_MENU (object));
378
379   menu = GTK_MENU (object);
380
381   gtk_menu_stop_scrolling (menu);
382   
383   data = gtk_object_get_data (object, attach_data_key);
384   if (data)
385     gtk_menu_detach (menu);
386   
387   gtk_menu_stop_navigating_submenu (menu);
388
389   gtk_menu_set_accel_group (menu, NULL);
390
391   if (menu->old_active_menu_item)
392     {
393       gtk_widget_unref (menu->old_active_menu_item);
394       menu->old_active_menu_item = NULL;
395     }
396
397   /* Add back the reference count for being a child */
398   if (menu->needs_destruction_ref_count)
399     {
400       menu->needs_destruction_ref_count = FALSE;
401       gtk_object_ref (object);
402     }
403   
404   if (menu->toplevel)
405     gtk_widget_destroy (menu->toplevel);
406   if (menu->tearoff_window)
407     gtk_widget_destroy (menu->tearoff_window);
408
409   GTK_OBJECT_CLASS (parent_class)->destroy (object);
410 }
411
412
413 void
414 gtk_menu_attach_to_widget (GtkMenu             *menu,
415                            GtkWidget           *attach_widget,
416                            GtkMenuDetachFunc    detacher)
417 {
418   GtkMenuAttachData *data;
419   
420   g_return_if_fail (GTK_IS_MENU (menu));
421   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
422   g_return_if_fail (detacher != NULL);
423   
424   /* keep this function in sync with gtk_widget_set_parent()
425    */
426   
427   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
428   if (data)
429     {
430       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
431                  gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
432       return;
433     }
434   
435   gtk_object_ref (GTK_OBJECT (menu));
436   gtk_object_sink (GTK_OBJECT (menu));
437   
438   data = g_new (GtkMenuAttachData, 1);
439   data->attach_widget = attach_widget;
440   data->detacher = detacher;
441   gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
442   
443   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
444     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
445   
446   /* we don't need to set the style here, since
447    * we are a toplevel widget.
448    */
449 }
450
451 GtkWidget*
452 gtk_menu_get_attach_widget (GtkMenu *menu)
453 {
454   GtkMenuAttachData *data;
455   
456   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
457   
458   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
459   if (data)
460     return data->attach_widget;
461   return NULL;
462 }
463
464 void
465 gtk_menu_detach (GtkMenu *menu)
466 {
467   GtkMenuAttachData *data;
468   
469   g_return_if_fail (GTK_IS_MENU (menu));
470   
471   /* keep this function in sync with gtk_widget_unparent()
472    */
473   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
474   if (!data)
475     {
476       g_warning ("gtk_menu_detach(): menu is not attached");
477       return;
478     }
479   gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
480   
481   data->detacher (data->attach_widget, menu);
482   
483   if (GTK_WIDGET_REALIZED (menu))
484     gtk_widget_unrealize (GTK_WIDGET (menu));
485   
486   g_free (data);
487   
488   gtk_widget_unref (GTK_WIDGET (menu));
489 }
490
491 void 
492 gtk_menu_remove (GtkContainer *container,
493                  GtkWidget    *widget)
494 {
495   GtkMenu *menu;
496   g_return_if_fail (GTK_IS_MENU (container));
497   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
498
499   menu = GTK_MENU (container);
500
501   /* Clear out old_active_menu_item if it matches the item we are removing
502    */
503   if (menu->old_active_menu_item == widget)
504     {
505       gtk_widget_unref (menu->old_active_menu_item);
506       menu->old_active_menu_item = NULL;
507     }
508
509   GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
510 }
511
512
513 GtkWidget*
514 gtk_menu_new (void)
515 {
516   return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
517 }
518
519 static void
520 gtk_menu_real_insert (GtkMenuShell     *menu_shell,
521                       GtkWidget        *child,
522                       gint              position)
523 {
524   if (GTK_WIDGET_REALIZED (menu_shell))
525     gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
526   
527   GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
528 }
529
530 static void
531 gtk_menu_tearoff_bg_copy (GtkMenu *menu)
532 {
533   GtkWidget *widget;
534   gint width, height;
535
536   widget = GTK_WIDGET (menu);
537
538   if (menu->torn_off)
539     {
540       GdkPixmap *pixmap;
541       GdkGC *gc;
542       GdkGCValues gc_values;
543
544       menu->tearoff_active = FALSE;
545       menu->saved_scroll_offset = menu->scroll_offset;
546       
547       gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
548       gc = gdk_gc_new_with_values (widget->window,
549                                    &gc_values, GDK_GC_SUBWINDOW);
550       
551       gdk_window_get_size (menu->tearoff_window->window, &width, &height);
552       
553       pixmap = gdk_pixmap_new (menu->tearoff_window->window,
554                                width,
555                                height,
556                                -1);
557
558       gdk_draw_pixmap (pixmap, gc,
559                        menu->tearoff_window->window,
560                        0, 0, 0, 0, -1, -1);
561       gdk_gc_unref (gc);
562
563       gtk_widget_set_usize (menu->tearoff_window,
564                             width,
565                             height);
566
567       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
568       gdk_pixmap_unref (pixmap);
569     }
570 }
571
572 void
573 gtk_menu_popup (GtkMenu             *menu,
574                 GtkWidget           *parent_menu_shell,
575                 GtkWidget           *parent_menu_item,
576                 GtkMenuPositionFunc  func,
577                 gpointer             data,
578                 guint                button,
579                 guint32              activate_time)
580 {
581   GtkWidget *widget;
582   GtkWidget *xgrab_shell;
583   GtkWidget *parent;
584   GdkEvent *current_event;
585   GtkMenuShell *menu_shell;
586
587   g_return_if_fail (GTK_IS_MENU (menu));
588   
589   widget = GTK_WIDGET (menu);
590   menu_shell = GTK_MENU_SHELL (menu);
591   
592   menu_shell->parent_menu_shell = parent_menu_shell;
593   menu_shell->active = TRUE;
594   menu_shell->button = button;
595
596   /* If we are popping up the menu from something other than, a button
597    * press then, as a heuristic, we ignore enter events for the menu
598    * until we get a MOTION_NOTIFY.  
599    */
600
601   current_event = gtk_get_current_event ();
602   if (current_event)
603     {
604       if ((current_event->type != GDK_BUTTON_PRESS) &&
605           (current_event->type != GDK_ENTER_NOTIFY))
606         menu_shell->ignore_enter = TRUE;
607     }
608
609   if (menu->torn_off)
610     {
611       gtk_menu_tearoff_bg_copy (menu);
612
613       gtk_menu_reparent (menu, menu->toplevel, FALSE);
614     }
615   
616   menu->parent_menu_item = parent_menu_item;
617   menu->position_func = func;
618   menu->position_func_data = data;
619   menu_shell->activate_time = activate_time;
620
621   gtk_menu_position (menu);
622
623   /* We need to show the menu _here_ because code expects to be
624    * able to tell if the menu is onscreen by looking at the
625    * GTK_WIDGET_VISIBLE (menu)
626    */
627   gtk_widget_show (GTK_WIDGET (menu));
628   gtk_widget_show (menu->toplevel);
629
630   if (current_event)
631     {
632       /* Also, if we're popping up from a key event, select the first
633        * item in the menu. Bad hack, but no better way to do it
634        * in current menu framework.
635        */
636       if (current_event->type == GDK_KEY_PRESS &&
637           GTK_MENU_SHELL (menu)->children)
638         {
639           gtk_menu_shell_select_item (GTK_MENU_SHELL (menu),
640                                       GTK_MENU_SHELL (menu)->children->data);
641         }
642       
643       gdk_event_free (current_event);
644     }
645   
646   gtk_menu_scroll_to (menu, menu->scroll_offset);
647
648   /* Find the last viewable ancestor, and make an X grab on it
649    */
650   parent = GTK_WIDGET (menu);
651   xgrab_shell = NULL;
652   while (parent)
653     {
654       gboolean viewable = TRUE;
655       GtkWidget *tmp = parent;
656       
657       while (tmp)
658         {
659           if (!GTK_WIDGET_MAPPED (tmp))
660             {
661               viewable = FALSE;
662               break;
663             }
664           tmp = tmp->parent;
665         }
666       
667       if (viewable)
668         xgrab_shell = parent;
669       
670       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
671     }
672   
673   if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
674     {
675       if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
676                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
677                              GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
678                              GDK_POINTER_MOTION_MASK,
679                              NULL, NULL, activate_time) == 0))
680         {
681           if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
682                                  activate_time) == 0)
683             GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
684           else
685             gdk_pointer_ungrab (activate_time);
686         }
687     }
688
689   gtk_grab_add (GTK_WIDGET (menu));
690 }
691
692 void
693 gtk_menu_popdown (GtkMenu *menu)
694 {
695   GtkMenuShell *menu_shell;
696
697   g_return_if_fail (GTK_IS_MENU (menu));
698   
699   menu_shell = GTK_MENU_SHELL (menu);
700   
701   menu_shell->parent_menu_shell = NULL;
702   menu_shell->active = FALSE;
703   menu_shell->ignore_enter = FALSE;
704
705   gtk_menu_stop_scrolling (menu);
706   
707   gtk_menu_stop_navigating_submenu (menu);
708   
709   if (menu_shell->active_menu_item)
710     {
711       if (menu->old_active_menu_item)
712         gtk_widget_unref (menu->old_active_menu_item);
713       menu->old_active_menu_item = menu_shell->active_menu_item;
714       gtk_widget_ref (menu->old_active_menu_item);
715     }
716
717   gtk_menu_shell_deselect (menu_shell);
718   
719   /* The X Grab, if present, will automatically be removed when we hide
720    * the window */
721   gtk_widget_hide (menu->toplevel);
722
723   if (menu->torn_off)
724     {
725       gint width, height;
726       gdk_window_get_size (menu->tearoff_window->window, &width, &height);
727       gtk_widget_set_usize (menu->tearoff_window,
728                             -1,
729                             height);
730       
731       if (GTK_BIN (menu->toplevel)->child) 
732         {
733           gtk_menu_reparent (menu, menu->tearoff_hbox, TRUE);
734         } 
735       else
736         {
737           /* We popped up the menu from the tearoff, so we need to 
738            * release the grab - we aren't actually hiding the menu.
739            */
740           if (menu_shell->have_xgrab)
741             {
742               gdk_pointer_ungrab (GDK_CURRENT_TIME);
743               gdk_keyboard_ungrab (GDK_CURRENT_TIME);
744             }
745         }
746
747       /* gtk_menu_popdown is called each time a menu item is selected from
748        * a torn off menu. Only scroll back to the saved position if the
749        * non-tearoff menu was popped down.
750        */
751       if (!menu->tearoff_active)
752         gtk_menu_scroll_to (menu, menu->saved_scroll_offset);
753       menu->tearoff_active = TRUE;
754     }
755   else
756     gtk_widget_hide (GTK_WIDGET (menu));
757
758   menu_shell->have_xgrab = FALSE;
759   gtk_grab_remove (GTK_WIDGET (menu));
760 }
761
762 GtkWidget*
763 gtk_menu_get_active (GtkMenu *menu)
764 {
765   GtkWidget *child;
766   GList *children;
767   
768   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
769   
770   if (!menu->old_active_menu_item)
771     {
772       child = NULL;
773       children = GTK_MENU_SHELL (menu)->children;
774       
775       while (children)
776         {
777           child = children->data;
778           children = children->next;
779           
780           if (GTK_BIN (child)->child)
781             break;
782           child = NULL;
783         }
784       
785       menu->old_active_menu_item = child;
786       if (menu->old_active_menu_item)
787         gtk_widget_ref (menu->old_active_menu_item);
788     }
789   
790   return menu->old_active_menu_item;
791 }
792
793 void
794 gtk_menu_set_active (GtkMenu *menu,
795                      guint    index)
796 {
797   GtkWidget *child;
798   GList *tmp_list;
799   
800   g_return_if_fail (GTK_IS_MENU (menu));
801   
802   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
803   if (tmp_list)
804     {
805       child = tmp_list->data;
806       if (GTK_BIN (child)->child)
807         {
808           if (menu->old_active_menu_item)
809             gtk_widget_unref (menu->old_active_menu_item);
810           menu->old_active_menu_item = child;
811           gtk_widget_ref (menu->old_active_menu_item);
812         }
813     }
814 }
815
816 void
817 gtk_menu_set_accel_group (GtkMenu       *menu,
818                           GtkAccelGroup *accel_group)
819 {
820   g_return_if_fail (GTK_IS_MENU (menu));
821   
822   if (menu->accel_group != accel_group)
823     {
824       if (menu->accel_group)
825         gtk_accel_group_unref (menu->accel_group);
826       menu->accel_group = accel_group;
827       if (menu->accel_group)
828         gtk_accel_group_ref (menu->accel_group);
829     }
830 }
831
832 GtkAccelGroup*
833 gtk_menu_get_accel_group (GtkMenu *menu)
834 {
835   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
836
837   return menu->accel_group;
838 }
839
840 void
841 gtk_menu_reposition (GtkMenu *menu)
842 {
843   g_return_if_fail (GTK_IS_MENU (menu));
844
845   if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
846     gtk_menu_position (menu);
847 }
848
849 static void
850 gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
851                             GtkMenu       *menu)
852 {
853   g_return_if_fail (GTK_IS_MENU (menu));
854
855   if (adjustment->value != menu->scroll_offset)
856     gtk_menu_scroll_to (menu, adjustment->value);
857 }
858
859 static void
860 gtk_menu_set_tearoff_hints (GtkMenu *menu,
861                             gint     width)
862 {
863   GdkGeometry geometry_hints;
864   
865   if (!menu->tearoff_window)
866     return;
867
868   geometry_hints.min_width = width;
869   geometry_hints.max_width = width;
870     
871   geometry_hints.min_height = 0;
872   geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height;
873   gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
874                                  NULL,
875                                  &geometry_hints,
876                                  GDK_HINT_MAX_SIZE|GDK_HINT_MIN_SIZE);
877 }
878
879
880 void       
881 gtk_menu_set_tearoff_state (GtkMenu  *menu,
882                             gboolean  torn_off)
883 {
884   gint width, height;
885   
886   g_return_if_fail (GTK_IS_MENU (menu));
887
888   if (menu->torn_off != torn_off)
889     {
890       menu->torn_off = torn_off;
891       menu->tearoff_active = torn_off;
892       
893       if (menu->torn_off)
894         {
895           if (GTK_WIDGET_VISIBLE (menu))
896             gtk_menu_popdown (menu);
897
898           if (!menu->tearoff_window)
899             {
900               GtkWidget *attach_widget;
901               gchar *title;
902               
903               menu->tearoff_window = g_object_connect (gtk_widget_new (GTK_TYPE_WINDOW,
904                                                                        "type", GTK_WINDOW_TOPLEVEL,
905                                                                        NULL),
906                                                        "signal::destroy", gtk_widget_destroyed, &menu->tearoff_window,
907                                                        NULL);
908               gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
909                                         GDK_WINDOW_TYPE_HINT_MENU);
910               gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0);
911               gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
912               gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),  
913                                   "event",
914                                   GTK_SIGNAL_FUNC (gtk_menu_window_event), 
915                                   GTK_OBJECT (menu));
916               gtk_widget_realize (menu->tearoff_window);
917               
918               title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
919               if (!title)
920                 {
921                   attach_widget = gtk_menu_get_attach_widget (menu);
922                   if (GTK_IS_MENU_ITEM (attach_widget))
923                     {
924                       GtkWidget *child = GTK_BIN (attach_widget)->child;
925                       if (GTK_IS_LABEL (child))
926                         gtk_label_get (GTK_LABEL (child), &title);
927                     }
928                 }
929
930               if (title)
931                 gdk_window_set_title (menu->tearoff_window->window, title);
932
933               gdk_window_set_decorations (menu->tearoff_window->window, 
934                                           GDK_DECOR_ALL |
935                                           GDK_DECOR_RESIZEH |
936                                           GDK_DECOR_MINIMIZE |
937                                           GDK_DECOR_MAXIMIZE);
938               gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
939                                      FALSE, FALSE, TRUE);
940
941               menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
942               gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
943
944               gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
945               menu->tearoff_adjustment =
946                 GTK_ADJUSTMENT (gtk_adjustment_new (0,
947                                                     0,
948                                                     GTK_WIDGET (menu)->requisition.height,
949                                                     MENU_SCROLL_STEP,
950                                                     height/2,
951                                                     height));
952               g_object_connect (GTK_OBJECT (menu->tearoff_adjustment),
953                                 "signal::value_changed", gtk_menu_scrollbar_changed, menu,
954                                 NULL);
955               menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
956
957               gtk_box_pack_end (GTK_BOX (menu->tearoff_hbox),
958                                 menu->tearoff_scrollbar,
959                                 FALSE, FALSE, 0);
960               
961               if (menu->tearoff_adjustment->upper > height)
962                 gtk_widget_show (menu->tearoff_scrollbar);
963               
964               gtk_widget_show (menu->tearoff_hbox);
965             }
966           
967           gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
968           
969           gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
970           if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
971             width += menu->tearoff_scrollbar->requisition.width;
972             
973           gtk_menu_set_tearoff_hints (menu, width);
974             
975           gtk_widget_realize (menu->tearoff_window);
976           gtk_menu_position (menu);
977           
978           gtk_widget_show (GTK_WIDGET (menu));
979           gtk_widget_show (menu->tearoff_window);
980
981           gtk_menu_scroll_to (menu, 0);
982
983         }
984       else
985         {
986           gtk_widget_hide (menu->tearoff_window);
987           gtk_menu_reparent (menu, menu->toplevel, FALSE);
988         }
989     }
990 }
991
992 /**
993  * gtk_menu_get_tearoff_state:
994  * @menu: a #GtkMenu
995  *
996  * Returns whether the menu is torn off. See
997  * gtk_menu_set_tearoff_state ().
998  *
999  * Return value: %TRUE if the menu is currently torn off.
1000  **/
1001 gboolean
1002 gtk_menu_get_tearoff_state (GtkMenu *menu)
1003 {
1004   g_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
1005
1006   return menu->torn_off;
1007 }
1008
1009 void       
1010 gtk_menu_set_title (GtkMenu     *menu,
1011                     const gchar *title)
1012 {
1013   g_return_if_fail (GTK_IS_MENU (menu));
1014
1015   gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
1016                             g_strdup (title), (GtkDestroyNotify) g_free);
1017   g_object_notify (G_OBJECT (menu), "tearoff_title");
1018 }
1019
1020 /**
1021  * gtk_menu_get_title:
1022  * @menu: a #GtkMenu
1023  *
1024  * Returns the title of the menu. See gtk_menu_set_title().
1025  *
1026  * Return value: the title of the menu, or %NULL if the menu has no
1027  *               title set on it.
1028  **/
1029 G_CONST_RETURN gchar *
1030 gtk_menu_get_title (GtkMenu *menu)
1031 {
1032   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1033
1034   return gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
1035 }
1036
1037 void
1038 gtk_menu_reorder_child (GtkMenu   *menu,
1039                         GtkWidget *child,
1040                         gint       position)
1041 {
1042   GtkMenuShell *menu_shell;
1043   g_return_if_fail (GTK_IS_MENU (menu));
1044   g_return_if_fail (GTK_IS_MENU_ITEM (child));
1045   menu_shell = GTK_MENU_SHELL (menu);
1046   if (g_list_find (menu_shell->children, child))
1047     {   
1048       menu_shell->children = g_list_remove (menu_shell->children, child);
1049       menu_shell->children = g_list_insert (menu_shell->children, child, position);   
1050       if (GTK_WIDGET_VISIBLE (menu_shell))
1051         gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
1052     }   
1053 }
1054
1055 static void
1056 gtk_menu_realize (GtkWidget *widget)
1057 {
1058   GdkWindowAttr attributes;
1059   gint attributes_mask;
1060   gint border_width;
1061   GtkMenu *menu;
1062   GtkWidget *child;
1063   GList *children;
1064
1065   g_return_if_fail (GTK_IS_MENU (widget));
1066
1067   menu = GTK_MENU (widget);
1068   
1069   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1070   
1071   attributes.window_type = GDK_WINDOW_CHILD;
1072   attributes.x = widget->allocation.x;
1073   attributes.y = widget->allocation.y;
1074   attributes.width = widget->allocation.width;
1075   attributes.height = widget->allocation.height;
1076   attributes.wclass = GDK_INPUT_OUTPUT;
1077   attributes.visual = gtk_widget_get_visual (widget);
1078   attributes.colormap = gtk_widget_get_colormap (widget);
1079   
1080   attributes.event_mask = gtk_widget_get_events (widget);
1081   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
1082                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
1083   
1084   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1085   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1086   gdk_window_set_user_data (widget->window, widget);
1087   
1088   border_width = GTK_CONTAINER (widget)->border_width;
1089   
1090   attributes.x = border_width + widget->style->xthickness;
1091   attributes.y = border_width + widget->style->ythickness;
1092   attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
1093   attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
1094
1095   if (menu->upper_arrow_visible)
1096     {
1097       attributes.y += MENU_SCROLL_ARROW_HEIGHT;
1098       attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
1099     }
1100   if (menu->lower_arrow_visible)
1101     attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
1102
1103   menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
1104   gdk_window_set_user_data (menu->view_window, menu);
1105
1106   attributes.x = 0;
1107   attributes.y = 0;
1108   attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness) * 2);
1109   
1110   menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
1111   gdk_window_set_user_data (menu->bin_window, menu);
1112
1113   children = GTK_MENU_SHELL (menu)->children;
1114   while (children)
1115     {
1116       child = children->data;
1117       children = children->next;
1118           
1119       gtk_widget_set_parent_window (child, menu->bin_window);
1120     }
1121   
1122   widget->style = gtk_style_attach (widget->style, widget->window);
1123   gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
1124   gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
1125   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1126
1127   gtk_menu_paint (widget);
1128   
1129   gdk_window_show (menu->bin_window);
1130   gdk_window_show (menu->view_window);
1131 }
1132
1133 static void
1134 gtk_menu_unrealize (GtkWidget *widget)
1135 {
1136   GtkMenu *menu;
1137
1138   g_return_if_fail (GTK_IS_MENU (widget));
1139
1140   menu = GTK_MENU (widget);
1141
1142   gdk_window_set_user_data (menu->view_window, NULL);
1143   gdk_window_destroy (menu->view_window);
1144   menu->view_window = NULL;
1145
1146   gdk_window_set_user_data (menu->bin_window, NULL);
1147   gdk_window_destroy (menu->bin_window);
1148   menu->bin_window = NULL;
1149
1150   (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1151 }
1152
1153 static void
1154 gtk_menu_size_request (GtkWidget      *widget,
1155                        GtkRequisition *requisition)
1156 {
1157   GtkMenu *menu;
1158   GtkMenuShell *menu_shell;
1159   GtkWidget *child;
1160   GList *children;
1161   guint max_toggle_size;
1162   guint max_accel_width;
1163   GtkRequisition child_requisition;
1164   gint width;
1165   
1166   g_return_if_fail (GTK_IS_MENU (widget));
1167   g_return_if_fail (requisition != NULL);
1168   
1169   menu = GTK_MENU (widget);
1170   menu_shell = GTK_MENU_SHELL (widget);
1171   
1172   requisition->width = 0;
1173   requisition->height = 0;
1174   
1175   max_toggle_size = 0;
1176   max_accel_width = 0;
1177   
1178   children = menu_shell->children;
1179   while (children)
1180     {
1181       child = children->data;
1182       children = children->next;
1183       
1184       if (GTK_WIDGET_VISIBLE (child))
1185         {
1186           gint toggle_size;
1187
1188           /* It's important to size_request the child
1189            * before doing the toggle size request, in
1190            * case the toggle size request depends on the size
1191            * request of a child of the child (e.g. for ImageMenuItem)
1192            */
1193           
1194           GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
1195           gtk_widget_size_request (child, &child_requisition);
1196           
1197           requisition->width = MAX (requisition->width, child_requisition.width);
1198           requisition->height += child_requisition.height;
1199
1200           gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
1201           max_toggle_size = MAX (max_toggle_size, toggle_size);
1202           max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
1203         }
1204     }
1205   
1206   requisition->width += max_toggle_size + max_accel_width;
1207   requisition->width += (GTK_CONTAINER (menu)->border_width +
1208                          widget->style->xthickness) * 2;
1209   requisition->height += (GTK_CONTAINER (menu)->border_width +
1210                           widget->style->ythickness) * 2;
1211   
1212   menu->toggle_size = max_toggle_size;
1213
1214   /* If the requested width was different than the allocated width, we need to change
1215    * the geometry hints for the tear off window so that the window can actually be resized.
1216    * Don't resize the tearoff if it is not active, beacuse it won't redraw (it is only a background pixmap).
1217    */
1218   if ((requisition->width != GTK_WIDGET (menu)->allocation.width) && menu->tearoff_active)
1219     {
1220       width = requisition->width;
1221       if (menu->tearoff_scrollbar && GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1222         width += menu->tearoff_scrollbar->requisition.width;
1223       gtk_menu_set_tearoff_hints (menu, width);
1224     }
1225 }
1226
1227 static void
1228 gtk_menu_size_allocate (GtkWidget     *widget,
1229                         GtkAllocation *allocation)
1230 {
1231   GtkMenu *menu;
1232   GtkMenuShell *menu_shell;
1233   GtkWidget *child;
1234   GtkAllocation child_allocation;
1235   GList *children;
1236   gint x, y;
1237   gint width, height;
1238
1239   g_return_if_fail (GTK_IS_MENU (widget));
1240   g_return_if_fail (allocation != NULL);
1241   
1242   menu = GTK_MENU (widget);
1243   menu_shell = GTK_MENU_SHELL (widget);
1244
1245   widget->allocation = *allocation;
1246
1247   x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness;
1248   y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness;
1249   
1250   width = MAX (1, allocation->width - x * 2);
1251   height = MAX (1, allocation->height - y * 2);
1252   
1253   if (menu->upper_arrow_visible && !menu->tearoff_active)
1254     {
1255       y += MENU_SCROLL_ARROW_HEIGHT;
1256       height -= MENU_SCROLL_ARROW_HEIGHT;
1257     }
1258   
1259   if (menu->lower_arrow_visible && !menu->tearoff_active)
1260     height -= MENU_SCROLL_ARROW_HEIGHT;
1261   
1262   if (GTK_WIDGET_REALIZED (widget))
1263     {
1264       gdk_window_move_resize (widget->window,
1265                               allocation->x, allocation->y,
1266                               allocation->width, allocation->height);
1267
1268       gdk_window_move_resize (menu->view_window,
1269                               x,
1270                               y,
1271                               width,
1272                               height);
1273     }
1274
1275   if (menu_shell->children)
1276     {
1277       child_allocation.x = 0;
1278       child_allocation.y = 0;
1279       child_allocation.width = width;
1280       
1281       children = menu_shell->children;
1282       while (children)
1283         {
1284           child = children->data;
1285           children = children->next;
1286           
1287           if (GTK_WIDGET_VISIBLE (child))
1288             {
1289               GtkRequisition child_requisition;
1290               gtk_widget_get_child_requisition (child, &child_requisition);
1291               
1292               child_allocation.height = child_requisition.height;
1293
1294               gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
1295                                                   menu->toggle_size);
1296               gtk_widget_size_allocate (child, &child_allocation);
1297               gtk_widget_queue_draw (child);
1298               
1299               child_allocation.y += child_allocation.height;
1300             }
1301         }
1302       
1303       /* Resize the item window */
1304       if (GTK_WIDGET_REALIZED (widget))
1305         {
1306           gdk_window_resize (menu->bin_window,
1307                              child_allocation.width,
1308                              child_allocation.y);
1309         }
1310
1311
1312       if (menu->tearoff_active)
1313         {
1314           if (allocation->height >= widget->requisition.height)
1315             {
1316               if (GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1317                 {
1318                   gtk_widget_hide (menu->tearoff_scrollbar);
1319                   gtk_menu_set_tearoff_hints (menu, allocation->width);
1320                   gtk_widget_set_usize (menu->tearoff_window, -1, allocation->height);
1321
1322                   gtk_menu_scroll_to (menu, 0);
1323                 }
1324             }
1325           else
1326             {
1327               menu->tearoff_adjustment->upper = widget->requisition.height;
1328               menu->tearoff_adjustment->page_size = allocation->height;
1329               
1330               if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size >
1331                   menu->tearoff_adjustment->upper)
1332                 {
1333                   gint value;
1334                   value = menu->tearoff_adjustment->upper - menu->tearoff_adjustment->page_size;
1335                   if (value < 0)
1336                     value = 0;
1337                   gtk_menu_scroll_to (menu, value);
1338                 }
1339               
1340               gtk_adjustment_changed (menu->tearoff_adjustment);
1341               
1342               if (!GTK_WIDGET_VISIBLE (menu->tearoff_scrollbar))
1343                 {
1344                   gtk_menu_set_tearoff_hints (menu,
1345                                               allocation->width + menu->tearoff_scrollbar->requisition.width);
1346                   gtk_widget_show (menu->tearoff_scrollbar);
1347                   gtk_widget_set_usize (menu->tearoff_window, -1, allocation->height);
1348                 }
1349             }
1350         }
1351     }
1352 }
1353
1354 static void
1355 gtk_menu_paint (GtkWidget *widget)
1356 {
1357   gint border_x;
1358   gint border_y;
1359   gint width, height;
1360   gint menu_height;
1361   gint top_pos;
1362   GtkMenu *menu;
1363   
1364   g_return_if_fail (GTK_IS_MENU (widget));
1365
1366   menu = GTK_MENU (widget);
1367   
1368   if (GTK_WIDGET_DRAWABLE (widget))
1369     {
1370       gtk_paint_box (widget->style,
1371                      widget->window,
1372                      GTK_STATE_NORMAL,
1373                      GTK_SHADOW_OUT,
1374                      NULL, widget, "menu",
1375                      0, 0, -1, -1);
1376
1377       border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
1378       border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
1379       gdk_window_get_size (widget->window, &width, &height);
1380
1381       if (menu->upper_arrow_visible && !menu->tearoff_active)
1382         {
1383           gtk_paint_box (widget->style,
1384                          widget->window,
1385                          menu->upper_arrow_prelight ?
1386                          GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1387                          GTK_SHADOW_OUT,
1388                          NULL, widget, "menu",
1389                          border_x,
1390                          border_y,
1391                          width - 2*border_x,
1392                          MENU_SCROLL_ARROW_HEIGHT);
1393           
1394           gtk_paint_arrow (widget->style,
1395                            widget->window,
1396                            menu->upper_arrow_prelight ?
1397                            GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1398                            GTK_SHADOW_OUT,
1399                            NULL, widget, "menu",
1400                            GTK_ARROW_UP,
1401                            TRUE,
1402                            width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
1403                            2 * border_y + 1,
1404                            MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
1405                            MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
1406         }
1407   
1408       if (menu->lower_arrow_visible && !menu->tearoff_active)
1409         {
1410           gtk_paint_box (widget->style,
1411                          widget->window,
1412                          menu->lower_arrow_prelight ?
1413                          GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1414                          GTK_SHADOW_OUT,
1415                          NULL, widget, "menu",
1416                          border_x,
1417                          height - border_y - MENU_SCROLL_ARROW_HEIGHT + 1,
1418                          width - 2*border_x,
1419                          MENU_SCROLL_ARROW_HEIGHT);
1420           
1421           gtk_paint_arrow (widget->style,
1422                            widget->window,
1423                            menu->lower_arrow_prelight ?
1424                            GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
1425                            GTK_SHADOW_OUT,
1426                            NULL, widget, "menu",
1427                            GTK_ARROW_DOWN,
1428                            TRUE,
1429                            width / 2 - MENU_SCROLL_ARROW_HEIGHT / 2 + 1,
1430                            height - MENU_SCROLL_ARROW_HEIGHT + 1,
1431                            MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2,
1432                            MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
1433         }
1434
1435       if (menu->scroll_offset < 0)
1436         gtk_paint_box (widget->style,
1437                        menu->view_window,
1438                        GTK_STATE_ACTIVE,
1439                        GTK_SHADOW_IN,
1440                        NULL, widget, "menu",
1441                        0, 0,
1442                        -1,
1443                        -menu->scroll_offset);
1444
1445       menu_height = widget->requisition.height - 2*border_y;
1446       top_pos = height - 2*border_y - (menu->upper_arrow_visible ? MENU_SCROLL_ARROW_HEIGHT : 0);
1447
1448       if (menu_height - menu->scroll_offset < top_pos)
1449         gtk_paint_box (widget->style,
1450                        menu->view_window,
1451                        GTK_STATE_ACTIVE,
1452                        GTK_SHADOW_IN,
1453                        NULL, widget, "menu",
1454                        0,
1455                        menu_height - menu->scroll_offset,
1456                        -1,
1457                        top_pos - (menu_height - menu->scroll_offset));
1458     }
1459 }
1460
1461 static gboolean
1462 gtk_menu_expose (GtkWidget      *widget,
1463                  GdkEventExpose *event)
1464 {
1465   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1466   g_return_val_if_fail (event != NULL, FALSE);
1467
1468   if (GTK_WIDGET_DRAWABLE (widget))
1469     {
1470       gtk_menu_paint (widget);
1471       
1472       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
1473     }
1474   
1475   return FALSE;
1476 }
1477
1478 static gboolean
1479 gtk_menu_key_press (GtkWidget   *widget,
1480                     GdkEventKey *event)
1481 {
1482   GtkMenuShell *menu_shell;
1483   GtkMenu *menu;
1484   gboolean delete = FALSE;
1485   gchar *accel = NULL;
1486   
1487   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1488   g_return_val_if_fail (event != NULL, FALSE);
1489       
1490   menu_shell = GTK_MENU_SHELL (widget);
1491   menu = GTK_MENU (widget);
1492   
1493   gtk_menu_stop_navigating_submenu (menu);
1494
1495   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1496     return TRUE;
1497     
1498   g_object_get (G_OBJECT (gtk_settings_get_default ()),
1499                 "gtk-menu-bar-accel",
1500                 &accel,
1501                 NULL);
1502
1503   if (accel)
1504     {
1505       guint keyval = 0;
1506       GdkModifierType mods = 0;
1507       gboolean handled = FALSE;
1508       
1509       gtk_accelerator_parse (accel, &keyval, &mods);
1510
1511       if (keyval == 0)
1512         g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
1513
1514       /* FIXME this is wrong, needs to be in the global accel resolution
1515        * thing, to properly consider i18n etc., but that probably requires
1516        * AccelGroup changes etc.
1517        */
1518       if (event->keyval == keyval &&
1519           (mods & event->state) == mods)
1520         {
1521           gtk_signal_emit_by_name (GTK_OBJECT (menu), "cancel");
1522         }
1523
1524       g_free (accel);
1525
1526       if (handled)
1527         return TRUE;
1528     }
1529   
1530   switch (event->keyval)
1531     {
1532     case GDK_Delete:
1533     case GDK_KP_Delete:
1534     case GDK_BackSpace:
1535       delete = TRUE;
1536       break;
1537     default:
1538       break;
1539     }
1540
1541   /* Modify the accelerators */
1542   if (menu_shell->active_menu_item &&
1543       GTK_BIN (menu_shell->active_menu_item)->child &&
1544       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&
1545       !gtk_widget_accelerators_locked (menu_shell->active_menu_item) &&
1546       (delete ||
1547        (gtk_accelerator_valid (event->keyval, event->state) &&
1548         (event->state ||
1549          (event->keyval >= GDK_F1 && event->keyval <= GDK_F35)))))
1550     {
1551       GtkMenuItem *menu_item;
1552       GtkAccelGroup *accel_group;
1553       
1554       menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1555       
1556       if (!GTK_MENU (widget)->accel_group)
1557         accel_group = gtk_accel_group_get_default ();
1558       else
1559         accel_group = GTK_MENU (widget)->accel_group;
1560       
1561       gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
1562                                       gtk_signal_name (menu_item->accelerator_signal),
1563                                       TRUE);
1564       
1565       if (!delete &&
1566           0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
1567                                               accel_group,
1568                                               event->keyval,
1569                                               event->state))
1570         {
1571           GSList *slist;
1572           
1573           slist = gtk_accel_group_entries_from_object (G_OBJECT (menu_item));
1574           while (slist)
1575             {
1576               GtkAccelEntry *ac_entry;
1577               
1578               ac_entry = slist->data;
1579               
1580               if (ac_entry->signal_id == menu_item->accelerator_signal)
1581                 break;
1582               
1583               slist = slist->next;
1584             }
1585           
1586           if (!slist)
1587             gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
1588                                         gtk_signal_name (menu_item->accelerator_signal),
1589                                         accel_group,
1590                                         event->keyval,
1591                                         event->state,
1592                                         GTK_ACCEL_VISIBLE);
1593         }
1594     }
1595   
1596   return TRUE;
1597 }
1598
1599 static gboolean
1600 gtk_menu_motion_notify  (GtkWidget         *widget,
1601                          GdkEventMotion    *event)
1602 {
1603   GtkWidget *menu_item;
1604   GtkMenu *menu;
1605   GtkMenuShell *menu_shell;
1606
1607   gboolean need_enter;
1608
1609
1610   if (GTK_IS_MENU (widget))
1611     gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
1612   
1613   /* We received the event for one of two reasons:
1614    *
1615    * a) We are the active menu, and did gtk_grab_add()
1616    * b) The widget is a child of ours, and the event was propagated
1617    *
1618    * Since for computation of navigation regions, we want the menu which
1619    * is the parent of the menu item, for a), we need to find that menu,
1620    * which may be different from 'widget'.
1621    */
1622   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1623   if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
1624       !GTK_IS_MENU (menu_item->parent))
1625     return FALSE;
1626
1627   menu_shell = GTK_MENU_SHELL (menu_item->parent);
1628   menu = GTK_MENU (menu_shell);
1629   
1630   need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
1631
1632   /* Check to see if we are within an active submenu's navigation region
1633    */
1634   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1635     return TRUE; 
1636
1637   if (need_enter)
1638     {
1639       /* The menu is now sensitive to enter events on its items, but
1640        * was previously sensitive.  So we fake an enter event.
1641        */
1642       gint width, height;
1643       
1644       menu_shell->ignore_enter = FALSE; 
1645       
1646       gdk_window_get_size (event->window, &width, &height);
1647       if (event->x >= 0 && event->x < width &&
1648           event->y >= 0 && event->y < height)
1649         {
1650           GdkEvent send_event;
1651
1652           memset (&send_event, 0, sizeof (send_event));
1653           send_event.crossing.type = GDK_ENTER_NOTIFY;
1654           send_event.crossing.window = event->window;
1655           send_event.crossing.time = event->time;
1656           send_event.crossing.send_event = TRUE;
1657           send_event.crossing.x_root = event->x_root;
1658           send_event.crossing.y_root = event->y_root;
1659           send_event.crossing.x = event->x;
1660           send_event.crossing.y = event->y;
1661
1662           /* We send the event to 'widget', the currently active menu,
1663            * instead of 'menu', the menu that the pointer is in. This
1664            * will ensure that the event will be ignored unless the
1665            * menuitem is a child of the active menu or some parent
1666            * menu of the active menu.
1667            */
1668           return gtk_widget_event (widget, &send_event);
1669         }
1670     }
1671
1672   return FALSE;
1673 }
1674
1675 static gboolean
1676 gtk_menu_scroll_timeout (gpointer  data)
1677 {
1678   GtkMenu *menu;
1679   GtkWidget *widget;
1680   gint offset;
1681   gint view_width, view_height;
1682
1683   GDK_THREADS_ENTER ();
1684
1685   menu = GTK_MENU (data);
1686   widget = GTK_WIDGET (menu);
1687
1688   offset = menu->scroll_offset + menu->scroll_step;
1689
1690   /* If we scroll upward and the non-visible top part
1691    * is smaller than the scroll arrow it would be
1692    * pretty stupid to show the arrow and taking more
1693    * screen space than just scrolling to the top.
1694    */
1695   if ((menu->scroll_step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
1696     offset = 0;
1697
1698   /* Don't scroll over the top if we weren't before: */
1699   if ((menu->scroll_offset >= 0) && (offset < 0))
1700     offset = 0;
1701
1702   gdk_window_get_size (widget->window, &view_width, &view_height);
1703
1704   /* Don't scroll past the bottom if we weren't before: */
1705   if (menu->scroll_offset > 0)
1706     view_height -= MENU_SCROLL_ARROW_HEIGHT;
1707   
1708   if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
1709       (offset + view_height > widget->requisition.height))
1710     offset = widget->requisition.height - view_height;
1711
1712   gtk_menu_scroll_to (menu, offset);
1713
1714   GDK_THREADS_LEAVE ();
1715
1716   return TRUE;
1717 }
1718
1719 static void
1720 gtk_menu_handle_scrolling (GtkMenu *menu, gboolean enter)
1721 {
1722   GtkMenuShell *menu_shell;
1723   gint width, height;
1724   gint x, y;
1725   gint border;
1726   GdkRectangle rect;
1727   gboolean in_arrow;
1728   gboolean scroll_fast = FALSE;
1729
1730   menu_shell = GTK_MENU_SHELL (menu);
1731
1732   gdk_window_get_pointer (GTK_WIDGET (menu)->window, &x, &y, NULL);
1733   gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
1734
1735   border = GTK_CONTAINER (menu)->border_width + GTK_WIDGET (menu)->style->ythickness;
1736
1737   if (menu->upper_arrow_visible && !menu->tearoff_active)
1738     {
1739       rect.x = 0;
1740       rect.y = 0;
1741       rect.width = width;
1742       rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
1743       
1744       in_arrow = FALSE;
1745       if ((x >= rect.x) && (x < rect.x + rect.width) &&
1746           (y >= rect.y) && (y < rect.y + rect.height))
1747         {
1748           in_arrow = TRUE;
1749           scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
1750         }
1751         
1752       if (enter && in_arrow &&
1753           (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast))
1754         {
1755           menu->upper_arrow_prelight = TRUE;
1756           menu->scroll_fast = scroll_fast;
1757           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
1758           
1759           /* Deselect the active item so that any submenus are poped down */
1760           gtk_menu_shell_deselect (menu_shell);
1761
1762           gtk_menu_stop_scrolling (menu);
1763           menu->scroll_step = -MENU_SCROLL_STEP;
1764           menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
1765                                             gtk_menu_scroll_timeout,
1766                                             menu);
1767         }
1768       else if (!enter && !in_arrow && menu->upper_arrow_prelight)
1769         {
1770           menu->upper_arrow_prelight = FALSE;
1771           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
1772           
1773           gtk_menu_stop_scrolling (menu);
1774         }
1775     }
1776   
1777   if (menu->lower_arrow_visible && !menu->tearoff_active)
1778     {
1779       rect.x = 0;
1780       rect.y = height - border - MENU_SCROLL_ARROW_HEIGHT;
1781       rect.width = width;
1782       rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
1783
1784       in_arrow = FALSE;
1785       if ((x >= rect.x) && (x < rect.x + rect.width) &&
1786           (y >= rect.y) && (y < rect.y + rect.height))
1787         {
1788           in_arrow = TRUE;
1789           scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
1790         }
1791
1792       if (enter && in_arrow &&
1793           (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast))
1794         {
1795           menu->lower_arrow_prelight = TRUE;
1796           menu->scroll_fast = scroll_fast;
1797           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
1798
1799           /* Deselect the active item so that any submenus are poped down */
1800           gtk_menu_shell_deselect (menu_shell);
1801
1802           gtk_menu_stop_scrolling (menu);
1803           menu->scroll_step = MENU_SCROLL_STEP;
1804           menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
1805                                             gtk_menu_scroll_timeout,
1806                                             menu);
1807         }
1808       else if (!enter && !in_arrow && menu->lower_arrow_prelight)
1809         {
1810           menu->lower_arrow_prelight = FALSE;
1811           gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
1812           
1813           gtk_menu_stop_scrolling (menu);
1814         }
1815     }
1816 }
1817
1818 static gboolean
1819 gtk_menu_enter_notify (GtkWidget        *widget,
1820                        GdkEventCrossing *event)
1821 {
1822   GtkWidget *menu_item;
1823
1824   if (widget && GTK_IS_MENU (widget))
1825     gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
1826       
1827   /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
1828    * will not correspond to the event widget's parent.  Check to see
1829    * if we are in the parent's navigation region.
1830    */
1831   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1832   if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
1833       gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
1834     return TRUE;
1835
1836   return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event); 
1837 }
1838
1839 static gboolean
1840 gtk_menu_leave_notify (GtkWidget        *widget,
1841                        GdkEventCrossing *event)
1842 {
1843   GtkMenuShell *menu_shell;
1844   GtkMenu *menu;
1845   GtkMenuItem *menu_item;
1846   GtkWidget *event_widget;
1847
1848   menu = GTK_MENU (widget);
1849   menu_shell = GTK_MENU_SHELL (widget); 
1850   
1851   if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1852     return TRUE; 
1853
1854   gtk_menu_handle_scrolling (menu, FALSE);
1855   
1856   event_widget = gtk_get_event_widget ((GdkEvent*) event);
1857   
1858   if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
1859     return TRUE;
1860   
1861   menu_item = GTK_MENU_ITEM (event_widget); 
1862   
1863   /* Here we check to see if we're leaving an active menu item with a submenu, 
1864    * in which case we enter submenu navigation mode. 
1865    */
1866   if (menu_shell->active_menu_item != NULL
1867       && menu_item->submenu != NULL
1868       && menu_item->submenu_placement == GTK_LEFT_RIGHT)
1869     {
1870       if (menu_item->submenu->window != NULL) 
1871         {
1872           gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
1873           return TRUE;
1874         }
1875     }
1876   
1877   return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event); 
1878 }
1879
1880 static void 
1881 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
1882 {
1883   if (menu->navigation_region) 
1884     {
1885       gdk_region_destroy (menu->navigation_region);
1886       menu->navigation_region = NULL;
1887     }
1888   
1889   if (menu->navigation_timeout)
1890     {
1891       gtk_timeout_remove (menu->navigation_timeout);
1892       menu->navigation_timeout = 0;
1893     }
1894 }
1895
1896 /* When the timeout is elapsed, the navigation region is destroyed
1897  * and the menuitem under the pointer (if any) is selected.
1898  */
1899 static gboolean
1900 gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
1901 {
1902   GtkMenu *menu = user_data;
1903   GdkWindow *child_window;
1904
1905   GDK_THREADS_ENTER ();
1906
1907   gtk_menu_stop_navigating_submenu (menu);
1908   
1909   if (GTK_WIDGET_REALIZED (menu))
1910     {
1911       child_window = gdk_window_get_pointer (GTK_WIDGET (menu)->window, NULL, NULL, NULL);
1912
1913       if (child_window)
1914         {
1915           GdkEventCrossing send_event;
1916
1917           memset (&send_event, 0, sizeof (send_event));
1918           send_event.window = child_window;
1919           send_event.type = GDK_ENTER_NOTIFY;
1920           send_event.time = GDK_CURRENT_TIME; /* Bogus */
1921           send_event.send_event = TRUE;
1922
1923           GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), &send_event); 
1924         }
1925     }
1926
1927   GDK_THREADS_LEAVE ();
1928
1929   return FALSE; 
1930 }
1931
1932 static gboolean
1933 gtk_menu_navigating_submenu (GtkMenu *menu,
1934                              gint     event_x,
1935                              gint     event_y)
1936 {
1937   if (menu->navigation_region)
1938     {
1939       if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
1940         return TRUE;
1941       else
1942         {
1943           gtk_menu_stop_navigating_submenu (menu);
1944           return FALSE;
1945         }
1946     }
1947   return FALSE;
1948 }
1949
1950 static void
1951 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
1952                                         GtkMenuItem      *menu_item,
1953                                         GdkEventCrossing *event)
1954 {
1955   gint submenu_left = 0;
1956   gint submenu_right = 0;
1957   gint submenu_top = 0;
1958   gint submenu_bottom = 0;
1959   gint width = 0;
1960   gint height = 0;
1961   GdkPoint point[3];
1962   GtkWidget *event_widget;
1963
1964   g_return_if_fail (menu_item->submenu != NULL);
1965   g_return_if_fail (event != NULL);
1966   
1967   event_widget = gtk_get_event_widget ((GdkEvent*) event);
1968   
1969   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
1970   gdk_window_get_size (menu_item->submenu->window, &width, &height);
1971   submenu_right = submenu_left + width;
1972   submenu_bottom = submenu_top + height;
1973   
1974   gdk_window_get_size (event_widget->window, &width, &height);
1975   
1976   if (event->x >= 0 && event->x < width)
1977     {
1978       /* Set navigation region */
1979       /* We fudge/give a little padding in case the user
1980        * ``misses the vertex'' of the triangle/is off by a pixel or two.
1981        */ 
1982       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
1983         point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING; 
1984       else                             
1985         point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING;  
1986       
1987       /* Exiting the top or bottom? */ 
1988       if (event->y < 0)
1989         { /* top */
1990           point[0].y = event->y_root + SUBMENU_NAV_REGION_PADDING;
1991           point[1].y = submenu_top;
1992
1993           if (point[0].y <= point[1].y)
1994             return;
1995         }
1996       else
1997         { /* bottom */
1998           point[0].y = event->y_root - SUBMENU_NAV_REGION_PADDING; 
1999           point[1].y = submenu_bottom;
2000
2001           if (point[0].y >= point[1].y)
2002             return;
2003         }
2004       
2005       /* Submenu is to the left or right? */ 
2006       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
2007         point[1].x = submenu_left;  /* right */
2008       else
2009         point[1].x = submenu_right; /* left */
2010       
2011       point[2].x = point[1].x;
2012       point[2].y = point[0].y;
2013
2014       gtk_menu_stop_navigating_submenu (menu);
2015       
2016       menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
2017
2018       menu->navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT, 
2019                                                   gtk_menu_stop_navigating_submenu_cb, menu);
2020     }
2021 }
2022
2023 static void
2024 gtk_menu_deactivate (GtkMenuShell *menu_shell)
2025 {
2026   GtkWidget *parent;
2027   
2028   g_return_if_fail (GTK_IS_MENU (menu_shell));
2029   
2030   parent = menu_shell->parent_menu_shell;
2031   
2032   menu_shell->activate_time = 0;
2033   gtk_menu_popdown (GTK_MENU (menu_shell));
2034   
2035   if (parent)
2036     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
2037 }
2038
2039 static void
2040 gtk_menu_position (GtkMenu *menu)
2041 {
2042   GtkWidget *widget;
2043   GtkRequisition requisition;
2044   gint x, y;
2045   gint screen_width;
2046   gint screen_height;
2047   gint scroll_offset;
2048   gint menu_height;
2049   gboolean push_in;
2050   
2051   g_return_if_fail (GTK_IS_MENU (menu));
2052
2053   widget = GTK_WIDGET (menu);
2054
2055   screen_width = gdk_screen_width ();
2056   screen_height = gdk_screen_height ();
2057
2058   gdk_window_get_pointer (NULL, &x, &y, NULL);
2059
2060   /* We need the requisition to figure out the right place to
2061    * popup the menu. In fact, we always need to ask here, since
2062    * if a size_request was queued while we weren't popped up,
2063    * the requisition won't have been recomputed yet.
2064    */
2065   gtk_widget_size_request (widget, &requisition);
2066
2067   push_in = FALSE;
2068   
2069   if (menu->position_func)
2070     (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data);
2071   else
2072     {
2073       x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
2074       y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
2075     }
2076
2077   scroll_offset = 0;
2078
2079   if (push_in)
2080     {
2081       menu_height = GTK_WIDGET (menu)->requisition.height;
2082
2083       if (y + menu_height > screen_height)
2084         {
2085           scroll_offset -= y + menu_height - screen_height;
2086           y = screen_height - menu_height;
2087         }
2088   
2089       if (y < 0)
2090         {
2091           scroll_offset -= y;
2092           y = 0;
2093         }
2094     }
2095
2096   if (y + requisition.height > screen_height)
2097     requisition.height = screen_height - y;
2098   
2099   if (y < 0)
2100     {
2101       scroll_offset -= y;
2102       requisition.height -= -y;
2103       y = 0;
2104     }
2105
2106   if (scroll_offset > 0)
2107     scroll_offset += MENU_SCROLL_ARROW_HEIGHT;
2108   
2109   /* FIXME: The MAX() here is because gtk_widget_set_uposition
2110    * is broken. Once we provide an alternate interface that
2111    * allows negative values, then we can remove them.
2112    */
2113   x = MAX (x, 0);
2114   gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
2115                             menu->toplevel : menu->tearoff_window, 
2116                             x, y);
2117   gtk_widget_set_usize (GTK_MENU_SHELL (menu)->active ?
2118                         menu->toplevel : menu->tearoff_hbox,
2119                         -1, requisition.height);
2120   menu->scroll_offset = scroll_offset;
2121 }
2122
2123 static void
2124 gtk_menu_stop_scrolling (GtkMenu *menu)
2125 {
2126   if (menu->timeout_id)
2127     {
2128       g_source_remove (menu->timeout_id);
2129       menu->timeout_id = 0;
2130     }
2131 }
2132
2133
2134 static void
2135 gtk_menu_scroll_to (GtkMenu *menu,
2136                     gint    offset)
2137 {
2138   GtkWidget *widget;
2139   gint x, y;
2140   gint view_width, view_height;
2141   gint border_width;
2142   gboolean last_visible;
2143   gint menu_height;
2144
2145   widget = GTK_WIDGET (menu);
2146
2147   if (menu->tearoff_active &&
2148       menu->tearoff_adjustment &&
2149       (menu->tearoff_adjustment->value != offset))
2150     {
2151       menu->tearoff_adjustment->value = offset;
2152       gtk_adjustment_value_changed (menu->tearoff_adjustment);
2153     }
2154   
2155   /* Scroll the menu: */
2156   gdk_window_move (menu->bin_window, 0, -offset);
2157
2158   /* Move/resize the viewport according to arrows: */
2159   gdk_window_get_size (widget->window, &view_width, &view_height);
2160
2161   border_width = GTK_CONTAINER (menu)->border_width;
2162   view_width -= (border_width + widget->style->xthickness) * 2;
2163   view_height -= (border_width + widget->style->ythickness) * 2;
2164   menu_height = widget->requisition.height - (border_width + widget->style->ythickness) * 2;
2165
2166   x = border_width + widget->style->xthickness;
2167   y = border_width + widget->style->ythickness;
2168   
2169   if (!menu->tearoff_active)
2170     {
2171       last_visible = menu->upper_arrow_visible;
2172       menu->upper_arrow_visible = (offset > 0);
2173
2174       if (menu->upper_arrow_visible)
2175         view_height -= MENU_SCROLL_ARROW_HEIGHT;
2176       
2177       if ( (last_visible != menu->upper_arrow_visible) &&
2178            !menu->upper_arrow_visible)
2179         {
2180           menu->upper_arrow_prelight = FALSE;
2181           
2182           /* If we hid the upper arrow, possibly remove timeout */
2183           if (menu->scroll_step < 0)
2184             gtk_menu_stop_scrolling (menu);
2185         }
2186       
2187       last_visible = menu->lower_arrow_visible;
2188       menu->lower_arrow_visible = (view_height + offset < menu_height);
2189       
2190       if (menu->lower_arrow_visible)
2191         view_height -= MENU_SCROLL_ARROW_HEIGHT;
2192       
2193       if ( (last_visible != menu->lower_arrow_visible) &&
2194            !menu->lower_arrow_visible)
2195         {
2196           menu->lower_arrow_prelight = FALSE;
2197           
2198           /* If we hid the lower arrow, possibly remove timeout */
2199           if (menu->scroll_step > 0)
2200             gtk_menu_stop_scrolling (menu);
2201         }
2202       
2203       if (menu->upper_arrow_visible)
2204         y += MENU_SCROLL_ARROW_HEIGHT;
2205     }
2206   
2207   
2208   gdk_window_move_resize (menu->view_window,
2209                           x,
2210                           y,
2211                           view_width,
2212                           view_height);
2213
2214   menu->scroll_offset = offset;
2215 }
2216
2217 static void
2218 gtk_menu_select_item (GtkMenuShell  *menu_shell,
2219                       GtkWidget     *menu_item)
2220 {
2221   GtkMenu *menu;
2222   GtkWidget *child;
2223   GList *children;
2224   GtkRequisition child_requisition;
2225   gint child_offset, child_height;
2226   gint width, height;
2227   gint y;
2228   gint arrow_height;
2229   gboolean last_child = 0;
2230   
2231   g_return_if_fail (GTK_IS_MENU (menu_shell));
2232
2233   menu = GTK_MENU (menu_shell);
2234
2235   /* We need to check if the selected item fully visible.
2236    * If not we need to scroll the menu so that it becomes fully
2237    * visible.
2238    */
2239
2240   child = NULL;
2241   child_offset = 0;
2242   child_height = 0;
2243   children = menu_shell->children;
2244   while (children)
2245     {
2246       child = children->data;
2247       children = children->next;
2248       
2249       if (GTK_WIDGET_VISIBLE (child))
2250         {
2251           gtk_widget_size_request (child, &child_requisition);
2252           child_offset += child_height;
2253           child_height = child_requisition.height;
2254         }
2255       
2256       if (child == menu_item)
2257         {
2258           last_child = (children == NULL);
2259           break;
2260         }
2261     }
2262
2263   if (child == menu_item)
2264     {
2265       y = menu->scroll_offset;
2266       gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
2267
2268       height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness;
2269       
2270       if (child_offset + child_height <= y)
2271         {
2272           /* Ignore the enter event we might get if the pointer is on the menu
2273            */
2274           menu_shell->ignore_enter = TRUE;
2275           gtk_menu_scroll_to (menu, child_offset);
2276         }
2277       else
2278         {
2279           arrow_height = 0;
2280           if (menu->upper_arrow_visible && !menu->tearoff_active)
2281             arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2282           if (menu->lower_arrow_visible && !menu->tearoff_active)
2283             arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2284           
2285           if (child_offset >= y + height - arrow_height)
2286             {
2287               arrow_height = 0;
2288               if (!last_child && !menu->tearoff_active)
2289                 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2290               
2291               y = child_offset + child_height - height + arrow_height;
2292               if ((y > 0) && !menu->tearoff_active)
2293                 {
2294                   /* Need upper arrow */
2295                   arrow_height += MENU_SCROLL_ARROW_HEIGHT;
2296                   y = child_offset + child_height - height + arrow_height;
2297                 }
2298               /* Ignore the enter event we might get if the pointer is on the menu
2299                */
2300               menu_shell->ignore_enter = TRUE;
2301               gtk_menu_scroll_to (menu, y);
2302             }
2303         }    
2304       
2305     }
2306
2307   GTK_MENU_SHELL_CLASS (parent_class)->select_item (menu_shell, menu_item);
2308 }
2309
2310
2311 /* Reparent the menu, taking care of the refcounting
2312  *
2313  * If unrealize is true we force a unrealize while reparenting the parent.
2314  * This can help eliminate flicker in some cases.
2315  *
2316  * What happens is that when the menu is unrealized and then re-realized,
2317  * the allocations are as follows:
2318  *
2319  *  parent - 1x1 at (0,0) 
2320  *  child1 - 100x20 at (0,0)
2321  *  child2 - 100x20 at (0,20)
2322  *  child3 - 100x20 at (0,40)
2323  *
2324  * That is, the parent is small but the children are full sized. Then,
2325  * when the queued_resize gets processed, the parent gets resized to
2326  * full size. 
2327  *
2328  * But in order to eliminate flicker when scrolling, gdkgeometry-x11.c
2329  * contains the following logic:
2330  * 
2331  * - if a move or resize operation on a window would change the clip 
2332  *   region on the children, then before the window is resized
2333  *   the background for children is temporarily set to None, the
2334  *   move/resize done, and the background for the children restored.
2335  *
2336  * So, at the point where the parent is resized to final size, the
2337  * background for the children is temporarily None, and thus they
2338  * are not cleared to the background color and the previous background
2339  * (the image of the menu) is left in place.
2340  */
2341 static void 
2342 gtk_menu_reparent (GtkMenu      *menu, 
2343                    GtkWidget    *new_parent, 
2344                    gboolean      unrealize)
2345 {
2346   GtkObject *object = GTK_OBJECT (menu);
2347   GtkWidget *widget = GTK_WIDGET (menu);
2348   gboolean was_floating = GTK_OBJECT_FLOATING (object);
2349
2350   gtk_object_ref (object);
2351   gtk_object_sink (object);
2352
2353   if (unrealize)
2354     {
2355       gtk_object_ref (object);
2356       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
2357       gtk_container_add (GTK_CONTAINER (new_parent), widget);
2358       gtk_object_unref (object);
2359     }
2360   else
2361     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
2362   
2363   if (was_floating)
2364     GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
2365   else
2366     gtk_object_unref (object);
2367 }
2368
2369 static void
2370 gtk_menu_show_all (GtkWidget *widget)
2371 {
2372   g_return_if_fail (GTK_IS_MENU (widget));
2373
2374   /* Show children, but not self. */
2375   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
2376 }
2377
2378
2379 static void
2380 gtk_menu_hide_all (GtkWidget *widget)
2381 {
2382   g_return_if_fail (GTK_IS_MENU (widget));
2383
2384   /* Hide children, but not self. */
2385   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
2386 }
2387
2388