]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/PopulateElementDropParticipant.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagramEditor / PopulateElementDropParticipant.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.modeling.ui.diagramEditor;\r
13 \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
27 \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
86 \r
87 /**\r
88  * This participant populates Elements from ElementClass-resources drops\r
89  */\r
90 public class PopulateElementDropParticipant extends AbstractDiagramParticipant implements IDropTargetParticipant {\r
91 \r
92     @Dependency PickContext pickContext;\r
93     @Dependency TransformUtil transformUtil;\r
94         \r
95     protected GraphToDiagramSynchronizer synchronizer;\r
96     protected IWorkbenchPartSite partSite;\r
97 \r
98     public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer) {\r
99         this(synchronizer, null);\r
100     }\r
101 \r
102     public PopulateElementDropParticipant(GraphToDiagramSynchronizer synchronizer, IWorkbenchPartSite partSite) {\r
103         this.synchronizer = synchronizer;\r
104         this.partSite = partSite;\r
105     }\r
106 \r
107     @Override\r
108     public void dragEnter(DropTargetDragEvent dtde, IDnDContext dp) {\r
109         if (diagram == null)\r
110             return;\r
111 \r
112         Transferable tr = dtde.getTransferable();\r
113         if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) {\r
114             // System.out.println("joo");\r
115             Object obj = null;\r
116 \r
117             // This must be done to have SWT transfer set the source data\r
118             try {\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
127             }\r
128 \r
129             // Check SWT\r
130             if (!(obj instanceof IStructuredSelection)) {\r
131                 obj = LocalObjectTransfer.getTransfer().getObject();\r
132                 // System.out.println("GOT FROM SWT: " + obj);\r
133             }\r
134 \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
141                             if (ec != null) {\r
142                                 dp.add(new ElementClassDragItem(ec));\r
143                             } else {\r
144                                 Resource r = (Resource) ((IAdaptable) elm).getAdapter(Resource.class);\r
145                                 if (r != null) {\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
150                                         try {\r
151                                             if (!DiagramTypeUtils.symbolAllowedOnDiagram(synchronizer.getSession(), symbol, dia)) {\r
152                                                 // Deny dragging of this symbol\r
153                                                 continue;\r
154                                             }\r
155                                         } catch (DatabaseException e) {\r
156                                             e.printStackTrace();\r
157                                             continue;\r
158                                         }*/\r
159                                     }\r
160 \r
161                                     try {\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
167                                             dp.add(item);\r
168                                         }\r
169                                     } catch (DatabaseException e) {\r
170                                         // Ignore node-class retrieval failures.\r
171                                         //System.out.println("error: " + e.getMessage());\r
172                                     }\r
173                                 }\r
174                             }\r
175                         }\r
176                     }\r
177 \r
178                     // Let the default logic handle out how many columns to use.\r
179                     dp.getHints().removeHint(DnDHints.KEY_DND_GRID_COLUMNS);\r
180                 }\r
181             }\r
182 \r
183             return;\r
184         }\r
185 \r
186         if (tr.isDataFlavorSupported(ElementClassTransferable.FLAVOR)) {\r
187             ResourceElementClassTransferData dada;\r
188             try {\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
194             }\r
195             Session s = synchronizer.getSession();\r
196             try {\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
201                 }\r
202             } catch (DatabaseException e) {\r
203                 throw new RuntimeException(e);\r
204             }\r
205 \r
206             return;\r
207         }\r
208     }\r
209 \r
210     private String validateDrop(RequestProcessor processor, final Resource draggedResource, final Resource dropTarget) throws DatabaseException {\r
211         return processor.syncRequest(new UniqueRead<String>() {\r
212             @Override\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
220 \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
229                 }\r
230                 \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
240                     }\r
241                 }\r
242 \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
250                         }\r
251                     }\r
252                 }\r
253 \r
254                 return null;\r
255             }\r
256         });\r
257     }\r
258 \r
259     @Override\r
260     public void dragExit(DropTargetEvent dte, IDnDContext dp) {\r
261         // System.out.println("exit");\r
262     }\r
263 \r
264     @Override\r
265     public void dragOver(DropTargetDragEvent dtde, IDnDContext dp) {\r
266         // System.out.println("over");\r
267     }\r
268 \r
269     private IElement tryPick(Point p) {\r
270 \r
271         Point2D canvas = transformUtil.controlToCanvas(p, null);\r
272         \r
273         assertDependencies();\r
274 \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
279 \r
280         if(picks.size() == 1) return picks.iterator().next();\r
281         \r
282         return null;\r
283         \r
284     }\r
285 \r
286     @Override\r
287     public void drop(DropTargetDropEvent dtde, final IDnDContext dp) {\r
288         TimeLogger.resetTimeAndLog(getClass(), "drop");\r
289 \r
290         final IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);\r
291         if (d == null)\r
292             return;\r
293 \r
294         IElement pick = tryPick(dtde.getLocation()); \r
295         if(pick != null) {\r
296 \r
297                         final List<WorkbenchSelectionElement> wses = new ArrayList<WorkbenchSelectionElement>();\r
298 \r
299                         for(IDragItem i : dp.toArray())\r
300                                 if(i instanceof WSEDragItem)\r
301                                         wses.add(((WSEDragItem)i).getObject());\r
302 \r
303                 final Resource element = (Resource)ElementUtils.getData(d, pick);\r
304                 if(element != null && !wses.isEmpty()) {\r
305 \r
306                 try {\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
312                             @Override\r
313                             public void perform(WriteGraph graph) throws DatabaseException {\r
314                                 Simantics.invokeSCLWrite(graph, function, wses);\r
315                             }\r
316                         });\r
317                         return;\r
318                     }\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
324                                     e));\r
325                     return;\r
326                 }\r
327 \r
328                 }\r
329 \r
330 \r
331         }\r
332         \r
333         Runnable creator = new Runnable() {\r
334             public void run() {\r
335                 DiagramUtils.mutateDiagram(d, m -> {\r
336                     IDragItem items[] = dp.toArray();\r
337 \r
338                     for (IDragItem i : items) {\r
339                         if (!(i instanceof ElementClassDragItem))\r
340                             continue;\r
341 \r
342                         ElementClassDragItem res = (ElementClassDragItem) i;\r
343                         ElementClass ec = res.getElementClass();\r
344 \r
345                         Point2D pos = dp.getItemPosition(i);\r
346                         // System.out.println(pos);\r
347                         assert (pos != null);\r
348 \r
349                         IElement element = m.newElement(ec);\r
350                         element.setHints(res.getHintContext().getHints());\r
351 \r
352                         setupDroppedElement(element, pos);\r
353 \r
354                         // Remove only the drag items we've processed.\r
355                         dp.remove(i);\r
356                     }\r
357                 });\r
358             }\r
359         };\r
360 \r
361         selectNewDiagramContentAfter(d, partSite, creator);\r
362 \r
363         getContext().getContentContext().setDirty();\r
364     }\r
365 \r
366     private static class PossibleVariableProperty extends ResourceRead2<Variable> {\r
367 \r
368         public PossibleVariableProperty(Resource entity, Resource property) {\r
369             super(entity, property);\r
370         }\r
371 \r
372         @Override\r
373         public Variable perform(ReadGraph graph) throws DatabaseException {\r
374             return Variables.tryGetProperty(graph, resource, resource2);\r
375         }\r
376 \r
377     }\r
378 \r
379     protected void selectNewDiagramContentAfter(IDiagram d, IWorkbenchPartSite activateSite, Runnable diagramModifier) {\r
380         try {\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
384 \r
385             diagramModifier.run();\r
386 \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
394                     .setOneshot(true)\r
395                     .track();\r
396                     if (activateSite != null)\r
397                         WorkbenchUtils.activatePart(activateSite);\r
398                 }\r
399             }\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
402         }\r
403     }\r
404 \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
410 \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
416         } else {\r
417             ElementUtils.setPos(element, dropPos);\r
418         }\r
419     }\r
420 \r
421     @Override\r
422     public void dropActionChanged(DropTargetDragEvent dtde, IDnDContext dp) {\r
423         dtde.acceptDrag(DnDConstants.ACTION_COPY);\r
424     }\r
425 \r
426     @Override\r
427     public int getAllowedOps() {\r
428         return DnDConstants.ACTION_COPY;\r
429     }\r
430     \r
431     @Override\r
432     public double getPriority() {\r
433         return 10.0;\r
434     }\r
435 \r
436 }