Ticket #21825: 21825.patch
File 21825.patch, 16.1 KB (added by , 4 years ago) |
---|
-
src/org/openstreetmap/josm/actions/DeleteAction.java
diff --git a/src/org/openstreetmap/josm/actions/DeleteAction.java b/src/org/openstreetmap/josm/actions/DeleteAction.java index 4400aea798..a480c2efae 100644
a b import java.awt.GridBagLayout; 9 9 import java.awt.event.ActionEvent; 10 10 import java.awt.event.KeyEvent; 11 11 import java.util.Collection; 12 import java.util.Collections; 12 13 13 14 import javax.swing.JOptionPane; 14 15 import javax.swing.JPanel; … … import org.openstreetmap.josm.gui.MainApplication; 23 24 import org.openstreetmap.josm.gui.MapFrame; 24 25 import org.openstreetmap.josm.gui.dialogs.DeleteFromRelationConfirmationDialog; 25 26 import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 27 import org.openstreetmap.josm.tools.Pair; 26 28 import org.openstreetmap.josm.tools.Shortcut; 27 29 28 30 /** … … public final class DeleteAction extends JosmAction { 49 51 50 52 @Override 51 53 public boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references) { 54 return this.confirmDeletionFromRelation(references, Collections.emptyList()); 55 } 56 57 @Override 58 public boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references, 59 Collection<Pair<Relation, Boolean>> parentsToDelete) { 52 60 DeleteFromRelationConfirmationDialog dialog = DeleteFromRelationConfirmationDialog.getInstance(); 53 61 dialog.getModel().populate(references); 62 dialog.getDeletedRelationsModel().populate(parentsToDelete); 54 63 dialog.setVisible(true); 55 64 return !dialog.isCanceled(); 56 65 } -
src/org/openstreetmap/josm/command/DeleteCommand.java
diff --git a/src/org/openstreetmap/josm/command/DeleteCommand.java b/src/org/openstreetmap/josm/command/DeleteCommand.java index e21234fea6..82dab79d38 100644
a b import org.openstreetmap.josm.data.osm.Way; 35 35 import org.openstreetmap.josm.data.osm.WaySegment; 36 36 import org.openstreetmap.josm.tools.CheckParameterUtil; 37 37 import org.openstreetmap.josm.tools.ImageProvider; 38 import org.openstreetmap.josm.tools.Pair; 38 39 import org.openstreetmap.josm.tools.Utils; 39 40 40 41 /** … … public class DeleteCommand extends Command { 101 102 * @since 12763 102 103 */ 103 104 boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references); 105 106 /** 107 * Confirm before removing a collection of primitives from their parent relations, with the probability of 108 * deleting the parents as well. 109 * @param references the list of relation-to-child references 110 * @param parentsToDelete the list of parents to delete (the boolean part will be {@code true} if the user wants 111 * to delete the relation). 112 * @return {@code true} if the user confirms the deletion 113 * @since xxx 114 */ 115 default boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references, 116 Collection<Pair<Relation, Boolean>> parentsToDelete) { 117 // This is a default method. Ensure that all the booleans are false. 118 parentsToDelete.forEach(pair -> pair.b = false); 119 return confirmDeletionFromRelation(references); 120 } 104 121 } 105 122 106 123 private static volatile DeletionCallback callback; … … public class DeleteCommand extends Command { 437 454 } 438 455 } 439 456 440 // get a confirmation that the objects to delete can be removed from their parent relations441 //442 if (!silent) {443 Set<RelationToChildReference> references = RelationToChildReference.getRelationToChildReferences(primitivesToDelete);444 references.removeIf(ref -> ref.getParent().isDeleted());445 if (!references.isEmpty() && !callback.confirmDeletionFromRelation(references)) {446 return null;447 }448 }449 450 457 // remove the objects from their parent relations 451 458 // 452 459 final Set<Relation> relationsToBeChanged = primitivesToDelete.stream() 453 460 .flatMap(p -> p.referrers(Relation.class)) 454 461 .collect(Collectors.toSet()); 462 final Set<Relation> additionalRelationsToDelete = new HashSet<>(); 455 463 for (Relation cur : relationsToBeChanged) { 456 464 List<RelationMember> newMembers = cur.getMembers(); 457 465 cur.getMembersFor(primitivesToDelete).forEach(newMembers::remove); 458 466 cmds.add(new ChangeMembersCommand(cur, newMembers)); 467 // If we don't have any members, we probably ought to delete the relation as well. 468 if (newMembers.isEmpty()) { 469 additionalRelationsToDelete.add(cur); 470 } 471 } 472 473 474 // get a confirmation that the objects to delete can be removed from their parent relations 475 // 476 if (!silent) { 477 Set<RelationToChildReference> references = RelationToChildReference.getRelationToChildReferences(primitivesToDelete); 478 references.removeIf(ref -> ref.getParent().isDeleted()); 479 final Collection<Pair<Relation, Boolean>> pairedRelations = additionalRelationsToDelete.stream() 480 /* 481 * Default to true, so that users have to make a choice to not delete. 482 * Default implementation converts it to false, so this should be safe. 483 */ 484 .map(relation -> new Pair<>(relation, true)).collect(Collectors.toSet()); 485 if (!references.isEmpty() && !callback.confirmDeletionFromRelation(references, pairedRelations)) { 486 return null; 487 } 488 pairedRelations.stream().filter(pair -> Boolean.TRUE.equals(pair.b)).map(pair -> pair.a) 489 .forEach(primitivesToDelete::add); 459 490 } 460 491 461 492 // build the delete command -
src/org/openstreetmap/josm/gui/dialogs/DeleteFromRelationConfirmationDialog.java
diff --git a/src/org/openstreetmap/josm/gui/dialogs/DeleteFromRelationConfirmationDialog.java b/src/org/openstreetmap/josm/gui/dialogs/DeleteFromRelationConfirmationDialog.java index 692347ac8e..3b06ce2c20 100644
a b import static org.openstreetmap.josm.tools.I18n.trn; 8 8 import java.awt.BorderLayout; 9 9 import java.awt.Dimension; 10 10 import java.awt.FlowLayout; 11 import java.awt.GridBagLayout; 11 12 import java.awt.event.ActionEvent; 12 13 import java.awt.event.WindowAdapter; 13 14 import java.awt.event.WindowEvent; … … import javax.swing.table.TableColumn; 34 35 import org.openstreetmap.josm.data.osm.DefaultNameFormatter; 35 36 import org.openstreetmap.josm.data.osm.NameFormatter; 36 37 import org.openstreetmap.josm.data.osm.OsmPrimitive; 38 import org.openstreetmap.josm.data.osm.Relation; 37 39 import org.openstreetmap.josm.data.osm.RelationToChildReference; 38 40 import org.openstreetmap.josm.gui.MainApplication; 39 41 import org.openstreetmap.josm.gui.PrimitiveRenderer; … … import org.openstreetmap.josm.gui.help.HelpUtil; 42 44 import org.openstreetmap.josm.gui.util.GuiHelper; 43 45 import org.openstreetmap.josm.gui.util.WindowGeometry; 44 46 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 47 import org.openstreetmap.josm.tools.GBC; 45 48 import org.openstreetmap.josm.tools.I18n; 46 49 import org.openstreetmap.josm.tools.ImageProvider; 50 import org.openstreetmap.josm.tools.Pair; 51 import org.openstreetmap.josm.tools.Utils; 47 52 48 53 /** 49 54 * This dialog is used to get a user confirmation that a collection of primitives can be removed … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 68 73 69 74 /** the data model */ 70 75 private RelationMemberTableModel model; 76 /** The data model for deleting relations */ 77 private RelationDeleteModel deletedRelationsModel; 78 /** The table to hide/show if the relations to delete are not empty*/ 71 79 private final HtmlPanel htmlPanel = new HtmlPanel(); 72 80 private boolean canceled; 73 81 private final JButton btnOK = new JButton(new OKAction()); 74 82 75 83 protected JPanel buildRelationMemberTablePanel() { 76 84 JTable table = new JTable(model, new RelationMemberTableColumnModel()); 77 JPanel pnl = new JPanel(new BorderLayout()); 78 pnl.add(new JScrollPane(table)); 85 JPanel pnl = new JPanel(new GridBagLayout()); 86 pnl.add(new JScrollPane(table), GBC.eol().fill()); 87 JTable deletedRelationsTable = new JTable(this.deletedRelationsModel, new RelationDeleteTableColumnModel()); 88 JScrollPane deletedRelationsModelTableScrollPane = new JScrollPane(deletedRelationsTable); 89 this.deletedRelationsModel.addTableModelListener(e -> deletedRelationsModelTableScrollPane.setVisible(this.deletedRelationsModel.getRowCount() > 0)); 90 // Default to not visible 91 deletedRelationsModelTableScrollPane.setVisible(false); 92 pnl.add(deletedRelationsModelTableScrollPane, GBC.eol().fill()); 79 93 return pnl; 80 94 } 81 95 … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 91 105 protected final void build() { 92 106 model = new RelationMemberTableModel(); 93 107 model.addTableModelListener(this); 108 this.deletedRelationsModel = new RelationDeleteModel(); 109 this.deletedRelationsModel.addTableModelListener(this); 94 110 getContentPane().setLayout(new BorderLayout()); 95 111 getContentPane().add(htmlPanel, BorderLayout.NORTH); 96 112 getContentPane().add(buildRelationMemberTablePanel(), BorderLayout.CENTER); … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 102 118 } 103 119 104 120 protected void updateMessage() { 105 int numObjectsToDelete = model.getNumObjectsToDelete();106 int numParentRelations = model.getNumParentRelations();121 int numObjectsToDelete = this.model.getNumObjectsToDelete() + this.deletedRelationsModel.getNumObjectsToDelete(); 122 int numParentRelations = this.model.getNumParentRelations() + this.deletedRelationsModel.getNumParentRelations(); 107 123 final String msg1 = trn( 108 124 "Please confirm to remove <strong>{0} object</strong>.", 109 125 "Please confirm to remove <strong>{0} objects</strong>.", … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 119 135 } 120 136 121 137 protected void updateTitle() { 122 int numObjectsToDelete = model.getNumObjectsToDelete();138 int numObjectsToDelete = this.model.getNumObjectsToDelete() + this.deletedRelationsModel.getNumObjectsToDelete(); 123 139 if (numObjectsToDelete > 0) { 124 140 setTitle(trn("Deleting {0} object", "Deleting {0} objects", numObjectsToDelete, numObjectsToDelete)); 125 141 } else { … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 144 160 return model; 145 161 } 146 162 163 /** 164 * Replies the data model used for relations that should probably be deleted. 165 * @return the data model 166 * @since xxx 167 */ 168 public RelationDeleteModel getDeletedRelationsModel() { 169 return this.deletedRelationsModel; 170 } 171 147 172 /** 148 173 * Replies true if the dialog was canceled 149 174 * … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 173 198 new WindowGeometry(this).remember(getClass().getName() + ".geometry"); 174 199 } 175 200 model.data.clear(); 201 this.deletedRelationsModel.data.clear(); 176 202 } 177 203 super.setVisible(visible); 178 204 } … … public class DeleteFromRelationConfirmationDialog extends JDialog implements Tab 325 351 } 326 352 } 327 353 354 /** 355 * The table model which manages relations that will be deleted, if their children are deleted. 356 * @since xxx 357 */ 358 public static class RelationDeleteModel extends DefaultTableModel { 359 private final transient List<Pair<Relation, Boolean>> data = new ArrayList<>(); 360 361 @Override 362 public int getRowCount() { 363 // This is called in the super constructor. Before we have instantiated the list. Removing the null check 364 // WILL LEAD TO A SILENT NPE! 365 if (this.data == null) { 366 return 0; 367 } 368 return this.data.size(); 369 } 370 371 /** 372 * Sets the data that should be displayed in the list. 373 * @param references A list of references to display 374 */ 375 public void populate(Collection<Pair<Relation, Boolean>> references) { 376 this.data.clear(); 377 if (references != null) { 378 this.data.addAll(references); 379 } 380 this.data.sort(Comparator.comparing(pair -> pair.a)); 381 fireTableDataChanged(); 382 } 383 384 /** 385 * Gets the list of children that are currently displayed. 386 * @return The children. 387 */ 388 public Set<Relation> getObjectsToDelete() { 389 return this.data.stream().filter(relation -> relation.b).map(relation -> relation.a).collect(Collectors.toSet()); 390 } 391 392 /** 393 * Gets the number of elements {@link #getObjectsToDelete()} would return. 394 * @return That number. 395 */ 396 public int getNumObjectsToDelete() { 397 return getObjectsToDelete().size(); 398 } 399 400 /** 401 * Gets the set of parent relations 402 * @return All parent relations of the references 403 */ 404 public Set<OsmPrimitive> getParentRelations() { 405 return this.data.stream() 406 .flatMap(pair -> Utils.filteredCollection(pair.a.getReferrers(), Relation.class).stream()) 407 .collect(Collectors.toSet()); 408 } 409 410 /** 411 * Gets the number of elements {@link #getParentRelations()} would return. 412 * @return That number. 413 */ 414 public int getNumParentRelations() { 415 return getParentRelations().size(); 416 } 417 418 @Override 419 public Object getValueAt(int rowIndex, int columnIndex) { 420 if (this.data.isEmpty()) { 421 return null; 422 } 423 Pair<Relation, Boolean> ref = this.data.get(rowIndex); 424 switch(columnIndex) { 425 case 0: return ref.a; 426 case 1: return ref.b; 427 default: 428 assert false : "Illegal column index"; 429 } 430 return null; 431 } 432 433 @Override 434 public boolean isCellEditable(int row, int column) { 435 return !this.data.isEmpty() && column == 1; 436 } 437 438 @Override 439 public void setValueAt(Object aValue, int row, int column) { 440 if (this.data.size() > row && column == 1 && aValue instanceof Boolean) { 441 this.data.get(row).b = ((Boolean) aValue); 442 } 443 } 444 445 @Override 446 public Class<?> getColumnClass(int columnIndex) { 447 switch (columnIndex) { 448 case 0: 449 return Relation.class; 450 case 1: 451 return Boolean.class; 452 default: 453 return super.getColumnClass(columnIndex); 454 } 455 } 456 } 457 458 private static class RelationDeleteTableColumnModel extends DefaultTableColumnModel { 459 protected final void createColumns() { 460 // column 0 - To Delete 461 TableColumn col = new TableColumn(0); 462 col.setHeaderValue(tr("Relation")); 463 col.setResizable(true); 464 col.setWidth(100); 465 col.setPreferredWidth(100); 466 col.setCellRenderer(new PrimitiveRenderer()); 467 addColumn(col); 468 469 // column 0 - From Relation 470 col = new TableColumn(1); 471 final String toDelete = tr("To delete"); 472 col.setHeaderValue(toDelete); 473 col.setResizable(true); 474 col.setPreferredWidth(toDelete.length()); 475 addColumn(col); 476 } 477 478 RelationDeleteTableColumnModel() { 479 createColumns(); 480 } 481 } 482 328 483 class OKAction extends AbstractAction { 329 484 OKAction() { 330 485 putValue(NAME, tr("OK"));