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