Removed unnecessary dependencies on org.apache.log4j
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / handler / CopyPasteHandler.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.handler;
13
14 import java.awt.Color;
15 import java.awt.event.KeyEvent;
16 import java.awt.geom.AffineTransform;
17 import java.awt.geom.Point2D;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Set;
23
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.jface.action.IStatusLineManager;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.ui.IPartListener;
29 import org.eclipse.ui.IWorkbenchPart;
30 import org.eclipse.ui.IWorkbenchPartSite;
31 import org.eclipse.ui.PlatformUI;
32 import org.simantics.Simantics;
33 import org.simantics.databoard.Bindings;
34 import org.simantics.db.ReadGraph;
35 import org.simantics.db.Resource;
36 import org.simantics.db.WriteGraph;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.common.utils.OrderedSetUtils;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.db.layer0.util.ClipboardUtils;
41 import org.simantics.db.layer0.util.SimanticsClipboard;
42 import org.simantics.db.layer0.util.SimanticsClipboard.Representation;
43 import org.simantics.db.layer0.util.SimanticsKeys;
44 import org.simantics.db.layer0.variable.Variable;
45 import org.simantics.db.layer0.variable.Variables;
46 import org.simantics.db.request.Read;
47 import org.simantics.diagram.content.Change;
48 import org.simantics.diagram.content.ConnectionUtil;
49 import org.simantics.diagram.content.DiagramContentChanges;
50 import org.simantics.diagram.content.DiagramContentTracker;
51 import org.simantics.diagram.internal.Activator;
52 import org.simantics.diagram.stubs.DiagramResource;
53 import org.simantics.diagram.stubs.G2DResource;
54 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
55 import org.simantics.diagram.synchronization.runtime.DiagramSelectionUpdater;
56 import org.simantics.diagram.ui.DiagramModelHints;
57 import org.simantics.g2d.canvas.ICanvasContext;
58 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
59 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
60 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
61 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
62 import org.simantics.g2d.connection.ConnectionEntity;
63 import org.simantics.g2d.connection.handler.ConnectionHandler;
64 import org.simantics.g2d.diagram.IDiagram;
65 import org.simantics.g2d.diagram.handler.Topology;
66 import org.simantics.g2d.diagram.handler.Topology.Connection;
67 import org.simantics.g2d.diagram.handler.Topology.Terminal;
68 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
69 import org.simantics.g2d.diagram.participant.Selection;
70 import org.simantics.g2d.element.ElementClass;
71 import org.simantics.g2d.element.ElementHints;
72 import org.simantics.g2d.element.ElementUtils;
73 import org.simantics.g2d.element.IElement;
74 import org.simantics.g2d.element.handler.BendsHandler;
75 import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;
76 import org.simantics.g2d.element.handler.Move;
77 import org.simantics.g2d.element.handler.TerminalTopology;
78 import org.simantics.g2d.element.handler.Transform;
79 import org.simantics.g2d.elementclass.FlagHandler;
80 import org.simantics.g2d.participant.GridPainter;
81 import org.simantics.g2d.participant.MouseUtil;
82 import org.simantics.g2d.participant.MouseUtil.MouseInfo;
83 import org.simantics.layer0.Layer0;
84 import org.simantics.modeling.ModelingResources;
85 import org.simantics.operation.Layer0X;
86 import org.simantics.project.IProject;
87 import org.simantics.scenegraph.INode;
88 import org.simantics.scenegraph.ParentNode;
89 import org.simantics.scenegraph.g2d.G2DParentNode;
90 import org.simantics.scenegraph.g2d.IG2DNode;
91 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
92 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
93 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;
94 import org.simantics.scenegraph.g2d.events.MouseEvent;
95 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
96 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
97 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
98 import org.simantics.scenegraph.g2d.events.command.Command;
99 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
100 import org.simantics.scenegraph.g2d.events.command.Commands;
101 import org.simantics.scenegraph.g2d.nodes.LinkNode;
102 import org.simantics.scenegraph.g2d.nodes.LocalDelegateNode;
103 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
104 import org.simantics.scenegraph.utils.NodeMapper;
105 import org.simantics.utils.datastructures.collections.CollectionUtils;
106 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
107 import org.simantics.utils.datastructures.hints.IHintContext.Key;
108 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
109 import org.simantics.utils.datastructures.hints.IHintListener;
110 import org.simantics.utils.datastructures.hints.IHintObservable;
111 import org.simantics.utils.logging.TimeLogger;
112 import org.simantics.utils.threads.SWTThread;
113 import org.simantics.utils.threads.ThreadUtils;
114 import org.simantics.utils.ui.ErrorLogger;
115 import org.simantics.utils.ui.SWTUtils;
116 import org.slf4j.Logger;
117 import org.slf4j.LoggerFactory;
118
119 /**
120  * CopyPasteHandler is a canvas handler for Commands.CUT, Commands.COPY and
121  * Commands.PASTE commands for an IDiagram.
122  * 
123  * <p>
124  * The handler attempts to copy/paste the current selection for pointer 0,
125  * meaning {@link Selection#SELECTION0}.
126  * </p>
127  * 
128  * <p>
129  * The handler logic follows the specifications at <a
130  * href="http://www.simantics.org/wiki/index.php/UC:Copy_Item" >UC:Copy Item</a>
131  * and <a href="http://www.simantics.org/wiki/index.php/UC:Cut_Item" >UC:Cut
132  * Item</a>.
133  * </p>
134  * 
135  * @see Selection current diagram selection source
136  * 
137  * @author Tuukka Lehtonen
138  * 
139  *         FIXME: translucent ghosting makes rendering REALLY sluggish, add a
140  *         timer that makes the ghost opaque when the user is interacting and
141  *         translucent only when still for a while.
142  */
143 public class CopyPasteHandler extends AbstractDiagramParticipant {
144
145     private static final Logger LOGGER = LoggerFactory.getLogger(CopyPasteHandler.class);
146
147     public static final Key KEY_CUT_SELECTION_FRAME_COLOR      = new KeyOf(Color.class, "CUT_SELECTION_FRAME_COLOR");
148     public static final Key KEY_CUT_SELECTION_CONTENT_COLOR    = new KeyOf(Color.class, "CUT_SELECTION_CONTENT_COLOR");
149     public static final Key KEY_COPIED_SELECTION_FRAME_COLOR   = new KeyOf(Color.class, "COPY_SELECTION_FRAME_COLOR");
150     public static final Key KEY_COPIED_SELECTION_CONTENT_COLOR = new KeyOf(Color.class, "COPY_SELECTION_CONTENT_COLOR");
151
152     /**
153      * A key for storing the current selection within the currently active
154      * project for copy/paste implementation.
155      */
156     private static final Key              KEY_DIAGRAM_SELECTION              = DiagramSelectionRepresentation.KEY_DIAGRAM_SELECTION;
157
158     private static final boolean          DEBUG                              = false;
159     private static final boolean          DEBUG_SELECTION_UPDATE             = false;
160
161     public static final int               COPY_GHOSTING_PAINT_PRIORITY       = 600;
162
163     protected static final int            HIGHLIGHT_PAINT_PRIORITY           = 500;
164
165     @Dependency
166     protected Selection                   sel;
167     @Dependency
168     protected MouseUtil                   mouseUtil;
169
170     protected final IStatusLineManager    statusLine;
171     protected final CopyPasteStrategy     strategy;
172     protected IWorkbenchPartSite          site;
173     protected IWorkbenchPartSite          listenedSite;
174
175     /**
176      * Workbench part listener for {@link #listenedSite} to keep proper track of
177      * whether this part is focused or not.
178      */
179     IPartListener partListener = new IPartListener() {
180         @Override
181         public void partOpened(IWorkbenchPart part) {
182         }
183         @Override
184         public void partDeactivated(IWorkbenchPart part) {
185             if (part == site.getPart())
186                 hasFocus = false;
187         }
188         @Override
189         public void partClosed(IWorkbenchPart part) {
190             // Make sure this listener is removed properly in any case.
191             if (listenedSite != null) {
192                 listenedSite.getPage().removePartListener(partListener);
193                 listenedSite = null;
194             }
195         }
196         @Override
197         public void partBroughtToTop(IWorkbenchPart part) {
198         }
199         @Override
200         public void partActivated(IWorkbenchPart part) {
201             if (part == site.getPart())
202                 hasFocus = true;
203         }
204     };
205
206     /**
207      * Indicates whether CopyPasteHandler thinks that {@link #site} has focus. 
208      */
209     protected boolean                     hasFocus                           = false;
210
211     protected AbstractCanvasParticipant   highlightMode                      = null;
212     private IProject                      observedProject                    = null;
213
214     /**
215      * A counter for how many times pasting has been performed without mouse and
216      * ghosting or how many times paste has been performed without moving the
217      * mouse on the diagram. This is used to offset the paste position
218      * accordingly so that copied elements don't wind up directly on top of each
219      * other.
220      */
221     private int                           pasteWithoutMovingGhostCounter     = 0;
222
223     /**
224      * An offset used when pasting without mouse/ghosting. It forces keyboard
225      * pastes to stack up on top of the latest paste performed with
226      * mouse/ghosting.
227      */
228     private final Point2D                 pasteOffset                        = new Point2D.Double(0, 0);
229
230     /**
231      * Stores the last MouseInfo for mouse 0 from the time of the previous
232      * received mouse event. Used for deciding the paste position.
233      * 
234      * @see #getPastePos(DiagramSelection)
235      */
236     private MouseInfo                     mouseInfo;
237
238     /**
239      * Scale to use for pasted diagram monitors from variables.
240      */
241     private double                        monitorScale = 0.2;
242
243     /**
244      * For updating the diagram selection after graph changes.
245      */
246     private DiagramSelectionUpdater       selectionUpdater                   = null;
247
248     public CopyPasteHandler() {
249         this(new DefaultCopyPasteStrategy());
250     }
251
252     public CopyPasteHandler(CopyPasteStrategy strategy) {
253         this(strategy, null);
254     }
255
256     public CopyPasteHandler(IStatusLineManager statusLine) {
257         this(new DefaultCopyPasteStrategy(), statusLine);
258     }
259
260     public CopyPasteHandler(CopyPasteStrategy strategy, IStatusLineManager statusLine) {
261         this.strategy = strategy != null ? strategy : new DefaultCopyPasteStrategy();
262         this.statusLine = statusLine;
263     }
264
265     public CopyPasteHandler(CopyPasteStrategy strategy, IStatusLineManager statusLine, double monitorScale) {
266         this.strategy = strategy != null ? strategy : new DefaultCopyPasteStrategy();
267         this.statusLine = statusLine;
268         setMonitorScale(monitorScale);
269     }
270
271     public CopyPasteHandler setMonitorScale(double scale) {
272         this.monitorScale = scale;
273         return this;
274     }
275
276     public CopyPasteHandler setWorkbenchSite(IWorkbenchPartSite site) {
277         this.site = site;
278         return this;
279     }
280
281     protected boolean isPasteAllowed() {
282         return listenedSite == null || hasFocus;
283     }
284
285     @Override
286     public void addedToContext(ICanvasContext ctx) {
287         super.addedToContext(ctx);
288         addProjectListener(peekProject());
289
290         listenedSite = site;
291         if (listenedSite != null) {
292             listenedSite.getPage().addPartListener(partListener);
293         }
294     }
295
296     @Override
297     public void removedFromContext(ICanvasContext ctx) {
298         // Remove project selection if its ours to prevent leaking memory.
299         DiagramSelection ds = getProjectSelection();
300         if (ds.getSourceCanvas() == ctx) {
301             removeProjectSelection();
302         }
303
304         if (listenedSite != null) {
305             listenedSite.getPage().removePartListener(partListener);
306             listenedSite = null;
307         }
308
309         removeProjectListener();
310         super.removedFromContext(ctx);
311     }
312
313     @Override
314     protected void onDiagramSet(IDiagram newDiagram, IDiagram oldDiagram) {
315         if (oldDiagram != null) {
316             if (selectionUpdater != null) {
317                 selectionUpdater.untrack();
318                 selectionUpdater = null;
319             }
320         }
321         if (newDiagram != null) {
322             selectionUpdater = new DiagramSelectionUpdater(getContext(), newDiagram).track();
323         }
324     }
325
326     IHintListener projectDiagramSelectionListener = new HintListenerAdapter() {
327         @Override
328         public void hintChanged(IHintObservable sender, Key key, Object oldValue, final Object newValue) {
329             //System.out.println(this + ": " + sender + ": " + newValue);
330             ICanvasContext ctx = getContext();
331             if (ctx != null && hasHighlight()) {
332                 //System.out.println(this + " HAS HIGHLIGHT");
333                 if (newValue == null || ((DiagramSelection) newValue).getSourceCanvas() != ctx) {
334                     //System.out.println(this + " REMOVING HIGHLIGHT");
335                     ctx.getThreadAccess().asyncExec(new Runnable() {
336                         @Override
337                         public void run() {
338                             removeHighlight();
339                         }
340                     });
341                 }
342             }
343         }
344     };
345
346     private void addProjectListener(IProject observable) {
347         if (observable != null) {
348             observable.addKeyHintListener(KEY_DIAGRAM_SELECTION, projectDiagramSelectionListener);
349             observedProject = observable;
350         }
351     }
352
353     private void removeProjectListener() {
354         if (observedProject != null) {
355             observedProject.removeKeyHintListener(KEY_DIAGRAM_SELECTION, projectDiagramSelectionListener);
356             observedProject = null;
357         }
358     }
359
360     IProject getProject() {
361         return Simantics.getProject();
362     }
363
364     IProject peekProject() {
365         return Simantics.peekProject();
366     }
367
368     public DiagramSelection getClipboardDiagramSelection() {
369         for (Set<Representation> content : Simantics.getClipboard().getContents()) {
370             try {
371                 DiagramSelection sel = ClipboardUtils.accept(content, DiagramSelectionRepresentation.KEY_DIAGRAM_SELECTION);
372                 if (sel != null)
373                     return sel;
374             } catch (DatabaseException e) {
375                 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to retrieve clipboard content.", e));
376             }
377         }
378         return DiagramSelection.EMPTY;
379     }
380
381     protected DiagramSelection getProjectSelection() {
382         IProject p = peekProject();
383         if (p == null)
384             return DiagramSelection.EMPTY;
385         DiagramSelection ds = p.getHint(KEY_DIAGRAM_SELECTION);
386         return ds != null ? ds : DiagramSelection.EMPTY;
387     }
388
389     protected void setDiagramSelection(DiagramSelection selection) {
390         setProjectSelection(selection);
391         strategy.copyToClipboard(selection);
392     }
393
394     protected void setProjectSelection(DiagramSelection selection) {
395         assert selection != null;
396         IProject p = getProject();
397         if (p == null)
398             throw new IllegalStateException("no active project for selection");
399         clearSG();
400         pasteWithoutMovingGhostCounter = 0;
401         pasteOffset.setLocation(0, 0);
402         p.setHint(KEY_DIAGRAM_SELECTION, selection);
403     }
404
405     protected void removeProjectSelection() {
406         setProjectSelection(DiagramSelection.EMPTY);
407         removeHighlight();
408         clearSG();
409         setDirty();
410     }
411
412     /**
413      * This DELETE command handler is required to remove any diagram selections
414      * when modules are deleted. This will prevent extra highlight painting from
415      * being left over in case a module marked for copying is deleted.
416      */
417     @EventHandler(priority = 100)
418     public boolean handleDelete(CommandEvent e) {
419         if (e.command.equals( Commands.DELETE) ) {
420             if (highlightMode != null) {
421                 message(null);
422                 removeProjectSelection();
423                 return false;
424             }
425         }
426         return false;
427     }
428
429     @EventHandler(priority = 0)
430     public boolean handleKey(org.simantics.scenegraph.g2d.events.KeyEvent e) {
431         if (e.keyCode == KeyEvent.VK_CONTROL) {
432             DiagramSelection ds = getProjectSelection();
433             if (!ds.isEmpty()) {
434                 if (e instanceof KeyPressedEvent) {
435                     if (ds.isCut())
436                         message("Move selection");
437                     else
438                         message("Paste selection");
439                     updateSG(ds);
440                 } else if (e instanceof KeyReleasedEvent) {
441                     selectedMessage(ds);
442                     hideSG(ds);
443                 }
444                 setDirty();
445             }
446         }
447         return false;
448     }
449
450     @EventHandler(priority = 0)
451     public boolean handleCommand(CommandEvent e) {
452         if (e.command.equals( Commands.CANCEL) ) {
453             DiagramSelection s = getProjectSelection();
454             if (highlightMode != null || !s.isEmpty()) {
455                 message(null);
456                 removeProjectSelection();
457                 return true;
458             }
459             return false;
460         }
461         if (e.command.equals( Commands.CUT ) || e.command.equals( Commands.COPY )) {
462             boolean ret = initiateCopy( e.command.equals( Commands.CUT ) );
463             if (!ret)
464                 removeProjectSelection();
465             return ret;
466         }
467         // Must have focus in order to paste! If mouse has left the editor, no
468         // pastes should be performed.
469         if (isPasteAllowed() && e.command.equals( Commands.PASTE )) {
470             DiagramSelection ds = getClipboardDiagramSelection();
471             if (ds.isEmpty()) {
472                 return tryPasteMonitors();
473             }
474             return paste(e.command, ds);
475         }
476         return false;
477     }
478
479     protected boolean initiateCopy(boolean cut) {
480         //System.out.println("INITIATING COPY");
481         int selectionId = 0;
482
483         Set<IElement> ss = sel.getSelection(selectionId);
484         Point2D copyPos = getCopyStartPos(ss);
485         if (ss.isEmpty() || copyPos == null) {
486             message("Nothing to " + (cut ? "cut" : "copy"));
487             return false;
488         }
489
490         // Validate selection, don't initiate copy if selection is invalid.
491         ElementAssortment ea = new ElementAssortment(ss);
492         String error = fixAssortment(ea, cut);
493
494         if (error != null) {
495             message(error);
496             return false;
497         }
498
499         pruneAssortment(ea, cut);
500         if (ea.isEmpty()) {
501             message("Nothing to " + (cut ? "cut" : "copy"));
502             return false;
503         }
504
505         // Allow the possible CopyStrategy to filter the copied element
506         // assortment also.
507         if (strategy instanceof CopyStrategy) {
508             if (DEBUG)
509                 System.out.println("Commencing CopyStrategy filtering with " + ea);
510             IStatus status = ((CopyStrategy) strategy).copy(new CopyOperation(getContext(), ea, cut));
511             if (!status.isOK()) {
512                 switch (status.getSeverity()) {
513                 case IStatus.CANCEL:
514                 case IStatus.WARNING:
515                     message(status.getMessage());
516                     break;
517                 case IStatus.ERROR:
518                     error(status.getMessage());
519                     break;
520                 }
521                 return false;
522             }
523         }
524
525         if (DEBUG)
526             System.out.println("Start copy with " + ea);
527
528         // Treat OTHER type elements as disconnected floating graphical elements
529         // that are always copied.
530
531         // Anything with connection parts cannot be copied
532         if (!cut && ea.containsAny(CopyPasteUtil.CONNECTION_PARTS)) {
533             error("Cannot copy connection segments nor branch points.");
534             return false;
535         }
536 //        if (ea.contains(CopyPasteUtil.MONITORS)) {
537 //            // TODO: allow copying of monitors, means fixing the component reference relations
538 //            error("Monitor " + (cut ? "cut" : "copy") + " not supported yet.");
539 //            return false;
540 //        }
541
542         // Pre-validate flag selection cases
543         if (ea.contains(CopyPasteUtil.FLAGS)) {
544             if (cut) {
545                 // Allow cutting of single flags or cutting of any amount of
546                 // flags within a single diagram.
547             } else {
548 //                // Deny flag copy if other kinds of elements are selected.
549 //                if (ea.containsAny(NOT_FLAGS)) {
550 //                    return false;
551 //                }
552                 // Only copy flags without correspondence for now.
553                 if (CopyPasteUtil.isFlagsOnlySelection(ea)) {
554                     if (!CopyPasteUtil.checkFlagsCorrespondences(ea.flags, false)) {
555                         error("Cannot copy flag that already has a correspondence.");
556                         return false;
557                     }
558                 }
559             }
560         }
561
562         // Selection is valid, go ahead and initiate a copy operation.
563         Resource sourceDiagram = diagram.<Resource>getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
564         DiagramSelection ds = new DiagramSelection(getContext(), sourceDiagram, ea.getAll(), cut, copyPos);
565         setDiagramSelection(ds);
566
567         removeHighlight();
568         highlightMode = new HighlightMode(ds, selectionId, HIGHLIGHT_PAINT_PRIORITY);
569         getContext().add(highlightMode);
570
571         selectedMessage(ds);
572
573 //        System.out.println("INITIATED COPY: " + ds);
574         return true;
575     }
576
577     public boolean paste(Command command, DiagramSelection ds) {
578         if (ds.isEmpty()) {
579             message(null);
580             return false;
581         }
582
583         TimeLogger.resetTimeAndLog(getClass(), "paste");
584
585         ElementObjectAssortment ea = ds.getAssortment();
586
587         if (DEBUG)
588             System.out.println("Initiate paste with " + ea);
589
590         try {
591             if (CopyPasteUtil.isFlagsOnlySelection(ea)) {
592                 // Do not copy if any of the flags already have a correspondence.
593                 if (!CopyPasteUtil.onlyFlagsWithoutCorrespondence(Simantics.getSession(), ea))
594                     return true;
595
596                 if (ds.isCut()) {
597                     normalPaste(command, ds, ea, true);
598                     removeHighlight();
599                     setDiagramSelection(DiagramSelection.EMPTY);
600                     resetSourceSelection(ds);
601                 } else {
602                     normalPaste(command, ds, ea, false);
603                     // There is no point in leaving the old copy selection hanging
604                     // around after copying a flag since it is a one shot operation.
605                     removeHighlight();
606                     setDiagramSelection(DiagramSelection.EMPTY);
607                 }
608             } else {
609                 if (ds.isCut()) {
610                     normalPaste(command, ds, ea, true);
611                     removeHighlight();
612                     setDiagramSelection(DiagramSelection.EMPTY);
613                     resetSourceSelection(ds);
614                 } else {
615                     normalPaste(command, ds, ea, false);
616
617 //                // This is necessary to keep the ghost diagram properly up-to-date
618 //                // after paste operations.
619 //                setProjectSelection(ds.remutate());
620                 }
621             }
622
623             message(null);
624
625         } catch (PasteException e) {
626             error( e.getLocalizedMessage() );
627             ErrorLogger.defaultLog( new Status(IStatus.INFO, Activator.PLUGIN_ID, "Problem in diagram paste operation, see exception for details.", e) );
628         } catch (DatabaseException e) {
629             error( e.getLocalizedMessage() );
630             ErrorLogger.defaultLog( new Status(IStatus.INFO, Activator.PLUGIN_ID, "Problem in diagram paste operation, see exception for details.", e) );
631         }
632
633         // Clear ghosting
634         clearSG();
635         setDirty();
636
637         return true;
638     }
639
640     /**
641      * In cut/paste cases, the source selection should reset (removed) after the
642      * paste operation has been performed. This method will reset the source
643      * selection of the specified DiagramSelection from the source canvas
644      * context. This will work regardless of which diagram/editor the selection
645      * originates from.
646      * 
647      * @param ds the source selection to reset
648      */
649     void resetSourceSelection(DiagramSelection ds) {
650         ICanvasContext cc = ds.getSourceCanvas();
651         boolean sameDiagram = diagram == ds.getSourceDiagram();
652         if (!sameDiagram && cc != null && !cc.isDisposed()) {
653             for (Selection sourceSelection : cc.getItemsByClass(Selection.class)) {
654                 Collection<IElement> empty = Collections.emptySet();
655                 sourceSelection.setSelection(0, empty);
656             }
657         }
658     }
659
660     private void normalPaste(Command command, DiagramSelection ds, ElementObjectAssortment ea, boolean cut) throws PasteException {
661         final Point2D copyPos = ds.getCopyPos();
662         final Point2D pastePos = getPastePos(ds);
663
664         double dx = pastePos.getX() - copyPos.getX();
665         double dy = pastePos.getY() - copyPos.getY();
666         final Point2D pasteOffset = new Point2D.Double(dx, dy);
667
668         try {
669             // Get diagram contents before the paste operation
670             Resource diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
671             final DiagramContentTracker tracker =
672                 diagramResource == null ?
673                         null
674                         : DiagramContentTracker.start(getContext(), Simantics.getSession(), diagramResource);
675
676             strategy.paste(new PasteOperation(command, getContext(), ds.getSourceDiagram(), diagramResource, diagram, ea, cut, pasteOffset));
677
678             if (tracker != null) {
679                 // Get difference of diagram contents to find out what was added.
680                 DiagramContentChanges changes = tracker.update();
681                 selectionUpdater.setNewSelection(0, changes.pick(changes.elements, Change.ADDED));
682                 if (DEBUG_SELECTION_UPDATE)
683                     System.out.println("stored diagram changes @" + System.currentTimeMillis() + ": " + selectionUpdater.getNewSelection());
684             }
685
686         } catch (DatabaseException e) {
687             ErrorLogger.defaultLogError(e);
688         }
689     }
690
691     protected String fixAssortment(ElementAssortment ea, boolean cut) {
692         Topology diagramTopology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
693         List<Connection> conns = new ArrayList<Connection>();
694
695         // Include flags whether they are selected or not
696         for (IElement edge : ea.edges) {
697             Connection bc = diagramTopology.getConnection(edge, EdgeEnd.Begin);
698             if (bc != null && bc.node != null) {
699                 if (bc.node.getElementClass().getAtMostOneItemOfClass(FlagHandler.class) != null)
700                     ea.add(ElementType.Flag, bc.node);
701             }
702             Connection ec = diagramTopology.getConnection(edge, EdgeEnd.End);
703             if (ec != null && ec.node != null) {
704                 if (ec.node.getElementClass().getAtMostOneItemOfClass(FlagHandler.class) != null)
705                     ea.add(ElementType.Flag, ec.node);
706             }
707         }
708
709         // Include connections for selected flags if we're not potentially
710         // making flag continuations.
711         if (!CopyPasteUtil.isFlagsOnlySelection(ea)) {
712             for (IElement flag : ea.flags) {
713                 conns.clear();
714                 diagramTopology.getConnections(flag, ElementUtils.getSingleTerminal(flag), conns);
715                 for (Connection conn : conns) {
716                     IElement edge = conn.edge;
717                     ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);
718                     ea.add(ElementType.Connection, ce.getConnection());
719                 }
720             }
721         }
722
723         // For each selected connection, make sure that all connected elements
724         // are in the selection, otherwise don't copy the connection.
725         List<IElement> connectionsToRemove = new ArrayList<IElement>(ea.connections.size());
726         for (IElement connection : ea.connections) {
727             ConnectionHandler ch = connection.getElementClass().getSingleItem(ConnectionHandler.class);
728             Collection<Connection> connectors = ch.getTerminalConnections(connection, null);
729             boolean allConnectorsSelected = true;
730             for (Connection c : connectors) {
731                 if (!(ea.nodes.contains(c.node) || ea.flags.contains(c.node) || ea.references.contains(c.node))) {
732                     allConnectorsSelected = false;
733                     break;
734                 }
735             }
736             if (!allConnectorsSelected)
737                 connectionsToRemove.add(connection);
738         }
739         ea.removeAll(ElementType.Connection, connectionsToRemove);
740
741         // Remove external flags whose connection(s) are not included
742         List<IElement> flagsToRemove = new ArrayList<IElement>(ea.flags.size());
743         for (IElement flag : ea.flags) {
744             if (CopyPasteUtil.flagIsExternal(flag)) {
745                 conns.clear();
746                 diagramTopology.getConnections(flag, ElementUtils.getSingleTerminal(flag), conns);
747                 for (Connection conn : conns) {
748                     IElement edge = conn.edge;
749                     ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);
750                     IElement connection = ce.getConnection();
751                     if (!ea.connections.contains(connection)) {
752                         flagsToRemove.add(flag);
753                     }
754                 }
755             }
756         }
757         ea.removeAll(ElementType.Flag, flagsToRemove);
758
759         if (cut) {
760             // Issue #1874: Prevent cut/paste for connected components
761             // https://www.simulationsite.net/redmine/issues/1874
762             // Fail if any of the included nodes has connections to it that are not
763             // included in the operation.
764             Collection<Connection> connections = new ArrayList<Connection>();
765             for (IElement node : CollectionUtils.join(ea.nodes, ea.flags)) {
766                 connections.clear();
767                 for (Connection connection : getAllConnections(node, connections)) {
768                     ConnectionEntity ce = connection.edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);
769                     IElement conn = ce.getConnection();
770                     if (ea.connections.contains(conn))
771                         continue;
772
773                     return "Cannot cut a node without all its connections.";
774                 }
775             }
776         }
777
778         // Remove all reference elements from the assortment whose parent elements are not in it.
779         if (!cut) {
780             Collection<IElement> referenceElementsToRemove = new ArrayList<IElement>();
781             for (IElement ref : ea.references) {
782                 IElement parent = ref.getHint(ElementHints.KEY_PARENT_ELEMENT);
783                 if (parent != null) {
784                     if (!ea.all.contains(parent)) {
785                         // Cannot copy reference element whose parent is not copied also.
786                         referenceElementsToRemove.add(ref);
787                     }
788                 } else {
789                     // OK, reference element has no parent. Free to copy/cut in any way.
790                 }
791             }
792             if (!referenceElementsToRemove.isEmpty()) {
793                 ea.removeAll(ElementType.Reference, referenceElementsToRemove);
794                 if (ea.isEmpty()) {
795                     return "Cannot copy reference elements whose parent is not copied.";
796                 }
797             }
798         }
799
800         return null;
801     }
802
803     private Collection<Terminal> getTerminals(IElement node) {
804         ArrayList<Terminal> result = new ArrayList<Terminal>();
805         for (TerminalTopology tt : node.getElementClass().getItemsByClass(TerminalTopology.class))
806             tt.getTerminals(node, result);
807         return result;
808     }
809
810     private Collection<Connection> getAllConnections(IElement node, Collection<Connection> result) {
811         IDiagram diagram = node.getDiagram();
812         Topology topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);
813         if (topology == null)
814             return result;
815         for (Terminal t : getTerminals(node))
816             topology.getConnections(node, t, result);
817         return result;
818     }
819
820     /**
821      * Classifies the specified diagram selection elements into categories
822      * appointed by the <code>ElementType</code> enumeration and prunes any
823      * edges from the selection returned assortment whose both ends are not
824      * connected to nodes within the selection.
825      *
826      * @param ea
827      * @return
828      */
829     protected void pruneAssortment(ElementAssortment ea, boolean cut) {
830         // Edges and branch points are never copied as such.
831         // They are always included as parts of copied connections.
832         // Edges can never be transformed or modified in any way as such,
833         // therefore it is safe to do this.
834         ea.clear(ElementType.Edge);
835
836         if (!cut)
837             ea.clear(ElementType.BranchPoint);
838     }
839
840     private Point2D getPastePos(DiagramSelection ds) {
841         MouseInfo mi = mouseUtil.getMouseInfo(0);
842         if (mi == null)
843             mi = mouseInfo;
844
845         if (mi != null) {
846             double xoff = mi.canvasPosition.getX() - ds.getCopyPos().getX();
847             double yoff = mi.canvasPosition.getY() - ds.getCopyPos().getY();
848             if (xoff == pasteOffset.getX() && yoff == pasteOffset.getY()) {
849                 // The mouse has not moved since last paste so let's offset the
850                 // paste down and right.
851                 double counterOffset = getOffsetGridSize() * (++pasteWithoutMovingGhostCounter);
852                 return new Point2D.Double(
853                         mi.canvasPosition.getX() + counterOffset,
854                         mi.canvasPosition.getY() + counterOffset);
855             }
856             pasteWithoutMovingGhostCounter = 0;
857             pasteOffset.setLocation(xoff, yoff);
858             return mi.canvasPosition;
859         } else {
860             //return ds.getCopyPos();
861             Point2D p = ds.getCopyPos();
862             double counterOffset = getOffsetGridSize() * (++pasteWithoutMovingGhostCounter);
863             return new Point2D.Double(
864                     p.getX() + pasteOffset.getX() + counterOffset,
865                     p.getY() + pasteOffset.getY() + counterOffset);
866         }
867     }
868
869     private double getOffsetGridSize() {
870         Double grid = getHint(GridPainter.KEY_GRID_SIZE);
871         return (grid == null || grid == 0) ? 1.0 : grid;
872     }
873
874     /**
875      * @param e
876      * @return
877      */
878     protected static boolean isConnectionOrEdge(IElement e) {
879         ElementClass ec = e.getElementClass();
880         return ec.containsClass(ConnectionHandler.class)|| ec.containsClass(BendsHandler.class);
881     }
882
883     /**
884      * @param e
885      * @return
886      */
887     protected static boolean isMoveable(IElement e) {
888         ElementClass ec = e.getElementClass();
889         return ec.containsClass(Move.class) && ec.containsClass(Transform.class);
890     }
891
892     /**
893      * @param ss
894      * @return <code>null</code> if a point of reference cannot be determined
895      *         for the specified selection.
896      */
897     protected Point2D getCopyStartPos(Set<IElement> ss) {
898 //        MouseInfo mi = mouseUtil.getMouseInfo(0);
899 //        if (mi != null) {
900 //            return (Point2D) mi.canvasPosition.clone();
901 //        }
902
903         // Find bounding rectangle top left corner
904         double mx = Double.MAX_VALUE;
905         double my = Double.MAX_VALUE;
906         for (IElement e : ss) {
907             if (isConnectionOrEdge(e) || !isMoveable(e))
908                 continue;
909
910             //Point2D pos = ElementUtils.getPos(e);
911             Point2D pos = ElementUtils.getAbsolutePos(e);
912             if (pos.getX() < mx)
913                 mx = pos.getX();
914             if (pos.getY() < my)
915                 my = pos.getY();
916         }
917
918         // Find element nearest to the top left corner
919         Point2D nearest = null;
920         double dist = Double.MAX_VALUE;
921         for (IElement e : ss) {
922             if (isConnectionOrEdge(e) || !isMoveable(e))
923                 continue;
924
925             Point2D pos = ElementUtils.getAbsolutePos(e);
926             double dx = pos.getX() - mx;
927             double dy = pos.getY() - my;
928             double d = dx*dx + dy*dy;
929             if (d < dist) {
930                 dist = d;
931                 nearest = pos;
932             }
933         }
934
935         return nearest;
936     }
937
938     private void moveGhostElements(DiagramSelection ds, Point2D pastePos) {
939         Point2D copyPos = ds.getCopyPos();
940         double dx = (pastePos.getX() - copyPos.getX());
941         double dy = (pastePos.getY() - copyPos.getY());
942
943         // Snap delta
944         Point2D snap = CopyPasteUtil.snap(getContext(), new Point2D.Double(dx, dy));
945
946         ghostNode.setTransform(AffineTransform.getTranslateInstance(snap.getX(), snap.getY()));
947         //System.out.println("ghost node: " + ghostNode);
948     }
949
950     protected SingleElementNode ghostNode = null;
951     protected NodeMapper ghostNodeMapper = new NodeMapper();
952
953     @SGInit
954     public void initSG(G2DParentNode parent) {
955         ghostNode = parent.addNode("cut/copy ghost", SingleElementNode.class);
956         ghostNode.setZIndex(COPY_GHOSTING_PAINT_PRIORITY);
957         //ghostNode.setComposite(AlphaComposite.SrcOver.derive(0.40f));
958         ghostNode.setVisible(Boolean.FALSE);
959     }
960
961     @SGCleanup
962     public void cleanupSG() {
963         ghostNode.remove();
964     }
965
966     void clearSG() {
967         ghostNode.removeNodes();
968         ghostNode.setVisible(Boolean.FALSE);
969         ghostNodeMapper.clear();
970     }
971
972     /**
973      * @param selection
974      * @return <code>true</code> if the ghost nodes were hidden and a refresh is
975      *         needed
976      */
977     boolean hideSG(DiagramSelection selection) {
978         if (ghostNode.isVisible()) {
979             // Make sure there's no leftover graphics.
980             ghostNode.removeNodes();
981             ghostNode.setVisible(Boolean.FALSE);
982             return true;
983         }
984         return false;
985     }
986
987     protected void scheduleActivateOwnerPart() {
988         if (site == null)
989             return;
990         SWTUtils.asyncExec(PlatformUI.getWorkbench().getDisplay(), new Runnable() {
991             @Override
992             public void run() {
993                 hasFocus = true;
994                 site.getPage().activate(site.getPart());
995             }
996         });
997     }
998
999     @EventHandler(priority = 0)
1000     public boolean handleMouse(MouseExitEvent e) {
1001         DiagramSelection ds = getProjectSelection();
1002         if (!ds.isEmpty()) {
1003             if (hideSG(ds))
1004                 setDirty();
1005         }
1006
1007         // The part might no longer have focus.
1008         // [Tuukka] commented out to fix Apros #3678
1009         //hasFocus = false;
1010
1011         return false;
1012     }
1013
1014     @EventHandler(priority = 0)
1015     public boolean handleMouse(MouseEnterEvent e) {
1016         DiagramSelection ds = getProjectSelection();
1017         if (!ds.isEmpty()) {
1018             if (site != null) {
1019                 if (inPasteMode(e)) {
1020                     scheduleActivateOwnerPart();
1021                 }
1022             }
1023         }
1024         return false;
1025     }
1026
1027     @EventHandler(priority = 0)
1028     public boolean handleMouse(MouseMovedEvent e) {
1029         DiagramSelection ds = getProjectSelection();
1030         if (!ds.isEmpty()) {
1031             MouseInfo mi = mouseUtil.getMouseInfo(0);
1032             //System.out.println("LAST MOUSE INFO: " + mi);
1033             if (mi != null)
1034                 mouseInfo = mi;
1035
1036             if (inPasteMode(e)) {
1037                 // Make sure that this owner part is active now.
1038                 if (!hasFocus)
1039                     scheduleActivateOwnerPart();
1040
1041                 updateSG(ds);
1042                 setDirty();
1043             } else {
1044                 if (hideSG(ds))
1045                     setDirty();
1046             }
1047         }
1048         return false;
1049     }
1050
1051     void updateSG(DiagramSelection selection) {
1052         MouseInfo mi = mouseUtil.getMouseInfo(0);
1053         if (mi == null)
1054             return;
1055
1056         //ghostNode.setComposite(AlphaComposite.SrcAtop.derive(0.40f));
1057         //ghostNode.setComposite(null);
1058
1059         moveGhostElements(selection, mi.canvasPosition);
1060         if (selection.getSourceCanvas() != getContext()) {
1061             for (IElement e : selection.getOriginalElements()) {
1062                 INode node = e.getHint(ElementHints.KEY_SG_NODE);
1063                 //System.out.println("ghost element: " + e + ", node=" + node);
1064                 if (node instanceof IG2DNode) {
1065                     LocalDelegateNode delegate = getOrCreateNode(ghostNode, ElementUtils.generateNodeId(e),
1066                             LocalDelegateNode.class);
1067                     delegate.setDelegate( (IG2DNode) node );
1068                 }
1069             }
1070         } else {
1071             for (IElement e : selection.getOriginalElements()) {
1072                 //System.out.println("ghost element: " + e);
1073                 INode node = e.getHint(ElementHints.KEY_SG_NODE);
1074                 if (node != null) {
1075                     //System.out.println("ghost node: " + node);
1076                     ghostNodeMapper.add(node);
1077                     String nodeId = ghostNodeMapper.getId(node);
1078                     //System.out.println("ghost node id: " + nodeId);
1079                     LinkNode delegate = getOrCreateNode(ghostNode, ElementUtils.generateNodeId(e), LinkNode.class);
1080                     delegate.setDelegateId( nodeId );
1081                 }
1082             }
1083         }
1084
1085         ghostNode.setVisible(true);
1086     }
1087
1088     private <T extends INode> T getOrCreateNode(ParentNode<?> parentNode, String id, Class<T> clazz) {
1089         INode n = ghostNode.getNode(id);
1090         if (clazz.isInstance(n))
1091             return clazz.cast(n);
1092         ghostNode.removeNode(id);
1093         return ghostNode.addNode(id, clazz);
1094     }
1095
1096     protected boolean hasHighlight() {
1097         return highlightMode != null;
1098     }
1099
1100     protected void removeHighlight() {
1101         if (isRemoved())
1102             return;
1103         assert getContext().getThreadAccess().currentThreadAccess();
1104         if (highlightMode != null) {
1105             if (!highlightMode.isRemoved()) {
1106                 highlightMode.remove();
1107                 setDirty();
1108             }
1109             highlightMode = null;
1110         }
1111     }
1112
1113     private boolean inPasteMode(MouseEvent e) {
1114         return (e.stateMask & MouseEvent.CTRL_MASK) != 0;
1115     }
1116
1117     protected void selectedMessage(DiagramSelection ds) {
1118         int size = ds.getOriginalElements().size();
1119         StringBuilder sb = new StringBuilder();
1120         if (size == 0) {
1121             sb.append("No elements to ");
1122             if (ds.isCut())
1123                 sb.append("cut");
1124             else
1125                 sb.append("copy");
1126         } else {
1127             if (ds.isCut())
1128                 sb.append("Cut ");
1129             else
1130                 sb.append("Copied ");
1131             sb.append(size);
1132             sb.append(" element");
1133             if (size > 1)
1134                 sb.append('s');
1135         }
1136         message(sb.toString());
1137     }
1138
1139     protected void message(final String message) {
1140         if (statusLine == null)
1141             return;
1142         swtExec(new Runnable() {
1143             @Override
1144             public void run() {
1145                 statusLine.setMessage(message);
1146                 statusLine.setErrorMessage(null);
1147             }
1148         });
1149     }
1150
1151     protected void error(final String message) {
1152         if (statusLine == null)
1153             return;
1154         swtExec(new Runnable() {
1155             @Override
1156             public void run() {
1157                 statusLine.setErrorMessage(message);
1158             }
1159         });
1160     }
1161
1162     protected void swtExec(Runnable r) {
1163         ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r);
1164     }
1165
1166     // MONITOR PASTE SUPPORT
1167
1168     private boolean tryPasteMonitors() {
1169         SimanticsClipboard clipboard = Simantics.getClipboard();
1170         for (Set<Representation> content : clipboard.getContents()) {
1171             try {
1172                 final Variable var_ = ClipboardUtils.accept(content, SimanticsKeys.KEY_VARIABLE);
1173                 if (var_ != null) {
1174                     final Resource diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
1175                     final Resource runtime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);
1176                     final Resource elementResource = Simantics.getSession().syncRequest(new Read<Resource>() {
1177                         @Override
1178                         public Resource perform(ReadGraph graph) throws DatabaseException {
1179                             DiagramResource DIA = DiagramResource.getInstance(graph);
1180
1181                             String diagramVariable = graph.getPossibleRelatedValue(runtime, DIA.RuntimeDiagram_HasVariable);
1182                             if (diagramVariable == null)
1183                                 return null;
1184
1185                             Variable diaVar = Variables.getPossibleVariable(graph, diagramVariable);
1186                             if (diaVar == null)
1187                                 return null;
1188
1189                             Variable var = Variables.switchRealization(graph, var_, Variables.getRealization(graph, diaVar));
1190                             if(var == null)
1191                                 return null;
1192
1193                             Variable component = Variables.getChild(graph, diaVar, var);
1194                             if (component == null)
1195                                 return null;
1196
1197                             Resource componentResource = component.getPossibleRepresents(graph);
1198                             if (componentResource == null)
1199                                 return null;
1200
1201                             return graph.getPossibleObject(componentResource, ModelingResources.getInstance(graph).ComponentToElement);
1202                         }
1203                     });
1204
1205                     if (elementResource == null)
1206                         return false;
1207
1208                     final AffineTransform monitorTransform = Simantics.getSession().syncRequest(new Read<AffineTransform>() {
1209                         @Override
1210                         public AffineTransform perform(ReadGraph graph) throws DatabaseException {
1211                             AffineTransform at = null;
1212
1213                             if (graph.isInstanceOf(elementResource, DiagramResource.getInstance(graph).Connection)) {
1214                                 Resource tailNode = ConnectionUtil.getConnectionTailNode(graph, elementResource);
1215                                 if (tailNode != null) {
1216                                     at = DiagramGraphUtil.getAffineTransform(graph, tailNode);
1217                                 }
1218                             }
1219                             if (at == null)
1220                                 at = DiagramGraphUtil.getAffineTransform(graph, elementResource);
1221
1222                             return at;
1223                         }
1224                     });
1225
1226                     MouseInfo mi = mouseUtil.getMouseInfo(0);
1227                     if (mi == null)
1228                         mi = mouseInfo;
1229                     final double dx = mi.canvasPosition.getX() - monitorTransform.getTranslateX();
1230                     final double dy = mi.canvasPosition.getY() - monitorTransform.getTranslateY();
1231
1232                     Simantics.getSession().asyncRequest(new WriteRequest() {
1233
1234                         @Override
1235                         public void perform(WriteGraph graph) throws DatabaseException {
1236                             Layer0 L0 = Layer0.getInstance(graph);
1237                             Layer0X L0X = Layer0X.getInstance(graph);
1238                             DiagramResource DIA = DiagramResource.getInstance(graph);
1239                             G2DResource G2D = G2DResource.getInstance(graph);
1240
1241                             String diagramVariable = graph.getPossibleRelatedValue(runtime, DIA.RuntimeDiagram_HasVariable);
1242                             if (diagramVariable == null)
1243                                 return;
1244
1245                             Variable diaVar = Variables.getPossibleVariable(graph, diagramVariable);
1246                             if (diaVar == null)
1247                                 return;
1248
1249                             Variable var = Variables.switchRealization(graph, var_, Variables.getRealization(graph, diaVar));
1250                             if(var == null)
1251                                 return;
1252
1253                             Variable component = Variables.getChild(graph, diaVar, var);
1254                             if (component == null)
1255                                 return;
1256
1257                             Resource componentResource = component.getPossibleRepresents(graph);
1258                             if (componentResource == null)
1259                                 return;
1260
1261                             String suffix = Variables.getRVI(graph, component, var);
1262
1263                             Resource resource = graph.newResource();
1264                             graph.claim(resource, L0.InstanceOf, null, DIA.Monitor);
1265
1266                             final double scale = monitorScale;
1267
1268                             DiagramGraphUtil.setTransform(graph, resource, new AffineTransform(scale, 0, 0, scale, dx, dy));
1269
1270                             OrderedSetUtils.add(graph, diagramResource, resource);
1271
1272                             // 5.1. Give running name to element and increment the counter attached to the diagram.
1273                             Long l = graph.getPossibleRelatedValue(diagramResource, DIA.HasModCount, Bindings.LONG);
1274                             if (l == null)
1275                                 l = Long.valueOf(0L);
1276                             graph.claimLiteral(resource, L0.HasName, l.toString(), Bindings.STRING);
1277                             graph.claimLiteral(diagramResource, DIA.HasModCount, ++l, Bindings.LONG);
1278
1279                             // 5.2. Make the diagram consist of the new element
1280                             graph.claim(diagramResource, L0.ConsistsOf, resource);
1281
1282                             graph.claim(resource, G2D.HasHorizontalAlignment, null, G2D.Alignment_Leading);
1283                             graph.claimLiteral(resource, DIA.HasDirection, 0.0);
1284
1285                             graph.claim(resource, DIA.HasMonitorComponent, componentResource);
1286                             graph.claimLiteral(resource, DIA.HasMonitorSuffix, suffix);
1287
1288                             Resource model = Variables.getModel(graph, diaVar);
1289                             if (model != null) {
1290                                 Resource template = graph.getPossibleObject(model, DIA.HasDefaultMonitorTemplate);
1291                                 if (template != null) {
1292                                     graph.claim(resource, L0X.ObtainsProperty1, null, template);
1293                                 }
1294                             }
1295
1296                         }
1297                     });
1298                 }
1299             } catch (DatabaseException e1) {
1300                 LOGGER.error("Monitor paste failed", e1);
1301             }
1302         }
1303         return true;
1304     }
1305
1306 }