Index: app/display/gimpdisplayshell-scroll.c
===================================================================
--- app/display/gimpdisplayshell-scroll.c	(revision 26017)
+++ app/display/gimpdisplayshell-scroll.c	(working copy)
@@ -34,7 +34,15 @@
 #include "gimpdisplayshell-scale.h"
 #include "gimpdisplayshell-scroll.h"
 
+#include "tools/tools-types.h"
+#include "tools/tool_manager.h"
+#include "tools/gimptool.h"
+#include "tools/gimptoolcontrol.h"
 
+#include "tools/gimpcroptool.h"
+
+#define  PAN_RATIO 0.5
+
 gboolean
 gimp_display_shell_scroll (GimpDisplayShell *shell,
                            gint              x_offset,
@@ -51,6 +59,7 @@
   shell->offset_x += x_offset;
   shell->offset_y += y_offset;
 
+  /* Clamp overpan to specified ratio */
   gimp_display_shell_scroll_clamp_offsets (shell);
 
   /*  the actual changes in offset  */
@@ -99,15 +108,18 @@
       sx = SCALEX (shell, gimp_image_get_width  (shell->display->image));
       sy = SCALEY (shell, gimp_image_get_height (shell->display->image));
 
-      shell->offset_x = CLAMP (shell->offset_x, 0,
-                               MAX (sx - shell->disp_width, 0));
+      shell->offset_x = CLAMP (shell->offset_x, -1 * PAN_RATIO * shell->disp_width,
+                               MAX (MAX (sx, shell->disp_width) - (1 - PAN_RATIO) * shell->disp_width, 
+                                    -1 * PAN_RATIO * shell->disp_width));
 
-      shell->offset_y = CLAMP (shell->offset_y, 0,
-                               MAX (sy - shell->disp_height, 0));
+      shell->offset_y = CLAMP (shell->offset_y, -1 * PAN_RATIO * shell->disp_height,
+                               MAX (MAX (sy, shell->disp_height) - (1 - PAN_RATIO) * shell->disp_height, 
+                                    -1 * PAN_RATIO * shell->disp_height));
     }
   else
     {
       shell->offset_x = 0;
       shell->offset_y = 0;
+
     }
 }
Index: app/display/gimpdisplayshell.c
===================================================================
--- app/display/gimpdisplayshell.c	(revision 26017)
+++ app/display/gimpdisplayshell.c	(working copy)
@@ -937,12 +937,21 @@
   shell->hsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_width,
                                                        1, 1, image_width));
   shell->hsb = gtk_hscrollbar_new (shell->hsbdata);
+
+  gtk_range_set_lower_stepper_sensitivity(GTK_RANGE(shell->hsb), GTK_SENSITIVITY_ON);
+  gtk_range_set_upper_stepper_sensitivity(GTK_RANGE(shell->hsb), GTK_SENSITIVITY_ON);
+
   GTK_WIDGET_UNSET_FLAGS (shell->hsb, GTK_CAN_FOCUS);
 
   /*  the vertical scrollbar  */
   shell->vsbdata = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, image_height,
                                                        1, 1, image_height));
   shell->vsb = gtk_vscrollbar_new (shell->vsbdata);
+
+  gtk_range_set_lower_stepper_sensitivity(GTK_RANGE(shell->vsb), GTK_SENSITIVITY_ON);
+  gtk_range_set_upper_stepper_sensitivity(GTK_RANGE(shell->vsb), GTK_SENSITIVITY_ON);
+
+
   GTK_WIDGET_UNSET_FLAGS (shell->vsb, GTK_CAN_FOCUS);
 
   /*  create the contents of the inner_table  ********************************/
Index: app/display/gimpdisplayshell-scale.c
===================================================================
--- app/display/gimpdisplayshell-scale.c	(revision 26017)
+++ app/display/gimpdisplayshell-scale.c	(working copy)
@@ -119,20 +119,48 @@
       sx = image_width;
       sy = image_height;
     }
+  
 
+
   shell->hsbdata->value          = shell->offset_x;
-  shell->hsbdata->upper          = sx;
   shell->hsbdata->page_size      = MIN (sx, shell->disp_width);
+
+  shell->hsbdata->lower          = MIN ( 0, shell->offset_x);
+  /* The reachable range of a scrollbar is influenced by page bar size.  We
+   * need to take that into account. */
+  if (sx > shell->disp_width) 
+    {
+      shell->hsbdata->upper       = MAX (sx, shell->offset_x + shell->disp_width);
+    }
+  else
+    {
+      shell->hsbdata->upper       = MAX (sx, sx + shell->offset_x);
+    }
+
   shell->hsbdata->page_increment = shell->disp_width / 2;
   shell->hsbdata->step_increment = shell->scale_x;
 
+  gtk_adjustment_changed (shell->hsbdata);
+  
   shell->vsbdata->value          = shell->offset_y;
-  shell->vsbdata->upper          = sy;
+ 
   shell->vsbdata->page_size      = MIN (sy, shell->disp_height);
+
+  shell->vsbdata->lower          = MIN ( 0, shell->offset_y);
+ 
+  /* The reachable range of a scrollbar is influenced by page bar size.  We
+   * need to take that into account. */
+  if (sy > shell->disp_height) 
+    {
+      shell->vsbdata->upper       = MAX (sy, shell->offset_y + shell->disp_height);
+    }
+  else
+    {
+      shell->vsbdata->upper       = MAX (sy, sy + shell->offset_y);
+    }
   shell->vsbdata->page_increment = shell->disp_height / 2;
   shell->vsbdata->step_increment = shell->scale_y;
 
-  gtk_adjustment_changed (shell->hsbdata);
   gtk_adjustment_changed (shell->vsbdata);
 
   /* horizontal ruler */
Index: app/display/gimpdisplayshell-callbacks.c
===================================================================
--- app/display/gimpdisplayshell-callbacks.c	(revision 26017)
+++ app/display/gimpdisplayshell-callbacks.c	(working copy)
@@ -84,7 +84,15 @@
                                                               GimpDisplayShell *shell);
 static void       gimp_display_shell_hscrollbar_update       (GtkAdjustment    *adjustment,
                                                               GimpDisplayShell *shell);
+static gboolean   gimp_display_shell_vscrollbar_update_range (GtkRange         *range,
+                                                              GtkScrollType     scroll,
+                                                              gdouble           value,
+                                                              GimpDisplayShell *shell);
 
+static gboolean   gimp_display_shell_hscrollbar_update_range (GtkRange         *range,
+                                                              GtkScrollType     scroll,
+                                                              gdouble           value,
+                                                              GimpDisplayShell *shell);
 static GdkModifierType
                   gimp_display_shell_key_to_state            (gint              key);
 
@@ -242,6 +250,14 @@
                     G_CALLBACK (gimp_display_shell_vscrollbar_update),
                     shell);
 
+  g_signal_connect (shell->hsb, "change-value",
+                    G_CALLBACK (gimp_display_shell_hscrollbar_update_range),
+                    shell);
+
+  g_signal_connect (shell->vsb, "change-value",
+                    G_CALLBACK (gimp_display_shell_vscrollbar_update_range),
+                    shell);
+
   /*  allow shrinking  */
   gtk_widget_set_size_request (GTK_WIDGET (shell), 0, 0);
 
@@ -327,6 +343,113 @@
     }
 }
 
+gboolean
+gimp_display_shell_vscrollbar_update_range (GtkRange         *range,
+                                            GtkScrollType     scroll,
+                                            gdouble           value,
+                                            GimpDisplayShell *shell)
+{
+  GimpImage *image;
+  gfloat     sy;
+  gint       image_height;
+
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), TRUE);
+
+  if (! shell->display)
+    return TRUE;
+
+  if ((scroll == GTK_SCROLL_JUMP) | (scroll == GTK_SCROLL_PAGE_BACKWARD) | (scroll == GTK_SCROLL_PAGE_FORWARD))
+    return FALSE;
+
+  image = shell->display->image;
+
+  if (image)
+    {
+      image_height = gimp_image_get_height (image);
+
+      sy = SCALEY (shell, image_height);
+    }
+  else
+    {
+      image_height = shell->disp_height;
+
+      sy = image_height;
+    }
+
+  shell->vsbdata->lower           = MIN ( 0, value);
+ 
+  /* The reachable range of a scrollbar is influenced by page bar size.  We
+   * need to take that into account. */
+  if (sy > shell->disp_height) 
+    {
+      shell->vsbdata->upper       = MAX (sy, value + shell->disp_height) +  shell->disp_height;
+    }
+  else
+    {
+      shell->vsbdata->upper       = MAX (sy, value + sy);
+    }
+  
+  shell->vsbdata->page_size       = MIN (sy, shell->disp_height);
+
+  gtk_adjustment_changed (shell->vsbdata);
+
+  return FALSE;
+}
+
+
+gboolean
+gimp_display_shell_hscrollbar_update_range (GtkRange         *range,
+                                            GtkScrollType     scroll,
+                                            gdouble           value,
+                                            GimpDisplayShell *shell)
+{
+  GimpImage *image;
+  gfloat     sx;
+  gint       image_width;
+
+  g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), TRUE);
+
+  if (! shell->display)
+    return TRUE;
+
+  if ((scroll == GTK_SCROLL_JUMP) | (scroll == GTK_SCROLL_PAGE_BACKWARD) | (scroll == GTK_SCROLL_PAGE_FORWARD))
+    return FALSE;
+
+  image = shell->display->image;
+
+  if (image)
+    {
+      image_width = gimp_image_get_width (image);
+
+      sx = SCALEX (shell, image_width);
+    }
+  else
+    {
+      image_width = shell->disp_width;
+
+      sx = image_width;
+    }
+
+  shell->hsbdata->lower           = MIN ( 0, value);
+ 
+  /* The reachable range of a scrollbar is influenced by page bar size.  We
+   * need to take that into account. */
+  if (sx > shell->disp_width) 
+    {
+      shell->hsbdata->upper       = MAX (sx, value + shell->disp_width) +  shell->disp_width;
+    }
+  else
+    {
+      shell->hsbdata->upper       = MAX (sx, value + sx);
+    }
+
+  shell->hsbdata->page_size       = MIN (sx, shell->disp_width);
+
+  gtk_adjustment_changed (shell->hsbdata);
+
+  return FALSE;
+}
+
 static void
 gimp_display_shell_check_device_cursor (GimpDisplayShell *shell)
 {

