]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/sg/DiagramSceneGraphProvider.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / sg / DiagramSceneGraphProvider.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.sg;
13
14 import java.awt.Color;\r
15 import java.awt.event.AWTEventListener;\r
16 \r
17 import org.eclipse.core.runtime.NullProgressMonitor;\r
18 import org.simantics.Simantics;\r
19 import org.simantics.databoard.Bindings;\r
20 import org.simantics.db.ReadGraph;\r
21 import org.simantics.db.Resource;\r
22 import org.simantics.db.common.ResourceArray;\r
23 import org.simantics.db.common.primitiverequest.PossibleAdapter;\r
24 import org.simantics.db.exception.DatabaseException;\r
25 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;\r
26 import org.simantics.db.exception.NoSingleResultException;\r
27 import org.simantics.db.exception.ServiceException;\r
28 import org.simantics.db.management.ISessionContext;\r
29 import org.simantics.db.request.Read;\r
30 import org.simantics.diagram.adapter.DefaultConnectionClassFactory;\r
31 import org.simantics.diagram.adapter.FlagClassFactory;\r
32 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;\r
33 import org.simantics.diagram.handler.CopyPasteHandler;\r
34 import org.simantics.diagram.handler.CopyPasteStrategy;\r
35 import org.simantics.diagram.handler.DefaultCopyPasteStrategy;\r
36 import org.simantics.diagram.handler.DeleteHandler;\r
37 import org.simantics.diagram.handler.SimpleElementTransformHandler;\r
38 import org.simantics.diagram.query.DiagramRequests;\r
39 import org.simantics.diagram.runtime.RuntimeDiagramManager;\r
40 import org.simantics.diagram.stubs.DiagramResource;\r
41 import org.simantics.diagram.synchronization.CopyAdvisor;\r
42 import org.simantics.diagram.synchronization.SynchronizationHints;\r
43 import org.simantics.diagram.ui.DiagramModelHints;\r
44 import org.simantics.g2d.canvas.Hints;\r
45 import org.simantics.g2d.canvas.ICanvasContext;\r
46 import org.simantics.g2d.canvas.impl.CanvasContext;\r
47 import org.simantics.g2d.diagram.DiagramHints;\r
48 import org.simantics.g2d.diagram.IDiagram;\r
49 import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
50 import org.simantics.g2d.diagram.participant.ElementInteractor;\r
51 import org.simantics.g2d.diagram.participant.ElementPainter;\r
52 import org.simantics.g2d.diagram.participant.Selection;\r
53 import org.simantics.g2d.diagram.participant.ZOrderHandler;\r
54 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;\r
55 import org.simantics.g2d.element.ElementClassProviders;\r
56 import org.simantics.g2d.element.ElementClasses;\r
57 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;\r
58 import org.simantics.g2d.multileveldiagram.TransitionFunction;\r
59 import org.simantics.g2d.multileveldiagram.ZoomTransitionParticipant;\r
60 import org.simantics.g2d.page.DiagramDesc;\r
61 import org.simantics.g2d.participant.BackgroundPainter;\r
62 import org.simantics.g2d.participant.CanvasBoundsParticipant;\r
63 import org.simantics.g2d.participant.CanvasGrab;\r
64 import org.simantics.g2d.participant.GridPainter;\r
65 import org.simantics.g2d.participant.KeyToCommand;\r
66 import org.simantics.g2d.participant.KeyUtil;\r
67 import org.simantics.g2d.participant.MouseUtil;\r
68 import org.simantics.g2d.participant.MultitouchPanZoomRotateInteractor;\r
69 import org.simantics.g2d.participant.Notifications;\r
70 import org.simantics.g2d.participant.PageBorderParticipant;\r
71 import org.simantics.g2d.participant.PanZoomRotateHandler;\r
72 import org.simantics.g2d.participant.PointerPainter;\r
73 import org.simantics.g2d.participant.RulerPainter;\r
74 import org.simantics.g2d.participant.SymbolUtil;\r
75 import org.simantics.g2d.participant.TimeParticipant;\r
76 import org.simantics.g2d.participant.TransformUtil;\r
77 import org.simantics.g2d.participant.ZoomToAreaHandler;\r
78 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;\r
79 import org.simantics.modeling.ModelingResources;\r
80 import org.simantics.modeling.mapping.ComponentCopyAdvisor;\r
81 import org.simantics.modeling.mapping.ElementCopyAdvisor;\r
82 import org.simantics.modeling.mapping.MappedElementCopyAdvisor;\r
83 import org.simantics.modeling.mapping.ModelingSynchronizationHints;\r
84 import org.simantics.modeling.ui.diagramEditor.PopulateElementDropParticipant;\r
85 import org.simantics.modeling.ui.diagramEditor.PopulateElementMonitorDropParticipant;\r
86 import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler;\r
87 import org.simantics.modeling.ui.diagramEditor.handlers.HeadlessStructuralBrowsingHandler.IDiagramUpdateSupport;\r
88 import org.simantics.scenegraph.g2d.G2DSceneGraph;\r
89 import org.simantics.scenegraph.g2d.events.adapter.AWTRemoteEventAdapter;\r
90 import org.simantics.scenegraph.g2d.events.command.CommandKeyBinding;\r
91 import org.simantics.structural.stubs.StructuralResource2;\r
92 import org.simantics.structural2.modelingRules.IModelingRules;\r
93 import org.simantics.ui.SimanticsUI;\r
94 import org.simantics.utils.datastructures.hints.HintContext;\r
95 import org.simantics.utils.datastructures.hints.IHintContext;\r
96 import org.simantics.utils.page.PageDesc;\r
97 import org.simantics.utils.page.PageOrientation;\r
98 import org.simantics.utils.threads.AWTThread;\r
99 import org.simantics.utils.threads.IThreadWorkQueue;\r
100 import org.simantics.utils.ui.ErrorLogger;\r
101
102
103 /**
104  * ISceneGraphProvider implementation for diagrams.
105  * TODO: decide if this should be stateless or stateful class
106  * 
107  * @author J-P Laine
108  *
109  */
110 public class DiagramSceneGraphProvider implements ICanvasSceneGraphProvider, IDiagramUpdateSupport {
111 \r
112         protected boolean isSymbol = false;
113     protected Resource resource;
114     protected ResourceArray structuralPath = null;
115     protected IDiagram diagram = null;
116     protected CanvasContext ctx = null;\r
117     protected boolean ownsContext = false;
118     protected GraphToDiagramSynchronizer synchronizer = null;
119     protected String view = null;
120     protected Boolean navigation = null;
121
122     protected AWTRemoteEventAdapter listener = null;
123 \r
124     final IHintContext initialHints = new HintContext();\r
125
126     public DiagramSceneGraphProvider(ReadGraph g, final Resource diagramOrComposite) {\r
127         this.structuralPath = new ResourceArray();
128         try {\r
129 \r
130             StructuralResource2 sr = StructuralResource2.getInstance(g);\r
131                 DiagramResource DIA = DiagramResource.getInstance(g);\r
132                 \r
133                 Resource diagram = diagramOrComposite;\r
134                 if(!g.isInstanceOf(diagram, DIA.Composite)) {\r
135                         \r
136                 ModelingResources mr = ModelingResources.getInstance(g);\r
137                         diagram = g.getPossibleObject(diagramOrComposite, mr.CompositeToDiagram);\r
138                         if(diagram == null) {\r
139                     // looks like we have a component without direct relation to composite.. try to solve this\r
140                     // FIXME: use adapter to get composite directly from resource\r
141                     final Resource type = g.getSingleType(diagramOrComposite, sr.Component);\r
142                     if (type == null)\r
143                         return;\r
144 \r
145                     final Resource definedBy = g.getPossibleObject(type, sr.IsDefinedBy);\r
146                     if (definedBy == null)\r
147                         return;\r
148 \r
149                     this.structuralPath = new ResourceArray(diagramOrComposite);\r
150                     diagram =  g.getPossibleObject(definedBy, mr.CompositeToDiagram);\r
151                         }\r
152                         \r
153                 } else {\r
154                         \r
155                         Resource possibleSymbol = g.getPossibleObject(diagram, sr.Defines);\r
156                         if(possibleSymbol != null && g.isInstanceOf(possibleSymbol, DIA.ElementClass)) isSymbol = true;\r
157                         \r
158                 }\r
159                 \r
160                         this.resource = diagram;\r
161                 \r
162         } catch (ManyObjectsForFunctionalRelationException e) {
163             // TODO Auto-generated catch block
164             e.printStackTrace();
165         } catch (ServiceException e) {
166             // TODO Auto-generated catch block
167             e.printStackTrace();
168         } catch (NoSingleResultException e) {
169             // TODO Auto-generated catch block
170             e.printStackTrace();
171         }
172     }
173 \r
174     protected CopyPasteStrategy getCopyPasteStrategy() {\r
175                 try {\r
176                         CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter<CopyPasteStrategy>(resource, CopyPasteStrategy.class));\r
177                         if(cpStrategy != null) return cpStrategy;\r
178                 } catch (DatabaseException e) {\r
179                 }\r
180                 return new DefaultCopyPasteStrategy();\r
181     }\r
182     \r
183     protected CopyAdvisor getCopyAdvisor() {\r
184                 try {\r
185                         CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter<CopyAdvisor>(resource, CopyAdvisor.class));\r
186                         if(advisor != null) return advisor;\r
187                 } catch (DatabaseException e) {\r
188                 }\r
189                 return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor());\r
190     }\r
191     
192     private void initContext(CanvasContext ctx) {\r
193         boolean unlock = false;\r
194         if (!ctx.isLocked()) {
195             ctx.setLocked(true);\r
196             unlock = true;\r
197         }\r
198 \r
199         IHintContext h = ctx.getDefaultHintContext();
200
201         // Support & Util Participants
202         ctx.add( new TransformUtil() );
203         ctx.add( new MouseUtil() );
204         ctx.add( new KeyUtil() );
205         ctx.add( new CanvasGrab() );
206         ctx.add( new SymbolUtil() );
207         ctx.add( new TimeParticipant() );
208         ctx.add( new CanvasBoundsParticipant() );
209         ctx.add( new Notifications() );
210
211         // SGFocusParticipant does not work for remote viewer at the moment.
212         //ctx.add( new SGFocusParticipant() );
213
214         h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
215 \r
216         ctx.add( new PanZoomRotateHandler(navigation == null || navigation == true) );
217         ctx.add( new MultitouchPanZoomRotateInteractor() );
218         ctx.add( new ZoomToAreaHandler() );
219         ctx.add( new SimpleElementTransformHandler() );
220         \r
221         // Key bindings
222         if(navigation == null || navigation == true)
223             ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );
224
225         // Grid & Ruler & Background
226         h.setHint(RulerPainter.KEY_RULER_ENABLED, false);
227         h.setHint(GridPainter.KEY_GRID_ENABLED, false);
228
229         ctx.add( new GridPainter() );
230         ctx.add( new RulerPainter() );
231         ctx.add( new BackgroundPainter() );\r
232         
233         h.setHint(Hints.KEY_DISPLAY_PAGE, Boolean.FALSE);\r
234         h.setHint(Hints.KEY_DISPLAY_MARGINS, Boolean.TRUE);\r
235         ctx.add( new PageBorderParticipant() );
236 \r
237         h.setHint(Hints.KEY_GRID_COLOR, new Color(0.95f, 0.95f, 0.95f));
238         h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.WHITE);
239         h.setHint(RulerPainter.KEY_RULER_BACKGROUND_COLOR, new Color(0.9f, 0.9f, 0.9f, 0.75f));
240         h.setHint(RulerPainter.KEY_RULER_TEXT_COLOR, Color.BLACK);
241
242         ////// Diagram Participants //////
243         ctx.add( new PointerInteractor(false, false, false, false, true, true, synchronizer.getElementClassProvider(), null) );
244         ctx.add( new ElementInteractor() );
245         ctx.add( new Selection() );
246         ctx.add( new DiagramParticipant() );
247         ctx.add( new ElementPainter(false) );
248
249         //ctx.add( new ElementHeartbeater() );
250         ctx.add( new ZOrderHandler() );
251         ctx.add( new ZoomTransitionParticipant(TransitionFunction.SIGMOID) );
252
253         /////// D'n'D ///////
254         ctx.add(new PopulateElementDropParticipant(synchronizer));
255         ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5));
256
257         h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
258
259         h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 1000.0);
260         h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0);
261
262         // Add visual browsing capabilities for structural models
263         ISessionContext sessionContext = Simantics.getSessionContext();
264         ctx.add(new HeadlessStructuralBrowsingHandler(this, sessionContext, structuralPath));
265
266         // Page settings
267         PageDesc pageDesc = PageDesc.DEFAULT.withOrientation(PageOrientation.Landscape);
268         // TODO: load page desc from graph
269         h.setHint(Hints.KEY_PAGE_DESC, pageDesc);
270 \r
271         ctx.add(new CopyPasteHandler(getCopyPasteStrategy()));\r
272         ctx.add(new DeleteHandler(null));\r
273 \r
274         if (resource != null)\r
275             loadPageSettings(ctx, resource);\r
276 \r
277         ctx.assertParticipantDependencies();\r
278         if (unlock)
279             ctx.setLocked(false);\r
280     }
281 \r
282     protected void loadPageSettings(ICanvasContext ctx, Resource diagramResource) {\r
283         try {\r
284             DiagramDesc diagramDesc = Simantics.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource));\r
285             if (diagramDesc != null)\r
286                 setDiagramDesc(ctx, diagramDesc);\r
287         } catch (DatabaseException e) {\r
288             ErrorLogger.defaultLogError(e);\r
289         }\r
290     }\r
291 \r
292     protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) {\r
293         IHintContext hints = ctx.getDefaultHintContext();\r
294         hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc());\r
295         //hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible());\r
296         hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible());\r
297     }\r
298
299     @Override
300     public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg) {
301         return initializeSceneGraph(sg, null);
302     }
303
304     @Override\r
305     public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String view) {\r
306         return initializeSceneGraph(sg, "", "", view);\r
307     }\r
308 \r
309     @Override\r
310     public G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI) {\r
311         return initializeSceneGraph(sg, modelURI, RVI, "");\r
312     }\r
313 \r
314     @Override\r
315     public G2DSceneGraph initializeSceneGraph(ICanvasContext context, String modelURI, String RVI) {\r
316         G2DSceneGraph sg = context.getSceneGraph();\r
317         return initializeSceneGraph(context, sg, modelURI, RVI, "");\r
318     }\r
319
320     /**\r
321      * @param sg\r
322      * @param view\r
323      * @param initialHints\r
324      * @return\r
325      */\r
326     private G2DSceneGraph initializeSceneGraph(G2DSceneGraph sg, String modelURI, String RVI, String view) {\r
327         IThreadWorkQueue thread = AWTThread.getThreadAccess();\r
328         ctx = new CanvasContext(thread, sg); // By giving the scene graph instance as parameter, we can use external serializer\r
329         return initializeSceneGraph(ctx, sg, modelURI, RVI, view);\r
330     }\r
331 \r
332     /**
333      * @param sg
334      * @param view
335      * @param initialHints
336      * @return
337      */
338     private G2DSceneGraph initializeSceneGraph(ICanvasContext context, G2DSceneGraph sg, String modelURI, String RVI, String view) {\r
339         this.ctx = (CanvasContext) context;\r
340         this.view = view;
341         if(view != null) {
342             //System.out.println("using layer '"+view+"'");
343             initialHints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view });
344         }
345 \r
346         try {\r
347 \r
348             IModelingRules modelingRules = Simantics.getSession().syncRequest(DiagramRequests.getModelingRules(resource, null));\r
349             if (modelingRules != null) {\r
350                 initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules);\r
351             }\r
352             \r
353             initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor());\r
354             
355             final RuntimeDiagramManager runtimeDiagramManager = RuntimeDiagramManager.create(Simantics.getSession(), resource, modelURI, RVI);\r
356             \r
357             synchronizer = Simantics.getSession().syncRequest(new Read<GraphToDiagramSynchronizer>() {
358                 @Override
359                 public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException {
360                     DiagramResource dr = DiagramResource.getInstance(graph);
361                     Boolean val = graph.getPossibleRelatedValue(resource, dr.NavigationEnabled, Bindings.BOOLEAN);
362                     if(val != null && navigation == null) { // Set only if navigation has not been set manually
363                         navigation = val;
364                     }
365                     GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx,\r
366                             ElementClassProviders.mappedProvider(
367                                     ElementClasses.CONNECTION, DefaultConnectionClassFactory.CLASS.newClassWith(new StaticObjectAdapter(dr.Connection)),
368                                     ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal)
369                             )
370                     );
371                     sync.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
372                     diagram = sync.loadDiagram(new NullProgressMonitor(), graph, null, resource, runtimeDiagramManager.getRuntimeDiagram(), structuralPath, initialHints); // FIXME
373                     return sync;
374                 }
375             });\r
376             
377         } catch (DatabaseException e) {
378             // TODO Auto-generated catch block
379             e.printStackTrace();
380         }
381         ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);\r
382 \r
383         initContext(ctx);\r
384 \r
385         return ctx.getSceneGraph();\r
386     }\r
387
388     @Override\r
389     public void dispose() {
390         if(ctx != null) {\r
391             if (ownsContext)
392                 ctx.dispose();
393             ctx = null;
394         }
395         if (synchronizer != null) {\r
396             synchronizer.dispose();
397             synchronizer = null;
398         }
399         if (diagram != null) {\r
400             diagram.dispose();
401             diagram = null;
402         }
403     }
404
405     /**
406      * Hack for returning canvasContext for some special cases
407      * this method brakes the interface, but this is not intended to be used in production context
408      * @return
409      */
410     @Override\r
411     public ICanvasContext getCanvasContext() {
412         return ctx;
413     }
414
415     @Override
416     public AWTEventListener getEventListener() {
417         if(ctx == null) return null;
418         if(listener == null) {
419             listener = new AWTRemoteEventAdapter(ctx, ctx.getEventQueue());
420         }
421         return listener;
422     }
423
424     @Override
425     public void updateDiagram(final ResourceArray structuralPath) {
426         final IHintContext hints = new HintContext();
427         if(view != null) {
428             System.out.println("using layer '"+view+"'");
429             hints.setHint(DiagramHints.KEY_FIXED_LAYERS, new String[] { view });
430         }
431
432         try {
433             // FIXME: I have no idea if this works or not..
434             diagram = SimanticsUI.getSession().syncRequest(new Read<IDiagram>() {
435                 @Override
436                 public IDiagram perform(ReadGraph graph) throws DatabaseException {
437                     IDiagram d = synchronizer.loadDiagram(new NullProgressMonitor(), graph, null, structuralPath.resources[0], null, structuralPath.removeFromBeginning(0), hints);
438                     return d;
439                 }
440             });
441             ctx.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, diagram);
442         } catch (DatabaseException e) {
443             // TODO Auto-generated catch block
444             e.printStackTrace();
445         }
446     }
447 \r
448     public void setExperiment(ReadGraph g, String identifier) {\r
449         if(diagram == null) return;\r
450         diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier);\r
451         initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, identifier);\r
452 \r
453 //              try {\r
454 //                      DiagramExperiment.setActiveExperiment(g, diagram, (String)identifier);\r
455 //              } catch (DatabaseException e) {\r
456 //                      // TODO Auto-generated catch block\r
457 //                      e.printStackTrace();\r
458 //              }\r
459 \r
460     }\r
461
462     /**
463      * Supported keys:
464      *     DiagramModelHints.KEY_SESSION_ID
465      *     DiagramHints.KEY_NAVIGATION_ENABLED
466      */
467     @Override
468     public void setHint(Object key, Object value) {\r
469         if(DiagramModelHints.KEY_SESSION_ID.equals(key)) {\r
470             if(diagram != null)\r
471                 diagram.setHint(DiagramModelHints.KEY_SESSION_ID, value);\r
472             initialHints.setHint(DiagramModelHints.KEY_SESSION_ID, value);\r
473         } else if(DiagramModelHints.KEY_ACTIVE_EXPERIMENT.equals(key)) {\r
474             if(diagram != null)
475                 diagram.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value);
476             initialHints.setHint(DiagramModelHints.KEY_ACTIVE_EXPERIMENT, value);\r
477         } else if(DiagramHints.KEY_NAVIGATION_ENABLED.equals(key)) {
478             navigation = (Boolean)value;
479         }
480     }
481 }