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