]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmenu.c
Place the search icon in the primary slot of the entry
[~andy/gtk] / gtk / gtkmenu.c
index 1da001e1e56513efcdf9d289832495a8c73ef27f..79bde09ab0686f4fd643d437724058a47320fe66 100644 (file)
@@ -35,7 +35,7 @@
  * #GtkMenuItem in a #GtkMenuBar or popped up by activating a
  * #GtkMenuItem in another #GtkMenu.
  *
- * A #GtkMenu can also be popped up by activating a #GtkOptionMenu.
+ * A #GtkMenu can also be popped up by activating a #GtkComboBox.
  * Other composite widgets such as the #GtkNotebook can pop up a
  * #GtkMenu as well.
  *
@@ -95,6 +95,7 @@
 
 #include "gtkaccellabel.h"
 #include "gtkaccelmap.h"
+#include "gtkadjustment.h"
 #include "gtkbindings.h"
 #include "gtkcheckmenuitem.h"
 #include "gtkmain.h"
 #include "gtkscrollbar.h"
 #include "gtksettings.h"
 #include "gtkprivate.h"
+#include "gtkwidgetpath.h"
 #include "gtkwidgetprivate.h"
 #include "gtkdnd.h"
 #include "gtkintl.h"
@@ -234,8 +236,6 @@ static gboolean gtk_menu_captured_event    (GtkWidget        *widget,
 static void     gtk_menu_stop_scrolling         (GtkMenu  *menu);
 static void     gtk_menu_remove_scroll_timeout  (GtkMenu  *menu);
 static gboolean gtk_menu_scroll_timeout         (gpointer  data);
-static gboolean gtk_menu_scroll_timeout_initial (gpointer  data);
-static void     gtk_menu_start_scrolling        (GtkMenu  *menu);
 
 static void     gtk_menu_scroll_item_visible (GtkMenuShell    *menu_shell,
                                               GtkWidget       *menu_item);
@@ -641,15 +641,6 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                      -1, G_MAXINT, -1,
                                                      GTK_PARAM_READWRITE));
 
-  gtk_widget_class_install_style_property (widget_class,
-                                           g_param_spec_int ("vertical-padding",
-                                                             P_("Vertical Padding"),
-                                                             P_("Extra space at the top and bottom of the menu"),
-                                                             0,
-                                                             G_MAXINT,
-                                                             1,
-                                                             GTK_PARAM_READABLE));
-
   /**
    * GtkMenu:reserve-toggle-size:
    *
@@ -671,6 +662,15 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                          TRUE,
                                                          GTK_PARAM_READWRITE));
 
+  /**
+   * GtkMenu:horizontal-padding:
+   *
+   * Extra space at the left and right edges of the menu.
+   *
+   * Deprecated: 3.8: use the standard padding CSS property (through objects
+   *   like #GtkStyleContext and #GtkCssProvider); the value of this style
+   *   property is ignored.
+   */
   gtk_widget_class_install_style_property (widget_class,
                                            g_param_spec_int ("horizontal-padding",
                                                              P_("Horizontal Padding"),
@@ -678,7 +678,27 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                              0,
                                                              G_MAXINT,
                                                              0,
-                                                             GTK_PARAM_READABLE));
+                                                             GTK_PARAM_READABLE |
+                                                             G_PARAM_DEPRECATED));
+
+  /**
+   * GtkMenu:vertical-padding:
+   *
+   * Extra space at the top and bottom of the menu.
+   *
+   * Deprecated: 3.8: use the standard padding CSS property (through objects
+   *   like #GtkStyleContext and #GtkCssProvider); the value of this style
+   *   property is ignored.
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_int ("vertical-padding",
+                                                             P_("Vertical Padding"),
+                                                             P_("Extra space at the top and bottom of the menu"),
+                                                             0,
+                                                             G_MAXINT,
+                                                             1,
+                                                             GTK_PARAM_READABLE |
+                                                             G_PARAM_DEPRECATED));
 
   gtk_widget_class_install_style_property (widget_class,
                                            g_param_spec_int ("vertical-offset",
@@ -753,7 +773,7 @@ gtk_menu_class_init (GtkMenuClass *class)
                                                                GTK_PARAM_READWRITE));
 
  /**
-  * GtkMenu:arrow-scaling
+  * GtkMenu:arrow-scaling:
   *
   * Arbitrary constant to scale down the size of the scroll arrow.
   *
@@ -1214,12 +1234,11 @@ gtk_menu_attach_to_widget (GtkMenu           *menu,
   g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
                           (GDestroyNotify) g_list_free);
 
-  if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0)
-    gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE);
-
   /* Attach the widget to the toplevel window. */
   gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget);
 
+  _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
+
   /* Fallback title for menu comes from attach widget */
   gtk_menu_update_title (menu);
 
@@ -1294,9 +1313,12 @@ gtk_menu_detach (GtkMenu *menu)
 
   g_slice_free (GtkMenuAttachData, data);
 
+  _gtk_widget_update_parent_muxer (GTK_WIDGET (menu));
+
   /* Fallback title for menu comes from attach widget */
   gtk_menu_update_title (menu);
 
+  g_object_notify (G_OBJECT (menu), "attach-widget");
   g_object_unref (menu);
 }
 
@@ -1409,6 +1431,7 @@ popup_grab_on_window (GdkWindow *window,
   if (pointer &&
       gdk_device_grab (pointer, window,
                        GDK_OWNERSHIP_WINDOW, TRUE,
+                       GDK_SMOOTH_SCROLL_MASK |
                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                        GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
                        GDK_POINTER_MOTION_MASK,
@@ -1459,7 +1482,6 @@ popup_grab_on_window (GdkWindow *window,
  * be used instead.
  *
  * Since: 3.0
- * Rename to: gtk_menu_popup
  */
 void
 gtk_menu_popup_for_device (GtkMenu             *menu,
@@ -1713,13 +1735,13 @@ gtk_menu_popup_for_device (GtkMenu             *menu,
 }
 
 /**
- * gtk_menu_popup: (skip)
+ * gtk_menu_popup:
  * @menu: a #GtkMenu
  * @parent_menu_shell: (allow-none): the menu shell containing the
  *     triggering menu item, or %NULL
  * @parent_menu_item: (allow-none): the menu item whose activation
  *     triggered the popup, or %NULL
- * @func: (allow-none): a user supplied function used to position
+ * @func: (scope async) (allow-none): 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.
@@ -1863,7 +1885,7 @@ gtk_menu_popdown (GtkMenu *menu)
  * @menu: a #GtkMenu
  *
  * Returns the selected menu item from the menu.  This is used by the
- * #GtkOptionMenu.
+ * #GtkComboBox.
  *
  * Returns: (transfer none): the #GtkMenuItem that was last selected
  *          in the menu.  If a selection has not yet been made, the
@@ -1908,7 +1930,7 @@ gtk_menu_get_active (GtkMenu *menu)
  *         from 0 to n-1
  *
  * Selects the specified menu item within the menu.  This is used by
- * the #GtkOptionMenu and should not be used by anyone else.
+ * the #GtkComboBox and should not be used by anyone else.
  */
 void
 gtk_menu_set_active (GtkMenu *menu,
@@ -2000,7 +2022,7 @@ gtk_menu_real_can_activate_accel (GtkWidget *widget,
 }
 
 /**
- * gtk_menu_set_accel_path
+ * gtk_menu_set_accel_path:
  * @menu:       a valid #GtkMenu
  * @accel_path: (allow-none): a valid accelerator path
  *
@@ -2043,7 +2065,7 @@ gtk_menu_set_accel_path (GtkMenu     *menu,
 }
 
 /**
- * gtk_menu_get_accel_path
+ * gtk_menu_get_accel_path:
  * @menu: a valid #GtkMenu
  *
  * Retrieves the accelerator path set on the menu.
@@ -2505,8 +2527,6 @@ gtk_menu_realize (GtkWidget *widget)
   gint border_width;
   GtkWidget *child;
   GList *children;
-  guint vertical_padding;
-  guint horizontal_padding;
   GtkBorder arrow_border, padding;
 
   g_return_if_fail (GTK_IS_MENU (widget));
@@ -2531,25 +2551,20 @@ gtk_menu_realize (GtkWidget *widget)
   window = gdk_window_new (gtk_widget_get_parent_window (widget),
                            &attributes, attributes_mask);
   gtk_widget_set_window (widget, window);
-  gdk_window_set_user_data (window, widget);
+  gtk_widget_register_window (widget, window);
 
   get_menu_padding (widget, &padding);
   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
   context = gtk_widget_get_style_context (widget);
 
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "vertical-padding", &vertical_padding,
-                        "horizontal-padding", &horizontal_padding,
-                        NULL);
-
   gtk_widget_get_allocation (widget, &allocation);
 
-  attributes.x = border_width + padding.left + horizontal_padding;
-  attributes.y = border_width + padding.top + vertical_padding;
+  attributes.x = border_width + padding.left;
+  attributes.y = border_width + padding.top;
   attributes.width = allocation.width -
-    (2 * (border_width + horizontal_padding)) - padding.left - padding.right;
+    (2 * border_width) - padding.left - padding.right;
   attributes.height = allocation.height -
-    (2 * (border_width + vertical_padding)) - padding.top - padding.bottom;
+    (2 * border_width) - padding.top - padding.bottom;
 
   get_arrows_border (menu, &arrow_border);
   attributes.y += arrow_border.top;
@@ -2561,15 +2576,15 @@ gtk_menu_realize (GtkWidget *widget)
 
   priv->view_window = gdk_window_new (window,
                                       &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->view_window, menu);
+  gtk_widget_register_window (widget, priv->view_window);
 
   gtk_widget_get_allocation (widget, &allocation);
 
   attributes.x = 0;
   attributes.y = 0;
-  attributes.width = allocation.width + (2 * (border_width + horizontal_padding)) +
+  attributes.width = allocation.width + (2 * border_width) +
     padding.left + padding.right;
-  attributes.height = priv->requested_height - (2 * (border_width + vertical_padding)) +
+  attributes.height = priv->requested_height - (2 * border_width) +
     padding.top + padding.bottom;
 
   attributes.width = MAX (1, attributes.width);
@@ -2577,7 +2592,7 @@ gtk_menu_realize (GtkWidget *widget)
 
   priv->bin_window = gdk_window_new (priv->view_window,
                                      &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->bin_window, menu);
+  gtk_widget_register_window (widget, priv->bin_window);
 
   children = GTK_MENU_SHELL (menu)->priv->children;
   while (children)
@@ -2633,7 +2648,7 @@ menu_grab_transfer_window_get (GtkMenu *menu)
 
       window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)),
                                &attributes, attributes_mask);
-      gdk_window_set_user_data (window, menu);
+      gtk_widget_register_window (GTK_WIDGET (menu), window);
 
       gdk_window_show (window);
 
@@ -2649,7 +2664,7 @@ menu_grab_transfer_window_destroy (GtkMenu *menu)
   GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window");
   if (window)
     {
-      gdk_window_set_user_data (window, NULL);
+      gtk_widget_unregister_window (GTK_WIDGET (menu), window);
       gdk_window_destroy (window);
       g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL);
     }
@@ -2663,11 +2678,11 @@ gtk_menu_unrealize (GtkWidget *widget)
 
   menu_grab_transfer_window_destroy (menu);
 
-  gdk_window_set_user_data (priv->view_window, NULL);
+  gtk_widget_unregister_window (widget, priv->view_window);
   gdk_window_destroy (priv->view_window);
   priv->view_window = NULL;
 
-  gdk_window_set_user_data (priv->bin_window, NULL);
+  gtk_widget_unregister_window (widget, priv->bin_window);
   gdk_window_destroy (priv->bin_window);
   priv->bin_window = NULL;
 
@@ -2685,7 +2700,6 @@ calculate_line_heights (GtkMenu *menu,
   GtkMenuShell   *menu_shell;
   GtkWidget      *child, *widget;
   GList          *children;
-  guint           horizontal_padding;
   guint           border_width;
   guint           n_columns;
   gint            n_heights;
@@ -2703,13 +2717,10 @@ calculate_line_heights (GtkMenu *menu,
   n_columns    = gtk_menu_get_n_columns (menu);
   avail_width  = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns;
 
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "horizontal-padding", &horizontal_padding,
-                        NULL);
   get_menu_padding (widget, &padding);
 
   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
-  avail_width -= (border_width + horizontal_padding) * 2 + padding.left + padding.right;
+  avail_width -= (border_width) * 2 + padding.left + padding.right;
 
   for (children = menu_shell->priv->children; children; children = children->next)
     {
@@ -2765,8 +2776,6 @@ gtk_menu_size_allocate (GtkWidget     *widget,
   gint x, y, i;
   gint width, height;
   guint border_width;
-  guint vertical_padding;
-  guint horizontal_padding;
   GtkBorder padding;
 
   g_return_if_fail (GTK_IS_MENU (widget));
@@ -2778,11 +2787,6 @@ gtk_menu_size_allocate (GtkWidget     *widget,
 
   gtk_widget_set_allocation (widget, allocation);
 
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "vertical-padding", &vertical_padding,
-                        "horizontal-padding", &horizontal_padding,
-                        NULL);
-
   get_menu_padding (widget, &padding);
   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
 
@@ -2793,16 +2797,15 @@ gtk_menu_size_allocate (GtkWidget     *widget,
                                                  NULL);
 
   /* refresh our cached height request */
-  priv->requested_height = (2 * (border_width + vertical_padding)) +
-    padding.top + padding.bottom;
+  priv->requested_height = (2 * border_width) + padding.top + padding.bottom;
   for (i = 0; i < priv->heights_length; i++)
     priv->requested_height += priv->heights[i];
 
-  x = border_width + padding.left + horizontal_padding;
-  y = border_width + padding.top + vertical_padding;
-  width = allocation->width - (2 * (border_width + horizontal_padding)) -
+  x = border_width + padding.left;
+  y = border_width + padding.top;
+  width = allocation->width - (2 * border_width) -
     padding.left - padding.right;
-  height = allocation->height - (2 * (border_width + vertical_padding)) -
+  height = allocation->height - (2 * border_width) -
     padding.top - padding.bottom;
 
   if (menu_shell->priv->active)
@@ -2932,22 +2935,18 @@ get_arrows_visible_area (GtkMenu      *menu,
   GtkArrowPlacement arrow_placement;
   GtkWidget *widget = GTK_WIDGET (menu);
   guint border_width;
-  guint vertical_padding;
-  guint horizontal_padding;
   gint scroll_arrow_height;
   GtkBorder menu_padding;
 
   gtk_widget_style_get (widget,
-                        "vertical-padding", &vertical_padding,
-                        "horizontal-padding", &horizontal_padding,
                         "scroll-arrow-vlength", &scroll_arrow_height,
                         "arrow-placement", &arrow_placement,
                         NULL);
 
   get_menu_padding (widget, &menu_padding);
   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-  border->x = border_width + menu_padding.left + horizontal_padding;
-  border->y = border_width + menu_padding.top + vertical_padding;
+  border->x = border_width + menu_padding.left;
+  border->y = border_width + menu_padding.top;
   border->width = gdk_window_get_width (gtk_widget_get_window (widget));
   border->height = gdk_window_get_height (gtk_widget_get_window (widget));
 
@@ -3132,7 +3131,6 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
   GList          *children;
   guint           max_toggle_size;
   guint           max_accel_width;
-  guint           horizontal_padding;
   guint           border_width;
   gint            child_min, child_nat;
   gint            min_width, nat_width;
@@ -3191,19 +3189,19 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
       !priv->no_toggle_size)
     {
       GtkStyleContext *context;
-      GtkWidgetPath *menu_path, *check_path;
+      GtkWidgetPath *check_path;
       guint toggle_spacing;
       guint indicator_size;
 
-      context = gtk_widget_get_style_context (widget);
-      menu_path = gtk_widget_path_copy (gtk_style_context_get_path (context));
+      context = gtk_style_context_new ();
 
       /* Create a GtkCheckMenuItem path, only to query indicator spacing */
-      check_path = gtk_widget_path_copy (menu_path);
+      check_path = _gtk_widget_create_path (widget);
       gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM);
 
       gtk_style_context_set_path (context, check_path);
       gtk_widget_path_free (check_path);
+      gtk_style_context_set_screen (context, gtk_widget_get_screen (widget));
 
       gtk_style_context_get_style (context,
                                    "toggle-spacing", &toggle_spacing,
@@ -3212,9 +3210,7 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
 
       max_toggle_size = indicator_size + toggle_spacing;
 
-      /* Restore real widget path */
-      gtk_style_context_set_path (context, menu_path);
-      gtk_widget_path_free (menu_path);
+      g_object_unref (context);
     }
 
   min_width += 2 * max_toggle_size + max_accel_width;
@@ -3223,16 +3219,10 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
   nat_width += 2 * max_toggle_size + max_accel_width;
   nat_width *= gtk_menu_get_n_columns (menu);
 
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "horizontal-padding", &horizontal_padding,
-                        NULL);
-
   get_menu_padding (widget, &padding);
   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
-  min_width   += (2 * (border_width + horizontal_padding)) +
-    padding.left + padding.right;
-  nat_width   += (2 * (border_width + horizontal_padding)) +
-    padding.left + padding.right;
+  min_width   += (2 * border_width) + padding.left + padding.right;
+  nat_width   += (2 * border_width) + padding.left + padding.right;
 
   priv->toggle_size = max_toggle_size;
   priv->accel_size  = max_accel_width;
@@ -3274,16 +3264,14 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget,
   GtkMenu        *menu = GTK_MENU (widget);
   GtkMenuPrivate *priv = menu->priv;
   guint          *min_heights, *nat_heights;
-  guint           vertical_padding, border_width;
+  guint           border_width;
   gint            n_heights, i;
   gint            min_height, nat_height;
 
-  gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, NULL);
   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
   get_menu_padding (widget, &padding);
 
-  min_height = nat_height = (border_width + vertical_padding) * 2 +
-         padding.top + padding.bottom;
+  min_height = nat_height = (2 * border_width) + padding.top + padding.bottom;
 
   n_heights =
     calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
@@ -3360,9 +3348,17 @@ static gboolean
 gtk_menu_button_press (GtkWidget      *widget,
                        GdkEventButton *event)
 {
+  GdkDevice *source_device;
+  GtkWidget *event_widget;
+  GtkMenu *menu;
+
   if (event->type != GDK_BUTTON_PRESS)
     return FALSE;
 
+  source_device = gdk_event_get_source_device ((GdkEvent *) event);
+  event_widget = gtk_get_event_widget ((GdkEvent *) event);
+  menu = GTK_MENU (widget);
+
   /*  Don't pass down to menu shell if a non-menuitem part of the menu
    *  was clicked. The check for the event_widget being a GtkMenuShell
    *  works because we have the pointer grabbed on menu_shell->window
@@ -3370,10 +3366,16 @@ gtk_menu_button_press (GtkWidget      *widget,
    *  the menu or on its border are delivered relative to
    *  menu_shell->window.
    */
-  if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) &&
+  if (GTK_IS_MENU_SHELL (event_widget) &&
       pointer_in_menu_window (widget, event->x_root, event->y_root))
     return TRUE;
 
+  if (GTK_IS_MENU_ITEM (event_widget) &&
+      gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
+      GTK_MENU_ITEM (event_widget)->priv->submenu != NULL &&
+      !gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu))
+    menu->priv->ignore_button_release = TRUE;
+
   return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event);
 }
 
@@ -3806,90 +3808,17 @@ gtk_menu_scroll_by (GtkMenu *menu,
     gtk_menu_scroll_to (menu, offset);
 }
 
-static void
-gtk_menu_do_timeout_scroll (GtkMenu  *menu,
-                            gboolean  touchscreen_mode)
-{
-  GtkMenuPrivate *priv = menu->priv;
-  gboolean upper_visible;
-  gboolean lower_visible;
-
-  upper_visible = priv->upper_arrow_visible;
-  lower_visible = priv->lower_arrow_visible;
-
-  gtk_menu_scroll_by (menu, priv->scroll_step);
-
-  if (touchscreen_mode &&
-      (upper_visible != priv->upper_arrow_visible ||
-       lower_visible != priv->lower_arrow_visible))
-    {
-      /* We are about to hide a scroll arrow while the mouse is pressed,
-       * this would cause the uncovered menu item to be activated on button
-       * release. Therefore we need to ignore button release here
-       */
-      GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE;
-      priv->ignore_button_release = TRUE;
-    }
-}
-
 static gboolean
 gtk_menu_scroll_timeout (gpointer data)
 {
   GtkMenu  *menu;
-  gboolean  touchscreen_mode;
 
   menu = GTK_MENU (data);
-
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
-                "gtk-touchscreen-mode", &touchscreen_mode,
-                NULL);
-
-  gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
+  gtk_menu_scroll_by (menu, menu->priv->scroll_step);
 
   return TRUE;
 }
 
-static gboolean
-gtk_menu_scroll_timeout_initial (gpointer data)
-{
-  GtkMenu  *menu;
-  guint     timeout;
-  gboolean  touchscreen_mode;
-
-  menu = GTK_MENU (data);
-
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
-                "gtk-timeout-repeat", &timeout,
-                "gtk-touchscreen-mode", &touchscreen_mode,
-                NULL);
-
-  gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
-
-  gtk_menu_remove_scroll_timeout (menu);
-
-  menu->priv->scroll_timeout =
-    gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu);
-
-  return FALSE;
-}
-
-static void
-gtk_menu_start_scrolling (GtkMenu *menu)
-{
-  guint    timeout;
-  gboolean touchscreen_mode;
-
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
-                "gtk-timeout-repeat", &timeout,
-                "gtk-touchscreen-mode", &touchscreen_mode,
-                NULL);
-
-  gtk_menu_do_timeout_scroll (menu, touchscreen_mode);
-
-  menu->priv->scroll_timeout =
-    gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu);
-}
-
 static gboolean
 gtk_menu_scroll (GtkWidget      *widget,
                  GdkEventScroll *event)
@@ -3906,6 +3835,9 @@ gtk_menu_scroll (GtkWidget      *widget,
     case GDK_SCROLL_UP:
       gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2);
       break;
+    case GDK_SCROLL_SMOOTH:
+      gtk_menu_scroll_by (menu, event->delta_y);
+      break;
     }
 
   return TRUE;
@@ -3921,7 +3853,6 @@ get_arrows_sensitive_area (GtkMenu      *menu,
   GdkWindow *window;
   gint width, height;
   guint border;
-  guint vertical_padding;
   gint win_x, win_y;
   gint scroll_arrow_height;
   GtkBorder padding;
@@ -3931,12 +3862,11 @@ get_arrows_sensitive_area (GtkMenu      *menu,
   height = gdk_window_get_height (window);
 
   gtk_widget_style_get (widget,
-                        "vertical-padding", &vertical_padding,
                         "scroll-arrow-vlength", &scroll_arrow_height,
                         "arrow-placement", &arrow_placement,
                         NULL);
 
-  border = gtk_container_get_border_width (GTK_CONTAINER (menu)) + vertical_padding;
+  border = gtk_container_get_border_width (GTK_CONTAINER (menu));
   get_menu_padding (widget, &padding);
 
   gdk_window_get_position (window, &win_x, &win_y);
@@ -4013,14 +3943,9 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
   gboolean in_arrow;
   gboolean scroll_fast = FALSE;
   gint top_x, top_y;
-  gboolean touchscreen_mode;
 
   menu_shell = GTK_MENU_SHELL (menu);
 
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
-                "gtk-touchscreen-mode", &touchscreen_mode,
-                NULL);
-
   gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
                            &top_x, &top_y);
   x -= top_x;
@@ -4038,82 +3963,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
       in_arrow = TRUE;
     }
 
-  if (touchscreen_mode)
-    priv->upper_arrow_prelight = in_arrow;
-
   if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
     {
       gboolean arrow_pressed = FALSE;
 
       if (priv->upper_arrow_visible && !priv->tearoff_active)
         {
-          if (touchscreen_mode)
+          scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
+
+          if (enter && in_arrow &&
+              (!priv->upper_arrow_prelight ||
+               priv->scroll_fast != scroll_fast))
             {
-              if (enter && priv->upper_arrow_prelight)
-                {
-                  if (priv->scroll_timeout == 0)
-                    {
-                      /* Deselect the active item so that
-                       * any submenus are popped down
-                       */
-                      gtk_menu_shell_deselect (menu_shell);
-
-                      gtk_menu_remove_scroll_timeout (menu);
-                      priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */
-
-                      if (!motion)
-                        {
-                          /* Only do stuff on click. */
-                          gtk_menu_start_scrolling (menu);
-                          arrow_pressed = TRUE;
-                        }
-                    }
-                  else
-                    {
-                      arrow_pressed = TRUE;
-                    }
-                }
-              else if (!enter)
-                {
-                  gtk_menu_stop_scrolling (menu);
-                }
+              priv->upper_arrow_prelight = TRUE;
+              priv->scroll_fast = scroll_fast;
+
+              /* Deselect the active item so that
+               * any submenus are popped down
+               */
+              gtk_menu_shell_deselect (menu_shell);
+
+              gtk_menu_remove_scroll_timeout (menu);
+              priv->scroll_step = scroll_fast
+                                    ? -MENU_SCROLL_STEP2
+                                    : -MENU_SCROLL_STEP1;
+
+              priv->scroll_timeout =
+                gdk_threads_add_timeout (scroll_fast
+                                           ? MENU_SCROLL_TIMEOUT2
+                                           : MENU_SCROLL_TIMEOUT1,
+                                         gtk_menu_scroll_timeout, menu);
             }
-          else /* !touchscreen_mode */
+          else if (!enter && !in_arrow && priv->upper_arrow_prelight)
             {
-              scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
-
-              if (enter && in_arrow &&
-                  (!priv->upper_arrow_prelight ||
-                   priv->scroll_fast != scroll_fast))
-                {
-                  priv->upper_arrow_prelight = TRUE;
-                  priv->scroll_fast = scroll_fast;
-
-                  /* Deselect the active item so that
-                   * any submenus are popped down
-                   */
-                  gtk_menu_shell_deselect (menu_shell);
-
-                  gtk_menu_remove_scroll_timeout (menu);
-                  priv->scroll_step = scroll_fast
-                                        ? -MENU_SCROLL_STEP2
-                                        : -MENU_SCROLL_STEP1;
-
-                  priv->scroll_timeout =
-                    gdk_threads_add_timeout (scroll_fast
-                                               ? MENU_SCROLL_TIMEOUT2
-                                               : MENU_SCROLL_TIMEOUT1,
-                                             gtk_menu_scroll_timeout, menu);
-                }
-              else if (!enter && !in_arrow && priv->upper_arrow_prelight)
-                {
-                  gtk_menu_stop_scrolling (menu);
-                }
+              gtk_menu_stop_scrolling (menu);
             }
         }
 
-      /*  gtk_menu_start_scrolling() might have hit the top of the
-       *  menu, so check if the button isn't insensitive before
+      /*  check if the button isn't insensitive before
        *  changing it to something else.
        */
       if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4148,82 +4035,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
       in_arrow = TRUE;
     }
 
-  if (touchscreen_mode)
-    priv->lower_arrow_prelight = in_arrow;
-
   if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
     {
       gboolean arrow_pressed = FALSE;
 
       if (priv->lower_arrow_visible && !priv->tearoff_active)
         {
-          if (touchscreen_mode)
+          scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
+
+          if (enter && in_arrow &&
+              (!priv->lower_arrow_prelight ||
+               priv->scroll_fast != scroll_fast))
             {
-              if (enter && priv->lower_arrow_prelight)
-                {
-                  if (priv->scroll_timeout == 0)
-                    {
-                      /* Deselect the active item so that
-                       * any submenus are popped down
-                       */
-                      gtk_menu_shell_deselect (menu_shell);
-
-                      gtk_menu_remove_scroll_timeout (menu);
-                      priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */
-
-                      if (!motion)
-                        {
-                          /* Only do stuff on click. */
-                          gtk_menu_start_scrolling (menu);
-                          arrow_pressed = TRUE;
-                        }
-                    }
-                  else
-                    {
-                      arrow_pressed = TRUE;
-                    }
-                }
-              else if (!enter)
-                {
-                  gtk_menu_stop_scrolling (menu);
-                }
+              priv->lower_arrow_prelight = TRUE;
+              priv->scroll_fast = scroll_fast;
+
+              /* Deselect the active item so that
+               * any submenus are popped down
+               */
+              gtk_menu_shell_deselect (menu_shell);
+
+              gtk_menu_remove_scroll_timeout (menu);
+              priv->scroll_step = scroll_fast
+                                    ? MENU_SCROLL_STEP2
+                                    : MENU_SCROLL_STEP1;
+
+              priv->scroll_timeout =
+                gdk_threads_add_timeout (scroll_fast
+                                           ? MENU_SCROLL_TIMEOUT2
+                                           : MENU_SCROLL_TIMEOUT1,
+                                         gtk_menu_scroll_timeout, menu);
             }
-          else /* !touchscreen_mode */
+          else if (!enter && !in_arrow && priv->lower_arrow_prelight)
             {
-              scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
-
-              if (enter && in_arrow &&
-                  (!priv->lower_arrow_prelight ||
-                   priv->scroll_fast != scroll_fast))
-                {
-                  priv->lower_arrow_prelight = TRUE;
-                  priv->scroll_fast = scroll_fast;
-
-                  /* Deselect the active item so that
-                   * any submenus are popped down
-                   */
-                  gtk_menu_shell_deselect (menu_shell);
-
-                  gtk_menu_remove_scroll_timeout (menu);
-                  priv->scroll_step = scroll_fast
-                                        ? MENU_SCROLL_STEP2
-                                        : MENU_SCROLL_STEP1;
-
-                  priv->scroll_timeout =
-                    gdk_threads_add_timeout (scroll_fast
-                                               ? MENU_SCROLL_TIMEOUT2
-                                               : MENU_SCROLL_TIMEOUT1,
-                                             gtk_menu_scroll_timeout, menu);
-                }
-              else if (!enter && !in_arrow && priv->lower_arrow_prelight)
-                {
-                  gtk_menu_stop_scrolling (menu);
-                }
+              gtk_menu_stop_scrolling (menu);
             }
         }
 
-      /*  gtk_menu_start_scrolling() might have hit the bottom of the
-       *  menu, so check if the button isn't insensitive before
+      /*  check if the button isn't insensitive before
        *  changing it to something else.
        */
       if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
@@ -4417,7 +4266,7 @@ gtk_menu_captured_event (GtkWidget *widget,
   menu = GTK_MENU (widget);
   priv = menu->priv;
 
-  if (!priv->upper_arrow_visible && !priv->lower_arrow_visible)
+  if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
     return retval;
 
   source_device = gdk_event_get_source_device (event);
@@ -4452,8 +4301,7 @@ gtk_menu_captured_event (GtkWidget *widget,
       break;
     case GDK_TOUCH_UPDATE:
     case GDK_MOTION_NOTIFY:
-      if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) 
-!= 0) &&
+      if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) != 0) &&
           gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
         {
           if (!priv->drag_already_pressed)
@@ -4491,7 +4339,10 @@ gtk_menu_captured_event (GtkWidget *widget,
               if (priv->lower_arrow_visible)
                 view_height -= arrow_border.bottom;
 
-              offset = CLAMP (offset, 0, priv->requested_height - view_height);
+              offset = CLAMP (offset,
+                              MIN (priv->scroll_offset, 0),
+                              MAX (priv->scroll_offset, priv->requested_height - view_height));
+
               gtk_menu_scroll_to (menu, offset);
 
               retval = TRUE;
@@ -4960,19 +4811,10 @@ static void
 gtk_menu_stop_scrolling (GtkMenu *menu)
 {
   GtkMenuPrivate *priv = menu->priv;
-  gboolean touchscreen_mode;
 
   gtk_menu_remove_scroll_timeout (menu);
-
-  g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
-                "gtk-touchscreen-mode", &touchscreen_mode,
-                NULL);
-
-  if (!touchscreen_mode)
-    {
-      priv->upper_arrow_prelight = FALSE;
-      priv->lower_arrow_prelight = FALSE;
-    }
+  priv->upper_arrow_prelight = FALSE;
+  priv->lower_arrow_prelight = FALSE;
 }
 
 static void
@@ -4986,8 +4828,6 @@ gtk_menu_scroll_to (GtkMenu *menu,
   gint view_width, view_height;
   gint border_width;
   gint menu_height;
-  guint vertical_padding;
-  guint horizontal_padding;
   gboolean double_arrows;
 
   widget = GTK_WIDGET (menu);
@@ -4999,23 +4839,17 @@ gtk_menu_scroll_to (GtkMenu *menu,
   view_width = gtk_widget_get_allocated_width (widget);
   view_height = gtk_widget_get_allocated_height (widget);
 
-  gtk_widget_style_get (GTK_WIDGET (menu),
-                        "vertical-padding", &vertical_padding,
-                        "horizontal-padding", &horizontal_padding,
-                        NULL);
-
   get_menu_padding (widget, &padding);
   double_arrows = get_double_arrows (menu);
 
   border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
 
-  view_width -= (2 * (border_width + horizontal_padding)) + padding.left + padding.right;
-  view_height -= (2 * (border_width + vertical_padding)) + padding.top + padding.bottom;
-  menu_height = priv->requested_height - (2 * (border_width + vertical_padding)) -
-    padding.top - padding.bottom;
+  view_width -= (2 * border_width) + padding.left + padding.right;
+  view_height -= (2 * border_width) + padding.top + padding.bottom;
+  menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom;
 
-  x = border_width + padding.left + horizontal_padding;
-  y = border_width + padding.top + vertical_padding;
+  x = border_width + padding.left;
+  y = border_width + padding.top;
 
   if (double_arrows && !priv->tearoff_active)
     {
@@ -5146,10 +4980,10 @@ gtk_menu_scroll_to (GtkMenu *menu,
 
   /* Scroll the menu: */
   if (gtk_widget_get_realized (widget))
-    gdk_window_move (priv->bin_window, 0, -offset);
-
-  if (gtk_widget_get_realized (widget))
-    gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
+    {
+      gdk_window_move (priv->bin_window, 0, -offset);
+      gdk_window_move_resize (priv->view_window, x, y, view_width, view_height);
+    }
 
   priv->scroll_offset = offset;
 }
@@ -5212,23 +5046,18 @@ gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
   if (compute_child_offset (menu, menu_item,
                             &child_offset, &child_height, &last_child))
     {
-      guint vertical_padding;
       gboolean double_arrows;
       GtkBorder padding;
 
       y = priv->scroll_offset;
       height = gdk_window_get_height (gtk_widget_get_window (widget));
 
-      gtk_widget_style_get (widget,
-                            "vertical-padding", &vertical_padding,
-                            NULL);
-
       double_arrows = get_double_arrows (menu);
       get_menu_padding (widget, &padding);
 
       height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) +
-                padding.top + padding.bottom +
-                2 * vertical_padding;
+        padding.top + padding.bottom;
+
       if (child_offset < y)
         {
           /* Ignore the enter event we might get if the pointer
@@ -5676,15 +5505,13 @@ static gint
 get_menu_height (GtkMenu *menu)
 {
   GtkMenuPrivate *priv = menu->priv;
-  GtkAllocation allocation;
   GtkWidget *widget = GTK_WIDGET (menu);
   GtkBorder padding;
   gint height;
 
-  gtk_widget_get_allocation (widget, &allocation);
   get_menu_padding (widget, &padding);
 
-  height = allocation.height;
+  height = priv->requested_height;
   height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) +
     padding.top + padding.bottom;
 
@@ -5748,6 +5575,7 @@ gtk_menu_real_move_scroll (GtkMenu       *menu,
             GtkWidget *new_child;
             gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active;
             GtkBorder arrow_border;
+
             get_arrows_border (menu, &arrow_border);
 
             if (priv->scroll_offset != old_offset)
@@ -5764,13 +5592,11 @@ gtk_menu_real_move_scroll (GtkMenu       *menu,
     case GTK_SCROLL_START:
       /* Ignore the enter event we might get if the pointer is on the menu */
       menu_shell->priv->ignore_enter = TRUE;
-      gtk_menu_scroll_to (menu, 0);
       gtk_menu_shell_select_first (menu_shell, TRUE);
       break;
     case GTK_SCROLL_END:
       /* Ignore the enter event we might get if the pointer is on the menu */
       menu_shell->priv->ignore_enter = TRUE;
-      gtk_menu_scroll_to (menu, end_position - page_size);
       _gtk_menu_shell_select_last (menu_shell, TRUE);
       break;
     default:
@@ -5928,3 +5754,32 @@ gtk_menu_get_reserve_toggle_size (GtkMenu *menu)
 
   return !menu->priv->no_toggle_size;
 }
+
+/**
+ * gtk_menu_new_from_model:
+ * @model: a #GMenuModel
+ *
+ * Creates a #GtkMenu and populates it with menu items and
+ * submenus according to @model.
+ *
+ * The created menu items are connected to actions found in the
+ * #GtkApplicationWindow to which the menu belongs - typically
+ * by means of being attached to a widget (see gtk_menu_attach_to_widget())
+ * that is contained within the #GtkApplicationWindows widget hierarchy.
+ *
+ * Returns: a new #GtkMenu
+ *
+ * Since: 3.4
+ */
+GtkWidget *
+gtk_menu_new_from_model (GMenuModel *model)
+{
+  GtkWidget *menu;
+
+  g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL);
+
+  menu = gtk_menu_new ();
+  gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), model, NULL, TRUE);
+
+  return menu;
+}