Configurable connection crossing styles
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagramEditor / DiagramViewer.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2019 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  *     Semantum Oy - issue #4384, gitlab #399
12  *******************************************************************************/
13 package org.simantics.modeling.ui.diagramEditor;
14
15 import java.awt.Color;
16 import java.awt.dnd.DnDConstants;
17 import java.util.Collections;
18 import java.util.Set;
19 import java.util.concurrent.TimeUnit;
20 import java.util.function.Supplier;
21
22 import org.eclipse.core.runtime.Assert;
23 import org.eclipse.core.runtime.IAdaptable;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.SubMonitor;
26 import org.eclipse.core.runtime.jobs.Job;
27 import org.eclipse.jface.action.IStatusLineManager;
28 import org.eclipse.jface.resource.JFaceResources;
29 import org.eclipse.jface.resource.LocalResourceManager;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Display;
32 import org.eclipse.ui.IEditorInput;
33 import org.eclipse.ui.IEditorSite;
34 import org.eclipse.ui.IWorkbenchPartSite;
35 import org.eclipse.ui.contexts.IContextService;
36 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
37 import org.simantics.Simantics;
38 import org.simantics.browsing.ui.model.browsecontexts.BrowseContext;
39 import org.simantics.db.ReadGraph;
40 import org.simantics.db.Resource;
41 import org.simantics.db.Session;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.common.primitiverequest.PossibleAdapter;
44 import org.simantics.db.common.primitiverequest.PossibleObject;
45 import org.simantics.db.common.procedure.adapter.ListenerDelegate;
46 import org.simantics.db.common.procedure.adapter.ListenerSupport;
47 import org.simantics.db.common.request.ParametrizedRead;
48 import org.simantics.db.common.request.TypeURIs;
49 import org.simantics.db.common.request.WriteRequest;
50 import org.simantics.db.common.utils.CommonDBUtils;
51 import org.simantics.db.common.utils.TagUtil;
52 import org.simantics.db.exception.DatabaseException;
53 import org.simantics.db.layer0.request.combinations.Combinators;
54 import org.simantics.db.management.ISessionContext;
55 import org.simantics.db.management.ISessionContextProvider;
56 import org.simantics.db.request.Read;
57 import org.simantics.diagram.DiagramTypeUtils;
58 import org.simantics.diagram.adapter.FlagClassFactory;
59 import org.simantics.diagram.adapter.GraphToDiagramSynchronizer;
60 import org.simantics.diagram.adapter.IDiagramLoader;
61 import org.simantics.diagram.connection.ModelledConnectionAdvisor;
62 import org.simantics.diagram.handler.ConnectionCommandHandler;
63 import org.simantics.diagram.handler.CopyPasteHandler;
64 import org.simantics.diagram.handler.CopyPasteStrategy;
65 import org.simantics.diagram.handler.DefaultCopyPasteStrategy;
66 import org.simantics.diagram.handler.DeleteHandler;
67 import org.simantics.diagram.handler.ExpandSelectionHandler;
68 import org.simantics.diagram.handler.SimpleElementTransformHandler;
69 import org.simantics.diagram.layer.ILayersViewPage;
70 import org.simantics.diagram.participant.ConnectionCrossingsParticipant;
71 import org.simantics.diagram.participant.ContextUtil;
72 import org.simantics.diagram.participant.PointerInteractor2;
73 import org.simantics.diagram.participant.SGFocusParticipant;
74 import org.simantics.diagram.query.DiagramRequests;
75 import org.simantics.diagram.runtime.RuntimeDiagramManager;
76 import org.simantics.diagram.stubs.DiagramResource;
77 import org.simantics.diagram.symbolcontribution.SymbolProviderFactory;
78 import org.simantics.diagram.synchronization.CopyAdvisor;
79 import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;
80 import org.simantics.diagram.synchronization.SynchronizationHints;
81 import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;
82 import org.simantics.diagram.ui.DiagramModelHints;
83 import org.simantics.diagram.ui.SWTPopupMenuParticipant;
84 import org.simantics.diagram.ui.SWTPopupMenuParticipantAwt;
85 import org.simantics.diagram.ui.WorkbenchSelectionProvider;
86 import org.simantics.g2d.canvas.Hints;
87 import org.simantics.g2d.canvas.ICanvasContext;
88 import org.simantics.g2d.canvas.ICanvasParticipant;
89 import org.simantics.g2d.canvas.impl.CanvasContext;
90 import org.simantics.g2d.chassis.AWTChassis;
91 import org.simantics.g2d.chassis.ICanvasChassis;
92 import org.simantics.g2d.chassis.IChassisListener;
93 import org.simantics.g2d.chassis.SWTChassis;
94 import org.simantics.g2d.connection.IConnectionAdvisor;
95 import org.simantics.g2d.diagram.DiagramClass;
96 import org.simantics.g2d.diagram.DiagramHints;
97 import org.simantics.g2d.diagram.IDiagram;
98 import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;
99 import org.simantics.g2d.diagram.impl.Diagram;
100 import org.simantics.g2d.diagram.participant.DelayedBatchElementPainter;
101 import org.simantics.g2d.diagram.participant.DiagramParticipant;
102 import org.simantics.g2d.diagram.participant.ElementInteractor;
103 import org.simantics.g2d.diagram.participant.ElementPainter;
104 import org.simantics.g2d.diagram.participant.Selection;
105 import org.simantics.g2d.diagram.participant.TerminalPainter;
106 import org.simantics.g2d.diagram.participant.ZOrderHandler;
107 import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor;
108 import org.simantics.g2d.element.ElementClassProviders;
109 import org.simantics.g2d.element.ElementClasses;
110 import org.simantics.g2d.element.ElementUtils;
111 import org.simantics.g2d.element.IElement;
112 import org.simantics.g2d.element.IElementClassProvider;
113 import org.simantics.g2d.element.handler.impl.StaticObjectAdapter;
114 import org.simantics.g2d.elementclass.connection.ConnectionClass;
115 import org.simantics.g2d.page.DiagramDesc;
116 import org.simantics.g2d.participant.BackgroundPainter;
117 import org.simantics.g2d.participant.CanvasBoundsParticipant;
118 import org.simantics.g2d.participant.CanvasGrab;
119 import org.simantics.g2d.participant.GridPainter;
120 import org.simantics.g2d.participant.KeyUtil;
121 import org.simantics.g2d.participant.MouseUtil;
122 import org.simantics.g2d.participant.Notifications;
123 import org.simantics.g2d.participant.PageBorderParticipant;
124 import org.simantics.g2d.participant.PanZoomRotateHandler;
125 import org.simantics.g2d.participant.PointerPainter;
126 import org.simantics.g2d.participant.RenderingQualityInteractor;
127 import org.simantics.g2d.participant.RulerPainter;
128 import org.simantics.g2d.participant.SymbolUtil;
129 import org.simantics.g2d.participant.TimeParticipant;
130 import org.simantics.g2d.participant.TransformUtil;
131 import org.simantics.g2d.participant.WorkbenchStatusLine;
132 import org.simantics.g2d.participant.ZoomToAreaHandler;
133 import org.simantics.g2d.tooltip.TerminalTooltipParticipant;
134 import org.simantics.g2d.utils.CanvasUtils;
135 import org.simantics.layer0.utils.triggers.IActivation;
136 import org.simantics.layer0.utils.triggers.IActivationManager;
137 import org.simantics.modeling.ModelingResources;
138 import org.simantics.modeling.mapping.ComponentCopyAdvisor;
139 import org.simantics.modeling.mapping.ElementCopyAdvisor;
140 import org.simantics.modeling.mapping.MappedElementCopyAdvisor;
141 import org.simantics.modeling.mapping.ModelingSynchronizationHints;
142 import org.simantics.modeling.ui.diagramEditor.handlers.LinkBrowsingHandler;
143 import org.simantics.modeling.ui.diagramEditor.handlers.StructuralBrowsingHandler;
144 import org.simantics.modeling.ui.preferences.DiagramPreferenceUtil;
145 import org.simantics.modeling.ui.preferences.DiagramPreferences;
146 import org.simantics.project.ontology.ProjectResource;
147 import org.simantics.scenegraph.INode;
148 import org.simantics.scenegraph.g2d.snap.GridSnapAdvisor;
149 import org.simantics.scenegraph.utils.NodeUtil;
150 import org.simantics.selectionview.StandardPropertyPage;
151 import org.simantics.structural.stubs.StructuralResource2;
152 import org.simantics.structural2.modelingRules.IModelingRules;
153 import org.simantics.ui.SimanticsUI;
154 import org.simantics.ui.jobs.SessionGarbageCollectorJob;
155 import org.simantics.ui.workbench.IPropertyPage;
156 import org.simantics.ui.workbench.IResourceEditorInput;
157 import org.simantics.ui.workbench.IResourceEditorInput2;
158 import org.simantics.ui.workbench.TitleRequest;
159 import org.simantics.ui.workbench.TitleUpdater;
160 import org.simantics.ui.workbench.ToolTipRequest;
161 import org.simantics.ui.workbench.editor.input.InputValidationCombinators;
162 import org.simantics.utils.DataContainer;
163 import org.simantics.utils.datastructures.hints.HintContext;
164 import org.simantics.utils.datastructures.hints.HintListenerAdapter;
165 import org.simantics.utils.datastructures.hints.IHintContext;
166 import org.simantics.utils.datastructures.hints.IHintContext.Key;
167 import org.simantics.utils.datastructures.hints.IHintListener;
168 import org.simantics.utils.datastructures.hints.IHintObservable;
169 import org.simantics.utils.threads.AWTThread;
170 import org.simantics.utils.threads.IThreadWorkQueue;
171 import org.simantics.utils.threads.SWTThread;
172 import org.simantics.utils.threads.ThreadUtils;
173 import org.simantics.utils.ui.ErrorLogger;
174 import org.simantics.utils.ui.ExceptionUtils;
175
176 import com.kitfox.svg.SVGCache;
177
178 /**
179  * @author Toni Kalajainen
180  * @author Tuukka Lehtonen
181  */
182 public class DiagramViewer 
183   //extends EditorPart implements IResourceEditorPart2, 
184     implements ListenerSupport, IAdaptable {
185
186     public interface DiagramViewerHost {
187         void doSetPartName(String name);
188         void doSetTitleToolTip(String name);
189     }
190
191     public static final String                      DIAGRAMMING_CONTEXT      = "org.simantics.modeling.ui.diagramming"; //$NON-NLS-1$
192     private static final String                     PREFERENCE_VIRTUAL_GRAPH = "preferences"; //$NON-NLS-1$
193
194     private static final boolean                    PROFILE                  = false;
195
196     ParametrizedRead<IResourceEditorInput, Boolean> INPUT_VALIDATOR =
197         Combinators.compose(
198                 InputValidationCombinators.or(
199                         // Normal configuration diagrams of a model
200                         Combinators.compose(
201                                 InputValidationCombinators.hasURI(),
202                                 InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite)
203                         ),
204                         // Configuration diagrams of a component type
205                         Combinators.compose(
206                                 InputValidationCombinators.hasURI(),
207                                 Combinators.compose(
208                                         InputValidationCombinators.partialFunction(StructuralResource2.URIs.Defines),
209                                         InputValidationCombinators.partialFunction(ModelingResources.URIs.DiagramToComposite)
210                                 )
211                         )
212                 ),
213                 InputValidationCombinators.extractInputResource()
214         );
215
216     protected EditorState                editorState;
217
218     protected boolean                    disposed           = false;
219     protected IThreadWorkQueue           swt;
220     protected IStatusLineManager         statusLineManager;
221     protected Display                    display;
222     protected LocalResourceManager       resourceManager;
223     protected SWTChassis                 c;
224     protected IDiagram                   sourceDiagram;
225     protected DataContainer<IDiagram>    sourceDiagramContainer;
226     protected CanvasContext              canvasContext;
227     protected ISessionContextProvider    sessionContextProvider;
228     protected ISessionContext            sessionContext;
229     protected Resource                   diagramResource;
230     protected GraphToDiagramSynchronizer synchronizer;
231     protected IActivation                activation;
232     protected ContextUtil                contextUtil;
233     protected SWTPopupMenuParticipant    popupMenuParticipant;
234     
235     protected DiagramPreferences         diagramPreferences;
236     protected DiagramDesc                diagramDesc;
237     protected GridSnapAdvisor            snapAdvisor;
238
239     private RuntimeDiagramManager        runtimeDiagramManager;
240     private Resource                     runtimeDiagramResourceCache;
241     private HasDiagramSourceListener     hasDiagramSourceListener;
242
243     /**
244      * Set externally in
245      * {@link #init(DiagramViewerHost, IEditorSite, IEditorInput, DataContainer, WorkbenchSelectionProvider)}
246      * .
247      */
248     protected WorkbenchSelectionProvider selectionProvider;
249
250     protected Resource getRuntimeFromManager() {
251         RuntimeDiagramManager rtdm = runtimeDiagramManager;
252         return (rtdm == null) ? null : rtdm.getRuntimeDiagram();
253     }
254
255     public Resource getRuntime() {
256         if (runtimeDiagramResourceCache != null)
257             return runtimeDiagramResourceCache;
258         runtimeDiagramResourceCache = getRuntimeFromManager();
259         return runtimeDiagramResourceCache;
260     }
261
262     public ParametrizedRead<IResourceEditorInput, Boolean> getInputValidator() {
263         return INPUT_VALIDATOR;
264     }
265
266     protected void addDropParticipants(ICanvasContext ctx) {
267         ctx.getDefaultHintContext().setHint(Hints.KEY_ALLOWED_DRAG_ACTIONS, DnDConstants.ACTION_COPY);
268
269         ctx.add(new PopulateElementDropParticipant(synchronizer, getSite()));
270         ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5));
271     }
272
273     protected CopyPasteStrategy getCopyPasteStrategy() {
274                 try {
275                         CopyPasteStrategy cpStrategy = Simantics.getSession().syncRequest(new PossibleAdapter<CopyPasteStrategy>(getInputResource(), CopyPasteStrategy.class));
276                         if(cpStrategy != null) return cpStrategy;
277                 } catch (DatabaseException e) {
278                 }
279                 return getDefaultCopyPasteStrategy();
280     }
281     
282     protected CopyPasteStrategy getDefaultCopyPasteStrategy() {
283         return new DefaultCopyPasteStrategy();
284     }
285
286     protected CopyAdvisor getCopyAdvisor() {
287                 try {
288                         CopyAdvisor advisor = Simantics.getSession().syncRequest(new PossibleAdapter<CopyAdvisor>(getInputResource(), CopyAdvisor.class));
289                         if(advisor != null) return advisor;
290                 } catch (DatabaseException e) {
291                 }
292                 return new MappedElementCopyAdvisor(new ElementCopyAdvisor(), new ComponentCopyAdvisor());
293     }
294     
295     /**
296      * @param ctx
297      * TODO: change argument from CanvasContext to ICanvasContext
298      */
299     protected void addKeyBindingParticipants(CanvasContext ctx) {
300         //ctx.add( new KeyToCommand( CommandKeyBinding.DEFAULT_BINDINGS ) );
301         ctx.add(new DeleteHandler(statusLineManager));
302         ctx.add(new CopyPasteHandler(getCopyPasteStrategy(), statusLineManager).setWorkbenchSite(getEditorSite()));
303         ctx.add(new ConnectionCommandHandler());
304     }
305
306     protected void addPopupmenu(ICanvasContext ctx) {
307         ctx.add(popupMenuParticipant);
308     }
309
310     protected void addWorkbenchSelectionProvider(ICanvasContext ctx) {
311         if (selectionProvider != null)
312             ctx.add(selectionProvider);
313     }
314
315     protected void addViewManipulationParticipants(CanvasContext ctx) {
316         ctx.add(new PanZoomRotateHandler());
317         //ctx.add(new MousePanZoomInteractor());
318         //ctx.add(new MultitouchPanZoomRotateInteractor());
319         // ctx.add( new OrientationRestorer() );
320         ctx.add(new ZoomToAreaHandler());
321     }
322
323     protected void addDiagramParticipants(ICanvasContext ctx) {
324         ctx.add(new ZOrderHandler());
325         ctx.add(getPointerInteractor());
326         ctx.add(new ElementInteractor());
327         ctx.add(new Selection());
328         ctx.add(new DiagramParticipant());
329         ctx.add(new ElementPainter());
330         //ctx.add(new ElementHeartbeater());
331         //ctx.add(new ZoomTransitionParticipant(TransitionFunction.SIGMOID));
332         //ctx.add(new TooltipParticipant());
333         ctx.add(new TerminalTooltipParticipant());
334         ctx.add(new ConnectionCrossingsParticipant(getInputResource()));
335     }
336
337     protected void addPainterParticipants(ICanvasContext ctx) {
338         ctx.add(new RenderingQualityInteractor());
339         ctx.add(new TerminalPainter(true, true, false, true));
340         ctx.add(new DelayedBatchElementPainter(PickFilter.FILTER_MONITORS, 500, TimeUnit.MILLISECONDS));
341     }
342
343     protected void addStructureParticipants(ICanvasContext ctx) {
344         addWorkbenchSelectionProvider(ctx);
345         // Add visual browsing capabilities for structural models
346         ctx.add(new StructuralBrowsingHandler(getSite(), sessionContext, getResourceInput2()));
347         ctx.add(new LinkBrowsingHandler(getSite(), this, sessionContext));
348     }
349
350     /**
351      * Override to add any diagram/canvas participants to the canvas context
352      * initialized for the editor.
353      * 
354      * @param ctx
355      */
356     protected void addOtherParticipants(CanvasContext ctx) {
357     }
358
359     public static Set<String> defaultPropertyBrowseContexts = Collections.singleton(ProjectResource.URIs.ProjectBrowseContext);
360
361     protected Set<String> getPropertyPageContexts() {
362         try {
363             return BrowseContext.getBrowseContextClosure(Simantics.getSession(), defaultPropertyBrowseContexts);
364         } catch (DatabaseException e) {
365             ExceptionUtils.logAndShowError(Messages.DiagramViewer_FailedtoLoadModeled, e);
366             return defaultPropertyBrowseContexts;
367         }
368     }
369
370     protected IPropertyPage createPropertyPage(IWorkbenchPartSite site, Set<String> contexts) {
371         return new StandardPropertyPage(site, contexts);
372     }
373
374     protected String getPopupId() {
375         return "#ModelingDiagramPopup"; //$NON-NLS-1$
376     }
377
378     protected void getPreferences() {
379         this.diagramPreferences = DiagramPreferenceUtil.getPreferences();
380     }
381
382     protected void initSession() {
383         sessionContextProvider = Simantics.getSessionContextProvider();
384         sessionContext = sessionContextProvider.getSessionContext();
385     }
386
387     protected void readNames() {
388         String name = getEditorInput().getName();
389         host.doSetPartName(name);
390         host.doSetTitleToolTip(name);
391     }
392
393     class ChassisListener implements IChassisListener {
394         @Override
395         public void chassisClosed(ICanvasChassis sender) {
396             // Prevent deadlock while disposing which using syncExec would result in.
397             final ICanvasContext ctx = canvasContext;
398             ThreadUtils.asyncExec(ctx.getThreadAccess(), new Runnable() {
399                 @Override
400                 public void run() {
401                     if (ctx != null) {
402                         saveEditorState(ctx);
403                         ctx.getHintStack().removeKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener);
404                         ctx.getHintStack().removeKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener);
405                         final AWTChassis awtChassis = c.getAWTComponent();
406                         if (awtChassis != null)
407                             awtChassis.setCanvasContext(null);
408                         ctx.dispose();
409                     }
410
411                     if (sourceDiagramContainer != null) {
412                         sourceDiagramContainer.set(null);
413                         sourceDiagramContainer = null;
414                     }
415
416                     if (sourceDiagram != null)
417                         sourceDiagram.dispose();
418
419                     if(synchronizer != null) {
420                         synchronizer.dispose();
421                         // Let GC work.
422                         synchronizer = null;
423                     }
424
425                     if (runtimeDiagramManager != null) {
426                         runtimeDiagramManager.dispose();
427                         runtimeDiagramManager = null;
428                     }
429                 }
430             });
431             c.removeChassisListener(ChassisListener.this);
432         }
433     }
434
435     protected void createChassis(Composite parent) {
436         resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
437         c = new SWTChassis(parent, 0);
438
439         Object task = BEGIN("DV.precreateParticipants"); //$NON-NLS-1$
440         createCustomParticipants();
441         END(task);
442
443         c.populate(component -> {
444             if (!disposed) {
445                 c.addChassisListener(new ChassisListener());
446                 initializeCanvas();
447             }
448         });
449     }
450
451     protected void beforeSetCanvasContext(ICanvasContext canvasContext2) {
452     }
453
454     /**
455      * Invoke this only from the AWT thread.
456      * @param context
457      */
458     protected void setCanvasContext(ICanvasContext context) {
459         // Cannot directly invoke SWTChassis.setCanvasContext only because it
460         // needs to be invoked in the SWT thread and AWTChassis.setCanvasContext in the
461         // AWT thread, but directly invoking SWTChassis.setCanvasContext would call both
462         // in the SWT thread which would cause synchronous scheduling of AWT
463         // runnables which is always a potential source of deadlocks.
464         c.getAWTComponent().setCanvasContext(canvasContext);
465         swt.asyncExec(new Runnable() {
466             @Override
467             public void run() {
468                 if (!c.isDisposed())
469                     // For AWT, this is a no-operation.
470                     c.setCanvasContext(canvasContext);
471             }
472         });
473     }
474
475     public void createPartControl(Composite parent) {
476         //ProfileObserver.begin = System.nanoTime();
477         display = parent.getDisplay(); 
478         swt = SWTThread.getThreadAccess(display);
479         statusLineManager = getEditorSite().getActionBars().getStatusLineManager();
480
481         Object task = BEGIN("DV.initSession"); //$NON-NLS-1$
482         initSession();
483         END(task);
484
485         diagramResource = getInputResource();
486         readNames();
487         getPreferences();
488
489         try {
490             this.runtimeDiagramManager = RuntimeDiagramManager.track(sessionContext.getSession(), diagramResource, getEditorInput(), this);
491
492             // Create the canvas context here before finishing createPartControl
493             // to give DiagramViewerActionContributor a chance to work.
494             // The context can be created in SWT thread without scheduling
495             // to the context thread and having potential deadlocks.
496             IThreadWorkQueue thread = AWTThread.getThreadAccess();
497             this.canvasContext = new CanvasContext(thread);
498             this.canvasContext.setLocked(true);
499
500             task = BEGIN("DV.createChassis"); //$NON-NLS-1$
501             createChassis(parent);
502             END(task);
503         } catch (DatabaseException e) {
504             ErrorLogger.defaultLogError(e);
505         }
506     }
507
508
509         /**
510      * A method invoked before chassis construction for creating such
511      * {@link ICanvasParticipant}s that need to be constructed in the SWT
512      * thread.
513      * 
514      * Use it for creating any such canvas participants during the viewer 
515      * construction and add them to the {@link ICanvasContext} later on from
516      * the AWT thread.
517      */
518     protected void createCustomParticipants() {
519         if (SimanticsUI.isLinuxGTK()) {
520                 popupMenuParticipant = new SWTPopupMenuParticipantAwt(getSite(), c, display, getPopupId());
521         } else {
522                 popupMenuParticipant = new SWTPopupMenuParticipant(getSite(), c, display, getPopupId());
523         }
524     }
525
526     /**
527      * Invoke this only from the AWT thread.
528      */
529     protected void initializeCanvas() {
530         Object canvasInit = BEGIN("DV.canvasInitialization"); //$NON-NLS-1$
531
532         Object task = BEGIN("DV.createViewerCanvas"); //$NON-NLS-1$
533         initializeCanvasContext(canvasContext);
534         END(task);
535
536         beforeSetCanvasContext(canvasContext);
537         //FullscreenUtils.addFullScreenHandler(canvasContext, s, canvasProvider);
538
539         // Without this all diagram participants will crash like there's no tomorrow.
540         // Just trying to keep the behavior a bit more sane in case of
541         // errors instead of flooding the console with exceptions.
542         canvasContext.getDefaultHintContext().setHint(DiagramHints.KEY_DIAGRAM, Diagram.spawnNew(DiagramClass.DEFAULT));
543
544         // Changes in ruler/grid activity shall be written as
545         // workspace-persistent diagram-specific preferences.
546         canvasContext.getHintStack().addKeyHintListener(GridPainter.KEY_GRID_ENABLED, canvasHintListener);
547         canvasContext.getHintStack().addKeyHintListener(RulerPainter.KEY_RULER_ENABLED, canvasHintListener);
548
549         task = BEGIN("DV.setCanvasContext"); //$NON-NLS-1$
550         setCanvasContext(canvasContext);
551         END(task);
552
553         // Finish loading in a worker thread because it may be a time consuming
554         // process to load a large diagram and we don't want unnecessary AWT
555         // thread contention.
556         Job loadJob = new DiagramViewerLoadJob(this);
557         loadJob.schedule();
558
559         END(canvasInit);
560     }
561
562     protected void activateUiContexts(ContextUtil util) {
563         util.activate(DIAGRAMMING_CONTEXT);
564     }
565
566     /**
567      * @param monitor the progress monitor to use for reporting progress to the
568      *        user. It is the caller's responsibility to call done() on the
569      *        given monitor. Accepts <code>null</code>, indicating that no
570      *        progress should be reported and that the operation cannot be
571      *        cancelled.
572      */
573     protected void performActivation(IProgressMonitor monitor) {
574         SubMonitor progress = SubMonitor.convert(monitor, Messages.DiagramViewer_MonitorActivateMapping, 100);
575         IActivationManager activationManager = sessionContext.getSession().peekService(IActivationManager.class);
576         if (activationManager != null) {
577             activation = activationManager.activate(diagramResource);
578         }
579         progress.worked(100);
580     }
581
582     protected void onCreated() {
583     }
584
585     /**
586      * @param diagram
587      */
588     protected void scheduleZoomToFit(IDiagram diagram) {
589         if (diagram == null)
590             throw new IllegalStateException("diagram is null"); //$NON-NLS-1$
591
592         CanvasUtils.scheduleZoomToFit(swt, () -> disposed, canvasContext, diagram);
593     }
594
595     /**
596      * Subclasses may override but should always invoke super.
597      * 
598      * @param diagram
599      * @param initialHints
600      * @throws DatabaseException
601      */
602     protected void fillInitialDiagramHints(Resource diagram, IHintContext initialHints) throws DatabaseException {
603         IModelingRules modelingRules = sessionContext.getSession().syncRequest(
604                 DiagramRequests.getModelingRules(diagram, null));
605         if (modelingRules != null) {
606             initialHints.setHint(DiagramModelHints.KEY_MODELING_RULES, modelingRules);
607             initialHints.setHint(DiagramHints.CONNECTION_ADVISOR,
608                     getConnectionAdvisor(modelingRules, sessionContext.getSession()));
609         }
610
611         initialHints.setHint(SynchronizationHints.COPY_ADVISOR, getCopyAdvisor());
612         initialHints.setHint(DiagramHints.KEY_USE_CONNECTION_FLAGS, Boolean.TRUE);
613         initialHints.setHint(DiagramHints.KEY_ALLOW_CONNECTION_BRANCHING, Boolean.TRUE);
614         initialHints.setHint(DiagramHints.KEY_ALLOW_ROUTE_POINTS, Boolean.TRUE);
615     }
616
617     /**
618      * @param monitor the progress monitor to use for reporting progress to the
619      *        user. It is the caller's responsibility to call done() on the
620      *        given monitor. Accepts <code>null</code>, indicating that no
621      *        progress should be reported and that the operation cannot be
622      *        cancelled.
623      * @param r
624      * @return
625      * @throws DatabaseException
626      */
627     protected IDiagram loadDiagram(IProgressMonitor monitor, final Resource r) throws DatabaseException {
628         // Pre-load modeling rules and possibly other hints too since they are
629         // needed already while loading the diagram contents.
630         IHintContext initialHints = new HintContext();
631         fillInitialDiagramHints(r, initialHints);
632         IDiagram d = loadDiagram(monitor, r, initialHints);
633         return d;
634     }
635
636     /**
637      * @param monitor the progress monitor to use for reporting progress to the
638      *        user. It is the caller's responsibility to call done() on the
639      *        given monitor. Accepts <code>null</code>, indicating that no
640      *        progress should be reported and that the operation cannot be
641      *        cancelled.
642      * @param diagram
643      * @param initialHints
644      * @return
645      * @throws DatabaseException
646      */
647     protected IDiagram loadDiagram(IProgressMonitor monitor, Resource diagram, IHintContext initialHints) throws DatabaseException {
648         RuntimeDiagramManager rtdm = runtimeDiagramManager;
649         Resource runtimeDiagram = rtdm != null ? rtdm.getRuntimeDiagram() : null;
650         IDiagramLoader loader = synchronizer;
651         if (rtdm == null || runtimeDiagram == null || loader == null)
652             return null;
653         IDiagram d = sessionContext.getSession().syncRequest((Read<IDiagram>) graph -> {
654             IDiagram result = DiagramRequests.loadDiagram(monitor, getResourceInput2().getModel(null), diagram,
655                     runtimeDiagram, null, loader, initialHints).perform(graph);
656
657             // #399: Enable certain PropertyTester implementation without database transactions
658             ModelingResources MOD = ModelingResources.getInstance(graph);
659             Resource composite = graph.getPossibleObject(diagram, MOD.DiagramToComposite);
660             result.setHint(
661                     DiagramModelHints.KEY_DIAGRAM_RESOURCE_TYPE_URIS,
662                     graph.syncRequest(new TypeURIs(diagram)));
663             result.setHint(
664                     DiagramModelHints.KEY_MAPPED_COMPOSITE_RESOURCE_TYPE_URIS,
665                     composite != null ? graph.syncRequest(new TypeURIs(composite)) : Collections.emptySet());
666
667             Resource diagramSource = graph.syncRequest(
668                     new PossibleObject(diagram, MOD.HasDiagramSource),
669                     hasDiagramSourceListener = new HasDiagramSourceListener(sourceDiagramContainer));
670             ElementUtils.setOrRemoveHint(
671                     result, 
672                     DiagramModelHints.KEY_HAS_DIAGRAM_SOURCE,
673                     diagramSource);
674
675             return result;
676         });
677         return d;
678     }
679
680     protected void beforeSetDiagram(IDiagram diagram) {
681     }
682
683     protected PointerInteractor getPointerInteractor() {
684         return new PointerInteractor2(true, true, true, false, true, false, synchronizer.getElementClassProvider());
685     }
686
687     protected IConnectionAdvisor getConnectionAdvisor(IModelingRules modelingRules, Session session) {
688         return new ModelledConnectionAdvisor(modelingRules, sessionContext.getSession());
689     }
690
691     protected GraphToDiagramSynchronizer createSynchronizer(final ICanvasContext ctx, final ISessionContext sessionContext) {
692         try {
693             return sessionContext.getSession().syncRequest(new Read<GraphToDiagramSynchronizer>() {
694                 @Override
695                 public GraphToDiagramSynchronizer perform(ReadGraph graph) throws DatabaseException {
696                     GraphToDiagramSynchronizer sync = new GraphToDiagramSynchronizer(graph, ctx, createElementClassProvider(graph));
697                     initializeSynchronizationContext(graph, sync);
698                     return sync;
699                 }
700             });
701         } catch (DatabaseException e) {
702             throw new UnsupportedOperationException("Failed to initialize data model synchronizer", e); //$NON-NLS-1$
703         }
704     }
705
706     protected void initializeSynchronizationContext(ReadGraph graph, IModifiableSynchronizationContext context) {
707         context.set(ModelingSynchronizationHints.MODELING_RESOURCE, ModelingResources.getInstance(graph));
708     }
709
710     protected IElementClassProvider createElementClassProvider(ReadGraph graph) {
711         DiagramResource dr = DiagramResource.getInstance(graph);
712         return ElementClassProviders.mappedProvider(
713                 ElementClasses.CONNECTION, ConnectionClass.CLASS.newClassWith(new StaticObjectAdapter(dr.RouteGraphConnection)),
714                 ElementClasses.FLAG, FlagClassFactory.createFlagClass(dr.Flag, dr.Flag_Terminal)
715         );
716     }
717     
718     protected SimpleElementTransformHandler getTransformHandler() {
719         return new SimpleElementTransformHandler(true, true, true);
720     }
721
722     public void initializeCanvasContext(CanvasContext ctx) {
723         IHintContext h = ctx.getDefaultHintContext();
724
725         // The canvas context should not be painted until it is ready to avoid
726         // unnecessary visual glitches.
727         h.setHint(Hints.KEY_DISABLE_PAINTING, Boolean.TRUE);
728
729         Object task = BEGIN("createSynchronizer"); //$NON-NLS-1$
730         this.synchronizer = createSynchronizer(ctx, sessionContext);
731         END(task);
732
733         IContextService contextService = (IContextService) getSite().getService(IContextService.class);
734         contextUtil = new ContextUtil(contextService, swt);
735
736         // Support & Util Participants
737         ctx.add(new TransformUtil());
738         ctx.add(new MouseUtil());
739         ctx.add(new KeyUtil());
740         ctx.add(contextUtil);
741         ctx.add(new WorkbenchStatusLine(statusLineManager));
742         ctx.add(new CanvasGrab());
743         ctx.add(new SymbolUtil());
744         ctx.add(new TimeParticipant());
745         ctx.add(new CanvasBoundsParticipant());
746         ctx.add(new Notifications());
747
748         ctx.add(new SGFocusParticipant(c, DIAGRAMMING_CONTEXT));
749
750         // Debug participant(s)
751         // ctx.add( new PointerPainter() );
752         // ctx.add( new HandPainter() );
753         h.setHint(PointerPainter.KEY_PAINT_POINTER, true);
754
755         // Pan & Zoom & Rotate
756         addViewManipulationParticipants(ctx);
757
758         ctx.add(getTransformHandler());
759         ctx.add(new ExpandSelectionHandler(getEditorSite().getActionBars().getStatusLineManager()));
760
761         // Key bindings
762         addKeyBindingParticipants(ctx);
763
764         // Grid & Ruler & Background
765         addGridRulerBackgroundParticipants(ctx);
766
767         h.setHint(Hints.KEY_DISPLAY_PAGE, diagramPreferences.get(DiagramPreferences.P_DISPLAY_PAGE_SIZE));
768         h.setHint(Hints.KEY_DISPLAY_MARGINS, diagramPreferences.get(DiagramPreferences.P_DISPLAY_MARGINS));
769         ctx.add(new PageBorderParticipant());
770
771         // h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
772         // h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.LIGHT_GRAY);
773         h.setHint(Hints.KEY_GRID_COLOR, new Color(0.9f, 0.9f, 0.9f));
774         h.setHint(Hints.KEY_BACKGROUND_COLOR, Color.WHITE);
775         h.setHint(RulerPainter.KEY_RULER_BACKGROUND_COLOR, new Color(0.9f, 0.9f, 0.9f, 0.75f));
776         h.setHint(RulerPainter.KEY_RULER_TEXT_COLOR, Color.BLACK);
777
778         ////// Diagram Participants //////
779         addDiagramParticipants(ctx);
780         addPainterParticipants(ctx);
781
782         /////// D'n'D ///////
783         addDropParticipants(ctx);
784
785         h.setHint(ElementPainter.KEY_SELECTION_FRAME_COLOR, Color.MAGENTA);
786         h.setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
787
788         h.setHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT, 100000.0);
789         h.setHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT, 10.0);
790
791         Double snapResolution = diagramPreferences.get(DiagramPreferences.P_SNAP_GRID_SIZE);
792         this.snapAdvisor = new GridSnapAdvisor(snapResolution);
793         h.setHint(DiagramHints.SNAP_ADVISOR, this.snapAdvisor);
794         h.setHint(GridPainter.KEY_GRID_SIZE, snapResolution);
795         h.setHint(GridPainter.KEY_GRID_ENABLED, Boolean.FALSE);
796
797         // Workbench selection provider
798         addStructureParticipants(ctx);
799
800         // Pop-up menu
801         addPopupmenu(ctx);
802
803         // Load page frame description settings
804         loadPageSettings(ctx);
805
806         addOtherParticipants(ctx);
807
808         ctx.assertParticipantDependencies();
809         ctx.setLocked(false);
810     }
811
812     protected void addGridRulerBackgroundParticipants(CanvasContext ctx) {
813         ctx.add(new GridPainter());
814         ctx.add(new RulerPainter());
815         ctx.add(new BackgroundPainter());
816     }
817
818     protected void loadPageSettings(ICanvasContext ctx) {
819         DiagramDesc diagramDesc = null;
820
821         // load diagram page configuration
822         if (diagramResource != null) {
823             try {
824                 diagramDesc = sessionContext.getSession().syncRequest(DiagramRequests.getDiagramDesc(diagramResource));
825             } catch (DatabaseException e) {
826                 ErrorLogger.defaultLogError(e);
827             }
828         }
829
830         if (diagramDesc == null) {
831             // Take page description from the preferences if nothing else is available.
832             final DiagramDesc desc = diagramDesc = diagramPreferences.getDiagramDesc();
833
834             // Write page configuration to graph
835             sessionContext.getSession().asyncRequest(new WriteRequest() {
836                 @Override
837                 public void perform(WriteGraph graph) throws DatabaseException {
838                     if (graph.isImmutable(diagramResource))
839                         return;
840                     CommonDBUtils.selectClusterSet(graph, diagramResource);
841                     DiagramGraphUtil.setDiagramDesc(graph, diagramResource, desc);
842                 }
843             }, parameter -> {
844                 if (parameter != null)
845                     ErrorLogger.defaultLogError("Failed to write default diagram page description to database, see exception for details", parameter); //$NON-NLS-1$
846             });
847         }
848
849         setDiagramDesc(ctx, diagramDesc);
850
851         // Create a listener to react to page setting changes.
852         sessionContext.getSession().asyncRequest(DiagramRequests.getDiagramDesc(diagramResource),
853                 new ListenerDelegate<DiagramDesc>(this) {
854             @Override
855             public void execute(final DiagramDesc result) {
856                 if (result != null && canvasContext != null) {
857                     ThreadUtils.asyncExec(canvasContext.getThreadAccess(), new Runnable() {
858                         @Override
859                         public void run() {
860                             if (!disposed)
861                                 setDiagramDesc(canvasContext, result);
862                         }
863                     });
864                 }
865             }
866         });
867     }
868
869     protected void setDiagramDesc(ICanvasContext ctx, DiagramDesc diagramDesc) {
870         if (diagramDesc == null)
871             throw new NullPointerException("null diagram desc"); //$NON-NLS-1$
872
873         if (diagramDesc.equals(this.diagramDesc))
874             return;
875
876         this.diagramDesc = diagramDesc;
877         IHintContext hints = ctx.getDefaultHintContext();
878         hints.setHint(Hints.KEY_PAGE_DESC, diagramDesc.getPageDesc());
879         hints.setHint(Hints.KEY_DISPLAY_PAGE, diagramDesc.isPageBordersVisible());
880         hints.setHint(Hints.KEY_DISPLAY_MARGINS, diagramDesc.isMarginsVisible());
881         hints.setHint(GridPainter.KEY_GRID_ENABLED, diagramDesc.isGridVisible());
882         hints.setHint(RulerPainter.KEY_RULER_ENABLED, diagramDesc.isRulerVisible());
883         snapAdvisor.setResolution(diagramDesc.getGridSize());
884         hints.setHint(GridPainter.KEY_GRID_SIZE, diagramDesc.getGridSize());
885     }
886
887     /**
888      * Must be invoked from the AWT thread only.
889      * 
890      * @param state
891      * @param ctx
892      */
893     protected void applyEditorState(final EditorState state, final ICanvasContext ctx) {
894         final IDiagram diagram = ctx.getHintStack().getHint(DiagramHints.KEY_DIAGRAM);
895
896         if (state.viewTransform != null && state.viewTransform.getDeterminant() != 0) {
897             for (PanZoomRotateHandler h : ctx.getItemsByClass(PanZoomRotateHandler.class)) {
898                 h.setTransform(state.viewTransform);
899             }
900         }
901
902         if (diagram != null) {
903             if (state.viewTransform != null)
904                 diagram.removeHint(DiagramHints.KEY_INITIAL_ZOOM_TO_FIT);
905
906             if (state.toolMode != null)
907                 ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, state.toToolMode());
908             else
909                 ctx.getDefaultHintContext().setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
910
911             final Set<IElement> selected = DiagramEditorStates.toElements(state.selection, diagram);
912             if (!selected.isEmpty()) {
913                 for (Selection s : ctx.getItemsByClass(Selection.class)) {
914                     s.setSelection(0, selected);
915                 }
916             }
917         }
918     }
919
920     protected EditorState getSavedEditorState(ICanvasContext ctx) {
921         return DiagramEditorStates.toEditorState(ctx, true, true, true);
922     }
923
924     protected void saveEditorState(ICanvasContext ctx) {
925         DiagramEditorStates.saveEditorState(PREFERENCE_VIRTUAL_GRAPH, diagramResource, getSavedEditorState(ctx) , DiagramViewer.this);
926     }
927
928     private boolean firstFocus = true;
929
930     public void setFocus() {
931         
932         if(c != null) {
933                 c.setFocus();
934
935                 if (firstFocus) {
936                     // This is a workaround for using the symbol viewer in multi-page
937                     // editors which causes the first zoom-to-fit scheduling to happen
938                     // already before the controls have been laid out properly.
939                     firstFocus = false;
940                     firstTimeSetFocus();
941                 }
942                 
943         }
944         
945     }
946
947     protected void firstTimeSetFocus() {
948         // [Tuukka@2010-02-11]
949         // Removed since this is actually a workaround for multi-page editors.
950         //scheduleZoomToFit();
951     }
952
953     public void dispose() {
954         // Deactivate all contexts here because after this the context service
955         // in question will not be available.
956         if (contextUtil != null) {
957             contextUtil.deactivateAll();
958         }
959
960         disposed = true;
961
962         if (hasDiagramSourceListener != null) {
963             hasDiagramSourceListener.dispose();
964             hasDiagramSourceListener = null;
965         }
966
967         if (activation != null) {
968             activation.deactivate();
969             activation = null;
970         }
971
972         if (resourceManager != null) {
973                 resourceManager.dispose();
974                 resourceManager = null;
975         }
976         //super.dispose();
977     }
978
979     protected Resource getInputResource() {
980         return getResourceInput().getResource();
981     }
982
983     public IResourceEditorInput getResourceInput() {
984         return (IResourceEditorInput) getEditorInput();
985     }
986
987     public IResourceEditorInput2 getResourceInput2() {
988         return (IResourceEditorInput2) getEditorInput();
989     }
990
991     public void init(DiagramViewerHost _host, IEditorSite site, IEditorInput input, DataContainer<IDiagram> diagramContainer, WorkbenchSelectionProvider selectionProvider) {
992         if (!(input instanceof IResourceEditorInput))
993             throw new RuntimeException("Invalid input: must be IResourceEditorInput"); //$NON-NLS-1$
994
995         setHost(_host);
996         setSite(site);
997         setInput(input);
998         this.sourceDiagramContainer = diagramContainer;
999         this.selectionProvider = selectionProvider;
1000
1001         // Set initial part name according to the name given by IEditorInput
1002         host.doSetPartName(getEditorInput().getName());
1003
1004         Session session = Simantics.peekSession();
1005         if (session != null) {
1006             Supplier<Boolean> disposedCallback = () -> disposed;
1007             session.asyncRequest(
1008                     new TitleRequest(site.getId(), getResourceInput()),
1009                     new TitleUpdater(site.getShell().getDisplay(), host::doSetPartName, disposedCallback));
1010             session.asyncRequest(
1011                     new ToolTipRequest(site.getId(), getResourceInput()),
1012                     new TitleUpdater(site.getShell().getDisplay(), host::doSetTitleToolTip, disposedCallback));
1013         }
1014
1015         try {
1016             // Read previous editor state from the database
1017             editorState = DiagramEditorStates.readEditorState(getInputResource());
1018         } catch (DatabaseException e) {
1019             exception(e);
1020         }
1021     }
1022
1023     @SuppressWarnings("unchecked")
1024     @Override
1025     public <T> T getAdapter(Class<T> adapter) {
1026 //        System.out.println("diagramViewer getAdapter " + adapter);
1027         // Property view support
1028         if (adapter == IPropertyPage.class)
1029             return (T) createPropertyPage(getSite(), getPropertyPageContexts());
1030         // Provide symbols for the editor
1031         if (adapter == SymbolProviderFactory.class) {
1032             try {
1033                 return (T) DiagramTypeUtils.readSymbolProviderFactory(sessionContext.getSession(), diagramResource);
1034             } catch (DatabaseException e) {
1035                 ErrorLogger.defaultLogError(getClass() + " failed to adapt to SymbolProviderFactory, see exception for details.", e); //$NON-NLS-1$
1036                 return null;
1037             }
1038         }
1039         // Outline view support
1040         if (adapter == IContentOutlinePage.class)
1041             return (T) new DiagramOutlinePage(sessionContextProvider, getResourceInput2());
1042         // Role view support
1043         if (adapter == ILayersViewPage.class)
1044             return (T) new DiagramLayersPage(sourceDiagram, canvasContext);
1045         // Support external steering of the diagram canvas, zooming etc.
1046         if (adapter == ICanvasContext.class)
1047             return (T) canvasContext;
1048         // Support scene graph debugger view
1049         if (adapter == INode.class) {
1050             if (canvasContext != null) {
1051                 INode node = canvasContext.getCanvasNode();
1052                 if (node != null)
1053                     return (T) NodeUtil.getRootNode(node);
1054             }
1055             return null;
1056         }
1057         // Support retrieval of the current diagram.
1058         if (adapter == IDiagram.class)
1059             return (T) sourceDiagram;
1060         // Why is this here ??
1061         if (adapter == Session.class)
1062             return (T) sessionContext.getSession();
1063         if(adapter == RuntimeDiagramManager.class)
1064             return (T) runtimeDiagramManager;
1065         if (adapter == Resource.class)
1066             return (T) getRuntime();
1067         if (adapter == ICanvasChassis.class)
1068             return (T) c;
1069
1070         return null;
1071     }
1072
1073     //-------------------------------------------------------------------------
1074     // Profiling aid
1075
1076     protected static Object BEGIN(String name) {
1077         if (PROFILE) {
1078             //return ThreadLog.BEGIN(name);
1079         }
1080         return null;
1081     }
1082
1083     protected static void END(Object task) {
1084         if (PROFILE) {
1085             //((Task) task).end();
1086         }
1087     }
1088
1089     //-------------------------------------------------------------------------
1090     // implement ListenerSupport
1091
1092     @Override
1093     public void exception(Throwable t) {
1094         ErrorLogger.defaultLogError(t);
1095     }
1096
1097     @Override
1098     public boolean isDisposed() {
1099         return disposed;
1100     }
1101
1102     protected void collectGarbage() {
1103         SessionGarbageCollectorJob.getInstance().schedule(0);
1104         AWTThread.getThreadAccess().asyncExec(new Runnable() {
1105             @Override
1106             public void run() {
1107                 //System.out.println("BEFORE CLEAR: " + SVGCache.getSVGUniverse().report());
1108                 SVGCache.getSVGUniverse().clearUnreferenced();
1109                 //System.out.println("AFTER CLEAR: " + SVGCache.getSVGUniverse().report());
1110             }
1111         });
1112     }
1113
1114     //-------------------------------------------------------------------------
1115     // Listener for certain canvas context hint changes
1116
1117     IHintListener canvasHintListener = new HintListenerAdapter() {
1118         @Override
1119         public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
1120             if (key == GridPainter.KEY_GRID_ENABLED) {
1121                 boolean v = Boolean.TRUE.equals(newValue);
1122                 if (diagramDesc.isGridVisible() != v)
1123                     setGlobalPreference(DiagramResource.URIs.DisplayGrid, v);
1124             } else if (key == RulerPainter.KEY_RULER_ENABLED) {
1125                 boolean v = Boolean.TRUE.equals(newValue);
1126                 if (diagramDesc.isRulerVisible() != v)
1127                     setGlobalPreference(DiagramResource.URIs.DisplayRuler, v);
1128             }
1129         }
1130     };
1131
1132     private <T> void setGlobalPreference(final String preferenceURI, boolean value) {
1133         TagUtil.execute(Simantics.getSession(), PREFERENCE_VIRTUAL_GRAPH, preferenceURI, value, Simantics.getProjectResource());
1134     }
1135
1136     /*
1137      * --------------------------------------------------------------------
1138      * Changes related to removal of EditorPart extension from here on down
1139      * --------------------------------------------------------------------
1140      */
1141
1142     private IWorkbenchPartSite partSite;
1143     private DiagramViewerHost host;
1144     private IEditorInput editorInput = null;
1145
1146     /* (non-Javadoc)
1147      * Method declared on IWorkbenchPart.
1148      */
1149     public IWorkbenchPartSite getSite() {
1150         return partSite;
1151     }
1152
1153     public IEditorSite getEditorSite() {
1154         return (IEditorSite) getSite();
1155     }
1156
1157     public IEditorInput getEditorInput() {
1158         return editorInput;
1159     }
1160
1161 //    protected void setPartName(String partName) {
1162 ////        if (compatibilityTitleListener != null) {
1163 ////            removePropertyListener(compatibilityTitleListener);
1164 ////            compatibilityTitleListener = null;
1165 ////        }
1166 ////
1167 ////        super.setPartName(partName);
1168 //    }
1169  
1170 //    protected void setTitleToolTip(String toolTip) {
1171 ////        toolTip = Util.safeString(toolTip);
1172 ////        //Do not send changes if they are the same
1173 ////        if (Util.equals(this.toolTip, toolTip)) {
1174 ////                    return;
1175 ////            }
1176 ////        this.toolTip = toolTip;
1177 ////        firePropertyChange(IWorkbenchPart.PROP_TITLE);
1178 //    }
1179
1180     protected void setHost(DiagramViewerHost host) {
1181         this.host = host;
1182     }
1183
1184     protected void setSite(IWorkbenchPartSite site) {
1185         this.partSite = site;
1186     }
1187
1188     protected void setInput(IEditorInput input) {
1189         Assert.isLegal(input != null);
1190         editorInput = input;
1191     }
1192
1193     public Composite getComposite() {
1194         return c;
1195     }
1196
1197 }