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

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