Index: src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 3544)
+++ src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(working copy)
@@ -15,8 +15,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
 
@@ -202,7 +202,7 @@
                     "Add and move a virtual new node to {0} ways", virtualWays.size(),
                     virtualWays.size());
             Main.main.undoRedo.add(new SequenceCommand(text, virtualCmds));
-            selectPrims(Collections.singleton((OsmPrimitive)virtualNode), false, false, false, false);
+            getCurrentDataSet().setSelected(Collections.singleton((OsmPrimitive)virtualNode));
             virtualWays.clear();
             virtualNode = null;
         } else {
@@ -268,33 +268,41 @@
         }
     }
 
-    private Collection<OsmPrimitive> getNearestCollectionVirtual(Point p, boolean allSegements) {
-        MapView c = Main.map.mapView;
+    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();
         OsmPrimitive osm = c.getNearestNode(p, OsmPrimitive.isSelectablePredicate);
-        virtualWays.clear();
-        virtualNode = null;
-        Node virtualWayNode = null;
 
-        if (osm == null)
-        {
-            Collection<WaySegment> nearestWaySegs = allSegements
-            ? c.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)
-                    : Collections.singleton(c.getNearestWaySegment(p, OsmPrimitive.isSelectablePredicate));
+        if (osm != null) {
+            for (Node n : c.getNearestNodes(p, OsmPrimitive.isSelectablePredicate)) {
+                if (sel.contains(n)) {
+                    osm = n;
+                }
+            }
+        } else {
+            Node virtualWayNode = null;
+            Way w = null;
 
-            for(WaySegment nearestWS : nearestWaySegs) {
+            Collection<WaySegment> virtualWaysInSel = new ArrayList<WaySegment>();
+            osm = c.getNearestWay(p, OsmPrimitive.isSelectablePredicate);
+
+            for(WaySegment nearestWS : c.getNearestWaySegments(p, OsmPrimitive.isSelectablePredicate)) {
                 if (nearestWS == null) {
                     continue;
                 }
+                if (sel.contains(w = nearestWS.way)) {
+                    osm = w;
+                }
 
-                osm = nearestWS.way;
-                if(Main.pref.getInteger("mappaint.node.virtual-size", 8) > 0)
-                {
-                    Way w = (Way)osm;
+                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, Main.pref.getInteger("mappaint.node.virtual-space", 70)))
+                    if(SimplePaintVisitor.isLargeSegment(p1, p2, virtualSpace))
                     {
                         Point pc = new Point((p1.x+p2.x)/2, (p1.y+p2.y)/2);
                         if (p.distanceSq(pc) < snapDistance)
@@ -303,7 +311,7 @@
                             // 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)
+                                if(!w.getNode(nearestWS.lowerIndex+1).equals(virtualWayNode)
                                         && !w.getNode(nearestWS.lowerIndex).equals(virtualWayNode)) {
                                     continue;
                                 }
@@ -311,7 +319,7 @@
                                 virtualWayNode = w.getNode(nearestWS.lowerIndex+1);
                             }
 
-                            virtualWays.add(nearestWS);
+                            (!sel.contains(w) ? virtualWays : virtualWaysInSel).add(nearestWS);
                             if(virtualNode == null) {
                                 virtualNode = new Node(Main.map.mapView.getLatLon(pc.x, pc.y));
                             }
@@ -319,7 +327,13 @@
                     }
                 }
             }
+
+            if (virtualNode != null) {
+                virtualWays = virtualWaysInSel.isEmpty() ? virtualWays : virtualWaysInSel;
+                osm = virtualWays.iterator().next().way;
+            }
         }
+
         if (osm == null)
             return Collections.emptySet();
         return Collections.singleton(osm);
@@ -337,8 +351,8 @@
     @Override public void mousePressed(MouseEvent e) {
         if(!Main.map.mapView.isActiveLayerVisible())
             return;
+
         // request focus in order to enable the expected keyboard shortcuts
-        //
         Main.map.mapView.requestFocus();
 
         cancelDrawMode = false;
@@ -346,7 +360,6 @@
         if (e.getButton() != MouseEvent.BUTTON1)
             return;
         boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
-        boolean alt = (e.getModifiers() & (ActionEvent.ALT_MASK|InputEvent.ALT_GRAPH_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
@@ -359,7 +372,7 @@
         didMove = false;
         initialMoveThresholdExceeded = false;
 
-        Collection<OsmPrimitive> osmColl = getNearestCollectionVirtual(e.getPoint(), alt);
+        Collection<OsmPrimitive> osmColl = getNearestCollectionVirtual(e.getPoint());
 
         if (ctrl && shift) {
             if (getCurrentDataSet().getSelected().isEmpty()) {
@@ -382,8 +395,8 @@
             selectionManager.register(Main.map.mapView);
             selectionManager.mousePressed(e);
         }
-        if(mode != Mode.move || shift || ctrl)
-        {
+
+        if(mode != Mode.move || shift || ctrl) {
             virtualNode = null;
             virtualWays.clear();
         }
@@ -420,14 +433,30 @@
         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.rotates", false);
+
+            virtualWays.clear();
+            virtualNode = null;
+
             if (!didMove) {
-                selectPrims(
-                        Main.map.mapView.getNearestCollection(e.getPoint(), OsmPrimitive.isSelectablePredicate),
-                        shift, ctrl, true, false);
+                Collection<OsmPrimitive> c = Main.map.mapView.getNearestCollection(e.getPoint(), OsmPrimitive.isSelectablePredicate);
+                if (!c.isEmpty() && alt) {
+                    if (c.iterator().next() instanceof Node) {
+                        // there is at least one node under the cursor:
+                        //   - make sure (first element of new list) equals (result of getNearestCollection)
+                        //   - do not consider ways at all, but all nearest nodes
+                        c = new ArrayList<OsmPrimitive>(Main.map.mapView.getNearestNodes(e.getPoint(), OsmPrimitive.isSelectablePredicate));
+                    } else {
+                        // consider all ways..
+                        c = Main.map.mapView.getAllNearest(e.getPoint(), OsmPrimitive.isSelectablePredicate);
+                    }
+                }
+                selectPrims(c, shift, ctrl, true, false);
 
                 // If the user double-clicked a node, change to draw mode
-                List<OsmPrimitive> sel = new ArrayList<OsmPrimitive>(getCurrentDataSet().getSelected());
-                if(e.getClickCount() >=2 && sel.size() == 1 && sel.get(0) instanceof Node) {
+                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(){
@@ -500,12 +529,77 @@
         selectPrims(selectionManager.getObjectsInRectangle(r, alt), shift, ctrl, 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();
-        if ((shift && ctrl) || (ctrl && !released))
+        if ((shift && ctrl) || (ctrl && !released) || (!virtualWays.isEmpty()))
             return; // not allowed together
 
+        for (OsmPrimitive p : selectionList) {
+            System.out.println("SelectAction:selectPrims(): " + p.getId());
+        }
+
+        // toggle through possible objects on mouse release
+        if (released && !area) {
+            if (selectionList.size() > 1) {
+                Collection<OsmPrimitive> coll = ds.getSelected();
+
+                OsmPrimitive first, foundInDS, node, nxt;
+                first = nxt = selectionList.iterator().next();
+                foundInDS = node = null;
+
+                for (Iterator<OsmPrimitive> i = selectionList.iterator(); i.hasNext(); ) {
+                    if (selMorePrims && shift) {
+                        if (!coll.contains(nxt = i.next())) {
+                            System.out.println("SelectAction:selectPrims(): taking " + nxt.getId());
+                            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;
+                            }
+                            System.out.println("SelectAction:selectPrims(): taking " + nxt.getId());
+                            break; // take next primitive of selList
+                        } else if (nxt instanceof Node && node == null) {
+                            node = nxt;
+                        }
+                    }
+                }
+
+                if (ctrl) {
+                    if (foundInDS != null) {
+                        // a member of selList was foundInDS
+                        if (!selectionList.contains(selCycleStart)) {
+                            selCycleStart = foundInDS;
+                            System.out.println("SelectAction:selectPrims(): cycleStart " + selCycleStart.getId());
+                        }
+                        // 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;
+                        System.out.println("SelectAction:selectPrims(): cycleStart " + selCycleStart.getId());
+                    }
+                }
+
+                selectionList = new ArrayList<OsmPrimitive>(1); // do not modify the passed object..
+                selectionList.add(nxt);
+                System.out.println("SelectAction:selectPrims(): truncated selList to id=" + nxt.getId());
+            }
+        }
+
+        // hard-wiring to false due to performance reasons, should do w/out
+        selMorePrims = (released || area) ? false : ds.getSelected().containsAll(selectionList);
+
         if (ctrl) {
             // Ctrl on an item toggles its selection status,
             // but Ctrl on an *area* just clears those items
