/******************************************************************************* * 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.common.ErrorLogger; 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.RuntimeDatabaseException; 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.jface.ActiveSelectionProvider; 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: *
    *
  1. Set view extension class to * org.simantics.browsing.ui.swt.ModelledView:configurationURI=ConfigURI * , where ConfigURI is the URI of your view configuration.
  2. *
  3. 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.
  4. *
* * @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 onInputChanged = null; protected SWTViewLoaderProcess loader; protected Composite body; protected Composite container; protected ModelledSupport support; ActiveSelectionProvider selectionProvider = new ActiveSelectionProvider() { @Override public void setSelection(ISelection selection) { super.setSelection(selection); } }; protected String configurationURI() { return configurationURI; } @Override protected WidgetSupportImpl createSupport() { try { runtime = Simantics.getSession().sync( new WriteResultRequest(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 (RuntimeDatabaseException | DatabaseException e) { LOGGER.error("Failed to initialize modelled widget support runtime scenegraph", 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>() { @Override public Function1 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); getSite().setSelectionProvider(selectionProvider); } catch (DatabaseException e) { LOGGER.error("Failed to create modelled 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 boolean visible = site.getPage().isPartVisible(this); 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 (RuntimeDatabaseException | DatabaseException e) { LOGGER.error("Failed to dispose of the modelled widget support runtime scenegraph", 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; } }