[gtk+/wip/attente/popup-at: 5/5] port to new gtk_menu_popup_at_* () functions



commit 161e50da5f4a0195debec900c75b35ba6a12a203
Author: William Hua <william hua canonical com>
Date:   Wed Jun 15 11:03:49 2016 -0400

    port to new gtk_menu_popup_at_* () functions

 demos/gtk-demo/clipboard.c    |    2 +-
 demos/gtk-demo/main.c         |    2 +-
 demos/gtk-demo/search_entry.c |    3 +-
 gtk/deprecated/gtkcolorsel.c  |   53 ++-----
 gtk/gtkappchooserwidget.c     |    3 +-
 gtk/gtkcombobox.c             |  285 ++++++++++-----------------------
 gtk/gtkentry.c                |   88 +++--------
 gtk/gtklabel.c                |   65 ++------
 gtk/gtklinkbutton.c           |   73 ++-------
 gtk/gtkmenu.c                 |    9 +-
 gtk/gtkmenubutton.c           |  298 +++++++++++++++-------------------
 gtk/gtkmenuitem.c             |  358 +++++++++++++++++------------------------
 gtk/gtkmenuitemprivate.h      |    1 -
 gtk/gtkmountoperation.c       |   39 +----
 gtk/gtknotebook.c             |   83 +++++------
 gtk/gtkplacesview.c           |   26 +---
 gtk/gtkrecentchooserdefault.c |   57 +------
 gtk/gtktextview.c             |  147 +++++------------
 gtk/gtktoolbar.c              |   95 ++++-------
 gtk/gtkwindow.c               |   21 +---
 20 files changed, 558 insertions(+), 1150 deletions(-)
---
diff --git a/demos/gtk-demo/clipboard.c b/demos/gtk-demo/clipboard.c
index f04a203..8bb44e9 100644
--- a/demos/gtk-demo/clipboard.c
+++ b/demos/gtk-demo/clipboard.c
@@ -193,7 +193,7 @@ button_press (GtkWidget      *widget,
   gtk_widget_show (item);
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
 
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, button->time);
+  gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) button);
   return TRUE;
 }
 
diff --git a/demos/gtk-demo/main.c b/demos/gtk-demo/main.c
index a6adc6a..923030f 100644
--- a/demos/gtk-demo/main.c
+++ b/demos/gtk-demo/main.c
@@ -966,7 +966,7 @@ end_cb (GtkMenuItem *item, GtkWidget *scrollbar)
 static gboolean
 scrollbar_popup (GtkWidget *scrollbar, GtkWidget *menu)
 {
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
+  gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
 
   return TRUE;
 }
diff --git a/demos/gtk-demo/search_entry.c b/demos/gtk-demo/search_entry.c
index 67a10b5..685e4b0 100644
--- a/demos/gtk-demo/search_entry.c
+++ b/demos/gtk-demo/search_entry.c
@@ -150,8 +150,7 @@ icon_press_cb (GtkEntry       *entry,
                gpointer        data)
 {
   if (position == GTK_ENTRY_ICON_PRIMARY)
-    gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
-                    event->button, event->time);
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
 }
 
 static void
diff --git a/gtk/deprecated/gtkcolorsel.c b/gtk/deprecated/gtkcolorsel.c
index f43edf2..27be9c1 100644
--- a/gtk/deprecated/gtkcolorsel.c
+++ b/gtk/deprecated/gtkcolorsel.c
@@ -1423,40 +1423,6 @@ palette_set_color (GtkWidget         *drawing_area,
 }
 
 static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer   user_data)
-{
-  GtkAllocation allocation;
-  GtkWidget *widget;
-  GtkRequisition req;
-  gint root_x, root_y;
-  GdkScreen *screen;
-
-  widget = GTK_WIDGET (user_data);
-
-  g_return_if_fail (gtk_widget_get_realized (widget));
-
-  gdk_window_get_origin (gtk_widget_get_window (widget),
-                         &root_x, &root_y);
-
-  gtk_widget_get_preferred_size (GTK_WIDGET (menu),
-                                 &req, NULL);
-  gtk_widget_get_allocation (widget, &allocation);
-
-  /* Put corner of menu centered on color cell */
-  *x = root_x + allocation.width / 2;
-  *y = root_y + allocation.height / 2;
-
-  /* Ensure sanity */
-  screen = gtk_widget_get_screen (widget);
-  *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
-  *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
-}
-
-static void
 save_color_selected (GtkWidget *menuitem,
                      gpointer   data)
 {
@@ -1477,7 +1443,7 @@ save_color_selected (GtkWidget *menuitem,
 static void
 do_popup (GtkColorSelection *colorsel,
           GtkWidget         *drawing_area,
-          guint32            timestamp)
+          const GdkEvent    *trigger_event)
 {
   GtkWidget *menu;
   GtkWidget *mi;
@@ -1499,9 +1465,14 @@ do_popup (GtkColorSelection *colorsel,
 
   gtk_widget_show_all (mi);
 
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-                  popup_position_func, drawing_area,
-                  3, timestamp);
+  if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
+  else
+    gtk_menu_popup_at_widget (GTK_MENU (menu),
+                              drawing_area,
+                              GDK_GRAVITY_CENTER,
+                              GDK_GRAVITY_NORTH_WEST,
+                              trigger_event);
 }
 
 
@@ -1540,7 +1511,7 @@ palette_press (GtkWidget      *drawing_area,
 
   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
     {
-      do_popup (colorsel, drawing_area, event->time);
+      do_popup (colorsel, drawing_area, (GdkEvent *) event);
       return TRUE;
     }
 
@@ -1638,9 +1609,7 @@ static gboolean
 palette_popup (GtkWidget *widget,
                gpointer   data)
 {
-  GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
-
-  do_popup (colorsel, widget, GDK_CURRENT_TIME);
+  do_popup (data, widget, NULL);
   return TRUE;
 }
 
diff --git a/gtk/gtkappchooserwidget.c b/gtk/gtkappchooserwidget.c
index 2aeb2b9..fd9e6b9 100644
--- a/gtk/gtkappchooserwidget.c
+++ b/gtk/gtkappchooserwidget.c
@@ -257,8 +257,7 @@ widget_button_press_event_cb (GtkWidget      *widget,
 
       if (n_children > 0)
         /* actually popup the menu */
-        gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
-                        event->button, event->time);
+        gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
 
       g_list_free (children);
     }
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index d0e2dd3..783ce12 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -306,21 +306,6 @@ static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
 
 static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
                                                     GtkWidget        *popup);
-static void     gtk_combo_box_menu_position_below  (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_position_over   (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
-static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
-                                                    gint             *x,
-                                                    gint             *y,
-                                                    gint             *push_in,
-                                                    gpointer          user_data);
 
 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
 
@@ -1922,187 +1907,6 @@ gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
 }
 
 static void
-gtk_combo_box_menu_position_below (GtkMenu  *menu,
-                                   gint     *x,
-                                   gint     *y,
-                                   gint     *push_in,
-                                   gpointer  user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  GtkAllocation child_allocation, border_allocation, content_allocation;
-  gint sx, sy;
-  GtkWidget *child;
-  GtkRequisition req;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle area;
-
-  /* FIXME: is using the size request here broken? */
-  child = gtk_bin_get_child (GTK_BIN (combo_box));
-
-  sx = sy = 0;
-
-  gtk_css_gadget_get_border_allocation (priv->gadget, &border_allocation, NULL);
-  gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, NULL);
-  gtk_widget_get_allocation (child, &child_allocation);
-
-  if (!gtk_widget_get_has_window (child))
-    {
-      sx += child_allocation.x;
-      sy += child_allocation.y;
-    }
-
-  gdk_window_get_root_coords (gtk_widget_get_window (child), sx, sy, &sx, &sy);
-
-  if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
-    sx += (content_allocation.x - border_allocation.x);
-  else
-    sx -= (content_allocation.x - border_allocation.x);
-
-  if (combo_box->priv->popup_fixed_width)
-    gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
-  else
-    gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
-
-  if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
-    *x = sx;
-  else
-    *x = sx + child_allocation.width - req.width;
-  *y = sy;
-
-  display = gtk_widget_get_display (GTK_WIDGET (combo_box));
-  monitor = gdk_display_get_monitor_at_window (display, gtk_widget_get_window (GTK_WIDGET (combo_box)));
-  gdk_monitor_get_workarea (monitor, &area);
-
-  if (*x < area.x)
-    *x = area.x;
-  else if (*x + req.width > area.x + area.width)
-    *x = area.x + area.width - req.width;
-
-  if (area.y + area.height - *y - child_allocation.height >= req.height)
-    *y += child_allocation.height;
-  else if (*y - area.y >= req.height)
-    *y -= req.height;
-  else if (area.y + area.height - *y - child_allocation.height > *y - area.y)
-    *y += child_allocation.height;
-  else
-    *y -= req.height;
-
-   *push_in = FALSE;
-}
-
-static void
-gtk_combo_box_menu_position_over (GtkMenu  *menu,
-                                  gint     *x,
-                                  gint     *y,
-                                  gboolean *push_in,
-                                  gpointer  user_data)
-{
-  GtkComboBox        *combo_box = GTK_COMBO_BOX (user_data);
-  GtkWidget          *widget    = GTK_WIDGET (combo_box);
-  GtkComboBoxPrivate *priv      = combo_box->priv;
-  GtkWidget          *active;
-  GtkWidget          *child;
-  GtkAllocation       content_allocation;
-  GtkAllocation       child_allocation;
-  GList              *children;
-  gint                menu_xpos;
-  gint                menu_ypos;
-  gint                menu_width;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-
-  active = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
-
-  gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, NULL);
-
-  menu_xpos = content_allocation.x;
-  menu_ypos = content_allocation.y + content_allocation.height / 2 - 2;
-
-  if (priv->popup_fixed_width)
-    gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
-  else
-    gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
-
-  if (active != NULL)
-    {
-      gtk_widget_get_allocation (active, &child_allocation);
-      menu_ypos -= child_allocation.height / 2;
-    }
-
-  children = GTK_MENU_SHELL (priv->popup_widget)->priv->children;
-  while (children)
-    {
-      child = children->data;
-
-      if (active == child)
-        break;
-
-      if (gtk_widget_get_visible (child))
-        {
-          gtk_widget_get_allocation (child, &child_allocation);
-
-          menu_ypos -= child_allocation.height;
-        }
-
-      children = children->next;
-    }
-
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    menu_xpos = menu_xpos + content_allocation.width - menu_width;
-
-  gdk_window_get_root_coords (gtk_widget_get_window (widget),
-                              menu_xpos, menu_ypos,
-                              &menu_xpos, &menu_ypos);
-
-  /* Clamp the position on screen */
-  display = gtk_widget_get_display (widget);
-  monitor = gdk_display_get_monitor_at_window (display, gtk_widget_get_window (widget));
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  if (menu_xpos < workarea.x)
-    menu_xpos = workarea.x;
-  else if ((menu_xpos + menu_width) > workarea.x + workarea.width)
-    menu_xpos -= (menu_xpos + menu_width) - (workarea.x + workarea.width);
-
-  *x = menu_xpos;
-  *y = menu_ypos;
-
-  *push_in = TRUE;
-}
-
-static void
-gtk_combo_box_menu_position (GtkMenu  *menu,
-                             gint     *x,
-                             gint     *y,
-                             gint     *push_in,
-                             gpointer  user_data)
-{
-  GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  GtkWidget *menu_item;
-
-  if (priv->wrap_width > 0 || priv->cell_view == NULL)
-    gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
-  else
-    {
-      /* FIXME handle nested menus better */
-      menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
-      if (menu_item)
-        gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
-                                    menu_item);
-
-      gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
-    }
-
-  if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->priv->toplevel))
-    gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->priv->toplevel),
-                              GDK_WINDOW_TYPE_HINT_COMBO);
-}
-
-static void
 gtk_combo_box_list_position (GtkComboBox *combo_box,
                              gint        *x,
                              gint        *y,
@@ -2309,6 +2113,13 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
   GtkTreePath *path;
   gint active_item;
   gint width, min_width, nat_width;
+  GtkAllocation border_allocation;
+  GtkAllocation content_allocation;
+  gint rect_anchor_dy = -2;
+  gint child_height;
+  GtkWidget *active;
+  GtkWidget *child;
+  GList *i;
 
   update_menu_sensitivity (combo_box, priv->popup_widget);
 
@@ -2328,8 +2139,6 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
 
   if (priv->wrap_width == 0)
     {
-      GtkAllocation content_allocation;
-
       gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, NULL);
       width = content_allocation.width;
       gtk_widget_set_size_request (priv->popup_widget, -1, -1);
@@ -2343,10 +2152,82 @@ gtk_combo_box_menu_popup (GtkComboBox *combo_box,
       gtk_widget_set_size_request (priv->popup_widget, width, -1);
     }
 
-  gtk_menu_popup (GTK_MENU (priv->popup_widget),
-                  NULL, NULL,
-                  gtk_combo_box_menu_position, combo_box,
-                  button, activate_time);
+  g_signal_handlers_disconnect_by_func (priv->popup_widget,
+                                        gtk_menu_update_scroll_offset,
+                                        NULL);
+
+  g_object_set (priv->popup_widget, "menu-type-hint", GDK_WINDOW_TYPE_HINT_COMBO, NULL);
+
+  if (priv->wrap_width > 0 || priv->cell_view == NULL)
+    {
+      gtk_css_gadget_get_border_allocation (priv->gadget, &border_allocation, NULL);
+      gtk_css_gadget_get_content_allocation (priv->gadget, &content_allocation, NULL);
+
+      g_object_set (priv->popup_widget,
+                    "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    "rect-anchor-dx", border_allocation.x - content_allocation.x,
+                    NULL);
+
+      gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget),
+                                gtk_bin_get_child (GTK_BIN (combo_box)),
+                                GDK_GRAVITY_SOUTH_WEST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                NULL);
+    }
+  else
+    {
+      /* FIXME handle nested menus better */
+      active = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
+
+      if (active)
+        gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget), active);
+      else
+        {
+          for (i = GTK_MENU_SHELL (priv->popup_widget)->priv->children; i && !active; i = i->next)
+            {
+              child = i->data;
+
+              if (child && gtk_widget_get_visible (child))
+                active = child;
+            }
+        }
+
+      if (active)
+        {
+          for (i = GTK_MENU_SHELL (priv->popup_widget)->priv->children; i && i->data != active; i = i->next)
+            {
+              child = i->data;
+
+              if (child && gtk_widget_get_visible (child))
+                {
+                  gtk_widget_get_preferred_height (child, &child_height, NULL);
+                  rect_anchor_dy -= child_height;
+                }
+            }
+
+          gtk_widget_get_preferred_height (active, &child_height, NULL);
+          rect_anchor_dy -= child_height / 2;
+        }
+
+      g_object_set (priv->popup_widget,
+                    "anchor-hints", GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    "rect-anchor-dy", rect_anchor_dy,
+                    NULL);
+
+      g_signal_connect (priv->popup_widget,
+                        "popped-up",
+                        G_CALLBACK (gtk_menu_update_scroll_offset),
+                        NULL);
+
+      gtk_menu_popup_at_widget (GTK_MENU (priv->popup_widget),
+                                GTK_WIDGET (combo_box),
+                                GDK_GRAVITY_WEST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                NULL);
+    }
 }
 
 static gboolean
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index dd792eb..15a1588 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -9430,56 +9430,10 @@ popup_menu_detach (GtkWidget *attach_widget,
   priv_attach->popup_menu = NULL;
 }
 
-static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer  user_data)
-{
-  GtkEntry *entry = GTK_ENTRY (user_data);
-  GtkEntryPrivate *priv = entry->priv;
-  GtkWidget *widget = GTK_WIDGET (entry);
-  GdkDisplay *display;
-  GtkRequisition menu_req;
-  GdkMonitor *monitor;
-  GdkRectangle area;
-  gint strong_x, height;
-
-  g_return_if_fail (gtk_widget_get_realized (widget));
-
-  gdk_window_get_origin (priv->text_area, x, y);
-
-  display = gtk_widget_get_display (widget);
-  monitor = gdk_display_get_monitor_at_window (display, priv->text_area);
-  gtk_menu_place_on_monitor (menu, monitor);
-  gdk_monitor_get_workarea (monitor, &area);
-  gtk_widget_get_preferred_size (priv->popup_menu, &menu_req, NULL);
-  height = gdk_window_get_height (priv->text_area);
-  gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &strong_x, NULL);
-
-  *x += 0 + strong_x - priv->scroll_offset;
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
-    *x -= menu_req.width;
-
-  if ((*y + height + menu_req.height) <= area.y + area.height)
-    *y += height;
-  else if ((*y - menu_req.height) >= area.y)
-    *y -= menu_req.height;
-  else if (area.y + area.height - (*y + height) > *y)
-    *y += height;
-  else
-    *y -= menu_req.height;
-
-  *push_in = FALSE;
-}
-
 typedef struct
 {
   GtkEntry *entry;
-  guint button;
-  guint time;
-  GdkDevice *device;
+  GdkEvent *trigger_event;
 } PopupInfo;
 
 static void
@@ -9490,6 +9444,7 @@ popup_targets_received (GtkClipboard     *clipboard,
   PopupInfo *info = user_data;
   GtkEntry *entry = info->entry;
   GtkEntryPrivate *info_entry_priv = entry->priv;
+  GdkRectangle rect = { 0, 0, 1, 0 };
 
   if (gtk_widget_get_realized (GTK_WIDGET (entry)))
     {
@@ -9540,19 +9495,26 @@ popup_targets_received (GtkClipboard     *clipboard,
 
       g_signal_emit (entry, signals[POPULATE_POPUP], 0, menu);
 
-      if (info->device)
-       gtk_menu_popup_for_device (GTK_MENU (menu),
-                                   info->device, NULL, NULL, NULL, NULL, NULL,
-                                   info->button, info->time);
+      if (info->trigger_event && gdk_event_triggers_context_menu (info->trigger_event))
+        gtk_menu_popup_at_pointer (GTK_MENU (menu), info->trigger_event);
       else
-       {
-          gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-                          popup_position_func, entry,
-                          0, gtk_get_current_event_time ());
+        {
+          gtk_entry_get_cursor_locations (entry, CURSOR_STANDARD, &rect.x, NULL);
+          rect.x -= info_entry_priv->scroll_offset;
+          rect.height = gdk_window_get_height (info_entry_priv->text_area);
+
+          gtk_menu_popup_at_rect (GTK_MENU (menu),
+                                  info_entry_priv->text_area,
+                                  &rect,
+                                  GDK_GRAVITY_SOUTH_EAST,
+                                  GDK_GRAVITY_NORTH_WEST,
+                                  info->trigger_event);
+
           gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
-       }
+        }
     }
 
+  g_clear_pointer (&info->trigger_event, gdk_event_free);
   g_object_unref (entry);
   g_slice_free (PopupInfo, info);
 }
@@ -9568,19 +9530,7 @@ gtk_entry_do_popup (GtkEntry       *entry,
    * we get them, then we actually pop up the menu.
    */
   info->entry = g_object_ref (entry);
-  
-  if (event)
-    {
-      gdk_event_get_button (event, &info->button);
-      info->time = gdk_event_get_time (event);
-      info->device = gdk_event_get_device (event);
-    }
-  else
-    {
-      info->button = 0;
-      info->time = gtk_get_current_event_time ();
-      info->device = NULL;
-    }
+  info->trigger_event = event ? gdk_event_copy (event) : gtk_get_current_event ();
 
   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (entry), GDK_SELECTION_CLIPBOARD),
                                  gdk_atom_intern_static_string ("TARGETS"),
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index dd2bbbf..342dadf 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -512,7 +512,7 @@ static void gtk_label_move_cursor        (GtkLabel        *label,
 static void gtk_label_copy_clipboard     (GtkLabel        *label);
 static void gtk_label_select_all         (GtkLabel        *label);
 static void gtk_label_do_popup           (GtkLabel        *label,
-                                         GdkEventButton  *event);
+                                         const GdkEvent  *event);
 static gint gtk_label_move_forward_word  (GtkLabel        *label,
                                          gint             start);
 static gint gtk_label_move_backward_word (GtkLabel        *label,
@@ -4954,7 +4954,7 @@ gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
         {
           info->link_clicked = 1;
           update_link_state (label);
-          gtk_label_do_popup (label, (GdkEventButton*) event);
+          gtk_label_do_popup (label, event);
           return;
         }
       else if (button == GDK_BUTTON_PRIMARY)
@@ -4977,7 +4977,7 @@ gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
   info->select_words = FALSE;
 
   if (gdk_event_triggers_context_menu (event))
-    gtk_label_do_popup (label, (GdkEventButton*) event);
+    gtk_label_do_popup (label, event);
   else if (button == GDK_BUTTON_PRIMARY)
     {
       if (!gtk_widget_has_focus (widget))
@@ -6545,48 +6545,6 @@ popup_menu_detach (GtkWidget *attach_widget,
 }
 
 static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer  user_data)
-{
-  GtkLabel *label;
-  GtkWidget *widget;
-  GtkAllocation allocation;
-  GtkRequisition req;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-
-  label = GTK_LABEL (user_data);
-  widget = GTK_WIDGET (label);
-
-  g_return_if_fail (gtk_widget_get_realized (widget));
-
-  display = gtk_widget_get_display (widget);
-  monitor = gdk_display_get_monitor_at_window (display,
-                                               gtk_widget_get_window (widget));
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
-  gtk_widget_get_allocation (widget, &allocation);
-
-  *x += allocation.x;
-  *y += allocation.y;
-
-  gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  *x += allocation.width / 2;
-  *y += allocation.height;
-
-  *x = CLAMP (*x, 0, MAX (0, workarea.width - req.width));
-  *y = CLAMP (*y, 0, MAX (0, workarea.height - req.height));
-}
-
-static void
 open_link_activate_cb (GtkMenuItem *menuitem,
                        GtkLabel    *label)
 {
@@ -6618,7 +6576,7 @@ gtk_label_popup_menu (GtkWidget *widget)
 
 static void
 gtk_label_do_popup (GtkLabel       *label,
-                    GdkEventButton *event)
+                    const GdkEvent *event)
 {
   GtkLabelPrivate *priv = label->priv;
   GtkWidget *menuitem;
@@ -6695,15 +6653,16 @@ gtk_label_do_popup (GtkLabel       *label,
 
   g_signal_emit (label, signals[POPULATE_POPUP], 0, menu);
 
-  if (event)
-    gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-                    NULL, NULL,
-                    event->button, event->time);
+  if (event && gdk_event_triggers_context_menu ((GdkEvent *) event))
+    gtk_menu_popup_at_pointer (GTK_MENU (menu), (GdkEvent *) event);
   else
     {
-      gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-                      popup_position_func, label,
-                      0, gtk_get_current_event_time ());
+      gtk_menu_popup_at_widget (GTK_MENU (menu),
+                                GTK_WIDGET (label),
+                                GDK_GRAVITY_SOUTH,
+                                GDK_GRAVITY_NORTH_WEST,
+                                event);
+
       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
     }
 }
diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c
index fe46867..92a783f 100644
--- a/gtk/gtklinkbutton.c
+++ b/gtk/gtklinkbutton.c
@@ -336,43 +336,6 @@ popup_menu_detach (GtkWidget *attach_widget,
 }
 
 static void
-popup_position_func (GtkMenu  *menu,
-                    gint     *x,
-                    gint     *y,
-                    gboolean *push_in,
-                    gpointer  user_data)
-{
-  GtkLinkButton *link_button = GTK_LINK_BUTTON (user_data);
-  GtkLinkButtonPrivate *priv = link_button->priv;
-  GtkAllocation allocation;
-  GtkWidget *widget = GTK_WIDGET (link_button);
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GtkRequisition req;
-  GdkRectangle area;
-
-  g_return_if_fail (gtk_widget_get_realized (widget));
-
-  gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
-
-  gtk_widget_get_preferred_size (priv->popup_menu, &req, NULL);
-
-  gtk_widget_get_allocation (widget, &allocation);
-  *x += allocation.width / 2;
-  *y += allocation.height;
-
-  display = gtk_widget_get_display (widget);
-  monitor = gdk_display_get_monitor_at_point (display, *x, *y);
-  gtk_menu_place_on_monitor (menu, monitor);
-  gdk_monitor_get_workarea (monitor, &area);
-
-  *x = CLAMP (*x, area.x, area.x + MAX (0, area.width - req.width));
-  *y = CLAMP (*y, area.y, area.y + MAX (0, area.height - req.height));
-
-  *push_in = FALSE;
-}
-
-static void
 copy_activate_cb (GtkWidget     *widget,
                  GtkLinkButton *link_button)
 {
@@ -385,22 +348,9 @@ copy_activate_cb (GtkWidget     *widget,
 
 static void
 gtk_link_button_do_popup (GtkLinkButton  *link_button,
-                         GdkEventButton *event)
+                          const GdkEvent *event)
 {
   GtkLinkButtonPrivate *priv = link_button->priv;
-  gint button;
-  guint time;
-  
-  if (event)
-    {
-      button = event->button;
-      time = event->time;
-    }
-  else
-    {
-      button = 0;
-      time = gtk_get_current_event_time ();
-    }
 
   if (gtk_widget_get_realized (GTK_WIDGET (link_button)))
     {
@@ -423,17 +373,18 @@ gtk_link_button_do_popup (GtkLinkButton  *link_button,
       gtk_widget_show (menu_item);
       gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menu_item);
 
-      if (button)
-        gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
-                       NULL, NULL,
-                       button, time);
+      if (event && gdk_event_triggers_context_menu (event))
+        gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), event);
       else
         {
-          gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
-                         popup_position_func, link_button,
-                         button, time);
-         gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
-       }
+          gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
+                                    GTK_WIDGET (link_button),
+                                    GDK_GRAVITY_SOUTH,
+                                    GDK_GRAVITY_NORTH_WEST,
+                                    event);
+
+          gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
+        }
     }
 }
 
@@ -449,7 +400,7 @@ gtk_link_button_button_press (GtkWidget      *widget,
   if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
       GTK_LINK_BUTTON (widget)->priv->uri != NULL)
     {
-      gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), event);
+      gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), (GdkEvent *) event);
 
       return TRUE;
     }
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 0bcadc1..a9fbc7e 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -2066,8 +2066,7 @@ gtk_menu_popup_at_rect (GtkMenu            *menu,
                            button,
                            activate_time);
 
-  if (current_event)
-    gdk_event_free (current_event);
+  g_clear_pointer (&current_event, gdk_event_free);
 }
 
 void
@@ -2127,8 +2126,7 @@ gtk_menu_popup_at_widget (GtkMenu        *menu,
                            button,
                            activate_time);
 
-  if (current_event)
-    gdk_event_free (current_event);
+  g_clear_pointer (&current_event, gdk_event_free);
 }
 
 void
@@ -2173,8 +2171,7 @@ gtk_menu_popup_at_pointer (GtkMenu        *menu,
                           GDK_GRAVITY_NORTH_WEST,
                           trigger_event);
 
-  if (current_event)
-    gdk_event_free (current_event);
+  g_clear_pointer (&current_event, gdk_event_free);
 }
 
 static void
diff --git a/gtk/gtkmenubutton.c b/gtk/gtkmenubutton.c
index 1f402fa..caceaa8 100644
--- a/gtk/gtkmenubutton.c
+++ b/gtk/gtkmenubutton.c
@@ -241,189 +241,157 @@ gtk_menu_button_state_flags_changed (GtkWidget    *widget,
 }
 
 static void
-menu_position_up_down_func (GtkMenu       *menu,
-                            gint          *x,
-                            gint          *y,
-                            gboolean      *push_in,
-                            GtkMenuButton *menu_button)
+popup_menu (GtkMenuButton *menu_button,
+            GdkEvent      *event)
 {
   GtkMenuButtonPrivate *priv = menu_button->priv;
-  GtkWidget *widget = GTK_WIDGET (menu_button);
-  GtkWidget *toplevel;
-  GtkTextDirection direction;
-  GdkRectangle workarea;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkWindow *window;
-  GtkAllocation menu_allocation, allocation, arrow_allocation;
-  GtkAlign align;
-
-  /* In the common case the menu button is showing a dropdown menu, set the
-   * corresponding type hint on the toplevel, so the WM can omit the top side
-   * of the shadows.
-   */
-  if (priv->arrow_type == GTK_ARROW_DOWN)
-    {
-      toplevel = gtk_widget_get_toplevel (priv->menu);
-      gtk_window_set_type_hint (GTK_WINDOW (toplevel), GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU);
-    }
-
-  align = gtk_widget_get_halign (priv->menu);
-  direction = gtk_widget_get_direction (widget);
-  window = gtk_widget_get_window (priv->align_widget ? priv->align_widget : widget);
-
-  display = gtk_widget_get_display (GTK_WIDGET (menu));
-  monitor = gdk_display_get_monitor_at_window (display, window);
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  gtk_widget_get_allocation (priv->align_widget ? priv->align_widget : widget, &allocation);
-  gtk_widget_get_allocation (widget, &arrow_allocation);
-  gtk_widget_get_allocation (priv->menu, &menu_allocation);
-
-  gdk_window_get_origin (window, x, y);
-  *x += allocation.x;
-  *y += allocation.y;
+  GdkGravity widget_anchor = GDK_GRAVITY_SOUTH_WEST;
+  GdkGravity menu_anchor = GDK_GRAVITY_NORTH_WEST;
 
-  /* treat the default align value like START */
-  if (align == GTK_ALIGN_FILL)
-    align = GTK_ALIGN_START;
+  if (priv->func)
+    priv->func (priv->user_data);
 
-  if (align == GTK_ALIGN_CENTER)
-    *x -= (menu_allocation.width - allocation.width) / 2;
-  else if ((align == GTK_ALIGN_START && direction == GTK_TEXT_DIR_LTR) ||
-           (align == GTK_ALIGN_END && direction == GTK_TEXT_DIR_RTL))
-    *x += MAX (allocation.width - menu_allocation.width, 0);
-  else if (menu_allocation.width > allocation.width)
-    *x -= menu_allocation.width - allocation.width;
+  if (!priv->menu)
+    return;
 
-  if (priv->arrow_type == GTK_ARROW_UP && *y - menu_allocation.height >= workarea.y)
-    {
-      *y -= menu_allocation.height;
-    }
-  else
+  switch (priv->arrow_type)
     {
-      if ((*y + arrow_allocation.height + menu_allocation.height) <= workarea.y + workarea.height)
-        *y += arrow_allocation.height;
-      else if ((*y - menu_allocation.height) >= workarea.y)
-        *y -= menu_allocation.height;
-      else if (workarea.y + workarea.height - (*y + arrow_allocation.height) > *y)
-        *y += arrow_allocation.height;
-      else
-        *y -= menu_allocation.height;
-    }
-
-  *push_in = FALSE;
-}
-
-static void
-menu_position_side_func (GtkMenu       *menu,
-                         gint          *x,
-                         gint          *y,
-                         gboolean      *push_in,
-                         GtkMenuButton *menu_button)
-{
-  GtkMenuButtonPrivate *priv = menu_button->priv;
-  GtkAllocation allocation;
-  GtkAllocation menu_allocation;
-  GtkWidget *widget = GTK_WIDGET (menu_button);
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-  GdkWindow *window;
-  GtkAlign align;
-  GtkTextDirection direction;
-
-  window = gtk_widget_get_window (widget);
-
-  direction = gtk_widget_get_direction (widget);
-  align = gtk_widget_get_valign (GTK_WIDGET (menu));
-  display = gtk_widget_get_display (GTK_WIDGET (menu));
-  monitor = gdk_display_get_monitor_at_window (display, window);
-  gdk_monitor_get_workarea (monitor, &workarea);
+    case GTK_ARROW_UP:
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    NULL);
 
-  gdk_window_get_origin (gtk_button_get_event_window (GTK_BUTTON (menu_button)), x, y);
+      switch (gtk_widget_get_halign (priv->menu))
+        {
+        case GTK_ALIGN_FILL:
+        case GTK_ALIGN_START:
+        case GTK_ALIGN_BASELINE:
+          widget_anchor = GDK_GRAVITY_NORTH_WEST;
+          menu_anchor = GDK_GRAVITY_SOUTH_WEST;
+          break;
+
+        case GTK_ALIGN_END:
+          widget_anchor = GDK_GRAVITY_NORTH_EAST;
+          menu_anchor = GDK_GRAVITY_SOUTH_EAST;
+          break;
+
+        case GTK_ALIGN_CENTER:
+          widget_anchor = GDK_GRAVITY_NORTH;
+          menu_anchor = GDK_GRAVITY_SOUTH;
+          break;
+        }
 
-  gtk_widget_get_allocation (widget, &allocation);
-  gtk_widget_get_allocation (priv->menu, &menu_allocation);
+      break;
 
-  if ((priv->arrow_type == GTK_ARROW_RIGHT && direction == GTK_TEXT_DIR_LTR) ||
-      (priv->arrow_type == GTK_ARROW_LEFT && direction == GTK_TEXT_DIR_RTL))
+    case GTK_ARROW_DOWN:
+      /* In the common case the menu button is showing a dropdown menu, set the
+       * corresponding type hint on the toplevel, so the WM can omit the top side
+       * of the shadows.
+       */
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    "menu-type-hint", GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU,
+                    NULL);
+
+      switch (gtk_widget_get_halign (priv->menu))
+        {
+        case GTK_ALIGN_FILL:
+        case GTK_ALIGN_START:
+        case GTK_ALIGN_BASELINE:
+          widget_anchor = GDK_GRAVITY_SOUTH_WEST;
+          menu_anchor = GDK_GRAVITY_NORTH_WEST;
+          break;
+
+        case GTK_ALIGN_END:
+          widget_anchor = GDK_GRAVITY_SOUTH_EAST;
+          menu_anchor = GDK_GRAVITY_NORTH_EAST;
+          break;
+
+        case GTK_ALIGN_CENTER:
+          widget_anchor = GDK_GRAVITY_SOUTH;
+          menu_anchor = GDK_GRAVITY_NORTH;
+          break;
+        }
 
-    {
-      if (*x + allocation.width + menu_allocation.width <= workarea.x + workarea.width)
-        *x += allocation.width;
-      else
-        *x -= menu_allocation.width;
-    }
-  else
-    {
-      if (*x - menu_allocation.width >= workarea.x)
-        *x -= menu_allocation.width;
-      else
-        *x += allocation.width;
-    }
+      break;
 
-  /* treat the default align value like START */
-  if (align == GTK_ALIGN_FILL)
-    align = GTK_ALIGN_START;
+    case GTK_ARROW_LEFT:
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_X |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    NULL);
 
-  if (align == GTK_ALIGN_CENTER)
-    *y -= (menu_allocation.height - allocation.height) / 2;
-  else if (align == GTK_ALIGN_END)
-    *y -= menu_allocation.height - allocation.height;
+      switch (gtk_widget_get_valign (priv->menu))
+        {
+        case GTK_ALIGN_FILL:
+        case GTK_ALIGN_START:
+        case GTK_ALIGN_BASELINE:
+          widget_anchor = GDK_GRAVITY_NORTH_WEST;
+          menu_anchor = GDK_GRAVITY_NORTH_EAST;
+          break;
+
+        case GTK_ALIGN_END:
+          widget_anchor = GDK_GRAVITY_SOUTH_WEST;
+          menu_anchor = GDK_GRAVITY_SOUTH_EAST;
+          break;
+
+        case GTK_ALIGN_CENTER:
+          widget_anchor = GDK_GRAVITY_WEST;
+          menu_anchor = GDK_GRAVITY_EAST;
+          break;
+        }
 
-  *push_in = FALSE;
-}
+      break;
 
-static void
-popup_menu (GtkMenuButton *menu_button,
-            GdkEvent      *event)
-{
-  GtkMenuButtonPrivate *priv = menu_button->priv;
-  GtkMenuPositionFunc func;
-  GdkDevice *device;
-  guint button;
-  guint32 time;
+    case GTK_ARROW_RIGHT:
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_X |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    NULL);
 
-  if (priv->func)
-    priv->func (priv->user_data);
+      switch (gtk_widget_get_valign (priv->menu))
+        {
+        case GTK_ALIGN_FILL:
+        case GTK_ALIGN_START:
+        case GTK_ALIGN_BASELINE:
+          widget_anchor = GDK_GRAVITY_NORTH_EAST;
+          menu_anchor = GDK_GRAVITY_NORTH_WEST;
+          break;
+
+        case GTK_ALIGN_END:
+          widget_anchor = GDK_GRAVITY_SOUTH_EAST;
+          menu_anchor = GDK_GRAVITY_SOUTH_WEST;
+          break;
+
+        case GTK_ALIGN_CENTER:
+          widget_anchor = GDK_GRAVITY_EAST;
+          menu_anchor = GDK_GRAVITY_WEST;
+          break;
+        }
 
-  if (!priv->menu)
-    return;
+      break;
 
-  switch (priv->arrow_type)
-    {
-      case GTK_ARROW_LEFT:
-      case GTK_ARROW_RIGHT:
-        func = (GtkMenuPositionFunc) menu_position_side_func;
-        break;
-      default:
-        func = (GtkMenuPositionFunc) menu_position_up_down_func;
-        break;
-  }
+    case GTK_ARROW_NONE:
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    NULL);
 
-  if (event != NULL &&
-      gdk_event_get_screen (event) == gtk_widget_get_screen (GTK_WIDGET (menu_button)))
-    {
-      device = gdk_event_get_device (event);
-      gdk_event_get_button (event, &button);
-      time = gdk_event_get_time (event);
-    }
-  else
-    {
-      device = NULL;
-      button = 0;
-      time = gtk_get_current_event_time ();
+      break;
     }
 
-  gtk_menu_popup_for_device (GTK_MENU (priv->menu),
-                             device,
-                             NULL, NULL,
-                             func,
-                             GTK_WIDGET (menu_button),
-                             NULL,
-                             button,
-                             time);
+  gtk_menu_popup_at_widget (GTK_MENU (priv->menu),
+                            GTK_WIDGET (menu_button),
+                            widget_anchor,
+                            menu_anchor,
+                            event);
 }
 
 static void
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index 7910806..bd51c9c 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -171,11 +171,6 @@ static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
 
 static void gtk_menu_item_ensure_label   (GtkMenuItem      *menu_item);
 static gint gtk_menu_item_popup_timeout  (gpointer          data);
-static void gtk_menu_item_position_menu  (GtkMenu          *menu,
-                                          gint             *x,
-                                          gint             *y,
-                                          gboolean         *push_in,
-                                          gpointer          user_data);
 static void gtk_menu_item_show_all       (GtkWidget        *widget);
 
 static void gtk_menu_item_forall         (GtkContainer    *container,
@@ -1909,19 +1904,61 @@ free_timeval (GTimeVal *val)
 }
 
 static void
-gtk_menu_item_real_popup_submenu (GtkWidget *widget,
-                                  gboolean   remember_exact_time)
+popped_up_cb (GtkMenu            *menu,
+              const GdkRectangle *flipped_rect,
+              const GdkRectangle *slid_rect,
+              gboolean            flipped_x,
+              gboolean            flipped_y,
+              GtkMenuItem        *menu_item)
+{
+  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (menu_item));
+  GtkMenu *parent_menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
+
+  if (parent_menu && GTK_IS_MENU_ITEM (parent_menu->priv->parent_menu_item))
+    menu_item->priv->submenu_direction = GTK_MENU_ITEM 
(parent_menu->priv->parent_menu_item)->priv->submenu_direction;
+  else
+    {
+      /* this case is stateful, do it at most once */
+      g_signal_handlers_disconnect_by_func (menu, popped_up_cb, menu_item);
+    }
+
+  if (flipped_x)
+    {
+      switch (menu_item->priv->submenu_direction)
+        {
+        case GTK_DIRECTION_LEFT:
+          menu_item->priv->submenu_direction = GTK_DIRECTION_RIGHT;
+          break;
+
+        case GTK_DIRECTION_RIGHT:
+          menu_item->priv->submenu_direction = GTK_DIRECTION_LEFT;
+          break;
+        }
+    }
+}
+
+static void
+gtk_menu_item_real_popup_submenu (GtkWidget      *widget,
+                                  const GdkEvent *trigger_event,
+                                  gboolean        remember_exact_time)
 {
   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
   GtkMenuItemPrivate *priv = menu_item->priv;
+  GtkSubmenuDirection submenu_direction;
+  GtkStyleContext *context;
+  GtkBorder parent_padding;
+  GtkBorder menu_padding;
+  gint horizontal_offset;
+  gint vertical_offset;
   GtkWidget *parent;
+  GtkMenu *parent_menu;
 
   parent = gtk_widget_get_parent (widget);
+  parent_menu = GTK_IS_MENU (parent) ? GTK_MENU (parent) : NULL;
 
   if (gtk_widget_is_sensitive (priv->submenu) && parent)
     {
       gboolean take_focus;
-      GtkMenuPositionFunc menu_position_func;
 
       take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (parent));
       gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (priv->submenu), take_focus);
@@ -1942,24 +1979,91 @@ gtk_menu_item_real_popup_submenu (GtkWidget *widget,
                              "gtk-menu-exact-popup-time", NULL);
         }
 
-      /* gtk_menu_item_position_menu positions the submenu from the
-       * menuitems position. If the menuitem doesn't have a window,
-       * that doesn't work. In that case we use the default
-       * positioning function instead which places the submenu at the
-       * mouse cursor.
+      /* Position the submenu at the menu item if it is mapped.
+       * Otherwise, position the submenu at the pointer device.
        */
       if (gtk_widget_get_window (widget))
-        menu_position_func = gtk_menu_item_position_menu;
+        {
+          switch (priv->submenu_placement)
+            {
+            case GTK_TOP_BOTTOM:
+              g_object_set (priv->submenu,
+                            "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                            GDK_ANCHOR_SLIDE |
+                                            GDK_ANCHOR_RESIZE,
+                            "menu-type-hint", priv->from_menubar ?
+                                              GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU :
+                                              GDK_WINDOW_TYPE_HINT_POPUP_MENU,
+                            NULL);
+
+              gtk_menu_popup_at_widget (GTK_MENU (priv->submenu),
+                                        widget,
+                                        GDK_GRAVITY_SOUTH_WEST,
+                                        GDK_GRAVITY_NORTH_WEST,
+                                        trigger_event);
+
+              break;
+
+            case GTK_LEFT_RIGHT:
+              if (parent_menu && GTK_IS_MENU_ITEM (parent_menu->priv->parent_menu_item))
+                submenu_direction = GTK_MENU_ITEM 
(parent_menu->priv->parent_menu_item)->priv->submenu_direction;
+              else
+                submenu_direction = priv->submenu_direction;
+
+              g_signal_handlers_disconnect_by_func (priv->submenu, popped_up_cb, menu_item);
+              g_signal_connect (priv->submenu, "popped-up", G_CALLBACK (popped_up_cb), menu_item);
+
+              gtk_widget_style_get (priv->submenu,
+                                    "horizontal-offset", &horizontal_offset,
+                                    "vertical-offset", &vertical_offset,
+                                    NULL);
+
+              context = gtk_widget_get_style_context (parent);
+              gtk_style_context_get_padding (context, gtk_style_context_get_state (context), 
&parent_padding);
+              context = gtk_widget_get_style_context (priv->submenu);
+              gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &menu_padding);
+
+              g_object_set (priv->submenu,
+                            "anchor-hints", GDK_ANCHOR_FLIP_X |
+                                            GDK_ANCHOR_SLIDE |
+                                            GDK_ANCHOR_RESIZE,
+                            "rect-anchor-dy", vertical_offset - menu_padding.top,
+                            NULL);
+
+              switch (submenu_direction)
+                {
+                case GTK_DIRECTION_RIGHT:
+                  g_object_set (priv->submenu,
+                                "rect-anchor-dx", horizontal_offset + parent_padding.right + 
menu_padding.left,
+                                NULL);
+
+                  gtk_menu_popup_at_widget (GTK_MENU (priv->submenu),
+                                            widget,
+                                            GDK_GRAVITY_NORTH_EAST,
+                                            GDK_GRAVITY_NORTH_WEST,
+                                            trigger_event);
+
+                  break;
+
+                case GTK_DIRECTION_LEFT:
+                  g_object_set (priv->submenu,
+                                "rect-anchor-dx", -(horizontal_offset + parent_padding.left + 
menu_padding.right),
+                                NULL);
+
+                  gtk_menu_popup_at_widget (GTK_MENU (priv->submenu),
+                                            widget,
+                                            GDK_GRAVITY_NORTH_WEST,
+                                            GDK_GRAVITY_NORTH_EAST,
+                                            trigger_event);
+
+                  break;
+                }
+
+              break;
+            }
+        }
       else
-        menu_position_func = NULL;
-
-      gtk_menu_popup (GTK_MENU (priv->submenu),
-                      parent,
-                      widget,
-                      menu_position_func,
-                      menu_item,
-                      GTK_MENU_SHELL (parent)->priv->button,
-                      0);
+        gtk_menu_popup_at_pointer (GTK_MENU (priv->submenu), trigger_event);
     }
 
   /* Enable themeing of the parent menu item depending on whether
@@ -1968,10 +2072,17 @@ gtk_menu_item_real_popup_submenu (GtkWidget *widget,
   gtk_widget_queue_draw (widget);
 }
 
+typedef struct
+{
+  GtkMenuItem *menu_item;
+  GdkEvent    *trigger_event;
+} PopupInfo;
+
 static gint
 gtk_menu_item_popup_timeout (gpointer data)
 {
-  GtkMenuItem *menu_item = GTK_MENU_ITEM (data);
+  PopupInfo *info = data;
+  GtkMenuItem *menu_item = info->menu_item;
   GtkMenuItemPrivate *priv = menu_item->priv;
   GtkWidget *parent;
 
@@ -1980,13 +2091,19 @@ gtk_menu_item_popup_timeout (gpointer data)
   if ((GTK_IS_MENU_SHELL (parent) && GTK_MENU_SHELL (parent)->priv->active) ||
       (GTK_IS_MENU (parent) && GTK_MENU (parent)->priv->torn_off))
     {
-      gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), TRUE);
-      if (priv->timer_from_keypress && priv->submenu)
+      gtk_menu_item_real_popup_submenu (GTK_WIDGET (menu_item), info->trigger_event, TRUE);
+      if (info->trigger_event &&
+          info->trigger_event->type != GDK_BUTTON_PRESS &&
+          info->trigger_event->type != GDK_ENTER_NOTIFY &&
+          priv->submenu)
         GTK_MENU_SHELL (priv->submenu)->priv->ignore_enter = TRUE;
     }
 
   priv->timer = 0;
 
+  g_clear_pointer (&info->trigger_event, gdk_event_free);
+  g_slice_free (PopupInfo, info);
+
   return FALSE;
 }
 
@@ -2022,28 +2139,21 @@ _gtk_menu_item_popup_submenu (GtkWidget *widget,
 
       if (popup_delay > 0)
         {
-          GdkEvent *event = gtk_get_current_event ();
+          PopupInfo *info = g_slice_new (PopupInfo);
+
+          info->menu_item = menu_item;
+          info->trigger_event = gtk_get_current_event ();
 
           priv->timer = gdk_threads_add_timeout (popup_delay,
                                                  gtk_menu_item_popup_timeout,
-                                                 menu_item);
+                                                 info);
           g_source_set_name_by_id (priv->timer, "[gtk+] gtk_menu_item_popup_timeout");
 
-          if (event &&
-              event->type != GDK_BUTTON_PRESS &&
-              event->type != GDK_ENTER_NOTIFY)
-            priv->timer_from_keypress = TRUE;
-          else
-            priv->timer_from_keypress = FALSE;
-
-          if (event)
-            gdk_event_free (event);
-
           return;
         }
     }
 
-  gtk_menu_item_real_popup_submenu (widget, FALSE);
+  gtk_menu_item_real_popup_submenu (widget, NULL, FALSE);
 }
 
 void
@@ -2069,176 +2179,6 @@ _gtk_menu_item_popdown_submenu (GtkWidget *widget)
     }
 }
 
-static void
-get_offsets (GtkMenu *menu,
-             gint    *horizontal_offset,
-             gint    *vertical_offset)
-{
-  GtkStyleContext *context;
-  GtkBorder padding;
-
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "horizontal-offset", horizontal_offset,
-                        "vertical-offset", vertical_offset,
-                        NULL);
-
-  context = gtk_widget_get_style_context (GTK_WIDGET (menu));
-  gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
-
-  *vertical_offset -= padding.top;
-  *horizontal_offset += padding.left;
-}
-
-static void
-gtk_menu_item_position_menu (GtkMenu  *menu,
-                             gint     *x,
-                             gint     *y,
-                             gboolean *push_in,
-                             gpointer  user_data)
-{
-  GtkMenuItem *menu_item = GTK_MENU_ITEM (user_data);
-  GtkMenuItemPrivate *priv = menu_item->priv;
-  GtkAllocation allocation;
-  GtkWidget *widget;
-  GtkMenuItem *parent_menu_item;
-  GtkWidget *parent;
-  GdkDisplay *display;
-  gint twidth, theight;
-  gint tx, ty;
-  GtkTextDirection direction;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-  gint horizontal_offset;
-  gint vertical_offset;
-  gint available_left, available_right;
-  GtkStyleContext *context;
-  GtkBorder parent_padding;
-
-  g_return_if_fail (menu != NULL);
-  g_return_if_fail (x != NULL);
-  g_return_if_fail (y != NULL);
-
-  widget = GTK_WIDGET (user_data);
-
-  if (push_in)
-    *push_in = FALSE;
-
-  direction = gtk_widget_get_direction (widget);
-
-  twidth = gtk_widget_get_allocated_width (GTK_WIDGET (menu));
-  theight = gtk_widget_get_allocated_height (GTK_WIDGET (menu));
-
-  display = gtk_widget_get_display (GTK_WIDGET (menu));
-  monitor = gdk_display_get_monitor_at_window (display, priv->event_window);
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  if (!gdk_window_get_origin (gtk_widget_get_window (widget), &tx, &ty))
-    {
-      g_warning ("Menu not on screen");
-      return;
-    }
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  tx += allocation.x;
-  ty += allocation.y;
-
-  get_offsets (menu, &horizontal_offset, &vertical_offset);
-
-  available_left = tx - workarea.x;
-  available_right = workarea.x + workarea.width - (tx + allocation.width);
-
-  parent = gtk_widget_get_parent (widget);
-  priv->from_menubar = GTK_IS_MENU_BAR (parent);
-
-  switch (priv->submenu_placement)
-    {
-    case GTK_TOP_BOTTOM:
-      if (direction == GTK_TEXT_DIR_LTR)
-        priv->submenu_direction = GTK_DIRECTION_RIGHT;
-      else
-        {
-          priv->submenu_direction = GTK_DIRECTION_LEFT;
-          tx += allocation.width - twidth;
-        }
-      if ((ty + allocation.height + theight) <= workarea.y + workarea.height)
-        ty += allocation.height;
-      else if ((ty - theight) >= workarea.y)
-        ty -= theight;
-      else if (workarea.y + workarea.height - (ty + allocation.height) > ty)
-        ty += allocation.height;
-      else
-        ty -= theight;
-      break;
-
-    case GTK_LEFT_RIGHT:
-      if (GTK_IS_MENU (parent))
-        parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->priv->parent_menu_item);
-      else
-        parent_menu_item = NULL;
-
-      context = gtk_widget_get_style_context (parent);
-      gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &parent_padding);
-
-      if (parent_menu_item && !GTK_MENU (parent)->priv->torn_off)
-        {
-          priv->submenu_direction = parent_menu_item->priv->submenu_direction;
-        }
-      else
-        {
-          if (direction == GTK_TEXT_DIR_LTR)
-            priv->submenu_direction = GTK_DIRECTION_RIGHT;
-          else
-            priv->submenu_direction = GTK_DIRECTION_LEFT;
-        }
-
-      switch (priv->submenu_direction)
-        {
-        case GTK_DIRECTION_LEFT:
-          if (tx - twidth - parent_padding.left - horizontal_offset >= workarea.x ||
-              available_left >= available_right)
-            tx -= twidth + parent_padding.left + horizontal_offset;
-          else
-            {
-              priv->submenu_direction = GTK_DIRECTION_RIGHT;
-              tx += allocation.width + parent_padding.right + horizontal_offset;
-            }
-          break;
-
-        case GTK_DIRECTION_RIGHT:
-          if (tx + allocation.width + parent_padding.right + horizontal_offset + twidth <= workarea.x + 
workarea.width ||
-              available_right >= available_left)
-            tx += allocation.width + parent_padding.right + horizontal_offset;
-          else
-            {
-              priv->submenu_direction = GTK_DIRECTION_LEFT;
-              tx -= twidth + parent_padding.left + horizontal_offset;
-            }
-          break;
-        }
-
-      ty += vertical_offset;
-
-      /* If the height of the menu doesn't fit we move it upward. */
-      ty = CLAMP (ty, workarea.y, MAX (workarea.y, workarea.y + workarea.height - theight));
-      break;
-    }
-
-  /* If we have negative, tx, here it is because we can't get
-   * the menu all the way on screen. Favor the left portion.
-   */
-  *x = CLAMP (tx, workarea.x, MAX (workarea.x, workarea.x + workarea.width - twidth));
-  *y = ty;
-
-  gtk_menu_place_on_monitor (menu, monitor);
-
-  if (!gtk_widget_get_visible (menu->priv->toplevel))
-    {
-      gtk_window_set_type_hint (GTK_WINDOW (menu->priv->toplevel), priv->from_menubar?
-                                GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU);
-    }
-}
-
 /**
  * gtk_menu_item_set_right_justified:
  * @menu_item: a #GtkMenuItem.
diff --git a/gtk/gtkmenuitemprivate.h b/gtk/gtkmenuitemprivate.h
index 108a188..cb213d9 100644
--- a/gtk/gtkmenuitemprivate.h
+++ b/gtk/gtkmenuitemprivate.h
@@ -47,7 +47,6 @@ struct _GtkMenuItemPrivate
   guint submenu_placement      : 1;
   guint submenu_direction      : 1;
   guint right_justify          : 1;
-  guint timer_from_keypress    : 1;
   guint from_menubar           : 1;
   guint use_action_appearance  : 1;
   guint reserve_indicator      : 1;
diff --git a/gtk/gtkmountoperation.c b/gtk/gtkmountoperation.c
index e78a5e2..95d856c 100644
--- a/gtk/gtkmountoperation.c
+++ b/gtk/gtkmountoperation.c
@@ -1299,16 +1299,11 @@ on_end_process_activated (GtkMenuItem *item,
 
 static gboolean
 do_popup_menu_for_process_tree_view (GtkWidget         *widget,
-                                     GdkEventButton    *event,
+                                     const GdkEvent    *event,
                                      GtkMountOperation *op)
 {
   GtkWidget *menu;
   GtkWidget *item;
-  gint button;
-  gint event_time;
-  gboolean popped_up_menu;
-
-  popped_up_menu = FALSE;
 
   menu = gtk_menu_new ();
   gtk_style_context_add_class (gtk_widget_get_style_context (menu),
@@ -1321,14 +1316,14 @@ do_popup_menu_for_process_tree_view (GtkWidget         *widget,
   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
   gtk_widget_show_all (menu);
 
-  if (event != NULL)
+  if (event && gdk_event_triggers_context_menu (event))
     {
       GtkTreePath *path;
       GtkTreeSelection *selection;
 
       if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (op->priv->process_tree_view),
-                                         (gint) event->x,
-                                         (gint) event->y,
+                                         (gint) event->button.x,
+                                         (gint) event->button.y,
                                          &path,
                                          NULL,
                                          NULL,
@@ -1341,30 +1336,12 @@ do_popup_menu_for_process_tree_view (GtkWidget         *widget,
       else
         {
           /* don't popup a menu if the user right-clicked in an area with no rows */
-          goto out;
+          return FALSE;
         }
-
-      button = event->button;
-      event_time = event->time;
-    }
-  else
-    {
-      button = 0;
-      event_time = gtk_get_current_event_time ();
     }
 
-  gtk_menu_popup (GTK_MENU (menu),
-                  NULL,
-                  widget,
-                  NULL,
-                  NULL,
-                  button,
-                  event_time);
-
-  popped_up_menu = TRUE;
-
- out:
-  return popped_up_menu;
+  gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
+  return TRUE;
 }
 
 static gboolean
@@ -1387,7 +1364,7 @@ on_button_press_event_for_process_tree_view (GtkWidget      *widget,
 
   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
     {
-      ret = do_popup_menu_for_process_tree_view (widget, event, op);
+      ret = do_popup_menu_for_process_tree_view (widget, (GdkEvent *) event, op);
     }
 
   return ret;
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 77a8a8b..a7291a7 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -2750,8 +2750,7 @@ gtk_notebook_button_press (GtkWidget      *widget,
 
   if (priv->menu && gdk_event_triggers_context_menu ((GdkEvent *) event))
     {
-      gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
-                      NULL, NULL, 3, event->time);
+      gtk_menu_popup_at_pointer (GTK_MENU (priv->menu), (GdkEvent *) event);
       return TRUE;
     }
 
@@ -2794,58 +2793,50 @@ gtk_notebook_button_press (GtkWidget      *widget,
   return TRUE;
 }
 
-static void
-popup_position_func (GtkMenu  *menu,
-                     gint     *x,
-                     gint     *y,
-                     gboolean *push_in,
-                     gpointer  data)
-{
-  GtkNotebook *notebook = data;
-  GtkNotebookPrivate *priv = notebook->priv;
-  GtkAllocation allocation;
-  GtkWidget *w;
-  GtkRequisition requisition;
-
-  if (priv->focus_tab)
-    {
-      GtkNotebookPage *page;
-
-      page = priv->focus_tab->data;
-      w = page->tab_label;
-    }
-  else
-   {
-     w = GTK_WIDGET (notebook);
-   }
-
-  gdk_window_get_origin (gtk_widget_get_window (w), x, y);
-
-  gtk_widget_get_allocation (w, &allocation);
-  gtk_widget_get_preferred_size (GTK_WIDGET (menu),
-                                 &requisition, NULL);
-
-  if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
-    *x += allocation.x + allocation.width - requisition.width;
-  else
-    *x += allocation.x;
-
-  *y += allocation.y + allocation.height;
-
-  *push_in = FALSE;
-}
-
 static gboolean
 gtk_notebook_popup_menu (GtkWidget *widget)
 {
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   GtkNotebookPrivate *priv = notebook->priv;
+  GtkNotebookPage *page;
+  GtkWidget *tab_label = NULL;
 
   if (priv->menu)
     {
-      gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
-                      popup_position_func, notebook,
-                      0, gtk_get_current_event_time ());
+      if (priv->focus_tab)
+        {
+          page = priv->focus_tab->data;
+          tab_label = page->tab_label;
+        }
+
+      if (tab_label)
+        {
+          g_object_set (priv->menu,
+                        "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                        GDK_ANCHOR_SLIDE |
+                                        GDK_ANCHOR_RESIZE,
+                        NULL);
+
+          gtk_menu_popup_at_widget (GTK_MENU (priv->menu),
+                                    tab_label,
+                                    GDK_GRAVITY_SOUTH_WEST,
+                                    GDK_GRAVITY_NORTH_WEST,
+                                    NULL);
+        }
+      else
+        {
+          g_object_set (priv->menu,
+                        "anchor-hints", GDK_ANCHOR_SLIDE |
+                                        GDK_ANCHOR_RESIZE,
+                        NULL);
+
+          gtk_menu_popup_at_widget (GTK_MENU (priv->menu),
+                                    widget,
+                                    GDK_GRAVITY_NORTH_WEST,
+                                    GDK_GRAVITY_NORTH_WEST,
+                                    NULL);
+        }
+
       gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
       return TRUE;
     }
diff --git a/gtk/gtkplacesview.c b/gtk/gtkplacesview.c
index de13d24..cc4e04c 100644
--- a/gtk/gtkplacesview.c
+++ b/gtk/gtkplacesview.c
@@ -1656,7 +1656,6 @@ popup_menu (GtkPlacesViewRow *row,
 {
   GtkPlacesViewPrivate *priv;
   GtkWidget *view;
-  gint button;
 
   view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
   priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (view));
@@ -1665,30 +1664,7 @@ popup_menu (GtkPlacesViewRow *row,
 
   build_popup_menu (GTK_PLACES_VIEW (view), row);
 
-  /* The event button needs to be 0 if we're popping up this menu from
-   * a button release, else a 2nd click outside the menu with any button
-   * other than the one that invoked the menu will be ignored (instead
-   * of dismissing the menu). This is a subtle fragility of the GTK menu code.
-   */
-  if (event)
-    {
-      if (event->type == GDK_BUTTON_PRESS)
-        button = 0;
-      else
-        button = event->button;
-    }
-  else
-    {
-      button = 0;
-    }
-
-  gtk_menu_popup (GTK_MENU (priv->popup_menu),
-                  NULL,
-                  NULL,
-                  NULL,
-                  NULL,
-                  button,
-                  event ? event->time : gtk_get_current_event_time ());
+  gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), (GdkEvent *) event);
 }
 
 static gboolean
diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c
index d1e9672..2a9cee9 100644
--- a/gtk/gtkrecentchooserdefault.c
+++ b/gtk/gtkrecentchooserdefault.c
@@ -1727,62 +1727,23 @@ recent_view_menu_build (GtkRecentChooserDefault *impl)
   recent_view_menu_ensure_state (impl);
 }
 
-/* taken from gtkfilechooserdefault.c */
-static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer  user_data)
-{
-  GtkAllocation allocation;
-  GtkWidget *widget = GTK_WIDGET (user_data);
-  GtkRequisition req;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-
-  if (G_UNLIKELY (!gtk_widget_get_realized (widget)))
-    return;
-
-  gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
-
-  gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
-
-  gtk_widget_get_allocation (widget, &allocation);
-  *x += (allocation.width - req.width) / 2;
-  *y += (allocation.height - req.height) / 2;
-
-  display = gtk_widget_get_display (widget);
-  monitor = gdk_display_get_monitor_at_point (display, *x, *y);
-  gtk_menu_place_on_monitor (menu, monitor);
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  *x = CLAMP (*x, workarea.x, workarea.x + MAX (0, workarea.width - req.width));
-  *y = CLAMP (*y, workarea.y, workarea.y + MAX (0, workarea.height - req.height));
-
-  *push_in = FALSE;
-}
-
-
 static void
 recent_view_menu_popup (GtkRecentChooserDefault *impl,
                        GdkEventButton          *event)
 {
   recent_view_menu_build (impl);
   
-  if (event)
-    gtk_menu_popup (GTK_MENU (impl->priv->recent_popup_menu),
-                   NULL, NULL, NULL, NULL,
-                   event->button, event->time);
+  if (event && gdk_event_triggers_context_menu ((GdkEvent *) event))
+    gtk_menu_popup_at_pointer (GTK_MENU (impl->priv->recent_popup_menu), (GdkEvent *) event);
   else
     {
-      gtk_menu_popup (GTK_MENU (impl->priv->recent_popup_menu),
-                     NULL, NULL,
-                     popup_position_func, impl->priv->recent_view,
-                     0, GDK_CURRENT_TIME);
-      gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->priv->recent_popup_menu),
-                                  FALSE);
+      gtk_menu_popup_at_widget (GTK_MENU (impl->priv->recent_popup_menu),
+                                impl->priv->recent_view,
+                                GDK_GRAVITY_CENTER,
+                                GDK_GRAVITY_CENTER,
+                                (GdkEvent *) event);
+
+      gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->priv->recent_popup_menu), FALSE);
     }
 }
 
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
index 22b3a59..154ee39 100644
--- a/gtk/gtktextview.c
+++ b/gtk/gtktextview.c
@@ -9347,91 +9347,10 @@ popup_menu_detach (GtkWidget *attach_widget,
   GTK_TEXT_VIEW (attach_widget)->priv->popup_menu = NULL;
 }
 
-static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer  user_data)
-{
-  GtkAllocation allocation;
-  GtkTextView *text_view;
-  GtkWidget *widget;
-  GdkRectangle cursor_rect;
-  GdkRectangle onscreen_rect;
-  gint root_x, root_y;
-  GtkTextIter iter;
-  GtkRequisition req;
-  GdkDisplay *display;
-  GdkMonitor *monitor;
-  GdkRectangle workarea;
-
-  text_view = GTK_TEXT_VIEW (user_data);
-  widget = GTK_WIDGET (text_view);
-
-  g_return_if_fail (gtk_widget_get_realized (widget));
-
-  display = gtk_widget_get_display (widget);
-
-  gdk_window_get_origin (gtk_widget_get_window (widget),
-                         &root_x, &root_y);
-
-  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
-                                    &iter,
-                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
-
-  gtk_text_view_get_iter_location (text_view,
-                                   &iter,
-                                   &cursor_rect);
-
-  gtk_text_view_get_visible_rect (text_view, &onscreen_rect);
-
-  gtk_widget_get_preferred_size (text_view->priv->popup_menu,
-                                 &req, NULL);
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  /* can't use rectangle_intersect since cursor rect can have 0 width */
-  if (cursor_rect.x >= onscreen_rect.x &&
-      cursor_rect.x < onscreen_rect.x + onscreen_rect.width &&
-      cursor_rect.y >= onscreen_rect.y &&
-      cursor_rect.y < onscreen_rect.y + onscreen_rect.height)
-    {
-      gtk_text_view_buffer_to_window_coords (text_view,
-                                             GTK_TEXT_WINDOW_WIDGET,
-                                             cursor_rect.x, cursor_rect.y,
-                                             &cursor_rect.x, &cursor_rect.y);
-
-      *x = root_x + cursor_rect.x + cursor_rect.width;
-      *y = root_y + cursor_rect.y + cursor_rect.height;
-    }
-  else
-    {
-      /* Just center the menu, since cursor is offscreen. */
-      *x = root_x + (allocation.width / 2 - req.width / 2);
-      *y = root_y + (allocation.height / 2 - req.height / 2);
-    }
-
-  /* Ensure sanity */
-  *x = CLAMP (*x, root_x, (root_x + allocation.width));
-  *y = CLAMP (*y, root_y, (root_y + allocation.height));
-
-  monitor = gdk_display_get_monitor_at_point (display, *x, *y);
-  gtk_menu_place_on_monitor (menu, monitor);
-  gdk_monitor_get_workarea (monitor, &workarea);
-
-  *x = CLAMP (*x, workarea.x, workarea.x + MAX (0, workarea.width - req.width));
-  *y = CLAMP (*y, workarea.y, workarea.y + MAX (0, workarea.height - req.height));
-
-  *push_in = FALSE;
-}
-
 typedef struct
 {
   GtkTextView *text_view;
-  guint button;
-  guint time;
-  GdkDevice *device;
+  GdkEvent *trigger_event;
 } PopupInfo;
 
 static gboolean
@@ -9475,6 +9394,9 @@ popup_targets_received (GtkClipboard     *clipboard,
       gboolean can_insert;
       GtkTextIter iter;
       GtkTextIter sel_start, sel_end;
+      GdkRectangle iter_location;
+      GdkRectangle visible_rect;
+      gboolean is_visible;
 
       clipboard_contains_text = gtk_selection_data_targets_include_text (data);
 
@@ -9532,19 +9454,46 @@ popup_targets_received (GtkClipboard     *clipboard,
       g_signal_emit (text_view, signals[POPULATE_POPUP],
                     0, priv->popup_menu);
 
-      if (info->device)
-       gtk_menu_popup_for_device (GTK_MENU (priv->popup_menu),
-                                   info->device, NULL, NULL, NULL, NULL, NULL,
-                                  info->button, info->time);
+      if (info->trigger_event && gdk_event_triggers_context_menu (info->trigger_event))
+        gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), info->trigger_event);
       else
-       {
-         gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
-                         popup_position_func, text_view,
-                         0, gtk_get_current_event_time ());
-         gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
-       }
+        {
+          gtk_text_view_get_iter_location (text_view, &iter, &iter_location);
+          gtk_text_view_get_visible_rect (text_view, &visible_rect);
+
+          is_visible = (iter_location.x + iter_location.width > visible_rect.x &&
+                        iter_location.x < visible_rect.x + visible_rect.width &&
+                        iter_location.y + iter_location.height > visible_rect.y &&
+                        iter_location.y < visible_rect.y + visible_rect.height);
+
+          if (is_visible)
+            {
+              gtk_text_view_buffer_to_window_coords (text_view,
+                                                     GTK_TEXT_WINDOW_WIDGET,
+                                                     iter_location.x,
+                                                     iter_location.y,
+                                                     &iter_location.x,
+                                                     &iter_location.y);
+
+              gtk_menu_popup_at_rect (GTK_MENU (priv->popup_menu),
+                                      gtk_widget_get_window (GTK_WIDGET (text_view)),
+                                      &iter_location,
+                                      GDK_GRAVITY_SOUTH_EAST,
+                                      GDK_GRAVITY_NORTH_WEST,
+                                      info->trigger_event);
+            }
+          else
+            gtk_menu_popup_at_widget (GTK_MENU (priv->popup_menu),
+                                      GTK_WIDGET (text_view),
+                                      GDK_GRAVITY_CENTER,
+                                      GDK_GRAVITY_CENTER,
+                                      info->trigger_event);
+
+          gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
+        }
     }
 
+  g_clear_pointer (&info->trigger_event, gdk_event_free);
   g_object_unref (text_view);
   g_slice_free (PopupInfo, info);
 }
@@ -9560,19 +9509,7 @@ gtk_text_view_do_popup (GtkTextView    *text_view,
    * we get them, then we actually pop up the menu.
    */
   info->text_view = g_object_ref (text_view);
-  
-  if (event)
-    {
-      gdk_event_get_button (event, &info->button);
-      info->time = gdk_event_get_time (event);
-      info->device = gdk_event_get_device (event);
-    }
-  else
-    {
-      info->button = 0;
-      info->time = gtk_get_current_event_time ();
-      info->device = NULL;
-    }
+  info->trigger_event = event ? gdk_event_copy (event) : gtk_get_current_event ();
 
   gtk_clipboard_request_contents (gtk_widget_get_clipboard (GTK_WIDGET (text_view),
                                                            GDK_SELECTION_CLIPBOARD),
diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c
index d77fef3..1077150 100644
--- a/gtk/gtktoolbar.c
+++ b/gtk/gtktoolbar.c
@@ -2618,79 +2618,52 @@ gtk_toolbar_real_style_changed (GtkToolbar     *toolbar,
 }
 
 static void
-menu_position_func (GtkMenu  *menu,
-                   gint     *x,
-                   gint     *y,
-                   gboolean *push_in,
-                   gpointer  user_data)
+show_menu (GtkToolbar     *toolbar,
+          GdkEventButton *event)
 {
-  GtkAllocation allocation;
-  GtkToolbar *toolbar = GTK_TOOLBAR (user_data);
   GtkToolbarPrivate *priv = toolbar->priv;
-  GtkRequisition req;
-  GtkRequisition menu_req;
-  GdkRectangle workarea;
-  GdkMonitor *monitor;
-  GdkDisplay *display;
+  GtkRequisition minimum_size;
 
-  gtk_widget_get_preferred_size (priv->arrow_button,
-                                 &req, NULL);
-  gtk_widget_get_preferred_size (GTK_WIDGET (menu),
-                                 &menu_req, NULL);
-
-  display = gtk_widget_get_display (GTK_WIDGET (menu));
-  monitor = gdk_display_get_monitor_at_window (display,
-                                               gtk_widget_get_window (priv->arrow_button));
-  gdk_monitor_get_workarea (monitor, &workarea);
+  rebuild_menu (toolbar);
 
-  gtk_widget_get_allocation (priv->arrow_button, &allocation);
+  gtk_widget_show_all (GTK_WIDGET (priv->menu));
 
-  gdk_window_get_origin (gtk_button_get_event_window (GTK_BUTTON (priv->arrow_button)), x, y);
-  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-    {
-      if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_LTR)
-       *x += allocation.width - req.width;
-      else
-       *x += req.width - menu_req.width;
-
-      if ((*y + allocation.height + menu_req.height) <= workarea.y + workarea.height)
-       *y += allocation.height;
-      else if ((*y - menu_req.height) >= workarea.y)
-       *y -= menu_req.height;
-      else if (workarea.y + workarea.height - (*y + allocation.height) > *y)
-       *y += allocation.height;
-      else
-       *y -= menu_req.height;
-    }
-  else
+  switch (priv->orientation)
     {
-      if (gtk_widget_get_direction (GTK_WIDGET (toolbar)) == GTK_TEXT_DIR_LTR)
-       *x += allocation.width;
-      else
-       *x -= menu_req.width;
+    case GTK_ORIENTATION_HORIZONTAL:
+      gtk_widget_get_preferred_size (priv->arrow_button, &minimum_size, NULL);
 
-      if (*y + menu_req.height > workarea.y + workarea.height &&
-         *y + allocation.height - workarea.y > workarea.y + workarea.height - *y)
-       *y += allocation.height - menu_req.height;
-    }
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_Y |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    "menu-type-hint", GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU,
+                    "rect-anchor-dx", -minimum_size.width,
+                    NULL);
 
-  *push_in = FALSE;
-}
+      gtk_menu_popup_at_widget (priv->menu,
+                                priv->arrow_button,
+                                GDK_GRAVITY_SOUTH_EAST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                (GdkEvent *) event);
 
-static void
-show_menu (GtkToolbar     *toolbar,
-          GdkEventButton *event)
-{
-  GtkToolbarPrivate *priv = toolbar->priv;
+      break;
 
-  rebuild_menu (toolbar);
+    case GTK_ORIENTATION_VERTICAL:
+      g_object_set (priv->menu,
+                    "anchor-hints", GDK_ANCHOR_FLIP_X |
+                                    GDK_ANCHOR_SLIDE |
+                                    GDK_ANCHOR_RESIZE,
+                    NULL);
 
-  gtk_widget_show_all (GTK_WIDGET (priv->menu));
+      gtk_menu_popup_at_widget (priv->menu,
+                                priv->arrow_button,
+                                GDK_GRAVITY_NORTH_EAST,
+                                GDK_GRAVITY_NORTH_WEST,
+                                (GdkEvent *) event);
 
-  gtk_menu_popup (priv->menu, NULL, NULL,
-                 menu_position_func, toolbar,
-                 event? event->button : 0,
-                 event? event->time : gtk_get_current_event_time());
+      break;
+    }
 }
 
 static void
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 95beb5d..008c197 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -8825,15 +8825,6 @@ popup_menu_detach (GtkWidget *widget,
   GTK_WINDOW (widget)->priv->popup_menu = NULL;
 }
 
-static void
-popup_position_func (GtkMenu   *menu,
-                     gint      *x,
-                     gint      *y,
-                     gboolean  *push_in,
-                     gpointer   user_data)
-{
-}
-
 static GdkWindowState
 gtk_window_get_state (GtkWindow *window)
 {
@@ -9046,17 +9037,7 @@ gtk_window_do_popup_fallback (GtkWindow      *window,
   g_signal_connect (G_OBJECT (menuitem), "activate",
                     G_CALLBACK (close_window_clicked), window);
   gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  if (event)
-    gtk_menu_popup (GTK_MENU (priv->popup_menu),
-                    NULL, NULL,
-                    NULL, NULL,
-                    event->button, event->time);
-  else
-    gtk_menu_popup (GTK_MENU (priv->popup_menu),
-                    NULL, NULL,
-                    popup_position_func, window,
-                    0, gtk_get_current_event_time ());
+  gtk_menu_popup_at_pointer (GTK_MENU (priv->popup_menu), (GdkEvent *) event);
 }
 
 static void


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]