Changeset 10604 in josm


Ignore:
Timestamp:
2016-07-23T14:54:19+02:00 (3 years ago)
Author:
Don-vip
Message:

fix #12478, fix #12565, fix #11114 - Use ​Swing Copy/Paste instead of CopyAction/PasteAction with custom buffer (patch by michael2402, modified) - gsoc-core

Location:
trunk
Files:
15 added
41 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/Main.java

    r10601 r10604  
    8484import org.openstreetmap.josm.gui.MapFrame;
    8585import org.openstreetmap.josm.gui.MapFrameListener;
     86import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    8687import org.openstreetmap.josm.gui.help.HelpUtil;
    8788import org.openstreetmap.josm.gui.io.SaveLayersDialog;
     
    178179    /**
    179180     * The global paste buffer.
    180      */
     181     * @deprecated Use swing CCP instead. See {@link OsmTransferHandler}
     182     */
     183    @Deprecated
    181184    public static final PrimitiveDeepCopy pasteBuffer = new PrimitiveDeepCopy();
    182185
    183186    /**
    184187     * The layer source from which {@link Main#pasteBuffer} data comes from.
    185      */
     188     * @deprecated During a copy operation, the layer should be added. See {@link OsmLayerTransferData}.
     189     */
     190    @Deprecated
    186191    public static Layer pasteSource;
    187192
  • trunk/src/org/openstreetmap/josm/actions/CopyAction.java

    r10448 r10604  
    99import java.awt.event.KeyEvent;
    1010import java.util.Collection;
     11import java.util.Collections;
    1112
    1213import javax.swing.JOptionPane;
    1314
    1415import org.openstreetmap.josm.Main;
     16import org.openstreetmap.josm.data.osm.DataSet;
    1517import org.openstreetmap.josm.data.osm.OsmPrimitive;
    16 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    17 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     18import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
     19import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable;
     20import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    1821import org.openstreetmap.josm.tools.Shortcut;
    19 import org.openstreetmap.josm.tools.Utils;
    2022
    2123/**
     
    2325 * @since 404
    2426 */
    25 public final class CopyAction extends JosmAction {
    26 
    27     /** regular expression that matches text clipboard contents after copying */
    28     public static final String CLIPBOARD_REGEXP = "((node|way|relation)\\s\\d+,)*(node|way|relation)\\s\\d+";
    29 
     27public class CopyAction extends JosmAction {
    3028    /**
    3129     * Constructs a new {@code CopyAction}.
     
    4341    @Override
    4442    public void actionPerformed(ActionEvent e) {
    45         if (isEmptySelection()) return;
    46         Collection<OsmPrimitive> selection = getLayerManager().getEditDataSet().getSelected();
     43        DataSet set = getLayerManager().getEditDataSet();
     44        Collection<OsmPrimitive> selection = set == null ? Collections.<OsmPrimitive>emptySet() : set.getSelected();
     45        if (selection.isEmpty()) {
     46            showEmptySelectionWarning();
     47            return;
     48        }
    4749
    48         copy(getLayerManager().getEditLayer(), selection);
     50        copy(selection);
    4951    }
    5052
     
    5254     * Copies the given primitive ids to the clipboard. The output by this function
    5355     * looks similar to: node 1089302677,node 1089303458,way 93793372
    54      * @param source The OSM data layer source
    5556     * @param primitives The OSM primitives to copy
    5657     */
    57     public static void copy(OsmDataLayer source, Collection<OsmPrimitive> primitives) {
     58    public static void copy(Collection<OsmPrimitive> primitives) {
    5859        // copy ids to the clipboard
    59         String ids = getCopyString(primitives);
    60         Utils.copyToClipboard(ids);
    61 
    62         Main.pasteBuffer.makeCopy(primitives);
    63         Main.pasteSource = source;
    64     }
    65 
    66     static String getCopyString(Collection<? extends OsmPrimitive> primitives) {
    67         StringBuilder idsBuilder = new StringBuilder();
    68         for (OsmPrimitive p : primitives) {
    69             idsBuilder.append(OsmPrimitiveType.from(p).getAPIName()).append(' ').append(p.getId()).append(',');
    70         }
    71         return idsBuilder.substring(0, idsBuilder.length() - 1);
     60        ClipboardUtils.copy(new PrimitiveTransferable(PrimitiveTransferData.getDataWithReferences(primitives)));
    7261    }
    7362
     
    8271    }
    8372
    84     private boolean isEmptySelection() {
    85         Collection<OsmPrimitive> sel = getLayerManager().getEditDataSet().getSelected();
    86         if (sel.isEmpty()) {
    87             JOptionPane.showMessageDialog(
    88                     Main.parent,
    89                     tr("Please select something to copy."),
    90                     tr("Information"),
    91                     JOptionPane.INFORMATION_MESSAGE
    92             );
    93             return true;
    94         }
    95         return false;
     73    protected void showEmptySelectionWarning() {
     74        JOptionPane.showMessageDialog(
     75                Main.parent,
     76                tr("Please select something to copy."),
     77                tr("Information"),
     78                JOptionPane.INFORMATION_MESSAGE
     79        );
    9680    }
    9781}
  • trunk/src/org/openstreetmap/josm/actions/CopyCoordinatesAction.java

    r10448 r10604  
    1212import org.openstreetmap.josm.data.osm.Node;
    1313import org.openstreetmap.josm.data.osm.OsmPrimitive;
     14import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    1415import org.openstreetmap.josm.tools.Shortcut;
    1516import org.openstreetmap.josm.tools.Utils;
     
    3536            s.append('\n');
    3637        }
    37         Utils.copyToClipboard(s.toString().trim());
     38        ClipboardUtils.copyString(s.toString().trim());
    3839    }
    3940
  • trunk/src/org/openstreetmap/josm/actions/DuplicateAction.java

    r10409 r10604  
    1212import org.openstreetmap.josm.Main;
    1313import org.openstreetmap.josm.data.osm.OsmPrimitive;
    14 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
     14import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
     15import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable;
     16import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    1517import org.openstreetmap.josm.tools.Shortcut;
    1618
     19/**
     20 * An action that dupplicates the given nodes. They are not added to the clipboard.
     21 */
    1722public final class DuplicateAction extends JosmAction {
    1823
     
    2227    public DuplicateAction() {
    2328        super(tr("Duplicate"), "duplicate",
    24                 tr("Duplicate selection by copy and immediate paste."),
     29                tr("Duplicate selection."),
    2530                Shortcut.registerShortcut("system:duplicate", tr("Edit: {0}", tr("Duplicate")), KeyEvent.VK_D, Shortcut.CTRL), true);
    2631        putValue("help", ht("/Action/Duplicate"));
     
    2934    @Override
    3035    public void actionPerformed(ActionEvent e) {
    31         Main.main.menu.paste.pasteData(
    32                 new PrimitiveDeepCopy(getLayerManager().getEditDataSet().getSelected()), getLayerManager().getEditLayer(), e);
     36        PrimitiveTransferData data = PrimitiveTransferData.getDataWithReferences(getLayerManager().getEditDataSet().getSelected());
     37        new OsmTransferHandler().pasteOn(Main.getLayerManager().getEditLayer(), data.getCenter(), new PrimitiveTransferable(data));
    3338    }
    3439
  • trunk/src/org/openstreetmap/josm/actions/MapRectifierWMSmenuAction.java

    r10463 r10604  
    2323import org.openstreetmap.josm.data.imagery.ImageryInfo;
    2424import org.openstreetmap.josm.gui.ExtendedDialog;
     25import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    2526import org.openstreetmap.josm.gui.layer.WMSLayer;
    2627import org.openstreetmap.josm.gui.widgets.JosmTextField;
     
    2829import org.openstreetmap.josm.tools.GBC;
    2930import org.openstreetmap.josm.tools.Shortcut;
    30 import org.openstreetmap.josm.tools.Utils;
    3131
    3232/**
     
    124124        JosmTextField tfWmsUrl = new JosmTextField(30);
    125125
    126         String clip = Utils.getClipboardContent();
     126        String clip = ClipboardUtils.getClipboardStringContent();
    127127        clip = clip == null ? "" : clip.trim();
    128128        ButtonGroup group = new ButtonGroup();
  • trunk/src/org/openstreetmap/josm/actions/PasteAction.java

    r10413 r10604  
    88import java.awt.MouseInfo;
    99import java.awt.Point;
     10import java.awt.datatransfer.FlavorEvent;
     11import java.awt.datatransfer.FlavorListener;
    1012import java.awt.event.ActionEvent;
    1113import java.awt.event.KeyEvent;
    12 import java.util.ArrayList;
    13 import java.util.HashMap;
    14 import java.util.List;
    15 import java.util.Map;
    1614
    1715import org.openstreetmap.josm.Main;
    18 import org.openstreetmap.josm.command.AddPrimitivesCommand;
    1916import org.openstreetmap.josm.data.coor.EastNorth;
    20 import org.openstreetmap.josm.data.osm.NodeData;
    21 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    22 import org.openstreetmap.josm.data.osm.PrimitiveData;
    23 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
    24 import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
    25 import org.openstreetmap.josm.data.osm.RelationData;
    26 import org.openstreetmap.josm.data.osm.RelationMemberData;
    27 import org.openstreetmap.josm.data.osm.WayData;
    28 import org.openstreetmap.josm.gui.ExtendedDialog;
    29 import org.openstreetmap.josm.gui.layer.Layer;
     17import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
     18import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    3019import org.openstreetmap.josm.tools.Shortcut;
    3120
     
    3423 * @since 404
    3524 */
    36 public final class PasteAction extends JosmAction implements PasteBufferChangedListener {
     25public final class PasteAction extends JosmAction implements FlavorListener {
     26
     27    private final OsmTransferHandler transferHandler;
    3728
    3829    /**
     
    4637        Main.registerActionShortcut(this,
    4738                Shortcut.registerShortcut("system:paste:cua", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_INSERT, Shortcut.SHIFT));
    48         Main.pasteBuffer.addPasteBufferChangedListener(this);
     39        transferHandler = new OsmTransferHandler();
     40        ClipboardUtils.getClipboard().addFlavorListener(this);
    4941    }
    5042
    5143    @Override
    5244    public void actionPerformed(ActionEvent e) {
    53         if (!isEnabled())
    54             return;
    55         pasteData(Main.pasteBuffer, Main.pasteSource, e);
    56     }
    57 
    58     /**
    59      * Paste OSM primitives from the given paste buffer and OSM data layer source to the current edit layer.
    60      * @param pasteBuffer The paste buffer containing primitive ids to copy
    61      * @param source The OSM data layer used to look for primitive ids
    62      * @param e The ActionEvent that triggered this operation
    63      */
    64     public void pasteData(PrimitiveDeepCopy pasteBuffer, Layer source, ActionEvent e) {
    65         /* Find the middle of the pasteBuffer area */
    66         double maxEast = -1E100;
    67         double minEast = 1E100;
    68         double maxNorth = -1E100;
    69         double minNorth = 1E100;
    70         boolean incomplete = false;
    71         for (PrimitiveData data : pasteBuffer.getAll()) {
    72             if (data instanceof NodeData) {
    73                 NodeData n = (NodeData) data;
    74                 if (n.getEastNorth() != null) {
    75                     double east = n.getEastNorth().east();
    76                     double north = n.getEastNorth().north();
    77                     if (east > maxEast) {
    78                         maxEast = east;
    79                     }
    80                     if (east < minEast) {
    81                         minEast = east;
    82                     }
    83                     if (north > maxNorth) {
    84                         maxNorth = north;
    85                     }
    86                     if (north < minNorth) {
    87                         minNorth = north;
    88                     }
    89                 }
    90             }
    91             if (data.isIncomplete()) {
    92                 incomplete = true;
    93             }
    94         }
    95 
    96         // Allow to cancel paste if there are incomplete primitives
    97         if (incomplete && !confirmDeleteIncomplete()) {
    98             return;
    99         }
    100 
    10145        // default to paste in center of map (pasted via menu or cursor not in MapView)
    10246        EastNorth mPosition = Main.map.mapView.getCenter();
     
    11357        }
    11458
    115         double offsetEast = mPosition.east() - (maxEast + minEast)/2.0;
    116         double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
    117 
    118         // Make a copy of pasteBuffer and map from old id to copied data id
    119         List<PrimitiveData> bufferCopy = new ArrayList<>();
    120         List<PrimitiveData> toSelect = new ArrayList<>();
    121         Map<Long, Long> newNodeIds = new HashMap<>();
    122         Map<Long, Long> newWayIds = new HashMap<>();
    123         Map<Long, Long> newRelationIds = new HashMap<>();
    124         for (PrimitiveData data: pasteBuffer.getAll()) {
    125             if (data.isIncomplete()) {
    126                 continue;
    127             }
    128             PrimitiveData copy = data.makeCopy();
    129             copy.clearOsmMetadata();
    130             if (data instanceof NodeData) {
    131                 newNodeIds.put(data.getUniqueId(), copy.getUniqueId());
    132             } else if (data instanceof WayData) {
    133                 newWayIds.put(data.getUniqueId(), copy.getUniqueId());
    134             } else if (data instanceof RelationData) {
    135                 newRelationIds.put(data.getUniqueId(), copy.getUniqueId());
    136             }
    137             bufferCopy.add(copy);
    138             if (pasteBuffer.getDirectlyAdded().contains(data)) {
    139                 toSelect.add(copy);
    140             }
    141         }
    142 
    143         // Update references in copied buffer
    144         for (PrimitiveData data:bufferCopy) {
    145             if (data instanceof NodeData) {
    146                 NodeData nodeData = (NodeData) data;
    147                 if (Main.getLayerManager().getEditLayer() == source) {
    148                     nodeData.setEastNorth(nodeData.getEastNorth().add(offsetEast, offsetNorth));
    149                 }
    150             } else if (data instanceof WayData) {
    151                 List<Long> newNodes = new ArrayList<>();
    152                 for (Long oldNodeId: ((WayData) data).getNodes()) {
    153                     Long newNodeId = newNodeIds.get(oldNodeId);
    154                     if (newNodeId != null) {
    155                         newNodes.add(newNodeId);
    156                     }
    157                 }
    158                 ((WayData) data).setNodes(newNodes);
    159             } else if (data instanceof RelationData) {
    160                 List<RelationMemberData> newMembers = new ArrayList<>();
    161                 for (RelationMemberData member: ((RelationData) data).getMembers()) {
    162                     OsmPrimitiveType memberType = member.getMemberType();
    163                     Long newId;
    164                     switch (memberType) {
    165                     case NODE:
    166                         newId = newNodeIds.get(member.getMemberId());
    167                         break;
    168                     case WAY:
    169                         newId = newWayIds.get(member.getMemberId());
    170                         break;
    171                     case RELATION:
    172                         newId = newRelationIds.get(member.getMemberId());
    173                         break;
    174                     default: throw new AssertionError();
    175                     }
    176                     if (newId != null) {
    177                         newMembers.add(new RelationMemberData(member.getRole(), memberType, newId));
    178                     }
    179                 }
    180                 ((RelationData) data).setMembers(newMembers);
    181             }
    182         }
    183 
    184         /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
    185         Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect));
    186         Main.map.mapView.repaint();
    187     }
    188 
    189     private static boolean confirmDeleteIncomplete() {
    190         ExtendedDialog ed = new ExtendedDialog(Main.parent,
    191                 tr("Delete incomplete members?"),
    192                 new String[] {tr("Paste without incomplete members"), tr("Cancel")});
    193         ed.setButtonIcons(new String[] {"dialogs/relation/deletemembers", "cancel"});
    194         ed.setContent(tr("The copied data contains incomplete objects.  "
    195                 + "When pasting the incomplete objects are removed.  "
    196                 + "Do you want to paste the data without the incomplete objects?"));
    197         ed.showDialog();
    198         return ed.getValue() == 1;
     59        transferHandler.pasteOn(Main.getLayerManager().getEditLayer(), mPosition);
    19960    }
    20061
    20162    @Override
    20263    protected void updateEnabledState() {
    203         if (getLayerManager().getEditDataSet() == null || Main.pasteBuffer == null) {
    204             setEnabled(false);
    205             return;
    206         }
    207         setEnabled(!Main.pasteBuffer.isEmpty());
     64        setEnabled(getLayerManager().getEditDataSet() != null && transferHandler.isDataAvailable());
    20865    }
    20966
    21067    @Override
    211     public void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer) {
     68    public void flavorsChanged(FlavorEvent e) {
    21269        updateEnabledState();
    21370    }
  • trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java

    r10383 r10604  
    2626import org.openstreetmap.josm.data.osm.TagCollection;
    2727import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
     28import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    2829import org.openstreetmap.josm.tools.I18n;
    2930import org.openstreetmap.josm.tools.Shortcut;
    3031import org.openstreetmap.josm.tools.TextTagParser;
    31 import org.openstreetmap.josm.tools.Utils;
    3232
    3333/**
     
    4242
    4343    private static final String help = ht("/Action/PasteTags");
     44    private final OsmTransferHandler transferHandler = new OsmTransferHandler();
    4445
    4546    /**
     
    5455    }
    5556
     57    /**
     58     * Used to update the tags.
     59     */
    5660    public static class TagPaster {
    5761
     
    259263            return;
    260264
    261         String buf = Utils.getClipboardContent();
    262         if (buf == null || buf.isEmpty() || buf.matches(CopyAction.CLIPBOARD_REGEXP)) {
    263             pasteTagsFromJOSMBuffer(selection);
    264         } else {
    265             // Paste tags from arbitrary text
    266             pasteTagsFromText(selection, buf);
    267         }
     265        transferHandler.pasteTags(selection);
    268266    }
    269267
     
    290288        commitCommands(selection, commands);
    291289        return !commands.isEmpty();
    292     }
    293 
    294     /**
    295      * Paste tags from JOSM buffer
    296      * @param selection objects that will have the tags
    297      * @return false if JOSM buffer was empty
    298      */
    299     public static boolean pasteTagsFromJOSMBuffer(Collection<OsmPrimitive> selection) {
    300         List<PrimitiveData> directlyAdded = Main.pasteBuffer.getDirectlyAdded();
    301         if (directlyAdded == null || directlyAdded.isEmpty()) return false;
    302 
    303         PasteTagsAction.TagPaster tagPaster = new PasteTagsAction.TagPaster(directlyAdded, selection);
    304         List<Command> commands = new ArrayList<>();
    305         for (Tag tag : tagPaster.execute()) {
    306             commands.add(new ChangePropertyCommand(selection, tag.getKey(), "".equals(tag.getValue()) ? null : tag.getValue()));
    307         }
    308         commitCommands(selection, commands);
    309         return true;
    310290    }
    311291
  • trunk/src/org/openstreetmap/josm/data/osm/PrimitiveData.java

    r10153 r10604  
    8181        oos.writeInt(changesetId);
    8282        oos.writeInt(timestamp);
     83        oos.writeObject(keys);
    8384        oos.defaultWriteObject();
    8485    }
     
    9293        changesetId = ois.readInt();
    9394        timestamp = ois.readInt();
     95        keys = (String[]) ois.readObject();
    9496        ois.defaultReadObject();
    9597    }
  • trunk/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java

    r10600 r10604  
    22package org.openstreetmap.josm.data.osm;
    33
     4import java.awt.datatransfer.UnsupportedFlavorException;
     5import java.io.IOException;
    46import java.util.ArrayList;
    57import java.util.Collection;
    6 import java.util.HashSet;
     8import java.util.Collections;
    79import java.util.List;
    8 import java.util.Set;
    9 import java.util.concurrent.CopyOnWriteArrayList;
    1010
    11 import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
     11import org.openstreetmap.josm.Main;
     12import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
     13import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
     14import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    1215
    1316/**
    1417 * This class allows to create and keep a deep copy of primitives. Provides methods to access directly added
    1518 * primitives and reference primitives
     19 * <p>
     20 * To be removed end of 2016
    1621 * @since 2305
     22 * @deprecated This has been replaced by Swing Copy+Paste support. Use {@link OsmTransferHandler} instead.
    1723 */
     24@Deprecated
    1825public class PrimitiveDeepCopy {
    19 
    20     @FunctionalInterface
    21     public interface PasteBufferChangedListener {
    22         void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer);
    23     }
    24 
    25     private final List<PrimitiveData> directlyAdded = new ArrayList<>();
    26     private final List<PrimitiveData> referenced = new ArrayList<>();
    27     private final CopyOnWriteArrayList<PasteBufferChangedListener> listeners = new CopyOnWriteArrayList<>();
    2826
    2927    /**
     
    3533
    3634    /**
    37      * Constructs a new {@code PrimitiveDeepCopy} of given OSM primitives.
    38      * @param primitives OSM primitives to copy
    39      * @since 7961
     35     * Gets the list of primitives that were explicitly added to this copy.
     36     * @return The added primitives
    4037     */
    41     public PrimitiveDeepCopy(final Collection<? extends OsmPrimitive> primitives) {
    42         makeCopy(primitives);
    43     }
    44 
    45     /**
    46      * Replace content of the object with copy of provided primitives.
    47      * @param primitives OSM primitives to copy
    48      * @since 7961
    49      */
    50     public final void makeCopy(final Collection<? extends OsmPrimitive> primitives) {
    51         directlyAdded.clear();
    52         referenced.clear();
    53 
    54         final Set<Long> visitedNodeIds = new HashSet<>();
    55         final Set<Long> visitedWayIds = new HashSet<>();
    56         final Set<Long> visitedRelationIds = new HashSet<>();
    57 
    58         new AbstractVisitor() {
    59             private boolean firstIteration;
    60 
    61             @Override
    62             public void visit(Node n) {
    63                 if (!visitedNodeIds.add(n.getUniqueId()))
    64                     return;
    65                 (firstIteration ? directlyAdded : referenced).add(n.save());
    66             }
    67 
    68             @Override
    69             public void visit(Way w) {
    70                 if (!visitedWayIds.add(w.getUniqueId()))
    71                     return;
    72                 (firstIteration ? directlyAdded : referenced).add(w.save());
    73                 firstIteration = false;
    74                 for (Node n : w.getNodes()) {
    75                     visit(n);
    76                 }
    77             }
    78 
    79             @Override
    80             public void visit(Relation r) {
    81                 if (!visitedRelationIds.add(r.getUniqueId()))
    82                     return;
    83                 (firstIteration ? directlyAdded : referenced).add(r.save());
    84                 firstIteration = false;
    85                 for (RelationMember m : r.getMembers()) {
    86                     m.getMember().accept(this);
    87                 }
    88             }
    89 
    90             public void visitAll() {
    91                 for (OsmPrimitive osm : primitives) {
    92                     firstIteration = true;
    93                     osm.accept(this);
    94                 }
    95             }
    96         }.visitAll();
    97 
    98         firePasteBufferChanged();
    99     }
    100 
    10138    public List<PrimitiveData> getDirectlyAdded() {
    102         return directlyAdded;
    103     }
    104 
    105     public List<PrimitiveData> getReferenced() {
    106         return referenced;
    107     }
    108 
    109     public List<PrimitiveData> getAll() {
    110         List<PrimitiveData> result = new ArrayList<>(directlyAdded.size() + referenced.size());
    111         result.addAll(directlyAdded);
    112         result.addAll(referenced);
    113         return result;
     39        try {
     40            PrimitiveTransferData data = (PrimitiveTransferData) ClipboardUtils.getClipboard().getData(PrimitiveTransferData.DATA_FLAVOR);
     41            return new ArrayList<>(data.getDirectlyAdded());
     42        } catch (UnsupportedFlavorException | IOException e) {
     43            Main.debug(e);
     44            return Collections.emptyList();
     45        }
    11446    }
    11547
    11648    public boolean isEmpty() {
    117         return directlyAdded.isEmpty() && referenced.isEmpty();
    118     }
    119 
    120     private void firePasteBufferChanged() {
    121         for (PasteBufferChangedListener listener: listeners) {
    122             listener.pasteBufferChanged(this);
    123         }
    124     }
    125 
    126     public void addPasteBufferChangedListener(PasteBufferChangedListener listener) {
    127         listeners.addIfAbsent(listener);
    128     }
    129 
    130     public void removePasteBufferChangedListener(PasteBufferChangedListener listener) {
    131         listeners.remove(listener);
     49        return !ClipboardUtils.getClipboard().isDataFlavorAvailable(PrimitiveTransferData.DATA_FLAVOR);
    13250    }
    13351}
  • trunk/src/org/openstreetmap/josm/data/osm/TagMap.java

    r9978 r10604  
    22package org.openstreetmap.josm.data.osm;
    33
     4import java.io.Serializable;
    45import java.util.AbstractMap;
    56import java.util.AbstractSet;
     7import java.util.ArrayList;
    68import java.util.Arrays;
    79import java.util.ConcurrentModificationException;
    810import java.util.Iterator;
     11import java.util.List;
     12import java.util.Map;
    913import java.util.NoSuchElementException;
    1014import java.util.Set;
     
    1721 * @author Michael Zangl
    1822 */
    19 public class TagMap extends AbstractMap<String, String> {
     23public class TagMap extends AbstractMap<String, String> implements Serializable {
     24    static final long serialVersionUID = 1;
    2025    /**
    2126     * We use this array every time we want to represent an empty map.
     
    108113     */
    109114    public TagMap() {
    110         this(null);
     115        this((String[]) null);
     116    }
     117
     118    /**
     119     * Create a new tag map and load it from the other map.
     120     * @param tags The map to load from.
     121     * @since 10604
     122     */
     123    public TagMap(Map<String, String> tags) {
     124        putAll(tags);
     125    }
     126
     127    /**
     128     * Copy constructor.
     129     * @param tagMap The map to copy from.
     130     * @since 10604
     131     */
     132    public TagMap(TagMap tagMap) {
     133        this(tagMap.tags);
    111134    }
    112135
     
    210233
    211234    /**
     235     * Gets a list of all tags contained in this map.
     236     * @return The list of tags in the order they were added.
     237     * @since 10604
     238     */
     239    public List<Tag> getTags() {
     240        List<Tag> tagList = new ArrayList<>();
     241        for (int i = 0; i < tags.length; i += 2) {
     242            tagList.add(new Tag(tags[i], tags[i+1]));
     243        }
     244        return tagList;
     245    }
     246
     247    /**
    212248     * Finds a key in an array that is structured like the {@link #tags} array and returns the position.
    213249     * <p>
  • trunk/src/org/openstreetmap/josm/gui/MapFrame.java

    r10600 r10604  
    99import java.awt.Dimension;
    1010import java.awt.Font;
    11 import java.awt.GraphicsEnvironment;
    1211import java.awt.GridBagLayout;
    1312import java.awt.Rectangle;
     
    197196
    198197        mapView = new MapView(Main.getLayerManager(), contentPane, viewportData);
    199         if (!GraphicsEnvironment.isHeadless()) {
    200             new FileDrop(mapView);
    201         }
    202198
    203199        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
  • trunk/src/org/openstreetmap/josm/gui/MapView.java

    r10600 r10604  
    5353import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
    5454import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
     55import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    5556import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
    5657import org.openstreetmap.josm.gui.layer.GpxLayer;
     
    581582            add(c);
    582583        }
     584        setTransferHandler(new OsmTransferHandler());
    583585    }
    584586
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferable.java

    r9969 r10604  
    55import java.awt.datatransfer.Transferable;
    66import java.awt.datatransfer.UnsupportedFlavorException;
    7 import java.io.Serializable;
    87import java.util.ArrayList;
    9 import java.util.Collection;
     8import java.util.Arrays;
     9import java.util.List;
    1010
    11 import org.openstreetmap.josm.data.osm.OsmPrimitive;
     11import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    1212import org.openstreetmap.josm.data.osm.PrimitiveData;
    13 import org.openstreetmap.josm.gui.DefaultNameFormatter;
    14 import org.openstreetmap.josm.tools.CheckParameterUtil;
     13import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
     14import org.openstreetmap.josm.gui.datatransfer.data.TagTransferData;
    1515
    1616/**
    17  * Transferable objects for {@link PrimitiveData}.
     17 * Transferable objects for {@link PrimitiveTransferData} objects
    1818 * @since 9369
     19 * @since 10604 Complete rework
    1920 */
    2021public class PrimitiveTransferable implements Transferable {
    2122
    2223    /**
    23      * A wrapper for a collection of {@link PrimitiveData}.
     24     * The flavors that are available for normal primitives.
    2425     */
    25     public static final class Data implements Serializable {
    26         private static final long serialVersionUID = -1485089993600213704L;
    27         private final Collection<PrimitiveData> primitiveData;
    28 
    29         private Data(Collection<PrimitiveData> primitiveData) {
    30             CheckParameterUtil.ensureThat(primitiveData instanceof Serializable, "primitiveData must be instanceof Serializable");
    31             this.primitiveData = primitiveData;
    32         }
    33 
    34         /**
    35          * Returns the contained {@link PrimitiveData}
    36          * @return the contained {@link PrimitiveData}
    37          */
    38         public Collection<PrimitiveData> getPrimitiveData() {
    39             return primitiveData;
    40         }
    41     }
    42 
    43     /**
    44      * Data flavor for {@link PrimitiveData} which is wrapped in {@link Data}.
    45      */
    46     public static final DataFlavor PRIMITIVE_DATA = new DataFlavor(Data.class, Data.class.getName());
    47     private final Collection<? extends OsmPrimitive> primitives;
     26    private static final List<DataFlavor> PRIMITIVE_FLAVORS = Arrays.asList(PrimitiveTransferData.DATA_FLAVOR,
     27            TagTransferData.FLAVOR, DataFlavor.stringFlavor);
     28    private final PrimitiveTransferData primitives;
    4829
    4930    /**
     
    5132     * @param primitives collection of OSM primitives
    5233     */
    53     public PrimitiveTransferable(Collection<? extends OsmPrimitive> primitives) {
     34    public PrimitiveTransferable(PrimitiveTransferData primitives) {
    5435        this.primitives = primitives;
    5536    }
     
    5738    @Override
    5839    public DataFlavor[] getTransferDataFlavors() {
    59         return new DataFlavor[]{PRIMITIVE_DATA, DataFlavor.stringFlavor};
     40        ArrayList<DataFlavor> flavors = new ArrayList<>(PRIMITIVE_FLAVORS);
     41        return flavors.toArray(new DataFlavor[flavors.size()]);
    6042    }
    6143
    6244    @Override
    6345    public boolean isDataFlavorSupported(DataFlavor flavor) {
    64         return flavor == PRIMITIVE_DATA;
     46        DataFlavor[] flavors = getTransferDataFlavors();
     47        for (DataFlavor f : flavors) {
     48            if (flavor.equals(f)) {
     49                return true;
     50            }
     51        }
     52        return false;
    6553    }
    6654
     
    6957        if (DataFlavor.stringFlavor.equals(flavor)) {
    7058            return getStringData();
    71         } else if (PRIMITIVE_DATA.equals(flavor)) {
    72             return getPrimitiveData();
     59        } else if (PrimitiveTransferData.DATA_FLAVOR.equals(flavor)) {
     60            return primitives;
     61        } else if (TagTransferData.FLAVOR.equals(flavor)) {
     62            return new TagTransferData(primitives.getDirectlyAdded());
     63        } else {
     64            throw new UnsupportedFlavorException(flavor);
    7365        }
    74         throw new UnsupportedFlavorException(flavor);
    7566    }
    7667
    7768    protected String getStringData() {
    7869        final StringBuilder sb = new StringBuilder();
    79         for (OsmPrimitive primitive : primitives) {
    80             sb.append(primitive.getType())
    81               .append(' ').append(primitive.getUniqueId())
    82               .append(" # ").append(primitive.getDisplayName(DefaultNameFormatter.getInstance()))
    83               .append('\n');
     70        for (PrimitiveData primitive : primitives.getAll()) {
     71            if (sb.length() > 0) {
     72                sb.append("\n");
     73            }
     74            sb.append(OsmPrimitiveType.from(primitive).getAPIName()).append(' ').append(primitive.getId());
    8475        }
    8576        return sb.toString().replace("\u200E", "").replace("\u200F", "");
    8677    }
    87 
    88     protected Data getPrimitiveData() {
    89         final Collection<PrimitiveData> r = new ArrayList<>(primitives.size());
    90         for (OsmPrimitive primitive : primitives) {
    91             r.add(primitive.save());
    92         }
    93         return new Data(r);
    94     }
    9578}
  • trunk/src/org/openstreetmap/josm/gui/datatransfer/RelationMemberTransferable.java

    r9969 r10604  
    77import java.io.Serializable;
    88import java.util.ArrayList;
     9import java.util.Arrays;
    910import java.util.Collection;
     11import java.util.Collections;
     12import java.util.HashSet;
    1013
     14import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1115import org.openstreetmap.josm.data.osm.RelationMember;
    1216import org.openstreetmap.josm.data.osm.RelationMemberData;
    1317import org.openstreetmap.josm.gui.DefaultNameFormatter;
     18import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    1419import org.openstreetmap.josm.tools.CheckParameterUtil;
    1520
     
    3742         */
    3843        public Collection<RelationMemberData> getRelationMemberData() {
    39             return relationMemberDatas;
     44            return Collections.unmodifiableCollection(relationMemberDatas);
     45        }
     46
     47        /**
     48         * Gets the Data for the given list of members.
     49         * @param members The collection. The order is preserved.
     50         * @return The data.
     51         */
     52        public static Data getData(Collection<RelationMember> members) {
     53            final Collection<RelationMemberData> r = new ArrayList<>(members.size());
     54            for (RelationMember member : members) {
     55                r.add(new RelationMemberData(member.getRole(), member.getType(), member.getUniqueId()));
     56            }
     57            return new Data(r);
    4058        }
    4159    }
     
    4462     * Data flavor for {@link RelationMemberData} which is wrapped in {@link Data}.
    4563     */
    46     public static final DataFlavor RELATION_MEMBER_DATA = new DataFlavor(Data.class, Data.class.getName());
     64    public static final DataFlavor RELATION_MEMBER_DATA = new DataFlavor(Data.class, "Relation member");
    4765    private final Collection<RelationMember> members;
    4866
     
    5270     */
    5371    public RelationMemberTransferable(Collection<RelationMember> members) {
    54         this.members = members;
     72        this.members = new ArrayList<>(members);
    5573    }
    5674
    5775    @Override
    5876    public DataFlavor[] getTransferDataFlavors() {
    59         return new DataFlavor[]{RELATION_MEMBER_DATA, DataFlavor.stringFlavor};
     77        return new DataFlavor[]{RELATION_MEMBER_DATA, PrimitiveTransferData.DATA_FLAVOR, DataFlavor.stringFlavor};
    6078    }
    6179
    6280    @Override
    6381    public boolean isDataFlavorSupported(DataFlavor flavor) {
    64         return flavor == RELATION_MEMBER_DATA;
     82        return Arrays.asList(getTransferDataFlavors()).contains(flavor);
    6583    }
    6684
     
    7189        } else if (RELATION_MEMBER_DATA.equals(flavor)) {
    7290            return getRelationMemberData();
     91        } else if (PrimitiveTransferData.DATA_FLAVOR.equals(flavor)) {
     92            return getPrimitiveData();
    7393        }
    7494        throw new UnsupportedFlavorException(flavor);
     95    }
     96
     97    private PrimitiveTransferData getPrimitiveData() {
     98        Collection<OsmPrimitive> primitives = new HashSet<>();
     99        for (RelationMember member : members) {
     100            primitives.add(member.getMember());
     101        }
     102        return PrimitiveTransferData.getData(primitives);
    75103    }
    76104
     
    88116
    89117    protected Data getRelationMemberData() {
    90         final Collection<RelationMemberData> r = new ArrayList<>(members.size());
    91         for (RelationMember member : members) {
    92             r.add(new RelationMemberData(member.getRole(), member.getType(), member.getUniqueId()));
    93         }
    94         return new Data(r);
     118        return Data.getData(members);
    95119    }
    96120}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/OsmIdSelectionDialog.java

    r10173 r10604  
    3434import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    3535import org.openstreetmap.josm.gui.ExtendedDialog;
     36import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    3637import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
    3738import org.openstreetmap.josm.gui.widgets.HtmlPanel;
     
    202203
    203204    protected void tryToPasteFromClipboard(OsmIdTextField tfId, OsmPrimitiveTypesComboBox cbType) {
    204         String buf = Utils.getClipboardContent();
     205        String buf = ClipboardUtils.getClipboardStringContent();
    205206        if (buf == null || buf.isEmpty()) return;
    206207        if (buf.length() > Main.pref.getInteger("downloadprimitive.max-autopaste-length", 2000)) return;
  • trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java

    r10453 r10604  
    7070import org.openstreetmap.josm.gui.SideButton;
    7171import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable;
     72import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    7273import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
    7374import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
     
    881882        @Override
    882883        protected Transferable createTransferable(JComponent c) {
    883             return new PrimitiveTransferable(getSelectedPrimitives());
     884            return new PrimitiveTransferable(PrimitiveTransferData.getData(getSelectedPrimitives()));
    884885        }
    885886    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java

    r10446 r10604  
    8787import org.openstreetmap.josm.gui.PopupMenuHandler;
    8888import org.openstreetmap.josm.gui.SideButton;
     89import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    8990import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
    9091import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
     
    12651266            String key = editHelper.getDataKey(tagTable.getSelectedRow());
    12661267            Collection<OsmPrimitive> sel = Main.main.getInProgressSelection();
    1267             String clipboard = Utils.getClipboardContent();
     1268            String clipboard = ClipboardUtils.getClipboardStringContent();
    12681269            if (sel.isEmpty() || clipboard == null)
    12691270                return;
     
    12951296            }
    12961297            if (!values.isEmpty()) {
    1297                 Utils.copyToClipboard(Utils.join("\n", values));
     1298                ClipboardUtils.copyString(Utils.join("\n", values));
    12981299            }
    12991300        }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/properties/TagEditHelper.java

    r10413 r10604  
    7777import org.openstreetmap.josm.data.preferences.StringProperty;
    7878import org.openstreetmap.josm.gui.ExtendedDialog;
     79import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    7980import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
    8081import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingComboBox;
     
    584585        private void selectACComboBoxSavingUnixBuffer(AutoCompletingComboBox cb) {
    585586            // select combobox with saving unix system selection (middle mouse paste)
    586             Clipboard sysSel = GuiHelper.getSystemSelection();
     587            Clipboard sysSel = ClipboardUtils.getSystemSelection();
    587588            if (sysSel != null) {
    588                 Transferable old = Utils.getTransferableContent(sysSel);
     589                Transferable old = ClipboardUtils.getClipboardContent(sysSel);
    589590                cb.requestFocusInWindow();
    590591                cb.getEditor().selectAll();
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java

    r10454 r10604  
    1212import java.awt.GridBagLayout;
    1313import java.awt.Window;
     14import java.awt.datatransfer.Clipboard;
     15import java.awt.datatransfer.FlavorListener;
    1416import java.awt.event.ActionEvent;
    1517import java.awt.event.FocusAdapter;
     
    6264import org.openstreetmap.josm.gui.DefaultNameFormatter;
    6365import org.openstreetmap.josm.gui.MainMenu;
     66import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    6467import org.openstreetmap.josm.gui.dialogs.relation.actions.AddSelectedAfterSelection;
    6568import org.openstreetmap.josm.gui.dialogs.relation.actions.AddSelectedAtEndAction;
     
    153156     */
    154157    private final CancelAction cancelAction;
     158    /**
     159     * A list of listeners that need to be notified on clipboard content changes.
     160     */
     161    private final ArrayList<FlavorListener> clipboardListeners = new ArrayList<>();
    155162
    156163    /**
     
    274281        // CHECKSTYLE.ON: LineLength
    275282
    276         registerCopyPasteAction(new PasteMembersAction(memberTableModel, getLayer(), this) {
     283        registerCopyPasteAction(new PasteMembersAction(memberTable, getLayer(), this) {
    277284            @Override
    278285            public void actionPerformed(ActionEvent e) {
     
    743750        }
    744751        super.setVisible(visible);
     752        Clipboard clipboard = ClipboardUtils.getClipboard();
    745753        if (visible) {
    746754            leftButtonToolbar.sortBelowButton.setVisible(ExpertToggleAction.isExpert());
     
    750758            }
    751759            tagEditorPanel.requestFocusInWindow();
     760            for (FlavorListener listener : clipboardListeners) {
     761                clipboard.addFlavorListener(listener);
     762            }
    752763        } else {
    753764            // make sure all registered listeners are unregistered
     
    760771                Main.main.menu.windowMenu.remove(windowMenuItem);
    761772                windowMenuItem = null;
     773            }
     774            for (FlavorListener listener : clipboardListeners) {
     775                clipboard.removeFlavorListener(listener);
    762776            }
    763777            dispose();
     
    824838    }
    825839
    826     private static void registerCopyPasteAction(AbstractAction action, Object actionName, KeyStroke shortcut,
     840    private void registerCopyPasteAction(AbstractAction action, Object actionName, KeyStroke shortcut,
    827841            JRootPane rootPane, JTable... tables) {
    828842        int mods = shortcut.getModifiers();
     
    840854            table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(shortcut, actionName);
    841855            table.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(shortcut, actionName);
     856        }
     857        if (action instanceof FlavorListener) {
     858            clipboardListeners.add((FlavorListener) action);
    842859        }
    843860    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTable.java

    r10478 r10604  
    8686        zoomToGap = new ZoomToGapAction();
    8787        registerListeners();
     88        menu.addSeparator();
    8889        getSelectionModel().addListSelectionListener(zoomToGap);
    8990        menu.add(zoomToGap);
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java

    r10413 r10604  
    480480        }
    481481        invalidateConnectionType();
    482         final List<Integer> selection = getSelectedIndices();
    483482        fireTableRowsInserted(index, idx - 1);
    484         setSelectedMembersIdx(selection);
    485483    }
    486484
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTransferHandler.java

    r10212 r10604  
    1717import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1818import org.openstreetmap.josm.data.osm.PrimitiveData;
     19import org.openstreetmap.josm.data.osm.PrimitiveId;
    1920import org.openstreetmap.josm.data.osm.RelationMember;
    2021import org.openstreetmap.josm.data.osm.RelationMemberData;
    21 import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable;
    2222import org.openstreetmap.josm.gui.datatransfer.RelationMemberTransferable;
    23 import org.openstreetmap.josm.tools.Utils.Function;
     23import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
    2424
    25 class MemberTransferHandler extends TransferHandler {
     25/**
     26 * A transfer handler that helps with importing / exporting members for relations.
     27 * @author Michael Zangl
     28 * @since 10604
     29 */
     30public class MemberTransferHandler extends TransferHandler {
    2631
    2732    @Override
     
    3843    @Override
    3944    public boolean canImport(TransferSupport support) {
    40         support.setShowDropLocation(true);
     45        if (support.isDrop()) {
     46            support.setShowDropLocation(true);
     47        }
    4148        return support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA)
    42                 || support.isDataFlavorSupported(PrimitiveTransferable.PRIMITIVE_DATA);
     49                || support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR);
    4350    }
    4451
    4552    @Override
    4653    public boolean importData(TransferSupport support) {
    47         final MemberTable destination = (MemberTable) support.getComponent();
    48         final int insertRow = ((JTable.DropLocation) support.getDropLocation()).getRow();
     54        MemberTable destination = (MemberTable) support.getComponent();
     55        int insertRow = computeInsertionRow(support, destination);
    4956
     57        return importDataAt(support, destination, insertRow);
     58    }
     59
     60    private int computeInsertionRow(TransferSupport support, MemberTable destination) {
     61        final int insertRow;
     62        if (support.isDrop()) {
     63            insertRow = ((JTable.DropLocation) support.getDropLocation()).getRow();
     64        } else {
     65            int selection = destination.getSelectedRow();
     66            if (selection < 0) {
     67                // no selection, add at the end.
     68                insertRow = destination.getRowCount();
     69            } else {
     70                insertRow = selection;
     71            }
     72        }
     73        return insertRow;
     74    }
     75
     76    private boolean importDataAt(TransferSupport support, MemberTable destination, int insertRow) {
    5077        try {
    5178            if (support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA)) {
    5279                importRelationMemberData(support, destination, insertRow);
    53             } else if (support.isDataFlavorSupported(PrimitiveTransferable.PRIMITIVE_DATA)) {
     80                return true;
     81            } else if (support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR)) {
    5482                importPrimitiveData(support, destination, insertRow);
     83                return true;
     84            } else {
     85                return false;
    5586            }
    5687        } catch (IOException | UnsupportedFlavorException e) {
     
    5889            return false;
    5990        }
    60 
    61         return true;
    6291    }
    6392
     
    6695        final RelationMemberTransferable.Data memberData = (RelationMemberTransferable.Data)
    6796                support.getTransferable().getTransferData(RelationMemberTransferable.RELATION_MEMBER_DATA);
    68         importData(destination, insertRow, memberData.getRelationMemberData(), new Function<RelationMemberData, RelationMember>() {
     97        importData(destination, insertRow, memberData.getRelationMemberData(), new AbstractRelationMemberConverter<RelationMemberData>() {
    6998            @Override
    70             public RelationMember apply(RelationMemberData member) {
    71                 final OsmPrimitive p = destination.getLayer().data.getPrimitiveById(member.getUniqueId(), member.getType());
    72                 if (p == null) {
    73                     Main.warn(tr("Cannot add {0} since it is not part of dataset", member));
    74                     return null;
    75                 } else {
    76                     return new RelationMember(member.getRole(), p);
    77                 }
     99            protected RelationMember getMember(MemberTable destination, RelationMemberData data, OsmPrimitive p) {
     100                return new RelationMember(data.getRole(), p);
    78101            }
    79102        });
     
    82105    protected void importPrimitiveData(TransferSupport support, final MemberTable destination, int insertRow)
    83106            throws UnsupportedFlavorException, IOException {
    84         final PrimitiveTransferable.Data data = (PrimitiveTransferable.Data)
    85                 support.getTransferable().getTransferData(PrimitiveTransferable.PRIMITIVE_DATA);
    86         importData(destination, insertRow, data.getPrimitiveData(), new Function<PrimitiveData, RelationMember>() {
     107        final PrimitiveTransferData data = (PrimitiveTransferData)
     108                support.getTransferable().getTransferData(PrimitiveTransferData.DATA_FLAVOR);
     109        importData(destination, insertRow, data.getDirectlyAdded(), new AbstractRelationMemberConverter<PrimitiveData>() {
    87110            @Override
    88             public RelationMember apply(PrimitiveData data) {
    89                 final OsmPrimitive p = destination.getLayer().data.getPrimitiveById(data);
    90                 if (p == null) {
    91                     Main.warn(tr("Cannot add {0} since it is not part of dataset", data));
    92                     return null;
    93                 } else {
    94                     return destination.getMemberTableModel().getRelationMemberForPrimitive(p);
    95                 }
     111            protected RelationMember getMember(MemberTable destination, PrimitiveData data, OsmPrimitive p) {
     112                return destination.getMemberTableModel().getRelationMemberForPrimitive(p);
    96113            }
    97114        });
    98115    }
    99116
    100     protected <T> void importData(MemberTable destination, int insertRow,
    101                                   Collection<T> memberData, Function<T, RelationMember> toMemberFunction) {
     117    protected <T extends PrimitiveId> void importData(MemberTable destination, int insertRow,
     118                                  Collection<T> memberData, AbstractRelationMemberConverter<T> toMemberFunction) {
    102119        final Collection<RelationMember> membersToAdd = new ArrayList<>(memberData.size());
    103         for (T i : memberData) {
    104             final RelationMember member = toMemberFunction.apply(i);
     120        for (T data : memberData) {
     121            final RelationMember member = toMemberFunction.importPrimitive(destination, data);
    105122            if (member != null) {
    106123                membersToAdd.add(member);
     
    120137        model.selectionChanged(null);
    121138    }
     139
     140    private abstract static class AbstractRelationMemberConverter<T extends PrimitiveId> {
     141        protected RelationMember importPrimitive(MemberTable destination, T data) {
     142            final OsmPrimitive p = destination.getLayer().data.getPrimitiveById(data);
     143            if (p == null) {
     144                Main.warn(tr("Cannot add {0} since it is not part of dataset", data));
     145                return null;
     146            } else {
     147                return getMember(destination, data, p);
     148            }
     149        }
     150
     151        protected abstract RelationMember getMember(MemberTable destination, T data, OsmPrimitive p);
     152    }
    122153}
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/CopyMembersAction.java

    r9710 r10604  
    55import java.util.Collection;
    66
    7 import org.openstreetmap.josm.actions.CopyAction;
    8 import org.openstreetmap.josm.data.osm.OsmPrimitive;
     7import org.openstreetmap.josm.data.osm.RelationMember;
     8import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
     9import org.openstreetmap.josm.gui.datatransfer.RelationMemberTransferable;
    910import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
    1011import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel;
     
    2930    @Override
    3031    public void actionPerformed(ActionEvent e) {
    31         final Collection<OsmPrimitive> primitives = memberTableModel.getSelectedChildPrimitives();
    32         if (!primitives.isEmpty()) {
    33             CopyAction.copy(layer, primitives);
     32        final Collection<RelationMember> members = memberTableModel.getSelectedMembers();
     33
     34        if (!members.isEmpty()) {
     35            ClipboardUtils.copy(new RelationMemberTransferable(members));
    3436        }
    3537    }
  • trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/PasteMembersAction.java

    r10420 r10604  
    22package org.openstreetmap.josm.gui.dialogs.relation.actions;
    33
    4 import static org.openstreetmap.josm.tools.I18n.tr;
     4import java.awt.datatransfer.FlavorEvent;
     5import java.awt.datatransfer.FlavorListener;
     6import java.awt.event.ActionEvent;
    57
    6 import java.awt.event.ActionEvent;
    7 import java.util.ArrayList;
    8 import java.util.List;
     8import javax.swing.TransferHandler.TransferSupport;
    99
    10 import javax.swing.JOptionPane;
    11 
    12 import org.openstreetmap.josm.Main;
    13 import org.openstreetmap.josm.data.osm.DataSet;
    14 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    15 import org.openstreetmap.josm.data.osm.PrimitiveData;
    16 import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor.AddAbortException;
     10import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    1711import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
    18 import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel;
     12import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
     13import org.openstreetmap.josm.gui.dialogs.relation.MemberTransferHandler;
    1914import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2015
     
    2318 * @since 9496
    2419 */
    25 public class PasteMembersAction extends AddFromSelectionAction {
     20public class PasteMembersAction extends AddFromSelectionAction implements FlavorListener {
    2621
    2722    /**
    2823     * Constructs a new {@code PasteMembersAction}.
    29      * @param memberTableModel member table model
     24     * @param memberTable member table
    3025     * @param layer OSM data layer
    3126     * @param editor relation editor
    3227     */
    33     public PasteMembersAction(MemberTableModel memberTableModel, OsmDataLayer layer, IRelationEditor editor) {
    34         super(null, memberTableModel, null, null, null, layer, editor);
     28    public PasteMembersAction(MemberTable memberTable, OsmDataLayer layer, IRelationEditor editor) {
     29        super(memberTable, null, null, null, null, layer, editor);
     30        updateEnabledState();
    3531    }
    3632
    3733    @Override
    3834    public void actionPerformed(ActionEvent e) {
    39         try {
    40             List<PrimitiveData> primitives = Main.pasteBuffer.getDirectlyAdded();
    41             DataSet ds = layer.data;
    42             List<OsmPrimitive> toAdd = new ArrayList<>();
    43             boolean hasNewInOtherLayer = false;
     35        new MemberTransferHandler().importData(getSupport());
     36    }
    4437
    45             for (PrimitiveData primitive: primitives) {
    46                 OsmPrimitive primitiveInDs = ds.getPrimitiveById(primitive);
    47                 if (primitiveInDs != null) {
    48                     toAdd.add(primitiveInDs);
    49                 } else if (!primitive.isNew()) {
    50                     OsmPrimitive p = primitive.getType().newInstance(primitive.getUniqueId(), true);
    51                     ds.addPrimitive(p);
    52                     toAdd.add(p);
    53                 } else {
    54                     hasNewInOtherLayer = true;
    55                     break;
    56                 }
    57             }
    58 
    59             if (hasNewInOtherLayer) {
    60                 JOptionPane.showMessageDialog(Main.parent,
    61                         tr("Members from paste buffer cannot be added because they are not included in current layer"));
    62                 return;
    63             }
    64 
    65             toAdd = filterConfirmedPrimitives(toAdd);
    66             int index = memberTableModel.getSelectionModel().getMaxSelectionIndex();
    67             if (index == -1) {
    68                 index = memberTableModel.getRowCount() - 1;
    69             }
    70             memberTableModel.addMembersAfterIdx(toAdd, index);
    71 
    72         } catch (AddAbortException ex) {
    73             Main.trace(ex);
    74         }
     38    private TransferSupport getSupport() {
     39        return new TransferSupport(memberTable, ClipboardUtils.getClipboard().getContents(null));
    7540    }
    7641
    7742    @Override
    7843    protected void updateEnabledState() {
    79         // Do nothing
     44        setEnabled(new MemberTransferHandler().canImport(getSupport()));
     45    }
     46
     47    @Override
     48    public void flavorsChanged(FlavorEvent e) {
     49        updateEnabledState();
    8050    }
    8151}
  • trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java

    r10424 r10604  
    3939import org.openstreetmap.josm.data.Bounds;
    4040import org.openstreetmap.josm.gui.MapView;
     41import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    4142import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
    4243import org.openstreetmap.josm.gui.help.HelpUtil;
     
    4849import org.openstreetmap.josm.tools.InputMapUtils;
    4950import org.openstreetmap.josm.tools.OsmUrlToBounds;
    50 import org.openstreetmap.josm.tools.Utils;
    5151import org.openstreetmap.josm.tools.WindowGeometry;
    5252
     
    243243            @Override
    244244            public void actionPerformed(ActionEvent e) {
    245                 String clip = Utils.getClipboardContent();
     245                String clip = ClipboardUtils.getClipboardStringContent();
    246246                if (clip == null) {
    247247                    return;
  • trunk/src/org/openstreetmap/josm/gui/layer/NoteLayer.java

    r10484 r10604  
    3131import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
    3232import org.openstreetmap.josm.gui.MapView;
     33import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    3334import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    3435import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
     
    4142import org.openstreetmap.josm.tools.ColorHelper;
    4243import org.openstreetmap.josm.tools.ImageProvider;
    43 import org.openstreetmap.josm.tools.Utils;
    4444import org.openstreetmap.josm.tools.date.DateUtils;
    4545
     
    238238        if (SwingUtilities.isRightMouseButton(e) && noteData.getSelectedNote() != null) {
    239239            final String url = OsmApi.getOsmApi().getBaseUrl() + "notes/" + noteData.getSelectedNote().getId();
    240             Utils.copyToClipboard(url);
     240            ClipboardUtils.copyString(url);
    241241            return;
    242242        } else if (!SwingUtilities.isLeftMouseButton(e)) {
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java

    r10436 r10604  
    5353import org.openstreetmap.josm.gui.NavigatableComponent;
    5454import org.openstreetmap.josm.gui.PleaseWaitRunnable;
     55import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    5556import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    5657import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
     
    725726    public void copyCurrentPhotoPath() {
    726727        if (data != null && !data.isEmpty() && currentPhoto >= 0 && currentPhoto < data.size()) {
    727             Utils.copyToClipboard(data.get(currentPhoto).getFile().toString());
     728            ClipboardUtils.copyString(data.get(currentPhoto).getFile().toString());
    728729        }
    729730    }
  • trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorModel.java

    r10217 r10604  
    88import java.util.ArrayList;
    99import java.util.Collection;
     10import java.util.Collections;
    1011import java.util.Comparator;
    1112import java.util.EnumSet;
     
    2526import org.openstreetmap.josm.data.osm.Tag;
    2627import org.openstreetmap.josm.data.osm.TagCollection;
     28import org.openstreetmap.josm.data.osm.TagMap;
    2729import org.openstreetmap.josm.data.osm.Tagged;
    2830import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
     
    4749
    4850    private transient OsmPrimitive primitive;
     51
     52    private EndEditListener endEditListener;
    4953
    5054    /**
     
    167171     */
    168172    public void clear() {
     173        commitPendingEdit();
    169174        boolean wasEmpty = tags.isEmpty();
    170175        tags.clear();
     
    183188     */
    184189    public void add(TagModel tag) {
     190        commitPendingEdit();
    185191        CheckParameterUtil.ensureParameterNotNull(tag, "tag");
    186192        tags.add(tag);
     
    189195    }
    190196
     197    /**
     198     * Add a tag at the beginning of the table.
     199     *
     200     * @param tag The tag to add
     201     *
     202     * @throws IllegalArgumentException if tag is null
     203     *
     204     * @see #add(TagModel)
     205     */
    191206    public void prepend(TagModel tag) {
     207        commitPendingEdit();
    192208        CheckParameterUtil.ensureParameterNotNull(tag, "tag");
    193209        tags.add(0, tag);
     
    209225     */
    210226    public void add(String name, String value) {
     227        commitPendingEdit();
    211228        String key = (name == null) ? "" : name;
    212229        String val = (value == null) ? "" : value;
     
    259276        if (tags == null)
    260277            return;
     278        commitPendingEdit();
    261279        for (int tagIdx : tagIndices) {
    262280            TagModel tag = tags.get(tagIdx);
     
    277295        if (tags == null)
    278296            return;
     297        commitPendingEdit();
    279298        for (int tagIdx : tagIndices) {
    280299            TagModel tag = tags.get(tagIdx);
     
    293312     */
    294313    public void delete(String name) {
     314        commitPendingEdit();
    295315        if (name == null)
    296316            return;
     
    318338        if (tags == null)
    319339            return;
     340        commitPendingEdit();
    320341        List<TagModel> toDelete = new ArrayList<>();
    321342        for (int tagIdx : tagIndices) {
     
    356377     */
    357378    public void initFromPrimitive(Tagged primitive) {
     379        commitPendingEdit();
    358380        this.tags.clear();
    359381        for (String key : primitive.keySet()) {
     
    361383            this.tags.add(new TagModel(key, value));
    362384        }
     385        sort();
    363386        TagModel tag = new TagModel();
    364         sort();
    365387        tags.add(tag);
    366388        setDirty(false);
     
    374396     */
    375397    public void initFromTags(Map<String, String> tags) {
     398        commitPendingEdit();
    376399        this.tags.clear();
    377400        for (Entry<String, String> entry : tags.entrySet()) {
     
    391414     */
    392415    public void initFromTags(TagCollection tags) {
     416        commitPendingEdit();
    393417        this.tags.clear();
    394418        if (tags == null) {
     
    424448     */
    425449    private Map<String, String> applyToTags(boolean keepEmpty) {
    426         Map<String, String> result = new HashMap<>();
     450        // TagMap preserves the order of tags.
     451        TagMap result = new TagMap();
    427452        for (TagModel tag: this.tags) {
    428453            // tag still holds an unchanged list of different values for the same key.
     
    433458
    434459            // tag name holds an empty key. Don't apply it to the selection.
    435             //
    436460            if (!keepEmpty && (tag.getName().trim().isEmpty() || tag.getValue().trim().isEmpty())) {
    437461                continue;
     
    539563     */
    540564    protected void sort() {
    541         java.util.Collections.sort(
     565        Collections.sort(
    542566                tags,
    543567                new Comparator<TagModel>() {
     
    591615     */
    592616    public void updateTags(List<Tag> tags) {
    593          if (tags.isEmpty())
     617        if (tags.isEmpty())
    594618            return;
    595619
     620        commitPendingEdit();
    596621        Map<String, TagModel> modelTags = new HashMap<>();
    597622        for (int i = 0; i < getRowCount(); i++) {
     
    648673    }
    649674
     675    /**
     676     * Sets the listener that is notified when an edit should be aborted.
     677     * @param endEditListener The listener to be notified when editing should be aborted.
     678     */
     679    public void setEndEditListener(EndEditListener endEditListener) {
     680        this.endEditListener = endEditListener;
     681    }
     682
     683    private void commitPendingEdit() {
     684        if (endEditListener != null) {
     685            endEditListener.endCellEditing();
     686        }
     687    }
     688
    650689    class SelectionStateMemento {
    651690        private final int rowMin;
     
    674713        }
    675714    }
     715
     716    /**
     717     * A listener that is called whenever the cells may be updated from outside the editor and the editor should thus be commited.
     718     * @since 10604
     719     */
     720    @FunctionalInterface
     721    public interface EndEditListener {
     722        /**
     723         * Requests to end the editing of any cells on this model
     724         */
     725        void endCellEditing();
     726    }
    676727}
  • trunk/src/org/openstreetmap/josm/gui/tagging/TagTable.java

    r10378 r10604  
    22package org.openstreetmap.josm.gui.tagging;
    33
    4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    54import static org.openstreetmap.josm.tools.I18n.tr;
    65
     
    1312import java.beans.PropertyChangeEvent;
    1413import java.beans.PropertyChangeListener;
    15 import java.util.ArrayList;
    1614import java.util.Collections;
    1715import java.util.EventObject;
    18 import java.util.List;
    19 import java.util.Map;
    2016import java.util.concurrent.CopyOnWriteArrayList;
    2117
     
    3228
    3329import org.openstreetmap.josm.Main;
    34 import org.openstreetmap.josm.actions.CopyAction;
    35 import org.openstreetmap.josm.actions.PasteTagsAction;
    36 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    37 import org.openstreetmap.josm.data.osm.PrimitiveData;
    3830import org.openstreetmap.josm.data.osm.Relation;
    39 import org.openstreetmap.josm.data.osm.Tag;
     31import org.openstreetmap.josm.data.osm.TagMap;
     32import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
     33import org.openstreetmap.josm.gui.tagging.TagEditorModel.EndEditListener;
    4034import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
    4135import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager;
    4236import org.openstreetmap.josm.gui.widgets.JosmTable;
    4337import org.openstreetmap.josm.tools.ImageProvider;
    44 import org.openstreetmap.josm.tools.TextTagParser;
    45 import org.openstreetmap.josm.tools.Utils;
    4638
    4739/**
     
    4941 * @since 1762
    5042 */
    51 public class TagTable extends JosmTable {
     43public class TagTable extends JosmTable implements EndEditListener {
    5244    /** the table cell editor used by this table */
    5345    private TagCellEditor editor;
     
    211203            }
    212204
    213             if (isEditing()) {
    214                 CellEditor cEditor = getCellEditor();
    215                 if (cEditor != null) {
    216                     cEditor.cancelCellEditing();
    217                 }
    218             }
     205            endCellEditing();
    219206
    220207            if (model.getRowCount() == 0) {
     
    233220
    234221        protected final void updateEnabledState() {
    235             if (isEditing() && getSelectedColumnCount() == 1 && getSelectedRowCount() == 1) {
    236                 setEnabled(true);
    237             } else if (!isEditing() && getSelectedColumnCount() == 1 && getSelectedRowCount() == 1) {
    238                 setEnabled(true);
    239             } else if (getSelectedColumnCount() > 1 || getSelectedRowCount() > 1) {
     222            if (getSelectedColumnCount() >= 1 && getSelectedRowCount() >= 1) {
    240223                setEnabled(true);
    241224            } else {
     
    295278            Relation relation = new Relation();
    296279            model.applyToPrimitive(relation);
    297 
    298             String buf = Utils.getClipboardContent();
    299             if (buf == null || buf.isEmpty() || buf.matches(CopyAction.CLIPBOARD_REGEXP)) {
    300                 List<PrimitiveData> directlyAdded = Main.pasteBuffer.getDirectlyAdded();
    301                 if (directlyAdded == null || directlyAdded.isEmpty()) return;
    302                 PasteTagsAction.TagPaster tagPaster = new PasteTagsAction.TagPaster(directlyAdded,
    303                         Collections.<OsmPrimitive>singletonList(relation));
    304                 model.updateTags(tagPaster.execute());
    305             } else {
    306                  // Paste tags from arbitrary text
    307                  Map<String, String> tags = TextTagParser.readTagsFromText(buf);
    308                  if (tags == null || tags.isEmpty()) {
    309                     TextTagParser.showBadBufferMessage(ht("/Action/PasteTags"));
    310                  } else if (TextTagParser.validateTags(tags)) {
    311                      List<Tag> newTags = new ArrayList<>();
    312                      for (Map.Entry<String, String> entry: tags.entrySet()) {
    313                         String k = entry.getKey();
    314                         String v = entry.getValue();
    315                         newTags.add(new Tag(k, v));
    316                      }
    317                      model.updateTags(newTags);
    318                  }
    319             }
     280            new OsmTransferHandler().pasteTags(Collections.singleton(relation));
     281            model.updateTags(new TagMap(relation.getKeys()).getTags());
    320282        }
    321283
     
    415377              model.getRowSelectionModel());
    416378        this.model = model;
     379        model.setEndEditListener(this);
    417380        init(maxCharacters);
    418381    }
     
    488451     */
    489452    public void setTagCellEditor(TagCellEditor editor) {
    490         if (isEditing()) {
    491             this.editor.cancelCellEditing();
    492         }
     453        endCellEditing();
    493454        this.editor = editor;
    494455        getColumnModel().getColumn(0).setCellEditor(editor);
     
    546507        // delegate to the default implementation
    547508        return super.editCellAt(row, column, e);
     509    }
     510
     511    @Override
     512    public void endCellEditing() {
     513        if (isEditing()) {
     514            CellEditor cEditor = getCellEditor();
     515            if (cEditor != null) {
     516                // First attempt to commit. If this does not work, cancel.
     517                cEditor.stopCellEditing();
     518                cEditor.cancelCellEditing();
     519            }
     520        }
    548521    }
    549522
  • trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletingComboBox.java

    r10308 r10604  
    2424
    2525import org.openstreetmap.josm.Main;
    26 import org.openstreetmap.josm.gui.util.GuiHelper;
     26import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    2727import org.openstreetmap.josm.gui.widgets.JosmComboBox;
    28 import org.openstreetmap.josm.tools.Utils;
    2928
    3029/**
     
    134133            final JTextComponent editorComponent = comboBox.getEditorComponent();
    135134            // save unix system selection (middle mouse paste)
    136             Clipboard sysSel = GuiHelper.getSystemSelection();
     135            Clipboard sysSel = ClipboardUtils.getSystemSelection();
    137136            if (sysSel != null) {
    138                 Transferable old = Utils.getTransferableContent(sysSel);
     137                Transferable old = ClipboardUtils.getClipboardContent(sysSel);
    139138                editorComponent.select(start, end);
    140139                if (old != null) {
     
    201200                        }
    202201                        // save unix system selection (middle mouse paste)
    203                         Clipboard sysSel = GuiHelper.getSystemSelection();
     202                        Clipboard sysSel = ClipboardUtils.getSystemSelection();
    204203                        if (sysSel != null) {
    205                             Transferable old = Utils.getTransferableContent(sysSel);
     204                            Transferable old = ClipboardUtils.getClipboardContent(sysSel);
    206205                            editorComponent.selectAll();
    207206                            if (old != null) {
  • trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java

    r10471 r10604  
    2121import java.awt.Toolkit;
    2222import java.awt.Window;
    23 import java.awt.datatransfer.Clipboard;
    2423import java.awt.event.ActionListener;
    2524import java.awt.event.HierarchyEvent;
     
    497496
    498497    /**
    499      * Gets the singleton instance of the system selection as a <code>Clipboard</code> object.
    500      * This allows an application to read and modify the current, system-wide selection.
    501      * @return the system selection as a <code>Clipboard</code>, or <code>null</code> if the native platform does not
    502      *         support a system selection <code>Clipboard</code> or if GraphicsEnvironment.isHeadless() returns true
    503      * @see Toolkit#getSystemSelection
    504      * @since 9576
    505      */
    506     public static Clipboard getSystemSelection() {
    507         return GraphicsEnvironment.isHeadless() ? null : Toolkit.getDefaultToolkit().getSystemSelection();
    508     }
    509 
    510     /**
    511498     * Returns the first <code>Window</code> ancestor of event source, or
    512499     * {@code null} if event source is not a component contained inside a <code>Window</code>.
  • trunk/src/org/openstreetmap/josm/gui/widgets/AbstractIdTextField.java

    r10212 r10604  
    55
    66import org.openstreetmap.josm.Main;
    7 import org.openstreetmap.josm.tools.Utils;
     7import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    88
    99/**
     
    7171     */
    7272    public void tryToPasteFromClipboard() {
    73         tryToPasteFrom(Utils.getClipboardContent());
     73        tryToPasteFrom(ClipboardUtils.getClipboardStringContent());
    7474    }
    7575
  • trunk/src/org/openstreetmap/josm/gui/widgets/UrlLabel.java

    r10181 r10604  
    1111import javax.swing.SwingUtilities;
    1212
     13import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    1314import org.openstreetmap.josm.tools.OpenBrowser;
    14 import org.openstreetmap.josm.tools.Utils;
    1515
    1616/**
     
    112112            OpenBrowser.displayUrl(url);
    113113        } else if (SwingUtilities.isRightMouseButton(e)) {
    114             Utils.copyToClipboard(url);
     114            ClipboardUtils.copyString(url);
    115115        }
    116116    }
  • trunk/src/org/openstreetmap/josm/tools/TextTagParser.java

    r10378 r10604  
    1919import org.openstreetmap.josm.Main;
    2020import org.openstreetmap.josm.gui.ExtendedDialog;
     21import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    2122import org.openstreetmap.josm.gui.help.HelpUtil;
    2223import org.openstreetmap.josm.gui.widgets.UrlLabel;
     
    291292        if (r == 0) r = 2;
    292293        // clean clipboard if user asked
    293         if (r == 3) Utils.copyToClipboard("");
     294        if (r == 3) ClipboardUtils.copyString("");
    294295        return r;
    295296    }
     
    326327        int r = ed.getValue();
    327328        // clean clipboard if user asked
    328         if (r == 2) Utils.copyToClipboard("");
     329        if (r == 2) ClipboardUtils.copyString("");
    329330    }
    330331}
  • trunk/src/org/openstreetmap/josm/tools/Utils.java

    r10600 r10604  
    88import java.awt.Color;
    99import java.awt.Font;
    10 import java.awt.HeadlessException;
    11 import java.awt.Toolkit;
    1210import java.awt.datatransfer.Clipboard;
    13 import java.awt.datatransfer.ClipboardOwner;
    14 import java.awt.datatransfer.DataFlavor;
    15 import java.awt.datatransfer.StringSelection;
    1611import java.awt.datatransfer.Transferable;
    17 import java.awt.datatransfer.UnsupportedFlavorException;
    1812import java.awt.font.FontRenderContext;
    1913import java.awt.font.GlyphVector;
     
    7367import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
    7468import org.openstreetmap.josm.Main;
     69import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    7570import org.w3c.dom.Document;
    7671import org.xml.sax.InputSource;
     
    645640     * @param s string to be copied to clipboard.
    646641     * @return true if succeeded, false otherwise.
    647      */
     642     * @deprecated Use {@link ClipboardUtils#copyString(String)}. To be removed end of 2016.
     643     */
     644    @Deprecated
    648645    public static boolean copyToClipboard(String s) {
    649         try {
    650             Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(s), new ClipboardOwner() {
    651                 @Override
    652                 public void lostOwnership(Clipboard clpbrd, Transferable t) {
    653                     // Do nothing
    654                 }
    655             });
    656             return true;
    657         } catch (IllegalStateException | HeadlessException ex) {
    658             Main.error(ex);
    659             return false;
    660         }
     646        return ClipboardUtils.copyString(s);
    661647    }
    662648
     
    666652     * @return clipboard contents if available, {@code null} otherwise.
    667653     * @since 8429
    668      */
     654     * @deprecated Use {@link ClipboardUtils#getClipboardContent(Clipboard)} instead. To be removed end of 2016.
     655     */
     656    @Deprecated
    669657    public static Transferable getTransferableContent(Clipboard clipboard) {
    670         Transferable t = null;
    671         for (int tries = 0; t == null && tries < 10; tries++) {
    672             try {
    673                 t = clipboard.getContents(null);
    674             } catch (IllegalStateException e) {
    675                 // Clipboard currently unavailable.
    676                 // On some platforms, the system clipboard is unavailable while it is accessed by another application.
    677                 try {
    678                     Thread.sleep(1);
    679                 } catch (InterruptedException ex) {
    680                     Main.warn("InterruptedException in "+Utils.class.getSimpleName()+" while getting clipboard content");
    681                 }
    682             } catch (NullPointerException e) {
    683                 // JDK-6322854: On Linux/X11, NPE can happen for unknown reasons, on all versions of Java
    684                 Main.error(e);
    685             }
    686         }
    687         return t;
     658        return ClipboardUtils.getClipboardContent(clipboard);
    688659    }
    689660
     
    691662     * Extracts clipboard content as string.
    692663     * @return string clipboard contents if available, {@code null} otherwise.
    693      */
     664     * @deprecated Use {@link ClipboardUtils#getClipboardStringContent()}. To be removed end of 2016
     665     */
     666    @Deprecated
    694667    public static String getClipboardContent() {
    695         try {
    696             Transferable t = getTransferableContent(Toolkit.getDefaultToolkit().getSystemClipboard());
    697             if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
    698                 return (String) t.getTransferData(DataFlavor.stringFlavor);
    699             }
    700         } catch (UnsupportedFlavorException | IOException | HeadlessException ex) {
    701             Main.error(ex);
    702             return null;
    703         }
    704         return null;
     668        return ClipboardUtils.getClipboardStringContent();
    705669    }
    706670
  • trunk/src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java

    r10585 r10604  
    66import javax.swing.JScrollPane;
    77
     8import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    89import org.openstreetmap.josm.gui.widgets.JosmTextArea;
    910import org.openstreetmap.josm.tools.Utils;
     
    6162
    6263    /**
    63      * Copies the debug text to the clippboard. This includes the code tags for trac.
     64     * Copies the debug text to the clipboard. This includes the code tags for trac.
    6465     * @return <code>true</code> if copy was successful
    6566     */
    6667    public boolean copyToClippboard() {
    67         return Utils.copyToClipboard(String.format(CODE_PATTERN, text));
     68        return ClipboardUtils.copyString(String.format(CODE_PATTERN, text));
    6869    }
    6970
  • trunk/test/unit/org/openstreetmap/josm/actions/CopyActionTest.java

    r8876 r10604  
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertFalse;
     6import static org.junit.Assert.assertNotNull;
     7import static org.junit.Assert.assertTrue;
     8import static org.junit.Assert.fail;
    59
     10import java.awt.datatransfer.Clipboard;
     11import java.awt.datatransfer.DataFlavor;
     12import java.awt.datatransfer.StringSelection;
     13import java.awt.datatransfer.UnsupportedFlavorException;
     14import java.io.IOException;
    615import java.util.Arrays;
    7 import java.util.Collections;
    816
    9 import org.junit.BeforeClass;
     17import org.junit.Rule;
    1018import org.junit.Test;
    11 import org.openstreetmap.josm.JOSMFixture;
    12 import org.openstreetmap.josm.data.osm.Relation;
     19import org.openstreetmap.josm.Main;
     20import org.openstreetmap.josm.data.coor.LatLon;
     21import org.openstreetmap.josm.data.osm.DataSet;
     22import org.openstreetmap.josm.data.osm.Node;
    1323import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
     25import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
     26import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     27import org.openstreetmap.josm.testutils.JOSMTestRules;
     28
     29import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    1430
    1531/**
     
    1733 */
    1834public class CopyActionTest {
     35    private static final class CapturingCopyAction extends CopyAction {
     36        private boolean warningShown;
    1937
    20     /**
    21      * Setup test.
    22      */
    23     @BeforeClass
    24     public static void setUpBeforeClass() {
    25         JOSMFixture.createUnitTestFixture().init();
     38        @Override
     39        protected void showEmptySelectionWarning() {
     40            warningShown = true;
     41        }
    2642    }
    2743
    2844    /**
    29      * Test of {@link CopyAction#getCopyString} method for a single way.
     45     * We need prefs for this.
     46     */
     47    @Rule
     48    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     49    public JOSMTestRules test = new JOSMTestRules().preferences().platform().fakeAPI();
     50
     51    /**
     52     * Test that copy action copies the selected primitive
     53     * @throws IOException if an I/O error occurs
     54     * @throws UnsupportedFlavorException if the requested data flavor is not supported
    3055     */
    3156    @Test
    32     public void testCopyStringWay() {
    33         final Way way = new Way(123L);
    34         assertEquals("way 123", CopyAction.getCopyString(Collections.singleton(way)));
     57    public void testWarnOnEmpty() throws UnsupportedFlavorException, IOException {
     58        Clipboard clipboard = ClipboardUtils.getClipboard();
     59        clipboard.setContents(new StringSelection("test"), null);
     60
     61        CapturingCopyAction action = new CapturingCopyAction();
     62
     63        action.updateEnabledState();
     64        assertFalse(action.isEnabled());
     65        action.actionPerformed(null);
     66        assertTrue(action.warningShown);
     67
     68        Main.getLayerManager().addLayer(new OsmDataLayer(new DataSet(), "test", null));
     69        action.warningShown = false;
     70
     71        action.updateEnabledState();
     72        assertFalse(action.isEnabled());
     73        action.actionPerformed(null);
     74        assertTrue(action.warningShown);
     75
     76        assertEquals("test", clipboard.getContents(null).getTransferData(DataFlavor.stringFlavor));
    3577    }
    3678
    3779    /**
    38      * Test of {@link CopyAction#getCopyString} method for a way and a relation.
     80     * Test that copy action copies the selected primitive
     81     * @throws Exception if an error occurs
    3982     */
    4083    @Test
    41     public void testCopyStringWayRelation() {
    42         final Way way = new Way(123L);
    43         final Relation relation = new Relation(456);
    44         assertEquals("way 123,relation 456", CopyAction.getCopyString(Arrays.asList(way, relation)));
    45         assertEquals("relation 456,way 123", CopyAction.getCopyString(Arrays.asList(relation, way)));
     84    public void testCopySinglePrimitive() throws Exception {
     85        DataSet data = new DataSet();
     86
     87        Node node1 = new Node();
     88        node1.setCoor(LatLon.ZERO);
     89        data.addPrimitive(node1);
     90
     91        Node node2 = new Node();
     92        node2.setCoor(LatLon.ZERO);
     93        data.addPrimitive(node2);
     94        Way way = new Way();
     95        way.setNodes(Arrays.asList(node1, node2));
     96        data.addPrimitive(way);
     97        data.setSelected(way);
     98
     99        Main.getLayerManager().addLayer(new OsmDataLayer(data, "test", null));
     100
     101        CopyAction action = new CopyAction() {
     102            @Override
     103            protected void showEmptySelectionWarning() {
     104                fail("Selection is not empty.");
     105            }
     106        };
     107        action.updateEnabledState();
     108        assertTrue(action.isEnabled());
     109        action.actionPerformed(null);
     110
     111        Object copied = ClipboardUtils.getClipboard().getContents(null).getTransferData(PrimitiveTransferData.DATA_FLAVOR);
     112        assertNotNull(copied);
     113        assertTrue(copied instanceof PrimitiveTransferData);
     114        PrimitiveTransferData ptd = (PrimitiveTransferData) copied;
     115        Object[] direct = ptd.getDirectlyAdded().toArray();
     116        assertEquals(1, direct.length);
     117        Object[] referenced = ptd.getReferenced().toArray();
     118        assertEquals(2, referenced.length);
    46119    }
    47120}
  • trunk/test/unit/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferableTest.java

    r9711 r10604  
    55import static org.junit.Assert.assertFalse;
    66import static org.junit.Assert.assertTrue;
    7 import static org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable.PRIMITIVE_DATA;
    87
    98import java.awt.datatransfer.DataFlavor;
    109import java.awt.datatransfer.UnsupportedFlavorException;
     10import java.util.Arrays;
    1111import java.util.Collection;
    1212import java.util.Collections;
     13import java.util.List;
    1314
    14 import org.junit.BeforeClass;
     15import org.junit.Rule;
    1516import org.junit.Test;
    16 import org.openstreetmap.josm.JOSMFixture;
    1717import org.openstreetmap.josm.data.osm.Node;
     18import org.openstreetmap.josm.data.osm.NodeData;
    1819import org.openstreetmap.josm.data.osm.PrimitiveData;
     20import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
     21import org.openstreetmap.josm.gui.datatransfer.data.TagTransferData;
     22import org.openstreetmap.josm.testutils.JOSMTestRules;
     23
     24import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    1925
    2026/**
     
    2228 */
    2329public class PrimitiveTransferableTest {
     30    /**
     31     * Prefs to use OSM primitives
     32     */
     33    @Rule
     34    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     35    public JOSMTestRules test = new JOSMTestRules().preferences();
    2436
    2537    /**
    26      * Setup tests
    27      */
    28     @BeforeClass
    29     public static void setUpBeforeClass() {
    30         JOSMFixture.createUnitTestFixture().init();
    31     }
    32 
    33     /**
    34      * Test of {@link PrimitiveTransferable#getTransferDataFlavors()} method.
     38     * Test of {@link PrimitiveTransferable#getTransferDataFlavors()} method response order
    3539     */
    3640    @Test
    3741    public void testGetTransferDataFlavors() {
    38         DataFlavor[] flavors = new PrimitiveTransferable(null).getTransferDataFlavors();
    39         assertEquals(2, flavors.length);
    40         assertEquals(PRIMITIVE_DATA, flavors[0]);
    41         assertEquals(DataFlavor.stringFlavor, flavors[1]);
     42        List<DataFlavor> flavors = Arrays.asList(new PrimitiveTransferable(null).getTransferDataFlavors());
     43        int ptd = flavors.indexOf(PrimitiveTransferData.DATA_FLAVOR);
     44        int tags = flavors.indexOf(TagTransferData.FLAVOR);
     45        int string = flavors.indexOf(DataFlavor.stringFlavor);
     46
     47        assertTrue(ptd >= 0);
     48        assertTrue(tags >= 0);
     49        assertTrue(string >= 0);
     50
     51        assertTrue(ptd < tags);
     52        assertTrue(tags < string);
    4253    }
    4354
     
    4758    @Test
    4859    public void testIsDataFlavorSupported() {
    49         assertTrue(new PrimitiveTransferable(null).isDataFlavorSupported(PRIMITIVE_DATA));
    50         assertFalse(new PrimitiveTransferable(null).isDataFlavorSupported(null));
     60        assertTrue(new PrimitiveTransferable(null).isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR));
     61        assertFalse(new PrimitiveTransferable(null).isDataFlavorSupported(DataFlavor.imageFlavor));
    5162    }
    5263
     
    5768    @Test
    5869    public void testGetTransferDataNominal() throws UnsupportedFlavorException {
    59         PrimitiveTransferable pt = new PrimitiveTransferable(Collections.singleton(new Node(1)));
    60         assertEquals("node 1 # incomplete\n", pt.getTransferData(DataFlavor.stringFlavor));
    61         Collection<PrimitiveData> td = ((PrimitiveTransferable.Data) pt.getTransferData(PRIMITIVE_DATA)).getPrimitiveData();
     70        PrimitiveTransferData data = PrimitiveTransferData.getData(Collections.singleton(new Node(1)));
     71        PrimitiveTransferable pt = new PrimitiveTransferable(data);
     72        assertEquals("node 1", pt.getTransferData(DataFlavor.stringFlavor));
     73        Collection<PrimitiveData> td = ((PrimitiveTransferData) pt.getTransferData(PrimitiveTransferData.DATA_FLAVOR)).getAll();
    6274        assertEquals(1, td.size());
    63         assertTrue(td.iterator().next() instanceof PrimitiveData);
     75        assertTrue(td.iterator().next() instanceof NodeData);
     76
     77
     78        data = PrimitiveTransferData.getData(Arrays.asList(new Node(1), new Node(2)));
     79        pt = new PrimitiveTransferable(data);
     80        assertEquals("node 1\nnode 2", pt.getTransferData(DataFlavor.stringFlavor));
    6481    }
    6582
     
    7087    @Test(expected = UnsupportedFlavorException.class)
    7188    public void testGetTransferDataError() throws UnsupportedFlavorException {
    72         new PrimitiveTransferable(Collections.singleton(new Node(1))).getTransferData(null);
     89        PrimitiveTransferData data = PrimitiveTransferData.getData(Collections.singleton(new Node(1)));
     90        new PrimitiveTransferable(data).getTransferData(DataFlavor.imageFlavor);
    7391    }
    7492}
  • trunk/test/unit/org/openstreetmap/josm/gui/datatransfer/RelationMemberTransferableTest.java

    r9717 r10604  
    3838    @Test
    3939    public void testGetTransferDataFlavors() {
    40         DataFlavor[] flavors = new RelationMemberTransferable(null).getTransferDataFlavors();
     40        DataFlavor[] flavors = new RelationMemberTransferable(Collections.<RelationMember>emptyList()).getTransferDataFlavors();
    4141        assertEquals(2, flavors.length);
    4242        assertEquals(RELATION_MEMBER_DATA, flavors[0]);
     
    4949    @Test
    5050    public void testIsDataFlavorSupported() {
    51         assertTrue(new RelationMemberTransferable(null).isDataFlavorSupported(RELATION_MEMBER_DATA));
    52         assertFalse(new RelationMemberTransferable(null).isDataFlavorSupported(null));
     51        RelationMemberTransferable transferable = new RelationMemberTransferable(Collections.<RelationMember>emptyList());
     52        assertTrue(transferable.isDataFlavorSupported(RELATION_MEMBER_DATA));
     53        assertFalse(transferable.isDataFlavorSupported(null));
    5354    }
    5455
  • trunk/test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/RelationEditorActionsTest.java

    r10113 r10604  
    22package org.openstreetmap.josm.gui.dialogs.relation.actions;
    33
    4 import org.junit.BeforeClass;
     4import org.junit.Rule;
    55import org.junit.Test;
    6 import org.openstreetmap.josm.JOSMFixture;
    76import org.openstreetmap.josm.data.osm.DataSet;
    87import org.openstreetmap.josm.data.osm.Relation;
     
    1514import org.openstreetmap.josm.gui.tagging.TagEditorModel;
    1615import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
     16import org.openstreetmap.josm.testutils.JOSMTestRules;
     17
     18import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
    1719
    1820/**
     
    2022 */
    2123public class RelationEditorActionsTest {
     24    /**
     25     * Plattform for tooltips.
     26     */
     27    @Rule
     28    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     29    public JOSMTestRules test = new JOSMTestRules().preferences().platform().commands();
    2230
    2331    /**
    24      * Setup test.
    25      */
    26     @BeforeClass
    27     public static void setUpBeforeClass() {
    28         JOSMFixture.createUnitTestFixture().init(true);
    29     }
    30 
    31     /**
    32      * Test all actions with minimal data.
     32     * Check that all actions do not crash.
    3333     */
    3434    @Test
     
    5858
    5959        new CopyMembersAction(memberTableModel, layer, editor).actionPerformed(null);
    60         new PasteMembersAction(memberTableModel, layer, editor).actionPerformed(null);
     60        new PasteMembersAction(memberTable, layer, editor).actionPerformed(null);
    6161
    6262        new DeleteCurrentRelationAction(layer, editor).actionPerformed(null);
  • trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java

    r10588 r10604  
    1212import org.junit.runners.model.InitializationError;
    1313import org.junit.runners.model.Statement;
     14import org.openstreetmap.josm.JOSMFixture;
    1415import org.openstreetmap.josm.Main;
    1516import org.openstreetmap.josm.data.projection.Projections;
     
    4041    private boolean platform;
    4142    private boolean useProjection;
     43    private boolean commands;
    4244    private boolean allowMemoryManagerLeaks;
    4345
     
    132134    public JOSMTestRules projection() {
    133135        useProjection = true;
     136        return this;
     137    }
     138
     139    /**
     140      * Allow the execution of commands using {@link Main#undoRedo}
     141      * @return this instance, for easy chaining
     142      */
     143    public JOSMTestRules commands() {
     144        commands = true;
    134145        return this;
    135146    }
     
    221232        if (platform) {
    222233            Main.determinePlatformHook();
     234        }
     235
     236        if (commands) {
     237            // TODO: Implement a more selective version of this once Main is restructured.
     238            JOSMFixture.createUnitTestFixture().init(true);
    223239        }
    224240    }
Note: See TracChangeset for help on using the changeset viewer.