]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrange.c
filechooserbutton: Don't duplicate tests for GTK_RESPONSE_DELETE_EVENT
[~andy/gtk] / gtk / gtkrange.c
index 678a2dd2e43a957c98d47674d6587821c7f93fe7..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,12 +81,10 @@ 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;
 
@@ -102,7 +102,7 @@ struct _GtkRangePrivate
   GQuark             slider_detail_quark;
   GQuark             stepper_detail_quark[4];
 
-  gboolean recalc_marks;
+  GtkOrientation     orientation;
 
   gdouble  fill_level;
   gdouble *marks;
@@ -116,8 +116,6 @@ struct _GtkRangePrivate
   gint  slider_start;                /* Slider range along the long dimension, in widget->window coords */
   gint  slider_end;
 
-  guint repaint_id;
-
   /* Steppers are: < > ---- < >
    *               a b      c d
    */
@@ -129,6 +127,7 @@ 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 */
 
@@ -136,9 +135,17 @@ struct _GtkRangePrivate
   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 */
 };
 
 
@@ -151,7 +158,8 @@ enum {
   PROP_UPPER_STEPPER_SENSITIVITY,
   PROP_SHOW_FILL_LEVEL,
   PROP_RESTRICT_TO_FILL_LEVEL,
-  PROP_FILL_LEVEL
+  PROP_FILL_LEVEL,
+  PROP_ROUND_DIGITS
 };
 
 enum {
@@ -373,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
@@ -385,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
    */
@@ -495,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"),
@@ -523,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,
@@ -556,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
    */
@@ -584,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
@@ -606,6 +631,7 @@ 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_ADJUSTMENT:
@@ -629,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;
@@ -670,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;
@@ -708,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;
@@ -898,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.
  *
@@ -1005,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.
@@ -1033,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.
@@ -1465,10 +1500,6 @@ gtk_range_destroy (GtkWidget *widget)
 
   gtk_range_remove_step_timer (range);
 
-  if (priv->repaint_id)
-    g_source_remove (priv->repaint_id);
-  priv->repaint_id = 0;
-
   if (priv->adjustment)
     {
       g_signal_handlers_disconnect_by_func (priv->adjustment,
@@ -1590,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);
@@ -1601,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)
         {
@@ -1660,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);
 }
@@ -1695,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);
 
@@ -1705,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
@@ -1716,7 +1749,7 @@ gtk_range_unrealize (GtkWidget *widget)
 
   gtk_range_remove_step_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;
 
@@ -1814,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
@@ -1823,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;
 
@@ -1872,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)
@@ -1882,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);
@@ -1909,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;
     }
@@ -1941,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) &&
       gtk_adjustment_get_upper (priv->adjustment) == gtk_adjustment_get_lower (priv->adjustment))
-    draw_trough = FALSE;
+    {
+      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),
@@ -1970,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, 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
@@ -2009,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;
@@ -2057,51 +2104,95 @@ 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);
@@ -2109,13 +2200,12 @@ gtk_range_draw (GtkWidget      *widget,
       if (priv->show_fill_level &&
           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);
@@ -2124,10 +2214,10 @@ gtk_range_draw (GtkWidget      *widget,
                               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 +
+          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) -
@@ -2137,11 +2227,11 @@ gtk_range_draw (GtkWidget      *widget,
 
               if (should_invert (range))
                 fill_x += priv->trough.width - fill_width;
-           }
-         else
-           {
-             fill_y      = priv->trough.y;
-             fill_height = (priv->slider.height +
+            }
+          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) -
@@ -2151,54 +2241,43 @@ gtk_range_draw (GtkWidget      *widget,
 
               if (should_invert (range))
                 fill_y += priv->trough.height - fill_height;
-           }
-
-         if (fill_level < gtk_adjustment_get_upper (priv->adjustment) - gtk_adjustment_get_page_size (priv->adjustment))
-           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);
@@ -2211,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;
 }
 
@@ -2253,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;
@@ -2278,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;
@@ -2290,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
@@ -2306,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;
         }
@@ -2323,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;
        }
@@ -2356,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;
@@ -2438,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);
@@ -2448,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)
     {
-      /* button 1 steps by page increment, as with button 2 on a stepper
+      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))
+    {
+      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 > 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;
@@ -2479,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;
@@ -2503,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;
           
@@ -2541,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)
         {
@@ -2573,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)
@@ -2587,11 +2709,25 @@ 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++)
     {
@@ -2605,13 +2741,13 @@ 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);
@@ -2632,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);
       
@@ -2683,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 *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 (gtk_adjustment_get_page_size (adjustment), 2.0 / 3.0);
+    scroll_unit = pow (page_size, 2.0 / 3.0);
   else
-    delta = gtk_adjustment_get_step_increment (adjustment) * 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)
@@ -2726,7 +2882,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, gtk_adjustment_get_value (priv->adjustment) + delta,
@@ -2859,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)
@@ -2889,13 +3031,8 @@ gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
       (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
@@ -3186,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;
@@ -3465,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;
@@ -3487,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,
@@ -3755,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;
 
@@ -4019,6 +4161,19 @@ gtk_range_remove_step_timer (GtkRange *range)
     }
 }
 
+void
+_gtk_range_set_has_origin (GtkRange *range,
+                           gboolean  has_origin)
+{
+  range->priv->has_origin = has_origin;
+}
+
+gboolean
+_gtk_range_get_has_origin (GtkRange *range)
+{
+  return range->priv->has_origin;
+}
+
 void
 _gtk_range_set_stop_values (GtkRange *range,
                             gdouble  *values,
@@ -4055,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