From 6aafda8d07eec1895f1e3e580dca8ab42ba0ded4 Mon Sep 17 00:00:00 2001
From: Giuseppe Bilotta <giuseppe.bilotta@gmail.com>
Date: Mon, 29 Jul 2013 08:29:30 +0200
Subject: [PATCH] Action to disconnect nodes from ways

---
 images/unjoinnodeway.png                           | Bin 0 -> 385 bytes
 .../josm/actions/UnJoinNodeWayAction.java          | 158 +++++++++++++++++++++
 src/org/openstreetmap/josm/gui/MainMenu.java       |   3 +
 3 files changed, 161 insertions(+)
 create mode 100644 images/unjoinnodeway.png
 create mode 100644 src/org/openstreetmap/josm/actions/UnJoinNodeWayAction.java

diff --git a/images/unjoinnodeway.png b/images/unjoinnodeway.png
new file mode 100644
index 0000000000000000000000000000000000000000..4179e6902577640d66deab6f78cab43c8b0a4786
GIT binary patch
literal 385
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b
z3=G`DAk4@xYYs>cdx@v7EBjq`Ssnvp>D3WU3=E8Xo-U3d9>?Ea-s{H{D8Tx_-@z~{
zQbL-=Y@@jL+J&1w&#Y0FW4M3d$h{-1Nl7Lf4FegKvp1VZhPx;oS@AHWxc=Ns<Kp)p
zOv7|H9x+*?<D`-oHu;T})$8@iElW0NEdT1~zxOitX|8JvOqjPf<X$oGu;iWdKtc5M
zVwqjRvx1(<nF%};*nPvjr+7<Ym-UxjYj1rq@0fj6Wy+1*t1jt(rArDLPw$A#ea<8E
zkjq~qN!|FXb4;?uTG!(h9;|K)&mVNy^}pkJPlmx9mUFvVb{fSO&gW7Q=Ha<y?WuN<
zeM(XM>PdD13OpY_bvE99@ag*bi}z>$`QKZlmY48ZeUjL|S_|fFVharAyNW+4_gt>9
cd3i{>Jl~Te!YJi0Ft8XrUHx3vIVCg!02-y3CIA2c

literal 0
HcmV?d00001

diff --git a/src/org/openstreetmap/josm/actions/UnJoinNodeWayAction.java b/src/org/openstreetmap/josm/actions/UnJoinNodeWayAction.java
new file mode 100644
index 0000000..6ffd3ed
--- /dev/null
+++ b/src/org/openstreetmap/josm/actions/UnJoinNodeWayAction.java
@@ -0,0 +1,158 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.command.ChangeNodesCommand;
+import org.openstreetmap.josm.command.Command;
+import org.openstreetmap.josm.data.osm.Node;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class UnJoinNodeWayAction extends JosmAction {
+    public UnJoinNodeWayAction() {
+        super(tr("Disconnect Node from Way"), "unjoinnodeway",
+                tr("Disconnect nodes from a way they currently belong to"),
+                Shortcut.registerShortcut("tools:unjoinnodeway",
+                    tr("Tool: {0}", tr("Disconnect Node from Way")), KeyEvent.VK_J, Shortcut.ALT), true);
+        putValue("help", ht("/Action/UnJoinNodeWay"));
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+
+        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
+
+        List<Node> selectedNodes = OsmPrimitive.getFilteredList(selection, Node.class);
+        List<Way> selectedWays = OsmPrimitive.getFilteredList(selection, Way.class);
+        List<Way> applicableWays = getApplicableWays(selectedWays, selectedNodes);
+
+        if (applicableWays == null) {
+            JOptionPane.showMessageDialog(
+                    Main.parent,
+                    tr("Select at least a node to be disconnected."),
+                    tr("Warning"),
+                    JOptionPane.WARNING_MESSAGE);
+            return;
+        } else if (applicableWays.isEmpty()) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    tr("The selected nodes do not share the same way."),
+                    tr("Warning"),
+                    JOptionPane.WARNING_MESSAGE);
+            return;
+        } else if (applicableWays.size() > 1) {
+            JOptionPane.showMessageDialog(Main.parent,
+                    trn("There is more than one way using the node you selected. Please select the way also.",
+                            "There is more than one way using the nodes you selected. Please select the way also.",
+                            selectedNodes.size()),
+                            tr("Warning"),
+                            JOptionPane.WARNING_MESSAGE);
+            return;
+        } else if (applicableWays.get(0).getRealNodesCount() < selectedNodes.size() + 2) {
+            // there is only one affected way, but removing the selected nodes would only leave it
+            // with less than 2 nodes
+            JOptionPane.showMessageDialog(Main.parent,
+                    trn("The affected way would disappear after removing the selected node.",
+                        "The affected way would disappear after removing the selected nodes.",
+                        selectedNodes.size()),
+                    tr("Warning"),
+                    JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        // Finally, applicableWays contains only one perfect way
+        Way selectedWay = applicableWays.get(0);
+        List<Node> otherNodes = selectedWay.getNodes();
+        otherNodes.removeAll(selectedNodes);
+        Main.main.undoRedo.add(new ChangeNodesCommand(selectedWay, otherNodes));
+        Main.map.repaint();
+    }
+
+    // shamefully copied over from SplitWayAction
+    private List<Way> getApplicableWays(List<Way> selectedWays, List<Node> selectedNodes) {
+        if (selectedNodes.isEmpty())
+            return null;
+
+        // Special case - one of the selected ways touches (not cross) way that we want to split
+        if (selectedNodes.size() == 1) {
+            Node n = selectedNodes.get(0);
+            List<Way> referedWays = OsmPrimitive.getFilteredList(n.getReferrers(), Way.class);
+            Way inTheMiddle = null;
+            boolean foundSelected = false;
+            for (Way w: referedWays) {
+                if (selectedWays.contains(w)) {
+                    foundSelected = true;
+                }
+                if (w.getNode(0) != n && w.getNode(w.getNodesCount() - 1) != n) {
+                    if (inTheMiddle == null) {
+                        inTheMiddle = w;
+                    } else {
+                        inTheMiddle = null;
+                        break;
+                    }
+                }
+            }
+            if (foundSelected && inTheMiddle != null)
+                return Collections.singletonList(inTheMiddle);
+        }
+
+        // List of ways shared by all nodes
+        List<Way> result = new ArrayList<Way>(OsmPrimitive.getFilteredList(selectedNodes.get(0).getReferrers(), Way.class));
+        for (int i=1; i<selectedNodes.size(); i++) {
+            List<OsmPrimitive> ref = selectedNodes.get(i).getReferrers();
+            for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
+                if (!ref.contains(it.next())) {
+                    it.remove();
+                }
+            }
+        }
+
+        // Remove broken ways
+        for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
+            if (it.next().getNodesCount() <= 2) {
+                it.remove();
+            }
+        }
+
+        if (selectedWays.isEmpty())
+            return result;
+        else {
+            // Return only selected ways
+            for (Iterator<Way> it = result.iterator(); it.hasNext(); ) {
+                if (!selectedWays.contains(it.next())) {
+                    it.remove();
+                }
+            }
+            return result;
+        }
+    }
+
+    @Override
+    protected void updateEnabledState() {
+        if (getCurrentDataSet() == null) {
+            setEnabled(false);
+        } else {
+            updateEnabledState(getCurrentDataSet().getSelected());
+        }
+    }
+
+    @Override
+    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
+        setEnabled(selection != null && !selection.isEmpty());
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/MainMenu.java b/src/org/openstreetmap/josm/gui/MainMenu.java
index 196e16e..dc75be8 100644
--- a/src/org/openstreetmap/josm/gui/MainMenu.java
+++ b/src/org/openstreetmap/josm/gui/MainMenu.java
@@ -84,6 +84,7 @@ import org.openstreetmap.josm.actions.SimplifyWayAction;
 import org.openstreetmap.josm.actions.SplitWayAction;
 import org.openstreetmap.josm.actions.ToggleGPXLinesAction;
 import org.openstreetmap.josm.actions.UnGlueAction;
+import org.openstreetmap.josm.actions.UnJoinNodeWayAction;
 import org.openstreetmap.josm.actions.UndoAction;
 import org.openstreetmap.josm.actions.UnselectAllAction;
 import org.openstreetmap.josm.actions.UpdateDataAction;
@@ -185,6 +186,7 @@ public class MainMenu extends JMenuBar {
     public final JosmAction createCircle = new CreateCircleAction();
     public final JosmAction mergeNodes = new MergeNodesAction();
     public final JosmAction joinNodeWay = new JoinNodeWayAction();
+    public final JosmAction unJoinNodeWay = new UnJoinNodeWayAction();
     public final JosmAction unglueNodes = new UnGlueAction();
     public final JosmAction simplifyWay = new SimplifyWayAction();
     public final JosmAction joinAreas = new JoinAreasAction();
@@ -603,6 +605,7 @@ public class MainMenu extends JMenuBar {
         toolsMenu.addSeparator();
         add(toolsMenu, mergeNodes);
         add(toolsMenu, joinNodeWay);
+        add(toolsMenu, unJoinNodeWay);
         add(toolsMenu, unglueNodes);
         toolsMenu.addSeparator();
         add(toolsMenu, joinAreas);
-- 
1.8.3.432.g1b9a440

