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