Index: app/display.c
===================================================================
--- app/display.c	(revision 4114)
+++ app/display.c	(working copy)
@@ -728,12 +728,6 @@
   ddisp->origo.x = x;
   ddisp->origo.y = y;
 
-  if (ddisp->zoom_factor<DDISPLAY_MIN_ZOOM)
-    ddisp->zoom_factor = DDISPLAY_MIN_ZOOM;
-  
-  if (ddisp->zoom_factor > DDISPLAY_MAX_ZOOM)
-    ddisp->zoom_factor = DDISPLAY_MAX_ZOOM;
-
   width = dia_renderer_get_width_pixels (ddisp->renderer);
   height = dia_renderer_get_height_pixels (ddisp->renderer);
   
@@ -765,11 +759,6 @@
   width = (visible->right - visible->left)/magnify;
   height = (visible->bottom - visible->top)/magnify;
 
-  if ((ddisp->zoom_factor <= DDISPLAY_MIN_ZOOM) && (magnify<=1.0))
-    return;
-  if ((ddisp->zoom_factor >= DDISPLAY_MAX_ZOOM) && (magnify>=1.0))
-    return;
-
   ddisp->zoom_factor *= magnify;
 
   ddisplay_set_origo(ddisp, point->x - width/2.0, point->y - height/2.0);
Index: app/cursor.c
===================================================================
--- app/cursor.c	(revision 4114)
+++ app/cursor.c	(working copy)
@@ -33,6 +33,12 @@
 #include "pixmaps/magnify-plus-mask.xbm"
 #include "pixmaps/magnify-minus-data.xbm"
 #include "pixmaps/magnify-minus-mask.xbm"
+#include "pixmaps/zoom-data.xbm"
+#include "pixmaps/zoom-mask.xbm"
+#include "pixmaps/zoom-in-data.xbm"
+#include "pixmaps/zoom-in-mask.xbm"
+#include "pixmaps/zoom-out-data.xbm"
+#include "pixmaps/zoom-out-mask.xbm"
 
 static struct {
   /* Can't use a union because it can't be statically initialized
@@ -68,6 +74,21 @@
     magnify_plus_data_width, magnify_plus_data_height,
     magnify_plus_mask_bits,
     magnify_plus_data_x_hot, magnify_plus_data_y_hot},
+  { DIA_CURSOR,
+    zoom_data_bits,
+    zoom_data_width, zoom_data_height,
+    zoom_mask_bits,
+    zoom_data_x_hot, zoom_data_y_hot},
+  { DIA_CURSOR,
+    zoom_in_data_bits,
+    zoom_in_data_width, zoom_in_data_height,
+    zoom_in_mask_bits,
+    zoom_in_data_x_hot, zoom_in_data_y_hot},
+  { DIA_CURSOR,
+    zoom_out_data_bits,
+    zoom_out_data_width, zoom_out_data_height,
+    zoom_out_mask_bits,
+    zoom_out_data_x_hot, zoom_out_data_y_hot},
   { GDK_CROSS_REVERSE },
   { GDK_XTERM },
 };
Index: app/cursor.h
===================================================================
--- app/cursor.h	(revision 4114)
+++ app/cursor.h	(working copy)
@@ -27,6 +27,9 @@
   CURSOR_GRABBING,
   CURSOR_ZOOM_OUT,
   CURSOR_ZOOM_IN,
+  CURSOR_SMOOTH_ZOOM,
+  CURSOR_SMOOTH_ZOOM_IN,
+  CURSOR_SMOOTH_ZOOM_OUT,
   CURSOR_CONNECT,
   CURSOR_XTERM,
   MAX_CURSORS
Index: app/grid.c
===================================================================
--- app/grid.c	(revision 4114)
+++ app/grid.c	(working copy)
@@ -36,16 +36,10 @@
 calculate_dynamic_grid(DDisplay *ddisp, real *width_x, real *width_y)
 {
   real zoom = ddisplay_untransform_length(ddisp, 1.0);
-  real ret, tmp;
+  real ret;
   /* Twiddle zoom to make change-over appropriate */
   zoom *= 5;
   ret = pow(10, ceil(log10(zoom)));
-  /* dont' make it too small or huge (this is in pixels) */
-  tmp = ddisplay_transform_length(ddisp, ret);
-  if (tmp < 10.0)
-    ret *= 2.0;
-  else if (tmp > 35.0)
-    ret /= 2.0;
   *width_x = ret;
   *width_y = ret;
 }
@@ -275,7 +269,7 @@
 
   int width = dia_renderer_get_width_pixels(ddisp->renderer);
   int height = dia_renderer_get_height_pixels(ddisp->renderer);
-  
+
   irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (renderer);
   if (prefs.pagebreak.visible) {
     Diagram *dia = ddisp->diagram;
Index: app/pixmaps/zoom-data.xbm
===================================================================
--- app/pixmaps/zoom-data.xbm	(revision 0)
+++ app/pixmaps/zoom-data.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_data_width 32
+#define zoom_data_height 32
+#define zoom_data_x_hot 8
+#define zoom_data_y_hot 8
+static unsigned char zoom_data_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+   0x20, 0x08, 0x00, 0x00, 0x90, 0x13, 0x00, 0x00, 0x08, 0x24, 0x00, 0x00,
+   0x04, 0x48, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00,
+   0x04, 0x40, 0x00, 0x10, 0x04, 0x40, 0x00, 0x38, 0x08, 0x20, 0x00, 0x54,
+   0x10, 0x30, 0x00, 0x10, 0x20, 0x78, 0x00, 0x10, 0xc0, 0xe7, 0x00, 0x10,
+   0x00, 0xc0, 0x01, 0x54, 0x00, 0x80, 0x03, 0x38, 0x00, 0x00, 0x07, 0x10,
+   0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x38, 0x00,
+   0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/pixmaps/zoom-in-data.xbm
===================================================================
--- app/pixmaps/zoom-in-data.xbm	(revision 0)
+++ app/pixmaps/zoom-in-data.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_in_data_width 32
+#define zoom_in_data_height 32
+#define zoom_in_data_x_hot 8
+#define zoom_in_data_y_hot 8
+static unsigned char zoom_in_data_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xc0, 0x07, 0x00, 0x10,
+   0x20, 0x08, 0x00, 0x10, 0x90, 0x13, 0x00, 0xfe, 0x08, 0x24, 0x00, 0x10,
+   0x04, 0x48, 0x00, 0x10, 0x04, 0x40, 0x00, 0x10, 0x04, 0x40, 0x00, 0x00,
+   0x04, 0x40, 0x00, 0x10, 0x04, 0x40, 0x00, 0x38, 0x08, 0x20, 0x00, 0x54,
+   0x10, 0x30, 0x00, 0x10, 0x20, 0x78, 0x00, 0x10, 0xc0, 0xe7, 0x00, 0x10,
+   0x00, 0xc0, 0x01, 0x54, 0x00, 0x80, 0x03, 0x38, 0x00, 0x00, 0x07, 0x10,
+   0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x38, 0x00,
+   0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/pixmaps/zoom-mask.xbm
===================================================================
--- app/pixmaps/zoom-mask.xbm	(revision 0)
+++ app/pixmaps/zoom-mask.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_mask_width 32
+#define zoom_mask_height 32
+#define zoom_mask_x_hot 8
+#define zoom_mask_y_hot 8
+static unsigned char zoom_mask_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00,
+   0xf0, 0x1f, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x1c, 0x70, 0x00, 0x00,
+   0x0e, 0xe0, 0x00, 0x00, 0x0e, 0xe0, 0x00, 0x00, 0x0e, 0xe0, 0x00, 0x00,
+   0x0e, 0xe0, 0x00, 0x38, 0x0e, 0xe0, 0x00, 0x7c, 0x1c, 0x70, 0x00, 0x7c,
+   0x38, 0x78, 0x00, 0x7c, 0xf0, 0xff, 0x00, 0x38, 0xe0, 0xff, 0x01, 0x7c,
+   0xc0, 0xe7, 0x03, 0x7c, 0x00, 0xc0, 0x07, 0x7c, 0x00, 0x80, 0x0f, 0x38,
+   0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7c, 0x00,
+   0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/pixmaps/zoom-in-mask.xbm
===================================================================
--- app/pixmaps/zoom-in-mask.xbm	(revision 0)
+++ app/pixmaps/zoom-in-mask.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_in_mask_width 32
+#define zoom_in_mask_height 32
+#define zoom_in_mask_x_hot 8
+#define zoom_in_mask_y_hot 8
+static unsigned char zoom_in_mask_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x38, 0xe0, 0x0f, 0x00, 0x38,
+   0xf0, 0x1f, 0x00, 0xfe, 0x38, 0x38, 0x00, 0xfe, 0x1c, 0x70, 0x00, 0xfe,
+   0x0e, 0xe0, 0x00, 0x38, 0x0e, 0xe0, 0x00, 0x38, 0x0e, 0xe0, 0x00, 0x00,
+   0x0e, 0xe0, 0x00, 0x38, 0x0e, 0xe0, 0x00, 0x7c, 0x1c, 0x70, 0x00, 0x7c,
+   0x38, 0x78, 0x00, 0x7c, 0xf0, 0xff, 0x00, 0x38, 0xe0, 0xff, 0x01, 0x7c,
+   0xc0, 0xe7, 0x03, 0x7c, 0x00, 0xc0, 0x07, 0x7c, 0x00, 0x80, 0x0f, 0x38,
+   0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x7c, 0x00,
+   0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/pixmaps/zoom-out-data.xbm
===================================================================
--- app/pixmaps/zoom-out-data.xbm	(revision 0)
+++ app/pixmaps/zoom-out-data.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_out_data_width 32
+#define zoom_out_data_height 32
+#define zoom_out_data_x_hot 8
+#define zoom_out_data_y_hot 8
+static unsigned char zoom_out_data_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00,
+   0x20, 0x08, 0x00, 0x00, 0x90, 0x13, 0x00, 0x00, 0x08, 0x24, 0x00, 0x00,
+   0x04, 0x48, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00,
+   0x04, 0x40, 0x00, 0x10, 0x04, 0x40, 0x00, 0x38, 0x08, 0x20, 0x00, 0x54,
+   0x10, 0x30, 0x00, 0x10, 0x20, 0x78, 0x00, 0x10, 0xc0, 0xe7, 0x00, 0x10,
+   0x00, 0xc0, 0x01, 0x54, 0x00, 0x80, 0x03, 0x38, 0x00, 0x00, 0x07, 0x10,
+   0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x38, 0xfe,
+   0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/pixmaps/zoom-out-mask.xbm
===================================================================
--- app/pixmaps/zoom-out-mask.xbm	(revision 0)
+++ app/pixmaps/zoom-out-mask.xbm	(revision 0)
@@ -0,0 +1,16 @@
+#define zoom_out_mask_width 32
+#define zoom_out_mask_height 32
+#define zoom_out_mask_x_hot 8
+#define zoom_out_mask_y_hot 8
+static unsigned char zoom_out_mask_bits[] = {
+   0x00, 0x00, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00,
+   0xf0, 0x1f, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x1c, 0x70, 0x00, 0x00,
+   0x0e, 0xe0, 0x00, 0x00, 0x0e, 0xe0, 0x00, 0x00, 0x0e, 0xe0, 0x00, 0x00,
+   0x0e, 0xe0, 0x00, 0x38, 0x0e, 0xe0, 0x00, 0x7c, 0x1c, 0x70, 0x00, 0x7c,
+   0x38, 0x78, 0x00, 0x7c, 0xf0, 0xff, 0x00, 0x38, 0xe0, 0xff, 0x01, 0x7c,
+   0xc0, 0xe7, 0x03, 0x7c, 0x00, 0xc0, 0x07, 0x7c, 0x00, 0x80, 0x0f, 0x38,
+   0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3e, 0xfe, 0x00, 0x00, 0x7c, 0xfe,
+   0x00, 0x00, 0x38, 0xfe, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Index: app/scroll_tool.c
===================================================================
--- app/scroll_tool.c	(revision 4114)
+++ app/scroll_tool.c	(working copy)
@@ -46,7 +46,7 @@
   tool->tool.double_click_func = (DoubleClickFunc) &scroll_double_click;
 
   tool->scrolling = FALSE;
-  tool->use_hand = TRUE;
+  tool->zooming = FALSE;
 
   ddisplay_set_all_cursor(get_cursor(CURSOR_GRAB));
   
@@ -74,18 +74,22 @@
 {
   Point clickedpoint;
 
-  tool->use_hand = (event->state & GDK_SHIFT_MASK) == 0;
-  if (tool->use_hand)
+  if((event->state & GDK_SHIFT_MASK) == 0) {
     ddisplay_set_all_cursor(get_cursor(CURSOR_GRABBING));
-  else
-    ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
 
-  ddisplay_untransform_coords(ddisp,
+    ddisplay_untransform_coords(ddisp,
 			      (int)event->x, (int)event->y,
 			      &clickedpoint.x, &clickedpoint.y);
 
-  tool->scrolling = TRUE;
-  tool->last_pos = clickedpoint;
+    tool->scrolling = TRUE;
+    tool->last_pos = clickedpoint;
+  } else {
+    ddisplay_set_all_cursor(get_cursor(CURSOR_SMOOTH_ZOOM));
+
+    tool->zooming = TRUE;
+    tool->start_pos.x = tool->last_pos.x = event->x;
+    tool->start_pos.y = tool->last_pos.y = event->y;
+  }
 }
 
 
@@ -95,28 +99,26 @@
 scroll_motion(ScrollTool *tool, GdkEventMotion *event,
 	      DDisplay *ddisp)
 {
+  Rectangle *visible;
+  Point tl;
   Point to;
+  Point sdelta;
   Point delta;
 
-  /* set the cursor appropriately, and change use_hand if needed */
-  if (!tool->scrolling) {
+  /* set the cursor appropriately */
+  if (!tool->scrolling && !tool->zooming) {
     /* try to minimise the number of cursor type changes */
     if ((event->state & GDK_SHIFT_MASK) == 0) {
-      if (!tool->use_hand) {
-	tool->use_hand = TRUE;
-	ddisplay_set_all_cursor(get_cursor(CURSOR_GRAB));
-      }
-    } else
-      if (tool->use_hand) {
-	tool->use_hand = FALSE;
-	ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
-      }
+      ddisplay_set_all_cursor(get_cursor(CURSOR_GRAB));
+    } else {
+      ddisplay_set_all_cursor(get_cursor(CURSOR_SMOOTH_ZOOM));
+    }
     return;
   }
 
-  ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y);
+  if (tool->scrolling) {
+    ddisplay_untransform_coords(ddisp, event->x, event->y, &to.x, &to.y);
 
-  if (tool->use_hand) {
     delta = tool->last_pos;
     point_sub(&delta, &to);
 
@@ -128,13 +130,37 @@
     ddisplay_set_origo(ddisp, delta.x, delta.y);
     ddisplay_update_scrollbars(ddisp);
     ddisplay_add_update_all(ddisp);
-  } else {
-    delta = to;
-    point_sub(&delta, &tool->last_pos);
-    point_scale(&delta, 0.5);
+  } else if (tool->zooming) {
+    to.x = event->x;
+    to.y = event->y;
 
-    ddisplay_scroll(ddisp, &delta);
+    sdelta = tool->start_pos;
+    point_sub(&sdelta, &tool->last_pos);
+
+    delta = tool->last_pos;
+    point_sub(&delta, &to);
+
+    if ((sdelta.y > 0 && delta.y < 0) || (sdelta.y < 0 && delta.y > 0)) {
+      tool->start_pos = tool->last_pos;
+    }
+
+    delta = tool->start_pos;
+    point_sub(&delta, &to);
+
     tool->last_pos = to;
+
+    visible = &ddisp->visible;
+
+    tl.x = visible->left + (visible->right - visible->left) / 2.0;
+    tl.y = visible->top + (visible->bottom - visible->top) / 2.0;
+
+    if (delta.y > 0) {
+      ddisplay_set_all_cursor(get_cursor(CURSOR_SMOOTH_ZOOM_IN));
+      ddisplay_zoom(ddisp, &tl, 1.02);
+    } else if (delta.y < 0) {
+      ddisplay_set_all_cursor(get_cursor(CURSOR_SMOOTH_ZOOM_OUT));
+      ddisplay_zoom(ddisp, &tl, 0.98);
+    }
   }
   ddisplay_flush(ddisp);
 }
@@ -144,12 +170,13 @@
 scroll_button_release(ScrollTool *tool, GdkEventButton *event,
 		      DDisplay *ddisp)
 {
-  tool->use_hand = (event->state & GDK_SHIFT_MASK) == 0;
-  if (tool->use_hand) {
+  if ((event->state & GDK_SHIFT_MASK) == 0) {
     ddisplay_set_all_cursor(get_cursor(CURSOR_GRAB));
-  } else
-    ddisplay_set_all_cursor(get_cursor(CURSOR_SCROLL));
+  } else {
+    ddisplay_set_all_cursor(get_cursor(CURSOR_SMOOTH_ZOOM));
+  }
 
   tool->scrolling = FALSE;
+  tool->zooming = FALSE;
 }
 
Index: app/scroll_tool.h
===================================================================
--- app/scroll_tool.h	(revision 4114)
+++ app/scroll_tool.h	(working copy)
@@ -27,7 +27,8 @@
   Tool tool;
 
   int scrolling;
-  int use_hand;
+  int zooming;
+  Point start_pos;
   Point last_pos;
 };
 
