1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.diagram.handler;
\r
14 import java.awt.geom.AffineTransform;
\r
15 import java.awt.geom.Point2D;
\r
16 import java.util.Collection;
\r
17 import java.util.EnumSet;
\r
18 import java.util.HashSet;
\r
19 import java.util.Set;
\r
21 import org.simantics.Simantics;
\r
22 import org.simantics.databoard.Bindings;
\r
23 import org.simantics.db.ReadGraph;
\r
24 import org.simantics.db.RequestProcessor;
\r
25 import org.simantics.db.Resource;
\r
26 import org.simantics.db.Session;
\r
27 import org.simantics.db.WriteGraph;
\r
28 import org.simantics.db.common.CommentMetadata;
\r
29 import org.simantics.db.common.request.IndexRoot;
\r
30 import org.simantics.db.common.request.UniqueRead;
\r
31 import org.simantics.db.common.request.WriteRequest;
\r
32 import org.simantics.db.common.utils.OrderedSetUtils;
\r
33 import org.simantics.db.exception.DatabaseException;
\r
34 import org.simantics.db.layer0.util.Layer0Utils;
\r
35 import org.simantics.diagram.flag.DiagramFlagPreferences;
\r
36 import org.simantics.diagram.flag.FlagLabelingScheme;
\r
37 import org.simantics.diagram.flag.FlagUtil;
\r
38 import org.simantics.diagram.flag.IOTableUtil;
\r
39 import org.simantics.diagram.flag.IOTablesInfo;
\r
40 import org.simantics.diagram.stubs.DiagramResource;
\r
41 import org.simantics.diagram.synchronization.CopyAdvisor;
\r
42 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
\r
43 import org.simantics.diagram.synchronization.SynchronizationHints;
\r
44 import org.simantics.diagram.synchronization.graph.AddElement;
\r
45 import org.simantics.diagram.synchronization.graph.CopyAdvisorUtil;
\r
46 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
\r
47 import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;
\r
48 import org.simantics.diagram.synchronization.graph.MoveRouteGraphConnection;
\r
49 import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;
\r
50 import org.simantics.diagram.ui.DiagramModelHints;
\r
51 import org.simantics.g2d.canvas.ICanvasContext;
\r
52 import org.simantics.g2d.diagram.DiagramHints;
\r
53 import org.simantics.g2d.diagram.IDiagram;
\r
54 import org.simantics.g2d.element.ElementUtils;
\r
55 import org.simantics.g2d.element.IElement;
\r
56 import org.simantics.g2d.elementclass.FlagClass.Type;
\r
57 import org.simantics.g2d.elementclass.FlagHandler;
\r
58 import org.simantics.layer0.Layer0;
\r
59 import org.simantics.modeling.ModelingResources;
\r
60 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
\r
61 import org.simantics.scl.commands.Command;
\r
62 import org.simantics.scl.commands.Commands;
\r
65 * @author Tuukka Lehtonen
\r
67 public final class CopyPasteUtil {
\r
69 static final EnumSet<ElementType> NODES = EnumSet.of(ElementType.Node);
\r
70 static final EnumSet<ElementType> CONNECTIONS = EnumSet.of(ElementType.Connection);
\r
71 public static final EnumSet<ElementType> CONNECTION_PARTS = EnumSet.of(ElementType.Edge, ElementType.BranchPoint);
\r
72 public static final EnumSet<ElementType> FLAGS = EnumSet.of(ElementType.Flag);
\r
73 static final EnumSet<ElementType> MONITORS = EnumSet.of(ElementType.Monitor);
\r
74 static final EnumSet<ElementType> OTHERS = EnumSet.of(ElementType.Other);
\r
75 static final EnumSet<ElementType> NODES_AND_EDGES = EnumSet.of(ElementType.Node);
\r
76 static final EnumSet<ElementType> NOT_FLAGS = EnumSet.complementOf(FLAGS);
\r
78 public static boolean isFlagsOnlySelection(IElementAssortment ea) {
\r
79 return !ea.containsAny(NOT_FLAGS)
\r
80 && ea.contains(FLAGS)
\r
81 && ea.count(ElementType.Flag) > 0;
\r
84 public static boolean onlyFlagsWithoutCorrespondence(RequestProcessor processor, ElementObjectAssortment ea)
\r
85 throws DatabaseException {
\r
86 return isFlagsOnlySelection(ea)
\r
87 && checkFlagsCorrespondences(processor, ea.flags, false);
\r
90 public static boolean onlyFlagsWithoutCorrespondence(ElementAssortment ea) {
\r
91 return isFlagsOnlySelection(ea)
\r
92 && checkFlagsCorrespondences(ea.flags, false);
\r
96 * Check that all specified flag elements either have a correspondence or
\r
97 * don't. Flag collections with both connected and disconnected flags will
\r
98 * always return false;
\r
101 * @param expectedValue
\r
103 * @throws DatabaseException
\r
105 public static boolean checkFlagsCorrespondences(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {
\r
106 return processor.syncRequest(new UniqueRead<Boolean>() {
\r
108 public Boolean perform(ReadGraph graph) throws DatabaseException {
\r
109 return checkFlagsCorrespondences(graph, flags, expectedValue);
\r
115 * Check that all specified flag elements either have a correspondence or
\r
116 * don't. Flag collections with both connected and disconnected flags will
\r
117 * always return false;
\r
120 * @param expectedValue
\r
122 * @throws DatabaseException
\r
124 public static boolean checkFlagsCorrespondences(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {
\r
125 for (Resource flag : flags) {
\r
126 if (FlagUtil.isJoined(graph, flag) != expectedValue) {
\r
134 * Check that all specified flag elements either have a correspondence or
\r
135 * don't. Flag collections with both connected and disconnected flags will
\r
136 * always return false;
\r
139 * @param expectedValue
\r
142 public static boolean checkFlagsCorrespondences(Iterable<IElement> flags, boolean expectedValue) {
\r
143 for (IElement flag : flags) {
\r
144 if (flagHasCorrespondence(flag) != expectedValue) {
\r
152 * @param flags flags to test
\r
153 * @param expectedValue <code>true</code> to return <code>true</code> only
\r
154 * if all flags are external, <code>false</code> to return
\r
155 * <code>true</code> only if all flags are not external
\r
157 * @throws DatabaseException
\r
159 public static boolean checkFlagExternality(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {
\r
160 return processor.syncRequest(new UniqueRead<Boolean>() {
\r
162 public Boolean perform(ReadGraph graph) throws DatabaseException {
\r
163 return checkFlagExternality(graph, flags, expectedValue);
\r
169 * @param flags flags to test
\r
170 * @param expectedValue <code>true</code> to return <code>true</code> only
\r
171 * if all flags are external, <code>false</code> to return
\r
172 * <code>true</code> only if all flags are not external
\r
174 * @throws DatabaseException
\r
176 public static boolean checkFlagExternality(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {
\r
177 for (Resource flag : flags)
\r
178 if (FlagUtil.isExternal(graph, flag) != expectedValue)
\r
184 * @param flags flags to test
\r
185 * @param expectedValue <code>true</code> to return <code>true</code> only
\r
186 * if all flags are external, <code>false</code> to return
\r
187 * <code>true</code> only if all flags are not external
\r
190 public static boolean checkFlagExternality(Iterable<IElement> flags, boolean expectedValue) {
\r
191 for (IElement flag : flags)
\r
192 if (flagIsExternal(flag) != expectedValue)
\r
197 public static boolean flagHasCorrespondence(IElement flag) {
\r
198 FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);
\r
200 throw new IllegalArgumentException("Not a flag element: " + flag);
\r
201 //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);
\r
202 return fh.getConnectionData(flag) != null;
\r
205 public static boolean flagIsExternal(IElement flag) {
\r
206 FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);
\r
208 throw new IllegalArgumentException("Not a flag element: " + flag);
\r
209 //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);
\r
210 return fh.isExternal(flag);
\r
215 * @param connection
\r
217 * @throws DatabaseException
\r
219 public static Set<Resource> gatherBranchPoints(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {
\r
220 Set<Resource> bps = new HashSet<Resource>();
\r
221 bps.addAll(ea.branchPoints);
\r
222 for (Resource connection : ea.connections)
\r
223 bps.addAll( getBranchPoints(graph, connection) );
\r
228 * @param connection
\r
230 * @throws DatabaseException
\r
232 public static Set<Resource> gatherRouteGraphConnections(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {
\r
233 Set<Resource> rgcs = new HashSet<Resource>();
\r
234 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
235 for (Resource connection : ea.connections) {
\r
236 if (graph.isInstanceOf(connection, DIA.RouteGraphConnection))
\r
237 rgcs.add(connection);
\r
243 * @param connection
\r
245 * @throws DatabaseException
\r
247 public static Collection<Resource> getBranchPoints(ReadGraph graph, Resource connection) throws DatabaseException {
\r
248 return graph.getObjects(connection, DiagramResource.getInstance(graph).HasBranchPoint);
\r
256 * @throws DatabaseException
\r
258 public static void moveElements(WriteGraph graph, Set<Resource> elements, double xoffset, double yoffset)
\r
259 throws DatabaseException {
\r
260 for (Resource e : elements) {
\r
261 AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);
\r
262 at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),
\r
263 at.getTranslateX() + xoffset,
\r
264 at.getTranslateY() + yoffset);
\r
265 DiagramGraphUtil.setTransform(graph, e, at);
\r
273 * @throws DatabaseException
\r
275 public static void moveElements(WriteGraph graph, Set<Resource> elements, Point2D offset) throws DatabaseException {
\r
276 moveElements(graph, elements, offset.getX(), offset.getY());
\r
284 * @throws DatabaseException
\r
286 public static void moveParentedElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, Resource parentRelation, double xoffset, double yoffset)
\r
287 throws DatabaseException {
\r
288 ModelingResources MOD = ModelingResources.getInstance(graph);
\r
289 for (Resource e : elements) {
\r
290 Resource referencedParentComponent = graph.getPossibleObject(e, parentRelation);
\r
291 if (referencedParentComponent == null)
\r
293 Resource referencedElement = graph.getPossibleObject(referencedParentComponent, MOD.ComponentToElement);
\r
294 // Don't move the element if it's parent element is also included in the moved set of elements.
\r
295 if (referencedElement != null && op.ea.all.contains(referencedElement))
\r
298 AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);
\r
299 at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),
\r
300 at.getTranslateX() + xoffset,
\r
301 at.getTranslateY() + yoffset);
\r
302 DiagramGraphUtil.setTransform(graph, e, at);
\r
311 * @throws DatabaseException
\r
313 public static void moveMonitors(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)
\r
314 throws DatabaseException {
\r
315 moveParentedElements(graph, op, elements, DiagramResource.getInstance(graph).HasMonitorComponent, xoffset, yoffset);
\r
324 * @throws DatabaseException
\r
326 public static void moveReferenceElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)
\r
327 throws DatabaseException {
\r
328 moveParentedElements(graph, op, elements, ModelingResources.getInstance(graph).HasParentComponent, xoffset, yoffset);
\r
333 * @param connections
\r
336 * @throws DatabaseException
\r
338 public static void moveRouteGraphConnections(WriteGraph graph, Set<Resource> connections, Point2D offset) throws DatabaseException {
\r
339 if(!connections.isEmpty()) {
\r
340 Command command = Commands.get(graph, "Simantics/Diagram/moveConnection");
\r
341 Resource root = graph.syncRequest(new IndexRoot(connections.iterator().next()));
\r
342 for (Resource r : connections)
\r
343 command.execute(graph, root, r, offset.getX(), offset.getY());
\r
347 public static void moveConnection(WriteGraph graph, Resource connection, double offsetX, double offsetY) throws DatabaseException {
\r
348 new MoveRouteGraphConnection(connection, offsetX, offsetY).perform(graph);
\r
357 public static void copyElementPosition(ICanvasContext ctx, IElement source, IElement target, Point2D offset) {
\r
358 Point2D pos = ElementUtils.getPos(source);
\r
359 double x = pos.getX() + offset.getX();
\r
360 double y = pos.getY() + offset.getY();
\r
361 ElementUtils.setPos(target, snap(ctx, new Point2D.Double(x, y)));
\r
369 * @throws DatabaseException
\r
371 public static AffineTransform copyElementPosition(WriteGraph graph, ICanvasContext ctx, Resource sourceElement, Resource targetElement, Point2D offset) throws DatabaseException {
\r
372 AffineTransform at = getCopyTransform(graph, ctx, sourceElement);
\r
373 Point2D snapped = snap(ctx, new Point2D.Double(at.getTranslateX() + offset.getX(), at.getTranslateY() + offset.getY()));
\r
374 at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), snapped.getX(), snapped.getY());
\r
375 DiagramGraphUtil.setTransform(graph, targetElement, at);
\r
379 private static AffineTransform getCopyTransform(ReadGraph graph, ICanvasContext ctx, Resource sourceElement) throws DatabaseException {
\r
381 Resource runtimeDiagram = (Resource)((IDiagram)ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM)).getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
\r
382 return DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, sourceElement);
\r
384 return DiagramGraphUtil.getAffineTransform(graph, sourceElement);
\r
393 public static Point2D snap(ICanvasContext ctx, Point2D p) {
\r
395 ISnapAdvisor snapAdvisor = ctx.getHintStack().getHint(DiagramHints.SNAP_ADVISOR);
\r
396 if (snapAdvisor != null)
\r
397 snapAdvisor.snap(p);
\r
402 // ------------------------------------------------------------------------
\r
405 * Performs the operations related to a diagram-local cut-paste operation.
\r
406 * This default implementation will merely translate the selection specified
\r
407 * by the operation.
\r
410 * @throws DatabaseException
\r
412 public static void localCutPaste(final PasteOperation op) throws DatabaseException {
\r
413 Simantics.getSession().sync(new WriteRequest() {
\r
415 public void perform(WriteGraph graph) throws DatabaseException {
\r
416 graph.markUndoPoint();
\r
417 localCutPaste(graph, op);
\r
418 Layer0Utils.addCommentMetadata(graph, "Cutted " + op + " to local target");
\r
424 * Performs the operations related to a diagram-local cut-paste operation.
\r
425 * This default implementation will merely translate the selection specified
\r
426 * by the operation.
\r
430 * @throws DatabaseException
\r
432 public static void localCutPaste(WriteGraph graph, PasteOperation op) throws DatabaseException {
\r
433 if (op.sameDiagram() && op.cut) {
\r
434 CopyPasteUtil.moveElements(graph, op.ea.nodes, op.offset);
\r
435 CopyPasteUtil.moveElements(graph, op.ea.flags, op.offset);
\r
436 if(!op.ea.flags.isEmpty()) {
\r
437 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
\r
438 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
439 for(Resource flag : op.ea.flags) {
\r
440 double[] transform = graph.getRelatedValue(flag, DIA.HasTransform, Bindings.DOUBLE_ARRAY);
\r
441 ioTablesInfo.updateBinding(graph, DIA, flag, transform[4], transform[5]);
\r
444 CopyPasteUtil.moveElements(graph, CopyPasteUtil.gatherBranchPoints(graph, op.ea), op.offset);
\r
445 CopyPasteUtil.moveRouteGraphConnections(graph, CopyPasteUtil.gatherRouteGraphConnections(graph, op.ea), op.offset);
\r
446 CopyPasteUtil.moveElements(graph, op.ea.others, op.offset);
\r
447 CopyPasteUtil.moveMonitors(graph, op, op.ea.monitors, op.offset.getX(), op.offset.getY());
\r
448 CopyPasteUtil.moveReferenceElements(graph, op, op.ea.references, op.offset.getX(), op.offset.getY());
\r
454 * @throws DatabaseException
\r
456 public static void continueFlags(final PasteOperation op) throws DatabaseException {
\r
457 Simantics.getSession().sync(new WriteRequest() {
\r
459 public void perform(WriteGraph graph) throws DatabaseException {
\r
460 continueFlags(graph, op);
\r
468 * @throws DatabaseException
\r
470 public static void continueFlags(WriteGraph graph, final PasteOperation op) throws DatabaseException {
\r
471 IModifiableSynchronizationContext targetContext = (IModifiableSynchronizationContext) op.target.getHint(SynchronizationHints.CONTEXT);
\r
472 if (targetContext == null)
\r
473 throw new IllegalArgumentException("target diagram has no synchronization context");
\r
475 CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);
\r
477 throw new IllegalArgumentException("no copy advisor");
\r
479 Layer0 L0 = Layer0.getInstance(graph);
\r
480 DiagramResource DIA = DiagramResource.getInstance(graph);
\r
482 FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);
\r
483 int joinedFlags = 0;
\r
485 for (Resource src : op.ea.flags) {
\r
486 Resource sourceDiagram = graph.getPossibleObject(src, L0.PartOf);
\r
487 Resource copy = CopyAdvisorUtil.copy(targetContext, graph, ca, src, sourceDiagram, op.targetDiagram);
\r
490 OrderedSetUtils.add(graph, op.targetDiagram, copy);
\r
491 graph.claim(op.targetDiagram, L0.ConsistsOf, copy);
\r
492 AddElement.claimFreshElementName(graph, op.targetDiagram, copy);
\r
494 GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);
\r
496 glm.removeFromAllLayers(graph, copy);
\r
497 glm.putElementOnVisibleLayers(op.target, graph, copy);
\r
500 AffineTransform at = CopyPasteUtil.copyElementPosition(graph, op.ctx, src, copy, op.offset);
\r
501 Type type = FlagUtil.getFlagType(graph, src, Type.Out);
\r
502 FlagUtil.setFlagType(graph, copy, type.other());
\r
504 FlagUtil.join(graph, src, copy);
\r
506 if (scheme != null) {
\r
507 String label = scheme.generateLabel(graph, op.targetDiagram);
\r
508 if (label != null) {
\r
509 graph.claimLiteral(src, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
\r
510 graph.claimLiteral(copy, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);
\r
514 // Update flag table binding
\r
515 IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);
\r
516 ioTablesInfo.updateBinding(graph, DIA, copy, at.getTranslateX(), at.getTranslateY());
\r
521 if (joinedFlags > 0) {
\r
522 // Add comment to change set.
\r
523 CommentMetadata cm = graph.getMetadata(CommentMetadata.class);
\r
524 graph.addMetadata(cm.add("Continued " + joinedFlags + " flag(s)"));
\r
530 * @throws DatabaseException
\r
532 public static void performDefaultPaste(PasteOperation op) throws DatabaseException {
\r
533 Session session = Simantics.getSession();
\r
534 new Paster(session, op).perform();
\r