diff --git a/Common/Context.h b/Common/Context.h
index da75f02e39ea11ff7630564ae59876c97411c681..5a452845d2a182615c70f61a987eb0ae007decb7 100644
--- a/Common/Context.h
+++ b/Common/Context.h
@@ -147,6 +147,7 @@ public :
 
   double model[16], proj[16]; // the modelview and projection matrix as they were
                               // at the time of the last InitPosition() call
+  double model_init[16]; // the modelview matrix before applying s, t and rot
 
   int forced_bbox; // dynamic variable tracking if the bbox is currently imposed
 
diff --git a/Fltk/Opengl_Window.cpp b/Fltk/Opengl_Window.cpp
index 42f472117a474630f1a9376b20058c2dfe8158e2..39026c0be8abe63642161bbed81b603f78114cd3 100644
--- a/Fltk/Opengl_Window.cpp
+++ b/Fltk/Opengl_Window.cpp
@@ -1,4 +1,4 @@
-// $Id: Opengl_Window.cpp,v 1.52 2005-12-16 19:17:33 geuzaine Exp $
+// $Id: Opengl_Window.cpp,v 1.53 2005-12-17 22:28:16 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -29,12 +29,88 @@
 #include "GUI.h"
 #include "Opengl_Window.h"
 
-// This file defines the Opengl_Window class (subclass of Fl_GL_Window)
-
 extern GUI *WID;
 extern Mesh M;
 extern Context_T CTX;
 
+void MousePosition::set()
+{
+  for(int i = 0; i < 3; i++){
+    s[i] = CTX.s[i];
+    t[i] = CTX.t[i];
+  }
+
+  win[0] = (double)Fl::event_x();
+  win[1] = (double)Fl::event_y();
+  win[2] = 0.;
+
+  wnr[0] = (CTX.vxmin + win[0] / (double)CTX.viewport[2] * 
+	    (CTX.vxmax - CTX.vxmin)) / CTX.s[0] - CTX.t[0];
+  wnr[1] = (CTX.vymax - win[1] / (double)CTX.viewport[3] * 
+	    (CTX.vymax - CTX.vymin)) / CTX.s[1] - CTX.t[1];
+  wnr[2] = 0.;
+
+  // might need this later
+  // Viewport2World(win, world);
+}
+
+void MousePosition::recenter()
+{
+  // this computes the equivalent translation to apply after the
+  // scaling so that the scaling is done around the point which was
+  // clicked. FIXME: needs to be generalized to the case where an
+  // initial translation is done BEFORE the scaling (necessary for the
+  // general perspective case, with the line of sight in the middle of
+  // the screen, and not just the z-axis).
+  CTX.t[0] = t[0] * (s[0] / CTX.s[0]) - wnr[0] * (1. - (s[0] / CTX.s[0]));
+  CTX.t[1] = t[1] * (s[1] / CTX.s[1]) - wnr[1] * (1. - (s[1] / CTX.s[1]));
+
+  /*
+  double sx, sy;
+  double tx0, ty0;
+  double tx, ty;
+  double model_new[16];
+
+  glPushMatrix();
+  glLoadMatrix(CTX.model_init);
+  glTranslated(tx0, ty0, 0.);
+  glScaled(sx, sy, sz);
+  glTranslated(-tx0, -ty0, 0.);
+  glTranslated(tx, ty, 0.);
+  glGetDoublev(GL_MODELVIEW_MATRIX, model_new);
+  glPopMatrix();
+
+  CTX.s[0] = model_new[0][0];
+  CTX.s[1] = model_new[1][1];
+  CTX.s[2] = model_new[2][2];
+
+  CTX.t[0] = model_new[0][3];
+  CTX.t[1] = model_new[1][3];
+  CTX.t[2] = model_new[2][3];
+  */
+}
+  
+void myZoom(MousePosition &click1, MousePosition &click2)
+{
+  if(click1.wnr[0] == click2.wnr[0] || click1.wnr[1] == click2.wnr[1])
+    return;
+
+  CTX.s[0] *= (CTX.vxmax - CTX.vxmin) / (click2.wnr[0] - click1.wnr[0]);
+  CTX.s[1] *= (CTX.vymax - CTX.vymin) / (click1.wnr[1] - click2.wnr[1]);
+  CTX.s[2] = MIN(CTX.s[0], CTX.s[1]); // bof...
+  
+  MousePosition tmp(click1);
+  tmp.wnr[0] = 0.5 * (click1.wnr[0] + click2.wnr[0]);
+  tmp.wnr[1] = 0.5 * (click1.wnr[1] + click2.wnr[1]);
+  tmp.t[0] = CTX.t[0];
+  tmp.t[1] = CTX.t[1]; 
+  tmp.recenter();
+
+  WID->update_manip_window();
+  InitPosition();
+  Draw();
+}
+
 void Opengl_Window::draw()
 {
   static int locked = 0;
@@ -42,7 +118,9 @@ void Opengl_Window::draw()
     return;
   else
     locked = 1;
+
   Msg(DEBUG, "Opengl_Window->draw()");
+
   if(!valid()) {
     valid(1);
     CTX.viewport[0] = 0;
@@ -75,56 +153,27 @@ void Opengl_Window::draw()
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     glDisable(GL_DEPTH_TEST);
-    glColor3f(1., 1., 1.);
-    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); // glBlendEquation(GL_FUNC_ADD);
+    glColor3d(1., 1., 1.);
+    // glBlendEquation(GL_FUNC_ADD);
+    glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
     glEnable(GL_BLEND);
     glLineWidth(0.2);
-    glBegin(GL_LINE_STRIP);
-    glVertex2d(xzoom0, yzoom0);
-    glVertex2d(xzoom1, yzoom0);
-    glVertex2d(xzoom1, yzoom1);
-    glVertex2d(xzoom0, yzoom1);
-    glVertex2d(xzoom0, yzoom0);
-    glEnd();
-    xzoom1 = CTX.vxmin + ((double)Fl::event_x() / (double)w()) * 
-      (CTX.vxmax - CTX.vxmin);
-    yzoom1 = CTX.vymax - ((double)Fl::event_y() / (double)h()) *
-      (CTX.vymax - CTX.vymin);
-    glBegin(GL_LINE_STRIP);
-    glVertex2d(xzoom0, yzoom0);
-    glVertex2d(xzoom1, yzoom0);
-    glVertex2d(xzoom1, yzoom1);
-    glVertex2d(xzoom0, yzoom1);
-    glVertex2d(xzoom0, yzoom0);
-    glEnd();
+    for(int i = 0; i < 2; i++){
+      glBegin(GL_LINE_STRIP);
+      glVertex2d(click.wnr[0], click.wnr[1]);
+      glVertex2d(zoom.wnr[0], click.wnr[1]);
+      glVertex2d(zoom.wnr[0], zoom.wnr[1]);
+      glVertex2d(click.wnr[0], zoom.wnr[1]);
+      glVertex2d(click.wnr[0], click.wnr[1]);
+      glEnd();
+      if(!i) zoom.set();
+    }
     glDisable(GL_BLEND);
     glEnable(GL_DEPTH_TEST);
-    ZoomMode = false;
   }
   locked = 0;
 }
 
-// FIXME: this is notoriously wrong :-)
-
-void myZoom(GLdouble X1, GLdouble X2, GLdouble Y1, GLdouble Y2,
-            GLdouble Xc1, GLdouble Xc2, GLdouble Yc1, GLdouble Yc2)
-{
-  GLdouble xscale1 = CTX.s[0];
-  GLdouble yscale1 = CTX.s[1];
-  CTX.s[0] *= (CTX.vxmax - CTX.vxmin) / (X2 - X1);
-  CTX.s[1] *= (CTX.vymax - CTX.vymin) / (Y1 - Y2);
-  CTX.s[2] = MIN(CTX.s[0], CTX.s[1]); // bof...
-  CTX.t[0] = CTX.t[0] * (xscale1 / CTX.s[0]) - 
-    ((Xc1 + Xc2) / 2.) * (1. - (xscale1 / CTX.s[0]));
-  CTX.t[1] = CTX.t[1] * (yscale1 / CTX.s[1]) - 
-    ((Yc1 + Yc2) / 2.) * (1. - (yscale1 / CTX.s[1]));
-  
-  WID->update_manip_window();
-  InitPosition();
-  Draw();
-}
-
-
 // The event model in FLTK is pretty different from other toolkits:
 // the events are passed to the widget handle of the widget that has
 // the focus. If this handle returns 1, then the event is considered
@@ -133,6 +182,7 @@ void myZoom(GLdouble X1, GLdouble X2, GLdouble Y1, GLdouble Y2,
 
 int Opengl_Window::handle(int event)
 {
+  double dx, dy;
   int numhits;
   hit hits[SELECTION_BUFFER_SIZE];
 
@@ -144,52 +194,42 @@ int Opengl_Window::handle(int event)
 
   case FL_SHORTCUT:
   case FL_KEYBOARD:
-    // this overrides the default navigation
-    if(WID->arrow_shortcuts()) {
+    // override the default widget navigation
+    if(WID->arrow_shortcuts())
       return 1;
-    }
     return Fl_Gl_Window::handle(event);
-
+    
   case FL_PUSH:
     take_focus(); // force keyboard focus when we click in the window
-    FirstClick = 1;
-    ibut = Fl::event_button();
-    xpos = Fl::event_x();
-    ypos = Fl::event_y();
+    curr.set();
     
-    if(ibut == 1 && !Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_ALT)) {
-      if(!ZoomClick && Fl::event_state(FL_CTRL)) {
-        xzoom0 = xzoom1 = CTX.vxmin + ((double)xpos / (double)w()) *
-	  (CTX.vxmax - CTX.vxmin);
-        yzoom0 = yzoom1 = CTX.vymax - ((double)ypos / (double)h()) * 
-	  (CTX.vymax - CTX.vymin);
-        xc1 = xzoom0 / CTX.s[0] - CTX.t[0];
-        yc1 = yzoom0 / CTX.s[1] - CTX.t[1];
-        ZoomClick = true;
+    if(Fl::event_button() == 1 && 
+       !Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_ALT)) {
+      if(!ZoomMode && Fl::event_state(FL_CTRL)) {
+        ZoomMode = true;
+	zoom.set();
       }
-      else if(ZoomClick) {
-        xc2 = xzoom1 / CTX.s[0] - CTX.t[0];
-        yc2 = yzoom1 / CTX.s[1] - CTX.t[1];
-        ZoomClick = false;
-        if(xzoom0 != xzoom1 && yzoom0 != yzoom1)
-          myZoom(xzoom0, xzoom1, yzoom0, yzoom1, xc1, xc2, yc1, yc2);
+      else if(ZoomMode) {
+        ZoomMode = false;
+	myZoom(click, curr);
       }
       else {
         WID->try_selection = 1;
       }
     }
-    else if(ibut == 2 || (ibut == 1 && Fl::event_state(FL_SHIFT))) {
-      if(Fl::event_state(FL_CTRL) && !ZoomClick) {
+    else if(Fl::event_button() == 2 || 
+	    (Fl::event_button() == 1 && Fl::event_state(FL_SHIFT))) {
+      if(Fl::event_state(FL_CTRL) && !ZoomMode) {
         CTX.s[1] = CTX.s[0];
         CTX.s[2] = CTX.s[0];
         redraw();
       }
       else {
-        ZoomClick = false;
+        ZoomMode = false;
       }
     }
     else {
-      if(Fl::event_state(FL_CTRL) && !ZoomClick) {
+      if(Fl::event_state(FL_CTRL) && !ZoomMode) {
         if(CTX.useTrackball)
           CTX.setQuaternion(0., 0., 0., 1.);
         else
@@ -199,106 +239,86 @@ int Opengl_Window::handle(int event)
         redraw();
       }
       else {
-        ZoomClick = false;
+        ZoomMode = false;
       }
     }
+
+    click.set();
+    prev.set();
     WID->update_manip_window();
     return 1;
 
   case FL_RELEASE:
-    ibut = Fl::event_button();
-    xpos = Fl::event_x();
-    ypos = Fl::event_y();
-    if(!ZoomClick) {
+    curr.set();
+    if(!ZoomMode) {
       CTX.mesh.draw = 1;
       CTX.post.draw = 1;
       redraw();
     }
+    prev.set();
     return 1;
 
   case FL_DRAG:
-    xmov = Fl::event_x() - xpos;
-    ymov = Fl::event_y() - ypos;
+    curr.set();
+    dx = curr.win[0] - prev.win[0];
+    dy = curr.win[1] - prev.win[1];
 
-    if(ZoomClick) {
-      ZoomMode = true;
+    if(ZoomMode) {
       redraw();
     }
     else {
-      if(FirstClick) {
-        xc1 = (((double)xpos / (double)w()) * (CTX.vxmax - CTX.vxmin) + CTX.vxmin)
-          / CTX.s[0] - CTX.t[0];
-        yc1 = (CTX.vymax - ((double)ypos / (double)h()) * (CTX.vymax - CTX.vymin))
-          / CTX.s[1] - CTX.t[1];
-        xt1 = CTX.t[0];
-        yt1 = CTX.t[1];
-        xscale1 = CTX.s[0];
-        yscale1 = CTX.s[1];
-        FirstClick = 0;
-      }
-
-      if(ibut == 1 && !Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_ALT)) {
+      if(Fl::event_button() == 1 && 
+	 !Fl::event_state(FL_SHIFT) && !Fl::event_state(FL_ALT)) {
         if(CTX.useTrackball)
-          CTX.addQuaternion((2.0 * xpos - w()) / w(),
-                            (h() - 2.0 * ypos) / h(),
-                            (2.0 * Fl::event_x() - w()) / w(),
-                            (h() - 2.0 * Fl::event_y()) / h());
+          CTX.addQuaternion((2. * prev.win[0] - w()) / w(),
+                            (h() - 2. * prev.win[1]) / h(),
+                            (2. * curr.win[0] - w()) / w(),
+                            (h() - 2. * curr.win[1]) / h());
         else {
-          CTX.r[1] += ((abs(xmov) > abs(ymov)) ? 180 * (double)xmov / (double)w() : 0);
-	  CTX.r[0] += ((abs(xmov) > abs(ymov)) ? 0 : 180 * (double)ymov / (double)h());
+          CTX.r[1] += ((fabs(dx) > fabs(dy)) ? 180. * dx / (double)w() : 0.);
+	  CTX.r[0] += ((fabs(dx) > fabs(dy)) ? 0. : 180. * dy / (double)h());
         }
       }
-      else if(ibut == 2 || (ibut == 1 && Fl::event_state(FL_SHIFT))) {
-	if(!CTX.useTrackball)
-          CTX.r[2] += (abs(ymov) > abs(xmov) ? 0 : -180 * (double)xmov / (double)w());
-	double zoomfact = (ymov > 0) ? 
-	  (double)(CTX.zoom_factor * abs(ymov) + h()) / (double)h() : 
-	  (double)(h()) / (double)(CTX.zoom_factor * abs(ymov) + h());
-	CTX.s[0] *= (abs(ymov) > abs(xmov) ? zoomfact : 1.);
-        CTX.s[1] = CTX.s[0];
-        CTX.s[2] = CTX.s[0];
-        if(abs(ymov) > abs(xmov)) {
-          CTX.t[0] = xt1 * (xscale1 / CTX.s[0]) - xc1 * (1. - (xscale1 / CTX.s[0]));
-          CTX.t[1] = yt1 * (yscale1 / CTX.s[1]) - yc1 * (1. - (yscale1 / CTX.s[1]));
-        }
+      else if(Fl::event_button() == 2 ||
+	      (Fl::event_button() == 1 && Fl::event_state(FL_SHIFT))) {
+        if(fabs(dy) > fabs(dx)) {
+	  double fact = (CTX.zoom_factor * fabs(dy) + h()) / (double)h();
+	  CTX.s[0] *= ((dy > 0) ? fact : 1./fact);
+	  CTX.s[1] = CTX.s[0];
+	  CTX.s[2] = CTX.s[0];
+	  click.recenter();
+	}
+	else if(!CTX.useTrackball)
+          CTX.r[2] += -180. * dx / (double)w();
       }
       else {
-        xc = (((double)xpos / (double)w()) * (CTX.vxmax - CTX.vxmin) + CTX.vxmin)
-          / CTX.s[0];
-        yc = (CTX.vymax - ((double)ypos / (double)h()) * (CTX.vymax - CTX.vymin))
-          / CTX.s[1];
-        CTX.t[0] = xc - xc1;
-        CTX.t[1] = yc - yc1;
+        CTX.t[0] += (curr.wnr[0] - click.wnr[0]);
+	CTX.t[1] += (curr.wnr[1] - click.wnr[1]);
         CTX.t[2] = 0.;
       }
-
       if(CTX.fast_redraw) {
         CTX.mesh.draw = 0;
         CTX.post.draw = 0;
       }
-
       redraw();
     }
 
-    xpos += xmov;
-    ypos += ymov;
+    prev.set();
     WID->update_manip_window();
     return 1;
 
   case FL_MOVE:
-    xpos = Fl::event_x();
-    ypos = Fl::event_y();
+    curr.set();
 
     if(AddPointMode && !Fl::event_state(FL_SHIFT)){
       WID->g_opengl_window->cursor(FL_CURSOR_CROSS, FL_BLACK, FL_WHITE);
       // find line in real space corresponding to current cursor position
       double p[3],d[3];
-      unproject(xpos, ypos, p, d);
+      unproject(curr.win[0], curr.win[1], p, d);
       // fin closest point to the center of gravity
-      double r[3] = {CTX.cg[0]-p[0], CTX.cg[1]-p[1], CTX.cg[2]-p[2]};
-      double t;
+      double r[3] = {CTX.cg[0] - p[0], CTX.cg[1] - p[1], CTX.cg[2] - p[2]}, t;
       prosca(r,d,&t);
-      double sol[3] = {p[0]+t*d[0], p[1]+t*d[1], p[2]+t*d[2]};
+      double sol[3] = {p[0] + t * d[0], p[1] + t * d[1], p[2] + t * d[2]};
       char str[32];
       sprintf(str, "%g", sol[0]);        
       WID->context_geometry_input[2]->value(str);
@@ -307,19 +327,15 @@ int Opengl_Window::handle(int event)
       sprintf(str, "%g", sol[2]);
       WID->context_geometry_input[4]->value(str);
     }
-    else if(ZoomClick) {
-      ZoomMode = true;
+    else if(ZoomMode) {
       redraw();
     }
     else {
       WID->make_opengl_current();
-      Process_SelectionBuffer(xpos, ypos, &numhits, hits);
-      ov = v;
-      oc = c;
-      os = s;
-      v = NULL;
-      c = NULL;
-      s = NULL;
+      Process_SelectionBuffer((int)curr.win[0], (int)curr.win[1], &numhits, hits);
+      ov = v; v = NULL;
+      oc = c; c = NULL;
+      os = s; s = NULL;
       Filter_SelectionBuffer(WID->selection, numhits, hits, &v, &c, &s, &M);
       if(ov != v || oc != c || os != s) {
         if((WID->selection == ENT_POINT && v) ||
@@ -331,11 +347,10 @@ int Opengl_Window::handle(int event)
         HighlightEntity(v, c, s, 0);
       }
     }
+    prev.set();
     return 1;
 
   default:
     return Fl_Gl_Window::handle(event);
-
   }
-
 }
diff --git a/Fltk/Opengl_Window.h b/Fltk/Opengl_Window.h
index 582e37184286ce6d400d40f414f2fe5979ad8efb..3e6f8e80a8a75aaa0b1cb5909846899a01a86f69 100644
--- a/Fltk/Opengl_Window.h
+++ b/Fltk/Opengl_Window.h
@@ -24,27 +24,46 @@
 #include <FL/Fl_Box.H>
 #include "Mesh.h"
 
+class MousePosition {
+ public:
+  double win[3]; // window coordinates
+  double wnr[3]; // world coordinates BEFORE rotation
+  double world[3]; // world coordinates (in a given plane // to screen)
+  double s[3]; // scaling state when the event was recorded
+  double t[3];  // translation state when the event was recorded
+  MousePosition(){
+    for(int i = 0; i < 3; i++)
+      win[i] = wnr[i] = world[i] = s[i] = t[i] = 0.;
+  }
+  MousePosition(const MousePosition &instance){
+    for(int i = 0; i < 3; i++){
+      win[i] = instance.win[i];
+      wnr[i] = instance.wnr[i];
+      world[i] = instance.world[i];
+      s[i] = instance.s[i];
+      t[i] = instance.t[i];
+    }
+  }
+  void set();
+  void recenter();
+};
+
 class Opengl_Window : public Fl_Gl_Window {
  public:
-  bool AddPointMode;
+  bool AddPointMode, ZoomMode;
  private:
-  bool ZoomMode, ZoomClick, FirstClick;
-  int xpos, ypos, xmov, ymov, ibut, hits;
-  double xzoom0, yzoom0, xzoom1, yzoom1;
-  GLdouble xc, yc, xc1, yc1, xc2, yc2, xt1, yt1, xscale1, yscale1;
+  int hits;
   Vertex *v, *ov;
   Curve *c, *oc;
   Surface *s, *os;
-
+  MousePosition click, curr, prev, zoom;
   void draw();
   int handle(int);
-
  public:
   Opengl_Window(int x,int y,int w,int h,const char *l=0)
     : Fl_Gl_Window(x, y, w, h, l) {
-    xpos = ypos = xmov = ymov = ibut = hits = 0;
-    xzoom0 = yzoom0 = xzoom1 = yzoom1 = 0.;
-    AddPointMode = ZoomMode = ZoomClick = FirstClick = false;
+    AddPointMode = ZoomMode = false;
+    hits = 0;
     v = ov = NULL;
     c = oc = NULL;
     s = os = NULL;
diff --git a/Graphics/Draw.cpp b/Graphics/Draw.cpp
index da79246cfa7ec23f54b5309bcbca77335625fb5d..d8226d81fda9f5832ecb9a2a0f976f7437e930fa 100644
--- a/Graphics/Draw.cpp
+++ b/Graphics/Draw.cpp
@@ -1,4 +1,4 @@
-// $Id: Draw.cpp,v 1.82 2005-12-16 19:17:34 geuzaine Exp $
+// $Id: Draw.cpp,v 1.83 2005-12-17 22:28:16 geuzaine Exp $
 //
 // Copyright (C) 1997-2005 C. Geuzaine, J.-F. Remacle
 //
@@ -298,6 +298,11 @@ void InitRenderModel(void)
 
 void InitPosition(void)
 {
+  // store the model transfo matrix before we position the object
+  // (this way we can isolate the scaling/translation/rotation) from
+  // any transformations made before to adjust the viewpoint
+  glGetDoublev(GL_MODELVIEW_MATRIX, CTX.model_init);
+
   glScaled(CTX.s[0], CTX.s[1], CTX.s[2]);
   glTranslated(CTX.t[0], CTX.t[1], CTX.t[2]);
 
diff --git a/tutorial/t9.geo b/tutorial/t9.geo
index d9c34517368d4f7ee2e9feb376ccdf2f35a0c418..58004910d9b92953ae642d968a75028150d4d559 100644
--- a/tutorial/t9.geo
+++ b/tutorial/t9.geo
@@ -54,7 +54,9 @@ Plugin(Annotate).Font = "Times-Roman" ;
 Plugin(Annotate).FontSize = 12 ; 
 Plugin(Annotate).Run ; 
 
-// We finish by setting some view options and redrawing the scene:
+// We finish by setting some options and redrawing the scene:
+
+General.BackgroundGradient = 1;
 
 View[0].Light = 1;
 View[0].IntervalsType = 1;