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