Index: trunk/src/org/openstreetmap/josm/gui/download/OsmMapControl.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/OsmMapControl.java	(revision 1601)
+++ trunk/src/org/openstreetmap/josm/gui/download/OsmMapControl.java	(revision 1602)
@@ -4,4 +4,6 @@
 
 import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
@@ -9,5 +11,10 @@
 import java.awt.event.MouseListener;
 import java.awt.event.MouseMotionListener;
-
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
 import javax.swing.JComponent;
 import javax.swing.JPanel;
@@ -24,4 +31,19 @@
 public class OsmMapControl extends MouseAdapter implements MouseMotionListener, MouseListener {
 
+    /** A Timer for smoothly moving the map area */
+    private static final Timer timer = new Timer(true);
+
+    /** Does the moving */
+    private MoveTask moveTask = new MoveTask();
+
+    /** How often to do the moving (milliseconds) */
+    private static long timerInterval = 20;
+
+    /** The maximum speed (pixels per timer interval) */
+    private static final double MAX_SPEED = 20;
+
+    /** The speed increase per timer interval when a cursor button is clicked */
+    private static final double ACCELERATION = 0.10;
+
     // start and end point of selection rectangle
     private Point iStartSelectionPoint;
@@ -43,17 +65,47 @@
 
         String[] n = { ",", ".", "up", "right", "down", "left" };
-        int[] k =
-                { KeyEvent.VK_COMMA, KeyEvent.VK_PERIOD, KeyEvent.VK_UP, KeyEvent.VK_RIGHT,
-                        KeyEvent.VK_DOWN, KeyEvent.VK_LEFT };
+        int[] k = { KeyEvent.VK_COMMA, KeyEvent.VK_PERIOD, KeyEvent.VK_UP, KeyEvent.VK_RIGHT, KeyEvent.VK_DOWN,
+                KeyEvent.VK_LEFT };
 
         if (contentPane != null) {
             for (int i = 0; i < n.length; ++i) {
                 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                        KeyStroke.getKeyStroke(k[i], KeyEvent.CTRL_DOWN_MASK),
-                        "MapMover.Zoomer." + n[i]);
+                        KeyStroke.getKeyStroke(k[i], KeyEvent.CTRL_DOWN_MASK), "MapMover.Zoomer." + n[i]);
             }
         }
         iSizeButton = sizeButton;
         iSourceButton = sourceButton;
+
+        InputMap inputMap = navComp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+        ActionMap actionMap = navComp.getActionMap();
+
+        // map moving
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "MOVE_RIGHT");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "MOVE_LEFT");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "MOVE_UP");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "MOVE_DOWN");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "STOP_MOVE_HORIZONTALLY");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "STOP_MOVE_HORIZONTALLY");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "STOP_MOVE_VERTICALLY");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "STOP_MOVE_VERTICALLY");
+
+        // zooming. To avoid confusion about which modifier key to use,
+        // we just add all keys left of the space bar
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK, false), "ZOOM_IN");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.META_DOWN_MASK, false), "ZOOM_IN");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK, false), "ZOOM_IN");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK, false), "ZOOM_OUT");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.META_DOWN_MASK, false), "ZOOM_OUT");
+        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK, false), "ZOOM_OUT");
+
+        // action mapping
+        actionMap.put("MOVE_RIGHT", new MoveXAction(1));
+        actionMap.put("MOVE_LEFT", new MoveXAction(-1));
+        actionMap.put("MOVE_UP", new MoveYAction(-1));
+        actionMap.put("MOVE_DOWN", new MoveYAction(1));
+        actionMap.put("STOP_MOVE_HORIZONTALLY", new MoveXAction(0));
+        actionMap.put("STOP_MOVE_VERTICALLY", new MoveYAction(0));
+        actionMap.put("ZOOM_IN", new ZoomInAction());
+        actionMap.put("ZOOM_OUT", new ZoomOutAction());
     }
 
@@ -70,10 +122,10 @@
             }
         }
-        
-    }
-
-    public void mouseDragged(MouseEvent e) {        
-        if((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK){
-            if (iStartSelectionPoint != null) {             
+
+    }
+
+    public void mouseDragged(MouseEvent e) {
+        if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
+            if (iStartSelectionPoint != null) {
                 iEndSelectionPoint = e.getPoint();
                 iSlippyMapChooser.setSelection(iStartSelectionPoint, iEndSelectionPoint);
@@ -89,19 +141,18 @@
     public void mouseReleased(MouseEvent e) {
         if (e.getButton() == MouseEvent.BUTTON1) {
-            
+
             int sourceButton = iSourceButton.hit(e.getPoint());
-            
+
             if (iSizeButton.hit(e.getPoint())) {
                 iSizeButton.toggle();
                 iSlippyMapChooser.resizeSlippyMap();
-            }
-            else if(sourceButton == SourceButton.HIDE_OR_SHOW) {
+            } else if (sourceButton == SourceButton.HIDE_OR_SHOW) {
                 iSourceButton.toggle();
                 iSlippyMapChooser.repaint();
-                
-            }else if(sourceButton == SourceButton.MAPNIK || sourceButton == SourceButton.OSMARENDER || sourceButton == SourceButton.CYCLEMAP) {
+
+            } else if (sourceButton == SourceButton.MAPNIK || sourceButton == SourceButton.OSMARENDER
+                    || sourceButton == SourceButton.CYCLEMAP) {
                 iSlippyMapChooser.toggleMapSource(sourceButton);
-            }
-            else {
+            } else {
                 if (e.getClickCount() == 1) {
                     iSlippyMapChooser.setSelection(iStartSelectionPoint, e.getPoint());
@@ -112,5 +163,5 @@
                 }
             }
-            
+
         }
     }
@@ -119,3 +170,137 @@
     }
 
+    private class MoveXAction extends AbstractAction {
+
+        int direction;
+
+        public MoveXAction(int direction) {
+            this.direction = direction;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            moveTask.setDirectionX(direction);
+        }
+    }
+
+    private class MoveYAction extends AbstractAction {
+
+        int direction;
+
+        public MoveYAction(int direction) {
+            this.direction = direction;
+        }
+
+        public void actionPerformed(ActionEvent e) {
+            moveTask.setDirectionY(direction);
+        }
+    }
+
+    /** Moves the map depending on which cursor keys are pressed (or not) */
+    private class MoveTask extends TimerTask {
+        /** The current x speed (pixels per timer interval) */
+        private double speedX = 1;
+
+        /** The current y speed (pixels per timer interval) */
+        private double speedY = 1;
+
+        /** The horizontal direction of movement, -1:left, 0:stop, 1:right */
+        private int directionX = 0;
+
+        /** The vertical direction of movement, -1:up, 0:stop, 1:down */
+        private int directionY = 0;
+
+        /**
+         * Indicated if <code>moveTask</code> is currently enabled (periodically
+         * executed via timer) or disabled
+         */
+        protected boolean scheduled = false;
+
+        protected void setDirectionX(int directionX) {
+            this.directionX = directionX;
+            updateScheduleStatus();
+        }
+
+        protected void setDirectionY(int directionY) {
+            this.directionY = directionY;
+            updateScheduleStatus();
+        }
+
+        private void updateScheduleStatus() {
+            boolean newMoveTaskState = !(directionX == 0 && directionY == 0);
+
+            if (newMoveTaskState != scheduled) {
+                scheduled = newMoveTaskState;
+                if (newMoveTaskState)
+                    timer.schedule(this, 0, timerInterval);
+                else {
+                    // We have to create a new instance because rescheduling a
+                    // once canceled TimerTask is not possible
+                    moveTask = new MoveTask();
+                    cancel(); // Stop this TimerTask
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            // update the x speed
+            switch (directionX) {
+            case -1:
+                if (speedX > -1)
+                    speedX = -1;
+                if (speedX > -1 * MAX_SPEED)
+                    speedX -= ACCELERATION;
+                break;
+            case 0:
+                speedX = 0;
+                break;
+            case 1:
+                if (speedX < 1)
+                    speedX = 1;
+                if (speedX < MAX_SPEED)
+                    speedX += ACCELERATION;
+                break;
+            }
+
+            // update the y speed
+            switch (directionY) {
+            case -1:
+                if (speedY > -1)
+                    speedY = -1;
+                if (speedY > -1 * MAX_SPEED)
+                    speedY -= ACCELERATION;
+                break;
+            case 0:
+                speedY = 0;
+                break;
+            case 1:
+                if (speedY < 1)
+                    speedY = 1;
+                if (speedY < MAX_SPEED)
+                    speedY += ACCELERATION;
+                break;
+            }
+
+            // move the map
+            int moveX = (int) Math.floor(speedX);
+            int moveY = (int) Math.floor(speedY);
+            if (moveX != 0 || moveY != 0)
+                iSlippyMapChooser.moveMap(moveX, moveY);
+        }
+    }
+
+    private class ZoomInAction extends AbstractAction {
+
+        public void actionPerformed(ActionEvent e) {
+            iSlippyMapChooser.zoomIn();
+        }
+    }
+
+    private class ZoomOutAction extends AbstractAction {
+
+        public void actionPerformed(ActionEvent e) {
+            iSlippyMapChooser.zoomOut();
+        }
+    }
+
 }
Index: trunk/src/org/openstreetmap/josm/gui/download/SlippyMapChooser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/SlippyMapChooser.java	(revision 1601)
+++ trunk/src/org/openstreetmap/josm/gui/download/SlippyMapChooser.java	(revision 1602)
@@ -105,6 +105,7 @@
         slipyyMapTabPanel.setLayout(new BorderLayout());
         slipyyMapTabPanel.add(this, BorderLayout.CENTER);
-        slipyyMapTabPanel.add(new JLabel((tr("Zoom: Mousewheel or double click.   "
-                + "Move map: Hold right mousebutton and move mouse.   Select: Click."))), BorderLayout.SOUTH);
+        String labelText = "<b>Zoom:</b> Mousewheel, double click or Ctrl + Up/Down "
+                + "<b>Move map:</b> Hold right mousebutton and move mouse or use cursor keys. <b>Select:</b> Click.";
+        slipyyMapTabPanel.add(new JLabel("<html>" + tr(labelText) + "</html>"), BorderLayout.SOUTH);
         iGui.tabpane.add(slipyyMapTabPanel, tr("Slippy map"));
         new OsmMapControl(this, slipyyMapTabPanel, iSizeButton, iSourceButton);
