1 /*******************************************************************************
2 * Copyright (c) 2007, 2020 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.diagramEditor.e4;
14 import java.awt.Point;
15 import java.awt.datatransfer.Transferable;
16 import java.awt.datatransfer.UnsupportedFlavorException;
17 import java.awt.dnd.DnDConstants;
18 import java.awt.dnd.DropTargetDragEvent;
19 import java.awt.dnd.DropTargetDropEvent;
20 import java.awt.dnd.DropTargetEvent;
21 import java.awt.geom.AffineTransform;
22 import java.awt.geom.Point2D;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.List;
28 import org.eclipse.core.runtime.IAdaptable;
29 import org.eclipse.core.runtime.IStatus;
30 import org.eclipse.core.runtime.Status;
31 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
32 import org.eclipse.jface.viewers.IStructuredSelection;
33 import org.eclipse.osgi.util.NLS;
34 import org.simantics.Simantics;
35 import org.simantics.db.ReadGraph;
36 import org.simantics.db.RequestProcessor;
37 import org.simantics.db.Resource;
38 import org.simantics.db.Session;
39 import org.simantics.db.WriteGraph;
40 import org.simantics.db.common.request.PossibleIndexRoot;
41 import org.simantics.db.common.request.UniqueRead;
42 import org.simantics.db.common.request.WriteRequest;
43 import org.simantics.db.common.utils.NameUtils;
44 import org.simantics.db.exception.DatabaseException;
45 import org.simantics.db.layer0.request.IsLinkedTo;
46 import org.simantics.db.layer0.util.Layer0Utils;
47 import org.simantics.db.service.SerialisationSupport;
48 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
49 import org.simantics.diagram.content.Change;
50 import org.simantics.diagram.content.DiagramContentChanges;
51 import org.simantics.diagram.content.DiagramContentTracker;
52 import org.simantics.diagram.stubs.DiagramResource;
53 import org.simantics.diagram.symbollibrary.ISymbolItem;
54 import org.simantics.diagram.synchronization.runtime.DiagramSelectionUpdater;
55 import org.simantics.diagram.ui.DiagramModelHints;
56 import org.simantics.diagram.ui.ElementClassTransferable;
57 import org.simantics.diagram.ui.ElementClassTransferable.ResourceElementClassTransferData;
58 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
59 import org.simantics.g2d.diagram.DiagramHints;
60 import org.simantics.g2d.diagram.DiagramUtils;
61 import org.simantics.g2d.diagram.IDiagram;
62 import org.simantics.g2d.diagram.handler.PickContext;
63 import org.simantics.g2d.diagram.handler.PickRequest;
64 import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
65 import org.simantics.g2d.dnd.DnDHints;
66 import org.simantics.g2d.dnd.ElementClassDragItem;
67 import org.simantics.g2d.dnd.IDnDContext;
68 import org.simantics.g2d.dnd.IDragItem;
69 import org.simantics.g2d.dnd.IDropTargetParticipant;
70 import org.simantics.g2d.element.ElementClass;
71 import org.simantics.g2d.element.ElementHints;
72 import org.simantics.g2d.element.ElementUtils;
73 import org.simantics.g2d.element.IElement;
74 import org.simantics.g2d.participant.TransformUtil;
75 import org.simantics.modeling.ModelingResources;
76 import org.simantics.modeling.ui.Activator;
77 import org.simantics.modeling.ui.diagramEditor.WSEDragItem;
78 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
79 import org.simantics.structural.stubs.StructuralResource2;
80 import org.simantics.ui.dnd.LocalObjectTransfer;
81 import org.simantics.ui.dnd.LocalObjectTransferable;
82 import org.simantics.ui.selection.WorkbenchSelectionElement;
83 import org.simantics.ui.workbench.e4.E4WorkbenchUtils;
84 import org.simantics.utils.logging.TimeLogger;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
89 * This participant populates Elements from ElementClass-resources drops
91 public class PopulateElementDropParticipant extends AbstractDiagramParticipant implements IDropTargetParticipant {
93 private static final Logger LOGGER = LoggerFactory.getLogger(PopulateElementDropParticipant.class);
95 @Dependency PickContext pickContext;
96 @Dependency TransformUtil transformUtil;
98 protected GraphToDiagramSynchronizer synchronizer;
101 public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer) {
102 this(synchronizer, null);
105 public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer, MPart part) {
106 this.synchronizer = synchronizer;
111 public void dragEnter(DropTargetDragEvent dtde, IDnDContext dp) {
115 Transferable tr = dtde.getTransferable();
116 if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) {
117 // System.out.println("joo");
120 // This must be done to have SWT transfer set the source data
122 obj = tr.getTransferData(LocalObjectTransferable.FLAVOR);
123 // System.out.println("GOT FROM AWT: " + obj);
124 } catch (UnsupportedFlavorException e) {
125 // TODO Auto-generated catch block
127 } catch (IOException e) {
128 // TODO Auto-generated catch block
133 if (!(obj instanceof IStructuredSelection)) {
134 obj = LocalObjectTransfer.getTransfer().getObject();
135 // System.out.println("GOT FROM SWT: " + obj);
138 if (obj instanceof IStructuredSelection) {
139 IStructuredSelection sel = (IStructuredSelection) obj;
140 if (!sel.isEmpty()) {
141 for (Object elm : sel.toList()) {
142 if (elm instanceof IAdaptable) {
143 ElementClass ec = (ElementClass) ((IAdaptable) elm).getAdapter(ElementClass.class);
145 dp.add(new ElementClassDragItem(ec));
147 Resource r = (Resource) ((IAdaptable) elm).getAdapter(Resource.class);
149 if (elm instanceof ISymbolItem) {
150 /* FIXME fix this check
151 ISymbolItem symbol = (ISymbolItem) elm;
152 Resource dia = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
154 if (!DiagramTypeUtils.symbolAllowedOnDiagram(synchronizer.getSession(), symbol, dia)) {
155 // Deny dragging of this symbol
158 } catch (DatabaseException e) {
165 String valid = validateDrop(synchronizer.getSession(), r,
166 diagram.<Resource> getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
168 ElementClassDragItem item = new ElementClassDragItem(synchronizer.getNodeClass(r));
169 item.getHintContext().setHint(ElementHints.KEY_TRANSFORM, AffineTransform.getScaleInstance(1, 1));
172 } catch (DatabaseException e) {
173 // Ignore node-class retrieval failures.
174 //System.out.println("error: " + e.getMessage());
181 // Let the default logic handle out how many columns to use.
182 dp.getHints().removeHint(DnDHints.KEY_DND_GRID_COLUMNS);
189 if (tr.isDataFlavorSupported(ElementClassTransferable.FLAVOR)) {
190 ResourceElementClassTransferData dada;
192 dada = (ResourceElementClassTransferData) tr.getTransferData(ElementClassTransferable.FLAVOR);
193 } catch (UnsupportedFlavorException e) {
195 } catch (IOException e) {
198 Session s = synchronizer.getSession();
200 for (String rid : dada.elementClassResourceRandomAccessReference) {
201 SerialisationSupport support = s.getService(SerialisationSupport.class);
202 Resource r = support.getResource(Long.parseLong(rid));
203 dp.add(new ElementClassDragItem(synchronizer.getNodeClass(r)));
205 } catch (DatabaseException e) {
206 throw new RuntimeException(e);
213 private String validateDrop(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget) throws DatabaseException {
214 return processor.syncRequest(new UniqueRead<String>() {
216 public String perform(ReadGraph graph) throws DatabaseException {
217 // System.out.println("dragged resource: " + draggedResource);
218 // System.out.println("drop target resource: " + dropTarget);
219 Resource sourceModel = graph.syncRequest(new PossibleIndexRoot(draggedResource));
220 Resource targetModel = graph.syncRequest(new PossibleIndexRoot(dropTarget));
221 // System.out.println("source model: " + sourceModel);
222 // System.out.println("target model: " + targetModel);
224 // Prevent dragging data from one source model to another.
225 // If source is not part of any model, everything is okay.
226 if (sourceModel != null && !graph.syncRequest(new IsLinkedTo(targetModel, sourceModel))) {
227 // Prevent a symbol instantiating within its own configuration.
228 // NOTE: this doesn't handle transitive cycles.
229 return NLS.bind(Messages.PopulateElementDropParticipant_CannotInstantiate,
230 new Object[] { NameUtils.getSafeName(graph, draggedResource),
231 NameUtils.getURIOrSafeNameInternal(graph, targetModel),
232 NameUtils.getURIOrSafeNameInternal(graph, sourceModel) });
235 // Prevent dragging to published components
236 ModelingResources MOD = ModelingResources.getInstance(graph);
237 StructuralResource2 STR = StructuralResource2.getInstance(graph);
238 Resource configuration = graph.getPossibleObject(dropTarget, MOD.DiagramToComposite);
239 if (configuration != null) {
240 Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
241 if (componentTypeFromDiagram != null) {
242 if (Layer0Utils.isPublished(graph, componentTypeFromDiagram))
243 return Messages.PopulateElementDropParticipant_CannotCreateElementIntoDiagram;
247 // Prevent dragging a symbol of component type into its own configuration.
248 Resource componentTypeFromSymbol = graph.getPossibleObject(draggedResource, MOD.SymbolToComponentType);
249 if (componentTypeFromSymbol != null) {
250 if (configuration != null) {
251 Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
252 if (componentTypeFromDiagram != null
253 && componentTypeFromSymbol.equals(componentTypeFromDiagram)) {
254 return Messages.PopulateElementDropParticipant_CannotInstantiateUserComponent;
265 public void dragExit(DropTargetEvent dte, IDnDContext dp) {
266 // System.out.println("exit");
270 public void dragOver(DropTargetDragEvent dtde, IDnDContext dp) {
271 // System.out.println("over");
274 private IElement tryPick(Point p) {
276 Point2D canvas = transformUtil.controlToCanvas(p, null);
278 assertDependencies();
280 PickRequest req = new PickRequest(canvas);
281 req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
282 List<IElement> picks = new ArrayList<IElement>();
283 pickContext.pick(diagram, req, picks);
285 if(picks.size() == 1) return picks.iterator().next();
292 public void drop(DropTargetDropEvent dtde, final IDnDContext dp) {
293 TimeLogger.resetTimeAndLog(getClass(), "drop"); //$NON-NLS-1$
295 final IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);
299 IElement pick = tryPick(dtde.getLocation());
302 final List<WorkbenchSelectionElement> wses = new ArrayList<WorkbenchSelectionElement>();
304 for(IDragItem i : dp.toArray())
305 if(i instanceof WSEDragItem)
306 wses.add(((WSEDragItem)i).getObject());
308 final Resource element = (Resource)ElementUtils.getData(d, pick);
309 if(element != null && !wses.isEmpty()) {
313 Simantics.getSession().syncRequest(new WriteRequest() {
316 public void perform(WriteGraph graph) throws DatabaseException {
318 DiagramResource DIA = DiagramResource.getInstance(graph);
320 Simantics.invokeSCLWrite(graph, element, DIA.symbolDropHandler, wses);
326 } catch (DatabaseException e) {
327 LOGGER.error("symbolDropHandler invocation failed", e);
337 Runnable creator = () -> {
338 DiagramUtils.mutateDiagram(d, m -> {
339 IDragItem items[] = dp.toArray();
341 for (IDragItem i : items) {
342 if (!(i instanceof ElementClassDragItem))
345 ElementClassDragItem res = (ElementClassDragItem) i;
346 ElementClass ec = res.getElementClass();
348 Point2D pos = dp.getItemPosition(i);
349 // System.out.println(pos);
350 assert (pos != null);
352 IElement element = m.newElement(ec);
353 element.setHints(res.getHintContext().getHints());
355 setupDroppedElement(element, pos);
357 // Remove only the drag items we've processed.
363 selectNewDiagramContentAfter(d, part, creator);
365 getContext().getContentContext().setDirty();
368 protected void selectNewDiagramContentAfter(IDiagram d, MPart activatePart, Runnable diagramModifier) {
370 Resource diagramResource = d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
371 final DiagramContentTracker tracker = diagramResource == null ? null
372 : DiagramContentTracker.start(getContext(), Simantics.getSession(), diagramResource);
374 diagramModifier.run();
376 if (tracker != null) {
377 // Get difference of diagram contents to find out what was added.
378 DiagramContentChanges changes = tracker.update();
379 Set<Resource> addedElements = changes.pick(changes.elements, Change.ADDED);
380 if (!addedElements.isEmpty()) {
381 new DiagramSelectionUpdater(getContext())
382 .setNewSelection(0, addedElements)
385 if (activatePart != null)
386 E4WorkbenchUtils.activatePart(activatePart);
389 } catch (DatabaseException e) {
390 Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.PopulateElementDropParticipant_ActivatorDiagramContentTrackingFailed, e));
394 protected void setupDroppedElement(IElement element, Point2D dropPos) {
395 // This works only for elements without parents.
396 ISnapAdvisor snapAdvisor = getContext().getHintStack().getHint(DiagramHints.SNAP_ADVISOR);
397 if(snapAdvisor != null)
398 snapAdvisor.snap(dropPos);
400 IElement parent = element.getHint(ElementHints.KEY_PARENT_ELEMENT);
401 if (parent != null) {
402 Point2D parentPos = ElementUtils.getPos(parent);
403 Point2D pos = new Point2D.Double(dropPos.getX() - parentPos.getX(), dropPos.getY() - parentPos.getY());
404 ElementUtils.setPos(element, pos);
406 ElementUtils.setPos(element, dropPos);
411 public void dropActionChanged(DropTargetDragEvent dtde, IDnDContext dp) {
412 dtde.acceptDrag(DnDConstants.ACTION_COPY);
416 public int getAllowedOps() {
417 return DnDConstants.ACTION_COPY;
421 public double getPriority() {