-/*******************************************************************************\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();
+ }
+
+}