From ab426801023babba7513d9e27a4dbdd44bdbc318 Mon Sep 17 00:00:00 2001
From: Michael Natterer <mitch@gimp.org>
Date: Tue, 8 May 2012 14:11:50 +0200
Subject: [PATCH 08/68] Smooth scrolling

---
 gdk/gdkevents.c               |   32 +++++++++++++++++
 gdk/gdkevents.h               |    6 ++++
 gdk/gdkwindow.c               |    3 ++
 gdk/quartz/gdkevents-quartz.c |   80 +++++++++++++++++++++++++++++++----------
 gtk/gtkrange.c                |   51 +++++++++++++++++++-------
 gtk/gtkrange.h                |    4 +--
 gtk/gtkscrolledwindow.c       |   74 ++++++++++++++++++++++++++++++--------
 7 files changed, 203 insertions(+), 47 deletions(-)

diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c
index 53833a0..0f8bba2 100644
--- a/gdk/gdkevents.c
+++ b/gdk/gdkevents.c
@@ -392,6 +392,8 @@ gdk_event_new (GdkEventType type)
       new_event->scroll.y = 0.;
       new_event->scroll.x_root = 0.;
       new_event->scroll.y_root = 0.;
+      new_event->scroll.delta_x = 0.;
+      new_event->scroll.delta_y = 0.;
       break;
     case GDK_ENTER_NOTIFY:
     case GDK_LEAVE_NOTIFY:
@@ -845,6 +847,36 @@ gdk_event_get_root_coords (const GdkEvent *event,
   return fetched;
 }

+gboolean
+gdk_event_get_scroll_deltas (const GdkEvent *event,
+                             gdouble        *delta_x,
+                             gdouble        *delta_y)
+{
+  gboolean fetched = TRUE;
+  gdouble dx = 0.0;
+  gdouble dy = 0.0;
+
+  switch (event->type)
+    {
+    case GDK_SCROLL:
+      fetched = event->scroll.has_deltas;
+      dx = event->scroll.delta_x;
+      dy = event->scroll.delta_y;
+      break;
+    default:
+      fetched = FALSE;
+      break;
+    }
+
+  if (delta_x)
+    *delta_x = dx;
+
+  if (delta_y)
+    *delta_y = dy;
+
+  return fetched;
+}
+
 /**
  * gdk_event_get_axis:
  * @event: a #GdkEvent
diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h
index 0602bd0..f6b4e04 100644
--- a/gdk/gdkevents.h
+++ b/gdk/gdkevents.h
@@ -337,6 +337,9 @@ struct _GdkEventScroll
   GdkScrollDirection direction;
   GdkDevice *device;
   gdouble x_root, y_root;
+  gboolean has_deltas;
+  gdouble delta_x;
+  gdouble delta_y;
 };

 struct _GdkEventKey
@@ -537,6 +540,9 @@ gboolean  gdk_event_get_coords		(const GdkEvent  *event,
 gboolean  gdk_event_get_root_coords	(const GdkEvent  *event,
					 gdouble	 *x_root,
					 gdouble	 *y_root);
+gboolean  gdk_event_get_scroll_deltas   (const GdkEvent *event,
+                                         gdouble         *delta_x,
+                                         gdouble         *delta_y);
 gboolean  gdk_event_get_axis            (const GdkEvent  *event,
                                          GdkAxisUse       axis_use,
                                          gdouble         *value);
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index f5f0339..d48751e 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -10800,6 +10800,9 @@ proxy_button_event (GdkEvent *source_event,
       event->scroll.y_root = source_event->scroll.y_root;
       event->scroll.state = state;
       event->scroll.device = source_event->scroll.device;
+      event->scroll.has_deltas = source_event->scroll.has_deltas;
+      event->scroll.delta_x = source_event->scroll.delta_x;
+      event->scroll.delta_y = source_event->scroll.delta_y;
       return TRUE;

     default:
diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
index c99a2c9..e7d97dc 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -57,6 +57,13 @@ static GdkWindow *find_toplevel_under_pointer   (GdkDisplay *display,
                                                  gint       *x,
                                                  gint       *y);

+/* Protocol to build cleanly for OSX < 10.7 */
+@protocol PreciseDeltas
+- (BOOL) hasPreciseScrollingDeltas;
+- (CGFloat) scrollingDeltaX;
+- (CGFloat) scrollingDeltaY;
+@end
+

 NSEvent *
 gdk_quartz_event_get_nsevent (GdkEvent *event)
@@ -980,6 +987,9 @@ fill_scroll_event (GdkWindow          *window,
                    gint                y,
                    gint                x_root,
                    gint                y_root,
+                   gboolean            has_deltas,
+                   gdouble             delta_x,
+                   gdouble             delta_y,
                    GdkScrollDirection  direction)
 {
   GdkWindowObject *private;
@@ -999,6 +1009,9 @@ fill_scroll_event (GdkWindow          *window,
   event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
   event->scroll.direction = direction;
   event->scroll.device = _gdk_display->core_pointer;
+  event->scroll.has_deltas = has_deltas;
+  event->scroll.delta_x = delta_x;
+  event->scroll.delta_y = delta_y;
 }

 static void
@@ -1471,28 +1484,59 @@ gdk_event_translate (GdkEvent *event,

     case NSScrollWheel:
       {
-	float dx = [nsevent deltaX];
-	float dy = [nsevent deltaY];
-	GdkScrollDirection direction;
-
-        if (dy != 0)
-          {
-            if (dy < 0.0)
-              direction = GDK_SCROLL_DOWN;
-            else
-              direction = GDK_SCROLL_UP;
+        GdkScrollDirection direction;
+	float dx;
+	float dy;

-            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
-          }
+	if (gdk_quartz_osx_version() >= GDK_OSX_LION &&
+	    [(id <PreciseDeltas>) nsevent hasPreciseScrollingDeltas])
+	  {
+	    dx = [(id <PreciseDeltas>) nsevent scrollingDeltaX];
+	    dy = [(id <PreciseDeltas>) nsevent scrollingDeltaY];

-        if (dx != 0)
-          {
-            if (dx < 0.0)
-              direction = GDK_SCROLL_RIGHT;
+            if (fabs (dy) > fabs (dx))
+              {
+                if (dy < 0.0)
+                  direction = GDK_SCROLL_DOWN;
+                else
+                  direction = GDK_SCROLL_UP;
+              }
             else
-              direction = GDK_SCROLL_LEFT;
+              {
+                if (dx < 0.0)
+                  direction = GDK_SCROLL_RIGHT;
+                else
+                  direction = GDK_SCROLL_LEFT;
+              }

-            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
+            fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                               TRUE, -dx, -dy, direction);
+	  }
+	else
+	  {
+	    dx = [nsevent deltaX];
+	    dy = [nsevent deltaY];
+
+            if (dy != 0.0)
+              {
+                if (dy < 0.0)
+                  direction = GDK_SCROLL_DOWN;
+                else
+                  direction = GDK_SCROLL_UP;
+
+                fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                                   FALSE, 0.0, fabs (dy), direction);
+              }
+            else if (dx != 0.0)
+              {
+                if (dx < 0.0)
+                  direction = GDK_SCROLL_RIGHT;
+                else
+                  direction = GDK_SCROLL_LEFT;
+
+                fill_scroll_event (window, event, nsevent, x, y, x_root, y_root,
+                                   FALSE, fabs (dx), 0.0, direction);
+              }
           }
       }
       break;
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 245fbf6..d39f045 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -2561,7 +2561,7 @@ gtk_range_button_release (GtkWidget      *widget,
 /**
  * _gtk_range_get_wheel_delta:
  * @range: a #GtkRange
- * @direction: A #GdkScrollDirection
+ * @event: A #GdkEventScroll
  *
  * Returns a good step value for the mouse wheel.
  *
@@ -2570,27 +2570,52 @@ gtk_range_button_release (GtkWidget      *widget,
  * Since: 2.4
  **/
 gdouble
-_gtk_range_get_wheel_delta (GtkRange           *range,
-			    GdkScrollDirection  direction)
+_gtk_range_get_wheel_delta (GtkRange       *range,
+                            GdkEventScroll *event)
 {
   GtkAdjustment *adj = range->adjustment;
+  gdouble dx, dy;
   gdouble delta;

-  if (GTK_IS_SCROLLBAR (range))
-    delta = pow (adj->page_size, 2.0 / 3.0);
+  if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy))
+    {
+      GtkAllocation allocation;
+
+      gtk_widget_get_allocation (GTK_WIDGET (range), &allocation);
+
+      if (gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL)
+        {
+          if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0)
+            delta = dx * adj->page_size / allocation.width;
+          else
+            delta = dx * (adj->upper - adj->lower) / allocation.width;
+        }
+      else
+        {
+          if (GTK_IS_SCROLLBAR (range) && adj->page_size > 0)
+            delta = dy * adj->page_size / allocation.height;
+          else
+            delta = dy * (adj->upper - adj->lower) / allocation.height;
+        }
+    }
   else
-    delta = adj->step_increment * 2;
-
-  if (direction == GDK_SCROLL_UP ||
-      direction == GDK_SCROLL_LEFT)
-    delta = - delta;
-
+    {
+      if (GTK_IS_SCROLLBAR (range))
+        delta = pow (adj->page_size, 2.0 / 3.0);
+      else
+        delta = adj->step_increment * 2;
+
+      if (event->direction == GDK_SCROLL_UP ||
+          event->direction == GDK_SCROLL_LEFT)
+        delta = - delta;
+    }
+
   if (range->inverted)
     delta = - delta;

   return delta;
 }
-
+
 static gboolean
 gtk_range_scroll_event (GtkWidget      *widget,
			GdkEventScroll *event)
@@ -2603,7 +2628,7 @@ gtk_range_scroll_event (GtkWidget      *widget,
       gdouble delta;
       gboolean handled;

-      delta = _gtk_range_get_wheel_delta (range, event->direction);
+      delta = _gtk_range_get_wheel_delta (range, event);

       g_signal_emit (range, signals[CHANGE_VALUE], 0,
                      GTK_SCROLL_JUMP, adj->value + delta,
diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h
index 5463140..c672acb 100644
--- a/gtk/gtkrange.h
+++ b/gtk/gtkrange.h
@@ -199,8 +199,8 @@ gint                gtk_range_get_round_digits             (GtkRange      *range


 /* internal API */
-gdouble            _gtk_range_get_wheel_delta              (GtkRange      *range,
-                                                            GdkScrollDirection direction);
+gdouble            _gtk_range_get_wheel_delta              (GtkRange       *range,
+                                                            GdkEventScroll *event);

 void               _gtk_range_set_stop_values              (GtkRange      *range,
                                                             gdouble       *values,
diff --git a/gtk/gtkscrolledwindow.c b/gtk/gtkscrolledwindow.c
index 1704d3c..2288b2f 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -1565,31 +1565,77 @@ static gboolean
 gtk_scrolled_window_scroll_event (GtkWidget      *widget,
				  GdkEventScroll *event)
 {
-  GtkWidget *range;
+  GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
+  gboolean handled = FALSE;
+  gdouble delta_x;
+  gdouble delta_y;

   g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);

-  if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
-    range = GTK_SCROLLED_WINDOW (widget)->vscrollbar;
-  else
-    range = GTK_SCROLLED_WINDOW (widget)->hscrollbar;
+  if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &delta_x, &delta_y))
+    {
+      if (delta_x != 0.0 && scrolled_window->hscrollbar &&
+          gtk_widget_get_visible (scrolled_window->hscrollbar))
+        {
+          GtkAdjustment *adj;
+          gdouble new_value;
+
+          adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->hscrollbar));
+
+          new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_x,
+                             gtk_adjustment_get_lower (adj),
+                             gtk_adjustment_get_upper (adj) -
+                             gtk_adjustment_get_page_size (adj));
+
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
+
+      if (delta_y != 0.0 && scrolled_window->vscrollbar &&
+          gtk_widget_get_visible (scrolled_window->vscrollbar))
+        {
+          GtkAdjustment *adj;
+          gdouble new_value;
+
+          adj = gtk_range_get_adjustment (GTK_RANGE (scrolled_window->vscrollbar));
+
+          new_value = CLAMP (gtk_adjustment_get_value (adj) + delta_y,
+                             gtk_adjustment_get_lower (adj),
+                             gtk_adjustment_get_upper (adj) -
+                             gtk_adjustment_get_page_size (adj));

-  if (range && gtk_widget_get_visible (range))
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
+    }
+  else
     {
-      GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
-      gdouble delta, new_value;
+      GtkWidget *range;

-      delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event->direction);
+      if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
+        range = scrolled_window->vscrollbar;
+      else
+        range = scrolled_window->hscrollbar;

-      new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
-
-      gtk_adjustment_set_value (adj, new_value);
+      if (range && gtk_widget_get_visible (range))
+        {
+          GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
+          gdouble delta, new_value;

-      return TRUE;
+          delta = _gtk_range_get_wheel_delta (GTK_RANGE (range), event);
+
+          new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
+
+          gtk_adjustment_set_value (adj, new_value);
+
+          handled = TRUE;
+        }
     }

-  return FALSE;
+  return handled;
 }

 static gboolean
--
1.7.10.2 (Apple Git-33)