+static gboolean
+pointer_on_menu_widget (GtkMenu *menu,
+ gdouble x_root,
+ gdouble y_root)
+{
+ GtkMenuPrivate *priv = menu->priv;
+ GtkAllocation allocation;
+ gint window_x, window_y;
+
+ gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation);
+ gdk_window_get_position (gtk_widget_get_window (priv->toplevel),
+ &window_x, &window_y);
+
+ if (x_root >= window_x && x_root < window_x + allocation.width &&
+ y_root >= window_y && y_root < window_y + allocation.height)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+gtk_menu_captured_event (GtkWidget *widget,
+ GdkEvent *event)
+{
+ GdkDevice *source_device;
+ gboolean retval = FALSE;
+ GtkMenuPrivate *priv;
+ GtkMenu *menu;
+ gdouble x_root, y_root;
+ guint button;
+ GdkModifierType state;
+
+ menu = GTK_MENU (widget);
+ priv = menu->priv;
+
+ if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0)
+ return retval;
+
+ source_device = gdk_event_get_source_device (event);
+ gdk_event_get_root_coords (event, &x_root, &y_root);
+
+ switch (event->type)
+ {
+ case GDK_TOUCH_BEGIN:
+ case GDK_BUTTON_PRESS:
+ if ((!gdk_event_get_button (event, &button) || button == 1) &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN &&
+ pointer_on_menu_widget (menu, x_root, y_root))
+ {
+ priv->drag_start_y = event->button.y_root;
+ priv->initial_drag_offset = priv->scroll_offset;
+ priv->drag_scroll_started = FALSE;
+ }
+ else
+ priv->drag_start_y = -1;
+
+ priv->drag_already_pressed = TRUE;
+ break;
+ case GDK_TOUCH_END:
+ case GDK_BUTTON_RELEASE:
+ if (priv->drag_scroll_started)
+ {
+ priv->drag_scroll_started = FALSE;
+ priv->drag_start_y = -1;
+ priv->drag_already_pressed = FALSE;
+ retval = TRUE;
+ }
+ break;
+ case GDK_TOUCH_UPDATE:
+ case GDK_MOTION_NOTIFY:
+ 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)
+ {
+ if (pointer_on_menu_widget (menu, x_root, y_root))
+ {
+ priv->drag_start_y = y_root;
+ priv->initial_drag_offset = priv->scroll_offset;
+ priv->drag_scroll_started = FALSE;
+ }
+ else
+ priv->drag_start_y = -1;
+
+ priv->drag_already_pressed = TRUE;
+ }
+
+ if (priv->drag_start_y < 0 && !priv->drag_scroll_started)
+ break;
+
+ if (priv->drag_scroll_started)
+ {
+ gint offset, view_height;
+ GtkBorder arrow_border;
+ gdouble y_diff;
+
+ y_diff = y_root - priv->drag_start_y;
+ offset = priv->initial_drag_offset - y_diff;
+
+ view_height = gdk_window_get_height (gtk_widget_get_window (widget));
+ get_arrows_border (menu, &arrow_border);
+
+ if (priv->upper_arrow_visible)
+ view_height -= arrow_border.top;
+
+ if (priv->lower_arrow_visible)
+ view_height -= arrow_border.bottom;
+
+ 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;
+ }
+ else if (gtk_drag_check_threshold (widget,
+ 0, priv->drag_start_y,
+ 0, y_root))
+ {
+ priv->drag_scroll_started = TRUE;
+ gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
+ retval = TRUE;
+ }
+ }
+ break;
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ if (priv->drag_scroll_started)
+ retval = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return retval;
+}
+