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

Last change on this file since 298 was 298, checked in by imi, 17 years ago
  • added license description to head of each source file
File size: 7.2 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.Map.Entry;
16
17import javax.swing.JOptionPane;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.command.ChangeCommand;
21import org.openstreetmap.josm.command.Command;
22import org.openstreetmap.josm.command.DeleteCommand;
23import org.openstreetmap.josm.command.SequenceCommand;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Segment;
27import org.openstreetmap.josm.data.osm.Way;
28import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
29import org.openstreetmap.josm.gui.MapFrame;
30import org.openstreetmap.josm.tools.ImageProvider;
31
32/**
33 * An action that enables the user to delete nodes and other objects.
34 *
35 * The user can click on an object, which get deleted if possible. When Ctrl is
36 * pressed when releasing the button, the objects and all its references are
37 * deleted. The exact definition of "all its references" are in
38 * @see #deleteWithReferences(OsmPrimitive)
39 *
40 * Pressing Alt will select the way instead of a segment, as usual.
41 *
42 * If the user did not press Ctrl and the object has any references, the user
43 * is informed and nothing is deleted.
44 *
45 * If the user enters the mapmode and any object is selected, all selected
46 * objects that can be deleted will.
47 *
48 * @author imi
49 */
50public class DeleteAction extends MapMode {
51
52 /**
53 * Construct a new DeleteAction. Mnemonic is the delete - key.
54 * @param mapFrame The frame this action belongs to.
55 */
56 public DeleteAction(MapFrame mapFrame) {
57 super(tr("Delete"),
58 "delete",
59 tr("Delete nodes, streets or segments."),
60 KeyEvent.VK_D,
61 mapFrame,
62 ImageProvider.getCursor("normal", "delete"));
63 }
64
65 @Override public void enterMode() {
66 super.enterMode();
67 Main.map.mapView.addMouseListener(this);
68 }
69
70 @Override public void exitMode() {
71 super.exitMode();
72 Main.map.mapView.removeMouseListener(this);
73 }
74
75
76 @Override public void actionPerformed(ActionEvent e) {
77 super.actionPerformed(e);
78 boolean ctrl = (e.getModifiers() & ActionEvent.CTRL_MASK) != 0;
79 if (ctrl)
80 deleteWithReferences(Main.ds.getSelected());
81 else
82 delete(Main.ds.getSelected(), false, false);
83 Main.map.repaint();
84 }
85
86 /**
87 * If user clicked with the left button, delete the nearest object.
88 * position.
89 */
90 @Override public void mouseClicked(MouseEvent e) {
91 if (e.getButton() != MouseEvent.BUTTON1)
92 return;
93
94 OsmPrimitive sel = Main.map.mapView.getNearest(e.getPoint(), (e.getModifiersEx() & MouseEvent.ALT_DOWN_MASK) != 0);
95 if (sel == null)
96 return;
97
98 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0)
99 deleteWithReferences(Collections.singleton(sel));
100 else
101 delete(Collections.singleton(sel), true, true);
102
103 Main.map.mapView.repaint();
104 }
105
106 /**
107 * Delete the primitives and everything they references.
108 *
109 * If a node is deleted, the node and all segments, ways and areas
110 * the node is part of are deleted as well.
111 *
112 * If a segment is deleted, all ways the segment is part of
113 * are deleted as well. No nodes are deleted.
114 *
115 * If a way is deleted, only the way and no segments or nodes are
116 * deleted.
117 *
118 * If an area is deleted, only the area gets deleted.
119 *
120 * @param selection The list of all object to be deleted.
121 */
122 private void deleteWithReferences(Collection<OsmPrimitive> selection) {
123 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
124 for (OsmPrimitive osm : selection)
125 osm.visit(v);
126 v.data.addAll(selection);
127 if (!v.data.isEmpty())
128 Main.main.editLayer().add(new DeleteCommand(v.data));
129 }
130
131 /**
132 * Try to delete all given primitives. If a primitive is
133 * used somewhere and that "somewhere" is not going to be deleted,
134 * inform the user and do not delete.
135 *
136 * If deleting a node which is part of exactly two segments, and both segments
137 * have no conflicting keys, join them and remove the node.
138 * If the two segments are part of the same way, remove the deleted segment
139 * from the way.
140 *
141 * @param selection The objects to delete.
142 * @param msgBox Whether a message box for errors should be shown
143 */
144 private void delete(Collection<OsmPrimitive> selection, boolean msgBox, boolean joinIfPossible) {
145 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>();
146 for (OsmPrimitive osm : selection) {
147 CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(Main.ds);
148 osm.visit(v);
149 if (!selection.containsAll(v.data)) {
150 if (osm instanceof Node && joinIfPossible) {
151 String reason = deleteNodeAndJoinSegment((Node)osm);
152 if (reason != null && msgBox) {
153 JOptionPane.showMessageDialog(Main.parent,tr("Cannot delete node.")+" "+reason);
154 return;
155 }
156 } else if (msgBox) {
157 JOptionPane.showMessageDialog(Main.parent, tr("This object is in use."));
158 return;
159 }
160 } else {
161 del.addAll(v.data);
162 del.add(osm);
163 }
164 }
165 if (!del.isEmpty())
166 Main.main.editLayer().add(new DeleteCommand(del));
167 }
168
169 private String deleteNodeAndJoinSegment(Node n) {
170 ArrayList<Segment> segs = new ArrayList<Segment>(2);
171 for (Segment s : Main.ds.segments) {
172 if (!s.deleted && (s.from == n || s.to == n)) {
173 if (segs.size() > 1)
174 return tr("Used by more than two segments.");
175 segs.add(s);
176 }
177 }
178 if (segs.size() != 2)
179 return tr("Used by only one segment.");
180 Segment seg1 = segs.get(0);
181 Segment seg2 = segs.get(1);
182 if (seg1.from == seg2.to) {
183 Segment s = seg1;
184 seg1 = seg2;
185 seg2 = s;
186 }
187 if (seg1.from == seg2.from || seg1.to == seg2.to)
188 return tr("Wrong direction of segments.");
189 for (Entry<String, String> e : seg1.entrySet())
190 if (seg2.keySet().contains(e.getKey()) && !seg2.get(e.getKey()).equals(e.getValue()))
191 return tr("Conflicting keys");
192 ArrayList<Way> ways = new ArrayList<Way>(2);
193 for (Way w : Main.ds.ways) {
194 if (w.deleted)
195 continue;
196 if ((w.segments.contains(seg1) && !w.segments.contains(seg2)) || (w.segments.contains(seg2) && !w.segments.contains(seg1)))
197 return tr("Segments are part of different ways.");
198 if (w.segments.contains(seg1) && w.segments.contains(seg2))
199 ways.add(w);
200 }
201 Segment s = new Segment(seg1);
202 s.to = seg2.to;
203 if (s.keys == null)
204 s.keys = seg2.keys;
205 else if (seg2.keys != null)
206 s.keys.putAll(seg2.keys);
207 Collection<Command> cmds = new LinkedList<Command>();
208 for (Way w : ways) {
209 Way copy = new Way(w);
210 copy.segments.remove(seg2);
211 cmds.add(new ChangeCommand(w, copy));
212 }
213 cmds.add(new ChangeCommand(seg1, s));
214 cmds.add(new DeleteCommand(Arrays.asList(new OsmPrimitive[]{n, seg2})));
215 Main.main.editLayer().add(new SequenceCommand(tr("Delete Node"), cmds));
216 return null;
217 }
218}
Note: See TracBrowser for help on using the repository browser.