Index: src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java	(revision 3566)
+++ src/org/openstreetmap/josm/actions/mapmode/ZoomAction.java	(working copy)
@@ -5,6 +5,7 @@
 
 import java.awt.Rectangle;
 import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.MapFrame;
@@ -49,7 +50,7 @@
     /**
      * Zoom to the rectangle on the map.
      */
-    public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
+    public void selectionEnded(Rectangle r, MouseEvent e) {
         if (r.width >= 3 && r.height >= 3 && Main.isDisplayingMapView()) {
             MapView mv = Main.map.mapView;
             mv.zoomToFactor(mv.getEastNorth(r.x+r.width/2, r.y+r.height/2), r.getWidth()/mv.getWidth());
Index: src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 3566)
+++ src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(working copy)
@@ -11,14 +11,11 @@
 import java.awt.event.InputEvent;
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseEvent;
-import java.util.ArrayList;
+import java.awt.geom.Point2D;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.Set;
-import java.util.TreeSet;
 
 import javax.swing.JOptionPane;
 
@@ -46,6 +43,7 @@
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.PlatformHookOsx;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -64,21 +62,30 @@
 
     enum Mode { move, rotate, select }
     private Mode mode = null;
-    private long mouseDownTime = 0;
-    private boolean didMove = false;
+    private SelectionManager selectionManager;
+
     private boolean cancelDrawMode = false;
-    private Node virtualNode = null;
-    private Collection<WaySegment> virtualWays = new ArrayList<WaySegment>();
+    private boolean didMouseDrag = false;
+
+    /**
+     * The component this SelectAction is associated with.
+     */
+    private final MapView mv;
 
     /**
      * The old cursor before the user pressed the mouse button.
      */
     private Cursor oldCursor;
+
     /**
      * The position of the mouse before the user moves a node.
      */
     private Point mousePos;
-    private SelectionManager selectionManager;
+
+    /**
+     * The time of the user mouse down event.
+     */
+    private long mouseDownTime = 0;
 
     /**
      * The time which needs to pass between click and release before something
@@ -92,6 +99,7 @@
      */
     private int initialMoveThreshold;
     private boolean initialMoveThresholdExceeded = false;
+
     /**
      * Create a new SelectAction
      * @param mapFrame The MapFrame this action belongs to.
@@ -101,8 +109,9 @@
                 Shortcut.registerShortcut("mapmode:select", tr("Mode: {0}", tr("Select")), KeyEvent.VK_S, Shortcut.GROUP_EDIT),
                 mapFrame,
                 getCursor("normal", "selection", Cursor.DEFAULT_CURSOR));
+        mv = mapFrame.mapView;
         putValue("help", "Action/Move/Move");
-        selectionManager = new SelectionManager(this, false, mapFrame.mapView);
+        selectionManager = new SelectionManager(this, false, mv);
         initialMoveDelay = Main.pref.getInteger("edit.initial-move-delay",200);
         initialMoveThreshold = Main.pref.getInteger("edit.initial-move-threshold",5);
     }
@@ -117,32 +126,32 @@
 
     private void setCursor(Cursor c) {
         if (oldCursor == null) {
-            oldCursor = Main.map.mapView.getCursor();
-            Main.map.mapView.setCursor(c);
+            oldCursor = mv.getCursor();
+            mv.setCursor(c);
         }
     }
 
     private void restoreCursor() {
         if (oldCursor != null) {
-            Main.map.mapView.setCursor(oldCursor);
+            mv.setCursor(oldCursor);
             oldCursor = null;
         }
     }
 
     @Override public void enterMode() {
         super.enterMode();
-        Main.map.mapView.addMouseListener(this);
-        Main.map.mapView.addMouseMotionListener(this);
-        Main.map.mapView.setVirtualNodesEnabled(
+        mv.addMouseListener(this);
+        mv.addMouseMotionListener(this);
+        mv.setVirtualNodesEnabled(
                 Main.pref.getInteger("mappaint.node.virtual-size", 8) != 0);
     }
 
     @Override public void exitMode() {
         super.exitMode();
-        selectionManager.unregister(Main.map.mapView);
-        Main.map.mapView.removeMouseListener(this);
-        Main.map.mapView.removeMouseMotionListener(this);
-        Main.map.mapView.setVirtualNodesEnabled(false);
+        selectionManager.unregister(mv);
+        mv.removeMouseListener(this);
+        mv.removeMouseMotionListener(this);
+        mv.setVirtualNodesEnabled(false);
     }
 
     /**
@@ -151,7 +160,7 @@
      * mouse (which will become selected).
      */
     @Override public void mouseDragged(MouseEvent e) {
-        if(!Main.map.mapView.isActiveLayerVisible())
+        if(!mv.isActiveLayerVisible())
             return;
 
         cancelDrawMode = true;
@@ -168,11 +177,6 @@
             setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
         }
 
-        if (mousePos == null) {
-            mousePos = e.getPoint();
-            return;
-        }
-
         if (!initialMoveThresholdExceeded) {
             int dxp = mousePos.x - e.getX();
             int dyp = mousePos.y - e.getY();
@@ -181,8 +185,8 @@
             initialMoveThresholdExceeded = true;
         }
 
-        EastNorth mouseEN = Main.map.mapView.getEastNorth(e.getX(), e.getY());
-        EastNorth mouseStartEN = Main.map.mapView.getEastNorth(mousePos.x, mousePos.y);
+        EastNorth mouseEN = mv.getEastNorth(e.getX(), e.getY());
+        EastNorth mouseStartEN = mv.getEastNorth(mousePos.x, mousePos.y);
         double dx = mouseEN.east() - mouseStartEN.east();
         double dy = mouseEN.north() - mouseStartEN.north();
         if (dx == 0 && dy == 0)
@@ -254,10 +258,10 @@
             }
         }
 
-        Main.map.mapView.repaint();
+        mv.repaint();
         mousePos = e.getPoint();
 
-        didMove = true;
+        didMouseDrag = true;
     }
 
     @Override public void mouseMoved(MouseEvent e) {
@@ -268,86 +272,127 @@
         }
     }
 
-    private Collection<OsmPrimitive> getNearestCollectionVirtual(Point p) {
-        int snapDistance = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8);
-        int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
-        snapDistance *= snapDistance;
-
-        MapView c = Main.map.mapView;
-        Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
+    private Node virtualNode = null;
+    private Collection<WaySegment> virtualWays = new LinkedList<WaySegment>();
 
-        // take nearest node
-        OsmPrimitive osm = c.getNearestNode(p, OsmPrimitive.isSelectablePredicate);
+    /**
+     * Calculate a virtual node if there is enough visual space to draw a crosshair
+     * node and the middle of a way segment is clicked.  If the user drags the
+     * crosshair node, it will be added to all ways in <code>virtualWays</code>.
+     * 
+     * @param e contains the point clicked
+     * @return whether <code>virtualNode</code> and <code>virtualWays</code> were setup.
+     */
+    private boolean setupVirtual(MouseEvent e) {
+        if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) {
+            int virtualSnapDistSq = Main.pref.getInteger("mappaint.node.virtual-snap-distance", 8);
+            int virtualSpace = Main.pref.getInteger("mappaint.node.virtual-space", 70);
+            virtualSnapDistSq *= virtualSnapDistSq;
 
-        if (osm != null) {
-            for (Node n : c.getNearestNodes(p, OsmPrimitive.isSelectablePredicate)) {
-                if (sel.contains(n)) {
-                    // take nearest selected node
-                    osm = n;
-                    break;
-                }
-            }
-        } else {
-            Node virtualWayNode = null;
+            Collection<WaySegment> selVirtualWays = new LinkedList<WaySegment>();
+            Pair<Node, Node> vnp = null, wnp = new Pair<Node, Node>(null, null);
+            Point p = e.getPoint();
             Way w = null;
 
-            Collection<WaySegment> virtualWaysInSel = new ArrayList<WaySegment>();
-            Collection<WaySegment> wss = c.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate);
-            for(WaySegment nearestWS : wss) {
-                if (nearestWS == null) {
-                    continue;
-                }
-
-                w = nearestWS.way;
-                if (osm == null && sel.contains(w)) {
-                    // take nearest selected way
-                    osm = w;
-                }
+            for(WaySegment ws : mv.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)) {
+                w = ws.way;
 
-                if (Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0) {
-                    Point p1 = c.getPoint(w.getNode(nearestWS.lowerIndex));
-                    Point p2 = c.getPoint(w.getNode(nearestWS.lowerIndex+1));
-                    if(SimplePaintVisitor.isLargeSegment(p1, p2, virtualSpace))
+                Point2D p1 = mv.getPoint2D(wnp.a = w.getNode(ws.lowerIndex));
+                Point2D p2 = mv.getPoint2D(wnp.b = w.getNode(ws.lowerIndex+1));
+                if(SimplePaintVisitor.isLargeSegment(p1, p2, virtualSpace))
+                {
+                    Point2D pc = new Point2D.Double((p1.getX()+p2.getX())/2, (p1.getY()+p2.getY())/2);
+                    if (p.distanceSq(pc) < virtualSnapDistSq)
                     {
-                        Point pc = new Point((p1.x+p2.x)/2, (p1.y+p2.y)/2);
-                        if (p.distanceSq(pc) < snapDistance)
-                        {
-                            // Check that only segments on top of each other get added to the
-                            // virtual ways list. Otherwise ways that coincidentally have their
-                            // virtual node at the same spot will be joined which is likely unwanted
-                            if(virtualWayNode != null) {
-                                if(!w.getNode(nearestWS.lowerIndex+1).equals(virtualWayNode)
-                                        && !w.getNode(nearestWS.lowerIndex).equals(virtualWayNode)) {
-                                    continue;
-                                }
-                            } else {
-                                virtualWayNode = w.getNode(nearestWS.lowerIndex+1);
-                            }
-
-                            (!sel.contains(w) ? virtualWays : virtualWaysInSel).add(nearestWS);
-                            if(virtualNode == null) {
-                                virtualNode = new Node(Main.map.mapView.getLatLon(pc.x, pc.y));
-                            }
+                        // Check that only segments on top of each other get added to the
+                        // virtual ways list. Otherwise ways that coincidentally have their
+                        // virtual node at the same spot will be joined which is likely unwanted
+                        Pair.sort(wnp);
+                        if (vnp == null) {
+                            vnp = new Pair<Node, Node>(wnp.a, wnp.b);
+                            virtualNode = new Node(mv.getLatLon(pc.getX(), pc.getY()));
+                        }
+                        if (vnp.equals(wnp)) {
+                            (w.isSelected() ? selVirtualWays : virtualWays).add(ws);
                         }
                     }
                 }
             }
 
-            if (virtualNode != null) {
-                // insert virtualNode into all segments if nothing was selected,
-                // else only into the (previously) selected segments
-                virtualWays = virtualWaysInSel.isEmpty() ? virtualWays : virtualWaysInSel;
+            if (!selVirtualWays.isEmpty()) {
+                virtualWays = selVirtualWays;
             }
+        }
 
-            if (osm == null && !wss.isEmpty()) {
-                // take nearest way
-                osm = wss.iterator().next().way;
+        return !virtualWays.isEmpty();
+    }
+
+    private Collection<OsmPrimitive> cycleList = Collections.emptyList();
+    private boolean cyclePrims = false;
+    private OsmPrimitive cycleStart = null;
+
+    /**
+     * 
+     * @param osm nearest primitive found by simple method
+     * @param e
+     * @return
+     */
+    private Collection<OsmPrimitive> cycleSetup(Collection<OsmPrimitive> single, MouseEvent e) {
+        OsmPrimitive osm = null;
+
+        if (single == null) {
+            single = MapView.asColl(
+                    mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, false));
+        }
+
+        if (!single.isEmpty()) {
+            boolean waitForMouseUp = Main.pref.getBoolean("mappaint.select.waits-for-mouse-up", false);
+            boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+            boolean alt = ((e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0
+                    || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false));
+
+            Point p = e.getPoint();
+            osm = single.iterator().next();
+
+            if (!alt) {
+                cycleList = MapView.asColl(osm);
+                if (waitForMouseUp) {
+                    // find a selected nearest node or way, not the true nearest..
+                    osm = mv.getNearestNodeOrWay(p, OsmPrimitive.isSelectablePredicate, true);
+                }
+            } else {
+                if (osm instanceof Node) {
+                    cycleList = new LinkedList<OsmPrimitive>(mv.getNearestNodes(p, OsmPrimitive.isSelectablePredicate));
+                } else if (osm instanceof Way) {
+                    cycleList = new LinkedList<OsmPrimitive>(mv.getNearestWays(p, OsmPrimitive.isSelectablePredicate));
+                }
+
+                if (!waitForMouseUp && cycleList.size()>1) {
+                    cyclePrims = false;
+
+                    OsmPrimitive old = osm;
+                    for (OsmPrimitive o : cycleList) {
+                        if (o.isSelected()) {
+                            cyclePrims = true;
+                            osm = o;
+                            break;
+                        }
+                    }
+
+                    // for cycle groups of 2, we can toggle to the true nearest
+                    // primitive on mouse presses, if it is not selected and if ctrl is not used
+                    // else, if rotation is possible, defer sel change to mouse release
+                    if (cycleList.size()==2) {
+                        if (!(old.equals(osm) || ctrl)) {
+                            cyclePrims = false;
+                            osm = old;
+                        }
+                    }
+                }
             }
         }
 
-        if (osm == null)
-            return Collections.emptySet();
-        return Collections.singleton(osm);
+        return MapView.asColl(osm);
     }
 
     /**
@@ -360,78 +405,72 @@
      * cursor to movement.
      */
     @Override public void mousePressed(MouseEvent e) {
-        if(!Main.map.mapView.isActiveLayerVisible())
+        debug("mousePressed: e.getPoint()=" + e.getPoint());
+
+        // return early
+        if(!mv.isActiveLayerVisible()
+                || !(Boolean)this.getValue("active")
+                || e.getButton() != MouseEvent.BUTTON1)
             return;
 
         // request focus in order to enable the expected keyboard shortcuts
-        Main.map.mapView.requestFocus();
+        mv.requestFocus();
 
-        cancelDrawMode = false;
-        if (! (Boolean)this.getValue("active")) return;
-        if (e.getButton() != MouseEvent.BUTTON1)
-            return;
         boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
         boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
 
         // We don't want to change to draw tool if the user tries to (de)select
         // stuff but accidentally clicks in an empty area when selection is empty
-        if(shift || ctrl) {
-            cancelDrawMode = true;
-        }
-
-        mouseDownTime = System.currentTimeMillis();
-        didMove = false;
+        cancelDrawMode = (shift || ctrl);
+        didMouseDrag = false;
         initialMoveThresholdExceeded = false;
+        mouseDownTime = System.currentTimeMillis();
+        mousePos = e.getPoint();
+
+        Collection<OsmPrimitive> c = MapView.asColl(
+                mv.getNearestNodeOrWay(e.getPoint(), OsmPrimitive.isSelectablePredicate, false));
 
-        Collection<OsmPrimitive> osmColl = getNearestCollectionVirtual(e.getPoint());
+        if (shift && ctrl) {
+            mode = Mode.rotate;
 
-        if (ctrl && shift) {
             if (getCurrentDataSet().getSelected().isEmpty()) {
-                selectPrims(osmColl, true, false, false, false);
+                getCurrentDataSet().setSelected(c);
             }
-            mode = Mode.rotate;
+
+            // Mode.select redraws when selectPrims is called
+            // Mode.move   redraws when mouseDragged is called
+            // Mode.rotate redraws here
             setCursor(ImageProvider.getCursor("rotate", null));
-        } else if (!osmColl.isEmpty()) {
-            // Don't replace the selection now if the user clicked on a
-            // selected object (this would break moving of selected groups).
-            // We'll do that later in mouseReleased if the user didn't try to
-            // move.
-            selectPrims(osmColl,
-                    shift || getCurrentDataSet().getSelected().containsAll(osmColl),
-                    ctrl, false, false);
+            mv.repaint();
+        } else if (!c.isEmpty()) {
             mode = Mode.move;
+
+            if (!cancelDrawMode && c.iterator().next() instanceof Way) {
+                setupVirtual(e);
+            }
+
+            selectPrims(cycleSetup(c, e), e, false, false);
         } else {
             mode = Mode.select;
-            oldCursor = Main.map.mapView.getCursor();
-            selectionManager.register(Main.map.mapView);
-            selectionManager.mousePressed(e);
-        }
 
-        if(mode != Mode.move || shift || ctrl) {
-            virtualNode = null;
-            virtualWays.clear();
+            oldCursor = mv.getCursor();
+            selectionManager.register(mv);
+            selectionManager.mousePressed(e);
         }
 
         updateStatusLine();
-        // Mode.select redraws when selectPrims is called
-        // Mode.move   redraws when mouseDragged is called
-        // Mode.rotate redraws here
-        if(mode == Mode.rotate) {
-            Main.map.mapView.repaint();
-        }
-
-        mousePos = e.getPoint();
     }
 
-    /**
-     * Restore the old mouse cursor.
-     */
-    @Override public void mouseReleased(MouseEvent e) {
-        if(!Main.map.mapView.isActiveLayerVisible())
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        debug("mouseReleased: e.getPoint()=" + e.getPoint());
+
+        if(!mv.isActiveLayerVisible())
             return;
 
+        restoreCursor();
         if (mode == Mode.select) {
-            selectionManager.unregister(Main.map.mapView);
+            selectionManager.unregister(mv);
 
             // Select Draw Tool if no selection has been made
             if(getCurrentDataSet().getSelected().size() == 0 && !cancelDrawMode) {
@@ -439,90 +478,59 @@
                 return;
             }
         }
-        restoreCursor();
 
         if (mode == Mode.move) {
-            boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
-            boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
-            boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_MASK)) != 0
-                            || Main.pref.getBoolean("selectaction.cycles.multiple.matches", false);
+            if (!didMouseDrag) {
+                // only built in move mode
+                virtualWays.clear();
+                virtualNode = null;
 
-            virtualWays.clear();
-            virtualNode = null;
+                // do nothing if the click was to short to be recognized as a drag,
+                // but the release position is farther than 10px away from the press position
+                if (mousePos.distanceSq(e.getPoint())<100) {
+                    selectPrims(cyclePrims(cycleList, e), e, true, false);
 
-            if (!didMove) {
-                Collection<OsmPrimitive> c = Main.map.mapView.getNearestCollection(e.getPoint(), OsmPrimitive.isSelectablePredicate);
-                if (!c.isEmpty() && alt) {
-                    if (c.iterator().next() instanceof Node) {
-                        // consider all nearest nodes
-                        c = new ArrayList<OsmPrimitive>(Main.map.mapView.getNearestNodes(e.getPoint(), OsmPrimitive.isSelectablePredicate));
-                    } else {
-                        // consider all nearest primitives (should be only ways at this point..)
-                        c = Main.map.mapView.getAllNearest(e.getPoint(), OsmPrimitive.isSelectablePredicate);
+                    // If the user double-clicked a node, change to draw mode
+                    Collection<OsmPrimitive> c = getCurrentDataSet().getSelected();
+                    if(e.getClickCount() >=2 && c.size() == 1 && c.iterator().next() instanceof Node) {
+                        // We need to do it like this as otherwise drawAction will see a double
+                        // click and switch back to SelectMode
+                        Main.worker.execute(new Runnable(){
+                            public void run() {
+                                Main.map.selectDrawTool(true);
+                            }
+                        });
+                        return;
                     }
                 }
-                selectPrims(c, shift, ctrl, true, false);
-
-                // If the user double-clicked a node, change to draw mode
-                c = getCurrentDataSet().getSelected();
-                if(e.getClickCount() >=2 && c.size() == 1 && c.iterator().next() instanceof Node) {
-                    // We need to do it like this as otherwise drawAction will see a double
-                    // click and switch back to SelectMode
-                    Main.worker.execute(new Runnable(){
-                        public void run() {
-                            Main.map.selectDrawTool(true);
-                        }
-                    });
-                    return;
-                }
             } else {
-                Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
-                Collection<OsmPrimitive> s = new TreeSet<OsmPrimitive>();
-                int max = Main.pref.getInteger("warn.move.maxelements", 20);
-                for (OsmPrimitive osm : selection)
-                {
-                    if(osm instanceof Node) {
-                        s.add(osm);
-                    } else if(osm instanceof Way)
-                    {
-                        s.add(osm);
-                        s.addAll(((Way)osm).getNodes());
+                int max = Main.pref.getInteger("warn.move.maxelements", 20), limit = max;
+                for (OsmPrimitive osm : getCurrentDataSet().getSelected()) {
+                    if (osm instanceof Way) {
+                        limit -= ((Way)osm).getNodes().size();
                     }
-                    if(s.size() > max)
-                    {
-                        ExtendedDialog ed = new ExtendedDialog(
-                                Main.parent,
-                                tr("Move elements"),
-                                new String[] {tr("Move them"), tr("Undo move")});
-                        ed.setButtonIcons(new String[] {"reorder.png", "cancel.png"});
-                        ed.setContent(tr("You moved more than {0} elements. "
-                                + "Moving a large number of elements is often an error.\n"
-                                + "Really move them?", max));
-                        ed.setCancelButton(2);
-                        ed.toggleEnable("movedManyElements");
-                        ed.showDialog();
-
-                        if(ed.getValue() != 1)
-                        {
-                            Main.main.undoRedo.undo();
-                        }
+                    if ((limit -= 1) < 0) {
                         break;
                     }
                 }
-                if (ctrl) {
-                    Collection<Node> affectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class);
-                    Collection<Node> nn = Main.map.mapView.getNearestNodes(e.getPoint(), affectedNodes, OsmPrimitive.isSelectablePredicate);
-                    if (nn != null) {
-                        Node targetNode = nn.iterator().next();
-                        Set<Node> nodesToMerge = new HashSet<Node>(affectedNodes);
-                        nodesToMerge.add(targetNode);
-                        if (!nodesToMerge.isEmpty()) {
-                            Command cmd = MergeNodesAction.mergeNodes(Main.main.getEditLayer(),nodesToMerge, targetNode);
-                            if(cmd != null) {
-                                Main.main.undoRedo.add(cmd);
-                            }
-                        }
+                if (limit < 0) {
+                    ExtendedDialog ed = new ExtendedDialog(
+                            Main.parent,
+                            tr("Move elements"),
+                            new String[] {tr("Move them"), tr("Undo move")});
+                    ed.setButtonIcons(new String[] {"reorder.png", "cancel.png"});
+                    ed.setContent(tr("You moved more than {0} elements. "
+                            + "Moving a large number of elements is often an error.\n"
+                            + "Really move them?", max));
+                    ed.setCancelButton(2);
+                    ed.toggleEnable("movedManyElements");
+                    ed.showDialog();
+
+                    if(ed.getValue() != 1) {
+                        Main.main.undoRedo.undo();
                     }
+                } else {
+                    mergePrims(getCurrentDataSet().getSelectedNodes(), e);
                 }
                 getCurrentDataSet().fireSelectionChanged();
             }
@@ -534,94 +542,132 @@
         updateStatusLine();
     }
 
-    public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl) {
-        selectPrims(selectionManager.getObjectsInRectangle(r, alt), shift, ctrl, true, true);
+    public void selectionEnded(Rectangle r, MouseEvent e) {
+        boolean alt = (e.getModifiersEx() & (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK)) != 0;
+        selectPrims(selectionManager.getObjectsInRectangle(r, alt), e, true, true);
     }
 
-    private boolean selMorePrims = false;
-    private OsmPrimitive selCycleStart = null;
-
-    public void selectPrims(Collection<OsmPrimitive> selectionList, boolean shift,
-            boolean ctrl, boolean released, boolean area) {
-        DataSet ds = getCurrentDataSet();
-
-        // decides on mousePressed whether
-        //      to cycle on mouseReleased (selList already selected)
-        //      or not                    (selList is a new selection)
-        selMorePrims = (released || area) ? selMorePrims : ds.getSelected().containsAll(selectionList);
+    /**
+     * Modifies current selection state and returns the next element in a
+     * selection cycle given by <code>prims</code>.
+     * @param prims the primitives that form the selection cycle
+     * @param shift whether shift is pressed
+     * @param ctrl whether ctrl is pressed
+     * @return the next element of cycle list <code>prims</code>.
+     */
+    private Collection<OsmPrimitive> cyclePrims(Collection<OsmPrimitive> prims, MouseEvent e) {
+        OsmPrimitive nxt = null;
 
-        // not allowed together: do not change dataset selection, return early
-        if ((shift && ctrl) || (ctrl && !released) || (!virtualWays.isEmpty()))
-            return;
+        debug("cyclePrims(): entry.....");
+        for (OsmPrimitive osm : prims) {
+            debug("cyclePrims(): prims id=" + osm.getId());
+        }
 
-        // toggle through possible objects on mouse release
-        if (released && !area) {
-            if (selectionList.size() > 1) {
-                Collection<OsmPrimitive> coll = ds.getSelected();
+        if (prims.size() > 1) {
+            boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+            boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
 
-                OsmPrimitive first, foundInDS, node, nxt;
-                first = nxt = selectionList.iterator().next();
-                foundInDS = node = null;
+            DataSet ds = getCurrentDataSet();
+            OsmPrimitive first = prims.iterator().next(), foundInDS = null;
+            nxt = first;
 
-                for (Iterator<OsmPrimitive> i = selectionList.iterator(); i.hasNext(); ) {
-                    if (selMorePrims && shift) {
-                        if (!coll.contains(nxt = i.next())) {
-                            break; // take first primitive not in dsSel or last if all contained
-                        }
-                    } else {
-                        if (coll.contains(nxt = i.next())) {
-                            foundInDS = nxt;
-                            if (selMorePrims || ctrl) {
-                                ds.clearSelection(nxt);
-                                nxt = i.hasNext() ? i.next() : first;
-                            }
-                            break; // take next primitive of selList
-                        } else if (nxt instanceof Node && node == null) {
-                            node = nxt;
+            for (Iterator<OsmPrimitive> i = prims.iterator(); i.hasNext(); ) {
+                if (cyclePrims && shift) {
+                    if (!(nxt = i.next()).isSelected()) {
+                        debug("cyclePrims(): taking " + nxt.getId());
+                        break; // take first primitive in prims list not in sel
+                    }
+                } else {
+                    if ((nxt = i.next()).isSelected()) {
+                        foundInDS = nxt;
+                        if (cyclePrims || ctrl) {
+                            ds.clearSelection(foundInDS);
+                            nxt = i.hasNext() ? i.next() : first;
                         }
+                        debug("selectPrims(): taking " + nxt.getId());
+                        break; // take next primitive in prims list
                     }
                 }
+            }
 
-                if (ctrl) {
-                    if (foundInDS != null) {
-                        // a member of selList was foundInDS
-                        if (!selectionList.contains(selCycleStart)) {
-                            selCycleStart = foundInDS;
-                        }
-                        // check if selCycleStart == prim (equals next(foundInDS))
-                        if (selCycleStart.equals(nxt)) {
-                            ds.addSelected(nxt);   // cycle complete, prim toggled below
-                            selCycleStart = null;  // check: might do w/out ??
-                        }
-                    } else {
-                        // no member of selList was foundInDS (sets were disjunct), setup for new cycle
-                        selCycleStart = nxt = (node != null) ? node : first;
+            if (ctrl) {
+                // a member of prims was found in the current dataset selection
+                if (foundInDS != null) {
+                    // mouse was moved to a different selection group w/ a previous sel
+                    if (!prims.contains(cycleStart)) {
+                        ds.clearSelection(prims);
+                        cycleStart = foundInDS;
+                        debug("selectPrims(): cycleStart set to foundInDS=" + cycleStart.getId());
+                    } else if (cycleStart.equals(nxt)) {
+                        // loop detected, insert deselect step
+                        ds.addSelected(nxt);
+                        debug("selectPrims(): cycleStart hit");
                     }
+                } else {
+                    // setup for iterating a sel group again or a new, different one..
+                    nxt = (prims.contains(cycleStart)) ? cycleStart : first;
+                    cycleStart = nxt;
+                    debug("selectPrims(): cycleStart set to nxt=" + cycleStart.getId());
                 }
+            } else {
+                cycleStart = null;
+            }
+
+            debug("cyclePrims(): truncated prims list to id=" + nxt.getId());
+        }
+
+        // pass on prims, if it had less than 2 elements
+        return (nxt != null) ? MapView.asColl(nxt) : prims;
+    }
+
+    private void mergePrims(Collection<Node> affectedNodes, MouseEvent e) {
+        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
 
-                selectionList = new ArrayList<OsmPrimitive>(1); // do not modify the passed object..
-                selectionList.add(nxt);
+        if (ctrl && !affectedNodes.isEmpty()) {
+            Collection<Node> target = mv.getNearestNodes(e.getPoint(), affectedNodes, OsmPrimitive.isSelectablePredicate);
+            if (!target.isEmpty()) {
+                Collection<Node> nodesToMerge = new LinkedList<Node>(affectedNodes);
+                nodesToMerge.add(target.iterator().next());
+
+                Command cmd = MergeNodesAction.mergeNodes(Main.main.getEditLayer(), nodesToMerge, target.iterator().next());
+                if(cmd != null) {
+                    Main.main.undoRedo.add(cmd);
+                }
             }
         }
+    }
+
+    private void selectPrims(Collection<OsmPrimitive> prims, MouseEvent e, boolean released, boolean area) {
+        boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
+        boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+        DataSet ds = getCurrentDataSet();
+
+        // not allowed together: do not change dataset selection, return early
+        if ((shift && ctrl) || (ctrl && !released) || (!virtualWays.isEmpty()))
+            return;
+
+        if (!released) {
+            // Don't replace the selection if the user clicked on a
+            // selected object (it breaks moving of selected groups).
+            // Do it later, on mouse release.
+            shift |= getCurrentDataSet().getSelected().containsAll(prims);
+        }
 
         if (ctrl) {
             // Ctrl on an item toggles its selection status,
             // but Ctrl on an *area* just clears those items
             // out of the selection.
             if (area) {
-                ds.clearSelection(selectionList);
+                ds.clearSelection(prims);
             } else {
-                ds.toggleSelected(selectionList);
+                ds.toggleSelected(prims);
             }
+        } else if (shift) {
+            // add prims to an existing selection
+            ds.addSelected(prims);
         } else {
-            // plain clicks with no modifiers
-            if (!shift) {
-                ds.setSelected(selectionList);
-            } else {
-                // add things to an
-                // existing selection.
-                ds.addSelected(selectionList);
-            }
+            // clear selection, then select the prims clicked
+            ds.setSelected(prims);
         }
     }
 
@@ -639,4 +685,8 @@
     @Override public boolean layerIsSupported(Layer l) {
         return l instanceof OsmDataLayer;
     }
+
+    private static void debug(String s) {
+        //System.err.println("SelectAction:" + s);
+    }
 }
Index: src/org/openstreetmap/josm/data/osm/visitor/paint/SimplePaintVisitor.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/SimplePaintVisitor.java	(revision 3566)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/SimplePaintVisitor.java	(working copy)
@@ -12,6 +12,7 @@
 import java.awt.RenderingHints;
 import java.awt.Stroke;
 import java.awt.geom.GeneralPath;
+import java.awt.geom.Point2D;
 import java.util.Collection;
 import java.util.Iterator;
 
@@ -25,7 +26,6 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
-import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
 import org.openstreetmap.josm.gui.NavigatableComponent;
 
 /**
@@ -265,27 +265,23 @@
             }
 
             final int size = max((ds.isSelected(n) ? selectedNodeSize : 0),
-                                    (n.isTagged() ? taggedNodeSize : 0),
-                                    (n.isConnectionNode() ? connectionNodeSize : 0),
-                                    unselectedNodeSize);
+                    (n.isTagged() ? taggedNodeSize : 0),
+                    (n.isConnectionNode() ? connectionNodeSize : 0),
+                    unselectedNodeSize);
 
             final boolean fill = (ds.isSelected(n) && fillSelectedNode) ||
-                                    (n.isTagged() && fillTaggedNode) ||
-                                    (n.isConnectionNode() && fillConnectionNode) ||
-                                    fillUnselectedNode;
+            (n.isTagged() && fillTaggedNode) ||
+            (n.isConnectionNode() && fillConnectionNode) ||
+            fillUnselectedNode;
 
             drawNode(n, color, size, fill);
         }
     }
 
-    public static boolean isLargeSegment(Point p1, Point p2, int space)
+    public static boolean isLargeSegment(Point2D p1, Point2D p2, int space)
     {
-        int xd = p1.x-p2.x; if(xd < 0) {
-            xd = -xd;
-        }
-        int yd = p1.y-p2.y; if(yd < 0) {
-            yd = -yd;
-        }
+        double xd = Math.abs(p1.getX()-p2.getX());
+        double yd = Math.abs(p1.getY()-p2.getY());
         return (xd+yd > space);
     }
 
Index: src/org/openstreetmap/josm/gui/SelectionManager.java
===================================================================
--- src/org/openstreetmap/josm/gui/SelectionManager.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/SelectionManager.java	(working copy)
@@ -60,7 +60,7 @@
          * @param ctrl Whether the ctrl key was pressed
          * @see InputEvent#getModifiersEx()
          */
-        public void selectionEnded(Rectangle r, boolean alt, boolean shift, boolean ctrl);
+        public void selectionEnded(Rectangle r, MouseEvent e);
         /**
          * Called to register the selection manager for "active" property.
          * @param listener The listener to register
@@ -188,11 +188,8 @@
         mousePosStart = null;
         mousePos = null;
 
-        boolean shift = (e.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0;
-        boolean alt = (e.getModifiersEx() & (MouseEvent.ALT_DOWN_MASK | MouseEvent.ALT_GRAPH_DOWN_MASK)) != 0;
-        boolean ctrl = (e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0;
         if ((e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == 0) {
-            selectionEndedListener.selectionEnded(r, alt, shift, ctrl);
+            selectionEndedListener.selectionEnded(r, e);
         }
     }
 
@@ -276,7 +273,7 @@
         Point center = new Point(r.x+r.width/2, r.y+r.height/2);
 
         if (clicked) {
-            OsmPrimitive osm = nc.getNearest(center, OsmPrimitive.isSelectablePredicate);
+            OsmPrimitive osm = nc.getNearestNodeOrWay(center, OsmPrimitive.isSelectablePredicate);
             if (osm != null) {
                 selection.add(osm);
             }
Index: src/org/openstreetmap/josm/gui/preferences/PrefJPanel.java
===================================================================
--- src/org/openstreetmap/josm/gui/preferences/PrefJPanel.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/preferences/PrefJPanel.java	(working copy)
@@ -194,6 +194,7 @@
         shortcutTable.getSelectionModel().addListSelectionListener(new cbAction(this));
         //shortcutTable.setFillsViewportHeight(true); Java 1.6
         shortcutTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+        shortcutTable.setAutoCreateRowSorter(true);
         listScrollPane.setViewportView(shortcutTable);
 
         listPane.add(listScrollPane);
Index: src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/NavigatableComponent.java	(working copy)
@@ -5,6 +5,7 @@
 
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.geom.Point2D;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -15,6 +16,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 import java.util.Stack;
 import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -84,10 +86,6 @@
         }
     }
 
-    public static final int snapDistance = Main.pref.getInteger("node.snap-distance", 10);
-    public static final int snapDistanceSq = sqr(snapDistance);
-
-    private static int sqr(int a) { return a*a;}
     /**
      * The scale factor in x or y-units per pixel. This means, if scale = 10,
      * every physical pixel on screen are 10 x or 10 y units in the
@@ -193,6 +191,10 @@
         return getProjection().eastNorth2latlon(getEastNorth(x, y));
     }
 
+    public LatLon getLatLon(double x, double y) {
+        return getLatLon((int)x, (int)y);
+    }
+
     /**
      * @param r
      * @return Minimum bounds that will cover rectangle
@@ -227,24 +229,45 @@
      * @return The point on screen where "point" would be drawn, relative
      *      to the own top/left.
      */
-    public Point getPoint(EastNorth p) {
+    public Point2D getPoint2D(EastNorth p) {
         if (null == p)
             return new Point();
         double x = (p.east()-center.east())/scale + getWidth()/2;
         double y = (center.north()-p.north())/scale + getHeight()/2;
-        return new Point((int)x,(int)y);
+        return new Point2D.Double(x, y);
     }
 
-    public Point getPoint(LatLon latlon) {
+    public Point2D getPoint2D(LatLon latlon) {
         if (latlon == null)
             return new Point();
         else if (latlon instanceof CachedLatLon)
-            return getPoint(((CachedLatLon)latlon).getEastNorth());
+            return getPoint2D(((CachedLatLon)latlon).getEastNorth());
         else
-            return getPoint(getProjection().latlon2eastNorth(latlon));
+            return getPoint2D(getProjection().latlon2eastNorth(latlon));
+    }
+    public Point2D getPoint2D(Node n) {
+        return getPoint2D(n.getEastNorth());
+    }
+
+    // looses precision, may overflow (depends on p and current scale)
+    //@Deprecated
+    public Point getPoint(EastNorth p) {
+        Point2D d = getPoint2D(p);
+        return new Point((int) d.getX(), (int) d.getY());
     }
+
+    // looses precision, may overflow (depends on p and current scale)
+    //@Deprecated
+    public Point getPoint(LatLon latlon) {
+        Point2D d = getPoint2D(latlon);
+        return new Point((int) d.getX(), (int) d.getY());
+    }
+
+    // looses precision, may overflow (depends on p and current scale)
+    //@Deprecated
     public Point getPoint(Node n) {
-        return getPoint(n.getEastNorth());
+        Point2D d = getPoint2D(n);
+        return new Point((int) d.getX(), (int) d.getY());
     }
 
     /**
@@ -428,133 +451,370 @@
         return !zoomRedoBuffer.isEmpty();
     }
 
-    private BBox getSnapDistanceBBox(Point p) {
+    private BBox getBBox(Point p, int snapDistance) {
         return new BBox(getLatLon(p.x - snapDistance, p.y - snapDistance),
                 getLatLon(p.x + snapDistance, p.y + snapDistance));
     }
 
-    @Deprecated
-    public final Node getNearestNode(Point p) {
-        return getNearestNode(p, OsmPrimitive.isUsablePredicate);
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return a sorted map with the keys representing the distance of
+     *      their associated nodes to point p.
+     */
+    private Map<Double, List<Node>> getNearestNodesImpl(Point p,
+            Predicate<OsmPrimitive> predicate) {
+        TreeMap<Double, List<Node>> nearestMap = new TreeMap<Double, List<Node>>();
+        DataSet ds = getCurrentDataSet();
+
+        if (ds != null) {
+            double dist, snapDistanceSq = Main.pref.getInteger("mappaint.node.snap-distance", 10);
+            snapDistanceSq *= snapDistanceSq;
+
+            for (Node n : ds.searchNodes(getBBox(p, Main.pref.getInteger("mappaint.node.snap-distance", 10)))) {
+                if (predicate.evaluate(n)
+                        && (dist = getPoint2D(n).distanceSq(p)) < snapDistanceSq)
+                {
+                    List<Node> nlist;
+                    if (nearestMap.containsKey(dist)) {
+                        nlist = nearestMap.get(dist);
+                    } else {
+                        nlist = new LinkedList<Node>();
+                        nearestMap.put(dist, nlist);
+                    }
+                    nlist.add(n);
+                }
+            }
+        }
+
+        return nearestMap;
+    }
+
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return All nodes nearest to point p that are in a belt from
+     *      dist(nearest) to dist(nearest)+4px around p and
+     *      that are not in ignore.
+     *
+     * @param p the point for which to search the nearest segment.
+     * @param ignore a collection of nodes which are not to be returned.
+     * @param predicate the returned objects have to fulfill certain properties.
+     */
+    public final List<Node> getNearestNodes(Point p,
+            Collection<Node> ignore, Predicate<OsmPrimitive> predicate) {
+        List<Node> nearestList = Collections.emptyList();
+
+        if (ignore == null) {
+            ignore = Collections.emptySet();
+        }
+
+        Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate);
+        if (!nlists.isEmpty()) {
+            Double minDistSq = null;
+            List<Node> nlist;
+            for (Double distSq : nlists.keySet()) {
+                nlist = nlists.get(distSq);
+
+                // filter nodes to be ignored before determining minDistSq..
+                nlist.removeAll(ignore);
+                if (minDistSq == null) {
+                    if (!nlist.isEmpty()) {
+                        minDistSq = distSq;
+                        nearestList = new ArrayList<Node>();
+                        nearestList.addAll(nlist);
+                    }
+                } else {
+                    if (distSq-minDistSq < (4)*(4)) {
+                        nearestList.addAll(nlist);
+                    }
+                }
+            }
+        }
+
+        return nearestList;
+    }
+
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return All nodes nearest to point p that are in a belt from
+     *      dist(nearest) to dist(nearest)+4px around p.
+     * @see #getNearestNodes(Point, Collection, Predicate)
+     * 
+     * @param p the point for which to search the nearest segment.
+     * @param predicate the returned objects have to fulfill certain properties.
+     */
+    public final List<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestNodes(p, null, predicate);
     }
 
     /**
-     * Return the nearest node to the screen point given.
-     * If more then one node within snapDistance pixel is found,
-     * the nearest node is returned.
+     * The *result* depends on the current map selection state.
+     * 
+     * If more than one node within node.snap-distance pixels is found,
+     * the nearest node selected is returned.
+     * 
+     * If no such node is found, the nearest new/id=0 node within
+     * about the same distance as the true nearest node is returned.
+     * 
+     * If no such node is found either, the true nearest
+     * node to p is returned.
+     * 
+     * Finally, if a node is not found at all, return null.
+     * 
+     * @return A node within snap-distance to point p,
+     *      that is chosen by the algorithm described.
+     * 
      * @param p the screen point
      * @param predicate this parameter imposes a condition on the returned object, e.g.
      *        give the nearest node that is tagged.
      */
     public final Node getNearestNode(Point p, Predicate<OsmPrimitive> predicate) {
-        DataSet ds = getCurrentDataSet();
-        if (ds == null)
-            return null;
+        Node n = null;
 
-        double minDistanceSq = snapDistanceSq;
-        Node minPrimitive = null;
-        for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
-            if (! predicate.evaluate(n))
-                continue;
-            Point sp = getPoint(n);
-            double dist = p.distanceSq(sp);
-            if (dist < minDistanceSq) {
-                minDistanceSq = dist;
-                minPrimitive = n;
-            }
-            // when multiple nodes on one point, prefer new or selected nodes
-            else if (dist == minDistanceSq && minPrimitive != null
-                    && ((n.isNew() && ds.isSelected(n))
-                            || (!ds.isSelected(minPrimitive) && (ds.isSelected(n) || n.isNew())))) {
-                minPrimitive = n;
+        Map<Double, List<Node>> nlists = getNearestNodesImpl(p, predicate);
+        if (!nlists.isEmpty()) {
+            Node ntsel = null, ntnew = null;
+            double minDistSq = nlists.keySet().iterator().next();
+
+            for (Double distSq : nlists.keySet()) {
+                for (Node nd : nlists.get(distSq)) {
+                    // find the nearest selected node
+                    if (ntsel == null && nd.isSelected()) {
+                        ntsel = nd;
+                    }
+                    // find the nearest newest node that is within about the same
+                    // distance as the true nearest node
+                    if (ntnew == null && nd.isNew() && (distSq-minDistSq < 1)) {
+                        ntnew = nd;
+                    }
+                }
             }
+
+            // take nearest selected, nearest new or true nearest node to p, in that order
+            n = (ntsel != null) ? ntsel : ((ntnew != null) ? ntnew
+                    : nlists.values().iterator().next().get(0));
         }
-        return minPrimitive;
+
+        return n;
+    }
+
+    @Deprecated
+    public final Node getNearestNode(Point p) {
+        return getNearestNode(p, OsmPrimitive.isUsablePredicate);
     }
 
     /**
-     * @return all way segments within 10px of p, sorted by their
-     * perpendicular distance.
-     *
-     * @param p the point for which to search the nearest segment.
-     * @param predicate the returned objects have to fulfill certain properties.
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return a sorted map with the keys representing the perpendicular
+     *      distance of their associated way segments to point p.
      */
-    public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) {
-        TreeMap<Double, List<WaySegment>> nearest = new TreeMap<Double, List<WaySegment>>();
+    private Map<Double, List<WaySegment>> getNearestWaySegmentsImpl(Point p,
+            Predicate<OsmPrimitive> predicate) {
+        Map<Double, List<WaySegment>> nearestMap = new TreeMap<Double, List<WaySegment>>();
         DataSet ds = getCurrentDataSet();
-        if (ds == null)
-            return null;
 
-        for (Way w : ds.searchWays(getSnapDistanceBBox(p))) {
-            if (!predicate.evaluate(w))
-                continue;
-            Node lastN = null;
-            int i = -2;
-            for (Node n : w.getNodes()) {
-                i++;
-                if (n.isDeleted() || n.isIncomplete()) {//FIXME: This shouldn't happen, raise exception?
-                    continue;
-                }
-                if (lastN == null) {
-                    lastN = n;
+        if (ds != null) {
+            double snapDistanceSq = Main.pref.getInteger("mappaint.segment.snap-distance", 10);
+            snapDistanceSq *= snapDistanceSq;
+
+            for (Way w : ds.searchWays(getBBox(p, Main.pref.getInteger("mappaint.segment.snap-distance", 10)))) {
+                if (!predicate.evaluate(w)) {
                     continue;
                 }
-
-                Point A = getPoint(lastN);
-                Point B = getPoint(n);
-                double c = A.distanceSq(B);
-                double a = p.distanceSq(B);
-                double b = p.distanceSq(A);
-                double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared
-                if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
-                    if (ds.isSelected(w)) {
-                        perDist -= 0.00001;
+                Node lastN = null;
+                int i = -2;
+                for (Node n : w.getNodes()) {
+                    i++;
+                    if (n.isDeleted() || n.isIncomplete()) { //FIXME: This shouldn't happen, raise exception?
+                        continue;
                     }
-                    List<WaySegment> l;
-                    if (nearest.containsKey(perDist)) {
-                        l = nearest.get(perDist);
-                    } else {
-                        l = new LinkedList<WaySegment>();
-                        nearest.put(perDist, l);
+                    if (lastN == null) {
+                        lastN = n;
+                        continue;
+                    }
+
+                    Point2D A = getPoint2D(lastN);
+                    Point2D B = getPoint2D(n);
+                    double c = A.distanceSq(B);
+                    double a = p.distanceSq(B);
+                    double b = p.distanceSq(A);
+
+                    /* perpendicular distance squared
+                     * loose some precision to account for possible deviations in the calculation above
+                     * e.g. if identical (A and B) come about reversed in another way, values may differ
+                     * -- zero out least significant 32 dual digits of mantissa..
+                     */
+                    double perDistSq = Double.longBitsToDouble(
+                            Double.doubleToLongBits( a - (a - b + c) * (a - b + c) / 4 / c )
+                            >> 32 << 32); // resolution in numbers with large exponent not needed here..
+
+                    if (perDistSq < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
+                        //System.err.println(Double.toHexString(perDistSq));
+
+                        List<WaySegment> wslist;
+                        if (nearestMap.containsKey(perDistSq)) {
+                            wslist = nearestMap.get(perDistSq);
+                        } else {
+                            wslist = new LinkedList<WaySegment>();
+                            nearestMap.put(perDistSq, wslist);
+                        }
+                        wslist.add(new WaySegment(w, i));
                     }
-                    l.add(new WaySegment(w, i));
+
+                    lastN = n;
                 }
+            }
+        }
 
-                lastN = n;
+        return nearestMap;
+    }
+
+    /**
+     * The result *order* depends on the current map selection state.
+     * Segments within 10px of p are searched and sorted by their distance to @param p,
+     * then, within groups of equally distant segments, prefer those that are selected.
+     * 
+     * @return all segments within 10px of p that are not in ignore,
+     *          sorted by their perpendicular distance.
+     * 
+     * @param p the point for which to search the nearest segments.
+     * @param ignore a collection of segments which are not to be returned.
+     * @param predicate the returned objects have to fulfill certain properties.
+     */
+    public final List<WaySegment> getNearestWaySegments(Point p,
+            Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) {
+        List<WaySegment> nearestList = new ArrayList<WaySegment>();
+        List<WaySegment> unselected = new LinkedList<WaySegment>();
+
+        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+            // put selected waysegs within each distance group first
+            // makes the order of nearestList dependent on current selection state
+            for (WaySegment ws : wss) {
+                (ws.way.isSelected() ? nearestList : unselected).add(ws);
             }
+            nearestList.addAll(unselected);
+            unselected.clear();
         }
-        ArrayList<WaySegment> nearestList = new ArrayList<WaySegment>();
-        for (List<WaySegment> wss : nearest.values()) {
-            nearestList.addAll(wss);
+        if (ignore != null) {
+            nearestList.removeAll(ignore);
         }
+
         return nearestList;
     }
 
     /**
-     * @return the nearest way segment to the screen point given that is not
-     * in ignore.
+     * The result *order* depends on the current map selection state.
+     * 
+     * @return all segments within 10px of p, sorted by their perpendicular distance.
+     * @see #getNearestWaySegments(Point, Collection, Predicate)
+     *
+     * @param p the point for which to search the nearest segments.
+     * @param predicate the returned objects have to fulfill certain properties.
+     */
+    public final List<WaySegment> getNearestWaySegments(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestWaySegments(p, null, predicate);
+    }
+
+    /**
+     * The *result* depends on the current map selection state.
+     * 
+     * @return The nearest way segment to point p,
+     *      prefer a nearest, selected way segment, if found.
+     * @see #getNearestWaySegments(Point, Collection, Predicate)
      *
      * @param p the point for which to search the nearest segment.
-     * @param ignore a collection of segments which are not to be returned.
+     * @param predicate the returned object has to fulfill certain properties.
+     */
+    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) {
+        WaySegment wayseg = null, ntsel = null;
+
+        for (List<WaySegment> wslist : getNearestWaySegmentsImpl(p, predicate).values()) {
+            if (wayseg != null && ntsel != null) {
+                break;
+            }
+            for (WaySegment ws : wslist) {
+                if (wayseg == null) {
+                    wayseg = ws;
+                }
+                if (ntsel == null && ws.way.isSelected()) {
+                    ntsel = ws;
+                }
+            }
+        }
+
+        return (ntsel != null) ? ntsel : wayseg;
+    }
+
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the perpendicular distance to point p.
+     * 
+     * @return all nearest ways to the screen point given that are not in ignore.
+     * @see #getNearestWaySegments(Point, Collection, Predicate)
+     * 
+     * @param p the point for which to search the nearest ways.
+     * @param ignore a collection of ways which are not to be returned.
      * @param predicate the returned object has to fulfill certain properties.
-     * May be null.
      */
-    public final WaySegment getNearestWaySegment
-                                    (Point p, Collection<WaySegment> ignore, Predicate<OsmPrimitive> predicate) {
-        List<WaySegment> nearest = getNearestWaySegments(p, predicate);
-        if(nearest == null)
-            return null;
+    public final List<Way> getNearestWays(Point p,
+            Collection<Way> ignore, Predicate<OsmPrimitive> predicate) {
+        List<Way> nearestList = new ArrayList<Way>();
+        Set<Way> wset = new HashSet<Way>();
+
+        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+            for (WaySegment ws : wss) {
+                if (wset.add(ws.way)) {
+                    nearestList.add(ws.way);
+                }
+            }
+        }
         if (ignore != null) {
-            nearest.removeAll(ignore);
+            nearestList.removeAll(ignore);
         }
-        return nearest.isEmpty() ? null : nearest.get(0);
+
+        return nearestList;
     }
 
     /**
-     * @return the nearest way segment to the screen point given.
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the perpendicular distance to point p.
+     * 
+     * @return all nearest ways to the screen point given.
+     * @see #getNearestWays(Point, Collection, Predicate)
+     * 
+     * @param p the point for which to search the nearest ways.
+     * @param predicate the returned object has to fulfill certain properties.
      */
-    public final WaySegment getNearestWaySegment(Point p, Predicate<OsmPrimitive> predicate) {
-        return getNearestWaySegment(p, null, predicate);
+    public final List<Way> getNearestWays(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestWays(p, null, predicate);
+    }
+
+    /**
+     * The *result* depends on the current map selection state.
+     *
+     * @return The nearest way to point p,
+     *      prefer a selected way if there are multiple nearest.
+     * @see #getNearestWaySegment(Point, Collection, Predicate)
+     *
+     * @param p the point for which to search the nearest segment.
+     * @param predicate the returned object has to fulfill certain properties.
+     */
+    public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) {
+        WaySegment nearestWaySeg = getNearestWaySegment(p, predicate);
+        return (nearestWaySeg == null) ? null : nearestWaySeg.way;
     }
 
     @Deprecated
@@ -563,132 +823,300 @@
     }
 
     /**
-     * @return the nearest way to the screen point given.
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * First, nodes will be searched. If there are nodes within BBox found,
+     * return a collection of those nodes only.
+     * 
+     * If no nodes are found, search for nearest ways. If there are ways
+     * within BBox found, return a collection of those ways only.
+     * 
+     * If nothing is found, return an empty collection.
+     * 
+     * @return Primitives nearest to the given screen point that are not in ignore.
+     * @see #getNearestNodes(Point, Collection, Predicate)
+     * @see #getNearestWays(Point, Collection, Predicate)
+     * 
+     * @param p The point on screen.
+     * @param ignore a collection of ways which are not to be returned.
+     * @param predicate the returned object has to fulfill certain properties.
      */
-    public final Way getNearestWay(Point p, Predicate<OsmPrimitive> predicate) {
-        WaySegment nearestWaySeg = getNearestWaySegment(p, predicate);
-        return nearestWaySeg == null ? null : nearestWaySeg.way;
+    public final List<OsmPrimitive> getNearestNodesOrWays(Point p,
+            Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) {
+        List<OsmPrimitive> nearestList = Collections.emptyList();
+        OsmPrimitive osm = getNearestNodeOrWay(p, predicate, false);
+
+        if (osm != null) {
+            if (osm instanceof Node) {
+                nearestList = new ArrayList<OsmPrimitive>(getNearestNodes(p, predicate));
+            } else if (osm instanceof Way) {
+                nearestList = new ArrayList<OsmPrimitive>(getNearestWays(p, predicate));
+            }
+            if (ignore != null) {
+                nearestList.removeAll(ignore);
+            }
+        }
+
+        return nearestList;
     }
 
     /**
-     * Return the object, that is nearest to the given screen point.
-     *
-     * First, a node will be searched. If a node within 10 pixel is found, the
-     * nearest node is returned.
-     *
-     * If no node is found, search for near ways.
-     *
-     * If nothing is found, return <code>null</code>.
-     *
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return Primitives nearest to the given screen point.
+     * @see #getNearests(Point, Collection, Predicate)
+     * 
      * @param p The point on screen.
      * @param predicate the returned object has to fulfill certain properties.
-     * @return  The primitive that is nearest to the point p.
      */
-    public OsmPrimitive getNearest(Point p, Predicate<OsmPrimitive> predicate) {
-        OsmPrimitive osm = getNearestNode(p, predicate);
-        if (osm == null)
-        {
-            osm = getNearestWay(p, predicate);
-        }
-        return osm;
+    public final List<OsmPrimitive> getNearestNodesOrWays(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestNodesOrWays(p, null, predicate);
     }
 
     /**
-     * Returns a singleton of the nearest object, or else an empty collection.
+     * This is used as a helper routine to {@link #getNearestNodeOrWay(Point, Predicate, boolean)}
+     * 
+     * @return true, if the node fulfills certain properties wrt p and use_sel
+     * 
+     * @param osm node to check
+     * @param p point clicked
+     * @param use_sel whether to prefer a selected node
      */
-    public Collection<OsmPrimitive> getNearestCollection(Point p, Predicate<OsmPrimitive> predicate) {
-        OsmPrimitive osm = getNearest(p, predicate);
-        if (osm == null)
-            return Collections.emptySet();
-        return Collections.singleton(osm);
+    private boolean isPrecedenceNode(Node osm, Point p, boolean use_selected) {
+        boolean ret = false;
+
+        if (osm != null) {
+            ret |= !(p.distanceSq(getPoint2D(osm)) > (4)*(4));
+            ret |= osm.isTagged();
+            if (use_selected) {
+                ret |= osm.isSelected();
+            }
+        }
+
+        return ret;
     }
 
     /**
-     * @return A list of all objects that are nearest to
-     * the mouse.
+     * The *result* depends on the current map selection state IF use_selected is true.
+     * 
+     * IF use_selected is true, use {@link #getNearestNode(Point, Predicate)} to find
+     * the nearest, selected node.  If not found, try {@link #getNearestWaySegment(Point, Predicate)}
+     * to find the nearest selected way.
+     * 
+     * IF use_selected is false, or if no selected primitive was found, do the following.
+     * 
+     * If the nearest node found is within 4px of p, simply take it.
+     * Else, find the nearest way segment. Then, if p is closer to its
+     * middle than to the node, take the way segment, else take the node.
+     * 
+     * Finally, if no nearest primitive is found at all, return null.
+     *
+     * @return A primitive within snap-distance to point p,
+     *      that is chosen by the algorithm described.
+     * @see getNearestNode(Point, Predicate)
+     * @see getNearestNodesImpl(Point, Predicate)
+     * @see getNearestWay(Point, Predicate)
      *
-     * @return A collection of all items or <code>null</code>
-     *      if no item under or near the point. The returned
-     *      list is never empty.
+     * @param p The point on screen.
+     * @param predicate the returned object has to fulfill certain properties.
+     * @param use_selected whether to prefer primitives that are currently selected.
      */
-    public Collection<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) {
-        Collection<OsmPrimitive> nearest = new HashSet<OsmPrimitive>();
-        DataSet ds = getCurrentDataSet();
-        if (ds == null)
-            return null;
-        for (Way w : ds.searchWays(getSnapDistanceBBox(p))) {
-            if (!predicate.evaluate(w))
-                continue;
-            Node lastN = null;
-            for (Node n : w.getNodes()) {
-                if (!predicate.evaluate(n))
-                    continue;
-                if (lastN == null) {
-                    lastN = n;
-                    continue;
+    public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate, boolean use_selected) {
+        OsmPrimitive osm = null;
+        WaySegment ws = null;
+
+        // find a nearest, selected primitive
+        if (use_selected) {
+            osm = getNearestNode(p, predicate);
+            use_selected = isPrecedenceNode((Node)osm, p, use_selected);
+
+            if (!use_selected) {
+                ws = getNearestWaySegment(p, predicate);
+                if (ws != null) {
+                    if (ws.way.isSelected() || osm == null) {
+                        // either no nearest nodes found or none were selected
+                        use_selected = true;
+                        osm = ws.way;
+                    } // else { unselected node and wayseg found, do the stuff below }
+                } else {
+                    // no nearest wayseg found, osm is null or the nearest node
+                    use_selected = true;
                 }
-                Point A = getPoint(lastN);
-                Point B = getPoint(n);
-                double c = A.distanceSq(B);
-                double a = p.distanceSq(B);
-                double b = p.distanceSq(A);
-                double perDist = a - (a - b + c) * (a - b + c) / 4 / c; // perpendicular distance squared
-                if (perDist < snapDistanceSq && a < c + snapDistanceSq && b < c + snapDistanceSq) {
-                    nearest.add(w);
+            }
+        }
+
+        // no nearest, selected primitive was found or caller does not care about current selection
+        if (!use_selected) {
+            if (osm == null) {
+                // get the true nearest node (if unselected found before, reuse it)
+                for (List<Node> nlist : getNearestNodesImpl(p, predicate).values()) {
+                    osm = nlist.get(0);
                     break;
                 }
-                lastN = n;
             }
-        }
-        for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
-            if (n.isUsable()
-                    && getPoint(n).distanceSq(p) < snapDistanceSq) {
-                nearest.add(n);
+
+            // there is no nearest node OR it does not fulfill criteria to simply be chosen
+            if (osm == null || !isPrecedenceNode((Node)osm, p, use_selected)) {
+                if (ws == null) {
+                    // get the true nearest wayseg (if unselected found before, reuse it)
+                    for (WaySegment wseg : getNearestWaySegments(p, predicate)) {
+                        ws = wseg;
+                        break;
+                    }
+                }
+                if (ws != null) {
+                    if (osm == null) {
+                        // a nearest node was not found
+                        osm = ws.way;
+                    } else {
+                        int maxWaySegLenSq = 3*Main.pref.getInteger("mappaint.node.snap-distance", 10);
+                        maxWaySegLenSq *= maxWaySegLenSq;
+
+                        Point2D wp1 = getPoint2D(ws.way.getNode(ws.lowerIndex));
+                        Point2D wp2 = getPoint2D(ws.way.getNode(ws.lowerIndex+1));
+
+                        // is wayseg shorter than maxWaySegLenSq and
+                        // is p closer to the middle of wayseg than to the nearest node?
+                        if (wp1.distanceSq(wp2) < maxWaySegLenSq &&
+                                p.distanceSq(project(0.5, wp1, wp2)) < p.distanceSq(getPoint2D((Node)osm))) {
+                            osm = ws.way;
+                        }
+                    }
+                }
             }
         }
-        return nearest.isEmpty() ? null : nearest;
+
+        return osm;
     }
 
     /**
-     * @return A list of all nodes that are nearest to
-     * the mouse.
-     *
-     * @return A collection of all nodes or <code>null</code>
-     *      if no node under or near the point. The returned
-     *      list is never empty.
+     * Convenience method to {@link #getNearestNodeOrWay(Point, Predicate, boolean)}.
+     * 
+     * @return The nearest primitive to point p.
      */
-    public Collection<Node> getNearestNodes(Point p, Predicate<OsmPrimitive> predicate) {
-        Collection<Node> nearest = new HashSet<Node>();
-        DataSet ds = getCurrentDataSet();
-        if (ds == null)
-            return null;
+    public final OsmPrimitive getNearestNodeOrWay(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestNodeOrWay(p, predicate, false);
+    }
 
-        for (Node n : ds.searchNodes(getSnapDistanceBBox(p))) {
-            if (!predicate.evaluate(n))
-                continue;
-            if (getPoint(n).distanceSq(p) < snapDistanceSq) {
-                nearest.add(n);
-            }
+    @Deprecated
+    public final OsmPrimitive getNearest(Point p, Predicate<OsmPrimitive> predicate) {
+        return getNearestNodeOrWay(p, predicate, false);
+    }
+
+    @Deprecated
+    public final Collection<OsmPrimitive> getNearestCollection(Point p, Predicate<OsmPrimitive> predicate) {
+        return asColl(getNearest(p, predicate));
+    }
+
+    /**
+     * @return o as collection of o's type.
+     */
+    public final static <T> Collection<T> asColl(T o) {
+        if (o == null)
+            return Collections.emptySet();
+        return Collections.singleton(o);
+    }
+
+    public final static double perDist(Point2D pt, Point2D a, Point2D b) {
+        if (pt != null && a != null && b != null) {
+            double pd = (
+                    (a.getX()-pt.getX())*(b.getX()-a.getX()) -
+                    (a.getY()-pt.getY())*(b.getY()-a.getY()) );
+            return Math.abs(pd) / a.distance(b);
         }
-        return nearest.isEmpty() ? null : nearest;
+        return 0d;
     }
 
     /**
-     * @return the nearest nodes to the screen point given that is not
-     * in ignore.
-     *
-     * @param p the point for which to search the nearest segment.
-     * @param ignore a collection of nodes which are not to be returned.
-     * @param predicate the returned objects have to fulfill certain properties.
-     * May be null.
+     * 
+     * @param pt point to project onto (ab)
+     * @param a root of vector
+     * @param b vector
+     * @return point of intersection of line given by (ab)
+     *      with its orthogonal line running through pt
+     */
+    public final static Point2D project(Point2D pt, Point2D a, Point2D b) {
+        if (pt != null && a != null && b != null) {
+            double r = ((
+                    (pt.getX()-a.getX())*(b.getX()-a.getX()) +
+                    (pt.getY()-a.getY())*(b.getY()-a.getY()) )
+                    / a.distanceSq(b));
+            return project(r, a, b);
+        }
+        return null;
+    }
+
+    /**
+     * if r = 0 returns a, if r=1 returns b,
+     * if r = 0.5 returns center between a and b, etc..
+     * 
+     * @param r scale value
+     * @param a root of vector
+     * @param b vector
+     * @return new point at a + r*(ab)
+     */
+    public final static Point2D project(double r, Point2D a, Point2D b) {
+        Point2D ret = null;
+
+        if (a != null && b != null) {
+            ret = new Point2D.Double(a.getX() + r*(b.getX()-a.getX()),
+                    a.getY() + r*(b.getY()-a.getY()));
+        }
+        return ret;
+    }
+
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return a list of all objects that are nearest to point p and
+     *          not in ignore or an empty list if nothing was found.
+     * 
+     * @param p The point on screen.
+     * @param ignore a collection of ways which are not to be returned.
+     * @param predicate the returned object has to fulfill certain properties.
      */
-    public final Collection<Node> getNearestNodes(Point p, Collection<Node> ignore, Predicate<OsmPrimitive> predicate) {
-        Collection<Node> nearest = getNearestNodes(p, predicate);
-        if (nearest == null) return null;
+    public final List<OsmPrimitive> getAllNearest(Point p,
+            Collection<OsmPrimitive> ignore, Predicate<OsmPrimitive> predicate) {
+        List<OsmPrimitive> nearestList = new ArrayList<OsmPrimitive>();
+        Set<Way> wset = new HashSet<Way>();
+
+        for (List<WaySegment> wss : getNearestWaySegmentsImpl(p, predicate).values()) {
+            for (WaySegment ws : wss) {
+                if (wset.add(ws.way)) {
+                    nearestList.add(ws.way);
+                }
+            }
+        }
+        for (List<Node> nlist : getNearestNodesImpl(p, predicate).values()) {
+            nearestList.addAll(nlist);
+        }
         if (ignore != null) {
-            nearest.removeAll(ignore);
+            nearestList.removeAll(ignore);
         }
-        return nearest.isEmpty() ? null : nearest;
+
+        return nearestList;
+    }
+
+    /**
+     * The *result* does not depend on the current map selection state,
+     * neither does the result *order*.
+     * It solely depends on the distance to point p.
+     * 
+     * @return a list of all objects that are nearest to point p
+     *          or an empty list if nothing was found.
+     * @see #getAllNearest(Point, Collection, Predicate)
+     * 
+     * @param p The point on screen.
+     * @param predicate the returned object has to fulfill certain properties.
+     */
+    public final List<OsmPrimitive> getAllNearest(Point p, Predicate<OsmPrimitive> predicate) {
+        return getAllNearest(p, null, predicate);
     }
 
     /**
Index: src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapView.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/MapView.java	(working copy)
@@ -35,9 +35,9 @@
 import org.openstreetmap.josm.actions.AutoScaleAction;
 import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
+import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSource;
Index: src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(working copy)
@@ -18,6 +18,7 @@
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.net.HttpURLConnection;
+import java.net.ProxySelector;
 import java.net.URI;
 import java.net.URLEncoder;
 import java.util.ArrayList;
@@ -1060,7 +1061,7 @@
             try {
                 String base = new String(Main.pref.get("url.openstreetmap-wiki", "http://wiki.openstreetmap.org/wiki/"));
                 String l = LanguageInfo.getWikiLanguagePrefix();
-                List<URI> uris = new ArrayList<URI>();
+                final List<URI> uris = new ArrayList<URI>();
                 int row;
                 if (propertyTable.getSelectedRowCount() == 1) {
                     row = propertyTable.getSelectedRow();
@@ -1096,39 +1097,47 @@
                     uris.add(new URI(String.format("%sMap_Features", base)));
                 }
 
-                // find a page that actually exists in the wiki
-                HttpURLConnection conn;
-                for(URI u : uris) {
-                    conn = (HttpURLConnection) u.toURL().openConnection();
+                Main.worker.execute(new Runnable(){
+                    public void run() {
+                        try {
+                            // find a page that actually exists in the wiki
+                            HttpURLConnection conn;
+                            for(URI u : uris) {
+                                conn = (HttpURLConnection) u.toURL().openConnection(ProxySelector.getDefault().select(u).get(0));
 
-                    if (conn.getResponseCode() != 200) {
-                        System.out.println("INFO: " + u + " does not exist");
-                        conn.disconnect();
-                    } else {
-                        int osize = conn.getContentLength();
-                        conn.disconnect();
+                                if (conn.getResponseCode() != 200) {
+                                    System.out.println("INFO: " + u + " does not exist");
+                                    conn.disconnect();
+                                } else {
+                                    int osize = conn.getContentLength();
+                                    conn.disconnect();
 
-                        conn = (HttpURLConnection) new URI(u.toString()
-                                .replace("=", "%3D") /* do not URLencode whole string! */
-                                .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
-                        ).toURL().openConnection();
+                                    conn = (HttpURLConnection) new URI(u.toString()
+                                            .replace("=", "%3D") /* do not URLencode whole string! */
+                                            .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
+                                    ).toURL().openConnection();
 
-                        /* redirect pages have different content length, but retrieving a "nonredirect"
-                         *  page using index.php and the direct-link method gives slightly different
-                         *  content lengths, so we have to be fuzzy.. (this is UGLY, recode if u know better)
-                         */
-                        if (Math.abs(conn.getContentLength()-osize) > 200) {
-                            System.out.println("INFO: " + u + " is a mediawiki redirect");
-                            conn.disconnect();
-                        } else {
-                            System.out.println("INFO: browsing to " + u);
-                            conn.disconnect();
+                                    /* redirect pages have different content length, but retrieving a "nonredirect"
+                                     *  page using index.php and the direct-link method gives slightly different
+                                     *  content lengths, so we have to be fuzzy.. (this is UGLY, recode if u know better)
+                                     */
+                                    if (Math.abs(conn.getContentLength()-osize) > 200) {
+                                        System.out.println("INFO: " + u + " is a mediawiki redirect");
+                                        conn.disconnect();
+                                    } else {
+                                        System.out.println("INFO: browsing to " + u);
+                                        conn.disconnect();
 
-                            OpenBrowser.displayUrl(u.toString());
-                            break;
+                                        OpenBrowser.displayUrl(u.toString());
+                                        break;
+                                    }
+                                }
+                            }
+                        } catch (Exception e) {
+                            e.printStackTrace();
                         }
                     }
-                }
+                });
             } catch (Exception e1) {
                 e1.printStackTrace();
             }
Index: src/org/openstreetmap/josm/gui/MapStatus.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapStatus.java	(revision 3566)
+++ src/org/openstreetmap/josm/gui/MapStatus.java	(working copy)
@@ -288,7 +288,7 @@
          * @param ms
          */
         private final void statusBarElementUpdate(MouseState ms) {
-            final OsmPrimitive osmNearest = mv.getNearest(ms.mousePos, OsmPrimitive.isUsablePredicate);
+            final OsmPrimitive osmNearest = mv.getNearestNodeOrWay(ms.mousePos, OsmPrimitive.isUsablePredicate);
             if (osmNearest != null) {
                 nameText.setText(osmNearest.getDisplayName(DefaultNameFormatter.getInstance()));
             } else {
