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