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