Ticket #12478: patch-ccp-primitives-copy.patch

File patch-ccp-primitives-copy.patch, 30.6 KB (added by michael2402, 9 years ago)
  • src/org/openstreetmap/josm/actions/PasteAction.java

    diff --git a/src/org/openstreetmap/josm/actions/PasteAction.java b/src/org/openstreetmap/josm/actions/PasteAction.java
    index 6030f29..2b1ba60 100644
    a b import java.awt.MouseInfo;  
    99import java.awt.Point;
    1010import java.awt.event.ActionEvent;
    1111import java.awt.event.KeyEvent;
    12 import java.util.ArrayList;
    13 import java.util.HashMap;
    14 import java.util.List;
    15 import java.util.Map;
    1612
    1713import org.openstreetmap.josm.Main;
    18 import org.openstreetmap.josm.command.AddPrimitivesCommand;
    1914import 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;
    2315import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
    2416import 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.OsmTransferHandler;
    3018import org.openstreetmap.josm.tools.Shortcut;
    3119
    3220/**
    import org.openstreetmap.josm.tools.Shortcut;  
    3523 */
    3624public final class PasteAction extends JosmAction implements PasteBufferChangedListener {
    3725
     26    private final OsmTransferHandler transferHandler;
     27
    3828    /**
    3929     * Constructs a new {@code PasteAction}.
    4030     */
    public final class PasteAction extends JosmAction implements PasteBufferChangedL  
    4636        Main.registerActionShortcut(this,
    4737                Shortcut.registerShortcut("system:paste:cua", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_INSERT, Shortcut.SHIFT));
    4838        Main.pasteBuffer.addPasteBufferChangedListener(this);
     39        transferHandler = new OsmTransferHandler();
    4940    }
    5041
    5142    @Override
    5243    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 
    10144        // default to paste in center of map (pasted via menu or cursor not in MapView)
    10245        EastNorth mPosition = Main.map.mapView.getCenter();
    10346        // We previously checked for modifier to know if the action has been trigerred via shortcut or via menu
    public final class PasteAction extends JosmAction implements PasteBufferChangedL  
    11255            }
    11356        }
    11457
    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.main.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;
     58        transferHandler.pasteOn(Main.getLayerManager().getEditLayer(), mPosition);
    19959    }
    20060
    20161    @Override
  • src/org/openstreetmap/josm/data/osm/PrimitiveData.java

    diff --git a/src/org/openstreetmap/josm/data/osm/PrimitiveData.java b/src/org/openstreetmap/josm/data/osm/PrimitiveData.java
    index 5664dd3..693d277 100644
    a b public abstract class PrimitiveData extends AbstractPrimitive implements Seriali  
    8080        oos.writeInt(version);
    8181        oos.writeInt(changesetId);
    8282        oos.writeInt(timestamp);
     83        oos.writeObject(keys);
    8384        oos.defaultWriteObject();
    8485    }
    8586
    public abstract class PrimitiveData extends AbstractPrimitive implements Seriali  
    9192        version = ois.readInt();
    9293        changesetId = ois.readInt();
    9394        timestamp = ois.readInt();
     95        keys = (String[]) ois.readObject();
    9496        ois.defaultReadObject();
    9597    }
    9698}
  • src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java

    diff --git a/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java b/src/org/openstreetmap/josm/data/osm/PrimitiveDeepCopy.java
    index 03db280..010677b 100644
    a b import java.util.Set;  
    99import java.util.concurrent.CopyOnWriteArrayList;
    1010
    1111import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
     12import org.openstreetmap.josm.gui.datatransfer.PasteBufferCompatibilityHelper;
    1213
    1314/**
    1415 * This class allows to create and keep a deep copy of primitives. Provides methods to access directly added
    public class PrimitiveDeepCopy {  
    2930     * Constructs a new {@code PrimitiveDeepCopy} without data. Use {@link #makeCopy(Collection)} after that.
    3031     */
    3132    public PrimitiveDeepCopy() {
    32         // Do nothing
     33        // Send everything to Swing CCP
     34        listeners.add(new PasteBufferCompatibilityHelper());
    3335    }
    3436
    3537    /**
    public class PrimitiveDeepCopy {  
    9799        firePasteBufferChanged();
    98100    }
    99101
     102    /**
     103     * Gets the list of primitives that were explicitly added to this copy.
     104     * @return The added primitives
     105     */
    100106    public List<PrimitiveData> getDirectlyAdded() {
    101107        return directlyAdded;
    102108    }
    103109
     110    /**
     111     * Gets the list of primitives that were implicitly added because they were referenced.
     112     * @return The primitives
     113     */
    104114    public List<PrimitiveData> getReferenced() {
    105115        return referenced;
    106116    }
    107117
     118    /**
     119     * Gets a list of all primitives in this copy.
     120     * @return The primitives
     121     * @see #getDirectlyAdded()
     122     * @see #getReferenced()
     123     */
    108124    public List<PrimitiveData> getAll() {
    109125        List<PrimitiveData> result = new ArrayList<>(directlyAdded.size() + referenced.size());
    110126        result.addAll(directlyAdded);
  • src/org/openstreetmap/josm/gui/MapFrame.java

    diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
    index 62030d9..0a74071 100644
    a b import java.awt.Component;  
    88import java.awt.Container;
    99import java.awt.Dimension;
    1010import java.awt.Font;
    11 import java.awt.GraphicsEnvironment;
    1211import java.awt.GridBagLayout;
    1312import java.awt.Rectangle;
    1413import java.awt.event.ActionEvent;
    public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi  
    197196        setLayout(new BorderLayout());
    198197
    199198        mapView = new MapView(Main.getLayerManager(), contentPane, viewportData);
    200         if (!GraphicsEnvironment.isHeadless()) {
    201             new FileDrop(mapView);
    202         }
    203199
    204200        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
    205201
  • src/org/openstreetmap/josm/gui/MapView.java

    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index 5f4df08..f93608b 100644
    a b import org.openstreetmap.josm.data.osm.OsmPrimitive;  
    5151import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    5252import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
    5353import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
     54import org.openstreetmap.josm.gui.datatransfer.OsmTransferHandler;
    5455import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
    5556import org.openstreetmap.josm.gui.layer.GpxLayer;
    5657import org.openstreetmap.josm.gui.layer.ImageryLayer;
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    532533        for (JComponent c : getMapNavigationComponents(MapView.this)) {
    533534            add(c);
    534535        }
     536        setTransferHandler(new OsmTransferHandler());
    535537    }
    536538
    537539    /**
  • new file src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java

    diff --git a/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java b/src/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandler.java
    new file mode 100644
    index 0000000..5424349
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.datatransfer;
     3
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
     6import java.awt.Toolkit;
     7import java.awt.datatransfer.Clipboard;
     8import java.awt.datatransfer.DataFlavor;
     9import java.awt.datatransfer.Transferable;
     10import java.awt.datatransfer.UnsupportedFlavorException;
     11import java.io.File;
     12import java.io.IOException;
     13import java.util.ArrayList;
     14import java.util.Arrays;
     15import java.util.Collection;
     16import java.util.HashMap;
     17import java.util.List;
     18import java.util.Map;
     19
     20import javax.swing.TransferHandler;
     21
     22import org.openstreetmap.josm.Main;
     23import org.openstreetmap.josm.actions.OpenFileAction;
     24import org.openstreetmap.josm.command.AddPrimitivesCommand;
     25import org.openstreetmap.josm.data.coor.EastNorth;
     26import org.openstreetmap.josm.data.osm.NodeData;
     27import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     28import org.openstreetmap.josm.data.osm.PrimitiveData;
     29import org.openstreetmap.josm.data.osm.RelationData;
     30import org.openstreetmap.josm.data.osm.RelationMemberData;
     31import org.openstreetmap.josm.data.osm.WayData;
     32import org.openstreetmap.josm.gui.ExtendedDialog;
     33import org.openstreetmap.josm.gui.datatransfer.PrimitiveTransferable.Data;
     34import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     35
     36/**
     37 * This transfer hanlder provides the ability to transfer OSM data. It allows you to receive files and {@link Data} objects.
     38 * @author Michael Zangl
     39 */
     40public class OsmTransferHandler extends TransferHandler {
     41
     42    private abstract static class AbstractDataFlavorSupport {
     43        protected final DataFlavor df;
     44
     45        AbstractDataFlavorSupport(DataFlavor df) {
     46            this.df = df;
     47        }
     48
     49        protected boolean supports(TransferSupport support) {
     50            return support.isDataFlavorSupported(df) && isCopy(support);
     51        }
     52
     53        private boolean isCopy(TransferSupport support) {
     54            return !support.isDrop() || (COPY & support.getSourceDropActions()) == COPY;
     55        }
     56
     57        protected abstract boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt)
     58                throws UnsupportedFlavorException, IOException;
     59    }
     60
     61    private static Collection<AbstractDataFlavorSupport> SUPPORTED = Arrays
     62            .asList(new AbstractDataFlavorSupport(DataFlavor.javaFileListFlavor) {
     63                @Override
     64                public boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth pasteAt)
     65                        throws UnsupportedFlavorException, IOException {
     66                    @SuppressWarnings("unchecked")
     67                    List<File> files = (List<File>) support.getTransferable().getTransferData(df);
     68                    OpenFileAction.OpenFileTask task = new OpenFileAction.OpenFileTask(files, null);
     69                    task.setRecordHistory(true);
     70                    Main.worker.submit(task);
     71                    return true;
     72                }
     73            }, new AbstractDataFlavorSupport(PrimitiveTransferData.DATA_FLAVOR) {
     74                @Override
     75                public boolean importData(TransferSupport support, final OsmDataLayer layer, EastNorth pasteAt)
     76                        throws UnsupportedFlavorException, IOException {
     77                    PrimitiveTransferData pasteBuffer = (PrimitiveTransferData) support.getTransferable()
     78                            .getTransferData(df);
     79                    // Allow to cancel paste if there are incomplete primitives
     80                    if (pasteBuffer.hasIncompleteData() && !confirmDeleteIncomplete()) {
     81                        return false;
     82                    }
     83
     84                    EastNorth center = pasteBuffer.getCenter();
     85                    EastNorth offset = pasteAt.subtract(center);
     86
     87                    // Make a copy of pasteBuffer and map from old id to copied data id
     88                    List<PrimitiveData> bufferCopy = new ArrayList<>();
     89                    List<PrimitiveData> toSelect = new ArrayList<>();
     90                    Map<Long, Long> newNodeIds = new HashMap<>();
     91                    Map<Long, Long> newWayIds = new HashMap<>();
     92                    Map<Long, Long> newRelationIds = new HashMap<>();
     93                    for (PrimitiveData data : pasteBuffer.getAll()) {
     94                        if (data.isIncomplete()) {
     95                            continue;
     96                        }
     97                        PrimitiveData copy = data.makeCopy();
     98                        copy.clearOsmMetadata();
     99                        if (data instanceof NodeData) {
     100                            newNodeIds.put(data.getUniqueId(), copy.getUniqueId());
     101                        } else if (data instanceof WayData) {
     102                            newWayIds.put(data.getUniqueId(), copy.getUniqueId());
     103                        } else if (data instanceof RelationData) {
     104                            newRelationIds.put(data.getUniqueId(), copy.getUniqueId());
     105                        }
     106                        bufferCopy.add(copy);
     107                        if (pasteBuffer.getDirectlyAdded().contains(data)) {
     108                            toSelect.add(copy);
     109                        }
     110                    }
     111
     112                    // Update references in copied buffer
     113                    for (PrimitiveData data : bufferCopy) {
     114                        if (data instanceof NodeData) {
     115                            NodeData nodeData = (NodeData) data;
     116                            nodeData.setEastNorth(nodeData.getEastNorth().add(offset));
     117                        } else if (data instanceof WayData) {
     118                            List<Long> newNodes = new ArrayList<>();
     119                            for (Long oldNodeId : ((WayData) data).getNodes()) {
     120                                Long newNodeId = newNodeIds.get(oldNodeId);
     121                                if (newNodeId != null) {
     122                                    newNodes.add(newNodeId);
     123                                }
     124                            }
     125                            ((WayData) data).setNodes(newNodes);
     126                        } else if (data instanceof RelationData) {
     127                            List<RelationMemberData> newMembers = new ArrayList<>();
     128                            for (RelationMemberData member : ((RelationData) data).getMembers()) {
     129                                OsmPrimitiveType memberType = member.getMemberType();
     130                                Long newId;
     131                                switch (memberType) {
     132                                case NODE:
     133                                    newId = newNodeIds.get(member.getMemberId());
     134                                    break;
     135                                case WAY:
     136                                    newId = newWayIds.get(member.getMemberId());
     137                                    break;
     138                                case RELATION:
     139                                    newId = newRelationIds.get(member.getMemberId());
     140                                    break;
     141                                default:
     142                                    throw new AssertionError();
     143                                }
     144                                if (newId != null) {
     145                                    newMembers.add(new RelationMemberData(member.getRole(), memberType, newId));
     146                                }
     147                            }
     148                            ((RelationData) data).setMembers(newMembers);
     149                        }
     150                    }
     151
     152                    /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
     153                    Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect) {
     154                        @Override
     155                        protected OsmDataLayer getLayer() {
     156                            return layer;
     157                        }
     158                    });
     159                    Main.map.mapView.repaint();
     160                    return true;
     161                }
     162            });
     163
     164
     165    @Override
     166    public boolean canImport(TransferSupport support) {
     167        // import everything for now, only support copy.
     168        for (AbstractDataFlavorSupport df : SUPPORTED) {
     169            if (df.supports(support)) {
     170                return true;
     171            }
     172        }
     173        return false;
     174    }
     175
     176    @Override
     177    public boolean importData(TransferSupport support) {
     178        return importData(support, Main.getLayerManager().getEditLayer(), null);
     179    }
     180
     181    private boolean importData(TransferSupport support, OsmDataLayer layer, EastNorth center) {
     182        for (AbstractDataFlavorSupport df : SUPPORTED) {
     183            if (df.supports(support)) {
     184                try {
     185                    df.importData(support, layer, center);
     186                    return true;
     187                } catch (UnsupportedFlavorException | IOException e) {
     188                    Main.warn(e);
     189                }
     190            }
     191        }
     192        return super.importData(support);
     193    }
     194
     195    /**
     196     * Invoke a copy for the given data.
     197     * @param data The data to copy.
     198     */
     199    public static void copy(final PrimitiveTransferData data) {
     200        getClippboard().setContents(new Transferable() {
     201            @Override
     202            public boolean isDataFlavorSupported(DataFlavor flavor) {
     203                return flavor == PrimitiveTransferData.DATA_FLAVOR;
     204            }
     205
     206            @Override
     207            public DataFlavor[] getTransferDataFlavors() {
     208                return new DataFlavor[] { PrimitiveTransferData.DATA_FLAVOR };
     209            }
     210
     211            @Override
     212            public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
     213                if (flavor == PrimitiveTransferData.DATA_FLAVOR) {
     214                    return data;
     215                } else {
     216                    throw new UnsupportedFlavorException(flavor);
     217                }
     218            }
     219        }, null);
     220    }
     221
     222    public void pasteOn(OsmDataLayer editLayer, EastNorth mPosition) {
     223        Transferable transferable = getClippboard().getContents(null);
     224        importData(new TransferSupport(Main.panel, transferable), editLayer, mPosition);
     225    }
     226
     227    private static boolean confirmDeleteIncomplete() {
     228        ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Delete incomplete members?"),
     229                new String[] { tr("Paste without incomplete members"), tr("Cancel") });
     230        ed.setButtonIcons(new String[] { "dialogs/relation/deletemembers", "cancel" });
     231        ed.setContent(tr(
     232                "The copied data contains incomplete objects.  " + "When pasting the incomplete objects are removed.  "
     233                        + "Do you want to paste the data without the incomplete objects?"));
     234        ed.showDialog();
     235        return ed.getValue() == 1;
     236    }
     237
     238    private static Clipboard getClippboard() {
     239        //TODO: Might be unsupported in some cases, we need a fake clippboard then.
     240        return Toolkit.getDefaultToolkit().getSystemClipboard();
     241    }
     242}
  • new file src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java

    diff --git a/src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java b/src/org/openstreetmap/josm/gui/datatransfer/PasteBufferCompatibilityHelper.java
    new file mode 100644
    index 0000000..338a823
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.datatransfer;
     3
     4import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
     5import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
     6
     7/**
     8 * Temporary helper class that allows the paste buffer and Swing CCP to coexist.
     9 * @author Michael Zangl
     10 * @since xxx
     11 */
     12public class PasteBufferCompatibilityHelper implements PasteBufferChangedListener {
     13    private OsmTransferHandler transferHandler = new OsmTransferHandler();
     14    @Override
     15    public void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer) {
     16        PrimitiveTransferData data = new PrimitiveTransferData(pasteBuffer);
     17        OsmTransferHandler.copy(data);
     18    }
     19}
  • new file src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java

    diff --git a/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java b/src/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferData.java
    new file mode 100644
    index 0000000..71b860c
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.datatransfer;
     3
     4import java.awt.datatransfer.DataFlavor;
     5import java.io.Serializable;
     6import java.util.ArrayList;
     7import java.util.Collection;
     8import java.util.Collections;
     9import java.util.HashSet;
     10import java.util.LinkedList;
     11import java.util.Queue;
     12
     13import org.openstreetmap.josm.data.coor.EastNorth;
     14import org.openstreetmap.josm.data.osm.NodeData;
     15import org.openstreetmap.josm.data.osm.OsmPrimitive;
     16import org.openstreetmap.josm.data.osm.PrimitiveData;
     17import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
     18import org.openstreetmap.josm.data.osm.Relation;
     19import org.openstreetmap.josm.data.osm.Way;
     20import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     21
     22/**
     23 * A list of primitives that are transfered. The list allows you to implicitly add primitives.
     24 * It distingluishes between primitives that were directly added and implicitly added ones.
     25 * @author Michael Zangl
     26 * @since xxx
     27 */
     28public class PrimitiveTransferData implements Serializable {
     29    private static final long serialVersionUID = 1L;
     30
     31    public static final DataFlavor DATA_FLAVOR = new DataFlavor(PrimitiveTransferData.class, "OSM Primitives");
     32
     33    private static final class GetReferences implements ReferenceGetter {
     34        @Override
     35        public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) {
     36            if (primitive instanceof Way) {
     37                return ((Way) primitive).getNodes();
     38            } else if (primitive instanceof Relation) {
     39                return ((Relation) primitive).getMemberPrimitives();
     40            } else {
     41                return Collections.emptyList();
     42            }
     43        }
     44    }
     45
     46    // TODO: Java8: Replace by Function.
     47    private interface ReferenceGetter {
     48        Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive);
     49    }
     50
     51    private final ArrayList<PrimitiveData> direct;
     52    private final ArrayList<PrimitiveData> referenced;
     53
     54    /**
     55     *
     56     * @param primitives
     57     * @param referencedGetter
     58     */
     59    private PrimitiveTransferData(Collection<OsmPrimitive> primitives, ReferenceGetter referencedGetter) {
     60        // convert to hash set first to remove dupplicates
     61        HashSet<OsmPrimitive> visited = new HashSet<>(primitives);
     62        this.direct = new ArrayList<>(visited.size());
     63
     64        this.referenced = new ArrayList<>();
     65        Queue<OsmPrimitive> toCheck = new LinkedList<>();
     66        for (OsmPrimitive p : visited) {
     67            direct.add(p.save());
     68            toCheck.addAll(referencedGetter.getReferedPrimitives(p));
     69        }
     70        while (!toCheck.isEmpty()) {
     71            OsmPrimitive p = toCheck.poll();
     72            if (visited.add(p)) {
     73                referenced.add(p.save());
     74                toCheck.addAll(referencedGetter.getReferedPrimitives(p));
     75            }
     76        }
     77    }
     78
     79    /**
     80     * Temporary copy constructor
     81     * @param pdc The pdc.
     82     */
     83    public PrimitiveTransferData(PrimitiveDeepCopy pdc) {
     84        this.direct = new ArrayList<>(pdc.getDirectlyAdded());
     85        this.referenced = new ArrayList<>(pdc.getReferenced());
     86    }
     87
     88    /**
     89     * Gets all primitives directly added.
     90     * @return The primitives
     91     */
     92    public ArrayList<PrimitiveData> getDirectlyAdded() {
     93        return direct;
     94    }
     95
     96    /**
     97     * Gets all primitives that were added because they were referenced.
     98     * @return The primitives
     99     */
     100    public ArrayList<PrimitiveData> getReferenced() {
     101        return referenced;
     102    }
     103
     104    public Collection<PrimitiveData> getAll() {
     105        ArrayList<PrimitiveData> list = new ArrayList<>();
     106        list.addAll(direct);
     107        list.addAll(referenced);
     108        return list;
     109    }
     110
     111    public static PrimitiveTransferData getData(Collection<OsmPrimitive> primitives) {
     112        return new PrimitiveTransferData(primitives, new ReferenceGetter() {
     113            @Override
     114            public Collection<? extends OsmPrimitive> getReferedPrimitives(OsmPrimitive primitive) {
     115                return Collections.emptyList();
     116            }
     117        });
     118    }
     119
     120    public static PrimitiveTransferData getDataWithReferences(Collection<OsmPrimitive> primitives) {
     121        return new PrimitiveTransferData(primitives, new GetReferences());
     122    }
     123
     124    /**
     125     * Compute the center of all nodes.
     126     * @return The center or null if this buffer has no location.
     127     */
     128    public EastNorth getCenter() {
     129        BoundingXYVisitor visitor = new BoundingXYVisitor();
     130        for (PrimitiveData pd : getAll()) {
     131            if (pd instanceof NodeData && !pd.isIncomplete()) {
     132                visitor.visit(((NodeData) pd).getEastNorth());
     133            }
     134        }
     135        if (visitor.hasExtend()) {
     136            return visitor.getBounds().getCenter();
     137        } else {
     138            return null;
     139        }
     140    }
     141
     142    public boolean hasIncompleteData() {
     143        for (PrimitiveData pd : getAll()) {
     144            if (pd.isIncomplete()) {
     145                return true;
     146            }
     147        }
     148        return false;
     149    }
     150}