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