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