]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrange.c
Add GtkBubbleWindow
[~andy/gtk] / gtk / gtkrange.c
index ffd45e97b44db78355218a48de92aedd610938df..8339e52b70c195a829185fd0ddad7217012e3d79 100644 (file)
@@ -13,9 +13,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
 #include <stdio.h>
 #include <math.h>
 
-#include "gtkmainprivate.h"
-#include "gtkmarshalers.h"
-#include "gtkorientable.h"
 #include "gtkrange.h"
+
+#include "gtkadjustment.h"
+#include "gtkcolorscaleprivate.h"
+#include "gtkintl.h"
+#include "gtkmain.h"
+#include "gtkmarshalers.h"
+#include "gtkorientableprivate.h"
+#include "gtkprivate.h"
 #include "gtkscale.h"
 #include "gtkscrollbar.h"
-#include "gtkwindow.h"
-#include "gtkprivate.h"
-#include "gtkintl.h"
 #include "gtktypebuiltins.h"
+#include "gtkwindow.h"
+#include "a11y/gtkrangeaccessible.h"
 
 /**
  * SECTION:gtkrange
@@ -79,15 +81,12 @@ struct _GtkRangePrivate
   gint  mouse_x;
   gint  mouse_y;
   MouseLocation      grab_location;   /* "grabbed" mouse location, OUTSIDE for no grab */
-  guint              grab_button : 8; /* 0 if none */
 
   GtkRangeStepTimer *timer;
 
   GtkAdjustment     *adjustment;
-  GtkOrientation     orientation;
   GtkSensitivityType lower_sensitivity;
   GtkSensitivityType upper_sensitivity;
-  GtkUpdateType      update_policy;
 
   GdkDevice         *grab_device;
   GdkRectangle       range_rect;     /* Area of entire stepper + trough assembly in widget->window coords */
@@ -103,7 +102,7 @@ struct _GtkRangePrivate
   GQuark             slider_detail_quark;
   GQuark             stepper_detail_quark[4];
 
-  gboolean recalc_marks;
+  GtkOrientation     orientation;
 
   gdouble  fill_level;
   gdouble *marks;
@@ -117,9 +116,6 @@ struct _GtkRangePrivate
   gint  slider_start;                /* Slider range along the long dimension, in widget->window coords */
   gint  slider_end;
 
-  guint repaint_id;
-  guint update_timeout_id;
-
   /* Steppers are: < > ---- < >
    *               a b      c d
    */
@@ -131,31 +127,39 @@ struct _GtkRangePrivate
   guint flippable              : 1;
   guint inverted               : 1;
   guint need_recalc            : 1;
+  guint recalc_marks           : 1;
   guint slider_size_fixed      : 1;
   guint trough_click_forward   : 1;  /* trough click was on the forward side of slider */
-  guint update_pending         : 1;  /* need to emit value_changed */
 
   /* Stepper sensitivity */
   guint lower_sensitive        : 1;
   guint upper_sensitive        : 1;
 
+  /* The range has an origin, should be drawn differently. Used by GtkScale */
+  guint has_origin             : 1;
+
+  /* Whether we're doing fine adjustment */
+  guint zoom                   : 1;
+
   /* Fill level */
   guint show_fill_level        : 1;
   guint restrict_to_fill_level : 1;
+
+  guint grab_button            : 8; /* 0 if none */
 };
 
 
 enum {
   PROP_0,
   PROP_ORIENTATION,
-  PROP_UPDATE_POLICY,
   PROP_ADJUSTMENT,
   PROP_INVERTED,
   PROP_LOWER_STEPPER_SENSITIVITY,
   PROP_UPPER_STEPPER_SENSITIVITY,
   PROP_SHOW_FILL_LEVEL,
   PROP_RESTRICT_TO_FILL_LEVEL,
-  PROP_FILL_LEVEL
+  PROP_FILL_LEVEL,
+  PROP_ROUND_DIGITS
 };
 
 enum {
@@ -266,14 +270,11 @@ static void          gtk_range_adjustment_changed       (GtkAdjustment *adjustme
 static void          gtk_range_add_step_timer           (GtkRange      *range,
                                                          GtkScrollType  step);
 static void          gtk_range_remove_step_timer        (GtkRange      *range);
-static void          gtk_range_reset_update_timer       (GtkRange      *range);
-static void          gtk_range_remove_update_timer      (GtkRange      *range);
 static GdkRectangle* get_area                           (GtkRange      *range,
                                                          MouseLocation  location);
 static gboolean      gtk_range_real_change_value        (GtkRange      *range,
                                                          GtkScrollType  scroll,
                                                          gdouble        value);
-static void          gtk_range_update_value             (GtkRange      *range);
 static gboolean      gtk_range_key_press                (GtkWidget     *range,
                                                         GdkEventKey   *event);
 
@@ -380,10 +381,8 @@ gtk_range_class_init (GtkRangeClass *class)
    * @range: the #GtkRange that received the signal
    * @scroll: the type of scroll action that was performed
    * @value: the new value resulting from the scroll action
-   * @returns: %TRUE to prevent other handlers from being invoked for the
-   * signal, %FALSE to propagate the signal further
    *
-   * The ::change-value signal is emitted when a scroll action is
+   * The #GtkRange::change-value signal is emitted when a scroll action is
    * performed on a range.  It allows an application to determine the
    * type of scroll event that occurred and the resultant new value.
    * The application can handle the event itself and return %TRUE to
@@ -392,12 +391,15 @@ gtk_range_class_init (GtkRangeClass *class)
    * reached.
    *
    * The value parameter is unrounded.  An application that overrides
-   * the ::change-value signal is responsible for clamping the value to
-   * the desired number of decimal digits; the default GTK+ handler 
-   * clamps the value based on @range->round_digits.
+   * the GtkRange::change-value signal is responsible for clamping the
+   * value to the desired number of decimal digits; the default GTK+
+   * handler clamps the value based on #GtkRange:round-digits.
    *
    * It is not possible to use delayed update policies in an overridden
-   * ::change-value handler.
+   * #GtkRange::change-value handler.
+   *
+   * Returns: %TRUE to prevent other handlers from being invoked for
+   *     the signal, %FALSE to propagate the signal further
    *
    * Since: 2.6
    */
@@ -416,15 +418,6 @@ gtk_range_class_init (GtkRangeClass *class)
                                     PROP_ORIENTATION,
                                     "orientation");
 
-  g_object_class_install_property (gobject_class,
-                                   PROP_UPDATE_POLICY,
-                                   g_param_spec_enum ("update-policy",
-                                                     P_("Update policy"),
-                                                     P_("How the range should be updated on the screen"),
-                                                     GTK_TYPE_UPDATE_TYPE,
-                                                     GTK_UPDATE_CONTINUOUS,
-                                                     GTK_PARAM_READWRITE));
-  
   g_object_class_install_property (gobject_class,
                                    PROP_ADJUSTMENT,
                                    g_param_spec_object ("adjustment",
@@ -511,6 +504,24 @@ gtk_range_class_init (GtkRangeClass *class)
                                                         G_MAXDOUBLE,
                                                         GTK_PARAM_READWRITE));
 
+  /**
+   * GtkRange:round-digits:
+   *
+   * The number of digits to round the value to when
+   * it changes, or -1. See #GtkRange::change-value.
+   *
+   * Since: 2.24
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ROUND_DIGITS,
+                                   g_param_spec_int ("round-digits",
+                                                     P_("Round Digits"),
+                                                     P_("The number of digits to round the value to."),
+                                                     -1,
+                                                     G_MAXINT,
+                                                     -1,
+                                                     GTK_PARAM_READWRITE));
+
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("slider-width",
                                                             P_("Slider Width"),
@@ -539,8 +550,6 @@ gtk_range_class_init (GtkRangeClass *class)
    * GtkRange:stepper-spacing:
    *
    * The spacing between the stepper buttons and thumb. Note that
-   * setting this value to anything > 0 will automatically set the
-   * trough-under-steppers style property to %TRUE as well. Also,
    * stepper-spacing won't have any effect if there are no steppers.
    */
   gtk_widget_class_install_style_property (widget_class,
@@ -572,9 +581,7 @@ gtk_range_class_init (GtkRangeClass *class)
    * GtkRange:trough-under-steppers:
    *
    * Whether to draw the trough across the full length of the range or
-   * to exclude the steppers and their spacing. Note that setting the
-   * #GtkRange:stepper-spacing style property to any value > 0 will
-   * automatically enable trough-under-steppers too.
+   * to exclude the steppers and their spacing.
    *
    * Since: 2.10
    */
@@ -600,6 +607,8 @@ gtk_range_class_init (GtkRangeClass *class)
                                                               GTK_PARAM_READABLE));
 
   g_type_class_add_private (class, sizeof (GtkRangePrivate));
+
+  gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_RANGE_ACCESSIBLE);
 }
 
 static void
@@ -622,11 +631,9 @@ gtk_range_set_property (GObject      *object,
       priv->stepper_detail_quark[2] = 0;
       priv->stepper_detail_quark[3] = 0;
 
+      _gtk_orientable_set_style_classes (GTK_ORIENTABLE (range));
       gtk_widget_queue_resize (GTK_WIDGET (range));
       break;
-    case PROP_UPDATE_POLICY:
-      gtk_range_set_update_policy (range, g_value_get_enum (value));
-      break;
     case PROP_ADJUSTMENT:
       gtk_range_set_adjustment (range, g_value_get_object (value));
       break;
@@ -648,6 +655,9 @@ gtk_range_set_property (GObject      *object,
     case PROP_FILL_LEVEL:
       gtk_range_set_fill_level (range, g_value_get_double (value));
       break;
+    case PROP_ROUND_DIGITS:
+      gtk_range_set_round_digits (range, g_value_get_int (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -668,9 +678,6 @@ gtk_range_get_property (GObject      *object,
     case PROP_ORIENTATION:
       g_value_set_enum (value, priv->orientation);
       break;
-    case PROP_UPDATE_POLICY:
-      g_value_set_enum (value, priv->update_policy);
-      break;
     case PROP_ADJUSTMENT:
       g_value_set_object (value, priv->adjustment);
       break;
@@ -692,6 +699,9 @@ gtk_range_get_property (GObject      *object,
     case PROP_FILL_LEVEL:
       g_value_set_double (value, gtk_range_get_fill_level (range));
       break;
+    case PROP_ROUND_DIGITS:
+      g_value_set_int (value, gtk_range_get_round_digits (range));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -712,7 +722,6 @@ gtk_range_init (GtkRange *range)
 
   priv->orientation = GTK_ORIENTATION_HORIZONTAL;
   priv->adjustment = NULL;
-  priv->update_policy = GTK_UPDATE_CONTINUOUS;
   priv->inverted = FALSE;
   priv->flippable = FALSE;
   priv->min_slider_size = 1;
@@ -731,6 +740,7 @@ gtk_range_init (GtkRange *range)
   priv->upper_sensitivity = GTK_SENSITIVITY_AUTO;
   priv->lower_sensitive = TRUE;
   priv->upper_sensitive = TRUE;
+  priv->has_origin = FALSE;
   priv->show_fill_level = FALSE;
   priv->restrict_to_fill_level = TRUE;
   priv->fill_level = G_MAXDOUBLE;
@@ -763,53 +773,6 @@ gtk_range_get_adjustment (GtkRange *range)
   return priv->adjustment;
 }
 
-/**
- * gtk_range_set_update_policy:
- * @range: a #GtkRange
- * @policy: update policy
- *
- * Sets the update policy for the range. #GTK_UPDATE_CONTINUOUS means that
- * anytime the range slider is moved, the range value will change and the
- * value_changed signal will be emitted. #GTK_UPDATE_DELAYED means that
- * the value will be updated after a brief timeout where no slider motion
- * occurs, so updates are spaced by a short time rather than
- * continuous. #GTK_UPDATE_DISCONTINUOUS means that the value will only
- * be updated when the user releases the button and ends the slider
- * drag operation.
- **/
-void
-gtk_range_set_update_policy (GtkRange      *range,
-                            GtkUpdateType  policy)
-{
-  GtkRangePrivate *priv;
-
-  g_return_if_fail (GTK_IS_RANGE (range));
-
-  priv = range->priv;
-
-  if (priv->update_policy != policy)
-    {
-      priv->update_policy = policy;
-      g_object_notify (G_OBJECT (range), "update-policy");
-    }
-}
-
-/**
- * gtk_range_get_update_policy:
- * @range: a #GtkRange
- *
- * Gets the update policy of @range. See gtk_range_set_update_policy().
- *
- * Return value: the current update policy
- **/
-GtkUpdateType
-gtk_range_get_update_policy (GtkRange *range)
-{
-  g_return_val_if_fail (GTK_IS_RANGE (range), GTK_UPDATE_CONTINUOUS);
-
-  return range->priv->update_policy;
-}
-
 /**
  * gtk_range_set_adjustment:
  * @range: a #GtkRange
@@ -968,7 +931,7 @@ gtk_range_get_flippable (GtkRange *range)
  * @size_fixed: %TRUE to make the slider size constant
  *
  * Sets whether the range's slider has a fixed size, or a size that
- * depends on it's adjustment's page size.
+ * depends on its adjustment's page size.
  *
  * This function is useful mainly for #GtkRange subclasses.
  *
@@ -1046,7 +1009,7 @@ gtk_range_set_min_slider_size (GtkRange *range,
       if (gtk_widget_is_drawable (GTK_WIDGET (range)))
         {
           priv->need_recalc = TRUE;
-          gtk_range_calc_layout (range, priv->adjustment->value);
+          gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
           gtk_widget_queue_draw (GTK_WIDGET (range));
         }
     }
@@ -1075,7 +1038,7 @@ gtk_range_get_min_slider_size (GtkRange *range)
 /**
  * gtk_range_get_range_rect:
  * @range: a #GtkRange
- * @range_rect: return location for the range rectangle
+ * @range_rect: (out): return location for the range rectangle
  *
  * This function returns the area that contains the range's trough
  * and its steppers, in widget->window coordinates.
@@ -1095,7 +1058,7 @@ gtk_range_get_range_rect (GtkRange     *range,
 
   priv = range->priv;
 
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
   *range_rect = priv->range_rect;
 }
@@ -1103,8 +1066,10 @@ gtk_range_get_range_rect (GtkRange     *range,
 /**
  * gtk_range_get_slider_range:
  * @range: a #GtkRange
- * @slider_start: (allow-none): return location for the slider's start, or %NULL
- * @slider_end: (allow-none): return location for the slider's end, or %NULL
+ * @slider_start: (out) (allow-none): return location for the slider's
+ *     start, or %NULL
+ * @slider_end: (out) (allow-none): return location for the slider's
+ *     end, or %NULL
  *
  * This function returns sliders range along the long dimension,
  * in widget->window coordinates.
@@ -1124,7 +1089,7 @@ gtk_range_get_slider_range (GtkRange *range,
 
   priv = range->priv;
 
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
   if (slider_start)
     *slider_start = priv->slider_start;
@@ -1158,7 +1123,7 @@ gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
       priv->lower_sensitivity = sensitivity;
 
       priv->need_recalc = TRUE;
-      gtk_range_calc_layout (range, priv->adjustment->value);
+      gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
       gtk_widget_queue_draw (GTK_WIDGET (range));
 
       g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
@@ -1209,7 +1174,7 @@ gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
       priv->upper_sensitivity = sensitivity;
 
       priv->need_recalc = TRUE;
-      gtk_range_calc_layout (range, priv->adjustment->value);
+      gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
       gtk_widget_queue_draw (GTK_WIDGET (range));
 
       g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
@@ -1251,16 +1216,19 @@ gtk_range_set_increments (GtkRange *range,
                           gdouble   step,
                           gdouble   page)
 {
-  GtkRangePrivate *priv;
+  GtkAdjustment *adjustment;
 
   g_return_if_fail (GTK_IS_RANGE (range));
 
-  priv = range->priv;
-
-  priv->adjustment->step_increment = step;
-  priv->adjustment->page_increment = page;
+  adjustment = range->priv->adjustment;
 
-  gtk_adjustment_changed (priv->adjustment);
+  gtk_adjustment_configure (adjustment,
+                            gtk_adjustment_get_value (adjustment),
+                            gtk_adjustment_get_lower (adjustment),
+                            gtk_adjustment_get_upper (adjustment),
+                            step,
+                            page,
+                            gtk_adjustment_get_page_size (adjustment));
 }
 
 /**
@@ -1279,24 +1247,27 @@ gtk_range_set_range (GtkRange *range,
                      gdouble   max)
 {
   GtkRangePrivate *priv;
+  GtkAdjustment *adjustment;
   gdouble value;
   
   g_return_if_fail (GTK_IS_RANGE (range));
   g_return_if_fail (min <= max);
 
   priv = range->priv;
+  adjustment = priv->adjustment;
 
-  priv->adjustment->lower = min;
-  priv->adjustment->upper = max;
-
-  value = priv->adjustment->value;
-
+  value = gtk_adjustment_get_value (adjustment);
   if (priv->restrict_to_fill_level)
-    value = MIN (value, MAX (priv->adjustment->lower,
+    value = MIN (value, MAX (gtk_adjustment_get_lower (adjustment),
                              priv->fill_level));
 
-  gtk_adjustment_set_value (priv->adjustment, value);
-  gtk_adjustment_changed (priv->adjustment);
+  gtk_adjustment_configure (adjustment,
+                            value,
+                            min,
+                            max,
+                            gtk_adjustment_get_step_increment (adjustment),
+                            gtk_adjustment_get_page_increment (adjustment),
+                            gtk_adjustment_get_page_size (adjustment));
 }
 
 /**
@@ -1320,7 +1291,7 @@ gtk_range_set_value (GtkRange *range,
   priv = range->priv;
 
   if (priv->restrict_to_fill_level)
-    value = MIN (value, MAX (priv->adjustment->lower,
+    value = MIN (value, MAX (gtk_adjustment_get_lower (priv->adjustment),
                              priv->fill_level));
 
   gtk_adjustment_set_value (priv->adjustment, value);
@@ -1339,7 +1310,7 @@ gtk_range_get_value (GtkRange *range)
 {
   g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
 
-  return range->priv->adjustment->value;
+  return gtk_adjustment_get_value (range->priv->adjustment);
 }
 
 /**
@@ -1528,11 +1499,6 @@ gtk_range_destroy (GtkWidget *widget)
   GtkRangePrivate *priv = range->priv;
 
   gtk_range_remove_step_timer (range);
-  gtk_range_remove_update_timer (range);
-
-  if (priv->repaint_id)
-    g_source_remove (priv->repaint_id);
-  priv->repaint_id = 0;
 
   if (priv->adjustment)
     {
@@ -1655,7 +1621,7 @@ modify_allocation_for_window_grip (GtkWidget     *widget,
     translated_rect = *allocation;
   else
     {
-      gtk_widget_translate_coordinates (gtk_widget_get_parent (widget),
+      gtk_widget_translate_coordinates (parent,
                                         window,
                                         allocation->x, allocation->y,
                                         &x, &y);
@@ -1666,7 +1632,7 @@ modify_allocation_for_window_grip (GtkWidget     *widget,
     }
 
   /* If the stepper button intersects the window resize grip.. */
-  if (gdk_rectangle_intersect (&grip_rect, &translated_rect, NULL))
+  if (gdk_rectangle_intersect (&grip_rect, &translated_rect, &grip_rect))
     {
       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
         {
@@ -1698,7 +1664,7 @@ gtk_range_size_allocate (GtkWidget     *widget,
   priv->recalc_marks = TRUE;
 
   priv->need_recalc = TRUE;
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
   if (gtk_widget_get_realized (widget))
     gdk_window_move_resize (priv->event_window,
@@ -1725,7 +1691,7 @@ gtk_range_hierarchy_changed (GtkWidget *widget,
                                           G_CALLBACK (resize_grip_visible_changed),
                                           widget);
   window = gtk_widget_get_toplevel (widget);
-  if (GTK_IS_WINDOW (window))
+  if (gtk_widget_is_toplevel (window))
     g_signal_connect (window, "notify::resize-grip-visible",
                       G_CALLBACK (resize_grip_visible_changed), widget);
 }
@@ -1740,7 +1706,7 @@ gtk_range_realize (GtkWidget *widget)
   GdkWindowAttr attributes;
   gint attributes_mask;
 
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
   gtk_widget_set_realized (widget, TRUE);
 
@@ -1760,9 +1726,11 @@ gtk_range_realize (GtkWidget *widget)
   attributes.wclass = GDK_INPUT_ONLY;
   attributes.event_mask = gtk_widget_get_events (widget);
   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
-                           GDK_BUTTON_RELEASE_MASK |
-                           GDK_ENTER_NOTIFY_MASK |
-                           GDK_LEAVE_NOTIFY_MASK |
+                            GDK_BUTTON_RELEASE_MASK |
+                            GDK_SCROLL_MASK |
+                            GDK_SMOOTH_SCROLL_MASK |
+                            GDK_ENTER_NOTIFY_MASK |
+                            GDK_LEAVE_NOTIFY_MASK |
                             GDK_POINTER_MOTION_MASK |
                             GDK_POINTER_MOTION_HINT_MASK);
 
@@ -1770,7 +1738,7 @@ gtk_range_realize (GtkWidget *widget)
 
   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
                                        &attributes, attributes_mask);
-  gdk_window_set_user_data (priv->event_window, range);
+  gtk_widget_register_window (widget, priv->event_window);
 }
 
 static void
@@ -1780,9 +1748,8 @@ gtk_range_unrealize (GtkWidget *widget)
   GtkRangePrivate *priv = range->priv;
 
   gtk_range_remove_step_timer (range);
-  gtk_range_remove_update_timer (range);
 
-  gdk_window_set_user_data (priv->event_window, NULL);
+  gtk_widget_unregister_window (widget, priv->event_window);
   gdk_window_destroy (priv->event_window);
   priv->event_window = NULL;
 
@@ -1880,7 +1847,6 @@ _gtk_range_update_context_for_stepper (GtkRange        *range,
     }
 
   gtk_style_context_set_junction_sides (context, sides);
-  gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
 }
 
 static void
@@ -1889,18 +1855,17 @@ draw_stepper (GtkRange     *range,
               cairo_t      *cr,
               GtkArrowType  arrow_type,
               gboolean      clicked,
-              gboolean      prelighted)
+              gboolean      prelighted,
+              GtkStateFlags state)
 {
   GtkRangePrivate *priv = range->priv;
   GtkAllocation allocation;
-  GtkStateFlags state = 0;
   GtkStyleContext *context;
   GtkWidget *widget = GTK_WIDGET (range);
-  GdkWindow *window;
   gfloat arrow_scaling;
   GdkRectangle *rect;
-  gint arrow_x;
-  gint arrow_y;
+  gdouble arrow_x;
+  gdouble arrow_y;
   gdouble arrow_size, angle;
   gboolean arrow_sensitive;
 
@@ -1938,8 +1903,12 @@ draw_stepper (GtkRange     *range,
       arrow_sensitive = priv->lower_sensitive;
     }
 
-  if (!gtk_widget_is_sensitive (GTK_WIDGET (range)) || !arrow_sensitive)
-    state = GTK_STATE_FLAG_INSENSITIVE;
+  state &= ~(GTK_STATE_FLAG_ACTIVE | GTK_STATE_FLAG_PRELIGHT);
+
+  if ((state & GTK_STATE_FLAG_INSENSITIVE) || !arrow_sensitive)
+    {
+      state |= GTK_STATE_FLAG_INSENSITIVE;
+    }
   else
     {
       if (clicked)
@@ -1948,13 +1917,36 @@ draw_stepper (GtkRange     *range,
         state |= GTK_STATE_FLAG_PRELIGHT;
     }
 
-  window = gtk_widget_get_window (widget);
   context = gtk_widget_get_style_context (widget);
 
   gtk_style_context_save (context);
-  _gtk_range_update_context_for_stepper (range, context, stepper);
+
+  /* don't set juction sides on scrollbar steppers */
+  if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_SCROLLBAR))
+    gtk_style_context_set_junction_sides (context, GTK_JUNCTION_NONE);
+  else
+    _gtk_range_update_context_for_stepper (range, context, stepper);
+
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
   gtk_style_context_set_state (context, state);
 
+  switch (arrow_type)
+    {
+    case GTK_ARROW_RIGHT:
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
+      break;
+    case GTK_ARROW_DOWN:
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
+      break;
+    case GTK_ARROW_LEFT:
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
+      break;
+    case GTK_ARROW_UP:
+    default:
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
+      break;
+    }
+
   gtk_render_background (context, cr,
                          rect->x, rect->y,
                          rect->width, rect->height);
@@ -1975,8 +1967,8 @@ draw_stepper (GtkRange     *range,
 
       gtk_range_get_props (GTK_RANGE (widget),
                            NULL, NULL, NULL, NULL, NULL, NULL,
-                          &arrow_displacement_x, &arrow_displacement_y);
-      
+                           &arrow_displacement_x, &arrow_displacement_y);
+
       arrow_x += arrow_displacement_x;
       arrow_y += arrow_displacement_y;
     }
@@ -2007,28 +1999,32 @@ draw_stepper (GtkRange     *range,
 }
 
 static gboolean
-gtk_range_draw (GtkWidget      *widget,
-                cairo_t        *cr)
+gtk_range_draw (GtkWidget *widget,
+                cairo_t   *cr)
 {
   GtkRange *range = GTK_RANGE (widget);
   GtkRangePrivate *priv = range->priv;
-  gboolean sensitive;
-  GtkStateFlags state = 0;
-  GdkWindow *window;
+  GtkStateFlags widget_state;
   gint focus_line_width = 0;
   gint focus_padding = 0;
-  gboolean touchscreen;
   gboolean draw_trough = TRUE;
+  gboolean draw_slider = TRUE;
   GtkStyleContext *context;
+  GtkBorder margin;
 
   context = gtk_widget_get_style_context (widget);
-  g_object_get (gtk_widget_get_settings (widget),
-                "gtk-touchscreen-mode", &touchscreen,
-                NULL);
 
   if (GTK_IS_SCALE (widget) &&
-      priv->adjustment->upper == priv->adjustment->lower)
-    draw_trough = FALSE;
+      gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
+    {
+      draw_trough = TRUE;
+      draw_slider = FALSE;
+    }
+  if (GTK_IS_COLOR_SCALE (widget))
+    {
+      draw_trough = FALSE;
+      draw_slider = TRUE;
+    }
 
   if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
     gtk_widget_style_get (GTK_WIDGET (range),
@@ -2036,17 +2032,10 @@ gtk_range_draw (GtkWidget      *widget,
                           "focus-padding", &focus_padding,
                           NULL);
 
-  window = gtk_widget_get_window (widget);
-
-  /* we're now exposing, so there's no need to force early repaints */
-  if (priv->repaint_id)
-    g_source_remove (priv->repaint_id);
-  priv->repaint_id = 0;
-
   gtk_range_calc_marks (range);
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
-  sensitive = gtk_widget_is_sensitive (widget);
+  widget_state = gtk_widget_get_state_flags (widget);
 
   /* Just to be confusing, we draw the trough for the whole
    * range rectangle, not the trough rectangle (the trough
@@ -2075,14 +2064,6 @@ gtk_range_draw (GtkWidget      *widget,
                             "stepper-spacing",       &stepper_spacing,
                             NULL);
 
-      gtk_style_context_save (context);
-
-      if (!sensitive)
-        gtk_style_context_set_state (context, GTK_STATE_FLAG_INSENSITIVE);
-
-      if (stepper_spacing > 0)
-        trough_under_steppers = FALSE;
-
       if (!trough_under_steppers)
         {
           gint offset  = 0;
@@ -2123,148 +2104,180 @@ gtk_range_draw (GtkWidget      *widget,
               y      += offset;
               height -= shorter;
             }
-       }
+        }
 
       gtk_style_context_save (context);
       gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
+      gtk_style_context_get_margin (context, widget_state, &margin);
+
+      x += margin.left;
+      y += margin.top;
+      width -= margin.left + margin.right;
+      height -= margin.top + margin.bottom;
 
       if (draw_trough)
         {
-         gint trough_change_pos_x = width;
-         gint trough_change_pos_y = height;
-
-         if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-           trough_change_pos_x = (priv->slider.x +
-                                   priv->slider.width / 2 -
-                                   x);
-         else
-           trough_change_pos_y = (priv->slider.y +
-                                   priv->slider.height / 2 -
-                                   y);
-
-          /* FIXME: was trough-upper and trough-lower really used,
-           * in that case, it should still be exposed somehow.
-           */
-          gtk_render_background (context, cr, x, y,
-                                 trough_change_pos_x,
-                                 trough_change_pos_y);
-
-         if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-           trough_change_pos_y = 0;
-         else
-           trough_change_pos_x = 0;
-
-          gtk_render_background (context, cr,
-                                 x + trough_change_pos_x, y + trough_change_pos_y,
-                                 width - trough_change_pos_x,
-                                 height - trough_change_pos_y);
-
-          gtk_render_frame (context, cr,
-                            x, y, width, height);
-        }
-      else
-        {
-          gtk_render_background (context, cr,
-                                 x, y, width, height);
-          gtk_render_frame (context, cr,
-                            x, y, width, height);
+          if (!priv->has_origin || !draw_slider)
+            {
+              gtk_render_background (context, cr,
+                                     x, y, width, height);
+
+              gtk_render_frame (context, cr,
+                                x, y, width, height);
+            }
+          else
+            {
+              gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+
+              gint trough_change_pos_x = width;
+              gint trough_change_pos_y = height;
+
+              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+                trough_change_pos_x = (priv->slider.x +
+                                       priv->slider.width / 2 -
+                                       x);
+              else
+                trough_change_pos_y = (priv->slider.y +
+                                       priv->slider.height / 2 -
+                                       y);
+
+              gtk_style_context_save (context);
+              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+                {
+                  gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
+
+                  if (!is_rtl)
+                    gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
+                }
+              else
+                gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
+
+              gtk_render_background (context, cr, x, y,
+                                     trough_change_pos_x,
+                                     trough_change_pos_y);
+
+              gtk_render_frame (context, cr, x, y,
+                                trough_change_pos_x,
+                                trough_change_pos_y);
+
+              gtk_style_context_restore (context);
+
+              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+                trough_change_pos_y = 0;
+              else
+                trough_change_pos_x = 0;
+
+              gtk_style_context_save (context);
+              if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+                {
+                  gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
+
+                  if (is_rtl)
+                    gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
+                }
+              else
+                {
+                  gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
+                  gtk_style_context_add_class (context, GTK_STYLE_CLASS_HIGHLIGHT);
+                }
+
+              gtk_render_background (context, cr,
+                                     x + trough_change_pos_x, y + trough_change_pos_y,
+                                     width - trough_change_pos_x,
+                                     height - trough_change_pos_y);
+
+              gtk_render_frame (context, cr,
+                                x + trough_change_pos_x, y + trough_change_pos_y,
+                                width - trough_change_pos_x,
+                                height - trough_change_pos_y);
+
+              gtk_style_context_restore (context);
+            }
         }
 
       gtk_style_context_restore (context);
 
       if (priv->show_fill_level &&
-          priv->adjustment->upper - priv->adjustment->page_size -
-          priv->adjustment->lower != 0)
-       {
+          gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) -
+          gtk_adjustment_get_lower (priv->adjustment) != 0)
+        {
           gdouble  fill_level  = priv->fill_level;
-         gint     fill_x      = x;
-         gint     fill_y      = y;
-         gint     fill_width  = width;
-         gint     fill_height = height;
-         gchar   *fill_detail;
+          gint     fill_x      = x;
+          gint     fill_y      = y;
+          gint     fill_width  = width;
+          gint     fill_height = height;
 
           gtk_style_context_save (context);
           gtk_style_context_add_class (context, GTK_STYLE_CLASS_PROGRESSBAR);
 
-          fill_level = CLAMP (fill_level, priv->adjustment->lower,
-                              priv->adjustment->upper -
-                              priv->adjustment->page_size);
-
-         if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-           {
-             fill_x     = priv->trough.x;
-             fill_width = (priv->slider.width +
-                            (fill_level - priv->adjustment->lower) /
-                            (priv->adjustment->upper -
-                             priv->adjustment->lower -
-                             priv->adjustment->page_size) *
+          fill_level = CLAMP (fill_level, gtk_adjustment_get_lower (priv->adjustment),
+                              gtk_adjustment_get_upper (priv->adjustment) -
+                              gtk_adjustment_get_page_size (priv->adjustment));
+
+          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
+            {
+              fill_x     = priv->trough.x;
+              fill_width = (priv->slider.width +
+                            (fill_level - gtk_adjustment_get_lower (priv->adjustment)) /
+                            (gtk_adjustment_get_upper (priv->adjustment) -
+                             gtk_adjustment_get_lower (priv->adjustment) -
+                             gtk_adjustment_get_page_size (priv->adjustment)) *
                             (priv->trough.width -
                              priv->slider.width));
 
               if (should_invert (range))
                 fill_x += priv->trough.width - fill_width;
-           }
-         else
-           {
-             fill_y      = priv->trough.y;
-             fill_height = (priv->slider.height +
-                             (fill_level - priv->adjustment->lower) /
-                             (priv->adjustment->upper -
-                              priv->adjustment->lower -
-                              priv->adjustment->page_size) *
+            }
+          else
+            {
+              fill_y      = priv->trough.y;
+              fill_height = (priv->slider.height +
+                             (fill_level - gtk_adjustment_get_lower (priv->adjustment)) /
+                             (gtk_adjustment_get_upper (priv->adjustment) -
+                              gtk_adjustment_get_lower (priv->adjustment) -
+                              gtk_adjustment_get_page_size (priv->adjustment)) *
                              (priv->trough.height -
                               priv->slider.height));
 
               if (should_invert (range))
                 fill_y += priv->trough.height - fill_height;
-           }
-
-         if (fill_level < priv->adjustment->upper - priv->adjustment->page_size)
-           fill_detail = "trough-fill-level-full";
-         else
-           fill_detail = "trough-fill-level";
+            }
 
           gtk_render_activity (context, cr,
                                fill_x, fill_y,
                                fill_width, fill_height);
 
           gtk_style_context_restore (context);
-       }
-
-      gtk_style_context_restore (context);
+        }
 
-      if (sensitive && gtk_widget_has_focus (widget))
+      if (!(widget_state & GTK_STATE_FLAG_INSENSITIVE) && gtk_widget_has_visible_focus (widget))
         {
-          gtk_style_context_save (context);
-          gtk_style_context_set_state (context,
-                                       gtk_widget_get_state_flags (widget));
-
           gtk_render_focus (context, cr,
                             priv->range_rect.x,
                             priv->range_rect.y,
                             priv->range_rect.width,
                             priv->range_rect.height);
-
-          gtk_style_context_restore (context);
         }
     }
 
   cairo_restore (cr);
 
-  if (!sensitive)
-    state = GTK_STATE_FLAG_INSENSITIVE;
-  else if (!touchscreen && priv->mouse_location == MOUSE_SLIDER)
-    state = GTK_STATE_FLAG_PRELIGHT;
+  if (draw_slider)
+    {
+      GtkStateFlags state = widget_state;
 
-  if (priv->grab_location == MOUSE_SLIDER)
-    state |= GTK_STATE_FLAG_ACTIVE;
+      state &= ~(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE);
 
-  cairo_save (cr);
-  gdk_cairo_rectangle (cr, &priv->slider);
-  cairo_clip (cr);
+      if (priv->mouse_location == MOUSE_SLIDER && !(state & GTK_STATE_FLAG_INSENSITIVE))
+        state |= GTK_STATE_FLAG_PRELIGHT;
+
+      if (priv->grab_location == MOUSE_SLIDER)
+        state |= GTK_STATE_FLAG_ACTIVE;
+
+      cairo_save (cr);
+      gdk_cairo_rectangle (cr, &priv->slider);
+      cairo_clip (cr);
 
-  if (draw_trough)
-    {
       gtk_style_context_save (context);
       gtk_style_context_add_class (context, GTK_STYLE_CLASS_SLIDER);
       gtk_style_context_set_state (context, state);
@@ -2277,34 +2290,38 @@ gtk_range_draw (GtkWidget      *widget,
                          priv->orientation);
 
       gtk_style_context_restore (context);
-    }
 
-  cairo_restore (cr);
+      cairo_restore (cr);
+    }
 
   if (priv->has_stepper_a)
     draw_stepper (range, STEPPER_A, cr,
                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
                   priv->grab_location == MOUSE_STEPPER_A,
-                  !touchscreen && priv->mouse_location == MOUSE_STEPPER_A);
+                  priv->mouse_location == MOUSE_STEPPER_A,
+                  widget_state);
 
   if (priv->has_stepper_b)
     draw_stepper (range, STEPPER_B, cr,
                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
                   priv->grab_location == MOUSE_STEPPER_B,
-                  !touchscreen && priv->mouse_location == MOUSE_STEPPER_B);
+                  priv->mouse_location == MOUSE_STEPPER_B,
+                  widget_state);
 
   if (priv->has_stepper_c)
     draw_stepper (range, STEPPER_C, cr,
                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_UP : GTK_ARROW_LEFT,
                   priv->grab_location == MOUSE_STEPPER_C,
-                  !touchscreen && priv->mouse_location == MOUSE_STEPPER_C);
+                  priv->mouse_location == MOUSE_STEPPER_C,
+                  widget_state);
 
   if (priv->has_stepper_d)
     draw_stepper (range, STEPPER_D, cr,
                   priv->orientation == GTK_ORIENTATION_VERTICAL ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
                   priv->grab_location == MOUSE_STEPPER_D,
-                  !touchscreen && priv->mouse_location == MOUSE_STEPPER_D);
-  
+                  priv->mouse_location == MOUSE_STEPPER_D,
+                  widget_state);
+
   return FALSE;
 }
 
@@ -2319,15 +2336,11 @@ range_grab_add (GtkRange      *range,
   if (device == priv->grab_device)
     return;
 
-  if (priv->grab_device != NULL)
-    {
-      g_warning ("GtkRange already had a grab device, releasing device grab");
-      gtk_device_grab_remove (GTK_WIDGET (range), priv->grab_device);
-    }
-
-  /* we don't actually gdk_grab, since a button is down */
-  gtk_device_grab_add (GTK_WIDGET (range), device, TRUE);
-
+  /* Don't perform any GDK/GTK+ grab here. Since a button
+   * is down, there's an ongoing implicit grab on
+   * priv->event_window, which pretty much guarantees this
+   * is the only widget receiving the pointer events.
+   */
   priv->grab_location = location;
   priv->grab_button = button;
   priv->grab_device = device;
@@ -2344,9 +2357,10 @@ range_grab_remove (GtkRange *range)
 
   if (priv->grab_device)
     {
-      gtk_device_grab_remove (GTK_WIDGET (range),
-                              priv->grab_device);
+      GdkDevice *grab_device = priv->grab_device;
+
       priv->grab_device = NULL;
+      gtk_device_grab_remove (GTK_WIDGET (range), grab_device);
     }
 
   location = priv->grab_location;
@@ -2356,6 +2370,8 @@ range_grab_remove (GtkRange *range)
   if (gtk_range_update_mouse_location (range) ||
       location != MOUSE_OUTSIDE)
     gtk_widget_queue_draw (GTK_WIDGET (range));
+
+  priv->zoom = FALSE;
 }
 
 static GtkScrollType
@@ -2372,13 +2388,13 @@ range_get_scroll_for_grab (GtkRange      *range)
     case MOUSE_STEPPER_C:
       switch (priv->grab_button)
         {
-        case 1:
+        case GDK_BUTTON_PRIMARY:
           return invert ? GTK_SCROLL_STEP_FORWARD : GTK_SCROLL_STEP_BACKWARD;
           break;
-        case 2:
+        case GDK_BUTTON_MIDDLE:
           return invert ? GTK_SCROLL_PAGE_FORWARD : GTK_SCROLL_PAGE_BACKWARD;
           break;
-        case 3:
+        case GDK_BUTTON_SECONDARY:
           return invert ? GTK_SCROLL_END : GTK_SCROLL_START;
           break;
         }
@@ -2389,13 +2405,13 @@ range_get_scroll_for_grab (GtkRange      *range)
     case MOUSE_STEPPER_D:
       switch (priv->grab_button)
         {
-        case 1:
+        case GDK_BUTTON_PRIMARY:
           return invert ? GTK_SCROLL_STEP_BACKWARD : GTK_SCROLL_STEP_FORWARD;
           break;
-        case 2:
+        case GDK_BUTTON_MIDDLE:
           return invert ? GTK_SCROLL_PAGE_BACKWARD : GTK_SCROLL_PAGE_FORWARD;
           break;
-        case 3:
+        case GDK_BUTTON_SECONDARY:
           return invert ? GTK_SCROLL_START : GTK_SCROLL_END;
           break;
        }
@@ -2422,7 +2438,7 @@ range_get_scroll_for_grab (GtkRange      *range)
 
 static gdouble
 coord_to_value (GtkRange *range,
-                gint      coord)
+                gdouble   coord)
 {
   GtkRangePrivate *priv = range->priv;
   gdouble frac;
@@ -2464,9 +2480,9 @@ coord_to_value (GtkRange *range,
   if (should_invert (range))
     frac = 1.0 - frac;
 
-  value = priv->adjustment->lower + frac * (priv->adjustment->upper -
-                                            priv->adjustment->lower -
-                                            priv->adjustment->page_size);
+  value = gtk_adjustment_get_lower (priv->adjustment) + frac * (gtk_adjustment_get_upper (priv->adjustment) -
+                                            gtk_adjustment_get_lower (priv->adjustment) -
+                                            gtk_adjustment_get_page_size (priv->adjustment));
 
   return value;
 }
@@ -2504,7 +2520,10 @@ gtk_range_button_press (GtkWidget      *widget,
 {
   GtkRange *range = GTK_RANGE (widget);
   GtkRangePrivate *priv = range->priv;
-  GdkDevice *device;
+  GdkDevice *device, *source_device;
+  GdkInputSource source;
+  gboolean primary_warps;
+  gint page_increment_button, warp_button;
 
   if (!gtk_widget_has_focus (widget))
     gtk_widget_grab_focus (widget);
@@ -2514,29 +2533,57 @@ gtk_range_button_press (GtkWidget      *widget,
     return FALSE;
 
   device = gdk_event_get_device ((GdkEvent *) event);
+  source_device = gdk_event_get_source_device ((GdkEvent *) event);
+  source = gdk_device_get_source (source_device);
+
   priv->mouse_x = event->x;
   priv->mouse_y = event->y;
 
   if (gtk_range_update_mouse_location (range))
     gtk_widget_queue_draw (widget);
 
-  if (priv->mouse_location == MOUSE_TROUGH  &&
-      event->button == 1)
+  g_object_get (gtk_widget_get_settings (widget),
+                "gtk-primary-button-warps-slider", &primary_warps,
+                NULL);
+  if (primary_warps)
+    {
+      warp_button = GDK_BUTTON_PRIMARY;
+      page_increment_button = GDK_BUTTON_SECONDARY;
+    }
+  else
+    {
+      warp_button = GDK_BUTTON_MIDDLE;
+      page_increment_button = GDK_BUTTON_PRIMARY;
+    }
+
+  if (priv->mouse_location == MOUSE_SLIDER &&
+      gdk_event_triggers_context_menu ((GdkEvent *)event))
     {
-      /* button 1 steps by page increment, as with button 2 on a stepper
+      gboolean handled;
+
+      g_signal_emit_by_name (widget, "popup-menu", &handled);
+
+      return TRUE;
+    }
+
+  if (source != GDK_SOURCE_TOUCHSCREEN &&
+      priv->mouse_location == MOUSE_TROUGH &&
+      event->button == page_increment_button)
+    {
+      /* button 2 steps by page increment, as with button 2 on a stepper
        */
       GtkScrollType scroll;
       gdouble click_value;
-      
+
       click_value = coord_to_value (range,
                                     priv->orientation == GTK_ORIENTATION_VERTICAL ?
                                     event->y : event->x);
 
-      priv->trough_click_forward = click_value > priv->adjustment->value;
+      priv->trough_click_forward = click_value > gtk_adjustment_get_value (priv->adjustment);
       range_grab_add (range, device, MOUSE_TROUGH, event->button);
-      
+
       scroll = range_get_scroll_for_grab (range);
-      
+
       gtk_range_add_step_timer (range, scroll);
 
       return TRUE;
@@ -2545,7 +2592,9 @@ gtk_range_button_press (GtkWidget      *widget,
             priv->mouse_location == MOUSE_STEPPER_B ||
             priv->mouse_location == MOUSE_STEPPER_C ||
             priv->mouse_location == MOUSE_STEPPER_D) &&
-           (event->button == 1 || event->button == 2 || event->button == 3))
+           (event->button == GDK_BUTTON_PRIMARY ||
+            event->button == GDK_BUTTON_MIDDLE ||
+            event->button == GDK_BUTTON_SECONDARY))
     {
       GtkAllocation allocation;
       GdkRectangle *stepper_area;
@@ -2569,17 +2618,17 @@ gtk_range_button_press (GtkWidget      *widget,
       return TRUE;
     }
   else if ((priv->mouse_location == MOUSE_TROUGH &&
-            event->button == 2) ||
+            (source == GDK_SOURCE_TOUCHSCREEN ||
+             event->button == warp_button)) ||
            priv->mouse_location == MOUSE_SLIDER)
     {
       gboolean need_value_update = FALSE;
 
       /* Any button can be used to drag the slider, but you can start
-       * dragging the slider with a trough click using button 2;
-       * On button 2 press, we warp the slider to mouse position,
-       * then begin the slider drag.
+       * dragging the slider with a trough click using button 1;
+       * we warp the slider to mouse position, then begin the slider drag.
        */
-      if (event->button == 2)
+      if (priv->mouse_location != MOUSE_SLIDER)
         {
           gdouble slider_low_value, slider_high_value, new_value;
           
@@ -2607,6 +2656,12 @@ gtk_range_button_press (GtkWidget      *widget,
           */
          need_value_update = TRUE;
         }
+      else
+        {
+          /* Shift-click in the slider = fine adjustment */
+          if (event->state & GDK_SHIFT_MASK)
+            priv->zoom = TRUE;
+        }
 
       if (priv->orientation == GTK_ORIENTATION_VERTICAL)
         {
@@ -2639,13 +2694,14 @@ update_slider_position (GtkRange *range,
                         gint      mouse_y)
 {
   GtkRangePrivate *priv = range->priv;
-  gint delta;
-  gint c;
+  gdouble delta;
+  gdouble c;
   gdouble new_value;
   gboolean handled;
   gdouble next_value;
   gdouble mark_value;
   gdouble mark_delta;
+  gdouble zoom;
   gint i;
 
   if (priv->orientation == GTK_ORIENTATION_VERTICAL)
@@ -2653,17 +2709,31 @@ update_slider_position (GtkRange *range,
   else
     delta = mouse_x - priv->slide_initial_coordinate;
 
-  c = priv->slide_initial_slider_position + delta;
+  if (priv->zoom)
+    {
+      zoom = MIN(1.0, (priv->orientation == GTK_ORIENTATION_VERTICAL ?
+                       priv->trough.height : priv->trough.width) /
+                       (gtk_adjustment_get_upper (priv->adjustment) -
+                        gtk_adjustment_get_lower (priv->adjustment) -
+                        gtk_adjustment_get_page_size (priv->adjustment)));
+      /* the above is ineffective for scales, so just set a zoom factor */
+      if (zoom == 1.0)
+        zoom = 0.25;
+    }
+  else
+    zoom = 1.0;
+
+  c = priv->slide_initial_slider_position + zoom * delta;
 
   new_value = coord_to_value (range, c);
   next_value = coord_to_value (range, c + 1);
-  mark_delta = fabs (next_value - new_value); 
+  mark_delta = fabs (next_value - new_value);
 
   for (i = 0; i < priv->n_marks; i++)
     {
       mark_value = priv->marks[i];
 
-      if (fabs (priv->adjustment->value - mark_value) < 3 * mark_delta)
+      if (fabs (gtk_adjustment_get_value (priv->adjustment) - mark_value) < 3 * mark_delta)
         {
           if (fabs (new_value - mark_value) < (priv->slider_end - priv->slider_start) * 0.5 * mark_delta)
             {
@@ -2671,19 +2741,17 @@ update_slider_position (GtkRange *range,
               break;
             }
         }
-    }  
+    }
 
   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
                  &handled);
 }
 
-static void 
+static void
 stop_scrolling (GtkRange *range)
 {
   range_grab_remove (range);
   gtk_range_remove_step_timer (range);
-  /* Flush any pending discontinuous/delayed updates */
-  gtk_range_update_value (range);
 }
 
 static gboolean
@@ -2700,7 +2768,7 @@ gtk_range_grab_broken (GtkWidget          *widget,
       priv->grab_location != MOUSE_OUTSIDE)
     {
       if (priv->grab_location == MOUSE_SLIDER)
-       update_slider_position (range, priv->mouse_x, priv->mouse_y);
+        update_slider_position (range, priv->mouse_x, priv->mouse_y);
 
       stop_scrolling (range);
       
@@ -2751,37 +2819,57 @@ 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.
- * 
- * Return value: A good step value for the mouse wheel. 
- * 
+ *
+ * Return value: A good step value for the mouse wheel.
+ *
  * Since: 2.4
  **/
 gdouble
-_gtk_range_get_wheel_delta (GtkRange           *range,
-                           GdkScrollDirection  direction)
+_gtk_range_get_wheel_delta (GtkRange       *range,
+                            GdkEventScroll *event)
 {
   GtkRangePrivate *priv = range->priv;
-  GtkAdjustment *adj = priv->adjustment;
+  GtkAdjustment *adjustment = priv->adjustment;
+  gdouble dx, dy;
   gdouble delta;
+  gdouble page_size;
+  gdouble page_increment;
+  gdouble scroll_unit;
+
+  page_size = gtk_adjustment_get_page_size (adjustment);
+  page_increment = gtk_adjustment_get_page_increment (adjustment);
 
   if (GTK_IS_SCROLLBAR (range))
-    delta = pow (adj->page_size, 2.0 / 3.0);
+    scroll_unit = pow (page_size, 2.0 / 3.0);
   else
-    delta = adj->step_increment * 2;
-  
-  if (direction == GDK_SCROLL_UP ||
-      direction == GDK_SCROLL_LEFT)
-    delta = - delta;
-  
+    scroll_unit = page_increment;
+
+  if (gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy))
+    {
+      if (dx != 0 &&
+          gtk_orientable_get_orientation (GTK_ORIENTABLE (range)) == GTK_ORIENTATION_HORIZONTAL)
+        delta = dx * scroll_unit;
+      else
+        delta = dy * scroll_unit;
+    }
+  else
+    {
+      if (event->direction == GDK_SCROLL_UP ||
+          event->direction == GDK_SCROLL_LEFT)
+        delta = - scroll_unit;
+      else
+        delta = scroll_unit;
+    }
+
   if (priv->inverted)
     delta = - delta;
 
   return delta;
 }
-      
+
 static gboolean
 gtk_range_scroll_event (GtkWidget      *widget,
                        GdkEventScroll *event)
@@ -2791,22 +2879,14 @@ gtk_range_scroll_event (GtkWidget      *widget,
 
   if (gtk_widget_get_realized (widget))
     {
-      GtkAdjustment *adj = priv->adjustment;
       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,
+                     GTK_SCROLL_JUMP, gtk_adjustment_get_value (priv->adjustment) + delta,
                      &handled);
-      
-      /* Policy DELAYED makes sense with scroll events,
-       * but DISCONTINUOUS doesn't, so we update immediately
-       * for DISCONTINUOUS
-       */
-      if (priv->update_policy == GTK_UPDATE_DISCONTINUOUS)
-        gtk_range_update_value (range);
     }
 
   return TRUE;
@@ -2920,7 +3000,7 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment,
 
   priv->recalc_marks = TRUE;
   priv->need_recalc = TRUE;
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
 
   /* now check whether the layout changed  */
   if (layout_changed (priv, &priv_aux))
@@ -2935,20 +3015,6 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment,
    */
 }
 
-static gboolean
-force_repaint (gpointer data)
-{
-  GtkRange *range = GTK_RANGE (data);
-  GtkRangePrivate *priv = range->priv;
-  GtkWidget *widget = GTK_WIDGET (range);
-
-  priv->repaint_id = 0;
-  if (gtk_widget_is_drawable (widget))
-    gdk_window_process_updates (gtk_widget_get_window (widget), FALSE);
-
-  return FALSE;
-}
-
 static void
 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
                                    gpointer       data)
@@ -2958,20 +3024,15 @@ gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
   GtkRangePrivate priv_aux = *priv;
 
   priv->need_recalc = TRUE;
-  gtk_range_calc_layout (range, priv->adjustment->value);
+  gtk_range_calc_layout (range, gtk_adjustment_get_value (priv->adjustment));
   
   /* now check whether the layout changed  */
   if (layout_changed (priv, &priv_aux) ||
       (GTK_IS_SCALE (range) && gtk_scale_get_draw_value (GTK_SCALE (range))))
     {
       gtk_widget_queue_draw (GTK_WIDGET (range));
-      /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
-      if (!priv->repaint_id)
-        priv->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS,
-                                                         181, force_repaint,
-                                                         range, NULL);
     }
-  
+
   /* Note that we don't round off to priv->round_digits here.
    * that's because it's really broken to change a value
    * in response to a change signal on that value; round_digits
@@ -3022,8 +3083,8 @@ step_back (GtkRange *range)
   gdouble newval;
   gboolean handled;
 
-  newval = priv->adjustment->value - priv->adjustment->step_increment;
-  apply_marks (range, priv->adjustment->value, &newval);
+  newval = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_step_increment (priv->adjustment);
+  apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
 }
@@ -3035,8 +3096,8 @@ step_forward (GtkRange *range)
   gdouble newval;
   gboolean handled;
 
-  newval = priv->adjustment->value + priv->adjustment->step_increment;
-  apply_marks (range, priv->adjustment->value, &newval);
+  newval = gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_step_increment (priv->adjustment);
+  apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
 }
@@ -3049,8 +3110,8 @@ page_back (GtkRange *range)
   gdouble newval;
   gboolean handled;
 
-  newval = priv->adjustment->value - priv->adjustment->page_increment;
-  apply_marks (range, priv->adjustment->value, &newval);
+  newval = gtk_adjustment_get_value (priv->adjustment) - gtk_adjustment_get_page_increment (priv->adjustment);
+  apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
 }
@@ -3062,8 +3123,8 @@ page_forward (GtkRange *range)
   gdouble newval;
   gboolean handled;
 
-  newval = priv->adjustment->value + priv->adjustment->page_increment;
-  apply_marks (range, priv->adjustment->value, &newval);
+  newval = gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_increment (priv->adjustment);
+  apply_marks (range, gtk_adjustment_get_value (priv->adjustment), &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
 }
@@ -3075,7 +3136,7 @@ scroll_begin (GtkRange *range)
   gboolean handled;
 
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
-                 GTK_SCROLL_START, priv->adjustment->lower,
+                 GTK_SCROLL_START, gtk_adjustment_get_lower (priv->adjustment),
                  &handled);
 }
 
@@ -3086,7 +3147,7 @@ scroll_end (GtkRange *range)
   gdouble newval;
   gboolean handled;
 
-  newval = priv->adjustment->upper - priv->adjustment->page_size;
+  newval = gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment);
   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_END, newval,
                  &handled);
 }
@@ -3096,7 +3157,7 @@ gtk_range_scroll (GtkRange     *range,
                   GtkScrollType scroll)
 {
   GtkRangePrivate *priv = range->priv;
-  gdouble old_value = priv->adjustment->value;
+  gdouble old_value = gtk_adjustment_get_value (priv->adjustment);
 
   switch (scroll)
     {
@@ -3188,7 +3249,7 @@ gtk_range_scroll (GtkRange     *range,
       break;
     }
 
-  return priv->adjustment->value != old_value;
+  return gtk_adjustment_get_value (priv->adjustment) != old_value;
 }
 
 static void
@@ -3234,13 +3295,6 @@ gtk_range_move_slider (GtkRange     *range,
 
   if (! gtk_range_scroll (range, scroll))
     gtk_widget_error_bell (GTK_WIDGET (range));
-
-  /* Policy DELAYED makes sense with key events,
-   * but DISCONTINUOUS doesn't, so we update immediately
-   * for DISCONTINUOUS
-   */
-  if (priv->update_policy == GTK_UPDATE_DISCONTINUOUS)
-    gtk_range_update_value (range);
 }
 
 static void
@@ -3269,9 +3323,6 @@ gtk_range_get_props (GtkRange  *range,
                        "arrow-displacement-y", &tmp_arrow_displacement_y,
                         NULL);
 
-  if (tmp_stepper_spacing > 0)
-    tmp_trough_under_steppers = FALSE;
-
   if (gtk_widget_get_can_focus (GTK_WIDGET (range)))
     {
       gint focus_line_width;
@@ -3548,13 +3599,15 @@ gtk_range_calc_layout (GtkRange *range,
   GtkRangePrivate *priv = range->priv;
   gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
   gint slider_length;
-  GtkBorder border;
+  GtkBorder border, trough_margin;
   gint n_steppers;
   gboolean has_steppers_ab;
   gboolean has_steppers_cd;
   gboolean trough_under_steppers;
   GdkRectangle range_rect;
   GtkWidget *widget;
+  GtkStyleContext *context;
+  GtkStateFlags state;
 
   if (!priv->need_recalc)
     return;
@@ -3570,6 +3623,13 @@ gtk_range_calc_layout (GtkRange *range,
    */
 
   widget = GTK_WIDGET (range);
+  context = gtk_widget_get_style_context (widget);
+
+  state = gtk_widget_get_state_flags (widget);
+  gtk_style_context_save (context);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_TROUGH);
+  gtk_style_context_get_margin (context, state, &trough_margin);
+  gtk_style_context_restore (context);
 
   gtk_range_get_props (range,
                        &slider_width, &stepper_size,
@@ -3716,9 +3776,9 @@ gtk_range_calc_layout (GtkRange *range,
          * total_adjustment_range) times the trough height in pixels
          */
 
-       if (priv->adjustment->upper - priv->adjustment->lower != 0)
-         height = ((bottom - top) * (priv->adjustment->page_size /
-                                      (priv->adjustment->upper - priv->adjustment->lower)));
+       if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
+         height = ((bottom - top) * (gtk_adjustment_get_page_size (priv->adjustment) /
+                                      (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment))));
        else
           height = priv->min_slider_size;
 
@@ -3730,9 +3790,9 @@ gtk_range_calc_layout (GtkRange *range,
         
         y = top;
 
-       if (priv->adjustment->upper - priv->adjustment->lower - priv->adjustment->page_size != 0)
-         y += (bottom - top - height) * ((adjustment_value - priv->adjustment->lower) /
-                                         (priv->adjustment->upper - priv->adjustment->lower - priv->adjustment->page_size));
+       if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) != 0)
+         y += (bottom - top - height) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
+                                         (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
 
         y = CLAMP (y, top, bottom);
         
@@ -3838,7 +3898,6 @@ gtk_range_calc_layout (GtkRange *range,
        */
       priv->trough.x = priv->stepper_b.x + priv->stepper_b.width + stepper_spacing * has_steppers_ab;
       priv->trough.y = range_rect.y;
-
       priv->trough.width = priv->stepper_c.x - priv->trough.x - stepper_spacing * has_steppers_cd;
       priv->trough.height = range_rect.height;
 
@@ -3865,9 +3924,9 @@ gtk_range_calc_layout (GtkRange *range,
          * total_adjustment_range) times the trough width in pixels
          */
 
-       if (priv->adjustment->upper - priv->adjustment->lower != 0)
-         width = ((right - left) * (priv->adjustment->page_size /
-                                   (priv->adjustment->upper - priv->adjustment->lower)));
+       if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) != 0)
+         width = ((right - left) * (gtk_adjustment_get_page_size (priv->adjustment) /
+                                   (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment))));
        else
          width = priv->min_slider_size;
 
@@ -3879,9 +3938,9 @@ gtk_range_calc_layout (GtkRange *range,
         
         x = left;
 
-       if (priv->adjustment->upper - priv->adjustment->lower - priv->adjustment->page_size != 0)
-          x += (right - left - width) * ((adjustment_value - priv->adjustment->lower) /
-                                         (priv->adjustment->upper - priv->adjustment->lower - priv->adjustment->page_size));
+       if (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment) != 0)
+          x += (right - left - width) * ((adjustment_value - gtk_adjustment_get_lower (priv->adjustment)) /
+                                         (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_lower (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
         
         x = CLAMP (x, left, right);
         
@@ -3903,8 +3962,8 @@ gtk_range_calc_layout (GtkRange *range,
     {
     case GTK_SENSITIVITY_AUTO:
       priv->upper_sensitive =
-        (priv->adjustment->value <
-         (priv->adjustment->upper - priv->adjustment->page_size));
+        (gtk_adjustment_get_value (priv->adjustment) <
+         (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
       break;
 
     case GTK_SENSITIVITY_ON:
@@ -3920,7 +3979,7 @@ gtk_range_calc_layout (GtkRange *range,
     {
     case GTK_SENSITIVITY_AUTO:
       priv->lower_sensitive =
-        (priv->adjustment->value > priv->adjustment->lower);
+        (gtk_adjustment_get_value (priv->adjustment) > gtk_adjustment_get_lower (priv->adjustment));
       break;
 
     case GTK_SENSITIVITY_ON:
@@ -3997,11 +4056,11 @@ gtk_range_real_change_value (GtkRange     *range,
   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
 
   if (priv->restrict_to_fill_level)
-    value = MIN (value, MAX (priv->adjustment->lower,
+    value = MIN (value, MAX (gtk_adjustment_get_lower (priv->adjustment),
                              priv->fill_level));
 
-  value = CLAMP (value, priv->adjustment->lower,
-                 (priv->adjustment->upper - priv->adjustment->page_size));
+  value = CLAMP (value, gtk_adjustment_get_lower (priv->adjustment),
+                 (gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment)));
 
   if (priv->round_digits >= 0)
     {
@@ -4016,49 +4075,17 @@ gtk_range_real_change_value (GtkRange     *range,
       value = floor ((value * power) + 0.5) / power;
     }
 
-  if (priv->adjustment->value != value)
+  if (gtk_adjustment_get_value (priv->adjustment) != value)
     {
       priv->need_recalc = TRUE;
 
       gtk_widget_queue_draw (GTK_WIDGET (range));
 
-      switch (priv->update_policy)
-        {
-        case GTK_UPDATE_CONTINUOUS:
-          gtk_adjustment_set_value (priv->adjustment, value);
-          break;
-
-          /* Delayed means we update after a period of inactivity */
-        case GTK_UPDATE_DELAYED:
-          gtk_range_reset_update_timer (range);
-          /* FALL THRU */
-
-          /* Discontinuous means we update on button release */
-        case GTK_UPDATE_DISCONTINUOUS:
-          /* don't emit value_changed signal */
-          priv->adjustment->value = value;
-          priv->update_pending = TRUE;
-          break;
-        }
+      gtk_adjustment_set_value (priv->adjustment, value);
     }
   return FALSE;
 }
 
-static void
-gtk_range_update_value (GtkRange *range)
-{
-  GtkRangePrivate *priv = range->priv;
-
-  gtk_range_remove_update_timer (range);
-
-  if (priv->update_pending)
-    {
-      gtk_adjustment_value_changed (priv->adjustment);
-
-      priv->update_pending = FALSE;
-    }
-}
-
 struct _GtkRangeStepTimer
 {
   guint timeout_id;
@@ -4134,41 +4161,17 @@ gtk_range_remove_step_timer (GtkRange *range)
     }
 }
 
-static gboolean
-update_timeout (gpointer data)
-{
-  GtkRange *range = GTK_RANGE (data);
-  GtkRangePrivate *priv = range->priv;
-
-  gtk_range_update_value (range);
-  priv->update_timeout_id = 0;
-
-  /* self-remove */
-  return FALSE;
-}
-
-static void
-gtk_range_reset_update_timer (GtkRange *range)
+void
+_gtk_range_set_has_origin (GtkRange *range,
+                           gboolean  has_origin)
 {
-  GtkRangePrivate *priv = range->priv;
-
-  gtk_range_remove_update_timer (range);
-
-  priv->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
-                                                     update_timeout,
-                                                     range);
+  range->priv->has_origin = has_origin;
 }
 
-static void
-gtk_range_remove_update_timer (GtkRange *range)
+gboolean
+_gtk_range_get_has_origin (GtkRange *range)
 {
-  GtkRangePrivate *priv = range->priv;
-
-  if (priv->update_timeout_id != 0)
-    {
-      g_source_remove (priv->update_timeout_id);
-      priv->update_timeout_id = 0;
-    }
+  return range->priv->has_origin;
 }
 
 void
@@ -4207,11 +4210,45 @@ _gtk_range_get_stop_positions (GtkRange  *range,
   return priv->n_marks;
 }
 
+/**
+ * gtk_range_set_round_digits:
+ * @range: a #GtkRange
+ * @round_digits: the precision in digits, or -1
+ *
+ * Sets the number of digits to round the value to when
+ * it changes. See #GtkRange::change-value.
+ *
+ * Since: 2.24
+ */
 void
-_gtk_range_set_round_digits (GtkRange *range,
-                             gint     round_digits)
+gtk_range_set_round_digits (GtkRange *range,
+                            gint      round_digits)
 {
+  g_return_if_fail (GTK_IS_RANGE (range));
+  g_return_if_fail (round_digits >= -1);
+
   range->priv->round_digits = round_digits;
+
+  g_object_notify (G_OBJECT (range), "round-digits");
+}
+
+/**
+ * gtk_range_get_round_digits:
+ * @range: a #GtkRange
+ *
+ * Gets the number of digits to round the value to when
+ * it changes. See #GtkRange::change-value.
+ *
+ * Return value: the number of digits to round to
+ *
+ * Since: 2.24
+ */
+gint
+gtk_range_get_round_digits (GtkRange *range)
+{
+  g_return_val_if_fail (GTK_IS_RANGE (range), -1);
+
+  return range->priv->round_digits;
 }
 
 void