--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.handler.e4;\r
+\r
+import java.awt.Color;\r
+import java.awt.event.KeyEvent;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Set;\r
+\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.e4.ui.model.application.ui.basic.MPart;\r
+import org.eclipse.e4.ui.workbench.modeling.EPartService;\r
+import org.eclipse.e4.ui.workbench.modeling.IPartListener;\r
+import org.eclipse.jface.action.IStatusLineManager;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.ui.PlatformUI;\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.ClipboardUtils;\r
+import org.simantics.db.layer0.util.SimanticsClipboard;\r
+import org.simantics.db.layer0.util.SimanticsClipboard.Representation;\r
+import org.simantics.db.layer0.util.SimanticsKeys;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.diagram.Logger;\r
+import org.simantics.diagram.content.Change;\r
+import org.simantics.diagram.content.ConnectionUtil;\r
+import org.simantics.diagram.content.DiagramContentChanges;\r
+import org.simantics.diagram.content.DiagramContentTracker;\r
+import org.simantics.diagram.handler.CopyPasteStrategy;\r
+import org.simantics.diagram.handler.CopyPasteUtil;\r
+import org.simantics.diagram.handler.DefaultCopyPasteStrategy;\r
+import org.simantics.diagram.handler.DiagramSelection;\r
+import org.simantics.diagram.handler.DiagramSelectionRepresentation;\r
+import org.simantics.diagram.handler.ElementAssortment;\r
+import org.simantics.diagram.handler.ElementObjectAssortment;\r
+import org.simantics.diagram.handler.ElementType;\r
+import org.simantics.diagram.handler.HighlightMode;\r
+import org.simantics.diagram.handler.PasteException;\r
+import org.simantics.diagram.handler.PasteOperation;\r
+import org.simantics.diagram.internal.Activator;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.stubs.G2DResource;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.diagram.synchronization.runtime.DiagramSelectionUpdater;\r
+import org.simantics.diagram.ui.DiagramModelHints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
+import org.simantics.g2d.connection.ConnectionEntity;\r
+import org.simantics.g2d.connection.handler.ConnectionHandler;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.handler.Topology;\r
+import org.simantics.g2d.diagram.handler.Topology.Connection;\r
+import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
+import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;\r
+import org.simantics.g2d.diagram.participant.Selection;\r
+import org.simantics.g2d.element.ElementClass;\r
+import org.simantics.g2d.element.ElementHints;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.BendsHandler;\r
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
+import org.simantics.g2d.element.handler.Move;\r
+import org.simantics.g2d.element.handler.TerminalTopology;\r
+import org.simantics.g2d.element.handler.Transform;\r
+import org.simantics.g2d.elementclass.FlagHandler;\r
+import org.simantics.g2d.participant.GridPainter;\r
+import org.simantics.g2d.participant.MouseUtil;\r
+import org.simantics.g2d.participant.MouseUtil.MouseInfo;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.operation.Layer0X;\r
+import org.simantics.project.IProject;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.ParentNode;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.scenegraph.g2d.IG2DNode;\r
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;\r
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Command;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.scenegraph.g2d.nodes.LinkNode;\r
+import org.simantics.scenegraph.g2d.nodes.LocalDelegateNode;\r
+import org.simantics.scenegraph.g2d.nodes.SingleElementNode;\r
+import org.simantics.scenegraph.utils.NodeMapper;\r
+import org.simantics.utils.datastructures.collections.CollectionUtils;\r
+import org.simantics.utils.datastructures.hints.HintListenerAdapter;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+import org.simantics.utils.datastructures.hints.IHintListener;\r
+import org.simantics.utils.datastructures.hints.IHintObservable;\r
+import org.simantics.utils.logging.TimeLogger;\r
+import org.simantics.utils.threads.SWTThread;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+import org.simantics.utils.ui.ErrorLogger;\r
+import org.simantics.utils.ui.SWTUtils;\r
+\r
+/**\r
+ * CopyPasteHandler is a canvas handler for Commands.CUT, Commands.COPY and\r
+ * Commands.PASTE commands for an IDiagram.\r
+ * \r
+ * <p>\r
+ * The handler attempts to copy/paste the current selection for pointer 0,\r
+ * meaning {@link Selection#SELECTION0}.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * The handler logic follows the specifications at <a\r
+ * href="http://www.simantics.org/wiki/index.php/UC:Copy_Item" >UC:Copy Item</a>\r
+ * and <a href="http://www.simantics.org/wiki/index.php/UC:Cut_Item" >UC:Cut\r
+ * Item</a>.\r
+ * </p>\r
+ * \r
+ * @see Selection current diagram selection source\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * FIXME: translucent ghosting makes rendering REALLY sluggish, add a\r
+ * timer that makes the ghost opaque when the user is interacting and\r
+ * translucent only when still for a while.\r
+ */\r
+public class CopyPasteHandler extends AbstractDiagramParticipant {\r
+\r
+ public static final Key KEY_CUT_SELECTION_FRAME_COLOR = new KeyOf(Color.class, "CUT_SELECTION_FRAME_COLOR");\r
+ public static final Key KEY_CUT_SELECTION_CONTENT_COLOR = new KeyOf(Color.class, "CUT_SELECTION_CONTENT_COLOR");\r
+ public static final Key KEY_COPIED_SELECTION_FRAME_COLOR = new KeyOf(Color.class, "COPY_SELECTION_FRAME_COLOR");\r
+ public static final Key KEY_COPIED_SELECTION_CONTENT_COLOR = new KeyOf(Color.class, "COPY_SELECTION_CONTENT_COLOR");\r
+\r
+ /**\r
+ * A key for storing the current selection within the currently active\r
+ * project for copy/paste implementation.\r
+ */\r
+ private static final Key KEY_DIAGRAM_SELECTION = DiagramSelectionRepresentation.KEY_DIAGRAM_SELECTION;\r
+\r
+ private static final boolean DEBUG = false;\r
+ private static final boolean DEBUG_SELECTION_UPDATE = false;\r
+\r
+ public static final int COPY_GHOSTING_PAINT_PRIORITY = 600;\r
+\r
+ private static final int HIGHLIGHT_PAINT_PRIORITY = 500;\r
+\r
+ @Dependency\r
+ private Selection sel;\r
+ @Dependency\r
+ private MouseUtil mouseUtil;\r
+\r
+ protected final IStatusLineManager statusLine;\r
+ protected final CopyPasteStrategy strategy;\r
+ protected MPart mPart;\r
+ protected MPart listenedMPart;\r
+\r
+ /**\r
+ * Workbench part listener for {@link #listenedMPart} to keep proper track of\r
+ * whether this part is focused or not.\r
+ */\r
+ \r
+ IPartListener partListener2 = new IPartListener() {\r
+ \r
+ @Override\r
+ public void partVisible(MPart part) {\r
+ }\r
+ \r
+ @Override\r
+ public void partHidden(MPart part) {\r
+ // Make sure this listener is removed properly in any case.\r
+ if (listenedMPart != null) {\r
+ mPart.getContext().get(EPartService.class).removePartListener(partListener2);\r
+ listenedMPart = null;\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void partDeactivated(MPart part) {\r
+ if (part == mPart)\r
+ hasFocus = false;\r
+ }\r
+ \r
+ @Override\r
+ public void partBroughtToTop(MPart part) {\r
+ }\r
+ \r
+ @Override\r
+ public void partActivated(MPart part) {\r
+ if (part == mPart)\r
+ hasFocus = true;\r
+ }\r
+ };\r
+\r
+ /**\r
+ * Indicates whether CopyPasteHandler thinks that {@link #mPart} has focus. \r
+ */\r
+ protected boolean hasFocus = false;\r
+\r
+ private AbstractCanvasParticipant highlightMode = null;\r
+ private IProject observedProject = null;\r
+\r
+ /**\r
+ * A counter for how many times pasting has been performed without mouse and\r
+ * ghosting or how many times paste has been performed without moving the\r
+ * mouse on the diagram. This is used to offset the paste position\r
+ * accordingly so that copied elements don't wind up directly on top of each\r
+ * other.\r
+ */\r
+ private int pasteWithoutMovingGhostCounter = 0;\r
+\r
+ /**\r
+ * An offset used when pasting without mouse/ghosting. It forces keyboard\r
+ * pastes to stack up on top of the latest paste performed with\r
+ * mouse/ghosting.\r
+ */\r
+ private final Point2D pasteOffset = new Point2D.Double(0, 0);\r
+\r
+ /**\r
+ * Stores the last MouseInfo for mouse 0 from the time of the previous\r
+ * received mouse event. Used for deciding the paste position.\r
+ * \r
+ * @see #getPastePos(DiagramSelection)\r
+ */\r
+ private MouseInfo mouseInfo;\r
+\r
+ /**\r
+ * Scale to use for pasted diagram monitors from variables.\r
+ */\r
+ private double monitorScale = 0.2;\r
+\r
+ /**\r
+ * For updating the diagram selection after graph changes.\r
+ */\r
+ private DiagramSelectionUpdater selectionUpdater = null;\r
+\r
+ public CopyPasteHandler() {\r
+ this(new DefaultCopyPasteStrategy());\r
+ }\r
+\r
+ public CopyPasteHandler(CopyPasteStrategy strategy) {\r
+ this(strategy, null);\r
+ }\r
+\r
+ public CopyPasteHandler(IStatusLineManager statusLine) {\r
+ this(new DefaultCopyPasteStrategy(), statusLine);\r
+ }\r
+\r
+ public CopyPasteHandler(CopyPasteStrategy strategy, IStatusLineManager statusLine) {\r
+ this.strategy = strategy != null ? strategy : new DefaultCopyPasteStrategy();\r
+ this.statusLine = statusLine;\r
+ }\r
+\r
+ public CopyPasteHandler(CopyPasteStrategy strategy, IStatusLineManager statusLine, double monitorScale) {\r
+ this.strategy = strategy != null ? strategy : new DefaultCopyPasteStrategy();\r
+ this.statusLine = statusLine;\r
+ setMonitorScale(monitorScale);\r
+ }\r
+\r
+ public CopyPasteHandler setMonitorScale(double scale) {\r
+ this.monitorScale = scale;\r
+ return this;\r
+ }\r
+\r
+ public CopyPasteHandler setWorkbenchSite(MPart part) {\r
+ this.mPart = part;\r
+ return this;\r
+ }\r
+\r
+ protected boolean isPasteAllowed() {\r
+ return listenedMPart == null || hasFocus;\r
+ }\r
+\r
+ @Override\r
+ public void addedToContext(ICanvasContext ctx) {\r
+ super.addedToContext(ctx);\r
+ addProjectListener(peekProject());\r
+\r
+ listenedMPart = mPart;\r
+ if (listenedMPart != null) {\r
+ listenedMPart.getContext().get(EPartService.class).addPartListener(partListener2);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void removedFromContext(ICanvasContext ctx) {\r
+ // Remove project selection if its ours to prevent leaking memory.\r
+ DiagramSelection ds = getProjectSelection();\r
+ if (ds.getSourceCanvas() == ctx) {\r
+ removeProjectSelection();\r
+ }\r
+\r
+ if (listenedMPart != null) {\r
+ listenedMPart.getContext().get(EPartService.class).removePartListener(partListener2);\r
+ listenedMPart = null;\r
+ }\r
+\r
+ removeProjectListener();\r
+ super.removedFromContext(ctx);\r
+ }\r
+\r
+ @Override\r
+ protected void onDiagramSet(IDiagram newDiagram, IDiagram oldDiagram) {\r
+ if (oldDiagram != null) {\r
+ if (selectionUpdater != null) {\r
+ selectionUpdater.untrack();\r
+ selectionUpdater = null;\r
+ }\r
+ }\r
+ if (newDiagram != null) {\r
+ selectionUpdater = new DiagramSelectionUpdater(getContext(), newDiagram).track();\r
+ }\r
+ }\r
+\r
+ IHintListener projectDiagramSelectionListener = new HintListenerAdapter() {\r
+ @Override\r
+ public void hintChanged(IHintObservable sender, Key key, Object oldValue, final Object newValue) {\r
+ //System.out.println(this + ": " + sender + ": " + newValue);\r
+ ICanvasContext ctx = getContext();\r
+ if (ctx != null && hasHighlight()) {\r
+ //System.out.println(this + " HAS HIGHLIGHT");\r
+ if (newValue == null || ((DiagramSelection) newValue).getSourceCanvas() != ctx) {\r
+ //System.out.println(this + " REMOVING HIGHLIGHT");\r
+ ctx.getThreadAccess().asyncExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ removeHighlight();\r
+ }\r
+ });\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ private void addProjectListener(IProject observable) {\r
+ if (observable != null) {\r
+ observable.addKeyHintListener(KEY_DIAGRAM_SELECTION, projectDiagramSelectionListener);\r
+ observedProject = observable;\r
+ }\r
+ }\r
+\r
+ private void removeProjectListener() {\r
+ if (observedProject != null) {\r
+ observedProject.removeKeyHintListener(KEY_DIAGRAM_SELECTION, projectDiagramSelectionListener);\r
+ observedProject = null;\r
+ }\r
+ }\r
+\r
+ IProject getProject() {\r
+ return Simantics.getProject();\r
+ }\r
+\r
+ IProject peekProject() {\r
+ return Simantics.peekProject();\r
+ }\r
+\r
+ public DiagramSelection getClipboardDiagramSelection() {\r
+ for (Set<Representation> content : Simantics.getClipboard().getContents()) {\r
+ try {\r
+ DiagramSelection sel = ClipboardUtils.accept(content, DiagramSelectionRepresentation.KEY_DIAGRAM_SELECTION);\r
+ if (sel != null)\r
+ return sel;\r
+ } catch (DatabaseException e) {\r
+ Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to retrieve clipboard content.", e));\r
+ }\r
+ }\r
+ return DiagramSelection.EMPTY;\r
+ }\r
+\r
+ private DiagramSelection getProjectSelection() {\r
+ IProject p = peekProject();\r
+ if (p == null)\r
+ return DiagramSelection.EMPTY;\r
+ DiagramSelection ds = p.getHint(KEY_DIAGRAM_SELECTION);\r
+ return ds != null ? ds : DiagramSelection.EMPTY;\r
+ }\r
+\r
+ void setDiagramSelection(DiagramSelection selection) {\r
+ setProjectSelection(selection);\r
+ strategy.copyToClipboard(selection);\r
+ }\r
+\r
+ void setProjectSelection(DiagramSelection selection) {\r
+ assert selection != null;\r
+ IProject p = getProject();\r
+ if (p == null)\r
+ throw new IllegalStateException("no active project for selection");\r
+ clearSG();\r
+ pasteWithoutMovingGhostCounter = 0;\r
+ pasteOffset.setLocation(0, 0);\r
+ p.setHint(KEY_DIAGRAM_SELECTION, selection);\r
+ }\r
+\r
+ private void removeProjectSelection() {\r
+ setProjectSelection(DiagramSelection.EMPTY);\r
+ removeHighlight();\r
+ clearSG();\r
+ setDirty();\r
+ }\r
+\r
+ /**\r
+ * This DELETE command handler is required to remove any diagram selections\r
+ * when modules are deleted. This will prevent extra highlight painting from\r
+ * being left over in case a module marked for copying is deleted.\r
+ */\r
+ @EventHandler(priority = 100)\r
+ public boolean handleDelete(CommandEvent e) {\r
+ if (e.command.equals( Commands.DELETE) ) {\r
+ if (highlightMode != null) {\r
+ message(null);\r
+ removeProjectSelection();\r
+ return false;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleKey(org.simantics.scenegraph.g2d.events.KeyEvent e) {\r
+ if (e.keyCode == KeyEvent.VK_CONTROL) {\r
+ DiagramSelection ds = getProjectSelection();\r
+ if (!ds.isEmpty()) {\r
+ if (e instanceof KeyPressedEvent) {\r
+ if (ds.isCut())\r
+ message("Move selection");\r
+ else\r
+ message("Paste selection");\r
+ updateSG(ds);\r
+ } else if (e instanceof KeyReleasedEvent) {\r
+ selectedMessage(ds);\r
+ hideSG(ds);\r
+ }\r
+ setDirty();\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleCommand(CommandEvent e) {\r
+ if (e.command.equals( Commands.CANCEL) ) {\r
+ DiagramSelection s = getProjectSelection();\r
+ if (highlightMode != null || !s.isEmpty()) {\r
+ message(null);\r
+ removeProjectSelection();\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ if (e.command.equals( Commands.CUT ) || e.command.equals( Commands.COPY )) {\r
+ boolean ret = initiateCopy( e.command.equals( Commands.CUT ) );\r
+ if (!ret)\r
+ removeProjectSelection();\r
+ return ret;\r
+ }\r
+ // Must have focus in order to paste! If mouse has left the editor, no\r
+ // pastes should be performed.\r
+ if (isPasteAllowed() && e.command.equals( Commands.PASTE )) {\r
+ DiagramSelection ds = getClipboardDiagramSelection();\r
+ if (ds.isEmpty()) {\r
+ return tryPasteMonitors();\r
+ }\r
+ return paste(e.command, ds);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ boolean initiateCopy(boolean cut) {\r
+ //System.out.println("INITIATING COPY");\r
+ int selectionId = 0;\r
+\r
+ Set<IElement> ss = sel.getSelection(selectionId);\r
+ Point2D copyPos = getCopyStartPos(ss);\r
+ if (ss.isEmpty() || copyPos == null) {\r
+ message("Nothing to " + (cut ? "cut" : "copy"));\r
+ return false;\r
+ }\r
+\r
+ // Validate selection, don't initiate copy if selection is invalid.\r
+ ElementAssortment ea = new ElementAssortment(ss);\r
+ String error = fixAssortment(ea, cut);\r
+\r
+ if (error != null) {\r
+ message(error);\r
+ return false;\r
+ }\r
+\r
+ pruneAssortment(ea, cut);\r
+ if (ea.isEmpty()) {\r
+ message("Nothing to " + (cut ? "cut" : "copy"));\r
+ return false;\r
+ }\r
+\r
+ if (DEBUG)\r
+ System.out.println("Start copy with " + ea);\r
+\r
+ // Treat OTHER type elements as disconnected floating graphical elements\r
+ // that are always copied.\r
+\r
+ // Anything with connection parts cannot be copied\r
+ if (!cut && ea.containsAny(CopyPasteUtil.CONNECTION_PARTS)) {\r
+ error("Cannot copy connection segments nor branch points.");\r
+ return false;\r
+ }\r
+// if (ea.contains(CopyPasteUtil.MONITORS)) {\r
+// // TODO: allow copying of monitors, means fixing the component reference relations\r
+// error("Monitor " + (cut ? "cut" : "copy") + " not supported yet.");\r
+// return false;\r
+// }\r
+\r
+ // Pre-validate flag selection cases\r
+ if (ea.contains(CopyPasteUtil.FLAGS)) {\r
+ if (cut) {\r
+ // Allow cutting of single flags or cutting of any amount of\r
+ // flags within a single diagram.\r
+ } else {\r
+// // Deny flag copy if other kinds of elements are selected.\r
+// if (ea.containsAny(NOT_FLAGS)) {\r
+// return false;\r
+// }\r
+ // Only copy flags without correspondence for now.\r
+ if (CopyPasteUtil.isFlagsOnlySelection(ea)) {\r
+ if (!CopyPasteUtil.checkFlagsCorrespondences(ea.flags, false)) {\r
+ error("Cannot copy flag that already has a correspondence.");\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // Selection is valid, go ahead and initiate a copy operation.\r
+ Resource sourceDiagram = diagram.<Resource>getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+ DiagramSelection ds = new DiagramSelection(getContext(), sourceDiagram, ea.getAll(), cut, copyPos);\r
+ setDiagramSelection(ds);\r
+\r
+ removeHighlight();\r
+ highlightMode = new HighlightMode(ds, selectionId, HIGHLIGHT_PAINT_PRIORITY);\r
+ getContext().add(highlightMode);\r
+\r
+ selectedMessage(ds);\r
+\r
+// System.out.println("INITIATED COPY: " + ds);\r
+ return true;\r
+ }\r
+\r
+ public boolean paste(Command command, DiagramSelection ds) {\r
+ if (ds.isEmpty()) {\r
+ message(null);\r
+ return false;\r
+ }\r
+ \r
+ TimeLogger.resetTimeAndLog(getClass(), "paste");\r
+\r
+ ElementObjectAssortment ea = ds.getAssortment();\r
+\r
+ if (DEBUG)\r
+ System.out.println("Initiate paste with " + ea);\r
+\r
+ try {\r
+ if (CopyPasteUtil.isFlagsOnlySelection(ea)) {\r
+ // Do not copy if any of the flags already have a correspondence.\r
+ if (!CopyPasteUtil.onlyFlagsWithoutCorrespondence(Simantics.getSession(), ea))\r
+ return true;\r
+\r
+ if (ds.isCut()) {\r
+ normalPaste(command, ds, ea, true);\r
+ removeHighlight();\r
+ setDiagramSelection(DiagramSelection.EMPTY);\r
+ resetSourceSelection(ds);\r
+ } else {\r
+ normalPaste(command, ds, ea, false);\r
+ // There is no point in leaving the old copy selection hanging\r
+ // around after copying a flag since it is a one shot operation.\r
+ removeHighlight();\r
+ setDiagramSelection(DiagramSelection.EMPTY);\r
+ }\r
+ } else {\r
+ if (ds.isCut()) {\r
+ normalPaste(command, ds, ea, true);\r
+ removeHighlight();\r
+ setDiagramSelection(DiagramSelection.EMPTY);\r
+ resetSourceSelection(ds);\r
+ } else {\r
+ normalPaste(command, ds, ea, false);\r
+\r
+// // This is necessary to keep the ghost diagram properly up-to-date\r
+// // after paste operations.\r
+// setProjectSelection(ds.remutate());\r
+ }\r
+ }\r
+\r
+ message(null);\r
+\r
+ } catch (PasteException e) {\r
+ error( e.getLocalizedMessage() );\r
+ ErrorLogger.defaultLog( new Status(IStatus.INFO, Activator.PLUGIN_ID, "Problem in diagram paste operation, see exception for details.", e) );\r
+ } catch (DatabaseException e) {\r
+ error( e.getLocalizedMessage() );\r
+ ErrorLogger.defaultLog( new Status(IStatus.INFO, Activator.PLUGIN_ID, "Problem in diagram paste operation, see exception for details.", e) );\r
+ }\r
+\r
+ // Clear ghosting\r
+ clearSG();\r
+ setDirty();\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * In cut/paste cases, the source selection should reset (removed) after the\r
+ * paste operation has been performed. This method will reset the source\r
+ * selection of the specified DiagramSelection from the source canvas\r
+ * context. This will work regardless of which diagram/editor the selection\r
+ * originates from.\r
+ * \r
+ * @param ds the source selection to reset\r
+ */\r
+ void resetSourceSelection(DiagramSelection ds) {\r
+ ICanvasContext cc = ds.getSourceCanvas();\r
+ boolean sameDiagram = diagram == ds.getSourceDiagram();\r
+ if (!sameDiagram && cc != null && !cc.isDisposed()) {\r
+ for (Selection sourceSelection : cc.getItemsByClass(Selection.class)) {\r
+ Collection<IElement> empty = Collections.emptySet();\r
+ sourceSelection.setSelection(0, empty);\r
+ }\r
+ }\r
+ }\r
+\r
+ private void normalPaste(Command command, DiagramSelection ds, ElementObjectAssortment ea, boolean cut) throws PasteException {\r
+ final Point2D copyPos = ds.getCopyPos();\r
+ final Point2D pastePos = getPastePos(ds);\r
+\r
+ double dx = pastePos.getX() - copyPos.getX();\r
+ double dy = pastePos.getY() - copyPos.getY();\r
+ final Point2D pasteOffset = new Point2D.Double(dx, dy);\r
+\r
+ try {\r
+ // Get diagram contents before the paste operation\r
+ Resource diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+ final DiagramContentTracker tracker =\r
+ diagramResource == null ?\r
+ null\r
+ : DiagramContentTracker.start(getContext(), Simantics.getSession(), diagramResource);\r
+\r
+ strategy.paste(new PasteOperation(command, getContext(), ds.getSourceDiagram(), diagramResource, diagram, ea, cut, pasteOffset));\r
+\r
+ if (tracker != null) {\r
+ // Get difference of diagram contents to find out what was added.\r
+ DiagramContentChanges changes = tracker.update();\r
+ selectionUpdater.setNewSelection(0, changes.pick(changes.elements, Change.ADDED));\r
+ if (DEBUG_SELECTION_UPDATE)\r
+ System.out.println("stored diagram changes @" + System.currentTimeMillis() + ": " + selectionUpdater.getNewSelection());\r
+ }\r
+\r
+ } catch (DatabaseException e) {\r
+ ErrorLogger.defaultLogError(e);\r
+ }\r
+ }\r
+\r
+ private String fixAssortment(ElementAssortment ea, boolean cut) {\r
+ Topology diagramTopology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
+ List<Connection> conns = new ArrayList<Connection>();\r
+\r
+ // Include flags whether they are selected or not\r
+ for (IElement edge : ea.edges) {\r
+ Connection bc = diagramTopology.getConnection(edge, EdgeEnd.Begin);\r
+ if (bc != null && bc.node != null) {\r
+ if (bc.node.getElementClass().getAtMostOneItemOfClass(FlagHandler.class) != null)\r
+ ea.add(ElementType.Flag, bc.node);\r
+ }\r
+ Connection ec = diagramTopology.getConnection(edge, EdgeEnd.End);\r
+ if (ec != null && ec.node != null) {\r
+ if (ec.node.getElementClass().getAtMostOneItemOfClass(FlagHandler.class) != null)\r
+ ea.add(ElementType.Flag, ec.node);\r
+ }\r
+ }\r
+\r
+ // Include connections for selected flags if we're not potentially\r
+ // making flag continuations.\r
+ if (!CopyPasteUtil.isFlagsOnlySelection(ea)) {\r
+ for (IElement flag : ea.flags) {\r
+ conns.clear();\r
+ diagramTopology.getConnections(flag, ElementUtils.getSingleTerminal(flag), conns);\r
+ for (Connection conn : conns) {\r
+ IElement edge = conn.edge;\r
+ ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+ ea.add(ElementType.Connection, ce.getConnection());\r
+ }\r
+ }\r
+ }\r
+\r
+ // For each selected connection, make sure that all connected elements\r
+ // are in the selection, otherwise don't copy the connection.\r
+ List<IElement> connectionsToRemove = new ArrayList<IElement>(ea.connections.size());\r
+ for (IElement connection : ea.connections) {\r
+ ConnectionHandler ch = connection.getElementClass().getSingleItem(ConnectionHandler.class);\r
+ Collection<Connection> connectors = ch.getTerminalConnections(connection, null);\r
+ boolean allConnectorsSelected = true;\r
+ for (Connection c : connectors) {\r
+ if (!(ea.nodes.contains(c.node) || ea.flags.contains(c.node) || ea.references.contains(c.node))) {\r
+ allConnectorsSelected = false;\r
+ break;\r
+ }\r
+ }\r
+ if (!allConnectorsSelected)\r
+ connectionsToRemove.add(connection);\r
+ }\r
+ ea.removeAll(ElementType.Connection, connectionsToRemove);\r
+\r
+ // Remove external flags whose connection(s) are not included\r
+ List<IElement> flagsToRemove = new ArrayList<IElement>(ea.flags.size());\r
+ for (IElement flag : ea.flags) {\r
+ if (CopyPasteUtil.flagIsExternal(flag)) {\r
+ conns.clear();\r
+ diagramTopology.getConnections(flag, ElementUtils.getSingleTerminal(flag), conns);\r
+ for (Connection conn : conns) {\r
+ IElement edge = conn.edge;\r
+ ConnectionEntity ce = edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+ IElement connection = ce.getConnection();\r
+ if (!ea.connections.contains(connection)) {\r
+ flagsToRemove.add(flag);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ ea.removeAll(ElementType.Flag, flagsToRemove);\r
+\r
+ if (cut) {\r
+ // Issue #1874: Prevent cut/paste for connected components\r
+ // https://www.simulationsite.net/redmine/issues/1874\r
+ // Fail if any of the included nodes has connections to it that are not\r
+ // included in the operation.\r
+ Collection<Connection> connections = new ArrayList<Connection>();\r
+ for (IElement node : CollectionUtils.join(ea.nodes, ea.flags)) {\r
+ connections.clear();\r
+ for (Connection connection : getAllConnections(node, connections)) {\r
+ ConnectionEntity ce = connection.edge.getHint(ElementHints.KEY_CONNECTION_ENTITY);\r
+ IElement conn = ce.getConnection();\r
+ if (ea.connections.contains(conn))\r
+ continue;\r
+\r
+ return "Cannot cut a node without all its connections.";\r
+ }\r
+ }\r
+ }\r
+\r
+ // Remove all reference elements from the assortment whose parent elements are not in it.\r
+ if (!cut) {\r
+ Collection<IElement> referenceElementsToRemove = new ArrayList<IElement>();\r
+ for (IElement ref : ea.references) {\r
+ IElement parent = ref.getHint(ElementHints.KEY_PARENT_ELEMENT);\r
+ if (parent != null) {\r
+ if (!ea.all.contains(parent)) {\r
+ // Cannot copy reference element whose parent is not copied also.\r
+ referenceElementsToRemove.add(ref);\r
+ }\r
+ } else {\r
+ // OK, reference element has no parent. Free to copy/cut in any way.\r
+ }\r
+ }\r
+ if (!referenceElementsToRemove.isEmpty()) {\r
+ ea.removeAll(ElementType.Reference, referenceElementsToRemove);\r
+ if (ea.isEmpty()) {\r
+ return "Cannot copy reference elements whose parent is not copied.";\r
+ }\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ private Collection<Terminal> getTerminals(IElement node) {\r
+ ArrayList<Terminal> result = new ArrayList<Terminal>();\r
+ for (TerminalTopology tt : node.getElementClass().getItemsByClass(TerminalTopology.class))\r
+ tt.getTerminals(node, result);\r
+ return result;\r
+ }\r
+\r
+ private Collection<Connection> getAllConnections(IElement node, Collection<Connection> result) {\r
+ IDiagram diagram = node.getDiagram();\r
+ Topology topology = diagram.getDiagramClass().getAtMostOneItemOfClass(Topology.class);\r
+ if (topology == null)\r
+ return result;\r
+ for (Terminal t : getTerminals(node))\r
+ topology.getConnections(node, t, result);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Classifies the specified diagram selection elements into categories\r
+ * appointed by the <code>ElementType</code> enumeration and prunes any\r
+ * edges from the selection returned assortment whose both ends are not\r
+ * connected to nodes within the selection.\r
+ *\r
+ * @param ea\r
+ * @return\r
+ */\r
+ private void pruneAssortment(ElementAssortment ea, boolean cut) {\r
+ // Edges and branch points are never copied as such.\r
+ // They are always included as parts of copied connections.\r
+ // Edges can never be transformed or modified in any way as such,\r
+ // therefore it is safe to do this.\r
+ ea.clear(ElementType.Edge);\r
+\r
+ if (!cut)\r
+ ea.clear(ElementType.BranchPoint);\r
+ }\r
+\r
+ private Point2D getPastePos(DiagramSelection ds) {\r
+ MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+ if (mi == null)\r
+ mi = mouseInfo;\r
+\r
+ if (mi != null) {\r
+ double xoff = mi.canvasPosition.getX() - ds.getCopyPos().getX();\r
+ double yoff = mi.canvasPosition.getY() - ds.getCopyPos().getY();\r
+ if (xoff == pasteOffset.getX() && yoff == pasteOffset.getY()) {\r
+ // The mouse has not moved since last paste so let's offset the\r
+ // paste down and right.\r
+ double counterOffset = getOffsetGridSize() * (++pasteWithoutMovingGhostCounter);\r
+ return new Point2D.Double(\r
+ mi.canvasPosition.getX() + counterOffset,\r
+ mi.canvasPosition.getY() + counterOffset);\r
+ }\r
+ pasteWithoutMovingGhostCounter = 0;\r
+ pasteOffset.setLocation(xoff, yoff);\r
+ return mi.canvasPosition;\r
+ } else {\r
+ //return ds.getCopyPos();\r
+ Point2D p = ds.getCopyPos();\r
+ double counterOffset = getOffsetGridSize() * (++pasteWithoutMovingGhostCounter);\r
+ return new Point2D.Double(\r
+ p.getX() + pasteOffset.getX() + counterOffset,\r
+ p.getY() + pasteOffset.getY() + counterOffset);\r
+ }\r
+ }\r
+\r
+ private double getOffsetGridSize() {\r
+ Double grid = getHint(GridPainter.KEY_GRID_SIZE);\r
+ return (grid == null || grid == 0) ? 1.0 : grid;\r
+ }\r
+\r
+ /**\r
+ * @param e\r
+ * @return\r
+ */\r
+ private static boolean isConnectionOrEdge(IElement e) {\r
+ ElementClass ec = e.getElementClass();\r
+ return ec.containsClass(ConnectionHandler.class)|| ec.containsClass(BendsHandler.class);\r
+ }\r
+\r
+ /**\r
+ * @param e\r
+ * @return\r
+ */\r
+ private static boolean isMoveable(IElement e) {\r
+ ElementClass ec = e.getElementClass();\r
+ return ec.containsClass(Move.class) && ec.containsClass(Transform.class);\r
+ }\r
+\r
+ /**\r
+ * @param ss\r
+ * @return <code>null</code> if a point of reference cannot be determined\r
+ * for the specified selection.\r
+ */\r
+ private Point2D getCopyStartPos(Set<IElement> ss) {\r
+// MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+// if (mi != null) {\r
+// return (Point2D) mi.canvasPosition.clone();\r
+// }\r
+\r
+ // Find bounding rectangle top left corner\r
+ double mx = Double.MAX_VALUE;\r
+ double my = Double.MAX_VALUE;\r
+ for (IElement e : ss) {\r
+ if (isConnectionOrEdge(e) || !isMoveable(e))\r
+ continue;\r
+\r
+ //Point2D pos = ElementUtils.getPos(e);\r
+ Point2D pos = ElementUtils.getAbsolutePos(e);\r
+ if (pos.getX() < mx)\r
+ mx = pos.getX();\r
+ if (pos.getY() < my)\r
+ my = pos.getY();\r
+ }\r
+\r
+ // Find element nearest to the top left corner\r
+ Point2D nearest = null;\r
+ double dist = Double.MAX_VALUE;\r
+ for (IElement e : ss) {\r
+ if (isConnectionOrEdge(e) || !isMoveable(e))\r
+ continue;\r
+\r
+ Point2D pos = ElementUtils.getAbsolutePos(e);\r
+ double dx = pos.getX() - mx;\r
+ double dy = pos.getY() - my;\r
+ double d = dx*dx + dy*dy;\r
+ if (d < dist) {\r
+ dist = d;\r
+ nearest = pos;\r
+ }\r
+ }\r
+\r
+ return nearest;\r
+ }\r
+\r
+ private void moveGhostElements(DiagramSelection ds, Point2D pastePos) {\r
+ Point2D copyPos = ds.getCopyPos();\r
+ double dx = (pastePos.getX() - copyPos.getX());\r
+ double dy = (pastePos.getY() - copyPos.getY());\r
+\r
+ // Snap delta\r
+ Point2D snap = CopyPasteUtil.snap(getContext(), new Point2D.Double(dx, dy));\r
+\r
+ ghostNode.setTransform(AffineTransform.getTranslateInstance(snap.getX(), snap.getY()));\r
+ //System.out.println("ghost node: " + ghostNode);\r
+ }\r
+\r
+ protected SingleElementNode ghostNode = null;\r
+ protected NodeMapper ghostNodeMapper = new NodeMapper();\r
+\r
+ @SGInit\r
+ public void initSG(G2DParentNode parent) {\r
+ ghostNode = parent.addNode("cut/copy ghost", SingleElementNode.class);\r
+ ghostNode.setZIndex(COPY_GHOSTING_PAINT_PRIORITY);\r
+ //ghostNode.setComposite(AlphaComposite.SrcOver.derive(0.40f));\r
+ ghostNode.setVisible(Boolean.FALSE);\r
+ }\r
+\r
+ @SGCleanup\r
+ public void cleanupSG() {\r
+ ghostNode.remove();\r
+ }\r
+\r
+ void clearSG() {\r
+ ghostNode.removeNodes();\r
+ ghostNode.setVisible(Boolean.FALSE);\r
+ ghostNodeMapper.clear();\r
+ }\r
+\r
+ /**\r
+ * @param selection\r
+ * @return <code>true</code> if the ghost nodes were hidden and a refresh is\r
+ * needed\r
+ */\r
+ boolean hideSG(DiagramSelection selection) {\r
+ if (ghostNode.isVisible()) {\r
+ // Make sure there's no leftover graphics.\r
+ ghostNode.removeNodes();\r
+ ghostNode.setVisible(Boolean.FALSE);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ protected void scheduleActivateOwnerPart() {\r
+ if (mPart == null)\r
+ return;\r
+ SWTUtils.asyncExec(PlatformUI.getWorkbench().getDisplay(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ hasFocus = true;\r
+ mPart.getContext().get(EPartService.class).activate(mPart);\r
+ }\r
+ });\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleMouse(MouseExitEvent e) {\r
+ DiagramSelection ds = getProjectSelection();\r
+ if (!ds.isEmpty()) {\r
+ if (hideSG(ds))\r
+ setDirty();\r
+ }\r
+\r
+ // The part might no longer have focus.\r
+ // [Tuukka] commented out to fix Apros #3678\r
+ //hasFocus = false;\r
+\r
+ return false;\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleMouse(MouseEnterEvent e) {\r
+ DiagramSelection ds = getProjectSelection();\r
+ if (!ds.isEmpty()) {\r
+ if (mPart != null) {\r
+ if (inPasteMode(e)) {\r
+ scheduleActivateOwnerPart();\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleMouse(MouseMovedEvent e) {\r
+ DiagramSelection ds = getProjectSelection();\r
+ if (!ds.isEmpty()) {\r
+ MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+ //System.out.println("LAST MOUSE INFO: " + mi);\r
+ if (mi != null)\r
+ mouseInfo = mi;\r
+\r
+ if (inPasteMode(e)) {\r
+ // Make sure that this owner part is active now.\r
+ if (!hasFocus)\r
+ scheduleActivateOwnerPart();\r
+\r
+ updateSG(ds);\r
+ setDirty();\r
+ } else {\r
+ if (hideSG(ds))\r
+ setDirty();\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ void updateSG(DiagramSelection selection) {\r
+ MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+ if (mi == null)\r
+ return;\r
+\r
+ //ghostNode.setComposite(AlphaComposite.SrcAtop.derive(0.40f));\r
+ //ghostNode.setComposite(null);\r
+\r
+ moveGhostElements(selection, mi.canvasPosition);\r
+ if (selection.getSourceCanvas() != getContext()) {\r
+ for (IElement e : selection.getOriginalElements()) {\r
+ INode node = e.getHint(ElementHints.KEY_SG_NODE);\r
+ //System.out.println("ghost element: " + e + ", node=" + node);\r
+ if (node instanceof IG2DNode) {\r
+ LocalDelegateNode delegate = getOrCreateNode(ghostNode, ElementUtils.generateNodeId(e),\r
+ LocalDelegateNode.class);\r
+ delegate.setDelegate( (IG2DNode) node );\r
+ }\r
+ }\r
+ } else {\r
+ for (IElement e : selection.getOriginalElements()) {\r
+ //System.out.println("ghost element: " + e);\r
+ INode node = e.getHint(ElementHints.KEY_SG_NODE);\r
+ if (node != null) {\r
+ //System.out.println("ghost node: " + node);\r
+ ghostNodeMapper.add(node);\r
+ String nodeId = ghostNodeMapper.getId(node);\r
+ //System.out.println("ghost node id: " + nodeId);\r
+ LinkNode delegate = getOrCreateNode(ghostNode, ElementUtils.generateNodeId(e), LinkNode.class);\r
+ delegate.setDelegateId( nodeId );\r
+ }\r
+ }\r
+ }\r
+\r
+ ghostNode.setVisible(true);\r
+ }\r
+\r
+ private <T extends INode> T getOrCreateNode(ParentNode<?> parentNode, String id, Class<T> clazz) {\r
+ INode n = ghostNode.getNode(id);\r
+ if (clazz.isInstance(n))\r
+ return clazz.cast(n);\r
+ ghostNode.removeNode(id);\r
+ return ghostNode.addNode(id, clazz);\r
+ }\r
+\r
+ private boolean hasHighlight() {\r
+ return highlightMode != null;\r
+ }\r
+\r
+ private void removeHighlight() {\r
+ if (isRemoved())\r
+ return;\r
+ assert getContext().getThreadAccess().currentThreadAccess();\r
+ if (highlightMode != null) {\r
+ if (!highlightMode.isRemoved()) {\r
+ highlightMode.remove();\r
+ setDirty();\r
+ }\r
+ highlightMode = null;\r
+ }\r
+ }\r
+\r
+ private boolean inPasteMode(MouseEvent e) {\r
+ return (e.stateMask & MouseEvent.CTRL_MASK) != 0;\r
+ }\r
+\r
+ void selectedMessage(DiagramSelection ds) {\r
+ int size = ds.getOriginalElements().size();\r
+ StringBuilder sb = new StringBuilder();\r
+ if (size == 0) {\r
+ sb.append("No elements to ");\r
+ if (ds.isCut())\r
+ sb.append("cut");\r
+ else\r
+ sb.append("copy");\r
+ } else {\r
+ if (ds.isCut())\r
+ sb.append("Cut ");\r
+ else\r
+ sb.append("Copied ");\r
+ sb.append(size);\r
+ sb.append(" element");\r
+ if (size > 1)\r
+ sb.append('s');\r
+ }\r
+ message(sb.toString());\r
+ }\r
+\r
+ void message(final String message) {\r
+ if (statusLine == null)\r
+ return;\r
+ swtExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ statusLine.setMessage(message);\r
+ statusLine.setErrorMessage(null);\r
+ }\r
+ });\r
+ }\r
+\r
+ void error(final String message) {\r
+ if (statusLine == null)\r
+ return;\r
+ swtExec(new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ statusLine.setErrorMessage(message);\r
+ }\r
+ });\r
+ }\r
+\r
+ void swtExec(Runnable r) {\r
+ ThreadUtils.asyncExec(SWTThread.getThreadAccess(Display.getDefault()), r);\r
+ }\r
+\r
+ // MONITOR PASTE SUPPORT\r
+\r
+ private boolean tryPasteMonitors() {\r
+ SimanticsClipboard clipboard = Simantics.getClipboard();\r
+ for (Set<Representation> content : clipboard.getContents()) {\r
+ try {\r
+ final Variable var_ = ClipboardUtils.accept(content, SimanticsKeys.KEY_VARIABLE);\r
+ if (var_ != null) {\r
+ final Resource diagramResource = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);\r
+ final Resource runtime = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
+ final Resource elementResource = Simantics.getSession().syncRequest(new Read<Resource>() {\r
+ @Override\r
+ public Resource perform(ReadGraph graph) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+ String diagramVariable = graph.getPossibleRelatedValue(runtime, DIA.RuntimeDiagram_HasVariable);\r
+ if (diagramVariable == null)\r
+ return null;\r
+\r
+ Variable diaVar = Variables.getPossibleVariable(graph, diagramVariable);\r
+ if (diaVar == null)\r
+ return null;\r
+\r
+ Variable var = Variables.switchRealization(graph, var_, Variables.getRealization(graph, diaVar));\r
+ if(var == null)\r
+ return null;\r
+\r
+ Variable component = Variables.getChild(graph, diaVar, var);\r
+ if (component == null)\r
+ return null;\r
+\r
+ Resource componentResource = component.getPossibleRepresents(graph);\r
+ if (componentResource == null)\r
+ return null;\r
+\r
+ return graph.getPossibleObject(componentResource, ModelingResources.getInstance(graph).ComponentToElement);\r
+ }\r
+ });\r
+\r
+ if (elementResource == null)\r
+ return false;\r
+\r
+ final AffineTransform monitorTransform = Simantics.getSession().syncRequest(new Read<AffineTransform>() {\r
+ @Override\r
+ public AffineTransform perform(ReadGraph graph) throws DatabaseException {\r
+ AffineTransform at = null;\r
+\r
+ if (graph.isInstanceOf(elementResource, DiagramResource.getInstance(graph).Connection)) {\r
+ Resource tailNode = ConnectionUtil.getConnectionTailNode(graph, elementResource);\r
+ if (tailNode != null) {\r
+ at = DiagramGraphUtil.getAffineTransform(graph, tailNode);\r
+ }\r
+ }\r
+ if (at == null)\r
+ at = DiagramGraphUtil.getAffineTransform(graph, elementResource);\r
+\r
+ return at;\r
+ }\r
+ });\r
+\r
+ MouseInfo mi = mouseUtil.getMouseInfo(0);\r
+ if (mi == null)\r
+ mi = mouseInfo;\r
+ final double dx = mi.canvasPosition.getX() - monitorTransform.getTranslateX();\r
+ final double dy = mi.canvasPosition.getY() - monitorTransform.getTranslateY();\r
+\r
+ Simantics.getSession().asyncRequest(new WriteRequest() {\r
+\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ Layer0X L0X = Layer0X.getInstance(graph);\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ G2DResource G2D = G2DResource.getInstance(graph);\r
+\r
+ String diagramVariable = graph.getPossibleRelatedValue(runtime, DIA.RuntimeDiagram_HasVariable);\r
+ if (diagramVariable == null)\r
+ return;\r
+\r
+ Variable diaVar = Variables.getPossibleVariable(graph, diagramVariable);\r
+ if (diaVar == null)\r
+ return;\r
+\r
+ Variable var = Variables.switchRealization(graph, var_, Variables.getRealization(graph, diaVar));\r
+ if(var == null)\r
+ return;\r
+\r
+ Variable component = Variables.getChild(graph, diaVar, var);\r
+ if (component == null)\r
+ return;\r
+\r
+ Resource componentResource = component.getPossibleRepresents(graph);\r
+ if (componentResource == null)\r
+ return;\r
+\r
+ String suffix = Variables.getRVI(graph, component, var);\r
+\r
+ Resource resource = graph.newResource();\r
+ graph.claim(resource, L0.InstanceOf, null, DIA.Monitor);\r
+\r
+ final double scale = monitorScale;\r
+\r
+ DiagramGraphUtil.setTransform(graph, resource, new AffineTransform(scale, 0, 0, scale, dx, dy));\r
+\r
+ OrderedSetUtils.add(graph, diagramResource, resource);\r
+\r
+ // 5.1. Give running name to element and increment the counter attached to the diagram.\r
+ Long l = graph.getPossibleRelatedValue(diagramResource, DIA.HasModCount, Bindings.LONG);\r
+ if (l == null)\r
+ l = Long.valueOf(0L);\r
+ graph.claimLiteral(resource, L0.HasName, l.toString(), Bindings.STRING);\r
+ graph.claimLiteral(diagramResource, DIA.HasModCount, ++l, Bindings.LONG);\r
+\r
+ // 5.2. Make the diagram consist of the new element\r
+ graph.claim(diagramResource, L0.ConsistsOf, resource);\r
+\r
+ graph.claim(resource, G2D.HasHorizontalAlignment, null, G2D.Alignment_Leading);\r
+ graph.claimLiteral(resource, DIA.HasDirection, 0.0);\r
+\r
+ graph.claim(resource, DIA.HasMonitorComponent, componentResource);\r
+ graph.claimLiteral(resource, DIA.HasMonitorSuffix, suffix);\r
+\r
+ Resource model = Variables.getModel(graph, diaVar);\r
+ if (model != null) {\r
+ Resource template = graph.getPossibleObject(model, DIA.HasDefaultMonitorTemplate);\r
+ if (template != null) {\r
+ graph.claim(resource, L0X.ObtainsProperty1, null, template);\r
+ }\r
+ }\r
+ \r
+ }\r
+ });\r
+ }\r
+ } catch (DatabaseException e1) {\r
+ Logger.defaultLogError(e1);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+}\r