1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.modeling.ui.diagramEditor;
\r
14 import java.awt.Point;
\r
15 import java.awt.datatransfer.Transferable;
\r
16 import java.awt.datatransfer.UnsupportedFlavorException;
\r
17 import java.awt.dnd.DnDConstants;
\r
18 import java.awt.dnd.DropTargetDragEvent;
\r
19 import java.awt.dnd.DropTargetDropEvent;
\r
20 import java.awt.dnd.DropTargetEvent;
\r
21 import java.awt.geom.AffineTransform;
\r
22 import java.awt.geom.Point2D;
\r
23 import java.io.IOException;
\r
24 import java.util.ArrayList;
\r
25 import java.util.List;
\r
26 import java.util.Set;
\r
28 import org.eclipse.core.runtime.IAdaptable;
\r
29 import org.eclipse.core.runtime.IStatus;
\r
30 import org.eclipse.core.runtime.Status;
\r
31 import org.eclipse.jface.viewers.IStructuredSelection;
\r
32 import org.eclipse.ui.IWorkbenchPartSite;
\r
33 import org.simantics.Simantics;
\r
34 import org.simantics.db.ReadGraph;
\r
35 import org.simantics.db.RequestProcessor;
\r
36 import org.simantics.db.Resource;
\r
37 import org.simantics.db.Session;
\r
38 import org.simantics.db.WriteGraph;
\r
39 import org.simantics.db.common.request.PossibleIndexRoot;
\r
40 import org.simantics.db.common.request.ResourceRead2;
\r
41 import org.simantics.db.common.request.UniqueRead;
\r
42 import org.simantics.db.common.request.WriteRequest;
\r
43 import org.simantics.db.common.utils.NameUtils;
\r
44 import org.simantics.db.exception.DatabaseException;
\r
45 import org.simantics.db.layer0.request.IsLinkedTo;
\r
46 import org.simantics.db.layer0.util.Layer0Utils;
\r
47 import org.simantics.db.layer0.variable.Variable;
\r
48 import org.simantics.db.layer0.variable.Variables;
\r
49 import org.simantics.db.service.SerialisationSupport;
\r
50 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
\r
51 import org.simantics.diagram.content.Change;
\r
52 import org.simantics.diagram.content.DiagramContentChanges;
\r
53 import org.simantics.diagram.content.DiagramContentTracker;
\r
54 import org.simantics.diagram.stubs.DiagramResource;
\r
55 import org.simantics.diagram.symbollibrary.ISymbolItem;
\r
56 import org.simantics.diagram.synchronization.runtime.DiagramSelectionUpdater;
\r
57 import org.simantics.diagram.ui.DiagramModelHints;
\r
58 import org.simantics.diagram.ui.ElementClassTransferable;
\r
59 import org.simantics.diagram.ui.ElementClassTransferable.ResourceElementClassTransferData;
\r
60 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
\r
61 import org.simantics.g2d.diagram.DiagramHints;
\r
62 import org.simantics.g2d.diagram.DiagramUtils;
\r
63 import org.simantics.g2d.diagram.IDiagram;
\r
64 import org.simantics.g2d.diagram.handler.PickContext;
\r
65 import org.simantics.g2d.diagram.handler.PickRequest;
\r
66 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
\r
67 import org.simantics.g2d.dnd.DnDHints;
\r
68 import org.simantics.g2d.dnd.ElementClassDragItem;
\r
69 import org.simantics.g2d.dnd.IDnDContext;
\r
70 import org.simantics.g2d.dnd.IDragItem;
\r
71 import org.simantics.g2d.dnd.IDropTargetParticipant;
\r
72 import org.simantics.g2d.element.ElementClass;
\r
73 import org.simantics.g2d.element.ElementHints;
\r
74 import org.simantics.g2d.element.ElementUtils;
\r
75 import org.simantics.g2d.element.IElement;
\r
76 import org.simantics.g2d.participant.TransformUtil;
\r
77 import org.simantics.modeling.ModelingResources;
\r
78 import org.simantics.modeling.ui.Activator;
\r
79 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
\r
80 import org.simantics.structural.stubs.StructuralResource2;
\r
81 import org.simantics.ui.dnd.LocalObjectTransfer;
\r
82 import org.simantics.ui.dnd.LocalObjectTransferable;
\r
83 import org.simantics.ui.selection.WorkbenchSelectionElement;
\r
84 import org.simantics.utils.logging.TimeLogger;
\r
85 import org.simantics.utils.ui.workbench.WorkbenchUtils;
\r
88 * This participant populates Elements from ElementClass-resources drops
\r
90 public class PopulateElementDropParticipant extends AbstractDiagramParticipant implements IDropTargetParticipant {
\r
92 @Dependency PickContext pickContext;
\r
93 @Dependency TransformUtil transformUtil;
\r
95 protected GraphToDiagramSynchronizer synchronizer;
\r
96 protected IWorkbenchPartSite partSite;
\r
98 public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer) {
\r
99 this(synchronizer, null);
\r
102 public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer, IWorkbenchPartSite partSite) {
\r
103 this.synchronizer = synchronizer;
\r
104 this.partSite = partSite;
\r
108 public void dragEnter(DropTargetDragEvent dtde, IDnDContext dp) {
\r
109 if (diagram == null)
\r
112 Transferable tr = dtde.getTransferable();
\r
113 if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) {
\r
114 // System.out.println("joo");
\r
117 // This must be done to have SWT transfer set the source data
\r
119 obj = tr.getTransferData(LocalObjectTransferable.FLAVOR);
\r
120 // System.out.println("GOT FROM AWT: " + obj);
\r
121 } catch (UnsupportedFlavorException e) {
\r
122 // TODO Auto-generated catch block
\r
123 e.printStackTrace();
\r
124 } catch (IOException e) {
\r
125 // TODO Auto-generated catch block
\r
126 e.printStackTrace();
\r
130 if (!(obj instanceof IStructuredSelection)) {
\r
131 obj = LocalObjectTransfer.getTransfer().getObject();
\r
132 // System.out.println("GOT FROM SWT: " + obj);
\r
135 if (obj instanceof IStructuredSelection) {
\r
136 IStructuredSelection sel = (IStructuredSelection) obj;
\r
137 if (!sel.isEmpty()) {
\r
138 for (Object elm : sel.toList()) {
\r
139 if (elm instanceof IAdaptable) {
\r
140 ElementClass ec = (ElementClass) ((IAdaptable) elm).getAdapter(ElementClass.class);
\r
142 dp.add(new ElementClassDragItem(ec));
\r
144 Resource r = (Resource) ((IAdaptable) elm).getAdapter(Resource.class);
\r
146 if (elm instanceof ISymbolItem) {
\r
147 /* FIXME fix this check
\r
148 ISymbolItem symbol = (ISymbolItem) elm;
\r
149 Resource dia = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
151 if (!DiagramTypeUtils.symbolAllowedOnDiagram(synchronizer.getSession(), symbol, dia)) {
\r
152 // Deny dragging of this symbol
\r
155 } catch (DatabaseException e) {
\r
156 e.printStackTrace();
\r
162 String valid = validateDrop(synchronizer.getSession(), r,
\r
163 diagram.<Resource> getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
\r
164 if (valid == null) {
\r
165 ElementClassDragItem item = new ElementClassDragItem(synchronizer.getNodeClass(r));
\r
166 item.getHintContext().setHint(ElementHints.KEY_TRANSFORM, AffineTransform.getScaleInstance(1, 1));
\r
169 } catch (DatabaseException e) {
\r
170 // Ignore node-class retrieval failures.
\r
171 //System.out.println("error: " + e.getMessage());
\r
178 // Let the default logic handle out how many columns to use.
\r
179 dp.getHints().removeHint(DnDHints.KEY_DND_GRID_COLUMNS);
\r
186 if (tr.isDataFlavorSupported(ElementClassTransferable.FLAVOR)) {
\r
187 ResourceElementClassTransferData dada;
\r
189 dada = (ResourceElementClassTransferData) tr.getTransferData(ElementClassTransferable.FLAVOR);
\r
190 } catch (UnsupportedFlavorException e) {
\r
191 throw new Error(e);
\r
192 } catch (IOException e) {
\r
193 throw new Error(e);
\r
195 Session s = synchronizer.getSession();
\r
197 for (String rid : dada.elementClassResourceRandomAccessReference) {
\r
198 SerialisationSupport support = s.getService(SerialisationSupport.class);
\r
199 Resource r = support.getResource(Long.parseLong(rid));
\r
200 dp.add(new ElementClassDragItem(synchronizer.getNodeClass(r)));
\r
202 } catch (DatabaseException e) {
\r
203 throw new RuntimeException(e);
\r
210 private String validateDrop(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget) throws DatabaseException {
\r
211 return processor.syncRequest(new UniqueRead<String>() {
\r
213 public String perform(ReadGraph graph) throws DatabaseException {
\r
214 // System.out.println("dragged resource: " + draggedResource);
\r
215 // System.out.println("drop target resource: " + dropTarget);
\r
216 Resource sourceModel = graph.syncRequest(new PossibleIndexRoot(draggedResource));
\r
217 Resource targetModel = graph.syncRequest(new PossibleIndexRoot(dropTarget));
\r
218 // System.out.println("source model: " + sourceModel);
\r
219 // System.out.println("target model: " + targetModel);
\r
221 // Prevent dragging data from one source model to another.
\r
222 // If source is not part of any model, everything is okay.
\r
223 if (sourceModel != null && !graph.syncRequest(new IsLinkedTo(targetModel, sourceModel))) {
\r
224 // Prevent a symbol instantiating within its own configuration.
\r
225 // NOTE: this doesn't handle transitive cycles.
\r
226 return "Cannot instantiate " + NameUtils.getSafeName(graph, draggedResource) + " into model "
\r
227 + NameUtils.getURIOrSafeNameInternal(graph, targetModel) + ". The source namespace ("
\r
228 + NameUtils.getURIOrSafeNameInternal(graph, sourceModel) + ") is not linked to the target model.";
\r
231 // Prevent dragging to published components
\r
232 ModelingResources MOD = ModelingResources.getInstance(graph);
\r
233 StructuralResource2 STR = StructuralResource2.getInstance(graph);
\r
234 Resource configuration = graph.getPossibleObject(dropTarget, MOD.DiagramToComposite);
\r
235 if (configuration != null) {
\r
236 Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
\r
237 if(componentTypeFromDiagram != null) {
\r
238 if(Layer0Utils.isPublished(graph, componentTypeFromDiagram))
\r
239 return "Cannot create elements into a diagram that belongs to a published user component.";
\r
243 // Prevent dragging a symbol of component type into its own configuration.
\r
244 Resource componentTypeFromSymbol = graph.getPossibleObject(draggedResource, MOD.SymbolToComponentType);
\r
245 if (componentTypeFromSymbol != null) {
\r
246 if (configuration != null) {
\r
247 Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
\r
248 if (componentTypeFromDiagram != null && componentTypeFromSymbol.equals(componentTypeFromDiagram)) {
\r
249 return "Cannot instantiate user component within its own configuration.";
\r
260 public void dragExit(DropTargetEvent dte, IDnDContext dp) {
\r
261 // System.out.println("exit");
\r
265 public void dragOver(DropTargetDragEvent dtde, IDnDContext dp) {
\r
266 // System.out.println("over");
\r
269 private IElement tryPick(Point p) {
\r
271 Point2D canvas = transformUtil.controlToCanvas(p, null);
\r
273 assertDependencies();
\r
275 PickRequest req = new PickRequest(canvas);
\r
276 req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
\r
277 List<IElement> picks = new ArrayList<IElement>();
\r
278 pickContext.pick(diagram, req, picks);
\r
280 if(picks.size() == 1) return picks.iterator().next();
\r
287 public void drop(DropTargetDropEvent dtde, final IDnDContext dp) {
\r
288 TimeLogger.resetTimeAndLog(getClass(), "drop");
\r
290 final IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);
\r
294 IElement pick = tryPick(dtde.getLocation());
\r
297 final List<WorkbenchSelectionElement> wses = new ArrayList<WorkbenchSelectionElement>();
\r
299 for(IDragItem i : dp.toArray())
\r
300 if(i instanceof WSEDragItem)
\r
301 wses.add(((WSEDragItem)i).getObject());
\r
303 final Resource element = (Resource)ElementUtils.getData(d, pick);
\r
304 if(element != null && !wses.isEmpty()) {
\r
307 Session db = Simantics.getSession();
\r
308 DiagramResource DIA = DiagramResource.getInstance(db);
\r
309 Variable function = db.syncRequest(new PossibleVariableProperty(element, DIA.symbolDropHandler));
\r
310 if (function != null) {
\r
311 db.syncRequest(new WriteRequest() {
\r
313 public void perform(WriteGraph graph) throws DatabaseException {
\r
314 Simantics.invokeSCLWrite(graph, function, wses);
\r
319 } catch (DatabaseException e) {
\r
320 Activator.getDefault().getLog()
\r
321 .log(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
\r
322 "Invocation to custom symbolDropHandler for element "
\r
323 + element + " failed.",
\r
333 Runnable creator = new Runnable() {
\r
334 public void run() {
\r
335 DiagramUtils.mutateDiagram(d, m -> {
\r
336 IDragItem items[] = dp.toArray();
\r
338 for (IDragItem i : items) {
\r
339 if (!(i instanceof ElementClassDragItem))
\r
342 ElementClassDragItem res = (ElementClassDragItem) i;
\r
343 ElementClass ec = res.getElementClass();
\r
345 Point2D pos = dp.getItemPosition(i);
\r
346 // System.out.println(pos);
\r
347 assert (pos != null);
\r
349 IElement element = m.newElement(ec);
\r
350 element.setHints(res.getHintContext().getHints());
\r
352 setupDroppedElement(element, pos);
\r
354 // Remove only the drag items we've processed.
\r
361 selectNewDiagramContentAfter(d, partSite, creator);
\r
363 getContext().getContentContext().setDirty();
\r
366 private static class PossibleVariableProperty extends ResourceRead2<Variable> {
\r
368 public PossibleVariableProperty(Resource entity, Resource property) {
\r
369 super(entity, property);
\r
373 public Variable perform(ReadGraph graph) throws DatabaseException {
\r
374 return Variables.tryGetProperty(graph, resource, resource2);
\r
379 protected void selectNewDiagramContentAfter(IDiagram d, IWorkbenchPartSite activateSite, Runnable diagramModifier) {
\r
381 Resource diagramResource = d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
\r
382 final DiagramContentTracker tracker = diagramResource == null ? null
\r
383 : DiagramContentTracker.start(getContext(), Simantics.getSession(), diagramResource);
\r
385 diagramModifier.run();
\r
387 if (tracker != null) {
\r
388 // Get difference of diagram contents to find out what was added.
\r
389 DiagramContentChanges changes = tracker.update();
\r
390 Set<Resource> addedElements = changes.pick(changes.elements, Change.ADDED);
\r
391 if (!addedElements.isEmpty()) {
\r
392 new DiagramSelectionUpdater(getContext())
\r
393 .setNewSelection(0, addedElements)
\r
396 if (activateSite != null)
\r
397 WorkbenchUtils.activatePart(activateSite);
\r
400 } catch (DatabaseException e) {
\r
401 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Diagram content change tracking failed.", e));
\r
405 protected void setupDroppedElement(IElement element, Point2D dropPos) {
\r
406 // This works only for elements without parents.
\r
407 ISnapAdvisor snapAdvisor = getContext().getHintStack().getHint(DiagramHints.SNAP_ADVISOR);
\r
408 if(snapAdvisor != null)
\r
409 snapAdvisor.snap(dropPos);
\r
411 IElement parent = element.getHint(ElementHints.KEY_PARENT_ELEMENT);
\r
412 if (parent != null) {
\r
413 Point2D parentPos = ElementUtils.getPos(parent);
\r
414 Point2D pos = new Point2D.Double(dropPos.getX() - parentPos.getX(), dropPos.getY() - parentPos.getY());
\r
415 ElementUtils.setPos(element, pos);
\r
417 ElementUtils.setPos(element, dropPos);
\r
422 public void dropActionChanged(DropTargetDragEvent dtde, IDnDContext dp) {
\r
423 dtde.acceptDrag(DnDConstants.ACTION_COPY);
\r
427 public int getAllowedOps() {
\r
428 return DnDConstants.ACTION_COPY;
\r
432 public double getPriority() {
\r