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 e6c516c..e0802eb 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 f058570..7327a23 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -10794,6 +10794,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 4625070..71db965 100644
--- a/gdk/quartz/gdkevents-quartz.c
+++ b/gdk/quartz/gdkevents-quartz.c
@@ -847,6 +847,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;
@@ -866,6 +869,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
@@ -1261,28 +1267,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 &&
+	    [nsevent hasPreciseScrollingDeltas])
+	  {
+	    dx = [nsevent scrollingDeltaX];
+	    dy = [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,
+                               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, direction);
+                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 3932976..ea434be 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -2546,7 +2546,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.
  * 
@@ -2555,27 +2555,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)
@@ -2588,7 +2613,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 a708879..3131fbe 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 05389fa..999df18 100644
--- a/gtk/gtkscrolledwindow.c
+++ b/gtk/gtkscrolledwindow.c
@@ -1568,31 +1568,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
