[298] | 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others
|
---|
[626] | 2 | package org.openstreetmap.josm.command;
|
---|
| 3 |
|
---|
[582] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 | import static org.openstreetmap.josm.tools.I18n.trn;
|
---|
| 6 |
|
---|
[1004] | 7 | import java.awt.GridBagLayout;
|
---|
| 8 | import java.awt.geom.Area;
|
---|
[988] | 9 | import java.util.ArrayList;
|
---|
[582] | 10 | import java.util.Collection;
|
---|
[630] | 11 | import java.util.Collections;
|
---|
[988] | 12 | import java.util.HashMap;
|
---|
| 13 | import java.util.HashSet;
|
---|
| 14 | import java.util.Iterator;
|
---|
| 15 | import java.util.LinkedList;
|
---|
| 16 | import java.util.List;
|
---|
[1989] | 17 | import java.util.Set;
|
---|
[582] | 18 |
|
---|
| 19 | import javax.swing.JLabel;
|
---|
[988] | 20 | import javax.swing.JOptionPane;
|
---|
[1004] | 21 | import javax.swing.JPanel;
|
---|
[582] | 22 | import javax.swing.tree.DefaultMutableTreeNode;
|
---|
| 23 | import javax.swing.tree.MutableTreeNode;
|
---|
| 24 |
|
---|
[988] | 25 | import org.openstreetmap.josm.Main;
|
---|
| 26 | import org.openstreetmap.josm.data.osm.Node;
|
---|
[582] | 27 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[1814] | 28 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
---|
[988] | 29 | import org.openstreetmap.josm.data.osm.Relation;
|
---|
| 30 | import org.openstreetmap.josm.data.osm.RelationMember;
|
---|
| 31 | import org.openstreetmap.josm.data.osm.Way;
|
---|
| 32 | import org.openstreetmap.josm.data.osm.WaySegment;
|
---|
| 33 | import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor;
|
---|
[1838] | 34 | import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
|
---|
[1990] | 35 | import org.openstreetmap.josm.gui.DefaultNameFormatter;
|
---|
[1397] | 36 | import org.openstreetmap.josm.gui.ExtendedDialog;
|
---|
[1856] | 37 | import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
---|
[582] | 38 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
[626] | 39 |
|
---|
| 40 | /**
|
---|
| 41 | * A command to delete a number of primitives from the dataset.
|
---|
| 42 | * @author imi
|
---|
| 43 | */
|
---|
| 44 | public class DeleteCommand extends Command {
|
---|
[1004] | 45 | /**
|
---|
[1750] | 46 | * The primitives that get deleted.
|
---|
[1004] | 47 | */
|
---|
[1750] | 48 | private final Collection<? extends OsmPrimitive> toDelete;
|
---|
[626] | 49 |
|
---|
[1004] | 50 | /**
|
---|
[630] | 51 | * Constructor for a collection of data
|
---|
| 52 | */
|
---|
[1004] | 53 | public DeleteCommand(Collection<? extends OsmPrimitive> data) {
|
---|
[1750] | 54 | super();
|
---|
| 55 | this.toDelete = data;
|
---|
[1004] | 56 | }
|
---|
| 57 |
|
---|
| 58 | /**
|
---|
| 59 | * Constructor for a single data item. Use the collection constructor to delete multiple
|
---|
| 60 | * objects.
|
---|
[630] | 61 | */
|
---|
| 62 | public DeleteCommand(OsmPrimitive data) {
|
---|
[1750] | 63 | this.toDelete = Collections.singleton(data);
|
---|
[630] | 64 | }
|
---|
[626] | 65 |
|
---|
[1856] | 66 | /**
|
---|
| 67 | * Constructor for a single data item. Use the collection constructor to delete multiple
|
---|
| 68 | * objects.
|
---|
[1898] | 69 | *
|
---|
[1856] | 70 | * @param layer the layer context for deleting this primitive
|
---|
| 71 | * @param data the primitive to delete
|
---|
| 72 | */
|
---|
| 73 | public DeleteCommand(OsmDataLayer layer, OsmPrimitive data) {
|
---|
| 74 | super(layer);
|
---|
| 75 | this.toDelete = Collections.singleton(data);
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | /**
|
---|
| 79 | * Constructor for a collection of data to be deleted in the context of
|
---|
| 80 | * a specific layer
|
---|
[1898] | 81 | *
|
---|
[1856] | 82 | * @param layer the layer context for deleting these primitives
|
---|
| 83 | * @param data the primitives to delete
|
---|
| 84 | */
|
---|
| 85 | public DeleteCommand(OsmDataLayer layer, Collection<? extends OsmPrimitive> data) {
|
---|
| 86 | super(layer);
|
---|
| 87 | this.toDelete = data;
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | @Override
|
---|
| 91 | public boolean executeCommand() {
|
---|
[1004] | 92 | super.executeCommand();
|
---|
[1750] | 93 | for (OsmPrimitive osm : toDelete) {
|
---|
[1004] | 94 | osm.delete(true);
|
---|
| 95 | }
|
---|
| 96 | return true;
|
---|
| 97 | }
|
---|
[626] | 98 |
|
---|
[1856] | 99 | @Override
|
---|
| 100 | public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
|
---|
[1004] | 101 | Collection<OsmPrimitive> added) {
|
---|
[1750] | 102 | deleted.addAll(toDelete);
|
---|
[1004] | 103 | }
|
---|
[626] | 104 |
|
---|
[1856] | 105 | @Override
|
---|
| 106 | public MutableTreeNode description() {
|
---|
[1750] | 107 | if (toDelete.size() == 1) {
|
---|
[1814] | 108 | OsmPrimitive primitive = toDelete.iterator().next();
|
---|
[1989] | 109 | String msg = "";
|
---|
| 110 | switch(OsmPrimitiveType.from(primitive)) {
|
---|
[2054] | 111 | case NODE: msg = "Delete node {0}"; break;
|
---|
| 112 | case WAY: msg = "Delete way {0}"; break;
|
---|
| 113 | case RELATION:msg = "Delete relation {0}"; break;
|
---|
[1989] | 114 | }
|
---|
| 115 |
|
---|
[1990] | 116 | return new DefaultMutableTreeNode(new JLabel(tr(msg, primitive.getDisplayName(DefaultNameFormatter.getInstance())),
|
---|
[1989] | 117 | ImageProvider.get(OsmPrimitiveType.from(primitive)), JLabel.HORIZONTAL));
|
---|
[1004] | 118 | }
|
---|
[988] | 119 |
|
---|
[1989] | 120 | Set<OsmPrimitiveType> typesToDelete = new HashSet<OsmPrimitiveType>();
|
---|
[1750] | 121 | for (OsmPrimitive osm : toDelete) {
|
---|
[1989] | 122 | typesToDelete.add(OsmPrimitiveType.from(osm));
|
---|
| 123 | }
|
---|
| 124 | String msg = "";
|
---|
| 125 | String apiname = "object";
|
---|
| 126 | if (typesToDelete.size() > 1) {
|
---|
| 127 | msg = trn("Delete {0} object", "Delete {0} objects", toDelete.size(), toDelete.size());
|
---|
| 128 | } else {
|
---|
| 129 | OsmPrimitiveType t = typesToDelete.iterator().next();
|
---|
| 130 | apiname = t.getAPIName();
|
---|
| 131 | switch(t) {
|
---|
[2054] | 132 | case NODE: msg = trn("Delete {0} node", "Delete {0} nodes", toDelete.size(), toDelete.size()); break;
|
---|
| 133 | case WAY: msg = trn("Delete {0} way", "Delete {0} ways", toDelete.size(), toDelete.size()); break;
|
---|
| 134 | case RELATION: msg = trn("Delete {0} relation", "Delete {0} relations", toDelete.size(), toDelete.size()); break;
|
---|
[1004] | 135 | }
|
---|
| 136 | }
|
---|
[1989] | 137 | DefaultMutableTreeNode root = new DefaultMutableTreeNode(
|
---|
| 138 | new JLabel(msg, ImageProvider.get("data", apiname), JLabel.HORIZONTAL)
|
---|
| 139 | );
|
---|
[1750] | 140 | for (OsmPrimitive osm : toDelete) {
|
---|
[1990] | 141 | root.add(new DefaultMutableTreeNode(new JLabel(
|
---|
| 142 | osm.getDisplayName(DefaultNameFormatter.getInstance()),
|
---|
| 143 | ImageProvider.get(OsmPrimitiveType.from(osm)), JLabel.HORIZONTAL)));
|
---|
[1004] | 144 | }
|
---|
| 145 | return root;
|
---|
| 146 | }
|
---|
[988] | 147 |
|
---|
[1004] | 148 | /**
|
---|
| 149 | * Delete the primitives and everything they reference.
|
---|
[1898] | 150 | *
|
---|
[1004] | 151 | * If a node is deleted, the node and all ways and relations the node is part of are deleted as
|
---|
| 152 | * well.
|
---|
[1898] | 153 | *
|
---|
[1004] | 154 | * If a way is deleted, all relations the way is member of are also deleted.
|
---|
[1898] | 155 | *
|
---|
[1004] | 156 | * If a way is deleted, only the way and no nodes are deleted.
|
---|
[1898] | 157 | *
|
---|
[2026] | 158 | * @param layer
|
---|
[1004] | 159 | * @param selection The list of all object to be deleted.
|
---|
[2026] | 160 | * @param simulate Set to true if the user should not be bugged with additional dialogs
|
---|
[1004] | 161 | * @return command A command to perform the deletions, or null of there is nothing to delete.
|
---|
| 162 | */
|
---|
[2026] | 163 | public static Command deleteWithReferences(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection, boolean simulate) {
|
---|
[1856] | 164 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(layer.data);
|
---|
[1656] | 165 | for (OsmPrimitive osm : selection) {
|
---|
[1004] | 166 | osm.visit(v);
|
---|
[1656] | 167 | }
|
---|
[1004] | 168 | v.data.addAll(selection);
|
---|
| 169 | if (v.data.isEmpty())
|
---|
| 170 | return null;
|
---|
[2026] | 171 | if (!checkAndConfirmOutlyingDeletes(layer,v.data) && !simulate)
|
---|
[1004] | 172 | return null;
|
---|
[1856] | 173 | return new DeleteCommand(layer,v.data);
|
---|
[1004] | 174 | }
|
---|
[988] | 175 |
|
---|
[2026] | 176 | public static Command deleteWithReferences(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection) {
|
---|
| 177 | return deleteWithReferences(layer, selection, false);
|
---|
| 178 | }
|
---|
| 179 |
|
---|
| 180 | private static int testRelation(Relation ref, OsmPrimitive osm, boolean simulate) {
|
---|
| 181 | // If this delete action is simulated, do not bug the user with dialogs
|
---|
| 182 | // and assume the relations should be deleted
|
---|
| 183 | if(simulate)
|
---|
| 184 | return 1;
|
---|
| 185 |
|
---|
[1004] | 186 | String role = new String();
|
---|
[1925] | 187 | for (RelationMember m : ref.getMembers()) {
|
---|
[1937] | 188 | if (m.getMember() == osm) {
|
---|
[1930] | 189 | role = m.getRole();
|
---|
[1004] | 190 | break;
|
---|
| 191 | }
|
---|
| 192 | }
|
---|
[2054] | 193 | ExtendedDialog dialog = new ExtendedDialog(
|
---|
| 194 | Main.parent,
|
---|
| 195 | tr("Conflicting relation"),
|
---|
| 196 | new String[] { tr("Delete from relation"),tr("Cancel") }
|
---|
| 197 | );
|
---|
| 198 | dialog.setButtonIcons( new String[] { "dialogs/delete.png", "cancel.png" });
|
---|
| 199 | if (role.length() > 0) {
|
---|
| 200 | dialog.setContent(
|
---|
| 201 | tr(
|
---|
| 202 | "<html>Selection \"{0}\" is used by relation \"{1}\" with role {2}.<br>Delete from relation?</html>",
|
---|
| 203 | osm.getDisplayName(DefaultNameFormatter.getInstance()),
|
---|
| 204 | ref.getDisplayName(DefaultNameFormatter.getInstance()),
|
---|
| 205 | role
|
---|
| 206 | )
|
---|
| 207 | );
|
---|
| 208 | dialog.showDialog();
|
---|
| 209 | return dialog.getValue();
|
---|
| 210 | } else {
|
---|
| 211 | dialog.setContent(
|
---|
| 212 | tr(
|
---|
| 213 | "<html>Selection \"{0}\" is used by relation \"{1}\".<br>Delete from relation?</html>",
|
---|
| 214 | osm.getDisplayName(DefaultNameFormatter.getInstance()),
|
---|
| 215 | ref.getDisplayName(DefaultNameFormatter.getInstance())
|
---|
| 216 | )
|
---|
| 217 | );
|
---|
| 218 | dialog.showDialog();
|
---|
| 219 | return dialog.getValue();
|
---|
| 220 | }
|
---|
[1004] | 221 | }
|
---|
[988] | 222 |
|
---|
[1856] | 223 | public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection) {
|
---|
[2026] | 224 | return delete(layer, selection, true, false);
|
---|
[1004] | 225 | }
|
---|
[988] | 226 |
|
---|
[1415] | 227 | /**
|
---|
[1856] | 228 | * Replies the collection of nodes referred to by primitives in <code>primitivesToDelete</code> which
|
---|
| 229 | * can be deleted too. A node can be deleted if
|
---|
| 230 | * <ul>
|
---|
| 231 | * <li>it is untagged (see {@see Node#isTagged()}</li>
|
---|
| 232 | * <li>it is not referred to by other primitives outside of <code>primitivesToDelete</code></li>
|
---|
| 233 | * <ul>
|
---|
| 234 | * @param layer the layer in whose context primitives are deleted
|
---|
| 235 | * @param primitivesToDelete the primitives to delete
|
---|
| 236 | * @return the collection of nodes referred to by primitives in <code>primitivesToDelete</code> which
|
---|
| 237 | * can be deleted too
|
---|
| 238 | */
|
---|
| 239 | protected static Collection<Node> computeNodesToDelete(OsmDataLayer layer, Collection<OsmPrimitive> primitivesToDelete) {
|
---|
| 240 | Collection<Node> nodesToDelete = new HashSet<Node>();
|
---|
| 241 | for (OsmPrimitive osm : primitivesToDelete) {
|
---|
| 242 | if (! (osm instanceof Way) ) {
|
---|
| 243 | continue;
|
---|
| 244 | }
|
---|
[1898] | 245 | for (Node n : ((Way) osm).getNodes()) {
|
---|
[1856] | 246 | if (n.isTagged()) {
|
---|
| 247 | continue;
|
---|
| 248 | }
|
---|
| 249 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(layer.data, false);
|
---|
| 250 | n.visit(v);
|
---|
| 251 | v.data.removeAll(primitivesToDelete);
|
---|
| 252 | if (v.data.isEmpty()) {
|
---|
| 253 | nodesToDelete.add(n);
|
---|
| 254 | }
|
---|
| 255 | }
|
---|
| 256 | }
|
---|
| 257 | return nodesToDelete;
|
---|
| 258 | }
|
---|
| 259 |
|
---|
| 260 | /**
|
---|
[1415] | 261 | * Try to delete all given primitives.
|
---|
[1898] | 262 | *
|
---|
[1415] | 263 | * If a node is used by a way, it's removed from that way. If a node or a way is used by a
|
---|
| 264 | * relation, inform the user and do not delete.
|
---|
[1898] | 265 | *
|
---|
[1415] | 266 | * If this would cause ways with less than 2 nodes to be created, delete these ways instead. If
|
---|
| 267 | * they are part of a relation, inform the user and do not delete.
|
---|
[1898] | 268 | *
|
---|
[1856] | 269 | * @param layer the {@see OsmDataLayer} in whose context a primitive the primitives are deleted
|
---|
[1415] | 270 | * @param selection The objects to delete.
|
---|
| 271 | * @param alsoDeleteNodesInWay <code>true</code> if nodes should be deleted as well
|
---|
[2026] | 272 | * @param simulate Set to true if the user should not be bugged with additional questions
|
---|
[1856] | 273 | * @return command a command to perform the deletions, or null if there is nothing to delete.
|
---|
[1415] | 274 | */
|
---|
[2026] | 275 | public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection,
|
---|
| 276 | boolean alsoDeleteNodesInWay) {
|
---|
| 277 | return delete(layer, selection, alsoDeleteNodesInWay, false);
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | public static Command delete(OsmDataLayer layer, Collection<? extends OsmPrimitive> selection,
|
---|
| 281 | boolean alsoDeleteNodesInWay, boolean simulate) {
|
---|
[1004] | 282 | if (selection.isEmpty())
|
---|
| 283 | return null;
|
---|
[988] | 284 |
|
---|
[1856] | 285 | Collection<OsmPrimitive> primitivesToDelete = new HashSet<OsmPrimitive>(selection);
|
---|
[1004] | 286 | Collection<Way> waysToBeChanged = new HashSet<Way>();
|
---|
| 287 | HashMap<OsmPrimitive, Collection<OsmPrimitive>> relationsToBeChanged = new HashMap<OsmPrimitive, Collection<OsmPrimitive>>();
|
---|
[988] | 288 |
|
---|
[1004] | 289 | if (alsoDeleteNodesInWay) {
|
---|
[1856] | 290 | // delete untagged nodes only referenced by primitives in primitivesToDelete,
|
---|
| 291 | // too
|
---|
| 292 | Collection<Node> nodesToDelete = computeNodesToDelete(layer, primitivesToDelete);
|
---|
| 293 | primitivesToDelete.addAll(nodesToDelete);
|
---|
[1004] | 294 | }
|
---|
[988] | 295 |
|
---|
[2054] | 296 | if (!simulate && !checkAndConfirmOutlyingDeletes(layer,primitivesToDelete))
|
---|
[1004] | 297 | return null;
|
---|
[988] | 298 |
|
---|
[1856] | 299 | for (OsmPrimitive osm : primitivesToDelete) {
|
---|
| 300 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(layer.data, false);
|
---|
[1004] | 301 | osm.visit(v);
|
---|
| 302 | for (OsmPrimitive ref : v.data) {
|
---|
[1856] | 303 | if (primitivesToDelete.contains(ref)) {
|
---|
[1004] | 304 | continue;
|
---|
[1656] | 305 | }
|
---|
[1004] | 306 | if (ref instanceof Way) {
|
---|
| 307 | waysToBeChanged.add((Way) ref);
|
---|
| 308 | } else if (ref instanceof Relation) {
|
---|
[2026] | 309 | if (testRelation((Relation) ref, osm, simulate) == 1) {
|
---|
[1004] | 310 | Collection<OsmPrimitive> relset = relationsToBeChanged.get(ref);
|
---|
[1656] | 311 | if (relset == null) {
|
---|
[1004] | 312 | relset = new HashSet<OsmPrimitive>();
|
---|
[1656] | 313 | }
|
---|
[1004] | 314 | relset.add(osm);
|
---|
| 315 | relationsToBeChanged.put(ref, relset);
|
---|
| 316 | } else
|
---|
| 317 | return null;
|
---|
[1656] | 318 | } else
|
---|
[1004] | 319 | return null;
|
---|
| 320 | }
|
---|
| 321 | }
|
---|
[988] | 322 |
|
---|
[1004] | 323 | Collection<Command> cmds = new LinkedList<Command>();
|
---|
| 324 | for (Way w : waysToBeChanged) {
|
---|
| 325 | Way wnew = new Way(w);
|
---|
[1856] | 326 | wnew.removeNodes(primitivesToDelete);
|
---|
[1898] | 327 | if (wnew.getNodesCount() < 2) {
|
---|
[1856] | 328 | primitivesToDelete.add(w);
|
---|
[988] | 329 |
|
---|
[1856] | 330 | CollectBackReferencesVisitor v = new CollectBackReferencesVisitor(layer.data, false);
|
---|
[1004] | 331 | w.visit(v);
|
---|
| 332 | for (OsmPrimitive ref : v.data) {
|
---|
[1856] | 333 | if (primitivesToDelete.contains(ref)) {
|
---|
[1004] | 334 | continue;
|
---|
[1656] | 335 | }
|
---|
[1004] | 336 | if (ref instanceof Relation) {
|
---|
| 337 | Boolean found = false;
|
---|
| 338 | Collection<OsmPrimitive> relset = relationsToBeChanged.get(ref);
|
---|
[1656] | 339 | if (relset == null) {
|
---|
[1004] | 340 | relset = new HashSet<OsmPrimitive>();
|
---|
[1656] | 341 | } else {
|
---|
[1004] | 342 | for (OsmPrimitive m : relset) {
|
---|
| 343 | if (m == w) {
|
---|
| 344 | found = true;
|
---|
| 345 | break;
|
---|
| 346 | }
|
---|
| 347 | }
|
---|
| 348 | }
|
---|
| 349 | if (!found) {
|
---|
[2026] | 350 | if (testRelation((Relation) ref, w, simulate) == 1) {
|
---|
[1004] | 351 | relset.add(w);
|
---|
| 352 | relationsToBeChanged.put(ref, relset);
|
---|
| 353 | } else
|
---|
| 354 | return null;
|
---|
| 355 | }
|
---|
[1656] | 356 | } else
|
---|
[1004] | 357 | return null;
|
---|
| 358 | }
|
---|
| 359 | } else {
|
---|
| 360 | cmds.add(new ChangeCommand(w, wnew));
|
---|
| 361 | }
|
---|
| 362 | }
|
---|
[988] | 363 |
|
---|
[1004] | 364 | Iterator<OsmPrimitive> iterator = relationsToBeChanged.keySet().iterator();
|
---|
| 365 | while (iterator.hasNext()) {
|
---|
| 366 | Relation cur = (Relation) iterator.next();
|
---|
| 367 | Relation rel = new Relation(cur);
|
---|
| 368 | for (OsmPrimitive osm : relationsToBeChanged.get(cur)) {
|
---|
[1750] | 369 | rel.removeMembersFor(osm);
|
---|
[1004] | 370 | }
|
---|
| 371 | cmds.add(new ChangeCommand(cur, rel));
|
---|
| 372 | }
|
---|
[988] | 373 |
|
---|
[1656] | 374 | // #2707: ways to be deleted can include new nodes (with node.id == 0).
|
---|
| 375 | // Remove them from the way before the way is deleted. Otherwise the
|
---|
| 376 | // deleted way is saved (or sent to the API) with a dangling reference to a node
|
---|
| 377 | // Example:
|
---|
[1856] | 378 | // <node id='2' action='delete' visible='true' version='1' ... />
|
---|
| 379 | // <node id='1' action='delete' visible='true' version='1' ... />
|
---|
| 380 | // <!-- missing node with id -1 because new deleted nodes are not persisted -->
|
---|
| 381 | // <way id='3' action='delete' visible='true' version='1'>
|
---|
| 382 | // <nd ref='1' />
|
---|
| 383 | // <nd ref='-1' /> <!-- heres the problem -->
|
---|
| 384 | // <nd ref='2' />
|
---|
| 385 | // </way>
|
---|
| 386 | for (OsmPrimitive primitive : primitivesToDelete) {
|
---|
| 387 | if (!(primitive instanceof Way)) {
|
---|
[1656] | 388 | continue;
|
---|
| 389 | }
|
---|
[1856] | 390 | Way w = (Way) primitive;
|
---|
[2025] | 391 | if (w.getId() == 0) { // new ways with id == 0 are fine,
|
---|
[1856] | 392 | continue; // process existing ways only
|
---|
[1656] | 393 | }
|
---|
| 394 | Way wnew = new Way(w);
|
---|
[1898] | 395 | List<Node> nodesToKeep = new ArrayList<Node>();
|
---|
[1656] | 396 | // lookup new nodes which have been added to the set of deleted
|
---|
| 397 | // nodes ...
|
---|
[1898] | 398 | for (Node n : wnew.getNodes()) {
|
---|
[2025] | 399 | if (n.getId() != 0 || !primitivesToDelete.contains(n)) {
|
---|
[1898] | 400 | nodesToKeep.add(n);
|
---|
[1656] | 401 | }
|
---|
| 402 | }
|
---|
| 403 | // .. and remove them from the way
|
---|
| 404 | //
|
---|
[1898] | 405 | wnew.setNodes(nodesToKeep);
|
---|
| 406 | if (nodesToKeep.size() < w.getNodesCount()) {
|
---|
[1856] | 407 | cmds.add(new ChangeCommand(w, wnew));
|
---|
[1656] | 408 | }
|
---|
| 409 | }
|
---|
| 410 |
|
---|
[1856] | 411 | if (!primitivesToDelete.isEmpty()) {
|
---|
| 412 | cmds.add(new DeleteCommand(layer,primitivesToDelete));
|
---|
[1656] | 413 | }
|
---|
[988] | 414 |
|
---|
[1004] | 415 | return new SequenceCommand(tr("Delete"), cmds);
|
---|
| 416 | }
|
---|
[988] | 417 |
|
---|
[1856] | 418 | public static Command deleteWaySegment(OsmDataLayer layer, WaySegment ws) {
|
---|
[1004] | 419 | List<Node> n1 = new ArrayList<Node>(), n2 = new ArrayList<Node>();
|
---|
[988] | 420 |
|
---|
[1898] | 421 | n1.addAll(ws.way.getNodes().subList(0, ws.lowerIndex + 1));
|
---|
| 422 | n2.addAll(ws.way.getNodes().subList(ws.lowerIndex + 1, ws.way.getNodesCount()));
|
---|
[988] | 423 |
|
---|
[1656] | 424 | if (n1.size() < 2 && n2.size() < 2)
|
---|
[1856] | 425 | return new DeleteCommand(layer, Collections.singleton(ws.way));
|
---|
[988] | 426 |
|
---|
[1004] | 427 | Way wnew = new Way(ws.way);
|
---|
| 428 |
|
---|
| 429 | if (n1.size() < 2) {
|
---|
[1898] | 430 | wnew.setNodes(n2);
|
---|
[1004] | 431 | return new ChangeCommand(ws.way, wnew);
|
---|
| 432 | } else if (n2.size() < 2) {
|
---|
[1898] | 433 | wnew.setNodes(n1);
|
---|
[1004] | 434 | return new ChangeCommand(ws.way, wnew);
|
---|
| 435 | } else {
|
---|
| 436 | Collection<Command> cmds = new LinkedList<Command>();
|
---|
| 437 |
|
---|
[1898] | 438 | wnew.setNodes(n1);
|
---|
[1004] | 439 | cmds.add(new ChangeCommand(ws.way, wnew));
|
---|
| 440 |
|
---|
| 441 | Way wnew2 = new Way();
|
---|
[1924] | 442 | wnew2.setKeys(wnew.getKeys());
|
---|
[1898] | 443 | wnew2.setNodes(n2);
|
---|
[1004] | 444 | cmds.add(new AddCommand(wnew2));
|
---|
| 445 |
|
---|
| 446 | return new SequenceCommand(tr("Split way segment"), cmds);
|
---|
| 447 | }
|
---|
| 448 | }
|
---|
| 449 |
|
---|
| 450 | /**
|
---|
[1856] | 451 | * Check whether user is about to delete data outside of the download area. Request confirmation
|
---|
| 452 | * if he is.
|
---|
[1898] | 453 | *
|
---|
[1856] | 454 | * @param layer the layer in whose context data is deleted
|
---|
| 455 | * @param primitivesToDelete the primitives to delete
|
---|
| 456 | * @return true, if deleting outlying primitives is OK; false, otherwise
|
---|
[1004] | 457 | */
|
---|
[1856] | 458 | private static boolean checkAndConfirmOutlyingDeletes(OsmDataLayer layer, Collection<OsmPrimitive> primitivesToDelete) {
|
---|
| 459 | Area a = layer.data.getDataSourceArea();
|
---|
[1004] | 460 | if (a != null) {
|
---|
[1856] | 461 | for (OsmPrimitive osm : primitivesToDelete) {
|
---|
[2025] | 462 | if (osm instanceof Node && osm.getId() != 0) {
|
---|
[1004] | 463 | Node n = (Node) osm;
|
---|
[1640] | 464 | if (!a.contains(n.getCoor())) {
|
---|
[1004] | 465 | JPanel msg = new JPanel(new GridBagLayout());
|
---|
| 466 | msg.add(new JLabel(
|
---|
[1656] | 467 | "<html>" +
|
---|
[1856] | 468 | // leave message in one tr() as there is a grammatical
|
---|
| 469 | // connection.
|
---|
| 470 | tr("You are about to delete nodes outside of the area you have downloaded."
|
---|
| 471 | + "<br>"
|
---|
| 472 | + "This can cause problems because other objects (that you don't see) might use them."
|
---|
| 473 | + "<br>" + "Do you really want to delete?") + "</html>"));
|
---|
[1838] | 474 | return ConditionalOptionPaneUtil.showConfirmationDialog(
|
---|
| 475 | "delete_outside_nodes",
|
---|
| 476 | Main.parent,
|
---|
| 477 | msg,
|
---|
| 478 | tr("Delete confirmation"),
|
---|
| 479 | JOptionPane.YES_NO_OPTION,
|
---|
| 480 | JOptionPane.QUESTION_MESSAGE,
|
---|
| 481 | JOptionPane.YES_OPTION
|
---|
| 482 | );
|
---|
[1004] | 483 | }
|
---|
| 484 | }
|
---|
| 485 | }
|
---|
| 486 | }
|
---|
| 487 | return true;
|
---|
| 488 | }
|
---|
[626] | 489 | }
|
---|