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}