source: josm/trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java@ 1023

Last change on this file since 1023 was 1023, checked in by stoecker, 16 years ago

close bug #1622. Keyboard shortcuts and specific OS handling

File size: 5.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.LinkedList;
12import java.util.List;
13
14import javax.swing.JOptionPane;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.command.AddCommand;
18import org.openstreetmap.josm.command.ChangeCommand;
19import org.openstreetmap.josm.command.Command;
20import org.openstreetmap.josm.command.SequenceCommand;
21import org.openstreetmap.josm.data.SelectionChangedListener;
22import org.openstreetmap.josm.data.osm.DataSet;
23import org.openstreetmap.josm.data.osm.Node;
24import org.openstreetmap.josm.data.osm.OsmPrimitive;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.RelationMember;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.tools.ShortCut;
29
30/**
31 * Dupe a node that is used my multiple ways, so each way has its own node.
32 *
33 * Resulting nodes are identical, up to their position.
34 *
35 * This is the opposite of the MergeNodesAction.
36 */
37
38public class UnGlueAction extends JosmAction implements SelectionChangedListener {
39
40 private Node selectedNode;
41 private Way selectedWay;
42
43 /**
44 * Create a new SplitWayAction.
45 */
46 public UnGlueAction() {
47 super(tr("UnGlue Ways"), "unglueways", tr("Duplicate the selected node so each way using it has its own copy."),
48 ShortCut.registerShortCut("tools:unglue", tr("Tool: Unglue"), KeyEvent.VK_G, ShortCut.GROUP_EDIT), true);
49 DataSet.selListeners.add(this);
50 }
51
52 /**
53 * Called when the action is executed.
54 *
55 * This method just collects the single node selected and calls the unGlueWay method.
56 */
57 public void actionPerformed(ActionEvent e) {
58
59 Collection<OsmPrimitive> selection = Main.ds.getSelected();
60
61 if (!checkSelection(selection)) {
62 JOptionPane.showMessageDialog(Main.parent, tr("The current selection cannot be used for unglueing."));
63 return;
64 }
65
66 int count = 0;
67 for (Way w : Main.ds.ways) {
68 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
69 if (!w.nodes.contains(selectedNode)) continue;
70 count++;
71 }
72 if (count < 2) {
73 JOptionPane.showMessageDialog(Main.parent, tr("You must select a node that is used by at least 2 ways."));
74 return;
75 }
76
77 // and then do the work.
78 unglueWays();
79 }
80
81 /**
82 * Checks if the selection consists of something we can work with.
83 * Checks only if the number and type of items selected looks good;
84 * does not check whether the selected items are really a valid
85 * input for splitting (this would be too expensive to be carried
86 * out from the selectionChanged listener).
87 */
88 private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
89
90 int size = selection.size();
91 if (size < 1 || size > 2)
92 return false;
93
94 selectedNode = null;
95 selectedWay = null;
96
97 for (OsmPrimitive p : selection) {
98 if (p instanceof Node) {
99 selectedNode = (Node) p;
100 if (size == 1 || selectedWay != null)
101 return size == 1 || selectedWay.nodes.contains(selectedNode);
102 } else if (p instanceof Way) {
103 selectedWay = (Way) p;
104 if (size == 2 && selectedNode != null)
105 return selectedWay.nodes.contains(selectedNode);
106 }
107 }
108
109 return false;
110 }
111
112 private boolean modifyWay(boolean firstway, Way w, List<Command> cmds,
113 List<Node> newNodes) {
114 ArrayList<Node> nn = new ArrayList<Node>();
115 for (Node pushNode : w.nodes) {
116 if (selectedNode == pushNode) {
117 if (firstway) {
118 // reuse the old node for the first (==a random) way
119 firstway = false;
120 } else {
121 // clone the node for all other ways
122 pushNode = new Node(selectedNode);
123 pushNode.id = 0;
124 newNodes.add(pushNode);
125 cmds.add(new AddCommand(pushNode));
126 }
127 }
128 nn.add(pushNode);
129 }
130 Way newWay = new Way(w);
131 newWay.nodes.clear();
132 newWay.nodes.addAll(nn);
133 cmds.add(new ChangeCommand(w, newWay));
134
135 return firstway;
136 }
137
138 /**
139 * see above
140 */
141 private void unglueWays() {
142
143 LinkedList<Command> cmds = new LinkedList<Command>();
144 List<Node> newNodes = new LinkedList<Node>();
145
146 if (selectedWay == null) {
147
148 boolean firstway = true;
149 // modify all ways containing the nodes
150 for (Way w : Main.ds.ways) {
151 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
152 if (!w.nodes.contains(selectedNode)) continue;
153
154 firstway = modifyWay(firstway, w, cmds, newNodes);
155 }
156 } else {
157 modifyWay(false, selectedWay, cmds, newNodes);
158 }
159
160 // modify all relations containing the node
161 Relation newRel = null;
162 HashSet<String> rolesToReAdd = null;
163 for (Relation r : Main.ds.relations) {
164 if (r.deleted || r.incomplete) continue;
165 newRel = null;
166 rolesToReAdd = null;
167 for (RelationMember rm : r.members) {
168 if (rm.member instanceof Node) {
169 if (rm.member == selectedNode) {
170 if (newRel == null) {
171 newRel = new Relation(r);
172 newRel.members.clear();
173 rolesToReAdd = new HashSet<String>();
174 }
175 rolesToReAdd.add(rm.role);
176 }
177 }
178 }
179 if (newRel != null) {
180 for (RelationMember rm : r.members) {
181 //if (rm.member != selectedNode) {
182 newRel.members.add(rm);
183 //}
184 }
185 for (Node n : newNodes) {
186 for (String role : rolesToReAdd) {
187 newRel.members.add(new RelationMember(role, n));
188 }
189 }
190 cmds.add(new ChangeCommand(r, newRel));
191 }
192 }
193
194 newNodes.add(selectedNode); // just for the next 2 lines
195 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe into {0} nodes", newNodes.size()), cmds));
196 Main.ds.setSelected(newNodes);
197
198 }
199
200 /**
201 * Enable the "split way" menu option if the selection looks like we could use it.
202 */
203 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
204 setEnabled(checkSelection(newSelection));
205 }
206}
Note: See TracBrowser for help on using the repository browser.