Index: src/org/openstreetmap/josm/actions/SelectionAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/SelectionAction.java	(revision 3)
+++ src/org/openstreetmap/josm/actions/SelectionAction.java	(revision 3)
@@ -0,0 +1,127 @@
+package org.openstreetmap.josm.actions;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import org.openstreetmap.josm.actions.SelectionManager.SelectionEnded;
+import org.openstreetmap.josm.data.osm.LineSegment;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.Track;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.MapView;
+
+/**
+ * This MapMode enables the user to easy make a selection of different objects.
+ * 
+ * The selected objects are drawn in a different style.
+ * 
+ * Holding and dragging the left mouse button draws an selection rectangle. 
+ * When releasing the left mouse button, all objects within the rectangle get 
+ * selected. 
+ * 
+ * When releasing the left mouse button while the right mouse button pressed,
+ * nothing happens (the selection rectangle will be cleared, however).
+ *
+ * When releasing the mouse button and one of the following keys was hold:
+ *
+ * If Alt key was hold, select all objects that are touched by the 
+ * selection rectangle. If the Alt key was not hold, select only those objects 
+ * completly within (e.g. for tracks mean: only if all nodes of the track are 
+ * within).  
+ *
+ * If Shift key was hold, the objects are added to the current selection. If
+ * Shift key wasn't hold, the current selection get replaced.
+ * 
+ * If Ctrl key was hold, remove all objects under the current rectangle from
+ * the active selection (if there were any). Nothing is added to the current
+ * selection.
+ *
+ * Alt can be combined with Ctrl or Shift. Ctrl and Shift cannot be combined.
+ * If both are pressed, nothing happens when releasing the mouse button.
+ *
+ * @author imi
+ */
+public class SelectionAction extends MapMode implements SelectionEnded {
+
+	/**
+	 * Shortcut for the MapView.
+	 */
+	private MapView mv;
+	/**
+	 * The SelectionManager that manages the selection rectangle.
+	 */
+	private SelectionManager selectionManager;
+
+	/**
+	 * Create a new SelectionAction in the given frame.
+	 * @param mapFrame
+	 */
+	public SelectionAction(MapFrame mapFrame) {
+		super("Selection", "selection", KeyEvent.VK_S, mapFrame);
+		this.mv = mapFrame.mapView;
+		this.selectionManager = new SelectionManager(this, false, mv);
+	}
+
+	@Override
+	public void registerListener(MapView mapView) {
+		selectionManager.register(mapView);
+	}
+
+	@Override
+	public void unregisterListener(MapView mapView) {
+		selectionManager.unregister(mapView);
+	}
+
+
+	/**
+	 * Check the state of the keys and buttons and set the selection accordingly.
+	 */
+	public void selectionEnded(Rectangle r, int modifiers) {
+		boolean shift = (modifiers & MouseEvent.SHIFT_DOWN_MASK) != 0;
+		boolean alt = (modifiers & MouseEvent.ALT_DOWN_MASK) != 0;
+		boolean ctrl = (modifiers & MouseEvent.CTRL_DOWN_MASK) != 0;
+		if (shift && ctrl)
+			return; // not allowed together
+
+		if (!ctrl && !shift) {
+			// remove the old selection. The new selection will replace the old.
+			mv.dataSet.clearSelection();
+		}
+
+		// now set the selection to this value
+		boolean selection = !ctrl;
+
+		// nodes
+		for (Node n : mv.dataSet.allNodes) {
+			Point sp = mv.getScreenPoint(n.coor);
+			if (r.contains(sp))
+				n.selected = selection;
+		}
+		
+		// tracks
+		for (Track t : mv.dataSet.tracks) {
+			boolean wholeTrackSelected = t.segments.size() > 0;
+			for (LineSegment ls : t.segments) {
+				if (alt) {
+					Point p1 = mv.getScreenPoint(ls.start.coor);
+					Point p2 = mv.getScreenPoint(ls.end.coor);
+					if (r.intersectsLine(p1.x, p1.y, p2.x, p2.y))
+						ls.selected = selection;
+					else
+						wholeTrackSelected = false;
+				} else {
+					if (r.contains(mv.getScreenPoint(ls.start.coor)) && 
+							r.contains(mv.getScreenPoint(ls.end.coor)))
+						ls.selected = selection;
+					else
+						wholeTrackSelected = false;
+				}
+			}
+			if (wholeTrackSelected)
+				t.selected = true;
+		}
+		mv.repaint();
+	}
+}
Index: src/org/openstreetmap/josm/actions/SelectionManager.java
===================================================================
--- src/org/openstreetmap/josm/actions/SelectionManager.java	(revision 3)
+++ src/org/openstreetmap/josm/actions/SelectionManager.java	(revision 3)
@@ -0,0 +1,231 @@
+package org.openstreetmap.josm.actions;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+/**
+ * Manages the selection of a rectangle. Listening to left and right mouse button
+ * presses and to mouse motions and draw the rectangle accordingly.
+ * 
+ * Left mouse button selects a rectangle from the press until release. Pressing
+ * right mouse button while left is still pressed enable the rectangle to move
+ * around. Releasing the left button fires an action event to the listener given
+ * at constructor, except if the right is still pressed, which just remove the
+ * selection rectangle and does nothing.
+ * 
+ * The point where the left mouse button was pressed and the current mouse 
+ * position are two opposite corners of the selection rectangle.
+ * 
+ * It is possible to specify an aspect ratio (width per height) which the 
+ * selection rectangle always must have. In this case, the selection rectangle
+ * will be the largest window with this aspect ratio, where the position the left
+ * mouse button was pressed and the corner of the current mouse position are at 
+ * opposite sites (the mouse position corner is the corner nearest to the mouse
+ * cursor). 
+ * 
+ * When the left mouse button was released, an ActionEvent is send to the 
+ * ActionListener given at constructor. The source of this event is this manager.
+ * 
+ * @author imi
+ */
+public class SelectionManager implements MouseListener, MouseMotionListener {
+
+	/**
+	 * This is the interface that an user of SelectionManager has to implement
+	 * to get informed when a selection closes.
+	 * @author imi
+	 */
+	public interface SelectionEnded {
+		/**
+		 * Called, when the left mouse button was released.
+		 * @param r The rectangle, that is currently the selection.
+		 * @param modifiers The modifiers returned from the MouseEvent when
+		 * 		the left mouse button was released. 
+		 * @see InputEvent#getModifiersEx()
+		 */
+		public void selectionEnded(Rectangle r, int modifiers);
+	}
+	/**
+	 * The listener that receives the events after left mouse button is released.
+	 */
+	private final SelectionEnded selectionEndedListener;
+	/**
+	 * Position of the map when the mouse button was pressed.
+	 * If this is not <code>null</code>, a rectangle is drawn on screen. 
+	 */
+	private Point mousePosStart;
+	/**
+	 * Position of the map when the selection rectangle was last drawn.
+	 */
+	private Point mousePos;
+	/**
+	 * The component, the selection rectangle is drawn onto.
+	 */
+	private final Component drawComponent;
+	/**
+	 * Whether the selection rectangle must obtain the aspect ratio of the 
+	 * drawComponent.
+	 */
+	private boolean aspectRatio;
+
+	/**
+	 * Create a new SelectionManager.
+	 *
+	 * @param actionListener The action listener that receives the event when
+	 * 		the left button is released.
+	 * @param aspectRatio If true, the selection window must obtain the aspect
+	 * 		ratio of the drawComponent.
+	 * @param drawComponent The component, the rectangle is drawn onto.
+	 */
+	public SelectionManager(SelectionEnded selectionEndedListener, boolean aspectRatio, Component drawComponent) {
+		this.selectionEndedListener = selectionEndedListener;
+		this.aspectRatio = aspectRatio;
+		this.drawComponent = drawComponent;
+	}
+	
+	/**
+	 * Register itself at the given event source.
+	 * @param eventSource The emitter of the mouse events.
+	 */
+	public void register(Component eventSource) {
+		eventSource.addMouseListener(this);
+		eventSource.addMouseMotionListener(this);
+	}
+	/**
+	 * Unregister itself from the given event source. If a selection rectangle is
+	 * shown, hide it first.
+	 *
+	 * @param eventSource The emitter of the mouse events.
+	 */
+	public void unregister(Component eventSource) {
+		eventSource.removeMouseListener(this);
+		eventSource.removeMouseMotionListener(this);
+	}
+
+	/**
+	 * If the correct button, start the "drawing rectangle" mode
+	 */
+	public void mousePressed(MouseEvent e) {
+		if (e.getButton() == MouseEvent.BUTTON1) {
+			mousePosStart = e.getPoint();
+			mousePos = e.getPoint();
+			paintRect();
+		}
+	}
+
+	/**
+	 * If the correct button is hold, draw the rectangle.
+	 */
+	public void mouseDragged(MouseEvent e) {
+		int buttonPressed = e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK); 
+
+		if (buttonPressed != 0) {
+			if (mousePosStart == null) {
+				mousePosStart = e.getPoint();
+				mousePos = e.getPoint();
+				paintRect();
+			}
+			paintRect();
+		}
+		
+		if (buttonPressed == MouseEvent.BUTTON1_DOWN_MASK) {
+			mousePos = e.getPoint();
+			paintRect();
+		} else if (buttonPressed == (MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) {
+			mousePosStart.x += e.getX()-mousePos.x;
+			mousePosStart.y += e.getY()-mousePos.y;
+			mousePos = e.getPoint();
+			paintRect();
+		}
+	}
+
+	/**
+	 * Check the state of the keys and buttons and set the selection accordingly.
+	 */
+	public void mouseReleased(MouseEvent e) {
+		if (e.getButton() != MouseEvent.BUTTON1)
+			return;
+		// disable the selection rect
+		paintRect();
+		Rectangle r = getSelectionRectangle();
+		mousePosStart = null;
+		mousePos = null;
+		if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0)
+			selectionEndedListener.selectionEnded(r, e.getModifiersEx());
+	}
+
+
+	/**
+	 * Draw a selection rectangle on screen. If already a rectangle is drawn,
+	 * it is removed instead.
+	 */
+	private void paintRect() {
+		Graphics g = drawComponent.getGraphics();
+		g.setColor(Color.BLACK);
+		g.setXORMode(Color.WHITE);
+
+		Rectangle r = getSelectionRectangle();
+		g.drawRect(r.x,r.y,r.width,r.height);
+	}
+
+	/**
+	 * Calculate and return the current selection rectangle
+	 * @return A rectangle that spans from mousePos to mouseStartPos
+	 */
+	private Rectangle getSelectionRectangle() {
+		int x = mousePosStart.x;
+		int y = mousePosStart.y;
+		int w = mousePos.x - mousePosStart.x;
+		int h = mousePos.y - mousePosStart.y;
+		if (w < 0) {
+			x += w;
+			w = -w;
+		}
+		if (h < 0) {
+			y += h;
+			h = -h;
+		}
+		
+		if (aspectRatio) {
+			// keep the aspect ration by shrinking the rectangle
+			double aspectRatio = (double)drawComponent.getWidth()/drawComponent.getHeight();
+			if ((double)w/h > aspectRatio) {
+				int neww = (int)(h*aspectRatio);
+				if (mousePos.x < mousePosStart.x)
+					x += w-neww;
+				w = neww;
+			} else {
+				int newh = (int)(w/aspectRatio);
+				if (mousePos.y < mousePosStart.y)
+					y += h-newh;
+				h = newh;
+			}
+		}
+		
+		return new Rectangle(x,y,w,h);
+	}
+	
+	/**
+	 * Does nothing. Only to satisfy MouseListener
+	 */
+	public void mouseClicked(MouseEvent e) {}
+	/**
+	 * Does nothing. Only to satisfy MouseListener
+	 */
+	public void mouseEntered(MouseEvent e) {}
+	/**
+	 * Does nothing. Only to satisfy MouseListener
+	 */
+	public void mouseExited(MouseEvent e) {}
+	/**
+	 * Does nothing. Only to satisfy MouseMotionListener
+	 */
+	public void mouseMoved(MouseEvent e) {}
+}
Index: src/org/openstreetmap/josm/gui/MapMover.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapMover.java	(revision 3)
+++ src/org/openstreetmap/josm/gui/MapMover.java	(revision 3)
@@ -0,0 +1,116 @@
+package org.openstreetmap.josm.gui;
+
+import java.awt.Cursor;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+
+import org.openstreetmap.josm.data.GeoPoint;
+
+/**
+ * Enables moving of the map by holding down the right mouse button and drag
+ * the mouse. Also, enables zooming by the mouse wheel.
+ *
+ * @author imi
+ */
+class MapMover extends MouseAdapter implements MouseMotionListener, MouseWheelListener {
+
+	/**
+	 * The point in the map that was the under the mouse point
+	 * when moving around started.
+	 */
+	private GeoPoint mousePosMove;
+	/**
+	 * The map to move around.
+	 */
+	private final MapView mv;
+	/**
+	 * The old cursor when we changed it to movement cursor.
+	 */
+	private Cursor oldCursor;
+
+	/**
+	 * Create a new MapMover
+	 * @param mapView The map that should be moved.
+	 */
+	public MapMover(MapView mapView) {
+		this.mv = mapView;
+		mv.addMouseListener(this);
+		mv.addMouseMotionListener(this);
+		mv.addMouseWheelListener(this);
+	}
+	
+	/**
+	 * If the right (and only the right) mouse button is pressed, move the map
+	 */
+	public void mouseDragged(MouseEvent e) {
+		int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
+		if ((e.getModifiersEx() & (MouseEvent.BUTTON3_DOWN_MASK | offMask)) == MouseEvent.BUTTON3_DOWN_MASK) {
+			if (mousePosMove == null)
+				startMovement(e);
+			GeoPoint center = mv.getCenter();
+			GeoPoint mouseCenter = mv.getPoint(e.getX(), e.getY(), false);
+			GeoPoint p = new GeoPoint();
+			p.x = mousePosMove.x + center.x - mouseCenter.x;  
+			p.y = mousePosMove.y + center.y - mouseCenter.y;  
+			mv.zoomTo(p, mv.getScale());
+		} else
+			endMovement();
+	}
+
+	/**
+	 * Start the movement, if it was the 3rd button (right button).
+	 */
+	public void mousePressed(MouseEvent e) {
+		int offMask = MouseEvent.BUTTON1_DOWN_MASK | MouseEvent.BUTTON2_DOWN_MASK;
+		if (e.getButton() == MouseEvent.BUTTON3 && (e.getModifiersEx() & offMask) == 0)
+			startMovement(e);
+	}
+
+	/**
+	 * Change the cursor back to it's pre-move cursor.
+	 */
+	public void mouseReleased(MouseEvent e) {
+		if (e.getButton() == MouseEvent.BUTTON3)
+			endMovement();
+	}
+
+	/**
+	 * Start movement by setting a new cursor and remember the current mouse
+	 * position.
+	 * @param e The mouse event that leat to the movement start.
+	 */
+	private void startMovement(MouseEvent e) {
+		mousePosMove = mv.getPoint(e.getX(), e.getY(), false);
+		oldCursor = mv.getCursor();
+		mv.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
+	}
+	
+	/**
+	 * End the movement. Setting back the cursor and clear the movement variables
+	 */
+	private void endMovement() {
+		if (oldCursor != null)
+			mv.setCursor(oldCursor);
+		else
+			mv.setCursor(Cursor.getDefaultCursor());
+		mousePosMove = null;
+		oldCursor = null;
+	}
+
+	/**
+	 * Zoom the map by 1/5th of current zoom per wheel-delta.
+	 * @param e The wheel event.
+	 */
+	public void mouseWheelMoved(MouseWheelEvent e) {
+		double zoom = Math.max(0.1, 1 + e.getWheelRotation()/5.0);
+		mv.zoomTo(mv.getCenter(), mv.getScale()*zoom);
+	}
+
+	/**
+	 * Does nothing. Only to satisfy MouseMotionListener
+	 */
+	public void mouseMoved(MouseEvent e) {}
+}
