]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/CopyPasteUtil.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / CopyPasteUtil.java
diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/CopyPasteUtil.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/handler/CopyPasteUtil.java
new file mode 100644 (file)
index 0000000..791826d
--- /dev/null
@@ -0,0 +1,537 @@
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ *     VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.handler;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
+import java.util.Collection;\r
+import java.util.EnumSet;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.request.UniqueRead;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.Layer0Utils;\r
+import org.simantics.diagram.flag.DiagramFlagPreferences;\r
+import org.simantics.diagram.flag.FlagLabelingScheme;\r
+import org.simantics.diagram.flag.FlagUtil;\r
+import org.simantics.diagram.flag.IOTableUtil;\r
+import org.simantics.diagram.flag.IOTablesInfo;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.CopyAdvisor;\r
+import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;\r
+import org.simantics.diagram.synchronization.SynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.AddElement;\r
+import org.simantics.diagram.synchronization.graph.CopyAdvisorUtil;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.MoveRouteGraphConnection;\r
+import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;\r
+import org.simantics.diagram.ui.DiagramModelHints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.elementclass.FlagClass.Type;\r
+import org.simantics.g2d.elementclass.FlagHandler;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;\r
+import org.simantics.scl.commands.Command;\r
+import org.simantics.scl.commands.Commands;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class CopyPasteUtil {\r
+\r
+    static final EnumSet<ElementType> NODES            = EnumSet.of(ElementType.Node);\r
+    static final EnumSet<ElementType> CONNECTIONS      = EnumSet.of(ElementType.Connection);\r
+    public static final EnumSet<ElementType> CONNECTION_PARTS = EnumSet.of(ElementType.Edge, ElementType.BranchPoint);\r
+    public static final EnumSet<ElementType> FLAGS            = EnumSet.of(ElementType.Flag);\r
+    static final EnumSet<ElementType> MONITORS         = EnumSet.of(ElementType.Monitor);\r
+    static final EnumSet<ElementType> OTHERS           = EnumSet.of(ElementType.Other);\r
+    static final EnumSet<ElementType> NODES_AND_EDGES  = EnumSet.of(ElementType.Node);\r
+    static final EnumSet<ElementType> NOT_FLAGS        = EnumSet.complementOf(FLAGS);\r
+\r
+    public static boolean isFlagsOnlySelection(IElementAssortment ea) {\r
+        return !ea.containsAny(NOT_FLAGS)\r
+        && ea.contains(FLAGS)\r
+        && ea.count(ElementType.Flag) > 0;\r
+    }\r
+\r
+    public static boolean onlyFlagsWithoutCorrespondence(RequestProcessor processor, ElementObjectAssortment ea)\r
+            throws DatabaseException {\r
+        return isFlagsOnlySelection(ea)\r
+        && checkFlagsCorrespondences(processor, ea.flags, false);\r
+    }\r
+\r
+    public static boolean onlyFlagsWithoutCorrespondence(ElementAssortment ea) {\r
+        return isFlagsOnlySelection(ea)\r
+        && checkFlagsCorrespondences(ea.flags, false);\r
+    }\r
+\r
+    /**\r
+     * Check that all specified flag elements either have a correspondence or\r
+     * don't. Flag collections with both connected and disconnected flags will\r
+     * always return false;\r
+     * \r
+     * @param flags\r
+     * @param expectedValue\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static boolean checkFlagsCorrespondences(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {\r
+        return processor.syncRequest(new UniqueRead<Boolean>() {\r
+            @Override\r
+            public Boolean perform(ReadGraph graph) throws DatabaseException {\r
+                return checkFlagsCorrespondences(graph, flags, expectedValue);\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * Check that all specified flag elements either have a correspondence or\r
+     * don't. Flag collections with both connected and disconnected flags will\r
+     * always return false;\r
+     * \r
+     * @param flags\r
+     * @param expectedValue\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static boolean checkFlagsCorrespondences(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {\r
+        for (Resource flag : flags) {\r
+            if (FlagUtil.isJoined(graph, flag) != expectedValue) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * Check that all specified flag elements either have a correspondence or\r
+     * don't. Flag collections with both connected and disconnected flags will\r
+     * always return false;\r
+     * \r
+     * @param flags\r
+     * @param expectedValue\r
+     * @return\r
+     */\r
+    public static boolean checkFlagsCorrespondences(Iterable<IElement> flags, boolean expectedValue) {\r
+        for (IElement flag : flags) {\r
+            if (flagHasCorrespondence(flag) != expectedValue) {\r
+                return false;\r
+            }\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * @param flags flags to test\r
+     * @param expectedValue <code>true</code> to return <code>true</code> only\r
+     *        if all flags are external, <code>false</code> to return\r
+     *        <code>true</code> only if all flags are not external\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static boolean checkFlagExternality(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {\r
+        return processor.syncRequest(new UniqueRead<Boolean>() {\r
+            @Override\r
+            public Boolean perform(ReadGraph graph) throws DatabaseException {\r
+                return checkFlagExternality(graph, flags, expectedValue);\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * @param flags flags to test\r
+     * @param expectedValue <code>true</code> to return <code>true</code> only\r
+     *        if all flags are external, <code>false</code> to return\r
+     *        <code>true</code> only if all flags are not external\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static boolean checkFlagExternality(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {\r
+        for (Resource flag : flags)\r
+            if (FlagUtil.isExternal(graph, flag) != expectedValue)\r
+                return false;\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * @param flags flags to test\r
+     * @param expectedValue <code>true</code> to return <code>true</code> only\r
+     *        if all flags are external, <code>false</code> to return\r
+     *        <code>true</code> only if all flags are not external\r
+     * @return\r
+     */\r
+    public static boolean checkFlagExternality(Iterable<IElement> flags, boolean expectedValue) {\r
+        for (IElement flag : flags)\r
+            if (flagIsExternal(flag) != expectedValue)\r
+                return false;\r
+        return true;\r
+    }\r
+\r
+    public static boolean flagHasCorrespondence(IElement flag) {\r
+        FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
+        if (fh == null)\r
+            throw new IllegalArgumentException("Not a flag element: " + flag);\r
+        //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);\r
+        return fh.getConnectionData(flag) != null;\r
+    }\r
+\r
+    public static boolean flagIsExternal(IElement flag) {\r
+        FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
+        if (fh == null)\r
+            throw new IllegalArgumentException("Not a flag element: " + flag);\r
+        //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);\r
+        return fh.isExternal(flag);\r
+    }\r
+\r
+    /**\r
+     * @param graph \r
+     * @param connection\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static Set<Resource> gatherBranchPoints(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {\r
+        Set<Resource> bps = new HashSet<Resource>();\r
+        bps.addAll(ea.branchPoints);\r
+        for (Resource connection : ea.connections)\r
+            bps.addAll( getBranchPoints(graph, connection) );\r
+        return bps;\r
+    }\r
+\r
+    /**\r
+     * @param connection\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static Set<Resource> gatherRouteGraphConnections(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {\r
+        Set<Resource> rgcs = new HashSet<Resource>();\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+        for (Resource connection : ea.connections) {\r
+            if (graph.isInstanceOf(connection, DIA.RouteGraphConnection))\r
+                rgcs.add(connection);\r
+        }\r
+        return rgcs;\r
+    }\r
+\r
+    /**\r
+     * @param connection\r
+     * @return\r
+     * @throws DatabaseException \r
+     */\r
+    public static Collection<Resource> getBranchPoints(ReadGraph graph, Resource connection) throws DatabaseException {\r
+        return graph.getObjects(connection, DiagramResource.getInstance(graph).HasBranchPoint);\r
+    }\r
+\r
+    /**\r
+     * @param m\r
+     * @param elements\r
+     * @param xoffset\r
+     * @param yoffset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveElements(WriteGraph graph, Set<Resource> elements, double xoffset, double yoffset)\r
+            throws DatabaseException {\r
+        for (Resource e : elements) {\r
+            AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);\r
+            at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),\r
+                    at.getTranslateX() + xoffset,\r
+                    at.getTranslateY() + yoffset);\r
+            DiagramGraphUtil.setTransform(graph, e, at);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param m\r
+     * @param elements\r
+     * @param offset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveElements(WriteGraph graph, Set<Resource> elements, Point2D offset) throws DatabaseException {\r
+        moveElements(graph, elements, offset.getX(), offset.getY());\r
+    }\r
+\r
+    /**\r
+     * @param m\r
+     * @param elements\r
+     * @param xoffset\r
+     * @param yoffset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveParentedElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, Resource parentRelation, double xoffset, double yoffset)\r
+            throws DatabaseException {\r
+        ModelingResources MOD = ModelingResources.getInstance(graph);\r
+        for (Resource e : elements) {\r
+            Resource referencedParentComponent = graph.getPossibleObject(e, parentRelation);\r
+            if (referencedParentComponent == null)\r
+                continue;\r
+            Resource referencedElement = graph.getPossibleObject(referencedParentComponent, MOD.ComponentToElement);\r
+            // Don't move the element if it's parent element is also included in the moved set of elements.\r
+            if (referencedElement != null && op.ea.all.contains(referencedElement))\r
+                continue;\r
+\r
+            AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);\r
+            at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),\r
+                    at.getTranslateX() + xoffset,\r
+                    at.getTranslateY() + yoffset);\r
+            DiagramGraphUtil.setTransform(graph, e, at);\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param m\r
+     * @param elements\r
+     * @param xoffset\r
+     * @param yoffset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveMonitors(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)\r
+            throws DatabaseException {\r
+        moveParentedElements(graph, op, elements, DiagramResource.getInstance(graph).HasMonitorComponent, xoffset, yoffset);\r
+    }\r
+\r
+\r
+    /**\r
+     * @param m\r
+     * @param elements\r
+     * @param xoffset\r
+     * @param yoffset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveReferenceElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)\r
+            throws DatabaseException {\r
+        moveParentedElements(graph, op, elements, ModelingResources.getInstance(graph).HasParentComponent, xoffset, yoffset);\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param connections\r
+     * @param xoffset\r
+     * @param yoffset\r
+     * @throws DatabaseException \r
+     */\r
+    public static void moveRouteGraphConnections(WriteGraph graph, Set<Resource> connections, Point2D offset) throws DatabaseException {\r
+        if(!connections.isEmpty()) {\r
+            Command command = Commands.get(graph, "Simantics/Diagram/moveConnection");\r
+            Resource root = graph.syncRequest(new IndexRoot(connections.iterator().next()));\r
+            for (Resource r : connections)\r
+                command.execute(graph, root, r, offset.getX(), offset.getY());\r
+        }\r
+    }\r
+    \r
+    public static void moveConnection(WriteGraph graph, Resource connection, double offsetX, double offsetY) throws DatabaseException {\r
+        new MoveRouteGraphConnection(connection, offsetX, offsetY).perform(graph);\r
+    }\r
+\r
+    /**\r
+     * @param ctx\r
+     * @param source\r
+     * @param target\r
+     * @param offset\r
+     */\r
+    public static void copyElementPosition(ICanvasContext ctx, IElement source, IElement target, Point2D offset) {\r
+        Point2D pos = ElementUtils.getPos(source);\r
+        double x = pos.getX() + offset.getX();\r
+        double y = pos.getY() + offset.getY();\r
+        ElementUtils.setPos(target, snap(ctx, new Point2D.Double(x, y)));\r
+    }\r
+\r
+    /**\r
+     * @param ctx\r
+     * @param source\r
+     * @param target\r
+     * @param offset\r
+     * @throws DatabaseException \r
+     */\r
+    public static AffineTransform copyElementPosition(WriteGraph graph, ICanvasContext ctx, Resource sourceElement, Resource targetElement, Point2D offset) throws DatabaseException {\r
+       AffineTransform at = getCopyTransform(graph, ctx, sourceElement);\r
+       Point2D snapped = snap(ctx, new Point2D.Double(at.getTranslateX() + offset.getX(), at.getTranslateY() + offset.getY()));\r
+       at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), snapped.getX(), snapped.getY());\r
+       DiagramGraphUtil.setTransform(graph, targetElement, at);\r
+       return at;\r
+    }\r
+    \r
+    private static AffineTransform getCopyTransform(ReadGraph graph, ICanvasContext ctx, Resource sourceElement) throws DatabaseException {\r
+       if(ctx != null){\r
+            Resource runtimeDiagram = (Resource)((IDiagram)ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM)).getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
+            return DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, sourceElement);\r
+       } else {\r
+            return DiagramGraphUtil.getAffineTransform(graph, sourceElement);\r
+       }\r
+    }\r
+\r
+    /**\r
+     * @param ctx\r
+     * @param p\r
+     * @return\r
+     */\r
+    public static Point2D snap(ICanvasContext ctx, Point2D p) {\r
+        if (ctx != null) {\r
+            ISnapAdvisor snapAdvisor = ctx.getHintStack().getHint(DiagramHints.SNAP_ADVISOR);\r
+            if (snapAdvisor != null)\r
+                snapAdvisor.snap(p);\r
+        }\r
+        return p;\r
+    }\r
+\r
+    // ------------------------------------------------------------------------\r
+\r
+    /**\r
+     * Performs the operations related to a diagram-local cut-paste operation.\r
+     * This default implementation will merely translate the selection specified\r
+     * by the operation.\r
+     * \r
+     * @param op\r
+     * @throws DatabaseException \r
+     */\r
+    public static void localCutPaste(final PasteOperation op) throws DatabaseException {\r
+        Simantics.getSession().sync(new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                graph.markUndoPoint();\r
+                localCutPaste(graph, op);\r
+                Layer0Utils.addCommentMetadata(graph, "Cutted " + op + " to local target");\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * Performs the operations related to a diagram-local cut-paste operation.\r
+     * This default implementation will merely translate the selection specified\r
+     * by the operation.\r
+     * \r
+     * @param graph\r
+     * @param op\r
+     * @throws DatabaseException \r
+     */\r
+    public static void localCutPaste(WriteGraph graph, PasteOperation op) throws DatabaseException {\r
+        if (op.sameDiagram() && op.cut) {\r
+            CopyPasteUtil.moveElements(graph, op.ea.nodes, op.offset);\r
+            CopyPasteUtil.moveElements(graph, op.ea.flags, op.offset);\r
+            if(!op.ea.flags.isEmpty()) {\r
+                IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);\r
+                DiagramResource DIA = DiagramResource.getInstance(graph);\r
+                for(Resource flag : op.ea.flags) {\r
+                    double[] transform = graph.getRelatedValue(flag, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
+                    ioTablesInfo.updateBinding(graph, DIA, flag, transform[4], transform[5]);\r
+                }\r
+            }\r
+            CopyPasteUtil.moveElements(graph, CopyPasteUtil.gatherBranchPoints(graph, op.ea), op.offset);\r
+            CopyPasteUtil.moveRouteGraphConnections(graph, CopyPasteUtil.gatherRouteGraphConnections(graph, op.ea), op.offset);\r
+            CopyPasteUtil.moveElements(graph, op.ea.others, op.offset);\r
+            CopyPasteUtil.moveMonitors(graph, op, op.ea.monitors, op.offset.getX(), op.offset.getY());\r
+            CopyPasteUtil.moveReferenceElements(graph, op, op.ea.references, op.offset.getX(), op.offset.getY());\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param op\r
+     * @throws DatabaseException \r
+     */\r
+    public static void continueFlags(final PasteOperation op) throws DatabaseException {\r
+        Simantics.getSession().sync(new WriteRequest() {\r
+            @Override\r
+            public void perform(WriteGraph graph) throws DatabaseException {\r
+                continueFlags(graph, op);\r
+            }\r
+        });\r
+    }\r
+\r
+    /**\r
+     * @param graph\r
+     * @param op\r
+     * @throws DatabaseException \r
+     */\r
+    public static void continueFlags(WriteGraph graph, final PasteOperation op) throws DatabaseException {\r
+        IModifiableSynchronizationContext targetContext = (IModifiableSynchronizationContext) op.target.getHint(SynchronizationHints.CONTEXT);\r
+        if (targetContext == null)\r
+            throw new IllegalArgumentException("target diagram has no synchronization context");\r
+\r
+        CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);\r
+        if (ca == null)\r
+            throw new IllegalArgumentException("no copy advisor");\r
+\r
+        Layer0 L0 = Layer0.getInstance(graph);\r
+        DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+        FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);\r
+        int joinedFlags = 0;\r
+\r
+        for (Resource src : op.ea.flags) {\r
+            Resource sourceDiagram = graph.getPossibleObject(src, L0.PartOf);\r
+            Resource copy = CopyAdvisorUtil.copy(targetContext, graph, ca, src, sourceDiagram, op.targetDiagram);\r
+            if(copy == null)\r
+                continue;\r
+            OrderedSetUtils.add(graph, op.targetDiagram, copy);\r
+            graph.claim(op.targetDiagram, L0.ConsistsOf, copy);\r
+            AddElement.claimFreshElementName(graph, op.targetDiagram, copy);\r
+\r
+            GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);\r
+            if (glm != null) {\r
+                glm.removeFromAllLayers(graph, copy);\r
+                glm.putElementOnVisibleLayers(op.target, graph, copy);\r
+            }\r
+\r
+            AffineTransform at = CopyPasteUtil.copyElementPosition(graph, op.ctx, src, copy, op.offset);\r
+            Type type = FlagUtil.getFlagType(graph, src, Type.Out);\r
+            FlagUtil.setFlagType(graph, copy, type.other());\r
+\r
+            FlagUtil.join(graph, src, copy);\r
+\r
+            if (scheme != null) {\r
+                String label = scheme.generateLabel(graph, op.targetDiagram);\r
+                if (label != null) {\r
+                    graph.claimLiteral(src, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);\r
+                    graph.claimLiteral(copy, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);\r
+                }\r
+            }\r
+\r
+            // Update flag table binding\r
+            IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);\r
+            ioTablesInfo.updateBinding(graph, DIA, copy, at.getTranslateX(), at.getTranslateY());\r
+\r
+            ++joinedFlags;\r
+        }\r
+\r
+        if (joinedFlags > 0) {\r
+            // Add comment to change set.\r
+            CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+            graph.addMetadata(cm.add("Continued " + joinedFlags + " flag(s)"));\r
+        }\r
+    }\r
+\r
+    /**\r
+     * @param op\r
+     * @throws DatabaseException\r
+     */\r
+    public static void performDefaultPaste(PasteOperation op) throws DatabaseException {\r
+        Session session = Simantics.getSession();\r
+        new Paster(session, op).perform();\r
+    }\r
+\r
+}\r