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