]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrange.c
Bug 569240 - Crasher when using markers
[~andy/gtk] / gtk / gtkrange.c
index b1dcc14491126c5e9f940289f199a282f1bc12b3..6db35b5f11840192a984028cdf38fc6205ef0e36 100644 (file)
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
-#include <config.h>
+#include "config.h"
+
 #include <stdio.h>
 #include <math.h>
+
 #include <gdk/gdkkeysyms.h>
-#include "gtkintl.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
+#include "gtkorientable.h"
 #include "gtkrange.h"
-#include "gtkintl.h"
 #include "gtkscrollbar.h"
 #include "gtkprivate.h"
+#include "gtkintl.h"
 #include "gtkalias.h"
 
 #define SCROLL_DELAY_FACTOR 5    /* Scroll repeat multiplier */
 
 enum {
   PROP_0,
+  PROP_ORIENTATION,
   PROP_UPDATE_POLICY,
   PROP_ADJUSTMENT,
   PROP_INVERTED,
   PROP_LOWER_STEPPER_SENSITIVITY,
-  PROP_UPPER_STEPPER_SENSITIVITY
+  PROP_UPPER_STEPPER_SENSITIVITY,
+  PROP_SHOW_FILL_LEVEL,
+  PROP_RESTRICT_TO_FILL_LEVEL,
+  PROP_FILL_LEVEL
 };
 
 enum {
@@ -69,6 +75,8 @@ typedef enum {
   MOUSE_WIDGET /* inside widget but not in any of the above GUI elements */
 } MouseLocation;
 
+#define GTK_RANGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RANGE, GtkRangeLayout))
+
 struct _GtkRangeLayout
 {
   /* These are in widget->window coordinates */
@@ -88,18 +96,35 @@ struct _GtkRangeLayout
   /* last mouse coords we got, or -1 if mouse is outside the range */
   gint mouse_x;
   gint mouse_y;
+
   /* "grabbed" mouse location, OUTSIDE for no grab */
   MouseLocation grab_location;
-  gint grab_button; /* 0 if none */
+  guint grab_button : 8; /* 0 if none */
 
   /* Stepper sensitivity */
+  guint lower_sensitive : 1;
+  guint upper_sensitive : 1;
+
+  /* Fill level */
+  guint show_fill_level : 1;
+  guint restrict_to_fill_level : 1;
+
   GtkSensitivityType lower_sensitivity;
   GtkSensitivityType upper_sensitivity;
+  guint repaint_id;
+
+  gdouble fill_level;
+
+  GQuark slider_detail_quark;
+  GQuark stepper_detail_quark;
+  gdouble *marks;
+  gint *mark_pos;
+  gint n_marks;
+  gboolean recalc_marks;
 };
 
 
-static void gtk_range_class_init     (GtkRangeClass    *klass);
-static void gtk_range_init           (GtkRange         *range);
 static void gtk_range_set_property   (GObject          *object,
                                       guint             prop_id,
                                       const GValue     *value,
@@ -109,7 +134,6 @@ static void gtk_range_get_property   (GObject          *object,
                                       GValue           *value,
                                       GParamSpec       *pspec);
 static void gtk_range_destroy        (GtkObject        *object);
-static void gtk_range_finalize       (GObject          *object);
 static void gtk_range_size_request   (GtkWidget        *widget,
                                       GtkRequisition   *requisition);
 static void gtk_range_size_allocate  (GtkWidget        *widget,
@@ -118,17 +142,17 @@ static void gtk_range_realize        (GtkWidget        *widget);
 static void gtk_range_unrealize      (GtkWidget        *widget);
 static void gtk_range_map            (GtkWidget        *widget);
 static void gtk_range_unmap          (GtkWidget        *widget);
-static gint gtk_range_expose         (GtkWidget        *widget,
+static gboolean gtk_range_expose         (GtkWidget        *widget,
                                       GdkEventExpose   *event);
-static gint gtk_range_button_press   (GtkWidget        *widget,
+static gboolean gtk_range_button_press   (GtkWidget        *widget,
                                       GdkEventButton   *event);
-static gint gtk_range_button_release (GtkWidget        *widget,
+static gboolean gtk_range_button_release (GtkWidget        *widget,
                                       GdkEventButton   *event);
-static gint gtk_range_motion_notify  (GtkWidget        *widget,
+static gboolean gtk_range_motion_notify  (GtkWidget        *widget,
                                       GdkEventMotion   *event);
-static gint gtk_range_enter_notify   (GtkWidget        *widget,
+static gboolean gtk_range_enter_notify   (GtkWidget        *widget,
                                       GdkEventCrossing *event);
-static gint gtk_range_leave_notify   (GtkWidget        *widget,
+static gboolean gtk_range_leave_notify   (GtkWidget        *widget,
                                       GdkEventCrossing *event);
 static gboolean gtk_range_grab_broken (GtkWidget          *widget,
                                       GdkEventGrabBroken *event);
@@ -136,7 +160,7 @@ static void gtk_range_grab_notify    (GtkWidget          *widget,
                                      gboolean            was_grabbed);
 static void gtk_range_state_changed  (GtkWidget          *widget,
                                      GtkStateType        previous_state);
-static gint gtk_range_scroll_event   (GtkWidget        *widget,
+static gboolean gtk_range_scroll_event   (GtkWidget        *widget,
                                       GdkEventScroll   *event);
 static void gtk_range_style_set      (GtkWidget        *widget,
                                       GtkStyle         *previous_style);
@@ -151,26 +175,32 @@ static void gtk_range_move_slider              (GtkRange         *range,
                                                 GtkScrollType     scroll);
 
 /* Internals */
-static void          gtk_range_scroll                   (GtkRange      *range,
+static gboolean      gtk_range_scroll                   (GtkRange      *range,
                                                          GtkScrollType  scroll);
 static gboolean      gtk_range_update_mouse_location    (GtkRange      *range);
 static void          gtk_range_calc_layout              (GtkRange      *range,
                                                         gdouble        adjustment_value);
+static void          gtk_range_calc_marks               (GtkRange      *range);
 static void          gtk_range_get_props                (GtkRange      *range,
                                                          gint          *slider_width,
                                                          gint          *stepper_size,
+                                                         gint          *focus_width,
                                                          gint          *trough_border,
                                                          gint          *stepper_spacing,
+                                                         gboolean      *trough_under_steppers,
                                                         gint          *arrow_displacement_x,
                                                         gint          *arrow_displacement_y);
 static void          gtk_range_calc_request             (GtkRange      *range,
                                                          gint           slider_width,
                                                          gint           stepper_size,
+                                                         gint           focus_width,
                                                          gint           trough_border,
                                                          gint           stepper_spacing,
                                                          GdkRectangle  *range_rect,
                                                          GtkBorder     *border,
                                                          gint          *n_steppers_p,
+                                                         gboolean      *has_steppers_ab,
+                                                         gboolean      *has_steppers_cd,
                                                          gint          *slider_length_p);
 static void          gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
                                                          gpointer       data);
@@ -191,37 +221,12 @@ static gboolean      gtk_range_key_press                (GtkWidget     *range,
                                                         GdkEventKey   *event);
 
 
-static GtkWidgetClass *parent_class = NULL;
-static guint signals[LAST_SIGNAL];
-
-
-GType
-gtk_range_get_type (void)
-{
-  static GType range_type = 0;
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GtkRange, gtk_range, GTK_TYPE_WIDGET,
+                                  G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE,
+                                                         NULL))
 
-  if (!range_type)
-    {
-      static const GTypeInfo range_info =
-      {
-       sizeof (GtkRangeClass),
-       NULL,           /* base_init */
-       NULL,           /* base_finalize */
-       (GClassInitFunc) gtk_range_class_init,
-       NULL,           /* class_finalize */
-       NULL,           /* class_data */
-       sizeof (GtkRange),
-       0,              /* n_preallocs */
-       (GInstanceInitFunc) gtk_range_init,
-       NULL,           /* value_table */
-      };
-
-      range_type = g_type_register_static (GTK_TYPE_WIDGET, I_("GtkRange"),
-                                          &range_info, G_TYPE_FLAG_ABSTRACT);
-    }
+static guint signals[LAST_SIGNAL];
 
-  return range_type;
-}
 
 static void
 gtk_range_class_init (GtkRangeClass *class)
@@ -234,11 +239,9 @@ gtk_range_class_init (GtkRangeClass *class)
   object_class = (GtkObjectClass*) class;
   widget_class = (GtkWidgetClass*) class;
 
-  parent_class = g_type_class_peek_parent (class);
-
   gobject_class->set_property = gtk_range_set_property;
   gobject_class->get_property = gtk_range_get_property;
-  gobject_class->finalize = gtk_range_finalize;
+
   object_class->destroy = gtk_range_destroy;
 
   widget_class->size_request = gtk_range_size_request;
@@ -266,17 +269,23 @@ gtk_range_class_init (GtkRangeClass *class)
   class->slider_detail = "slider";
   class->stepper_detail = "stepper";
 
+  /**
+   * GtkRange::value-changed:
+   * @range: the #GtkRange
+   *
+   * Emitted when the range value changes.
+   */
   signals[VALUE_CHANGED] =
-    g_signal_new (I_("value_changed"),
+    g_signal_new (I_("value-changed"),
                   G_TYPE_FROM_CLASS (gobject_class),
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkRangeClass, value_changed),
                   NULL, NULL,
-                  _gtk_marshal_NONE__NONE,
+                  _gtk_marshal_VOID__VOID,
                   G_TYPE_NONE, 0);
   
   signals[ADJUST_BOUNDS] =
-    g_signal_new (I_("adjust_bounds"),
+    g_signal_new (I_("adjust-bounds"),
                   G_TYPE_FROM_CLASS (gobject_class),
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkRangeClass, adjust_bounds),
@@ -285,8 +294,15 @@ gtk_range_class_init (GtkRangeClass *class)
                   G_TYPE_NONE, 1,
                   G_TYPE_DOUBLE);
   
+  /**
+   * GtkRange::move-slider:
+   * @range: the #GtkRange
+   * @step: how to move the slider
+   *
+   * Virtual function that moves the slider. Used for keybindings.
+   */
   signals[MOVE_SLIDER] =
-    g_signal_new (I_("move_slider"),
+    g_signal_new (I_("move-slider"),
                   G_TYPE_FROM_CLASS (gobject_class),
                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                   G_STRUCT_OFFSET (GtkRangeClass, move_slider),
@@ -297,11 +313,11 @@ gtk_range_class_init (GtkRangeClass *class)
 
   /**
    * GtkRange::change-value:
-   * @range: the range that received the signal.
-   * @scroll: the type of scroll action that was performed.
-   * @value: the new value resulting from the scroll action.
+   * @range: the range 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.
+   * signal, %FALSE to propagate the signal further
    *
    * The ::change-value signal is emitted when a scroll action is
    * performed on a range.  It allows an application to determine the
@@ -322,7 +338,7 @@ gtk_range_class_init (GtkRangeClass *class)
    * Since: 2.6
    */
   signals[CHANGE_VALUE] =
-    g_signal_new (I_("change_value"),
+    g_signal_new (I_("change-value"),
                   G_TYPE_FROM_CLASS (gobject_class),
                   G_SIGNAL_RUN_LAST,
                   G_STRUCT_OFFSET (GtkRangeClass, change_value),
@@ -331,7 +347,11 @@ gtk_range_class_init (GtkRangeClass *class)
                   G_TYPE_BOOLEAN, 2,
                   GTK_TYPE_SCROLL_TYPE,
                   G_TYPE_DOUBLE);
-  
+
+  g_object_class_override_property (gobject_class,
+                                    PROP_ORIENTATION,
+                                    "orientation");
+
   g_object_class_install_property (gobject_class,
                                    PROP_UPDATE_POLICY,
                                    g_param_spec_enum ("update-policy",
@@ -375,6 +395,58 @@ gtk_range_class_init (GtkRangeClass *class)
                                                      GTK_SENSITIVITY_AUTO,
                                                      GTK_PARAM_READWRITE));
 
+  /**
+   * GtkRange:show-fill-level:
+   *
+   * The show-fill-level property controls whether fill level indicator
+   * graphics are displayed on the trough. See
+   * gtk_range_set_show_fill_level().
+   *
+   * Since: 2.12
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_SHOW_FILL_LEVEL,
+                                   g_param_spec_boolean ("show-fill-level",
+                                                         P_("Show Fill Level"),
+                                                         P_("Whether to display a fill level indicator graphics on trough."),
+                                                         FALSE,
+                                                         GTK_PARAM_READWRITE));
+
+  /**
+   * GtkRange:restrict-to-fill-level:
+   *
+   * The restrict-to-fill-level property controls whether slider
+   * movement is restricted to an upper boundary set by the
+   * fill level. See gtk_range_set_restrict_to_fill_level().
+   *
+   * Since: 2.12
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_RESTRICT_TO_FILL_LEVEL,
+                                   g_param_spec_boolean ("restrict-to-fill-level",
+                                                         P_("Restrict to Fill Level"),
+                                                         P_("Whether to restrict the upper boundary to the fill level."),
+                                                         TRUE,
+                                                         GTK_PARAM_READWRITE));
+
+  /**
+   * GtkRange:fill-level:
+   *
+   * The fill level (e.g. prebuffering of a network stream).
+   * See gtk_range_set_fill_level().
+   *
+   * Since: 2.12
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_FILL_LEVEL,
+                                   g_param_spec_double ("fill-level",
+                                                       P_("Fill Level"),
+                                                       P_("The fill level."),
+                                                       -G_MAXDOUBLE,
+                                                       G_MAXDOUBLE,
+                                                        G_MAXDOUBLE,
+                                                        GTK_PARAM_READWRITE));
+
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("slider-width",
                                                             P_("Slider Width"),
@@ -399,6 +471,14 @@ gtk_range_class_init (GtkRangeClass *class)
                                                             G_MAXINT,
                                                             14,
                                                             GTK_PARAM_READABLE));
+  /**
+   * 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,
                                           g_param_spec_int ("stepper-spacing",
                                                             P_("Stepper Spacing"),
@@ -425,11 +505,59 @@ gtk_range_class_init (GtkRangeClass *class)
                                                             GTK_PARAM_READABLE));
 
   gtk_widget_class_install_style_property (widget_class,
-                                          g_param_spec_boolean ("activate_slider",
+                                          g_param_spec_boolean ("activate-slider",
                                                                  P_("Draw slider ACTIVE during drag"),
                                                                 P_("With this option set to TRUE, sliders will be drawn ACTIVE and with shadow IN while they are dragged"),
                                                                 FALSE,
-                                                                G_PARAM_READABLE));
+                                                                GTK_PARAM_READABLE));
+
+  /**
+   * GtkRange:trough-side-details:
+   *
+   * When %TRUE, the parts of the trough on the two sides of the 
+   * slider are drawn with different details.
+   *
+   * Since: 2.10
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_boolean ("trough-side-details",
+                                                                 P_("Trough Side Details"),
+                                                                 P_("When TRUE, the parts of the trough on the two sides of the slider are drawn with different details"),
+                                                                 FALSE,
+                                                                 GTK_PARAM_READABLE));
+
+  /**
+   * 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.
+   *
+   * Since: 2.10
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_boolean ("trough-under-steppers",
+                                                                 P_("Trough Under Steppers"),
+                                                                 P_("Whether to draw trough for full length of range or exclude the steppers and spacing"),
+                                                                 TRUE,
+                                                                 GTK_PARAM_READABLE));
+
+  /**
+   * GtkRange:arrow-scaling:
+   *
+   * The arrow size proportion relative to the scroll button size.
+   *
+   * Since: 2.14
+   */
+  gtk_widget_class_install_style_property (widget_class,
+                                           g_param_spec_float ("arrow-scaling",
+                                                              P_("Arrow scaling"),
+                                                              P_("Arrow scaling with regard to scroll button size"),
+                                                              0.0, 1.0, 0.5,
+                                                              GTK_PARAM_READABLE));
+
+  g_type_class_add_private (class, sizeof (GtkRangeLayout));
 }
 
 static void
@@ -438,12 +566,18 @@ gtk_range_set_property (GObject      *object,
                        const GValue *value,
                        GParamSpec   *pspec)
 {
-  GtkRange *range;
-
-  range = GTK_RANGE (object);
+  GtkRange *range = GTK_RANGE (object);
 
   switch (prop_id)
     {
+    case PROP_ORIENTATION:
+      range->orientation = g_value_get_enum (value);
+
+      range->layout->slider_detail_quark = 0;
+      range->layout->stepper_detail_quark = 0;
+
+      gtk_widget_queue_resize (GTK_WIDGET (range));
+      break;
     case PROP_UPDATE_POLICY:
       gtk_range_set_update_policy (range, g_value_get_enum (value));
       break;
@@ -459,6 +593,15 @@ gtk_range_set_property (GObject      *object,
     case PROP_UPPER_STEPPER_SENSITIVITY:
       gtk_range_set_upper_stepper_sensitivity (range, g_value_get_enum (value));
       break;
+    case PROP_SHOW_FILL_LEVEL:
+      gtk_range_set_show_fill_level (range, g_value_get_boolean (value));
+      break;
+    case PROP_RESTRICT_TO_FILL_LEVEL:
+      gtk_range_set_restrict_to_fill_level (range, g_value_get_boolean (value));
+      break;
+    case PROP_FILL_LEVEL:
+      gtk_range_set_fill_level (range, g_value_get_double (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -471,12 +614,13 @@ gtk_range_get_property (GObject      *object,
                        GValue       *value,
                        GParamSpec   *pspec)
 {
-  GtkRange *range;
-
-  range = GTK_RANGE (object);
+  GtkRange *range = GTK_RANGE (object);
 
   switch (prop_id)
     {
+    case PROP_ORIENTATION:
+      g_value_set_enum (value, range->orientation);
+      break;
     case PROP_UPDATE_POLICY:
       g_value_set_enum (value, range->update_policy);
       break;
@@ -492,6 +636,15 @@ gtk_range_get_property (GObject      *object,
     case PROP_UPPER_STEPPER_SENSITIVITY:
       g_value_set_enum (value, gtk_range_get_upper_stepper_sensitivity (range));
       break;
+    case PROP_SHOW_FILL_LEVEL:
+      g_value_set_boolean (value, gtk_range_get_show_fill_level (range));
+      break;
+    case PROP_RESTRICT_TO_FILL_LEVEL:
+      g_value_set_boolean (value, gtk_range_get_restrict_to_fill_level (range));
+      break;
+    case PROP_FILL_LEVEL:
+      g_value_set_double (value, gtk_range_get_fill_level (range));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -503,6 +656,7 @@ gtk_range_init (GtkRange *range)
 {
   GTK_WIDGET_SET_FLAGS (range, GTK_NO_WINDOW);
 
+  range->orientation = GTK_ORIENTATION_HORIZONTAL;
   range->adjustment = NULL;
   range->update_policy = GTK_UPDATE_CONTINUOUS;
   range->inverted = FALSE;
@@ -514,7 +668,7 @@ gtk_range_init (GtkRange *range)
   range->has_stepper_d = FALSE;
   range->need_recalc = TRUE;
   range->round_digits = -1;
-  range->layout = g_new0 (GtkRangeLayout, 1);
+  range->layout = GTK_RANGE_GET_PRIVATE (range);
   range->layout->mouse_location = MOUSE_OUTSIDE;
   range->layout->mouse_x = -1;
   range->layout->mouse_y = -1;
@@ -522,6 +676,11 @@ gtk_range_init (GtkRange *range)
   range->layout->grab_button = 0;
   range->layout->lower_sensitivity = GTK_SENSITIVITY_AUTO;
   range->layout->upper_sensitivity = GTK_SENSITIVITY_AUTO;
+  range->layout->lower_sensitive = TRUE;
+  range->layout->upper_sensitive = TRUE;
+  range->layout->show_fill_level = FALSE;
+  range->layout->restrict_to_fill_level = TRUE;
+  range->layout->fill_level = G_MAXDOUBLE;
   range->timer = NULL;  
 }
 
@@ -560,7 +719,6 @@ gtk_range_get_adjustment (GtkRange *range)
  * 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,
@@ -603,7 +761,6 @@ gtk_range_get_update_policy (GtkRange *range)
  * is normally 0 for #GtkScale and nonzero for #GtkScrollbar, and
  * indicates the size of the visible area of the widget being scrolled.
  * The page size affects the size of the scrollbar slider.
- * 
  **/
 void
 gtk_range_set_adjustment (GtkRange      *range,
@@ -635,7 +792,7 @@ gtk_range_set_adjustment (GtkRange      *range,
       g_signal_connect (adjustment, "changed",
                        G_CALLBACK (gtk_range_adjustment_changed),
                        range);
-      g_signal_connect (adjustment, "value_changed",
+      g_signal_connect (adjustment, "value-changed",
                        G_CALLBACK (gtk_range_adjustment_value_changed),
                        range);
       
@@ -653,7 +810,6 @@ gtk_range_set_adjustment (GtkRange      *range,
  * slider moves from top to bottom or left to right. Inverted
  * ranges have higher values at the top or on the right rather than
  * on the bottom or left.
- * 
  **/
 void
 gtk_range_set_inverted (GtkRange *range,
@@ -695,7 +851,7 @@ gtk_range_get_inverted (GtkRange *range)
  * Sets the sensitivity policy for the stepper that points to the
  * 'lower' end of the GtkRange's adjustment.
  *
- * Sine: 2.10
+ * Since: 2.10
  **/
 void
 gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
@@ -706,7 +862,11 @@ gtk_range_set_lower_stepper_sensitivity (GtkRange           *range,
   if (range->layout->lower_sensitivity != sensitivity)
     {
       range->layout->lower_sensitivity = sensitivity;
+
+      range->need_recalc = TRUE;
+      gtk_range_calc_layout (range, range->adjustment->value);
       gtk_widget_queue_draw (GTK_WIDGET (range));
+
       g_object_notify (G_OBJECT (range), "lower-stepper-sensitivity");
     }
 }
@@ -738,7 +898,7 @@ gtk_range_get_lower_stepper_sensitivity (GtkRange *range)
  * Sets the sensitivity policy for the stepper that points to the
  * 'upper' end of the GtkRange's adjustment.
  *
- * Sine: 2.10
+ * Since: 2.10
  **/
 void
 gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
@@ -749,7 +909,11 @@ gtk_range_set_upper_stepper_sensitivity (GtkRange           *range,
   if (range->layout->upper_sensitivity != sensitivity)
     {
       range->layout->upper_sensitivity = sensitivity;
+
+      range->need_recalc = TRUE;
+      gtk_range_calc_layout (range, range->adjustment->value);
       gtk_widget_queue_draw (GTK_WIDGET (range));
+
       g_object_notify (G_OBJECT (range), "upper-stepper-sensitivity");
     }
 }
@@ -783,7 +947,6 @@ gtk_range_get_upper_stepper_sensitivity (GtkRange *range)
  * The step size is used when the user clicks the #GtkScrollbar
  * arrows or moves #GtkScale via arrow keys. The page size
  * is used for example when moving via Page Up or Page Down keys.
- * 
  **/
 void
 gtk_range_set_increments (GtkRange *range,
@@ -821,9 +984,11 @@ gtk_range_set_range (GtkRange *range,
   range->adjustment->lower = min;
   range->adjustment->upper = max;
 
-  value = CLAMP (range->adjustment->value,
-                 range->adjustment->lower,
-                 (range->adjustment->upper - range->adjustment->page_size));
+  value = range->adjustment->value;
+
+  if (range->layout->restrict_to_fill_level)
+    value = MIN (value, MAX (range->adjustment->lower,
+                             range->layout->fill_level));
 
   gtk_adjustment_set_value (range->adjustment, value);
   gtk_adjustment_changed (range->adjustment);
@@ -836,18 +1001,18 @@ gtk_range_set_range (GtkRange *range,
  *
  * Sets the current value of the range; if the value is outside the
  * minimum or maximum range values, it will be clamped to fit inside
- * them. The range emits the "value_changed" signal if the value
- * changes.
- * 
+ * them. The range emits the #GtkRange::value-changed signal if the 
+ * value changes.
  **/
 void
 gtk_range_set_value (GtkRange *range,
                      gdouble   value)
 {
   g_return_if_fail (GTK_IS_RANGE (range));
-  
-  value = CLAMP (value, range->adjustment->lower,
-                 (range->adjustment->upper - range->adjustment->page_size));
+
+  if (range->layout->restrict_to_fill_level)
+    value = MIN (value, MAX (range->adjustment->lower,
+                             range->layout->fill_level));
 
   gtk_adjustment_set_value (range->adjustment, value);
 }
@@ -868,6 +1033,159 @@ gtk_range_get_value (GtkRange *range)
   return range->adjustment->value;
 }
 
+/**
+ * gtk_range_set_show_fill_level:
+ * @range:           A #GtkRange
+ * @show_fill_level: Whether a fill level indicator graphics is shown.
+ *
+ * Sets whether a graphical fill level is show on the trough. See
+ * gtk_range_set_fill_level() for a general description of the fill
+ * level concept.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_range_set_show_fill_level (GtkRange *range,
+                               gboolean  show_fill_level)
+{
+  g_return_if_fail (GTK_IS_RANGE (range));
+
+  show_fill_level = show_fill_level ? TRUE : FALSE;
+
+  if (show_fill_level != range->layout->show_fill_level)
+    {
+      range->layout->show_fill_level = show_fill_level;
+      g_object_notify (G_OBJECT (range), "show-fill-level");
+      gtk_widget_queue_draw (GTK_WIDGET (range));
+    }
+}
+
+/**
+ * gtk_range_get_show_fill_level:
+ * @range: A #GtkRange
+ *
+ * Gets whether the range displays the fill level graphically.
+ *
+ * Return value: %TRUE if @range shows the fill level.
+ *
+ * Since: 2.12
+ **/
+gboolean
+gtk_range_get_show_fill_level (GtkRange *range)
+{
+  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
+
+  return range->layout->show_fill_level;
+}
+
+/**
+ * gtk_range_set_restrict_to_fill_level:
+ * @range:                  A #GtkRange
+ * @restrict_to_fill_level: Whether the fill level restricts slider movement.
+ *
+ * Sets whether the slider is restricted to the fill level. See
+ * gtk_range_set_fill_level() for a general description of the fill
+ * level concept.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_range_set_restrict_to_fill_level (GtkRange *range,
+                                      gboolean  restrict_to_fill_level)
+{
+  g_return_if_fail (GTK_IS_RANGE (range));
+
+  restrict_to_fill_level = restrict_to_fill_level ? TRUE : FALSE;
+
+  if (restrict_to_fill_level != range->layout->restrict_to_fill_level)
+    {
+      range->layout->restrict_to_fill_level = restrict_to_fill_level;
+      g_object_notify (G_OBJECT (range), "restrict-to-fill-level");
+
+      gtk_range_set_value (range, gtk_range_get_value (range));
+    }
+}
+
+/**
+ * gtk_range_get_restrict_to_fill_level:
+ * @range: A #GtkRange
+ *
+ * Gets whether the range is restricted to the fill level.
+ *
+ * Return value: %TRUE if @range is restricted to the fill level.
+ *
+ * Since: 2.12
+ **/
+gboolean
+gtk_range_get_restrict_to_fill_level (GtkRange *range)
+{
+  g_return_val_if_fail (GTK_IS_RANGE (range), FALSE);
+
+  return range->layout->restrict_to_fill_level;
+}
+
+/**
+ * gtk_range_set_fill_level:
+ * @range: a #GtkRange
+ * @fill_level: the new position of the fill level indicator
+ *
+ * Set the new position of the fill level indicator.
+ *
+ * The "fill level" is probably best described by its most prominent
+ * use case, which is an indicator for the amount of pre-buffering in
+ * a streaming media player. In that use case, the value of the range
+ * would indicate the current play position, and the fill level would
+ * be the position up to which the file/stream has been downloaded.
+ *
+ * This amount of prebuffering can be displayed on the range's trough
+ * and is themeable separately from the trough. To enable fill level
+ * display, use gtk_range_set_show_fill_level(). The range defaults
+ * to not showing the fill level.
+ *
+ * Additionally, it's possible to restrict the range's slider position
+ * to values which are smaller than the fill level. This is controller
+ * by gtk_range_set_restrict_to_fill_level() and is by default
+ * enabled.
+ *
+ * Since: 2.12
+ **/
+void
+gtk_range_set_fill_level (GtkRange *range,
+                          gdouble   fill_level)
+{
+  g_return_if_fail (GTK_IS_RANGE (range));
+
+  if (fill_level != range->layout->fill_level)
+    {
+      range->layout->fill_level = fill_level;
+      g_object_notify (G_OBJECT (range), "fill-level");
+
+      if (range->layout->show_fill_level)
+        gtk_widget_queue_draw (GTK_WIDGET (range));
+
+      if (range->layout->restrict_to_fill_level)
+        gtk_range_set_value (range, gtk_range_get_value (range));
+    }
+}
+
+/**
+ * gtk_range_get_fill_level:
+ * @range : A #GtkRange
+ *
+ * Gets the current position of the fill level indicator.
+ *
+ * Return value: The current fill level
+ *
+ * Since: 2.12
+ **/
+gdouble
+gtk_range_get_fill_level (GtkRange *range)
+{
+  g_return_val_if_fail (GTK_IS_RANGE (range), 0.0);
+
+  return range->layout->fill_level;
+}
+
 static gboolean
 should_invert (GtkRange *range)
 {  
@@ -880,16 +1198,6 @@ should_invert (GtkRange *range)
     return range->inverted;
 }
 
-static void
-gtk_range_finalize (GObject *object)
-{
-  GtkRange *range = GTK_RANGE (object);
-
-  g_free (range->layout);
-
-  (* G_OBJECT_CLASS (parent_class)->finalize) (object);
-}
-
 static void
 gtk_range_destroy (GtkObject *object)
 {
@@ -897,7 +1205,11 @@ gtk_range_destroy (GtkObject *object)
 
   gtk_range_remove_step_timer (range);
   gtk_range_remove_update_timer (range);
-  
+
+  if (range->layout->repaint_id)
+    g_source_remove (range->layout->repaint_id);
+  range->layout->repaint_id = 0;
+
   if (range->adjustment)
     {
       g_signal_handlers_disconnect_by_func (range->adjustment,
@@ -910,7 +1222,16 @@ gtk_range_destroy (GtkObject *object)
       range->adjustment = NULL;
     }
 
-  (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+  if (range->layout->n_marks)
+    {
+      g_free (range->layout->marks);
+      range->layout->marks = NULL;
+      g_free (range->layout->mark_pos);
+      range->layout->mark_pos = NULL;
+      range->layout->n_marks = 0;
+    }
+
+  GTK_OBJECT_CLASS (gtk_range_parent_class)->destroy (object);
 }
 
 static void
@@ -918,19 +1239,22 @@ gtk_range_size_request (GtkWidget      *widget,
                         GtkRequisition *requisition)
 {
   GtkRange *range;
-  gint slider_width, stepper_size, trough_border, stepper_spacing;
+  gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
   GdkRectangle range_rect;
   GtkBorder border;
   
   range = GTK_RANGE (widget);
   
   gtk_range_get_props (range,
-                       &slider_width, &stepper_size, &trough_border, &stepper_spacing,
-                      NULL, NULL);
+                       &slider_width, &stepper_size,
+                       &focus_width, &trough_border,
+                       &stepper_spacing, NULL,
+                       NULL, NULL);
 
   gtk_range_calc_request (range, 
-                          slider_width, stepper_size, trough_border, stepper_spacing,
-                          &range_rect, &border, NULL, NULL);
+                          slider_width, stepper_size,
+                          focus_width, trough_border, stepper_spacing,
+                          &range_rect, &border, NULL, NULL, NULL, NULL);
 
   requisition->width = range_rect.width + border.left + border.right;
   requisition->height = range_rect.height + border.top + border.bottom;
@@ -946,6 +1270,8 @@ gtk_range_size_allocate (GtkWidget     *widget,
 
   widget->allocation = *allocation;
   
+  range->layout->recalc_marks = TRUE;
+
   range->need_recalc = TRUE;
   gtk_range_calc_layout (range, range->adjustment->value);
 
@@ -1007,9 +1333,8 @@ gtk_range_unrealize (GtkWidget *widget)
   gdk_window_set_user_data (range->event_window, NULL);
   gdk_window_destroy (range->event_window);
   range->event_window = NULL;
-  
-  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
-    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+
+  GTK_WIDGET_CLASS (gtk_range_parent_class)->unrealize (widget);
 }
 
 static void
@@ -1019,7 +1344,7 @@ gtk_range_map (GtkWidget *widget)
   
   gdk_window_show (range->event_window);
 
-  GTK_WIDGET_CLASS (parent_class)->map (widget);
+  GTK_WIDGET_CLASS (gtk_range_parent_class)->map (widget);
 }
 
 static void
@@ -1031,7 +1356,59 @@ gtk_range_unmap (GtkWidget *widget)
 
   gdk_window_hide (range->event_window);
 
-  GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+  GTK_WIDGET_CLASS (gtk_range_parent_class)->unmap (widget);
+}
+
+static const gchar *
+gtk_range_get_slider_detail (GtkRange *range)
+{
+  const gchar *slider_detail;
+
+  if (range->layout->slider_detail_quark)
+    return g_quark_to_string (range->layout->slider_detail_quark);
+
+  slider_detail = GTK_RANGE_GET_CLASS (range)->slider_detail;
+
+  if (slider_detail && slider_detail[0] == 'X')
+    {
+      gchar *detail = g_strdup (slider_detail);
+
+      detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
+
+      range->layout->slider_detail_quark = g_quark_from_string (detail);
+
+      g_free (detail);
+
+      return g_quark_to_string (range->layout->slider_detail_quark);
+    }
+
+  return slider_detail;
+}
+
+static const gchar *
+gtk_range_get_stepper_detail (GtkRange *range)
+{
+  const gchar *stepper_detail;
+
+  if (range->layout->stepper_detail_quark)
+    return g_quark_to_string (range->layout->stepper_detail_quark);
+
+  stepper_detail = GTK_RANGE_GET_CLASS (range)->stepper_detail;
+
+  if (stepper_detail && stepper_detail[0] == 'X')
+    {
+      gchar *detail = g_strdup (stepper_detail);
+
+      detail[0] = range->orientation == GTK_ORIENTATION_HORIZONTAL ? 'h' : 'v';
+
+      range->layout->stepper_detail_quark = g_quark_from_string (detail);
+
+      g_free (detail);
+
+      return g_quark_to_string (range->layout->stepper_detail_quark);
+    }
+
+  return stepper_detail;
 }
 
 static void
@@ -1046,6 +1423,7 @@ draw_stepper (GtkRange     *range,
   GtkShadowType shadow_type;
   GdkRectangle intersection;
   GtkWidget *widget = GTK_WIDGET (range);
+  gfloat arrow_scaling;
 
   gint arrow_x;
   gint arrow_y;
@@ -1066,40 +1444,11 @@ draw_stepper (GtkRange     *range,
       (range->inverted  && (arrow_type == GTK_ARROW_UP ||
                             arrow_type == GTK_ARROW_LEFT)))
     {
-      switch (range->layout->upper_sensitivity)
-        {
-        case GTK_SENSITIVITY_AUTO:
-          arrow_sensitive =
-            (range->adjustment->value <
-             (range->adjustment->upper - range->adjustment->page_size));
-          break;
-
-        case GTK_SENSITIVITY_ON:
-          arrow_sensitive = TRUE;
-          break;
-
-        case GTK_SENSITIVITY_OFF:
-          arrow_sensitive = FALSE;
-          break;
-        }
+      arrow_sensitive = range->layout->upper_sensitive;
     }
   else
     {
-      switch (range->layout->lower_sensitivity)
-        {
-        case GTK_SENSITIVITY_AUTO:
-          arrow_sensitive =
-            (range->adjustment->value > range->adjustment->lower);
-          break;
-
-        case GTK_SENSITIVITY_ON:
-          arrow_sensitive = TRUE;
-          break;
-
-        case GTK_SENSITIVITY_OFF:
-          arrow_sensitive = FALSE;
-          break;
-        }
+      arrow_sensitive = range->layout->lower_sensitive;
     }
 
   if (!GTK_WIDGET_IS_SENSITIVE (range) || !arrow_sensitive)
@@ -1120,23 +1469,26 @@ draw_stepper (GtkRange     *range,
                 widget->window,
                 state_type, shadow_type,
                 &intersection, widget,
-                GTK_RANGE_GET_CLASS (range)->stepper_detail,
+                gtk_range_get_stepper_detail (range),
                 widget->allocation.x + rect->x,
                 widget->allocation.y + rect->y,
                 rect->width,
                 rect->height);
 
-  arrow_width = rect->width / 2;
-  arrow_height = rect->height / 2;
+  gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
+
+  arrow_width = rect->width * arrow_scaling;
+  arrow_height = rect->height * arrow_scaling;
   arrow_x = widget->allocation.x + rect->x + (rect->width - arrow_width) / 2;
   arrow_y = widget->allocation.y + rect->y + (rect->height - arrow_height) / 2;
-  
+
   if (clicked && arrow_sensitive)
     {
       gint arrow_displacement_x;
       gint arrow_displacement_y;
 
-      gtk_range_get_props (GTK_RANGE (widget), NULL, NULL, NULL, NULL,
+      gtk_range_get_props (GTK_RANGE (widget),
+                           NULL, NULL, NULL, NULL, NULL, NULL,
                           &arrow_displacement_x, &arrow_displacement_y);
       
       arrow_x += arrow_displacement_x;
@@ -1147,17 +1499,17 @@ draw_stepper (GtkRange     *range,
                    widget->window,
                    state_type, shadow_type, 
                    &intersection, widget,
-                   GTK_RANGE_GET_CLASS (range)->stepper_detail,
+                   gtk_range_get_stepper_detail (range),
                    arrow_type,
                    TRUE,
                   arrow_x, arrow_y, arrow_width, arrow_height);
 }
 
-static gint
+static gboolean
 gtk_range_expose (GtkWidget      *widget,
                  GdkEventExpose *event)
 {
-  GtkRange *range;
+  GtkRange *range = GTK_RANGE (widget);
   gboolean sensitive;
   GtkStateType state;
   GtkShadowType shadow_type;
@@ -1166,26 +1518,26 @@ gtk_range_expose (GtkWidget      *widget,
   gint focus_line_width = 0;
   gint focus_padding = 0;
   gboolean touchscreen;
-  gboolean activate_slider;
 
   g_object_get (gtk_widget_get_settings (widget),
                 "gtk-touchscreen-mode", &touchscreen,
                 NULL);
+  if (GTK_WIDGET_CAN_FOCUS (range))
+    gtk_widget_style_get (GTK_WIDGET (range),
+                          "focus-line-width", &focus_line_width,
+                          "focus-padding", &focus_padding,
+                          NULL);
 
-  range = GTK_RANGE (widget);
+  /* we're now exposing, so there's no need to force early repaints */
+  if (range->layout->repaint_id)
+    g_source_remove (range->layout->repaint_id);
+  range->layout->repaint_id = 0;
 
-  if (GTK_WIDGET_CAN_FOCUS (range))
-    {
-      gtk_widget_style_get (GTK_WIDGET (range),
-                           "focus-line-width", &focus_line_width,
-                           "focus-padding", &focus_padding,
-                           NULL);
-    }
-  
   expose_area = event->area;
   expose_area.x -= widget->allocation.x;
   expose_area.y -= widget->allocation.y;
   
+  gtk_range_calc_marks (range);
   gtk_range_calc_layout (range, range->adjustment->value);
 
   sensitive = GTK_WIDGET_IS_SENSITIVE (widget);
@@ -1200,20 +1552,181 @@ gtk_range_expose (GtkWidget      *widget,
   if (gdk_rectangle_intersect (&expose_area, &range->range_rect,
                                &area))
     {
+      gint     x      = (widget->allocation.x + range->range_rect.x +
+                         focus_line_width + focus_padding);
+      gint     y      = (widget->allocation.y + range->range_rect.y +
+                         focus_line_width + focus_padding);
+      gint     width  = (range->range_rect.width -
+                         2 * (focus_line_width + focus_padding));
+      gint     height = (range->range_rect.height -
+                         2 * (focus_line_width + focus_padding));
+      gboolean trough_side_details;
+      gboolean trough_under_steppers;
+      gint     stepper_size;
+      gint     stepper_spacing;
+
       area.x += widget->allocation.x;
       area.y += widget->allocation.y;
-      
-      gtk_paint_box (widget->style,
-                     widget->window,
-                     sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
-                     GTK_SHADOW_IN,
-                     &area, GTK_WIDGET(range), "trough",
-                     widget->allocation.x + range->range_rect.x + focus_line_width + focus_padding,
-                     widget->allocation.y + range->range_rect.y + focus_line_width + focus_padding,
-                     range->range_rect.width - 2 * (focus_line_width + focus_padding),
-                     range->range_rect.height - 2 * (focus_line_width + focus_padding));
-      
-                 
+
+      gtk_widget_style_get (GTK_WIDGET (range),
+                            "trough-side-details",   &trough_side_details,
+                            "trough-under-steppers", &trough_under_steppers,
+                            "stepper-size",          &stepper_size,
+                            "stepper-spacing",       &stepper_spacing,
+                            NULL);
+
+      if (stepper_spacing > 0)
+        trough_under_steppers = FALSE;
+
+      if (! trough_under_steppers)
+        {
+          gint offset  = 0;
+          gint shorter = 0;
+
+          if (range->has_stepper_a)
+            offset += stepper_size;
+
+          if (range->has_stepper_b)
+            offset += stepper_size;
+
+          shorter += offset;
+
+          if (range->has_stepper_c)
+            shorter += stepper_size;
+
+          if (range->has_stepper_d)
+            shorter += stepper_size;
+
+          if (range->has_stepper_a || range->has_stepper_b)
+            {
+              offset  += stepper_spacing;
+              shorter += stepper_spacing;
+            }
+
+          if (range->has_stepper_c || range->has_stepper_d)
+            {
+              shorter += stepper_spacing;
+            }
+
+          if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+            {
+              x     += offset;
+              width -= shorter;
+            }
+          else
+            {
+              y      += offset;
+              height -= shorter;
+            }
+       }
+
+      if (! trough_side_details)
+        {
+          gtk_paint_box (widget->style,
+                         widget->window,
+                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+                         GTK_SHADOW_IN,
+                         &area, GTK_WIDGET(range), "trough",
+                         x, y,
+                         width, height);
+        }
+      else
+        {
+         gint trough_change_pos_x = width;
+         gint trough_change_pos_y = height;
+
+         if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+           trough_change_pos_x = (range->layout->slider.x +
+                                   range->layout->slider.width / 2 -
+                                   (x - widget->allocation.x));
+         else
+           trough_change_pos_y = (range->layout->slider.y +
+                                   range->layout->slider.height / 2 -
+                                   (y - widget->allocation.y));
+
+          gtk_paint_box (widget->style,
+                         widget->window,
+                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+                         GTK_SHADOW_IN,
+                         &area, GTK_WIDGET (range),
+                         should_invert (range) ? "trough-upper" : "trough-lower",
+                         x, y,
+                         trough_change_pos_x, trough_change_pos_y);
+
+         if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+           trough_change_pos_y = 0;
+         else
+           trough_change_pos_x = 0;
+
+          gtk_paint_box (widget->style,
+                         widget->window,
+                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+                         GTK_SHADOW_IN,
+                         &area, GTK_WIDGET (range),
+                         should_invert (range) ? "trough-lower" : "trough-upper",
+                         x + trough_change_pos_x, y + trough_change_pos_y,
+                         width - trough_change_pos_x,
+                         height - trough_change_pos_y);
+        }
+
+      if (range->layout->show_fill_level &&
+          range->adjustment->upper - range->adjustment->page_size -
+          range->adjustment->lower != 0)
+       {
+          gdouble  fill_level  = range->layout->fill_level;
+         gint     fill_x      = x;
+         gint     fill_y      = y;
+         gint     fill_width  = width;
+         gint     fill_height = height;
+         gchar   *fill_detail;
+
+          fill_level = CLAMP (fill_level, range->adjustment->lower,
+                              range->adjustment->upper -
+                              range->adjustment->page_size);
+
+         if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+           {
+             fill_x     = widget->allocation.x + range->layout->trough.x;
+             fill_width = (range->layout->slider.width +
+                            (fill_level - range->adjustment->lower) /
+                            (range->adjustment->upper -
+                             range->adjustment->lower -
+                             range->adjustment->page_size) *
+                            (range->layout->trough.width -
+                             range->layout->slider.width));
+
+              if (should_invert (range))
+                fill_x += range->layout->trough.width - fill_width;
+           }
+         else
+           {
+             fill_y      = widget->allocation.y + range->layout->trough.y;
+             fill_height = (range->layout->slider.height +
+                             (fill_level - range->adjustment->lower) /
+                             (range->adjustment->upper -
+                              range->adjustment->lower -
+                              range->adjustment->page_size) *
+                             (range->layout->trough.height -
+                              range->layout->slider.height));
+
+              if (should_invert (range))
+                fill_y += range->layout->trough.height - fill_height;
+           }
+
+         if (fill_level < range->adjustment->upper - range->adjustment->page_size)
+           fill_detail = "trough-fill-level-full";
+         else
+           fill_detail = "trough-fill-level";
+
+          gtk_paint_box (widget->style,
+                         widget->window,
+                         sensitive ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE,
+                         GTK_SHADOW_OUT,
+                         &area, GTK_WIDGET (range), fill_detail,
+                         fill_x, fill_y,
+                         fill_width, fill_height);
+       }
+
       if (sensitive &&
           GTK_WIDGET_HAS_FOCUS (range))
         gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
@@ -1235,8 +1748,10 @@ gtk_range_expose (GtkWidget      *widget,
 
   if (range->layout->grab_location == MOUSE_SLIDER)
     {
-      gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL);
-      
+      gboolean activate_slider;
+
+      gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
+
       if (activate_slider)
         {
           state = GTK_STATE_ACTIVE;
@@ -1257,7 +1772,7 @@ gtk_range_expose (GtkWidget      *widget,
                         shadow_type,
                         &area,
                         widget,
-                        GTK_RANGE_GET_CLASS (range)->slider_detail,
+                        gtk_range_get_slider_detail (range),
                         widget->allocation.x + range->layout->slider.x,
                         widget->allocation.y + range->layout->slider.y,
                         range->layout->slider.width,
@@ -1315,12 +1830,16 @@ range_grab_add (GtkRange      *range,
 static void
 range_grab_remove (GtkRange *range)
 {
+  MouseLocation location;
+
   gtk_grab_remove (GTK_WIDGET (range));
-  
+  location = range->layout->grab_location; 
   range->layout->grab_location = MOUSE_OUTSIDE;
   range->layout->grab_button = 0;
 
-  if (gtk_range_update_mouse_location (range))
+  if (gtk_range_update_mouse_location (range) ||
+      location != MOUSE_OUTSIDE)
     gtk_widget_queue_draw (GTK_WIDGET (range));
 }
 
@@ -1391,25 +1910,46 @@ coord_to_value (GtkRange *range,
 {
   gdouble frac;
   gdouble value;
-  
+  gint    trough_length;
+  gint    trough_start;
+  gint    slider_length;
+  gint    trough_border;
+  gint    trough_under_steppers;
+
   if (range->orientation == GTK_ORIENTATION_VERTICAL)
-    if (range->layout->trough.height == range->layout->slider.height)
-      frac = 1.0;
-    else 
-      frac = ((coord - range->layout->trough.y) /
-             (gdouble) (range->layout->trough.height - range->layout->slider.height));
+    {
+      trough_length = range->layout->trough.height;
+      trough_start  = range->layout->trough.y;
+      slider_length = range->layout->slider.height;
+    }
+  else
+    {
+      trough_length = range->layout->trough.width;
+      trough_start  = range->layout->trough.x;
+      slider_length = range->layout->slider.width;
+    }
+
+  gtk_range_get_props (range, NULL, NULL, NULL, &trough_border, NULL,
+                       &trough_under_steppers, NULL, NULL);
+
+  if (! trough_under_steppers)
+    {
+      trough_start += trough_border;
+      trough_length -= 2 * trough_border;
+    }
+
+  if (trough_length == slider_length)
+    frac = 1.0;
   else
-    if (range->layout->trough.width == range->layout->slider.width)
-      frac = 1.0;
-    else
-      frac = ((coord - range->layout->trough.x) /
-             (gdouble) (range->layout->trough.width - range->layout->slider.width));
+    frac = (MAX (0, coord - trough_start) /
+            (gdouble) (trough_length - slider_length));
 
   if (should_invert (range))
     frac = 1.0 - frac;
-  
-  value = range->adjustment->lower +
-    frac * (range->adjustment->upper - range->adjustment->lower - range->adjustment->page_size);
+
+  value = range->adjustment->lower + frac * (range->adjustment->upper -
+                                             range->adjustment->lower -
+                                             range->adjustment->page_size);
 
   return value;
 }
@@ -1418,20 +1958,21 @@ static gboolean
 gtk_range_key_press (GtkWidget   *widget,
                     GdkEventKey *event)
 {
-  GtkRange *range = (GtkRange *)widget;
+  GtkRange *range = GTK_RANGE (widget);
 
-  if (event->keyval == GDK_Escape)
+  if (event->keyval == GDK_Escape &&
+      range->layout->grab_location != MOUSE_OUTSIDE)
     {
       stop_scrolling (range);
 
-      update_slider_position (range, 
+      update_slider_position (range,
                              range->slide_initial_coordinate,
                              range->slide_initial_coordinate);
 
       return TRUE;
     }
 
-  return FALSE;
+  return GTK_WIDGET_CLASS (gtk_range_parent_class)->key_press_event (widget, event);
 }
 
 static gint
@@ -1550,13 +2091,15 @@ gtk_range_button_press (GtkWidget      *widget,
         }
 
       range_grab_add (range, MOUSE_SLIDER, event->button);
-      
-      gtk_widget_style_get (widget, "activate_slider", &activate_slider, NULL);
-      
-      /* force a redraw, if the active slider is drawn differently to the prelight one */
+
+      gtk_widget_style_get (widget, "activate-slider", &activate_slider, NULL);
+
+      /* force a redraw, if the active slider is drawn differently to the
+       * prelight one
+       */
       if (activate_slider)
         gtk_widget_queue_draw (widget);
-      
+
       if (need_value_update)
         update_slider_position (range, event->x, event->y);
 
@@ -1576,6 +2119,10 @@ update_slider_position (GtkRange *range,
   gint c;
   gdouble new_value;
   gboolean handled;
+  gdouble next_value;
+  gdouble mark_value;
+  gdouble mark_delta;
+  gint i;
 
   if (range->orientation == GTK_ORIENTATION_VERTICAL)
     delta = mouse_y - range->slide_initial_coordinate;
@@ -1585,7 +2132,23 @@ update_slider_position (GtkRange *range,
   c = range->slide_initial_slider_position + delta;
 
   new_value = coord_to_value (range, c);
-  
+  next_value = coord_to_value (range, c + 1);
+  mark_delta = fabs (next_value - new_value); 
+
+  for (i = 0; i < range->layout->n_marks; i++)
+    {
+      mark_value = range->layout->marks[i];
+
+      if (fabs (range->adjustment->value - mark_value) < 3 * mark_delta)
+        {
+          if (fabs (new_value - mark_value) < (range->slider_end - range->slider_start) * 0.5 * mark_delta)
+            {
+              new_value = mark_value;
+              break;
+            }
+        }
+    }  
+
   g_signal_emit (range, signals[CHANGE_VALUE], 0, GTK_SCROLL_JUMP, new_value,
                  &handled);
 }
@@ -1597,11 +2160,6 @@ stop_scrolling (GtkRange *range)
   gtk_range_remove_step_timer (range);
   /* Flush any pending discontinuous/delayed updates */
   gtk_range_update_value (range);
-  
-  /* Just be lazy about this, if we scrolled it will all redraw anyway,
-   * so no point optimizing the button deactivate case
-   */
-  gtk_widget_queue_draw (GTK_WIDGET (range));
 }
 
 static gboolean
@@ -1688,7 +2246,7 @@ _gtk_range_get_wheel_delta (GtkRange           *range,
   return delta;
 }
       
-static gint
+static gboolean
 gtk_range_scroll_event (GtkWidget      *widget,
                        GdkEventScroll *event)
 {
@@ -1717,7 +2275,7 @@ gtk_range_scroll_event (GtkWidget      *widget,
   return TRUE;
 }
 
-static gint
+static gboolean
 gtk_range_motion_notify (GtkWidget      *widget,
                         GdkEventMotion *event)
 {
@@ -1741,7 +2299,7 @@ gtk_range_motion_notify (GtkWidget      *widget,
   return range->layout->mouse_location != MOUSE_OUTSIDE;
 }
 
-static gint
+static gboolean
 gtk_range_enter_notify (GtkWidget        *widget,
                        GdkEventCrossing *event)
 {
@@ -1756,7 +2314,7 @@ gtk_range_enter_notify (GtkWidget        *widget,
   return TRUE;
 }
 
-static gint
+static gboolean
 gtk_range_leave_notify (GtkWidget        *widget,
                        GdkEventCrossing *event)
 {
@@ -1806,6 +2364,9 @@ layout_changed (GtkRangeLayout *layout1,
   check_rectangle (layout1->stepper_b, layout2->stepper_b);
   check_rectangle (layout1->stepper_c, layout2->stepper_c);
 
+  if (layout1->upper_sensitive != layout2->upper_sensitive) return TRUE;
+  if (layout1->lower_sensitive != layout2->lower_sensitive) return TRUE;
+
   return FALSE;
 }
 
@@ -1817,6 +2378,7 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment,
   /* create a copy of the layout */
   GtkRangeLayout layout = *range->layout;
 
+  range->layout->recalc_marks = TRUE;
   range->need_recalc = TRUE;
   gtk_range_calc_layout (range, range->adjustment->value);
   
@@ -1833,6 +2395,16 @@ gtk_range_adjustment_changed (GtkAdjustment *adjustment,
    */
 }
 
+static gboolean
+force_repaint (gpointer data)
+{
+  GtkRange *range = GTK_RANGE (data);
+  range->layout->repaint_id = 0;
+  if (GTK_WIDGET_DRAWABLE (range))
+    gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
+  return FALSE;
+}
+
 static void
 gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
                                    gpointer       data)
@@ -1848,10 +2420,9 @@ gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
   if (layout_changed (range->layout, &layout))
     {
       gtk_widget_queue_draw (GTK_WIDGET (range));
-      
-      /* This is so we don't lag the widget being scrolled. */
-      if (GTK_WIDGET_REALIZED (range))
-        gdk_window_process_updates (GTK_WIDGET (range)->window, FALSE);
+      /* setup a timer to ensure the range isn't lagging too much behind the scroll position */
+      if (!range->layout->repaint_id)
+        range->layout->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
     }
   
   /* Note that we don't round off to range->round_digits here.
@@ -1873,7 +2444,27 @@ gtk_range_style_set (GtkWidget *widget,
 
   range->need_recalc = TRUE;
 
-  (* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
+  GTK_WIDGET_CLASS (gtk_range_parent_class)->style_set (widget, previous_style);
+}
+
+static void
+apply_marks (GtkRange *range, 
+             gdouble   oldval,
+             gdouble  *newval)
+{
+  gint i;
+  gdouble mark;
+
+  for (i = 0; i < range->layout->n_marks; i++)
+    {
+      mark = range->layout->marks[i];
+      if ((oldval < mark && mark < *newval) ||
+          (oldval > mark && mark > *newval))
+        {
+          *newval = mark;
+          return;
+        }
+    }
 }
 
 static void
@@ -1883,6 +2474,7 @@ step_back (GtkRange *range)
   gboolean handled;
   
   newval = range->adjustment->value - range->adjustment->step_increment;
+  apply_marks (range, range->adjustment->value, &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_STEP_BACKWARD, newval, &handled);
 }
@@ -1894,6 +2486,7 @@ step_forward (GtkRange *range)
   gboolean handled;
 
   newval = range->adjustment->value + range->adjustment->step_increment;
+  apply_marks (range, range->adjustment->value, &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_STEP_FORWARD, newval, &handled);
 }
@@ -1906,6 +2499,7 @@ page_back (GtkRange *range)
   gboolean handled;
 
   newval = range->adjustment->value - range->adjustment->page_increment;
+  apply_marks (range, range->adjustment->value, &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_PAGE_BACKWARD, newval, &handled);
 }
@@ -1917,6 +2511,7 @@ page_forward (GtkRange *range)
   gboolean handled;
 
   newval = range->adjustment->value + range->adjustment->page_increment;
+  apply_marks (range, range->adjustment->value, &newval);
   g_signal_emit (range, signals[CHANGE_VALUE], 0,
                  GTK_SCROLL_PAGE_FORWARD, newval, &handled);
 }
@@ -1941,10 +2536,12 @@ scroll_end (GtkRange *range)
                  &handled);
 }
 
-static void
+static gboolean
 gtk_range_scroll (GtkRange     *range,
                   GtkScrollType scroll)
 {
+  gdouble old_value = range->adjustment->value;
+
   switch (scroll)
     {
     case GTK_SCROLL_STEP_LEFT:
@@ -2034,13 +2631,52 @@ gtk_range_scroll (GtkRange     *range,
     case GTK_SCROLL_NONE:
       break;
     }
+
+  return range->adjustment->value != old_value;
 }
 
 static void
 gtk_range_move_slider (GtkRange     *range,
                        GtkScrollType scroll)
 {
-  gtk_range_scroll (range, scroll);
+  gboolean cursor_only;
+
+  g_object_get (gtk_widget_get_settings (GTK_WIDGET (range)),
+                "gtk-keynav-cursor-only", &cursor_only,
+                NULL);
+
+  if (cursor_only)
+    {
+      GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (range));
+
+      if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+        {
+          if (scroll == GTK_SCROLL_STEP_UP ||
+              scroll == GTK_SCROLL_STEP_DOWN)
+            {
+              if (toplevel)
+                gtk_widget_child_focus (toplevel,
+                                        scroll == GTK_SCROLL_STEP_UP ?
+                                        GTK_DIR_UP : GTK_DIR_DOWN);
+              return;
+            }
+        }
+      else
+        {
+          if (scroll == GTK_SCROLL_STEP_LEFT ||
+              scroll == GTK_SCROLL_STEP_RIGHT)
+            {
+              if (toplevel)
+                gtk_widget_child_focus (toplevel,
+                                        scroll == GTK_SCROLL_STEP_LEFT ?
+                                        GTK_DIR_LEFT : GTK_DIR_RIGHT);
+              return;
+            }
+        }
+    }
+
+  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
@@ -2054,13 +2690,16 @@ static void
 gtk_range_get_props (GtkRange  *range,
                      gint      *slider_width,
                      gint      *stepper_size,
+                     gint      *focus_width,
                      gint      *trough_border,
                      gint      *stepper_spacing,
+                     gboolean  *trough_under_steppers,
                     gint      *arrow_displacement_x,
                     gint      *arrow_displacement_y)
 {
   GtkWidget *widget =  GTK_WIDGET (range);
-  gint tmp_slider_width, tmp_stepper_size, tmp_trough_border, tmp_stepper_spacing;
+  gint tmp_slider_width, tmp_stepper_size, tmp_focus_width, tmp_trough_border;
+  gint tmp_stepper_spacing, tmp_trough_under_steppers;
   gint tmp_arrow_displacement_x, tmp_arrow_displacement_y;
   
   gtk_widget_style_get (widget,
@@ -2068,10 +2707,14 @@ gtk_range_get_props (GtkRange  *range,
                         "trough-border", &tmp_trough_border,
                         "stepper-size", &tmp_stepper_size,
                         "stepper-spacing", &tmp_stepper_spacing,
+                        "trough-under-steppers", &tmp_trough_under_steppers,
                        "arrow-displacement-x", &tmp_arrow_displacement_x,
                        "arrow-displacement-y", &tmp_arrow_displacement_y,
                         NULL);
-  
+
+  if (tmp_stepper_spacing > 0)
+    tmp_trough_under_steppers = FALSE;
+
   if (GTK_WIDGET_CAN_FOCUS (range))
     {
       gint focus_line_width;
@@ -2081,13 +2724,20 @@ gtk_range_get_props (GtkRange  *range,
                            "focus-line-width", &focus_line_width,
                            "focus-padding", &focus_padding,
                            NULL);
-      
-      tmp_trough_border += focus_line_width + focus_padding;
+
+      tmp_focus_width = focus_line_width + focus_padding;
+    }
+  else
+    {
+      tmp_focus_width = 0;
     }
   
   if (slider_width)
     *slider_width = tmp_slider_width;
 
+  if (focus_width)
+    *focus_width = tmp_focus_width;
+
   if (trough_border)
     *trough_border = tmp_trough_border;
 
@@ -2097,6 +2747,9 @@ gtk_range_get_props (GtkRange  *range,
   if (stepper_spacing)
     *stepper_spacing = tmp_stepper_spacing;
 
+  if (trough_under_steppers)
+    *trough_under_steppers = tmp_trough_under_steppers;
+
   if (arrow_displacement_x)
     *arrow_displacement_x = tmp_arrow_displacement_x;
 
@@ -2246,15 +2899,20 @@ static void
 gtk_range_calc_request (GtkRange      *range,
                         gint           slider_width,
                         gint           stepper_size,
+                        gint           focus_width,
                         gint           trough_border,
                         gint           stepper_spacing,
                         GdkRectangle  *range_rect,
                         GtkBorder     *border,
                         gint          *n_steppers_p,
+                        gboolean      *has_steppers_ab,
+                        gboolean      *has_steppers_cd,
                         gint          *slider_length_p)
 {
   gint slider_length;
   gint n_steppers;
+  gint n_steppers_ab;
+  gint n_steppers_cd;
 
   border->left = 0;
   border->right = 0;
@@ -2262,17 +2920,21 @@ gtk_range_calc_request (GtkRange      *range,
   border->bottom = 0;
 
   if (GTK_RANGE_GET_CLASS (range)->get_range_border)
-    (* GTK_RANGE_GET_CLASS (range)->get_range_border) (range, border);
-  
-  n_steppers = 0;
+    GTK_RANGE_GET_CLASS (range)->get_range_border (range, border);
+
+  n_steppers_ab = 0;
+  n_steppers_cd = 0;
+
   if (range->has_stepper_a)
-    n_steppers += 1;
+    n_steppers_ab += 1;
   if (range->has_stepper_b)
-    n_steppers += 1;
+    n_steppers_ab += 1;
   if (range->has_stepper_c)
-    n_steppers += 1;
+    n_steppers_cd += 1;
   if (range->has_stepper_d)
-    n_steppers += 1;
+    n_steppers_cd += 1;
+
+  n_steppers = n_steppers_ab + n_steppers_cd;
 
   slider_length = range->min_slider_size;
 
@@ -2284,18 +2946,36 @@ gtk_range_calc_request (GtkRange      *range,
    */
   if (range->orientation == GTK_ORIENTATION_VERTICAL)
     {
-      range_rect->width = trough_border * 2 + slider_width;
-      range_rect->height = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length;
+      range_rect->width = (focus_width + trough_border) * 2 + slider_width;
+      range_rect->height = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
+
+      if (n_steppers_ab > 0)
+        range_rect->height += stepper_spacing;
+
+      if (n_steppers_cd > 0)
+        range_rect->height += stepper_spacing;
     }
   else
     {
-      range_rect->width = stepper_size * n_steppers + stepper_spacing * 2 + trough_border * 2 + slider_length;
-      range_rect->height = trough_border * 2 + slider_width;
+      range_rect->width = stepper_size * n_steppers + (focus_width + trough_border) * 2 + slider_length;
+      range_rect->height = (focus_width + trough_border) * 2 + slider_width;
+
+      if (n_steppers_ab > 0)
+        range_rect->width += stepper_spacing;
+
+      if (n_steppers_cd > 0)
+        range_rect->width += stepper_spacing;
     }
 
   if (n_steppers_p)
     *n_steppers_p = n_steppers;
 
+  if (has_steppers_ab)
+    *has_steppers_ab = (n_steppers_ab > 0);
+
+  if (has_steppers_cd)
+    *has_steppers_cd = (n_steppers_cd > 0);
+
   if (slider_length_p)
     *slider_length_p = slider_length;
 }
@@ -2304,10 +2984,13 @@ static void
 gtk_range_calc_layout (GtkRange *range,
                       gdouble   adjustment_value)
 {
-  gint slider_width, stepper_size, trough_border, stepper_spacing;
+  gint slider_width, stepper_size, focus_width, trough_border, stepper_spacing;
   gint slider_length;
   GtkBorder border;
   gint n_steppers;
+  gboolean has_steppers_ab;
+  gboolean has_steppers_cd;
+  gboolean trough_under_steppers;
   GdkRectangle range_rect;
   GtkRangeLayout *layout;
   GtkWidget *widget;
@@ -2329,12 +3012,16 @@ gtk_range_calc_layout (GtkRange *range,
   layout = range->layout;
   
   gtk_range_get_props (range,
-                       &slider_width, &stepper_size, &trough_border, &stepper_spacing,
+                       &slider_width, &stepper_size,
+                       &focus_width, &trough_border,
+                       &stepper_spacing, &trough_under_steppers,
                       NULL, NULL);
 
   gtk_range_calc_request (range, 
-                          slider_width, stepper_size, trough_border, stepper_spacing,
-                          &range_rect, &border, &n_steppers, &slider_length);
+                          slider_width, stepper_size,
+                          focus_width, trough_border, stepper_spacing,
+                          &range_rect, &border, &n_steppers,
+                          &has_steppers_ab, &has_steppers_cd, &slider_length);
   
   /* We never expand to fill available space in the small dimension
    * (i.e. vertical scrollbars are always a fixed width)
@@ -2361,7 +3048,10 @@ gtk_range_calc_layout (GtkRange *range,
        * height, or if we don't have enough height, divided equally
        * among available space.
        */
-      stepper_width = range_rect.width - trough_border * 2;
+      stepper_width = range_rect.width - focus_width * 2;
+
+      if (trough_under_steppers)
+        stepper_width -= trough_border * 2;
 
       if (stepper_width < 1)
         stepper_width = range_rect.width; /* screw the trough border */
@@ -2373,8 +3063,8 @@ gtk_range_calc_layout (GtkRange *range,
 
       /* Stepper A */
       
-      layout->stepper_a.x = range_rect.x + trough_border;
-      layout->stepper_a.y = range_rect.y + trough_border;
+      layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
+      layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
 
       if (range->has_stepper_a)
         {
@@ -2417,7 +3107,7 @@ gtk_range_calc_layout (GtkRange *range,
         }
       
       layout->stepper_d.x = layout->stepper_a.x;
-      layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - trough_border;
+      layout->stepper_d.y = range_rect.y + range_rect.height - layout->stepper_d.height - focus_width - trough_border * trough_under_steppers;
 
       /* Stepper C */
 
@@ -2436,26 +3126,32 @@ gtk_range_calc_layout (GtkRange *range,
       layout->stepper_c.y = layout->stepper_d.y - layout->stepper_c.height;
 
       /* Now the trough is the remaining space between steppers B and C,
-       * if any
+       * if any, minus spacing
        */
       layout->trough.x = range_rect.x;
-      layout->trough.y = layout->stepper_b.y + layout->stepper_b.height;
+      layout->trough.y = layout->stepper_b.y + layout->stepper_b.height + stepper_spacing * has_steppers_ab;
       layout->trough.width = range_rect.width;
-      layout->trough.height = layout->stepper_c.y - (layout->stepper_b.y + layout->stepper_b.height);
-      
+      layout->trough.height = layout->stepper_c.y - layout->trough.y - stepper_spacing * has_steppers_cd;
+
       /* Slider fits into the trough, with stepper_spacing on either side,
        * and the size/position based on the adjustment or fixed, depending.
        */
-      layout->slider.x = layout->trough.x + trough_border;
-      layout->slider.width = layout->trough.width - trough_border * 2;
+      layout->slider.x = layout->trough.x + focus_width + trough_border;
+      layout->slider.width = layout->trough.width - (focus_width + trough_border) * 2;
 
       /* Compute slider position/length */
       {
         gint y, bottom, top, height;
         
-        top = layout->trough.y + stepper_spacing;
-        bottom = layout->trough.y + layout->trough.height - stepper_spacing;
-        
+        top = layout->trough.y;
+        bottom = layout->trough.y + layout->trough.height;
+
+        if (! trough_under_steppers)
+          {
+            top += trough_border;
+            bottom -= trough_border;
+          }
+
         /* slider height is the fraction (page_size /
          * total_adjustment_range) times the trough height in pixels
          */
@@ -2469,8 +3165,8 @@ gtk_range_calc_layout (GtkRange *range,
         if (height < range->min_slider_size ||
             range->slider_size_fixed)
           height = range->min_slider_size;
-        
-        height = MIN (height, (layout->trough.height - stepper_spacing * 2));
+
+        height = MIN (height, layout->trough.height);
         
         y = top;
         
@@ -2499,7 +3195,10 @@ gtk_range_calc_layout (GtkRange *range,
        * width, or if we don't have enough width, divided equally
        * among available space.
        */
-      stepper_height = range_rect.height - trough_border * 2;
+      stepper_height = range_rect.height + focus_width * 2;
+
+      if (trough_under_steppers)
+        stepper_height -= trough_border * 2;
 
       if (stepper_height < 1)
         stepper_height = range_rect.height; /* screw the trough border */
@@ -2511,8 +3210,8 @@ gtk_range_calc_layout (GtkRange *range,
 
       /* Stepper A */
       
-      layout->stepper_a.x = range_rect.x + trough_border;
-      layout->stepper_a.y = range_rect.y + trough_border;
+      layout->stepper_a.x = range_rect.x + focus_width + trough_border * trough_under_steppers;
+      layout->stepper_a.y = range_rect.y + focus_width + trough_border * trough_under_steppers;
 
       if (range->has_stepper_a)
         {
@@ -2554,7 +3253,7 @@ gtk_range_calc_layout (GtkRange *range,
           layout->stepper_d.height = 0;
         }
 
-      layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - trough_border;
+      layout->stepper_d.x = range_rect.x + range_rect.width - layout->stepper_d.width - focus_width - trough_border * trough_under_steppers;
       layout->stepper_d.y = layout->stepper_a.y;
 
 
@@ -2577,25 +3276,31 @@ gtk_range_calc_layout (GtkRange *range,
       /* Now the trough is the remaining space between steppers B and C,
        * if any
        */
-      layout->trough.x = layout->stepper_b.x + layout->stepper_b.width;
+      layout->trough.x = layout->stepper_b.x + layout->stepper_b.width + stepper_spacing * has_steppers_ab;
       layout->trough.y = range_rect.y;
 
-      layout->trough.width = layout->stepper_c.x - (layout->stepper_b.x + layout->stepper_b.width);
+      layout->trough.width = layout->stepper_c.x - layout->trough.x - stepper_spacing * has_steppers_cd;
       layout->trough.height = range_rect.height;
-      
+
       /* Slider fits into the trough, with stepper_spacing on either side,
        * and the size/position based on the adjustment or fixed, depending.
        */
-      layout->slider.y = layout->trough.y + trough_border;
-      layout->slider.height = layout->trough.height - trough_border * 2;
+      layout->slider.y = layout->trough.y + focus_width + trough_border;
+      layout->slider.height = layout->trough.height - (focus_width + trough_border) * 2;
 
       /* Compute slider position/length */
       {
         gint x, left, right, width;
         
-        left = layout->trough.x + stepper_spacing;
-        right = layout->trough.x + layout->trough.width - stepper_spacing;
-        
+        left = layout->trough.x;
+        right = layout->trough.x + layout->trough.width;
+
+        if (! trough_under_steppers)
+          {
+            left += trough_border;
+            right -= trough_border;
+          }
+
         /* slider width is the fraction (page_size /
          * total_adjustment_range) times the trough width in pixels
          */
@@ -2610,7 +3315,7 @@ gtk_range_calc_layout (GtkRange *range,
             range->slider_size_fixed)
           width = range->min_slider_size;
         
-        width = MIN (width, (layout->trough.width - stepper_spacing * 2));
+        width = MIN (width, layout->trough.width);
         
         x = left;
         
@@ -2633,6 +3338,39 @@ gtk_range_calc_layout (GtkRange *range,
     }
   
   gtk_range_update_mouse_location (range);
+
+  switch (range->layout->upper_sensitivity)
+    {
+    case GTK_SENSITIVITY_AUTO:
+      range->layout->upper_sensitive =
+        (range->adjustment->value <
+         (range->adjustment->upper - range->adjustment->page_size));
+      break;
+
+    case GTK_SENSITIVITY_ON:
+      range->layout->upper_sensitive = TRUE;
+      break;
+
+    case GTK_SENSITIVITY_OFF:
+      range->layout->upper_sensitive = FALSE;
+      break;
+    }
+
+  switch (range->layout->lower_sensitivity)
+    {
+    case GTK_SENSITIVITY_AUTO:
+      range->layout->lower_sensitive =
+        (range->adjustment->value > range->adjustment->lower);
+      break;
+
+    case GTK_SENSITIVITY_ON:
+      range->layout->lower_sensitive = TRUE;
+      break;
+
+    case GTK_SENSITIVITY_OFF:
+      range->layout->lower_sensitive = FALSE;
+      break;
+    }
 }
 
 static GdkRectangle*
@@ -2662,6 +3400,29 @@ get_area (GtkRange     *range,
   return NULL;
 }
 
+static void
+gtk_range_calc_marks (GtkRange *range)
+{
+  gint i;
+  
+  if (!range->layout->recalc_marks)
+    return;
+
+  range->layout->recalc_marks = FALSE;
+
+  for (i = 0; i < range->layout->n_marks; i++)
+    {
+      range->need_recalc = TRUE;
+      gtk_range_calc_layout (range, range->layout->marks[i]);
+      if (range->orientation == GTK_ORIENTATION_HORIZONTAL)
+        range->layout->mark_pos[i] = range->layout->slider.x + range->layout->slider.width / 2;
+      else
+        range->layout->mark_pos[i] = range->layout->slider.y + range->layout->slider.height / 2;
+    }
+
+  range->need_recalc = TRUE;
+}
+
 static gboolean
 gtk_range_real_change_value (GtkRange     *range,
                              GtkScrollType scroll,
@@ -2670,6 +3431,10 @@ gtk_range_real_change_value (GtkRange     *range,
   /* potentially adjust the bounds _before we clamp */
   g_signal_emit (range, signals[ADJUST_BOUNDS], 0, value);
 
+  if (range->layout->restrict_to_fill_level)
+    value = MIN (value, MAX (range->adjustment->lower,
+                             range->layout->fill_level));
+
   value = CLAMP (value, range->adjustment->lower,
                  (range->adjustment->upper - range->adjustment->page_size));
 
@@ -2738,10 +3503,8 @@ second_timeout (gpointer data)
 {
   GtkRange *range;
 
-  GDK_THREADS_ENTER ();
   range = GTK_RANGE (data);
   gtk_range_scroll (range, range->timer->step);
-  GDK_THREADS_LEAVE ();
   
   return TRUE;
 }
@@ -2753,16 +3516,13 @@ initial_timeout (gpointer data)
   GtkSettings *settings;
   guint        timeout;
 
-  GDK_THREADS_ENTER ();
   settings = gtk_widget_get_settings (GTK_WIDGET (data));
   g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
   range = GTK_RANGE (data);
-  range->timer->timeout_id = g_timeout_add (timeout * SCROLL_DELAY_FACTOR,
+  range->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
                                             second_timeout,
                                             range);
-  GDK_THREADS_LEAVE ();
-
   /* remove self */
   return FALSE;
 }
@@ -2782,7 +3542,7 @@ gtk_range_add_step_timer (GtkRange      *range,
 
   range->timer = g_new (GtkRangeStepTimer, 1);
 
-  range->timer->timeout_id = g_timeout_add (timeout,
+  range->timer->timeout_id = gdk_threads_add_timeout (timeout,
                                             initial_timeout,
                                             range);
   range->timer->step = step;
@@ -2809,11 +3569,9 @@ update_timeout (gpointer data)
 {
   GtkRange *range;
 
-  GDK_THREADS_ENTER ();
   range = GTK_RANGE (data);
   gtk_range_update_value (range);
   range->update_timeout_id = 0;
-  GDK_THREADS_LEAVE ();
 
   /* self-remove */
   return FALSE;
@@ -2824,7 +3582,7 @@ gtk_range_reset_update_timer (GtkRange *range)
 {
   gtk_range_remove_update_timer (range);
 
-  range->update_timeout_id = g_timeout_add (UPDATE_DELAY,
+  range->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
                                             update_timeout,
                                             range);
 }
@@ -2839,5 +3597,38 @@ gtk_range_remove_update_timer (GtkRange *range)
     }
 }
 
+void
+_gtk_range_set_stop_values (GtkRange *range,
+                            gdouble  *values,
+                            gint      n_values)
+{
+  gint i;
+
+  g_free (range->layout->marks);
+  range->layout->marks = g_new (gdouble, n_values);
+
+  g_free (range->layout->mark_pos);
+  range->layout->mark_pos = g_new (gint, n_values);
+
+  range->layout->n_marks = n_values;
+
+  for (i = 0; i < n_values; i++) 
+    range->layout->marks[i] = values[i];
+
+  range->layout->recalc_marks = TRUE;
+}
+
+gint
+_gtk_range_get_stop_positions (GtkRange  *range,
+                               gint     **values)
+{
+  gtk_range_calc_marks (range);
+
+  if (values)
+    *values = g_memdup (range->layout->mark_pos, range->layout->n_marks * sizeof (gint));
+
+  return range->layout->n_marks;
+}
+
 #define __GTK_RANGE_C__
 #include "gtkaliasdef.c"