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