001//License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trn; 007 008import java.awt.event.ActionEvent; 009import java.awt.event.KeyEvent; 010import java.util.ArrayList; 011import java.util.Collection; 012import java.util.Collections; 013import java.util.Iterator; 014import java.util.List; 015 016import javax.swing.JOptionPane; 017 018import org.openstreetmap.josm.Main; 019import org.openstreetmap.josm.command.RemoveNodesCommand; 020import org.openstreetmap.josm.command.Command; 021import org.openstreetmap.josm.data.osm.Node; 022import org.openstreetmap.josm.data.osm.OsmPrimitive; 023import org.openstreetmap.josm.data.osm.Way; 024import org.openstreetmap.josm.tools.Shortcut; 025 026public class UnJoinNodeWayAction extends JosmAction { 027 public UnJoinNodeWayAction() { 028 super(tr("Disconnect Node from Way"), "unjoinnodeway", 029 tr("Disconnect nodes from a way they currently belong to"), 030 Shortcut.registerShortcut("tools:unjoinnodeway", 031 tr("Tool: {0}", tr("Disconnect Node from Way")), KeyEvent.VK_J, Shortcut.ALT), true); 032 putValue("help", ht("/Action/UnJoinNodeWay")); 033 } 034 035 @Override 036 public void actionPerformed(ActionEvent e) { 037 038 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected(); 039 040 List<Node> selectedNodes = OsmPrimitive.getFilteredList(selection, Node.class); 041 List<Way> selectedWays = OsmPrimitive.getFilteredList(selection, Way.class); 042 List<Way> applicableWays = getApplicableWays(selectedWays, selectedNodes); 043 044 if (applicableWays == null) { 045 JOptionPane.showMessageDialog( 046 Main.parent, 047 tr("Select at least one node to be disconnected."), 048 tr("Warning"), 049 JOptionPane.WARNING_MESSAGE); 050 return; 051 } else if (applicableWays.isEmpty()) { 052 JOptionPane.showMessageDialog(Main.parent, 053 trn("Selected node cannot be disconnected from anything.", 054 "Selected nodes cannot be disconnected from anything.", 055 selectedNodes.size()), 056 tr("Warning"), 057 JOptionPane.WARNING_MESSAGE); 058 return; 059 } else if (applicableWays.size() > 1) { 060 JOptionPane.showMessageDialog(Main.parent, 061 trn("There is more than one way using the node you selected. Please select the way also.", 062 "There is more than one way using the nodes you selected. Please select the way also.", 063 selectedNodes.size()), 064 tr("Warning"), 065 JOptionPane.WARNING_MESSAGE); 066 return; 067 } else if (applicableWays.get(0).getRealNodesCount() < selectedNodes.size() + 2) { 068 // there is only one affected way, but removing the selected nodes would only leave it 069 // with less than 2 nodes 070 JOptionPane.showMessageDialog(Main.parent, 071 trn("The affected way would disappear after disconnecting the selected node.", 072 "The affected way would disappear after disconnecting the selected nodes.", 073 selectedNodes.size()), 074 tr("Warning"), 075 JOptionPane.WARNING_MESSAGE); 076 return; 077 } 078 079 080 // Finally, applicableWays contains only one perfect way 081 Way selectedWay = applicableWays.get(0); 082 083 // I'm sure there's a better way to handle this 084 Main.main.undoRedo.add(new RemoveNodesCommand(selectedWay, selectedNodes)); 085 Main.map.repaint(); 086 } 087 088 // Find ways to which the disconnect can be applied. This is the list of ways with more 089 // than two nodes which pass through all the given nodes, intersected with the selected ways (if any) 090 private List<Way> getApplicableWays(List<Way> selectedWays, List<Node> selectedNodes) { 091 if (selectedNodes.isEmpty()) 092 return null; 093 094 // List of ways shared by all nodes 095 List<Way> result = new ArrayList<Way>(OsmPrimitive.getFilteredList(selectedNodes.get(0).getReferrers(), Way.class)); 096 for (int i=1; i<selectedNodes.size(); i++) { 097 List<OsmPrimitive> ref = selectedNodes.get(i).getReferrers(); 098 for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { 099 if (!ref.contains(it.next())) { 100 it.remove(); 101 } 102 } 103 } 104 105 // Remove broken ways 106 for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { 107 if (it.next().getNodesCount() <= 2) { 108 it.remove(); 109 } 110 } 111 112 if (selectedWays.isEmpty()) 113 return result; 114 else { 115 // Return only selected ways 116 for (Iterator<Way> it = result.iterator(); it.hasNext(); ) { 117 if (!selectedWays.contains(it.next())) { 118 it.remove(); 119 } 120 } 121 return result; 122 } 123 } 124 125 @Override 126 protected void updateEnabledState() { 127 if (getCurrentDataSet() == null) { 128 setEnabled(false); 129 } else { 130 updateEnabledState(getCurrentDataSet().getSelected()); 131 } 132 } 133 134 @Override 135 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 136 setEnabled(selection != null && !selection.isEmpty()); 137 } 138}