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