Index: trunk/src/org/openstreetmap/josm/actions/ViewportFollowToggleAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ViewportFollowToggleAction.java	(revision 3837)
+++ trunk/src/org/openstreetmap/josm/actions/ViewportFollowToggleAction.java	(revision 3837)
@@ -0,0 +1,65 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.ButtonModel;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class ViewportFollowToggleAction extends JosmAction {
+    private final List<ButtonModel> buttonModels = new ArrayList<ButtonModel>();
+    private boolean selected;
+    public ViewportFollowToggleAction() {
+        super(
+                tr("Viewport Following"),
+                "viewport-follow",
+                tr("Enable/disable automatic moving of the map view to last placed node"),
+                Shortcut.registerShortcut("menu:view:viewportfollow", tr("Toggle Viewport Following"),KeyEvent.VK_F, Shortcut.GROUP_MENU, Shortcut.SHIFT_DEFAULT),
+                true /* register shortcut */
+        );
+        selected = false; 
+        notifySelectedState();
+    }
+
+    public void addButtonModel(ButtonModel model) {
+        if (model != null && !buttonModels.contains(model)) {
+            buttonModels.add(model);
+            model.setSelected(selected);
+        }
+    }
+
+    public void removeButtonModel(ButtonModel model) {
+        if (model != null && buttonModels.contains(model)) {
+            buttonModels.remove(model);
+        }
+    }
+
+    protected void notifySelectedState() {
+        for (ButtonModel model: buttonModels) {
+            if (model.isSelected() != selected) {
+                model.setSelected(selected);
+            }
+        }
+    }
+
+    protected void toggleSelectedState() {
+        selected = !selected;
+        Main.map.mapView.viewportFollowing = selected;
+        notifySelectedState();
+    }
+    public void actionPerformed(ActionEvent e) {
+        toggleSelectedState();
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        setEnabled(Main.map != null && Main.main.getEditLayer() != null);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/actions/mapmode/DrawAction.java	(revision 3837)
@@ -559,4 +559,9 @@
         getCurrentDataSet().setSelected(newSelection);
 
+        // "viewport following" mode for tracing long features 
+        // from aerial imagery or GPS tracks. 
+        if (n != null && Main.map.mapView.viewportFollowing) {
+            Main.map.mapView.smoothScrollTo(n.getEastNorth());
+        };
         computeHelperLine();
         removeHighlighting();
Index: trunk/src/org/openstreetmap/josm/data/coor/EastNorth.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/coor/EastNorth.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/data/coor/EastNorth.java	(revision 3837)
@@ -34,4 +34,8 @@
     public EastNorth getCenter(EastNorth en2) {
         return new EastNorth((this.x + en2.x)/2.0, (this.y + en2.y)/2.0);
+    }
+
+    public double distance(EastNorth en2) {
+        return Math.sqrt((this.x-en2.x)*(this.x-en2.x) + (this.y-en2.y)*(this.y-en2.y));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 3837)
@@ -74,4 +74,5 @@
 import org.openstreetmap.josm.actions.UploadAction;
 import org.openstreetmap.josm.actions.UploadSelectionAction;
+import org.openstreetmap.josm.actions.ViewportFollowToggleAction;
 import org.openstreetmap.josm.actions.WireframeToggleAction;
 import org.openstreetmap.josm.actions.ZoomInAction;
@@ -286,4 +287,11 @@
         }
 
+        // -- viewport follow toggle action
+        ViewportFollowToggleAction viewportFollowToggleAction = new ViewportFollowToggleAction();
+        final JCheckBoxMenuItem vft = new JCheckBoxMenuItem(viewportFollowToggleAction);
+        viewMenu.add(vft);
+        vft.setAccelerator(viewportFollowToggleAction.getShortcut().getKeyStroke());
+        viewportFollowToggleAction.addButtonModel(vft.getModel());
+
         // -- changeset manager toggle action
         ChangesetManagerToggleAction changesetManagerToggleAction = new ChangesetManagerToggleAction();
Index: trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 3837)
@@ -82,4 +82,6 @@
     }
 
+    public boolean viewportFollowing = false;
+
     /**
      * the layer listeners
Index: trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 3836)
+++ trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 3837)
@@ -23,4 +23,5 @@
 
 import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.Main;
@@ -356,4 +357,45 @@
     }
 
+    public void smoothScrollTo(LatLon newCenter) {
+        if (newCenter instanceof CachedLatLon) {
+            smoothScrollTo(((CachedLatLon)newCenter).getEastNorth());
+        } else {
+            smoothScrollTo(getProjection().latlon2eastNorth(newCenter));
+        }
+    }
+
+    /**
+     * Create a thread that moves the viewport to the given center in an 
+     * animated fashion.
+     */
+    public void smoothScrollTo(EastNorth newCenter) {
+        // fixme make these configurable.
+        final int fps = 20;     // animation frames per second
+        final int speed = 1500; // milliseconds for full-screen-width pan
+        if (!newCenter.equals(center)) {
+            final EastNorth oldCenter = center;
+            final double distance = newCenter.distance(oldCenter) / scale;
+            final double milliseconds = distance / getWidth() * speed;
+            final double frames = milliseconds * fps / 1000;
+            final EastNorth finalNewCenter = newCenter;
+
+            // we attempt to smooth-scroll at 10 fps, and use 2 seconds to scroll one 
+            // screen width.
+
+            new Thread(
+                new Runnable() {
+                    public void run() {
+                        for (int i=0; i<frames; i++)
+                        {
+                            // fixme - not use zoom history here
+                            zoomTo(oldCenter.interpolate(finalNewCenter, (double) (i+1) / (double) frames));
+                            try { Thread.sleep(1000 / fps); } catch (InterruptedException ex) { };
+                        }
+                    }
+                }
+            ).start();
+        }
+    }
+
     public void zoomToFactor(double x, double y, double factor) {
         double newScale = scale*factor;
