001// License: GPL. For details, see LICENSE file.
002// Author: David Earl
003package org.openstreetmap.josm.actions;
004
005import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
006import static org.openstreetmap.josm.tools.I18n.tr;
007
008import java.awt.event.ActionEvent;
009import java.awt.event.KeyEvent;
010import java.util.Collection;
011
012import javax.swing.JOptionPane;
013
014import org.openstreetmap.josm.Main;
015import org.openstreetmap.josm.data.osm.OsmPrimitive;
016import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
017import org.openstreetmap.josm.gui.layer.OsmDataLayer;
018import org.openstreetmap.josm.tools.Shortcut;
019import org.openstreetmap.josm.tools.Utils;
020
021/**
022 * Copy OSM primitives to clipboard in order to paste them, or their tags, somewhere else.
023 * @since 404
024 */
025public final class CopyAction extends JosmAction {
026
027    // regular expression that matches text clipboard contents after copying
028    public static final String CLIPBOARD_REGEXP = "((node|way|relation)\\s\\d+,)*(node|way|relation)\\s\\d+";
029
030    /**
031     * Constructs a new {@code CopyAction}.
032     */
033    public CopyAction() {
034        super(tr("Copy"), "copy",
035                tr("Copy selected objects to paste buffer."),
036                Shortcut.registerShortcut("system:copy", tr("Edit: {0}", tr("Copy")), KeyEvent.VK_C, Shortcut.CTRL), true);
037        putValue("help", ht("/Action/Copy"));
038        // CUA shortcut for copy (https://en.wikipedia.org/wiki/IBM_Common_User_Access#Description)
039        Main.registerActionShortcut(this,
040                Shortcut.registerShortcut("system:copy:cua", tr("Edit: {0}", tr("Copy")), KeyEvent.VK_INSERT, Shortcut.CTRL));
041    }
042
043    @Override
044    public void actionPerformed(ActionEvent e) {
045        if(isEmptySelection()) return;
046        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
047
048        copy(getEditLayer(), selection);
049    }
050
051    /**
052     * Copies the given primitive ids to the clipboard. The output by this function
053     * looks similar to: node 1089302677,node 1089303458,way 93793372
054     * @param source The OSM data layer source
055     * @param primitives The OSM primitives to copy
056     */
057    public static void copy(OsmDataLayer source, Collection<OsmPrimitive> primitives) {
058        /* copy ids to the clipboard */
059        StringBuilder idsBuilder = new StringBuilder();
060        for (OsmPrimitive p : primitives) {
061            idsBuilder.append(OsmPrimitiveType.from(p).getAPIName()).append(" ");
062            idsBuilder.append(p.getId()).append(",");
063        }
064        String ids = idsBuilder.substring(0, idsBuilder.length() - 1);
065        Utils.copyToClipboard(ids);
066
067        Main.pasteBuffer.makeCopy(primitives);
068        Main.pasteSource = source;
069    }
070
071    @Override
072    protected void updateEnabledState() {
073        if (getCurrentDataSet() == null) {
074            setEnabled(false);
075        } else {
076            updateEnabledState(getCurrentDataSet().getSelected());
077        }
078    }
079
080    @Override
081    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
082        setEnabled(selection != null && !selection.isEmpty());
083    }
084
085    private boolean isEmptySelection() {
086        Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected();
087        if (sel.isEmpty()) {
088            JOptionPane.showMessageDialog(
089                    Main.parent,
090                    tr("Please select something to copy."),
091                    tr("Information"),
092                    JOptionPane.INFORMATION_MESSAGE
093            );
094            return true;
095        }
096        return false;
097    }
098}