]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.views.swt/src/org/simantics/views/swt/ModelledView.java
Usability fixes for GraphExplorerImpl -related WB selection propagation
[simantics/platform.git] / bundles / org.simantics.views.swt / src / org / simantics / views / swt / ModelledView.java
index edbba6af677b4d643306b23ed0d0bcc134f729cc..92396f421de9d37e0b9673d8989d3ca894db64eb 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- *     VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.views.swt;\r
-\r
-import org.eclipse.core.runtime.IConfigurationElement;\r
-import org.eclipse.jface.layout.GridDataFactory;\r
-import org.eclipse.jface.layout.GridLayoutFactory;\r
-import org.eclipse.jface.viewers.ISelection;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.eclipse.ui.IPartListener2;\r
-import org.eclipse.ui.IWorkbenchPage;\r
-import org.eclipse.ui.IWorkbenchPart;\r
-import org.eclipse.ui.IWorkbenchPartReference;\r
-import org.eclipse.ui.IWorkbenchSite;\r
-import org.simantics.Simantics;\r
-import org.simantics.browsing.ui.common.ErrorLogger;\r
-import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport;\r
-import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.common.request.WriteResultRequest;\r
-import org.simantics.db.common.utils.Logger;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.ServiceNotFoundException;\r
-import org.simantics.db.layer0.util.RemoverUtil;\r
-import org.simantics.db.layer0.variable.Variable;\r
-import org.simantics.db.management.ISessionContext;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.scenegraph.ontology.ScenegraphResources;\r
-import org.simantics.scl.runtime.function.Function1;\r
-import org.simantics.ui.workbench.IPropertyPage;\r
-import org.simantics.utils.ui.jface.ActiveSelectionProvider;\r
-import org.simantics.views.swt.client.base.SWTRoot;\r
-\r
-/**\r
- * To use this class, first model your view contents in .pgraph files according\r
- * to the Browsing.pgraph ontology. After that there are two ways to put your\r
- * configuration to use by defining a new view extension:\r
- * <ol>\r
- * <li>Set view extension class to\r
- * <code>org.simantics.browsing.ui.swt.ModelledView:configurationURI=ConfigURI</code>\r
- * , where ConfigURI is the URI of your view configuration.</li>\r
- * <li>Extend this class and override at least {@link #configurationURI()} to\r
- * define the URI from which the configuration for the view is found. Set view\r
- * extension class to the created class.</li>\r
- * </ol>\r
- * \r
- * @author Antti Villberg\r
- */\r
-public class ModelledView extends SimanticsView implements IPartListener2 {\r
-\r
-    public static final int TIME_BEFORE_DISPOSE_WHEN_HIDDEN = 30000; // ms\r
-    \r
-    private static final boolean           DEBUG             = false;\r
-\r
-    protected Resource                     runtime;\r
-    protected String                       configurationURI;\r
-\r
-    protected SWTRoot                      root;\r
-\r
-    protected Variable                     viewVariable;\r
-\r
-    protected Function1<Variable, Boolean> onInputChanged    = null;\r
-\r
-    protected SWTViewLoaderProcess         loader;\r
-\r
-    protected Composite                    body;\r
-\r
-    protected Composite                    container;\r
-\r
-    protected ModelledSupport              support;\r
-\r
-    ActiveSelectionProvider                selectionProvider = new ActiveSelectionProvider() {\r
-        @Override\r
-        public void setSelection(ISelection selection) {\r
-            super.setSelection(selection);\r
-        }\r
-    };\r
-\r
-    protected String configurationURI() {\r
-        return configurationURI;\r
-    }\r
-\r
-    @Override\r
-    protected WidgetSupportImpl createSupport() {\r
-\r
-        try {\r
-            runtime = Simantics.getSession().sync(\r
-                    new WriteResultRequest<Resource>(Simantics.getSession().getService(VirtualGraph.class)) {\r
-                        @Override\r
-                        public Resource perform(WriteGraph graph) throws DatabaseException {\r
-                            Layer0 L0 = Layer0.getInstance(graph);\r
-                            ScenegraphResources SG = ScenegraphResources.getInstance(graph);\r
-                            Resource runtime = graph.newResource();\r
-                            graph.claim(runtime, L0.InstanceOf, null, SG.Runtime);\r
-                            return runtime;\r
-                        }\r
-                    });\r
-        } catch (ServiceNotFoundException e) {\r
-            Logger.defaultLogError(e);\r
-        } catch (DatabaseException e) {\r
-            Logger.defaultLogError(e);\r
-        }\r
-\r
-        support = new ModelledSupport(this);\r
-\r
-        return support;\r
-\r
-    }\r
-\r
-    public void fireInput() {\r
-        if (onInputChanged != null)\r
-            onInputChanged.apply(viewVariable);\r
-    }\r
-\r
-    @Override\r
-    public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {\r
-        super.setInitializationData(cfig, propertyName, data);\r
-        if (data instanceof String) {\r
-            String[] parameters = ((String) data).split(";");\r
-\r
-            for (String parameter : parameters) {\r
-                String[] keyValue = parameter.split("=");\r
-                if (keyValue.length > 2) {\r
-                    ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: "\r
-                            + data, null);\r
-                    continue;\r
-                }\r
-                String key = keyValue[0];\r
-                String value = keyValue.length > 1 ? keyValue[1] : "";\r
-\r
-                if ("configurationURI".equals(key)) {\r
-                    configurationURI = value;\r
-                }\r
-            }\r
-        }\r
-    }\r
-\r
-    protected void doCreateControls(boolean load) {\r
-        if (DEBUG)\r
-            System.out.println(this + " doCreateControls(" + load + ")");\r
-\r
-        if (container == null) {\r
-            GridLayoutFactory.fillDefaults().applyTo(body);\r
-            container = new Composite(body, SWT.NONE);\r
-            GridDataFactory.fillDefaults().grab(true, true).applyTo(container);\r
-            GridLayoutFactory.fillDefaults().applyTo(container);\r
-        }\r
-\r
-        if (load) {\r
-\r
-            try {\r
-\r
-                loader = new SWTViewLoaderProcess(this, getSite(), getClass().getSimpleName());\r
-\r
-                viewVariable = loader.getVariable(Simantics.getSession(), configurationURI(), runtime);\r
-\r
-                onInputChanged = Simantics.getSession().syncRequest(new Read<Function1<Variable, Boolean>>() {\r
-\r
-                    @Override\r
-                    public Function1<Variable, Boolean> perform(ReadGraph graph) throws DatabaseException {\r
-                        return viewVariable.getPossiblePropertyValue(graph, "onInputChanged");\r
-                    }\r
-\r
-                });\r
-\r
-                root = loader.load(Simantics.getSession(), viewVariable);\r
-                root.createControls(container);\r
-                root.getControl().addListener(SWT.Dispose, new Listener() {\r
-\r
-                    final SWTViewLoaderProcess oldLoader = ModelledView.this.loader;\r
-\r
-                    @Override\r
-                    public void handleEvent(Event event) {\r
-\r
-                        if (oldLoader != null && !oldLoader.isDisposed())\r
-                            oldLoader.dispose();\r
-\r
-                    }\r
-\r
-                });\r
-\r
-                body.layout(true);\r
-\r
-                getSite().setSelectionProvider(selectionProvider);\r
-\r
-            } catch (DatabaseException e) {\r
-\r
-                e.printStackTrace();\r
-                Logger.defaultLogError(e);\r
-\r
-            }\r
-\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) {\r
-        this.body = body;\r
-\r
-        // Only create controls if the part is TRULY visible.\r
-        // Fast view parts seem to cause calls to createPartControl even\r
-        // when the part is hidden in reality\r
-        boolean visible = site.getPage().isPartVisible(this);\r
-        if (DEBUG)\r
-            System.out.println(this + ": createControls( visible=" + site.getPage().isPartVisible(this) + " )");\r
-        doCreateControls(true);\r
-\r
-        getSite().setSelectionProvider(selectionProvider);\r
-        getSite().getPage().addPartListener(this);\r
-\r
-    }\r
-\r
-    protected void inputChanged(IWorkbenchPart provider, Object input) {\r
-        // Do not accept selections from self\r
-        if (provider == this)\r
-            return;\r
-        applySessionContext(getSessionContext());\r
-    }\r
-\r
-    @Override\r
-    public void setFocus() {\r
-        if (root != null && !root.isNodeDisposed())\r
-            root.setFocus();\r
-    }\r
-\r
-    public void setVisible(boolean value) {\r
-        if (root != null && !root.isNodeDisposed())\r
-            root.setVisible(value);\r
-    }\r
-\r
-    @Override\r
-    public void dispose() {\r
-\r
-        disposeRuntime(runtime);\r
-\r
-        IWorkbenchSite site = getSite();\r
-        if (site != null) {\r
-            IWorkbenchPage page = site.getPage();\r
-            if (page != null) {\r
-                page.removePartListener(this);\r
-            }\r
-        }\r
-\r
-        if (root != null) {\r
-            root.cleanup();\r
-            root = null;\r
-        }\r
-        if (loader != null) {\r
-            loader.dispose();\r
-            loader = null;\r
-        }\r
-        if (support != null) {\r
-            support.dispose();\r
-            support = null;\r
-        }\r
-\r
-        super.dispose();\r
-        \r
-    }\r
-\r
-    protected void disposeRuntime(Resource runtime) {\r
-        final Resource rt = this.runtime;\r
-        this.runtime = null;\r
-        if (rt == null)\r
-            return;\r
-\r
-        try {\r
-            Simantics.getSession().sync(new WriteRequest(Simantics.getSession().getService(VirtualGraph.class)) {\r
-                @Override\r
-                public void perform(WriteGraph graph) throws DatabaseException {\r
-                    RemoverUtil.remove(graph, rt);\r
-                }\r
-            });\r
-        } catch (ServiceNotFoundException e) {\r
-            Logger.defaultLogError(e);\r
-        } catch (DatabaseException e) {\r
-            Logger.defaultLogError(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partActivated(IWorkbenchPartReference partRef) {\r
-        if (DEBUG) {\r
-            IWorkbenchPart part = partRef.getPart(false);\r
-            if (this.equals(part)) {\r
-                System.out.println(this + ": ACTIVATED ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partBroughtToTop(IWorkbenchPartReference partRef) {\r
-        if (DEBUG) {\r
-            IWorkbenchPart part = partRef.getPart(false);\r
-            if (this.equals(part)) {\r
-                System.out.println(this + ": BROUGHT TO TOP ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partClosed(IWorkbenchPartReference partRef) {\r
-        if (DEBUG) {\r
-            IWorkbenchPart part = partRef.getPart(false);\r
-            if (this.equals(part)) {\r
-                System.out.println(this + ": CLOSED ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partDeactivated(IWorkbenchPartReference partRef) {\r
-        IWorkbenchPart part = partRef.getPart(false);\r
-        if (this.equals(part)) {\r
-            if (DEBUG)\r
-                System.out.println(this + ": DEACTIVATED ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            // clearExisting();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partOpened(IWorkbenchPartReference partRef) {\r
-        if (DEBUG) {\r
-            IWorkbenchPart part = partRef.getPart(false);\r
-            if (this.equals(part)) {\r
-                System.out.println(this + ": OPENED ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            }\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partInputChanged(IWorkbenchPartReference partRef) {\r
-    }\r
-\r
-    @Override\r
-    public void partHidden(IWorkbenchPartReference partRef) {\r
-        IWorkbenchPart part = partRef.getPart(false);\r
-        if (this.equals(part)) {\r
-            if (DEBUG)\r
-                System.out.println(this + ": HID ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            clearExisting();\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void partVisible(IWorkbenchPartReference partRef) {\r
-        IWorkbenchPart part = partRef.getPart(false);\r
-        if (this.equals(part)) {\r
-            if (DEBUG)\r
-                System.out.println(this + ": MADE VISIBLE ( loader=" + loader + ", visible="\r
-                        + getSite().getPage().isPartVisible(part) + " )");\r
-            createControlsIfNecessary(false);\r
-        }\r
-    }\r
-\r
-    private void createControlsIfNecessary(boolean forceContainerRepaint) {\r
-        // Cancel potential dispose before creating controls\r
-        reallyClearExisting = false;\r
-        if (loader == null) {\r
-            doCreateControls(true);\r
-            body.layout(true);\r
-\r
-            if (forceContainerRepaint) {\r
-                container.layout(true);\r
-                Point size = container.getSize();\r
-                container.redraw(0, 0, size.x, size.y, true);\r
-                container.update();\r
-            }\r
-        }\r
-    }\r
-    \r
-    // Can be used to cancel already scheduled dispose\r
-    volatile boolean reallyClearExisting = false;\r
-    \r
-    Runnable clearExisting = new Runnable() {\r
-\r
-        @Override\r
-        public void run() {\r
-            if(!reallyClearExisting)\r
-                return;\r
-            viewVariable = null;\r
-            onInputChanged = null;\r
-\r
-            if (loader != null) {\r
-                loader.dispose();\r
-                loader = null;\r
-            }\r
-            if (container != null) {\r
-\r
-                final Composite oldContainer = container;\r
-                Display.getCurrent().asyncExec(new Runnable() {\r
-                    @Override\r
-                    public void run() {\r
-                        if (!oldContainer.isDisposed())\r
-                            oldContainer.dispose();\r
-                    }\r
-                });\r
-\r
-                if (!container.isDisposed())\r
-                    GridDataFactory.fillDefaults().exclude(true).applyTo(container);\r
-                container = null;\r
-\r
-            }\r
-\r
-            root = null;\r
-        }\r
-        \r
-    };\r
-\r
-    private void clearExisting() {\r
-        Display.getCurrent().timerExec(TIME_BEFORE_DISPOSE_WHEN_HIDDEN, clearExisting);\r
-        \r
-        // Do this after scheduling the runnable, because otherwise already scheduled runnable could \r
-        // get the flag.\r
-        reallyClearExisting = true;\r
-    }\r
-\r
-    @Override\r
-    protected IPropertyPage getPropertyPage() {\r
-       return null;\r
-    }\r
-    \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.views.swt;
+
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.IWorkbenchSite;
+import org.simantics.Simantics;
+import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport;
+import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.request.WriteRequest;
+import org.simantics.db.common.request.WriteResultRequest;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.ServiceNotFoundException;
+import org.simantics.db.layer0.util.RemoverUtil;
+import org.simantics.db.layer0.variable.Variable;
+import org.simantics.db.management.ISessionContext;
+import org.simantics.db.request.Read;
+import org.simantics.layer0.Layer0;
+import org.simantics.scenegraph.ontology.ScenegraphResources;
+import org.simantics.scl.runtime.function.Function1;
+import org.simantics.ui.workbench.IPropertyPage;
+import org.simantics.utils.ui.ErrorLogger;
+import org.simantics.utils.ui.jface.BasePostSelectionProvider;
+import org.simantics.views.swt.client.base.SWTRoot;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * To use this class, first model your view contents in .pgraph files according
+ * to the Browsing.pgraph ontology. After that there are two ways to put your
+ * configuration to use by defining a new view extension:
+ * <ol>
+ * <li>Set view extension class to
+ * <code>org.simantics.browsing.ui.swt.ModelledView:configurationURI=ConfigURI</code>
+ * , where ConfigURI is the URI of your view configuration.</li>
+ * <li>Extend this class and override at least {@link #configurationURI()} to
+ * define the URI from which the configuration for the view is found. Set view
+ * extension class to the created class.</li>
+ * </ol>
+ * 
+ * @author Antti Villberg
+ */
+public class ModelledView extends SimanticsView implements IPartListener2 {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ModelledView.class);
+
+    public static final int TIME_BEFORE_DISPOSE_WHEN_HIDDEN = 30000; // ms
+    
+    private static final boolean           DEBUG             = false;
+
+    protected Resource                     runtime;
+    protected String                       configurationURI;
+
+    protected SWTRoot                      root;
+
+    protected Variable                     viewVariable;
+
+    protected Function1<Variable, Boolean> onInputChanged    = null;
+
+    protected SWTViewLoaderProcess         loader;
+
+    protected Composite                    body;
+
+    protected Composite                    container;
+
+    protected ModelledSupport              support;
+
+    private BasePostSelectionProvider      selectionProvider = new BasePostSelectionProvider() {
+        @Override
+        public void setSelection(ISelection selection) {
+            super.setAndFireNonEqualSelection(selection);
+            super.firePostSelection(selection);
+        }
+    };
+
+    protected String configurationURI() {
+        return configurationURI;
+    }
+
+    @Override
+    protected WidgetSupportImpl createSupport() {
+        try {
+            runtime = Simantics.getSession().sync(
+                    new WriteResultRequest<Resource>(Simantics.getSession().getService(VirtualGraph.class)) {
+                        @Override
+                        public Resource perform(WriteGraph graph) throws DatabaseException {
+                            Layer0 L0 = Layer0.getInstance(graph);
+                            ScenegraphResources SG = ScenegraphResources.getInstance(graph);
+                            Resource runtime = graph.newResource();
+                            graph.claim(runtime, L0.InstanceOf, null, SG.Runtime);
+                            return runtime;
+                        }
+                    });
+        } catch (ServiceNotFoundException | DatabaseException e) {
+            LOGGER.error("Failed to initialize modelled view database runtime support structures", e);
+        }
+
+        support = new ModelledSupport(this);
+
+        return support;
+    }
+
+    public void fireInput() {
+        if (onInputChanged != null)
+            onInputChanged.apply(viewVariable);
+    }
+
+    @Override
+    public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
+        super.setInitializationData(cfig, propertyName, data);
+        if (data instanceof String) {
+            String[] parameters = ((String) data).split(";");
+
+            for (String parameter : parameters) {
+                String[] keyValue = parameter.split("=");
+                if (keyValue.length > 2) {
+                    ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: "
+                            + data, null);
+                    continue;
+                }
+                String key = keyValue[0];
+                String value = keyValue.length > 1 ? keyValue[1] : "";
+
+                if ("configurationURI".equals(key)) {
+                    configurationURI = value;
+                }
+            }
+        }
+    }
+
+    protected void doCreateControls(boolean load) {
+        if (DEBUG)
+            System.out.println(this + " doCreateControls(" + load + ")");
+
+        if (container == null) {
+            GridLayoutFactory.fillDefaults().applyTo(body);
+            container = new Composite(body, SWT.NONE);
+            GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
+            GridLayoutFactory.fillDefaults().applyTo(container);
+        }
+
+        if (load) {
+            try {
+                loader = new SWTViewLoaderProcess(this, getSite(), getClass().getSimpleName());
+
+                viewVariable = loader.getVariable(Simantics.getSession(), configurationURI(), runtime);
+
+                onInputChanged = Simantics.getSession().syncRequest(new Read<Function1<Variable, Boolean>>() {
+
+                    @Override
+                    public Function1<Variable, Boolean> perform(ReadGraph graph) throws DatabaseException {
+                        return viewVariable.getPossiblePropertyValue(graph, "onInputChanged");
+                    }
+
+                });
+
+                root = loader.load(Simantics.getSession(), viewVariable);
+                root.createControls(container);
+                root.getControl().addListener(SWT.Dispose, new Listener() {
+
+                    final SWTViewLoaderProcess oldLoader = ModelledView.this.loader;
+
+                    @Override
+                    public void handleEvent(Event event) {
+
+                        if (oldLoader != null && !oldLoader.isDisposed())
+                            oldLoader.dispose();
+
+                    }
+
+                });
+
+                body.layout(true);
+
+            } catch (DatabaseException e) {
+                LOGGER.error("Failed to create modelled SWT view controls", e);
+            }
+        }
+    }
+
+    @Override
+    protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) {
+        this.body = body;
+
+        // Only create controls if the part is TRULY visible.
+        // Fast view parts seem to cause calls to createPartControl even
+        // when the part is hidden in reality
+        if (DEBUG)
+            System.out.println(this + ": createControls( visible=" + site.getPage().isPartVisible(this) + " )");
+        doCreateControls(true);
+
+        getSite().setSelectionProvider(selectionProvider);
+        getSite().getPage().addPartListener(this);
+    }
+
+    protected void inputChanged(IWorkbenchPart provider, Object input) {
+        // Do not accept selections from self
+        if (provider == this)
+            return;
+        applySessionContext(getSessionContext());
+    }
+
+    @Override
+    public void setFocus() {
+        if (root != null && !root.isNodeDisposed())
+            root.setFocus();
+    }
+
+    public void setVisible(boolean value) {
+        if (root != null && !root.isNodeDisposed())
+            root.setVisible(value);
+    }
+
+    @Override
+    public void dispose() {
+
+        disposeRuntime(runtime);
+
+        IWorkbenchSite site = getSite();
+        if (site != null) {
+            IWorkbenchPage page = site.getPage();
+            if (page != null) {
+                page.removePartListener(this);
+            }
+        }
+
+        if (root != null) {
+            root.cleanup();
+            root = null;
+        }
+        if (loader != null) {
+            loader.dispose();
+            loader = null;
+        }
+        if (support != null) {
+            support.dispose();
+            support = null;
+        }
+
+        super.dispose();
+        
+    }
+
+    protected void disposeRuntime(Resource runtime) {
+        final Resource rt = this.runtime;
+        this.runtime = null;
+        if (rt == null)
+            return;
+
+        try {
+            Simantics.getSession().sync(new WriteRequest(Simantics.getSession().getService(VirtualGraph.class)) {
+                @Override
+                public void perform(WriteGraph graph) throws DatabaseException {
+                    RemoverUtil.remove(graph, rt);
+                }
+            });
+        } catch (ServiceNotFoundException | DatabaseException e) {
+            LOGGER.error("Failed to dispose of modelled view database runtime support structures", e);
+        }
+    }
+
+    @Override
+    public void partActivated(IWorkbenchPartReference partRef) {
+        if (DEBUG) {
+            IWorkbenchPart part = partRef.getPart(false);
+            if (this.equals(part)) {
+                System.out.println(this + ": ACTIVATED ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            }
+        }
+    }
+
+    @Override
+    public void partBroughtToTop(IWorkbenchPartReference partRef) {
+        if (DEBUG) {
+            IWorkbenchPart part = partRef.getPart(false);
+            if (this.equals(part)) {
+                System.out.println(this + ": BROUGHT TO TOP ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            }
+        }
+    }
+
+    @Override
+    public void partClosed(IWorkbenchPartReference partRef) {
+        if (DEBUG) {
+            IWorkbenchPart part = partRef.getPart(false);
+            if (this.equals(part)) {
+                System.out.println(this + ": CLOSED ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            }
+        }
+    }
+
+    @Override
+    public void partDeactivated(IWorkbenchPartReference partRef) {
+        IWorkbenchPart part = partRef.getPart(false);
+        if (this.equals(part)) {
+            if (DEBUG)
+                System.out.println(this + ": DEACTIVATED ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            // clearExisting();
+        }
+    }
+
+    @Override
+    public void partOpened(IWorkbenchPartReference partRef) {
+        if (DEBUG) {
+            IWorkbenchPart part = partRef.getPart(false);
+            if (this.equals(part)) {
+                System.out.println(this + ": OPENED ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            }
+        }
+    }
+
+    @Override
+    public void partInputChanged(IWorkbenchPartReference partRef) {
+    }
+
+    @Override
+    public void partHidden(IWorkbenchPartReference partRef) {
+        IWorkbenchPart part = partRef.getPart(false);
+        if (this.equals(part)) {
+            if (DEBUG)
+                System.out.println(this + ": HID ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            clearExisting();
+        }
+    }
+
+    @Override
+    public void partVisible(IWorkbenchPartReference partRef) {
+        IWorkbenchPart part = partRef.getPart(false);
+        if (this.equals(part)) {
+            if (DEBUG)
+                System.out.println(this + ": MADE VISIBLE ( loader=" + loader + ", visible="
+                        + getSite().getPage().isPartVisible(part) + " )");
+            createControlsIfNecessary(false);
+        }
+    }
+
+    private void createControlsIfNecessary(boolean forceContainerRepaint) {
+        // Cancel potential dispose before creating controls
+        reallyClearExisting = false;
+        if (loader == null) {
+            doCreateControls(true);
+            body.layout(true);
+
+            if (forceContainerRepaint) {
+                container.layout(true);
+                Point size = container.getSize();
+                container.redraw(0, 0, size.x, size.y, true);
+                container.update();
+            }
+        }
+    }
+
+    // Can be used to cancel already scheduled dispose
+    volatile boolean reallyClearExisting = false;
+
+    Runnable clearExisting = new Runnable() {
+
+        @Override
+        public void run() {
+            if(!reallyClearExisting)
+                return;
+            viewVariable = null;
+            onInputChanged = null;
+
+            if (loader != null) {
+                loader.dispose();
+                loader = null;
+            }
+            if (container != null) {
+
+                final Composite oldContainer = container;
+                Display.getCurrent().asyncExec(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!oldContainer.isDisposed())
+                            oldContainer.dispose();
+                    }
+                });
+
+                if (!container.isDisposed())
+                    GridDataFactory.fillDefaults().exclude(true).applyTo(container);
+                container = null;
+
+            }
+
+            root = null;
+        }
+        
+    };
+
+    private void clearExisting() {
+        Display.getCurrent().timerExec(TIME_BEFORE_DISPOSE_WHEN_HIDDEN, clearExisting);
+        
+        // Do this after scheduling the runnable, because otherwise already scheduled runnable could 
+        // get the flag.
+        reallyClearExisting = true;
+    }
+
+    @Override
+    protected IPropertyPage getPropertyPage() {
+       return null;
+    }
+
+}