001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006import static org.openstreetmap.josm.tools.I18n.trn; 007 008import java.awt.GridBagLayout; 009import java.awt.event.ActionEvent; 010import java.awt.event.KeyEvent; 011import java.util.Collection; 012 013import javax.swing.JOptionPane; 014import javax.swing.JPanel; 015 016import org.openstreetmap.josm.command.DeleteCommand.DeletionCallback; 017import org.openstreetmap.josm.data.osm.DefaultNameFormatter; 018import org.openstreetmap.josm.data.osm.OsmPrimitive; 019import org.openstreetmap.josm.data.osm.Relation; 020import org.openstreetmap.josm.data.osm.RelationToChildReference; 021import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 022import org.openstreetmap.josm.gui.MainApplication; 023import org.openstreetmap.josm.gui.MapFrame; 024import org.openstreetmap.josm.gui.dialogs.DeleteFromRelationConfirmationDialog; 025import org.openstreetmap.josm.gui.widgets.JMultilineLabel; 026import org.openstreetmap.josm.tools.Shortcut; 027 028/** 029 * Action that deletes selected objects. 030 * @since 770 031 */ 032public final class DeleteAction extends JosmAction { 033 034 /** 035 * The default {@link DeletionCallback} for {@code DeleteCommand}. 036 * @since 12760 037 */ 038 public static final DeletionCallback defaultDeletionCallback = new DeletionCallback() { 039 @Override 040 public boolean checkAndConfirmOutlyingDelete(Collection<? extends OsmPrimitive> primitives, 041 Collection<? extends OsmPrimitive> ignore) { 042 return DeleteAction.checkAndConfirmOutlyingDelete(primitives, ignore); 043 } 044 045 @Override 046 public boolean confirmRelationDeletion(Collection<Relation> relations) { 047 return DeleteAction.confirmRelationDeletion(relations); 048 } 049 050 @Override 051 public boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references) { 052 DeleteFromRelationConfirmationDialog dialog = DeleteFromRelationConfirmationDialog.getInstance(); 053 dialog.getModel().populate(references); 054 dialog.setVisible(true); 055 return !dialog.isCanceled(); 056 } 057 }; 058 059 /** 060 * Constructs a new {@code DeleteAction}. 061 */ 062 public DeleteAction() { 063 super(tr("Delete"), "dialogs/delete", tr("Delete selected objects."), 064 Shortcut.registerShortcut("system:delete", tr("Edit: {0}", tr("Delete")), KeyEvent.VK_DELETE, Shortcut.DIRECT), true); 065 setHelpId(ht("/Action/Delete")); 066 } 067 068 @Override 069 public void actionPerformed(ActionEvent e) { 070 MapFrame map = MainApplication.getMap(); 071 if (!isEnabled() || !map.mapView.isActiveLayerVisible()) 072 return; 073 map.mapModeDelete.doActionPerformed(e); 074 } 075 076 @Override 077 protected void updateEnabledState() { 078 updateEnabledStateOnCurrentSelection(); 079 } 080 081 @Override 082 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 083 updateEnabledStateOnModifiableSelection(selection); 084 } 085 086 /** 087 * Check whether user is about to delete data outside of the download area. 088 * Request confirmation if he is. 089 * @param primitives the primitives to operate on 090 * @param ignore {@code null} or a primitive to be ignored 091 * @return true, if operating on outlying primitives is OK; false, otherwise 092 * @since 12749 (moved from DeleteCommand) 093 */ 094 public static boolean checkAndConfirmOutlyingDelete(Collection<? extends OsmPrimitive> primitives, 095 Collection<? extends OsmPrimitive> ignore) { 096 return checkAndConfirmOutlyingOperation("delete", 097 tr("Delete confirmation"), 098 tr("You are about to delete nodes which can have other referrers not yet downloaded." 099 + "<br>" 100 + "This can cause problems because other objects (that you do not see) might use them." 101 + "<br>" 102 + "Do you really want to delete?"), 103 tr("You are about to delete incomplete objects." 104 + "<br>" 105 + "This will cause problems because you don''t see the real object." 106 + "<br>" + "Do you really want to delete?"), 107 primitives, ignore); 108 } 109 110 /** 111 * Confirm before deleting a relation, as it is a common newbie error. 112 * @param relations relation to check for deletion 113 * @return {@code true} if user confirms the deletion 114 * @since 12760 115 */ 116 public static boolean confirmRelationDeletion(Collection<Relation> relations) { 117 if (relations.stream().allMatch(Relation::isNew)) { 118 return true; 119 } 120 JPanel msg = new JPanel(new GridBagLayout()); 121 msg.add(new JMultilineLabel("<html>" + trn( 122 "You are about to delete {0} relation: {1}" 123 + "<br/>" 124 + "This step is rarely necessary and cannot be undone easily after being uploaded to the server." 125 + "<br/>" 126 + "Do you really want to delete?", 127 "You are about to delete {0} relations: {1}" 128 + "<br/>" 129 + "This step is rarely necessary and cannot be undone easily after being uploaded to the server." 130 + "<br/>" 131 + "Do you really want to delete?", 132 relations.size(), relations.size(), DefaultNameFormatter.getInstance().formatAsHtmlUnorderedList(relations, 20)) 133 + "</html>")); 134 return ConditionalOptionPaneUtil.showConfirmationDialog( 135 "delete_relations", 136 MainApplication.getMainFrame(), 137 msg, 138 tr("Delete relation?"), 139 JOptionPane.YES_NO_OPTION, 140 JOptionPane.QUESTION_MESSAGE, 141 JOptionPane.YES_OPTION); 142 } 143}