]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/handler/CopyPasteUtil.java
Logger fixes after merge commit:fdbe8762
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / CopyPasteUtil.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.diagram.handler;\r
13 \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
20 \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
63 \r
64 /**\r
65  * @author Tuukka Lehtonen\r
66  */\r
67 public final class CopyPasteUtil {\r
68 \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
77 \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
82     }\r
83 \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
88     }\r
89 \r
90     public static boolean onlyFlagsWithoutCorrespondence(ElementAssortment ea) {\r
91         return isFlagsOnlySelection(ea)\r
92         && checkFlagsCorrespondences(ea.flags, false);\r
93     }\r
94 \r
95     /**\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
99      * \r
100      * @param flags\r
101      * @param expectedValue\r
102      * @return\r
103      * @throws DatabaseException \r
104      */\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
107             @Override\r
108             public Boolean perform(ReadGraph graph) throws DatabaseException {\r
109                 return checkFlagsCorrespondences(graph, flags, expectedValue);\r
110             }\r
111         });\r
112     }\r
113 \r
114     /**\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
118      * \r
119      * @param flags\r
120      * @param expectedValue\r
121      * @return\r
122      * @throws DatabaseException \r
123      */\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
127                 return false;\r
128             }\r
129         }\r
130         return true;\r
131     }\r
132 \r
133     /**\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
137      * \r
138      * @param flags\r
139      * @param expectedValue\r
140      * @return\r
141      */\r
142     public static boolean checkFlagsCorrespondences(Iterable<IElement> flags, boolean expectedValue) {\r
143         for (IElement flag : flags) {\r
144             if (flagHasCorrespondence(flag) != expectedValue) {\r
145                 return false;\r
146             }\r
147         }\r
148         return true;\r
149     }\r
150 \r
151     /**\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
156      * @return\r
157      * @throws DatabaseException \r
158      */\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
161             @Override\r
162             public Boolean perform(ReadGraph graph) throws DatabaseException {\r
163                 return checkFlagExternality(graph, flags, expectedValue);\r
164             }\r
165         });\r
166     }\r
167 \r
168     /**\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
173      * @return\r
174      * @throws DatabaseException \r
175      */\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
179                 return false;\r
180         return true;\r
181     }\r
182 \r
183     /**\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
188      * @return\r
189      */\r
190     public static boolean checkFlagExternality(Iterable<IElement> flags, boolean expectedValue) {\r
191         for (IElement flag : flags)\r
192             if (flagIsExternal(flag) != expectedValue)\r
193                 return false;\r
194         return true;\r
195     }\r
196 \r
197     public static boolean flagHasCorrespondence(IElement flag) {\r
198         FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
199         if (fh == null)\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
203     }\r
204 \r
205     public static boolean flagIsExternal(IElement flag) {\r
206         FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
207         if (fh == null)\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
211     }\r
212 \r
213     /**\r
214      * @param graph \r
215      * @param connection\r
216      * @return\r
217      * @throws DatabaseException \r
218      */\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
224         return bps;\r
225     }\r
226 \r
227     /**\r
228      * @param connection\r
229      * @return\r
230      * @throws DatabaseException \r
231      */\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
238         }\r
239         return rgcs;\r
240     }\r
241 \r
242     /**\r
243      * @param connection\r
244      * @return\r
245      * @throws DatabaseException \r
246      */\r
247     public static Collection<Resource> getBranchPoints(ReadGraph graph, Resource connection) throws DatabaseException {\r
248         return graph.getObjects(connection, DiagramResource.getInstance(graph).HasBranchPoint);\r
249     }\r
250 \r
251     /**\r
252      * @param m\r
253      * @param elements\r
254      * @param xoffset\r
255      * @param yoffset\r
256      * @throws DatabaseException \r
257      */\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
266         }\r
267     }\r
268 \r
269     /**\r
270      * @param m\r
271      * @param elements\r
272      * @param offset\r
273      * @throws DatabaseException \r
274      */\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
277     }\r
278 \r
279     /**\r
280      * @param m\r
281      * @param elements\r
282      * @param xoffset\r
283      * @param yoffset\r
284      * @throws DatabaseException \r
285      */\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
292                 continue;\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
296                 continue;\r
297 \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
303         }\r
304     }\r
305 \r
306     /**\r
307      * @param m\r
308      * @param elements\r
309      * @param xoffset\r
310      * @param yoffset\r
311      * @throws DatabaseException \r
312      */\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
316     }\r
317 \r
318 \r
319     /**\r
320      * @param m\r
321      * @param elements\r
322      * @param xoffset\r
323      * @param yoffset\r
324      * @throws DatabaseException \r
325      */\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
329     }\r
330 \r
331     /**\r
332      * @param graph\r
333      * @param connections\r
334      * @param xoffset\r
335      * @param yoffset\r
336      * @throws DatabaseException \r
337      */\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
344         }\r
345     }\r
346     \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
349     }\r
350 \r
351     /**\r
352      * @param ctx\r
353      * @param source\r
354      * @param target\r
355      * @param offset\r
356      */\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
362     }\r
363 \r
364     /**\r
365      * @param ctx\r
366      * @param source\r
367      * @param target\r
368      * @param offset\r
369      * @throws DatabaseException \r
370      */\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
376         return at;\r
377     }\r
378     \r
379     private static AffineTransform getCopyTransform(ReadGraph graph, ICanvasContext ctx, Resource sourceElement) throws DatabaseException {\r
380         if(ctx != null){\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
383         } else {\r
384             return DiagramGraphUtil.getAffineTransform(graph, sourceElement);\r
385         }\r
386     }\r
387 \r
388     /**\r
389      * @param ctx\r
390      * @param p\r
391      * @return\r
392      */\r
393     public static Point2D snap(ICanvasContext ctx, Point2D p) {\r
394         if (ctx != null) {\r
395             ISnapAdvisor snapAdvisor = ctx.getHintStack().getHint(DiagramHints.SNAP_ADVISOR);\r
396             if (snapAdvisor != null)\r
397                 snapAdvisor.snap(p);\r
398         }\r
399         return p;\r
400     }\r
401 \r
402     // ------------------------------------------------------------------------\r
403 \r
404     /**\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
408      * \r
409      * @param op\r
410      * @throws DatabaseException \r
411      */\r
412     public static void localCutPaste(final PasteOperation op) throws DatabaseException {\r
413         Simantics.getSession().sync(new WriteRequest() {\r
414             @Override\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
419             }\r
420         });\r
421     }\r
422 \r
423     /**\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
427      * \r
428      * @param graph\r
429      * @param op\r
430      * @throws DatabaseException \r
431      */\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
442                 }\r
443             }\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
449         }\r
450     }\r
451 \r
452     /**\r
453      * @param op\r
454      * @throws DatabaseException \r
455      */\r
456     public static void continueFlags(final PasteOperation op) throws DatabaseException {\r
457         Simantics.getSession().sync(new WriteRequest() {\r
458             @Override\r
459             public void perform(WriteGraph graph) throws DatabaseException {\r
460                 continueFlags(graph, op);\r
461             }\r
462         });\r
463     }\r
464 \r
465     /**\r
466      * @param graph\r
467      * @param op\r
468      * @throws DatabaseException \r
469      */\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
474 \r
475         CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);\r
476         if (ca == null)\r
477             throw new IllegalArgumentException("no copy advisor");\r
478 \r
479         Layer0 L0 = Layer0.getInstance(graph);\r
480         DiagramResource DIA = DiagramResource.getInstance(graph);\r
481 \r
482         FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);\r
483         int joinedFlags = 0;\r
484 \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
488             if(copy == null)\r
489                 continue;\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
493 \r
494             GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);\r
495             if (glm != null) {\r
496                 glm.removeFromAllLayers(graph, copy);\r
497                 glm.putElementOnVisibleLayers(op.target, graph, copy);\r
498             }\r
499 \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
503 \r
504             FlagUtil.join(graph, src, copy);\r
505 \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
511                 }\r
512             }\r
513 \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
517 \r
518             ++joinedFlags;\r
519         }\r
520 \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
525         }\r
526     }\r
527 \r
528     /**\r
529      * @param op\r
530      * @throws DatabaseException\r
531      */\r
532     public static void performDefaultPaste(PasteOperation op) throws DatabaseException {\r
533         Session session = Simantics.getSession();\r
534         new Paster(session, op).perform();\r
535     }\r
536 \r
537 }\r