Index: trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 7300)
+++ trunk/src/org/openstreetmap/josm/actions/JoinNodeWayAction.java	(revision 7302)
@@ -19,4 +19,5 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
+import java.util.Comparator;
 import java.util.Collection;
 import java.util.Collections;
@@ -24,4 +25,5 @@
 import java.util.List;
 import java.util.Map;
+import java.util.HashMap;
 import java.util.Set;
 import java.util.SortedSet;
@@ -66,10 +68,6 @@
             return;
         Collection<Node> selectedNodes = getCurrentDataSet().getSelectedNodes();
-        // Allow multiple selected nodes too?
-        if (selectedNodes.size() != 1) return;
-
-        final Node node = selectedNodes.iterator().next();
-
         Collection<Command> cmds = new LinkedList<>();
+        Map<Way, MultiMap<Integer, Node>> data = new HashMap<>();
 
         // If the user has selected some ways, only join the node to these.
@@ -77,38 +75,66 @@
                 !getCurrentDataSet().getSelectedWays().isEmpty();
 
-        List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
-                Main.map.mapView.getPoint(node), OsmPrimitive.isSelectablePredicate);
-        MultiMap<Way, Integer> insertPoints = new MultiMap<>();
-        for (WaySegment ws : wss) {
-            // Maybe cleaner to pass a "isSelected" predicate to getNearestWaySegments, but this is less invasive.
-            if (restrictToSelectedWays && !ws.way.isSelected()) {
-                continue;
-            }
-
-            if (ws.getFirstNode() != node && ws.getSecondNode() != node) {
-                insertPoints.put(ws.way, ws.lowerIndex);
-            }
-        }
-
-        for (Map.Entry<Way, Set<Integer>> entry : insertPoints.entrySet()) {
+        // Planning phase: decide where we'll insert the nodes and put it all in "data"
+        for (Node node : selectedNodes) {
+            List<WaySegment> wss = Main.map.mapView.getNearestWaySegments(
+                    Main.map.mapView.getPoint(node), OsmPrimitive.isSelectablePredicate);
+
+            MultiMap<Way, Integer> insertPoints = new MultiMap<>();
+            for (WaySegment ws : wss) {
+                // Maybe cleaner to pass a "isSelected" predicate to getNearestWaySegments, but this is less invasive.
+                if (restrictToSelectedWays && !ws.way.isSelected()) {
+                    continue;
+                }
+
+                if (ws.getFirstNode() != node && ws.getSecondNode() != node) {
+                    insertPoints.put(ws.way, ws.lowerIndex);
+                }
+            }
+            for (Map.Entry<Way, Set<Integer>> entry : insertPoints.entrySet()) {
+                final Way w = entry.getKey();
+                final Set<Integer> insertPointsForWay = entry.getValue();
+                for (int i : pruneSuccs(insertPointsForWay)) {
+                    MultiMap<Integer, Node> innerMap;
+                    if (!data.containsKey(w)) {
+                        innerMap = new MultiMap<>();
+                    } else {
+                        innerMap = data.get(w);
+                    }
+                    innerMap.put(i, node);
+                    data.put(w, innerMap);
+                }
+            }
+        }
+
+        // Execute phase: traverse the structure "data" and finally put the nodes into place
+        for (Map.Entry<Way, MultiMap<Integer, Node>> entry : data.entrySet()) {
             final Way w = entry.getKey();
-            final Set<Integer> insertPointsForWay = entry.getValue();
-            if (insertPointsForWay.isEmpty()) {
-                continue;
-            }
-
-            List<Node> nodesToAdd = w.getNodes();
-            for (int i : pruneSuccsAndReverse(insertPointsForWay)) {
+            final MultiMap<Integer, Node> innerEntry = entry.getValue();
+
+            List<Integer> segmentIndexes = new LinkedList<>();
+            segmentIndexes.addAll(innerEntry.keySet());
+            Collections.sort(segmentIndexes, Collections.reverseOrder());
+
+            List<Node> wayNodes = w.getNodes();
+            for (Integer segmentIndex : segmentIndexes) {
+                final Set<Node> nodesInSegment = innerEntry.get(segmentIndex);
                 if (joinWayToNode) {
-                    EastNorth newPosition = Geometry.closestPointToSegment(
-                            w.getNode(i).getEastNorth(), w.getNode(i + 1).getEastNorth(), node.getEastNorth());
-                    cmds.add(new MoveCommand(node, Projections.inverseProject(newPosition)));
-                }
-                nodesToAdd.add(i + 1, node);
+                    for (Node node : nodesInSegment) {
+                        EastNorth newPosition = Geometry.closestPointToSegment(w.getNode(segmentIndex).getEastNorth(),
+                                                                            w.getNode(segmentIndex+1).getEastNorth(),
+                                                                            node.getEastNorth());
+                        cmds.add(new MoveCommand(node, Projections.inverseProject(newPosition)));
+                    }
+                }
+                List<Node> nodesToAdd = new LinkedList<>();
+                nodesToAdd.addAll(nodesInSegment);
+                Collections.sort(nodesToAdd, new NodeDistanceToRefNodeComparator(w.getNode(segmentIndex), w.getNode(segmentIndex+1), !joinWayToNode));
+                wayNodes.addAll(segmentIndex + 1, nodesToAdd);
             }
             Way wnew = new Way(w);
-            wnew.setNodes(nodesToAdd);
+            wnew.setNodes(wayNodes);
             cmds.add(new ChangeCommand(w, wnew));
         }
+
         if (cmds.isEmpty()) return;
         Main.main.undoRedo.add(new SequenceCommand(getValue(NAME).toString(), cmds));
@@ -116,6 +142,6 @@
     }
 
-    private static SortedSet<Integer> pruneSuccsAndReverse(Collection<Integer> is) {
-        SortedSet<Integer> is2 = new TreeSet<>(Collections.reverseOrder());
+    private static SortedSet<Integer> pruneSuccs(Collection<Integer> is) {
+        SortedSet<Integer> is2 = new TreeSet<>();
         for (int i : is) {
             if (!is2.contains(i - 1) && !is2.contains(i + 1)) {
@@ -124,4 +150,40 @@
         }
         return is2;
+    }
+
+    /**
+     * Sorts collinear nodes by their distance to a common reference node.
+     */
+    private static class NodeDistanceToRefNodeComparator implements Comparator<Node> {
+        private final EastNorth refPoint;
+        private EastNorth refPoint2;
+        private final boolean projectToSegment;
+        NodeDistanceToRefNodeComparator(Node referenceNode) {
+            refPoint = referenceNode.getEastNorth();
+            projectToSegment = false;
+        }
+        NodeDistanceToRefNodeComparator(Node referenceNode, Node referenceNode2, boolean projectFirst) {
+            refPoint = referenceNode.getEastNorth();
+            refPoint2 = referenceNode2.getEastNorth();
+            projectToSegment = projectFirst;
+        }
+        @Override
+        public int compare(Node first, Node second) {
+            EastNorth firstPosition = first.getEastNorth();
+            EastNorth secondPosition = second.getEastNorth();
+
+            if (projectToSegment) {
+                firstPosition = Geometry.closestPointToSegment(refPoint, refPoint2, firstPosition);
+                secondPosition = Geometry.closestPointToSegment(refPoint, refPoint2, secondPosition);
+            }
+
+            double distanceFirst = firstPosition.distance(refPoint);
+            double distanceSecond = secondPosition.distance(refPoint);
+            double difference =  distanceFirst - distanceSecond;
+
+            if (difference > 0.0) return 1;
+            if (difference < 0.0) return -1;
+            return 0;
+        }
     }
 
