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