]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkrange.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkrange.c
index 2e4331610d37668f14d2bc6b9f32139782ab3900..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 "gtkrange.h"
+
+#include "gtkadjustment.h"
+#include "gtkcolorscaleprivate.h"
+#include "gtkintl.h"
+#include "gtkmain.h"
 #include "gtkmarshalers.h"
 #include "gtkorientableprivate.h"
-#include "gtkrange.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"
 
 /**
@@ -115,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
    */
@@ -136,6 +135,12 @@ 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;
@@ -376,8 +381,6 @@ 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 #GtkRange::change-value signal is emitted when a scroll action is
    * performed on a range.  It allows an application to determine the
@@ -395,6 +398,9 @@ gtk_range_class_init (GtkRangeClass *class)
    * It is not possible to use delayed update policies in an overridden
    * #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
    */
   signals[CHANGE_VALUE] =
@@ -734,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;
@@ -1493,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,
@@ -1618,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);
@@ -1629,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)
         {
@@ -1723,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);
 
@@ -1733,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
@@ -1744,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;
 
@@ -1850,11 +1855,11 @@ 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);
   gfloat arrow_scaling;
@@ -1898,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)
@@ -1921,6 +1930,23 @@ draw_stepper (GtkRange     *range,
   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);
@@ -1978,22 +2004,27 @@ gtk_range_draw (GtkWidget *widget,
 {
   GtkRange *range = GTK_RANGE (widget);
   GtkRangePrivate *priv = range->priv;
-  gboolean sensitive;
-  GtkStateFlags state = 0;
+  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),
@@ -2001,15 +2032,10 @@ gtk_range_draw (GtkWidget *widget,
                           "focus-padding", &focus_padding,
                           NULL);
 
-  /* 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
@@ -2038,11 +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 (!trough_under_steppers)
         {
           gint offset  = 0;
@@ -2087,47 +2108,91 @@ gtk_range_draw (GtkWidget *widget,
 
       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->has_origin || !draw_slider)
+            {
+              gtk_render_background (context, cr,
+                                     x, y, width, height);
 
-          if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
-            trough_change_pos_y = 0;
+              gtk_render_frame (context, cr,
+                                x, y, width, height);
+            }
           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);
+            {
+              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);
@@ -2178,13 +2243,6 @@ gtk_range_draw (GtkWidget *widget,
                 fill_y += priv->trough.height - fill_height;
             }
 
-#if 0
-          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";
-#endif
-
           gtk_render_activity (context, cr,
                                fill_x, fill_y,
                                fill_width, fill_height);
@@ -2192,40 +2250,34 @@ gtk_range_draw (GtkWidget *widget,
           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);
@@ -2238,33 +2290,37 @@ 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;
 }
@@ -2280,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;
@@ -2305,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;
@@ -2317,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
@@ -2333,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;
         }
@@ -2350,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;
        }
@@ -2383,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;
@@ -2465,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);
@@ -2475,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;
@@ -2506,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;
@@ -2530,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;
           
@@ -2568,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)
         {
@@ -2600,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)
@@ -2614,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++)
     {
@@ -2632,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);
@@ -2659,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);
       
@@ -2710,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)
@@ -2753,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,
@@ -2886,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)
@@ -2916,11 +3031,6 @@ 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.
@@ -3489,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;
@@ -3511,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,
@@ -3779,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;
 
@@ -4043,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,