001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import java.awt.MouseInfo;
005import java.awt.Point;
006import java.awt.PointerInfo;
007import java.awt.datatransfer.FlavorEvent;
008import java.awt.datatransfer.FlavorListener;
009import java.awt.datatransfer.Transferable;
010import java.awt.event.ActionEvent;
011
012import org.openstreetmap.josm.data.coor.EastNorth;
013import org.openstreetmap.josm.gui.MainApplication;
014import org.openstreetmap.josm.gui.MapView;
015import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
016import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
017import org.openstreetmap.josm.tools.Shortcut;
018
019/**
020 * This is the base class for all actions that paste objects.
021 * @author Michael Zangl
022 * @since 10765
023 */
024public abstract class AbstractPasteAction extends JosmAction implements FlavorListener {
025
026    protected final OsmTransferHandler transferHandler;
027
028    /**
029     * Constructs a new {@link AbstractPasteAction}.
030     * @param name the action's text as displayed on the menu (if it is added to a menu)
031     * @param iconName the filename of the icon to use
032     * @param tooltip  a longer description of the action that will be displayed in the tooltip. Please note
033     *           that html is not supported for menu actions on some platforms.
034     * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
035     *            do want a shortcut, remember you can always register it with group=none, so you
036     *            won't be assigned a shortcut unless the user configures one. If you pass null here,
037     *            the user CANNOT configure a shortcut for your action.
038     * @param registerInToolbar register this action for the toolbar preferences?
039     */
040    public AbstractPasteAction(String name, String iconName, String tooltip, Shortcut shortcut,
041            boolean registerInToolbar) {
042        this(name, iconName, tooltip, shortcut, registerInToolbar, null);
043    }
044
045    /**
046     * Constructs a new {@link AbstractPasteAction}.
047     * @param name the action's text as displayed on the menu (if it is added to a menu)
048     * @param iconName the filename of the icon to use
049     * @param tooltip  a longer description of the action that will be displayed in the tooltip. Please note
050     *           that html is not supported for menu actions on some platforms.
051     * @param shortcut a ready-created shortcut object or null if you don't want a shortcut. But you always
052     *            do want a shortcut, remember you can always register it with group=none, so you
053     *            won't be assigned a shortcut unless the user configures one. If you pass null here,
054     *            the user CANNOT configure a shortcut for your action.
055     * @param registerInToolbar register this action for the toolbar preferences?
056     * @param toolbarId identifier for the toolbar preferences. The iconName is used, if this parameter is null
057     */
058    public AbstractPasteAction(String name, String iconName, String tooltip, Shortcut shortcut,
059            boolean registerInToolbar, String toolbarId) {
060        super(name, iconName, tooltip, shortcut, registerInToolbar, toolbarId, true);
061        transferHandler = new OsmTransferHandler();
062        ClipboardUtils.getClipboard().addFlavorListener(this);
063    }
064
065    /**
066     * Compute the location the objects should be pasted at.
067     * @param e The action event that triggered the paste
068     * @return The paste position.
069     */
070    protected EastNorth computePastePosition(ActionEvent e) {
071        // default to paste in center of map (pasted via menu or cursor not in MapView)
072        MapView mapView = MainApplication.getMap().mapView;
073        EastNorth mPosition = mapView.getCenter();
074        // We previously checked for modifier to know if the action has been trigerred via shortcut or via menu
075        // But this does not work if the shortcut is changed to a single key (see #9055)
076        // Observed behaviour: getActionCommand() returns Action.NAME when triggered via menu, but shortcut text when triggered with it
077        if (e != null && !getValue(NAME).equals(e.getActionCommand())) {
078            final PointerInfo pointerInfo = MouseInfo.getPointerInfo();
079            if (pointerInfo != null) {
080                final Point mp = pointerInfo.getLocation();
081                final Point tl = mapView.getLocationOnScreen();
082                final Point pos = new Point(mp.x-tl.x, mp.y-tl.y);
083                if (mapView.contains(pos)) {
084                    mPosition = mapView.getEastNorth(pos.x, pos.y);
085                }
086            }
087        }
088        return mPosition;
089    }
090
091    @Override
092    public void actionPerformed(ActionEvent e) {
093        doPaste(e, ClipboardUtils.getClipboardContent());
094    }
095
096    protected void doPaste(ActionEvent e, Transferable contents) {
097        transferHandler.pasteOn(getLayerManager().getEditLayer(), computePastePosition(e), contents);
098    }
099
100    @Override
101    protected void updateEnabledState() {
102        setEnabled(getLayerManager().getEditDataSet() != null && transferHandler != null && transferHandler.isDataAvailable());
103    }
104
105    @Override
106    public void flavorsChanged(FlavorEvent e) {
107        updateEnabledState();
108    }
109}