]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmenu.c
Change NAVIGATION_REGION_OVERSHOOT back to 50, and create stay-up regions
[~andy/gtk] / gtk / gtkmenu.c
index 47db2b26bf9cc00b7b97eee3ea83011206da38ae..e8ced9faba19f85a8f9db74ab1e57029fdbd3083 100644 (file)
@@ -24,6 +24,8 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
+#define GTK_MENU_INTERNALS
+
 #include <string.h> /* memset */
 #include "gdk/gdkkeysyms.h"
 #include "gtkaccelmap.h"
@@ -32,7 +34,6 @@
 #include "gtkmain.h"
 #include "gtkmenu.h"
 #include "gtkmenuitem.h"
-#include "gtksignal.h"
 #include "gtkwindow.h"
 #include "gtkhbox.h"
 #include "gtkvscrollbar.h"
 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_GET_CLASS (w)
 #define        MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
 
-#define SUBMENU_NAV_REGION_PADDING 2
-#define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
+#define DEFAULT_POPUP_DELAY    225
+#define DEFAULT_POPDOWN_DELAY  1000
+
+#define NAVIGATION_REGION_OVERSHOOT 50  /* How much the navigation region
+                                        * extends below the submenu
+                                        */
 
 #define MENU_SCROLL_STEP 10
 #define MENU_SCROLL_ARROW_HEIGHT 16
@@ -53,6 +58,7 @@
 #define MENU_SCROLL_TIMEOUT2 50
 
 typedef struct _GtkMenuAttachData      GtkMenuAttachData;
+typedef struct _GtkMenuPrivate         GtkMenuPrivate;
 
 struct _GtkMenuAttachData
 {
@@ -60,6 +66,13 @@ struct _GtkMenuAttachData
   GtkMenuDetachFunc detacher;
 };
 
+struct _GtkMenuPrivate 
+{
+  gboolean have_position;
+  gint x;
+  gint y;
+};
+
 enum {
   PROP_0,
   PROP_TEAROFF_TITLE
@@ -113,6 +126,12 @@ static void     gtk_menu_handle_scrolling  (GtkMenu          *menu,
                                            gboolean         enter);
 static void     gtk_menu_set_tearoff_hints (GtkMenu          *menu,
                                            gint             width);
+static void     gtk_menu_style_set         (GtkWidget        *widget,
+                                           GtkStyle         *previous_style);
+static gboolean gtk_menu_focus             (GtkWidget        *widget,
+                                           GtkDirectionType direction);
+static gint     gtk_menu_get_popup_delay   (GtkMenuShell    *menu_shell);
+
 
 static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
 static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
@@ -144,26 +163,51 @@ static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
 static GtkMenuShellClass *parent_class = NULL;
 static const gchar      *attach_data_key = "gtk-menu-attach-data";
 
-GtkType
+GtkMenuPrivate *
+gtk_menu_get_private (GtkMenu *menu)
+{
+  GtkMenuPrivate *private;
+  static GQuark private_quark = 0;
+
+  if (!private_quark)
+    private_quark = g_quark_from_static_string ("gtk-menu-private");
+
+  private = g_object_get_qdata (G_OBJECT (menu), private_quark);
+
+  if (!private)
+    {
+      private = g_new0 (GtkMenuPrivate, 1);
+      private->have_position = FALSE;
+      
+      g_object_set_qdata_full (G_OBJECT (menu), private_quark,
+                              private, g_free);
+    }
+
+  return private;
+}
+
+GType
 gtk_menu_get_type (void)
 {
-  static GtkType menu_type = 0;
+  static GType menu_type = 0;
   
   if (!menu_type)
     {
-      static const GtkTypeInfo menu_info =
+      static const GTypeInfo menu_info =
       {
-       "GtkMenu",
-       sizeof (GtkMenu),
        sizeof (GtkMenuClass),
-       (GtkClassInitFunc) gtk_menu_class_init,
-       (GtkObjectInitFunc) gtk_menu_init,
-       /* reserved_1 */ NULL,
-       /* reserved_2 */ NULL,
-        (GtkClassInitFunc) NULL,
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_menu_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkMenu),
+       0,              /* n_preallocs */
+       (GInstanceInitFunc) gtk_menu_init,
       };
       
-      menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
+      menu_type = g_type_register_static (GTK_TYPE_MENU_SHELL, "GtkMenu",
+                                         &menu_info, 0);
     }
   
   return menu_type;
@@ -189,9 +233,10 @@ gtk_menu_class_init (GtkMenuClass *class)
                                    PROP_TEAROFF_TITLE,
                                    g_param_spec_string ("tearoff-title",
                                                         _("Tearoff Title"),
-                                                        _("A title that may be displayed by the window manager when this menu is torn-off."),
+                                                        _("A title that may be displayed by the window manager when this menu is torn-off"),
                                                         "",
                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
+
   object_class->destroy = gtk_menu_destroy;
   
   widget_class->realize = gtk_menu_realize;
@@ -206,6 +251,8 @@ gtk_menu_class_init (GtkMenuClass *class)
   widget_class->hide_all = gtk_menu_hide_all;
   widget_class->enter_notify_event = gtk_menu_enter_notify;
   widget_class->leave_notify_event = gtk_menu_leave_notify;
+  widget_class->style_set = gtk_menu_style_set;
+  widget_class->focus = gtk_menu_focus;
 
   container_class->remove = gtk_menu_remove;
   
@@ -213,6 +260,7 @@ gtk_menu_class_init (GtkMenuClass *class)
   menu_shell_class->deactivate = gtk_menu_deactivate;
   menu_shell_class->select_item = gtk_menu_select_item;
   menu_shell_class->insert = gtk_menu_real_insert;
+  menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
 
   binding_set = gtk_binding_set_by_class (class);
   gtk_binding_entry_add_signal (binding_set,
@@ -258,9 +306,26 @@ gtk_menu_class_init (GtkMenuClass *class)
 
   gtk_settings_install_property (g_param_spec_boolean ("gtk-can-change-accels",
                                                       _("Can change accelerators"),
-                                                      _("Whether menu accelerators can be changed by pressing a key over the menu item."),
+                                                      _("Whether menu accelerators can be changed by pressing a key over the menu item"),
                                                       FALSE,
                                                       G_PARAM_READWRITE));
+
+  gtk_settings_install_property (g_param_spec_int ("gtk-menu-popup-delay",
+                                                  _("Delay before submenus appear"),
+                                                  _("Minimum time the pointer must stay over a menu item before the submenu appear"),
+                                                  0,
+                                                  G_MAXINT,
+                                                  DEFAULT_POPUP_DELAY,
+                                                  G_PARAM_READWRITE));
+
+  gtk_settings_install_property (g_param_spec_int ("gtk-menu-popdown-delay",
+                                                  _("Delay before hiding a submenu"),
+                                                  _("The time before hiding a submenu when the pointer is moving towards the submenu"),
+                                                  0,
+                                                  G_MAXINT,
+                                                  DEFAULT_POPDOWN_DELAY,
+                                                  G_PARAM_READWRITE));
+                                                  
 }
 
 
@@ -313,8 +378,8 @@ gtk_menu_window_event (GtkWidget *window,
 {
   gboolean handled = FALSE;
 
-  gtk_widget_ref (window);
-  gtk_widget_ref (menu);
+  g_object_ref (window);
+  g_object_ref (menu);
 
   switch (event->type)
     {
@@ -326,12 +391,28 @@ gtk_menu_window_event (GtkWidget *window,
       break;
     }
 
-  gtk_widget_unref (window);
-  gtk_widget_unref (menu);
+  g_object_unref (window);
+  g_object_unref (menu);
 
   return handled;
 }
 
+static void
+gtk_menu_window_size_request (GtkWidget      *window,
+                             GtkRequisition *requisition,
+                             GtkMenu        *menu)
+{
+  GtkMenuPrivate *private = gtk_menu_get_private (menu);
+
+  if (private->have_position)
+    {
+      gint screen_height = gdk_screen_height ();
+
+      if (private->y + requisition->height > screen_height)
+       requisition->height = screen_height - private->y;
+    }
+}
+
 static void
 gtk_menu_init (GtkMenu *menu)
 {
@@ -342,15 +423,15 @@ gtk_menu_init (GtkMenu *menu)
   menu->position_func_data = NULL;
   menu->toggle_size = 0;
 
-  menu->toplevel = g_object_connect (gtk_widget_new (GTK_TYPE_WINDOW,
-                                                    "type", GTK_WINDOW_POPUP,
-                                                    "child", menu,
-                                                    NULL),
+  menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
+                                                  "type", GTK_WINDOW_POPUP,
+                                                  "child", menu,
+                                                  NULL),
                                     "signal::event", gtk_menu_window_event, menu,
+                                    "signal::size_request", gtk_menu_window_size_request, menu,
                                     "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
                                     NULL);
-  gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
-                        FALSE, FALSE, TRUE);
+  gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE);
   gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
 
   /* Refloat the menu, so that reference counting for the menu isn't
@@ -394,7 +475,7 @@ gtk_menu_destroy (GtkObject *object)
 
   gtk_menu_stop_scrolling (menu);
   
-  data = gtk_object_get_data (object, attach_data_key);
+  data = g_object_get_data (G_OBJECT (object), attach_data_key);
   if (data)
     gtk_menu_detach (menu);
   
@@ -402,7 +483,7 @@ gtk_menu_destroy (GtkObject *object)
 
   if (menu->old_active_menu_item)
     {
-      gtk_widget_unref (menu->old_active_menu_item);
+      g_object_unref (menu->old_active_menu_item);
       menu->old_active_menu_item = NULL;
     }
 
@@ -410,7 +491,7 @@ gtk_menu_destroy (GtkObject *object)
   if (menu->needs_destruction_ref_count)
     {
       menu->needs_destruction_ref_count = FALSE;
-      gtk_object_ref (object);
+      g_object_ref (object);
     }
   
   if (menu->accel_group)
@@ -451,21 +532,21 @@ gtk_menu_attach_to_widget (GtkMenu               *menu,
   /* keep this function in sync with gtk_widget_set_parent()
    */
   
-  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
+  data = g_object_get_data (G_OBJECT (menu), attach_data_key);
   if (data)
     {
       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
-                gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
+                g_type_name (G_TYPE_FROM_INSTANCE (data->attach_widget)));
       return;
     }
   
-  gtk_object_ref (GTK_OBJECT (menu));
+  g_object_ref (menu);
   gtk_object_sink (GTK_OBJECT (menu));
   
   data = g_new (GtkMenuAttachData, 1);
   data->attach_widget = attach_widget;
   data->detacher = detacher;
-  gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
+  g_object_set_data (G_OBJECT (menu), attach_data_key, data);
   
   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
@@ -485,7 +566,7 @@ gtk_menu_get_attach_widget (GtkMenu *menu)
   
   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
   
-  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
+  data = g_object_get_data (G_OBJECT (menu), attach_data_key);
   if (data)
     return data->attach_widget;
   return NULL;
@@ -500,13 +581,13 @@ gtk_menu_detach (GtkMenu *menu)
   
   /* keep this function in sync with gtk_widget_unparent()
    */
-  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
+  data = g_object_get_data (G_OBJECT (menu), attach_data_key);
   if (!data)
     {
       g_warning ("gtk_menu_detach(): menu is not attached");
       return;
     }
-  gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
+  g_object_set_data (G_OBJECT (menu), attach_data_key, NULL);
   
   data->detacher (data->attach_widget, menu);
   
@@ -518,7 +599,7 @@ gtk_menu_detach (GtkMenu *menu)
   /* Fallback title for menu comes from attach widget */
   gtk_menu_update_title (menu);
 
-  gtk_widget_unref (GTK_WIDGET (menu));
+  g_object_unref (menu);
 }
 
 static void 
@@ -535,7 +616,7 @@ gtk_menu_remove (GtkContainer *container,
    */
   if (menu->old_active_menu_item == widget)
     {
-      gtk_widget_unref (menu->old_active_menu_item);
+      g_object_unref (menu->old_active_menu_item);
       menu->old_active_menu_item = NULL;
     }
 
@@ -546,7 +627,7 @@ gtk_menu_remove (GtkContainer *container,
 GtkWidget*
 gtk_menu_new (void)
 {
-  return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
+  return g_object_new (GTK_TYPE_MENU, NULL);
 }
 
 static void
@@ -556,7 +637,7 @@ gtk_menu_real_insert (GtkMenuShell     *menu_shell,
 {
   if (GTK_WIDGET_REALIZED (menu_shell))
     gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
-  
+
   GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
 }
 
@@ -581,24 +662,24 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
       gc = gdk_gc_new_with_values (widget->window,
                                   &gc_values, GDK_GC_SUBWINDOW);
       
-      gdk_window_get_size (menu->tearoff_window->window, &width, &height);
+      gdk_drawable_get_size (menu->tearoff_window->window, &width, &height);
       
       pixmap = gdk_pixmap_new (menu->tearoff_window->window,
                               width,
                               height,
                               -1);
 
-      gdk_draw_pixmap (pixmap, gc,
-                      menu->tearoff_window->window,
-                      0, 0, 0, 0, -1, -1);
-      gdk_gc_unref (gc);
+      gdk_draw_drawable (pixmap, gc,
+                        menu->tearoff_window->window,
+                        0, 0, 0, 0, -1, -1);
+      g_object_unref (gc);
 
-      gtk_widget_set_usize (menu->tearoff_window,
-                           width,
-                           height);
+      gtk_widget_set_size_request (menu->tearoff_window,
+                                  width,
+                                  height);
 
       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
-      gdk_pixmap_unref (pixmap);
+      g_object_unref (pixmap);
     }
 }
 
@@ -617,7 +698,8 @@ popup_grab_on_window (GdkWindow *window,
        return TRUE;
       else
        {
-         gdk_pointer_ungrab (activate_time);
+         gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
+                                     activate_time);
          return FALSE;
        }
     }
@@ -625,6 +707,32 @@ popup_grab_on_window (GdkWindow *window,
   return FALSE;
 }
 
+/**
+ * gtk_menu_popup:
+ * @menu: a #GtkMenu.
+ * @parent_menu_shell: the menu shell containing the triggering menu item, or %NULL
+ * @parent_menu_item: the menu item whose activation triggered the popup, or %NULL
+ * @func: a user supplied function used to position the menu, or %NULL
+ * @data: user supplied data to be passed to @func.
+ * @button: the mouse button which was pressed to initiate the event.
+ * @activate_time: the time at which the activation event occurred.
+ *
+ * Displays a menu and makes it available for selection.  Applications can use
+ * this function to display context-sensitive menus, and will typically supply
+ * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data 
+ * parameters. The default menu positioning function will position the menu
+ * at the current mouse cursor position.
+ *
+ * The @button parameter should be the mouse button pressed to initiate
+ * the menu popup. If the menu popup was initiated by something other than
+ * a mouse button press, such as a mouse button release or a keypress,
+ * @button should be 0.
+ *
+ * The @activate_time parameter should be the time stamp of the event that
+ * initiated the popup. If such an event is not available, use
+ * gtk_get_current_event_time() instead.
+ *
+ */
 void
 gtk_menu_popup (GtkMenu                    *menu,
                GtkWidget           *parent_menu_shell,
@@ -639,6 +747,7 @@ gtk_menu_popup (GtkMenu                 *menu,
   GtkWidget *parent;
   GdkEvent *current_event;
   GtkMenuShell *menu_shell;
+  GtkMenuAttachData *attach_data;
 
   g_return_if_fail (GTK_IS_MENU (menu));
   
@@ -646,6 +755,21 @@ gtk_menu_popup (GtkMenu                *menu,
   menu_shell = GTK_MENU_SHELL (menu);
   
   menu_shell->parent_menu_shell = parent_menu_shell;
+  
+  if (!g_object_get_data (G_OBJECT (menu), "gtk-menu-explicit-screen"))
+    {
+      /* The screen was not set explicitly, if the menu is
+       * attached to a widget, try to get screen from its 
+       * toplevel window else go with the default
+       */
+      attach_data = g_object_get_data (G_OBJECT (menu), attach_data_key);
+      if (attach_data)
+       {
+         if (!GTK_WIDGET_REALIZED (menu))
+         gtk_window_set_screen (GTK_WINDOW (menu->toplevel),
+                                gtk_widget_get_screen (attach_data->attach_widget));
+       }
+    }
 
   /* Find the last viewable ancestor, and make an X grab on it
    */
@@ -709,6 +833,7 @@ gtk_menu_popup (GtkMenu                 *menu,
        * try again.
        */
       menu_shell->parent_menu_shell = NULL;
+      menu_grab_transfer_window_destroy (menu);
       return;
     }
 
@@ -748,8 +873,12 @@ gtk_menu_popup (GtkMenu                *menu,
    */
   gtk_widget_show (GTK_WIDGET (menu));
 
+  /* Position the menu, possibly changing the size request
+   */
+  gtk_menu_position (menu);
+
   /* Compute the size of the toplevel and realize it so we
-   * can position and scroll correctly.
+   * can scroll correctly.
    */
   {
     GtkRequisition tmp_request;
@@ -765,8 +894,6 @@ gtk_menu_popup (GtkMenu                 *menu,
     gtk_widget_realize (GTK_WIDGET (menu));
   }
 
-  gtk_menu_position (menu);
-
   gtk_menu_scroll_to (menu, menu->scroll_offset);
 
   /* Once everything is set up correctly, map the toplevel window on
@@ -783,16 +910,20 @@ gtk_menu_popup (GtkMenu               *menu,
 void
 gtk_menu_popdown (GtkMenu *menu)
 {
+  GtkMenuPrivate *private;
   GtkMenuShell *menu_shell;
 
   g_return_if_fail (GTK_IS_MENU (menu));
   
   menu_shell = GTK_MENU_SHELL (menu);
+  private = gtk_menu_get_private (menu);
   
   menu_shell->parent_menu_shell = NULL;
   menu_shell->active = FALSE;
   menu_shell->ignore_enter = FALSE;
 
+  private->have_position = FALSE;
+
   gtk_menu_stop_scrolling (menu);
   
   gtk_menu_stop_navigating_submenu (menu);
@@ -800,9 +931,9 @@ gtk_menu_popdown (GtkMenu *menu)
   if (menu_shell->active_menu_item)
     {
       if (menu->old_active_menu_item)
-       gtk_widget_unref (menu->old_active_menu_item);
+       g_object_unref (menu->old_active_menu_item);
       menu->old_active_menu_item = menu_shell->active_menu_item;
-      gtk_widget_ref (menu->old_active_menu_item);
+      g_object_ref (menu->old_active_menu_item);
     }
 
   gtk_menu_shell_deselect (menu_shell);
@@ -813,11 +944,7 @@ gtk_menu_popdown (GtkMenu *menu)
 
   if (menu->torn_off)
     {
-      gint width, height;
-      gdk_window_get_size (menu->tearoff_window->window, &width, &height);
-      gtk_widget_set_usize (menu->tearoff_window,
-                           -1,
-                           height);
+      gtk_widget_set_size_request (menu->tearoff_window, -1, -1);
       
       if (GTK_BIN (menu->toplevel)->child) 
        {
@@ -830,8 +957,10 @@ gtk_menu_popdown (GtkMenu *menu)
           */
          if (menu_shell->have_xgrab)
            {
-             gdk_pointer_ungrab (GDK_CURRENT_TIME);
-             gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+             GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu));
+             
+             gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
+             gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
            }
        }
 
@@ -877,7 +1006,7 @@ gtk_menu_get_active (GtkMenu *menu)
       
       menu->old_active_menu_item = child;
       if (menu->old_active_menu_item)
-       gtk_widget_ref (menu->old_active_menu_item);
+       g_object_ref (menu->old_active_menu_item);
     }
   
   return menu->old_active_menu_item;
@@ -899,9 +1028,9 @@ gtk_menu_set_active (GtkMenu *menu,
       if (GTK_BIN (child)->child)
        {
          if (menu->old_active_menu_item)
-           gtk_widget_unref (menu->old_active_menu_item);
+           g_object_unref (menu->old_active_menu_item);
          menu->old_active_menu_item = child;
-         gtk_widget_ref (menu->old_active_menu_item);
+         g_object_ref (menu->old_active_menu_item);
        }
     }
 }
@@ -915,10 +1044,10 @@ gtk_menu_set_accel_group (GtkMenu        *menu,
   if (menu->accel_group != accel_group)
     {
       if (menu->accel_group)
-       gtk_accel_group_unref (menu->accel_group);
+       g_object_unref (menu->accel_group);
       menu->accel_group = accel_group;
       if (menu->accel_group)
-       gtk_accel_group_ref (menu->accel_group);
+       g_object_ref (menu->accel_group);
       _gtk_menu_refresh_accel_paths (menu, TRUE);
     }
 }
@@ -1041,6 +1170,7 @@ gtk_menu_set_tearoff_hints (GtkMenu *menu,
     
   geometry_hints.min_height = 0;
   geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height;
+
   gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window),
                                 NULL,
                                 &geometry_hints,
@@ -1092,35 +1222,28 @@ gtk_menu_set_tearoff_state (GtkMenu  *menu,
 
          if (!menu->tearoff_window)
            {
-             menu->tearoff_window = g_object_connect (gtk_widget_new (GTK_TYPE_WINDOW,
-                                                                      "type", GTK_WINDOW_TOPLEVEL,
-                                                                      NULL),
-                                                      "signal::destroy", gtk_widget_destroyed, &menu->tearoff_window,
-                                                      NULL);
+             menu->tearoff_window = gtk_widget_new (GTK_TYPE_WINDOW,
+                                                    "type", GTK_WINDOW_TOPLEVEL,
+                                                    "screen", gtk_widget_get_screen (menu->toplevel),
+                                                    "app_paintable", TRUE,
+                                                    NULL);
+
              gtk_window_set_type_hint (GTK_WINDOW (menu->tearoff_window),
                                        GDK_WINDOW_TYPE_HINT_MENU);
              gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->tearoff_window), 0);
-             gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
-             gtk_signal_connect (GTK_OBJECT (menu->tearoff_window),  
-                                 "event",
-                                 GTK_SIGNAL_FUNC (gtk_menu_window_event), 
-                                 GTK_OBJECT (menu));
+             g_signal_connect (menu->tearoff_window, "destroy",
+                               G_CALLBACK (gtk_widget_destroyed), &menu->tearoff_window);
+             g_signal_connect (menu->tearoff_window, "event",
+                               G_CALLBACK (gtk_menu_window_event), menu);
 
              gtk_menu_update_title (menu);
 
              gtk_widget_realize (menu->tearoff_window);
              
-             gdk_window_set_decorations (menu->tearoff_window->window, 
-                                         GDK_DECOR_ALL |
-                                         GDK_DECOR_RESIZEH |
-                                         GDK_DECOR_MINIMIZE |
-                                         GDK_DECOR_MAXIMIZE);
-             gtk_window_set_resizable (GTK_WINDOW (menu->tearoff_window), FALSE);
-
              menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE);
              gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox);
 
-             gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+             gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
              menu->tearoff_adjustment =
                GTK_ADJUSTMENT (gtk_adjustment_new (0,
                                                    0,
@@ -1128,7 +1251,7 @@ gtk_menu_set_tearoff_state (GtkMenu  *menu,
                                                    MENU_SCROLL_STEP,
                                                    height/2,
                                                    height));
-             g_object_connect (GTK_OBJECT (menu->tearoff_adjustment),
+             g_object_connect (menu->tearoff_adjustment,
                                "signal::value_changed", gtk_menu_scrollbar_changed, menu,
                                NULL);
              menu->tearoff_scrollbar = gtk_vscrollbar_new (menu->tearoff_adjustment);
@@ -1145,7 +1268,7 @@ gtk_menu_set_tearoff_state (GtkMenu  *menu,
          
          gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE);
 
-         gdk_window_get_size (GTK_WIDGET (menu)->window, &width, NULL);
+         gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, NULL);
 
          /* Update menu->requisition
           */
@@ -1218,14 +1341,15 @@ gtk_menu_set_title (GtkMenu     *menu,
  * Returns the title of the menu. See gtk_menu_set_title().
  *
  * Return value: the title of the menu, or %NULL if the menu has no
- *               title set on it.
+ * title set on it. This string is owned by the widget and should
+ * not be modified or freed.
  **/
 G_CONST_RETURN gchar *
 gtk_menu_get_title (GtkMenu *menu)
 {
   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
 
-  return gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
+  return g_object_get_data (G_OBJECT (menu), "gtk-menu-title");
 }
 
 void
@@ -1246,6 +1370,20 @@ gtk_menu_reorder_child (GtkMenu   *menu,
     }   
 }
 
+static void
+gtk_menu_style_set (GtkWidget *widget,
+                   GtkStyle  *previous_style)
+{
+  if (GTK_WIDGET_REALIZED (widget))
+    {
+      GtkMenu *menu = GTK_MENU (widget);
+      
+      gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL);
+      gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL);
+      gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+    }
+}
+
 static void
 gtk_menu_realize (GtkWidget *widget)
 {
@@ -1326,6 +1464,16 @@ gtk_menu_realize (GtkWidget *widget)
   gdk_window_show (menu->view_window);
 }
 
+static gboolean 
+gtk_menu_focus (GtkWidget       *widget,
+                GtkDirectionType direction)
+{
+  /*
+   * A menu or its menu items cannot have focus
+   */
+  return FALSE;
+}
+
 /* See notes in gtk_menu_popup() for information about the "grab transfer window"
  */
 static GdkWindow *
@@ -1348,7 +1496,8 @@ menu_grab_transfer_window_get (GtkMenu *menu)
 
       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
       
-      window = gdk_window_new (NULL, &attributes, attributes_mask);
+      window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
+                              &attributes, attributes_mask);
       gdk_window_set_user_data (window, menu);
 
       gdk_window_show (window);
@@ -1453,11 +1602,9 @@ gtk_menu_size_request (GtkWidget      *widget,
   
   menu->toggle_size = max_toggle_size;
 
-  /* If the requested width was different than the allocated width, we need to change
-   * the geometry hints for the tear off window so that the window can actually be resized.
-   * Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
+  /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
    */
-  if ((requisition->width != GTK_WIDGET (menu)->allocation.width) && menu->tearoff_active)
+  if (menu->tearoff_active)
     gtk_menu_set_tearoff_hints (menu, requisition->width);
 }
 
@@ -1486,6 +1633,9 @@ gtk_menu_size_allocate (GtkWidget     *widget,
   
   width = MAX (1, allocation->width - x * 2);
   height = MAX (1, allocation->height - y * 2);
+
+  if (menu_shell->active)
+    gtk_menu_scroll_to (menu, menu->scroll_offset);
   
   if (menu->upper_arrow_visible && !menu->tearoff_active)
     {
@@ -1554,7 +1704,6 @@ gtk_menu_size_allocate (GtkWidget     *widget,
                {
                  gtk_widget_hide (menu->tearoff_scrollbar);
                  gtk_menu_set_tearoff_hints (menu, allocation->width);
-                 gtk_widget_set_usize (menu->tearoff_window, -1, allocation->height);
 
                  gtk_menu_scroll_to (menu, 0);
                }
@@ -1580,7 +1729,6 @@ gtk_menu_size_allocate (GtkWidget     *widget,
                {
                  gtk_widget_show (menu->tearoff_scrollbar);
                  gtk_menu_set_tearoff_hints (menu, allocation->width);
-                 gtk_widget_set_usize (menu->tearoff_window, -1, allocation->height);
                }
            }
        }
@@ -1601,7 +1749,7 @@ gtk_menu_paint (GtkWidget      *widget,
   
   border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
   border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness;
-  gdk_window_get_size (widget->window, &width, &height);
+  gdk_drawable_get_size (widget->window, &width, &height);
 
   if (event->window == widget->window)
     {
@@ -1665,35 +1813,6 @@ gtk_menu_paint (GtkWidget      *widget,
                           MENU_SCROLL_ARROW_HEIGHT - 2 * border_y - 2);
        }
     }
-  else if (event->window == menu->view_window)
-    {
-      gint menu_height;
-      gint top_pos;
-      
-      if (menu->scroll_offset < 0)
-       gtk_paint_box (widget->style,
-                      menu->view_window,
-                      GTK_STATE_ACTIVE,
-                      GTK_SHADOW_IN,
-                      NULL, widget, "menu",
-                      0, 0,
-                      -1,
-                      -menu->scroll_offset);
-
-      menu_height = widget->requisition.height - 2*border_y;
-      top_pos = height - 2*border_y - (menu->upper_arrow_visible ? MENU_SCROLL_ARROW_HEIGHT : 0);
-
-      if (menu_height - menu->scroll_offset < top_pos)
-       gtk_paint_box (widget->style,
-                      menu->view_window,
-                      GTK_STATE_ACTIVE,
-                      GTK_SHADOW_IN,
-                      NULL, widget, "menu",
-                      0,
-                      menu_height - menu->scroll_offset,
-                      -1,
-                      top_pos - (menu_height - menu->scroll_offset));
-    }
 }
 
 static gboolean
@@ -1734,6 +1853,7 @@ gtk_menu_key_press (GtkWidget     *widget,
   gchar *accel = NULL;
   guint accel_key, accel_mods;
   GdkModifierType consumed_modifiers;
+  GdkDisplay *display;
   
   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
@@ -1745,10 +1865,12 @@ gtk_menu_key_press (GtkWidget   *widget,
 
   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
     return TRUE;
+
+  display = gtk_widget_get_display (widget);
     
-  g_object_get (G_OBJECT (gtk_settings_get_default ()),
-                "gtk-menu-bar-accel",
-                &accel,
+  g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
+                "gtk-menu-bar-accel", &accel,
+               "gtk-can-change-accels", &can_change_accels,
                 NULL);
 
   if (accel)
@@ -1769,7 +1891,7 @@ gtk_menu_key_press (GtkWidget     *widget,
       if (event->keyval == keyval &&
           (mods & event->state) == mods)
         {
-          gtk_signal_emit_by_name (GTK_OBJECT (menu), "cancel");
+          g_signal_emit_by_name (menu, "cancel", 0);
         }
 
       g_free (accel);
@@ -1789,12 +1911,8 @@ gtk_menu_key_press (GtkWidget    *widget,
       break;
     }
 
-  g_object_get (G_OBJECT (gtk_settings_get_default ()),
-               "gtk-can-change-accels", &can_change_accels,
-               NULL);
-
   /* Figure out what modifiers went into determining the key symbol */
-  gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (),
+  gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (display),
                                       event->hardware_keycode, event->state, event->group,
                                       NULL, NULL, NULL, &consumed_modifiers);
 
@@ -1812,20 +1930,20 @@ gtk_menu_key_press (GtkWidget   *widget,
       menu_shell->active_menu_item &&
       GTK_BIN (menu_shell->active_menu_item)->child &&                 /* no seperators */
       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL && /* no submenus */
-      (delete || gtk_accelerator_valid (event->keyval, event->state)))
+      (delete || gtk_accelerator_valid (accel_key, accel_mods)))
     {
       GtkWidget *menu_item = menu_shell->active_menu_item;
-      gboolean replace_accels = TRUE;
+      gboolean locked, replace_accels = TRUE;
       const gchar *path;
 
-      path = _gtk_widget_get_accel_path (menu_item);
-      if (!path)
+      path = _gtk_widget_get_accel_path (menu_item, &locked);
+      if (!path || locked)
        {
          /* can't change accelerators on menu_items without paths
           * (basically, those items are accelerator-locked).
           */
-         /* g_print("item has no path, menu prefix: %s\n", menu->accel_path); */
-         gdk_beep ();
+         /* g_print("item has no path or is locked, menu prefix: %s\n", menu->accel_path); */
+         gdk_display_beep (display);
        }
       else
        {
@@ -1854,7 +1972,7 @@ gtk_menu_key_press (GtkWidget     *widget,
               * locked already
               */
              /* g_print("failed to change\n"); */
-             gdk_beep ();
+             gdk_display_beep (display);
            }
        }
     }
@@ -1874,7 +1992,7 @@ gtk_menu_motion_notify  (GtkWidget           *widget,
 
   if (GTK_IS_MENU (widget))
     gtk_menu_handle_scrolling (GTK_MENU (widget), TRUE);
-  
+
   /* We received the event for one of two reasons:
    *
    * a) We are the active menu, and did gtk_grab_add()
@@ -1909,21 +2027,20 @@ gtk_menu_motion_notify  (GtkWidget         *widget,
       
       menu_shell->ignore_enter = FALSE; 
       
-      gdk_window_get_size (event->window, &width, &height);
+      gdk_drawable_get_size (event->window, &width, &height);
       if (event->x >= 0 && event->x < width &&
          event->y >= 0 && event->y < height)
        {
-         GdkEvent send_event;
-
-         memset (&send_event, 0, sizeof (send_event));
-         send_event.crossing.type = GDK_ENTER_NOTIFY;
-         send_event.crossing.window = event->window;
-         send_event.crossing.time = event->time;
-         send_event.crossing.send_event = TRUE;
-         send_event.crossing.x_root = event->x_root;
-         send_event.crossing.y_root = event->y_root;
-         send_event.crossing.x = event->x;
-         send_event.crossing.y = event->y;
+         GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
+         gboolean result;
+
+         send_event->crossing.window = g_object_ref (event->window);
+         send_event->crossing.time = event->time;
+         send_event->crossing.send_event = TRUE;
+         send_event->crossing.x_root = event->x_root;
+         send_event->crossing.y_root = event->y_root;
+         send_event->crossing.x = event->x;
+         send_event->crossing.y = event->y;
 
          /* We send the event to 'widget', the currently active menu,
           * instead of 'menu', the menu that the pointer is in. This
@@ -1931,7 +2048,10 @@ gtk_menu_motion_notify  (GtkWidget          *widget,
           * menuitem is a child of the active menu or some parent
           * menu of the active menu.
           */
-         return gtk_widget_event (widget, &send_event);
+         result = gtk_widget_event (widget, send_event);
+         gdk_event_free (send_event);
+
+         return result;
        }
     }
 
@@ -1965,7 +2085,7 @@ gtk_menu_scroll_timeout (gpointer  data)
   if ((menu->scroll_offset >= 0) && (offset < 0))
     offset = 0;
 
-  gdk_window_get_size (widget->window, &view_width, &view_height);
+  gdk_drawable_get_size (widget->window, &view_width, &view_height);
 
   /* Don't scroll past the bottom if we weren't before: */
   if (menu->scroll_offset > 0)
@@ -1996,7 +2116,7 @@ gtk_menu_handle_scrolling (GtkMenu *menu, gboolean enter)
   menu_shell = GTK_MENU_SHELL (menu);
 
   gdk_window_get_pointer (GTK_WIDGET (menu)->window, &x, &y, NULL);
-  gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+  gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
 
   border = GTK_CONTAINER (menu)->border_width + GTK_WIDGET (menu)->style->ythickness;
 
@@ -2178,15 +2298,15 @@ gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
 
       if (child_window)
        {
-         GdkEventCrossing send_event;
+         GdkEvent *send_event = gdk_event_new (GDK_ENTER_NOTIFY);
+
+         send_event->crossing.window = g_object_ref (child_window);
+         send_event->crossing.time = GDK_CURRENT_TIME; /* Bogus */
+         send_event->crossing.send_event = TRUE;
 
-         memset (&send_event, 0, sizeof (send_event));
-         send_event.window = child_window;
-         send_event.type = GDK_ENTER_NOTIFY;
-         send_event.time = GDK_CURRENT_TIME; /* Bogus */
-         send_event.send_event = TRUE;
+         GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), (GdkEventCrossing *)send_event);
 
-         GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), &send_event); 
+         gdk_event_free (send_event);
        }
     }
 
@@ -2213,6 +2333,71 @@ gtk_menu_navigating_submenu (GtkMenu *menu,
   return FALSE;
 }
 
+#undef DRAW_STAY_UP_TRIANGLE
+
+#ifdef DRAW_STAY_UP_TRIANGLE
+
+static void
+draw_stay_up_triangle (GdkWindow *window,
+                      GdkRegion *region)
+{
+  /* Draw ugly color all over the stay-up triangle */
+  GdkColor ugly_color = { 0, 50000, 10000, 10000 };
+  GdkGCValues gc_values;
+  GdkGC *ugly_gc;
+  GdkRectangle clipbox;
+
+  gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+  ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW);
+  gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
+  gdk_gc_set_clip_region (ugly_gc, region);
+
+  gdk_region_get_clipbox (region, &clipbox);
+  
+  gdk_draw_rectangle (window,
+                     ugly_gc,
+                     TRUE,
+                     clipbox.x, clipbox.y,
+                     clipbox.width, clipbox.height);
+  
+  g_object_unref (G_OBJECT (ugly_gc));
+}
+#endif
+
+static GdkRegion *
+flip_region (GdkRegion *region,
+            gboolean   flip_x,
+            gboolean   flip_y)
+{
+  gint n_rectangles;
+  GdkRectangle *rectangles;
+  GdkRectangle clipbox;
+  GdkRegion *new_region;
+  gint i;
+
+  new_region = gdk_region_new ();
+  
+  gdk_region_get_rectangles (region, &rectangles, &n_rectangles);
+  gdk_region_get_clipbox (region, &clipbox);
+
+  for (i = 0; i < n_rectangles; ++i)
+    {
+      GdkRectangle rect = rectangles[i];
+
+      if (flip_y)
+       rect.y -= 2 * (rect.y - clipbox.y) + rect.height;
+
+      if (flip_x)
+       rect.x -= 2 * (rect.x - clipbox.x) + rect.width;
+
+      gdk_region_union_with_rect (new_region, &rect);
+    }
+
+  g_free (rectangles);
+
+  return new_region;
+}
+
 static void
 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
                                        GtkMenuItem      *menu_item,
@@ -2233,56 +2418,80 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
   event_widget = gtk_get_event_widget ((GdkEvent*) event);
   
   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
-  gdk_window_get_size (menu_item->submenu->window, &width, &height);
+  gdk_drawable_get_size (menu_item->submenu->window, &width, &height);
+  
   submenu_right = submenu_left + width;
   submenu_bottom = submenu_top + height;
   
-  gdk_window_get_size (event_widget->window, &width, &height);
+  gdk_drawable_get_size (event_widget->window, &width, &height);
   
   if (event->x >= 0 && event->x < width)
     {
-      /* Set navigation region */
-      /* We fudge/give a little padding in case the user
-       * ``misses the vertex'' of the triangle/is off by a pixel or two.
-       */ 
-      if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
-       point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING; 
-      else                             
-       point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING;  
+      gint popdown_delay;
+      gboolean flip_y = FALSE;
+      gboolean flip_x = FALSE;
       
-      /* Exiting the top or bottom? */ 
+      gtk_menu_stop_navigating_submenu (menu);
+
+      if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
+       {
+         /* right */
+         point[0].x = event->x_root;
+         point[1].x = submenu_left;
+       }
+      else
+       {
+         /* left */
+         point[0].x = event->x_root + 1;
+         point[1].x = 2 * (event->x_root + 1) - submenu_right;
+
+         flip_x = TRUE;
+       }
+
       if (event->y < 0)
-        { /* top */
+       {
+         /* top */
          point[0].y = event->y_root + 1;
-         point[1].y = submenu_top;
+         point[1].y = 2 * (event->y_root + 1) - submenu_top + NAVIGATION_REGION_OVERSHOOT;
 
-         if (point[0].y <= point[1].y)
+         if (point[0].y >= point[1].y - NAVIGATION_REGION_OVERSHOOT)
            return;
+
+         flip_y = TRUE;
        }
       else
-        { /* bottom */
+       {
+         /* bottom */
          point[0].y = event->y_root;
-         point[1].y = submenu_bottom;
+         point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT;
 
-         if (point[0].y >= point[1].y)
+         if (point[0].y >= submenu_bottom)
            return;
        }
-      
-      /* Submenu is to the left or right? */ 
-      if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
-       point[1].x = submenu_left;  /* right */
-      else
-       point[1].x = submenu_right; /* left */
-      
+
       point[2].x = point[1].x;
       point[2].y = point[0].y;
 
-      gtk_menu_stop_navigating_submenu (menu);
-      
       menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
 
-      menu->navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT, 
+      if (flip_x || flip_y)
+       {
+         GdkRegion *new_region = flip_region (menu->navigation_region, flip_x, flip_y);
+         gdk_region_destroy (menu->navigation_region);
+         menu->navigation_region = new_region;
+       }
+
+      g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (menu))),
+                   "gtk-menu-popdown-delay", &popdown_delay,
+                   NULL);
+
+      menu->navigation_timeout = gtk_timeout_add (popdown_delay,
                                                  gtk_menu_stop_navigating_submenu_cb, menu);
+
+#ifdef DRAW_STAY_UP_TRIANGLE
+      draw_stay_up_triangle (gdk_get_default_root_window(),
+                            menu->navigation_region);
+#endif
     }
 }
 
@@ -2307,21 +2516,27 @@ gtk_menu_position (GtkMenu *menu)
 {
   GtkWidget *widget;
   GtkRequisition requisition;
+  GtkMenuPrivate *private;
   gint x, y;
-  gint screen_width;
-  gint screen_height;
   gint scroll_offset;
   gint menu_height;
   gboolean push_in;
-  
+  GdkScreen *screen;
+  GdkRectangle monitor;
+  gint monitor_num;
+
   g_return_if_fail (GTK_IS_MENU (menu));
 
   widget = GTK_WIDGET (menu);
 
-  screen_width = gdk_screen_width ();
-  screen_height = gdk_screen_height ();
+  gdk_window_get_pointer (gtk_widget_get_root_window (widget),
+                         &x, &y, NULL);
 
-  gdk_window_get_pointer (NULL, &x, &y, NULL);
+  screen = gtk_widget_get_screen (widget);
+  monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+  if (monitor_num < 0)
+    monitor_num = 0;
+  gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
 
   /* We need the requisition to figure out the right place to
    * popup the menu. In fact, we always need to ask here, since
@@ -2336,8 +2551,8 @@ gtk_menu_position (GtkMenu *menu)
     (* menu->position_func) (menu, &x, &y, &push_in, menu->position_func_data);
   else
     {
-      x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
-      y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
+      x = CLAMP (x - 2, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
+      y = CLAMP (y - 2, monitor.y, MAX (monitor.y, monitor.y + monitor.height - requisition.height));      
     }
 
   scroll_offset = 0;
@@ -2346,27 +2561,30 @@ gtk_menu_position (GtkMenu *menu)
     {
       menu_height = GTK_WIDGET (menu)->requisition.height;
 
-      if (y + menu_height > screen_height)
+      if (y + menu_height > monitor.y + monitor.height)
        {
-         scroll_offset -= y + menu_height - screen_height;
-         y = screen_height - menu_height;
+         scroll_offset -= y + menu_height - (monitor.y + monitor.height);
+         y = (monitor.y + monitor.height) - menu_height;
        }
   
-      if (y < 0)
+      if (y < monitor.y)
        {
          scroll_offset -= y;
-         y = 0;
+         y = monitor.y;
        }
     }
 
-  if (y + requisition.height > screen_height)
-    requisition.height = screen_height - y;
+  /* FIXME: should this be done in the various position_funcs ? */
+  x = CLAMP (x, monitor.x, MAX (monitor.x, monitor.x + monitor.width - requisition.width));
+  if (y + requisition.height > monitor.y + monitor.height)
+    requisition.height = (monitor.y + monitor.height) - y;
   
-  if (y < 0)
+  if (y < monitor.y)
     {
       scroll_offset -= y;
       requisition.height -= -y;
-      y = 0;
+      y = monitor.y;
     }
 
   if (scroll_offset > 0)
@@ -2374,9 +2592,22 @@ gtk_menu_position (GtkMenu *menu)
   
   gtk_window_move (GTK_WINDOW (GTK_MENU_SHELL (menu)->active ? menu->toplevel : menu->tearoff_window), 
                   x, y);
-  gtk_widget_set_usize (GTK_MENU_SHELL (menu)->active ?
-                       menu->toplevel : menu->tearoff_hbox,
-                       -1, requisition.height);
+
+  if (GTK_MENU_SHELL (menu)->active)
+    {
+      private = gtk_menu_get_private (menu);
+      private->have_position = TRUE;
+      private->x = x;
+      private->y = y;
+
+      gtk_widget_queue_resize (menu->toplevel);
+    }
+  else
+    {
+      gtk_window_resize (GTK_WINDOW (menu->tearoff_window),
+                        requisition.width, requisition.height);
+    }
+  
   menu->scroll_offset = scroll_offset;
 }
 
@@ -2412,11 +2643,9 @@ gtk_menu_scroll_to (GtkMenu *menu,
       gtk_adjustment_value_changed (menu->tearoff_adjustment);
     }
   
-  /* Scroll the menu: */
-  gdk_window_move (menu->bin_window, 0, -offset);
-
   /* Move/resize the viewport according to arrows: */
-  gdk_window_get_size (widget->window, &view_width, &view_height);
+  view_width = widget->allocation.width;
+  view_height = widget->allocation.height;
 
   border_width = GTK_CONTAINER (menu)->border_width;
   view_width -= (border_width + widget->style->xthickness) * 2;
@@ -2430,7 +2659,7 @@ gtk_menu_scroll_to (GtkMenu *menu,
     {
       last_visible = menu->upper_arrow_visible;
       menu->upper_arrow_visible = (offset > 0);
-
+      
       if (menu->upper_arrow_visible)
        view_height -= MENU_SCROLL_ARROW_HEIGHT;
       
@@ -2463,12 +2692,19 @@ gtk_menu_scroll_to (GtkMenu *menu,
       if (menu->upper_arrow_visible)
        y += MENU_SCROLL_ARROW_HEIGHT;
     }
-  
-  gdk_window_move_resize (menu->view_window,
-                         x,
-                         y,
-                         view_width,
-                         view_height);
+
+  offset = CLAMP (offset, 0, menu_height - view_height);
+
+  /* Scroll the menu: */
+  if (GTK_WIDGET_REALIZED (menu))
+    gdk_window_move (menu->bin_window, 0, -offset);
+
+  if (GTK_WIDGET_REALIZED (menu))
+    gdk_window_move_resize (menu->view_window,
+                           x,
+                           y,
+                           view_width,
+                           view_height);
 
   menu->scroll_offset = offset;
 }
@@ -2520,7 +2756,7 @@ gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
   if (child == menu_item)
     {
       y = menu->scroll_offset;
-      gdk_window_get_size (GTK_WIDGET (menu)->window, &width, &height);
+      gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
 
       height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness;
       
@@ -2614,15 +2850,15 @@ gtk_menu_reparent (GtkMenu      *menu,
   GtkWidget *widget = GTK_WIDGET (menu);
   gboolean was_floating = GTK_OBJECT_FLOATING (object);
 
-  gtk_object_ref (object);
+  g_object_ref (object);
   gtk_object_sink (object);
 
   if (unrealize)
     {
-      gtk_object_ref (object);
+      g_object_ref (object);
       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
       gtk_container_add (GTK_CONTAINER (new_parent), widget);
-      gtk_object_unref (object);
+      g_object_unref (object);
     }
   else
     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
@@ -2630,7 +2866,7 @@ gtk_menu_reparent (GtkMenu      *menu,
   if (was_floating)
     GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
   else
-    gtk_object_unref (object);
+    g_object_unref (object);
 }
 
 static void
@@ -2652,4 +2888,33 @@ gtk_menu_hide_all (GtkWidget *widget)
   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
 }
 
+/**
+ * gtk_menu_set_screen:
+ * @menu: a #GtkMenu.
+ * @screen: a #GtkScreen.
+ *
+ * Sets the #GtkScreen on which the GtkMenu will be displayed.
+ * This function can only be called before @menu is realized.
+ **/
+void
+gtk_menu_set_screen (GtkMenu *menu, 
+                    GdkScreen *screen)
+{
+  g_return_if_fail (GTK_IS_MENU (menu));
+  gtk_window_set_screen (GTK_WINDOW (menu->toplevel), 
+                        screen);
+  g_object_set_data (G_OBJECT (menu), "gtk-menu-explicit-screen", screen);
+}
+
 
+static gint
+gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
+{
+  gint popup_delay;
+
+  g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (menu_shell))),
+               "gtk-menu-popup-delay", &popup_delay,
+               NULL);
+
+  return popup_delay;
+}