source: josm/trunk/src/org/openstreetmap/josm/actions/mapmode/DeleteAction.java@ 582

Last change on this file since 582 was 582, checked in by framm, 16 years ago
  • got rid of a few compiler warnings
File size: 8.4 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions.mapmode;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.awt.event.MouseEvent;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.HashSet;
13import java.util.HashMap;
14import java.util.LinkedList;
15import java.util.List;
16
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.command.*;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Way;
24import org.openstreetmap.josm.data.osm.WaySegment;
25import org.openstreetmap.josm.data.osm.Relation;
26import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
27import org.openstreetmap.josm.gui.MapFrame;
28import org.openstreetmap.josm.tools.*;
29
30/**
31 * An action that enables the user to delete nodes and other objects.
32 *
33 * The user can click on an object, which get deleted if possible. When Ctrl is
34 * pressed when releasing the button, the objects and all its references are
35 * deleted. The exact definition of "all its references" are in
36 * @see #deleteWithReferences(OsmPrimitive)
37 *
38 * If the user did not press Ctrl and the object has any references, the user
39 * is informed and nothing is deleted.
40 *
41 * If the user enters the mapmode and any object is selected, all selected
42 * objects that can be deleted will.
43 *
44 * @author imi
45 */
46public class DeleteAction extends MapMode {
47
48 /**
49 * Construct a new DeleteAction. Mnemonic is the delete - key.
50 * @param mapFrame The frame this action belongs to.
51 */
52 public DeleteAction(MapFrame mapFrame) {
53 super(tr("Delete"),
54 "delete",
55 tr("Delete nodes or ways."),
56 KeyEvent.VK_D,
57 mapFrame,
58 ImageProvider.getCursor("normal", "delete"));
59 }
60
61 @Override public void enterMode() {
62 super.enterMode();
63 Main.map.mapView.addMouseListener(this);
64 }
65
66 @Override public void exitMode() {
67 super.exitMode();
68 Main.map.mapView.removeMouseListener(this);
69 }
70
71
72 @Override public void actionPerformed(ActionEvent e) {
73 super.actionPerformed(e);
74 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
75 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
76
77 Command c;
78 if (ctrl) {
79 c = deleteWithReferences(Main.ds.getSelected());
80 } else {
81 c = delete(Main.ds.getSelected(), !alt);
82 }
83 if (c != null) {
84 Main.main.undoRedo.add(c);
85 }
86
87 Main.map.repaint();
88 }
89
90 /**
91 * If user clicked with the left button, delete the nearest object.
92 * position.
93 */
94 @Override public void mouseClicked(MouseEvent e) {
95 if (e.getButton() != MouseEvent.BUTTON1)
96 return;
97 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
98 boolean shift = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
99 boolean alt = (e.getModifiers() & ActionEvent.ALT_MASK) != 0;
100
101 OsmPrimitive sel = Main.map.mapView.getNearestNode(e.getPoint());
102 Command c = null;
103 if (sel == null) {
104 WaySegment ws = Main.map.mapView.getNearestWaySegment(e.getPoint());
105 if (ws != null) {
106 if (shift) {
107 c = deleteWaySegment(ws);
108 } else if (ctrl) {
109 c = deleteWithReferences(Collections.singleton((OsmPrimitive)ws.way));
110 } else {
111 c = delete(Collections.singleton((OsmPrimitive)ws.way), !alt);
112 }
113 }
114 } else if (ctrl) {
115 c = deleteWithReferences(Collections.singleton(sel));
116 } else {
117 c = delete(Collections.singleton(sel), !alt);
118 }
119 if (c != null) {
120 Main.main.undoRedo.add(c);
121 }
122
123 Main.map.mapView.repaint();
124 }
125
126 /**
127 * Delete the primitives and everything they reference.
128 *
129 * If a node is deleted, the node and all ways and relations
130 * the node is part of are deleted as well.
131 *
132 * If a way is deleted, all relations the way is member of are also deleted.
133 *
134 * If a way is deleted, only the way and no nodes are deleted.
135 *
136 * @param selection The list of all object to be deleted.
137 * @return command A command to perform the deletions, or null of there is
138 * nothing to delete.
139 */
140 private Command deleteWithReferences(Collection<OsmPrimitive> selection) {
141 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
142 for (OsmPrimitive osm : selection)
143 osm.visit(v);
144 v.data.addAll(selection);
145 if (v.data.isEmpty())
146 return null;
147 return new DeleteCommand(v.data);
148 }
149
150 /**
151 * Try to delete all given primitives.
152 *
153 * If a node is used by a way, it's removed from that way. If a node or a
154 * way is used by a relation, inform the user and do not delete.
155 *
156 * If this would cause ways with less than 2 nodes to be created, delete
157 * these ways instead. If they are part of a relation, inform the user
158 * and do not delete.
159 *
160 * @param selection The objects to delete.
161 * @param alsoDeleteNodesInWay true if nodes should be deleted as well
162 * @return command A command to perform the deletions, or null of there is
163 * nothing to delete.
164 */
165 private Command delete(Collection<OsmPrimitive> selection, boolean alsoDeleteNodesInWay) {
166 if (selection.isEmpty()) return null;
167
168 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>(selection);
169 Collection<Way> waysToBeChanged = new HashSet<Way>();
170
171 if (alsoDeleteNodesInWay) {
172 // Delete untagged nodes that are to be unreferenced.
173 Collection<OsmPrimitive> delNodes = new HashSet<OsmPrimitive>();
174 for (OsmPrimitive osm : del) {
175 if (osm instanceof Way) {
176 for (Node n : ((Way)osm).nodes) {
177 if (!n.tagged) {
178 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
179 n.visit(v);
180 v.data.removeAll(del);
181 if (v.data.isEmpty()) {
182 delNodes.add(n);
183 }
184 }
185 }
186 }
187 }
188 del.addAll(delNodes);
189 }
190
191 for (OsmPrimitive osm : del) {
192 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
193 osm.visit(v);
194 for (OsmPrimitive ref : v.data) {
195 if (del.contains(ref)) continue;
196 if (ref instanceof Way) {
197 waysToBeChanged.add((Way) ref);
198 } else if (ref instanceof Relation) {
199 JOptionPane.showMessageDialog(Main.parent,
200 tr("Cannot delete: Selection is used by relation"));
201 return null;
202 } else {
203 return null;
204 }
205 }
206 }
207
208 Collection<Command> cmds = new LinkedList<Command>();
209 for (Way w : waysToBeChanged) {
210 Way wnew = new Way(w);
211 wnew.nodes.removeAll(del);
212 if (wnew.nodes.size() < 2) {
213 del.add(w);
214
215 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds, false);
216 w.visit(v);
217 for (OsmPrimitive ref : v.data) {
218 if (del.contains(ref)) continue;
219 if (ref instanceof Relation) {
220 JOptionPane.showMessageDialog(Main.parent,
221 tr("Cannot delete: Selection is used by relation"));
222 } else {
223 return null;
224 }
225 }
226 } else {
227 cmds.add(new ChangeCommand(w, wnew));
228 }
229 }
230
231 if (!del.isEmpty()) cmds.add(new DeleteCommand(del));
232
233 return new SequenceCommand(tr("Delete"), cmds);
234 }
235
236 private Command deleteWaySegment(WaySegment ws) {
237 List<Node> n1 = new ArrayList<Node>(),
238 n2 = new ArrayList<Node>();
239
240 n1.addAll(ws.way.nodes.subList(0, ws.lowerIndex + 1));
241 n2.addAll(ws.way.nodes.subList(ws.lowerIndex + 1, ws.way.nodes.size()));
242
243 if (n1.size() < 2 && n2.size() < 2) {
244 return new DeleteCommand(Collections.singleton(ws.way));
245 }
246
247 Way wnew = new Way(ws.way);
248 wnew.nodes.clear();
249
250 if (n1.size() < 2) {
251 wnew.nodes.addAll(n2);
252 return new ChangeCommand(ws.way, wnew);
253 } else if (n2.size() < 2) {
254 wnew.nodes.addAll(n1);
255 return new ChangeCommand(ws.way, wnew);
256 } else {
257 Collection<Command> cmds = new LinkedList<Command>();
258
259 wnew.nodes.addAll(n1);
260 cmds.add(new ChangeCommand(ws.way, wnew));
261
262 Way wnew2 = new Way();
263 if (wnew.keys != null) {
264 wnew2.keys = new HashMap<String, String>(wnew.keys);
265 wnew2.checkTagged();
266 }
267 wnew2.nodes.addAll(n2);
268 cmds.add(new AddCommand(wnew2));
269
270 return new SequenceCommand(tr("Split way segment"), cmds);
271 }
272 }
273
274 @Override public String getModeHelpText() {
275 return tr("Click to delete. Shift: delete way segment. Alt: don't delete unused nodes when deleting a way. Ctrl: delete referring objects.");
276 }
277}
Note: See TracBrowser for help on using the repository browser.