]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/e4/PopulateElementDropParticipant.java
51f5d82698201b412e8dde2437e8cc13ff2287ac
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagramEditor / e4 / PopulateElementDropParticipant.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.modeling.ui.diagramEditor.e4;
13
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;
26 import java.util.Set;
27
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.simantics.Simantics;
34 import org.simantics.db.ReadGraph;
35 import org.simantics.db.RequestProcessor;
36 import org.simantics.db.Resource;
37 import org.simantics.db.Session;
38 import org.simantics.db.WriteGraph;
39 import org.simantics.db.common.request.PossibleIndexRoot;
40 import org.simantics.db.common.request.UniqueRead;
41 import org.simantics.db.common.request.WriteRequest;
42 import org.simantics.db.common.utils.NameUtils;
43 import org.simantics.db.exception.DatabaseException;
44 import org.simantics.db.layer0.request.IsLinkedTo;
45 import org.simantics.db.layer0.util.Layer0Utils;
46 import org.simantics.db.service.SerialisationSupport;
47 import org.simantics.diagram.Logger;
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
86 /**
87  * This participant populates Elements from ElementClass-resources drops
88  */
89 public class PopulateElementDropParticipant extends AbstractDiagramParticipant implements IDropTargetParticipant {
90
91     @Dependency PickContext pickContext;
92     @Dependency TransformUtil transformUtil;
93         
94     protected GraphToDiagramSynchronizer synchronizer;
95     protected MPart part;
96
97     public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer) {
98         this(synchronizer, null);
99     }
100
101     public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer, MPart part) {
102         this.synchronizer = synchronizer;
103         this.part = part;
104     }
105
106     @Override
107     public void dragEnter(DropTargetDragEvent dtde, IDnDContext dp) {
108         if (diagram == null)
109             return;
110
111         Transferable tr = dtde.getTransferable();
112         if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) {
113             // System.out.println("joo");
114             Object obj = null;
115
116             // This must be done to have SWT transfer set the source data
117             try {
118                 obj = tr.getTransferData(LocalObjectTransferable.FLAVOR);
119                 // System.out.println("GOT FROM AWT: " + obj);
120             } catch (UnsupportedFlavorException e) {
121                 // TODO Auto-generated catch block
122                 e.printStackTrace();
123             } catch (IOException e) {
124                 // TODO Auto-generated catch block
125                 e.printStackTrace();
126             }
127
128             // Check SWT
129             if (!(obj instanceof IStructuredSelection)) {
130                 obj = LocalObjectTransfer.getTransfer().getObject();
131                 // System.out.println("GOT FROM SWT: " + obj);
132             }
133
134             if (obj instanceof IStructuredSelection) {
135                 IStructuredSelection sel = (IStructuredSelection) obj;
136                 if (!sel.isEmpty()) {
137                     for (Object elm : sel.toList()) {
138                         if (elm instanceof IAdaptable) {
139                             ElementClass ec = (ElementClass) ((IAdaptable) elm).getAdapter(ElementClass.class);
140                             if (ec != null) {
141                                 dp.add(new ElementClassDragItem(ec));
142                             } else {
143                                 Resource r = (Resource) ((IAdaptable) elm).getAdapter(Resource.class);
144                                 if (r != null) {
145                                     if (elm instanceof ISymbolItem) {
146                                         /* FIXME fix this check 
147                                         ISymbolItem symbol = (ISymbolItem) elm;
148                                         Resource dia = diagram.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
149                                         try {
150                                             if (!DiagramTypeUtils.symbolAllowedOnDiagram(synchronizer.getSession(), symbol, dia)) {
151                                                 // Deny dragging of this symbol
152                                                 continue;
153                                             }
154                                         } catch (DatabaseException e) {
155                                             e.printStackTrace();
156                                             continue;
157                                         }*/
158                                     }
159
160                                     try {
161                                         String valid = validateDrop(synchronizer.getSession(), r,
162                                                 diagram.<Resource> getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE));
163                                         if (valid == null) {
164                                             ElementClassDragItem item = new ElementClassDragItem(synchronizer.getNodeClass(r));
165                                             item.getHintContext().setHint(ElementHints.KEY_TRANSFORM, AffineTransform.getScaleInstance(1, 1));
166                                             dp.add(item);
167                                         }
168                                     } catch (DatabaseException e) {
169                                         // Ignore node-class retrieval failures.
170                                         //System.out.println("error: " + e.getMessage());
171                                     }
172                                 }
173                             }
174                         }
175                     }
176
177                     // Let the default logic handle out how many columns to use.
178                     dp.getHints().removeHint(DnDHints.KEY_DND_GRID_COLUMNS);
179                 }
180             }
181
182             return;
183         }
184
185         if (tr.isDataFlavorSupported(ElementClassTransferable.FLAVOR)) {
186             ResourceElementClassTransferData dada;
187             try {
188                 dada = (ResourceElementClassTransferData) tr.getTransferData(ElementClassTransferable.FLAVOR);
189             } catch (UnsupportedFlavorException e) {
190                 throw new Error(e);
191             } catch (IOException e) {
192                 throw new Error(e);
193             }
194             Session s = synchronizer.getSession();
195             try {
196                 for (String rid : dada.elementClassResourceRandomAccessReference) {
197                     SerialisationSupport support = s.getService(SerialisationSupport.class);
198                     Resource r = support.getResource(Long.parseLong(rid));
199                     dp.add(new ElementClassDragItem(synchronizer.getNodeClass(r)));
200                 }
201             } catch (DatabaseException e) {
202                 throw new RuntimeException(e);
203             }
204
205             return;
206         }
207     }
208
209     private String validateDrop(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget) throws DatabaseException {
210         return processor.syncRequest(new UniqueRead<String>() {
211             @Override
212             public String perform(ReadGraph graph) throws DatabaseException {
213 //                System.out.println("dragged resource: " + draggedResource);
214 //                System.out.println("drop target resource: " + dropTarget);
215                 Resource sourceModel = graph.syncRequest(new PossibleIndexRoot(draggedResource));
216                 Resource targetModel = graph.syncRequest(new PossibleIndexRoot(dropTarget));
217 //                System.out.println("source model: " + sourceModel);
218 //                System.out.println("target model: " + targetModel);
219
220                 // Prevent dragging data from one source model to another.
221                 // If source is not part of any model, everything is okay.
222                 if (sourceModel != null && !graph.syncRequest(new IsLinkedTo(targetModel, sourceModel))) {
223                     // Prevent a symbol instantiating within its own configuration.
224                     // NOTE: this doesn't handle transitive cycles.
225                     return "Cannot instantiate " + NameUtils.getSafeName(graph, draggedResource) + " into model "
226                             + NameUtils.getURIOrSafeNameInternal(graph, targetModel) + ". The source namespace ("
227                             + NameUtils.getURIOrSafeNameInternal(graph, sourceModel) + ") is not linked to the target model.";
228                 }
229                 
230                 // Prevent dragging to published components
231                 ModelingResources MOD = ModelingResources.getInstance(graph);
232                 StructuralResource2 STR = StructuralResource2.getInstance(graph);
233                 Resource configuration = graph.getPossibleObject(dropTarget, MOD.DiagramToComposite);
234                 if (configuration != null) {
235                     Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
236                     if(componentTypeFromDiagram != null) {
237                             if(Layer0Utils.isPublished(graph, componentTypeFromDiagram))
238                                 return "Cannot create elements into a diagram that belongs to a published user component.";
239                     }
240                 }
241
242                 // Prevent dragging a symbol of component type into its own configuration.
243                 Resource componentTypeFromSymbol = graph.getPossibleObject(draggedResource, MOD.SymbolToComponentType);
244                 if (componentTypeFromSymbol != null) {
245                     if (configuration != null) {
246                         Resource componentTypeFromDiagram = graph.getPossibleObject(configuration, STR.Defines);
247                         if (componentTypeFromDiagram != null && componentTypeFromSymbol.equals(componentTypeFromDiagram)) {
248                             return "Cannot instantiate user component within its own configuration.";
249                         }
250                     }
251                 }
252
253                 return null;
254             }
255         });
256     }
257
258     @Override
259     public void dragExit(DropTargetEvent dte, IDnDContext dp) {
260         // System.out.println("exit");
261     }
262
263     @Override
264     public void dragOver(DropTargetDragEvent dtde, IDnDContext dp) {
265         // System.out.println("over");
266     }
267
268     private IElement tryPick(Point p) {
269
270         Point2D canvas = transformUtil.controlToCanvas(p, null);
271         
272         assertDependencies();
273
274         PickRequest     req                     = new PickRequest(canvas);
275         req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
276         List<IElement>  picks                   = new ArrayList<IElement>();
277         pickContext.pick(diagram, req, picks);
278
279         if(picks.size() == 1) return picks.iterator().next();
280         
281         return null;
282         
283     }
284
285     @Override
286     public void drop(DropTargetDropEvent dtde, final IDnDContext dp) {
287         TimeLogger.resetTimeAndLog(getClass(), "drop");
288
289         final IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);
290         if (d == null)
291             return;
292
293         IElement pick = tryPick(dtde.getLocation()); 
294         if(pick != null) {
295                 
296                         final List<WorkbenchSelectionElement> wses = new ArrayList<WorkbenchSelectionElement>();
297                         
298                         for(IDragItem i : dp.toArray())
299                                 if(i instanceof WSEDragItem)
300                                         wses.add(((WSEDragItem)i).getObject());
301
302                 final Resource element = (Resource)ElementUtils.getData(d, pick);
303                 if(element != null && !wses.isEmpty()) {
304                 
305                 try {
306                         
307                                         Simantics.getSession().syncRequest(new WriteRequest() {
308
309                                                 @Override
310                                                 public void perform(WriteGraph graph) throws DatabaseException {
311                                                         
312                                                         DiagramResource DIA = DiagramResource.getInstance(graph);
313                                                         
314                                                 Simantics.invokeSCLWrite(graph, element, DIA.symbolDropHandler, wses);
315                                                 
316                                                 }
317                                                 
318                                         });
319                                         
320                                 } catch (DatabaseException e) {
321                                         Logger.defaultLogError(e);
322                                 }
323                 
324                 return;
325                 
326                 }
327                 
328
329         }
330         
331         Runnable creator = () -> {
332             DiagramUtils.mutateDiagram(d, m -> {
333                 IDragItem items[] = dp.toArray();
334
335                 for (IDragItem i : items) {
336                     if (!(i instanceof ElementClassDragItem))
337                         continue;
338
339                     ElementClassDragItem res = (ElementClassDragItem) i;
340                     ElementClass ec = res.getElementClass();
341
342                     Point2D pos = dp.getItemPosition(i);
343                     // System.out.println(pos);
344                     assert (pos != null);
345
346                     IElement element = m.newElement(ec);
347                     element.setHints(res.getHintContext().getHints());
348
349                     setupDroppedElement(element, pos);
350
351                     // Remove only the drag items we've processed.
352                     dp.remove(i);
353                 }
354             });
355         };
356
357         selectNewDiagramContentAfter(d, part, creator);
358
359         getContext().getContentContext().setDirty();
360     }
361
362     protected void selectNewDiagramContentAfter(IDiagram d, MPart activatePart, Runnable diagramModifier) {
363         try {
364             Resource diagramResource = d.getHint(DiagramModelHints.KEY_DIAGRAM_RESOURCE);
365             final DiagramContentTracker tracker = diagramResource == null ? null
366                     : DiagramContentTracker.start(getContext(), Simantics.getSession(), diagramResource);
367
368             diagramModifier.run();
369
370             if (tracker != null) {
371                 // Get difference of diagram contents to find out what was added.
372                 DiagramContentChanges changes = tracker.update();
373                 Set<Resource> addedElements = changes.pick(changes.elements, Change.ADDED);
374                 if (!addedElements.isEmpty()) {
375                     new DiagramSelectionUpdater(getContext())
376                     .setNewSelection(0, addedElements)
377                     .setOneshot(true)
378                     .track();
379                     if (activatePart != null)
380                         E4WorkbenchUtils.activatePart(activatePart);
381                 }
382             }
383         } catch (DatabaseException e) {
384             Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Diagram content change tracking failed.", e));
385         }
386     }
387
388     protected void setupDroppedElement(IElement element, Point2D dropPos) {
389         // This works only for elements without parents.
390         ISnapAdvisor snapAdvisor = getContext().getHintStack().getHint(DiagramHints.SNAP_ADVISOR);
391         if(snapAdvisor != null)
392             snapAdvisor.snap(dropPos);
393
394         IElement parent = element.getHint(ElementHints.KEY_PARENT_ELEMENT);
395         if (parent != null) {
396             Point2D parentPos = ElementUtils.getPos(parent);
397             Point2D pos = new Point2D.Double(dropPos.getX() - parentPos.getX(), dropPos.getY() - parentPos.getY());
398             ElementUtils.setPos(element, pos);
399         } else {
400             ElementUtils.setPos(element, dropPos);
401         }
402     }
403
404     @Override
405     public void dropActionChanged(DropTargetDragEvent dtde, IDnDContext dp) {
406         dtde.acceptDrag(DnDConstants.ACTION_COPY);
407     }
408
409     @Override
410     public int getAllowedOps() {
411         return DnDConstants.ACTION_COPY;
412     }
413     
414     @Override
415     public double getPriority() {
416         return 10.0;
417     }
418
419 }