/******************************************************************************* * Copyright (c) 2012, 2017 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 * Semantum Oy - #7586 *******************************************************************************/ package org.simantics.modeling.ui.diagramEditor; import java.lang.reflect.Constructor; import java.util.function.Predicate; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IPartListener2; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.EditorPart; import org.osgi.framework.Bundle; import org.simantics.db.Resource; import org.simantics.diagram.ui.WorkbenchSelectionProvider; import org.simantics.g2d.diagram.IDiagram; import org.simantics.modeling.ui.diagramEditor.DiagramViewer.DiagramViewerHost; import org.simantics.ui.workbench.IResourceEditorInput; import org.simantics.ui.workbench.IResourceEditorInput2; import org.simantics.ui.workbench.IResourceEditorPart2; import org.simantics.ui.workbench.ResourceEditorSupport; import org.simantics.utils.DataContainer; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.SWTThread; import org.simantics.utils.ui.ErrorLogger; /** * A class for diagram editor parts that contains logic for destruction and * (re)initialization of the actual diagram viewer and its controls during the * life cycle of this editor part. * *

* To use this class in an editor part extension, define the following in the * class attribute of the extension: * *

 * class="org.simantics.modeling.ui.diagramEditor.DiagramEditor:viewer=%VIEWER%"
 * 
* * where %VIEWER% is the name of the class that either is or * extends {@link org.simantics.modeling.ui.diagramEditor.DiagramViewer}. The * viewer argument tells {@link DiagramEditor} where to find the * initializer for the diagram editor controls. The initializer must have a * default constructor. * *

* This class is not intended to be extended by clients. Customizations should * be performed through the viewer class. * * @author Tuukka Lehtonen * @author Antti Villberg */ public class DiagramEditor extends EditorPart implements IResourceEditorPart2, IPartListener2, DiagramViewerHost, IExecutableExtension { /** * The {@value #ARG_VIEWER} argument for this editor part class tells the * name of the class to use for initializing the diagram viewer, i.e. * {@link #viewer}. The class is instantiated through reflection using the * class loader of the bundle named {@link #viewerContributor}. * * @see #setInitializationData(IConfigurationElement, String, Object) */ public static final String ARG_VIEWER = "viewer"; private Composite parent; private String viewerContributor; private String viewerClassName; private ResourceEditorSupport support; private DiagramViewer viewer; /** * Used for distributing the reference to the IDiagram eventually loaded by * the diagram viewer to both the diagram viewer and * {@link #createSelectionProvider()}. {@link DiagramViewerLoadJob} is what * ultimately does the actual loading and sets this container's value. * @see #createSelectionProvider() * @see DiagramViewerLoadJob */ protected DataContainer diagramContainer = new DataContainer(); protected IThreadWorkQueue swt; protected WorkbenchSelectionProvider selectionProvider; /** * Reads the class arguments from the string in the data argument. * * @see org.eclipse.ui.part.EditorPart#setInitializationData(org.eclipse.core.runtime.IConfigurationElement, * java.lang.String, java.lang.Object) * @see #createViewer() */ @Override public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) { super.setInitializationData(cfig, propertyName, data); if (data instanceof String) { viewerContributor = cfig.getContributor().getName(); 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 (ARG_VIEWER.equals(key)) { viewerClassName = value; } } } } protected DiagramViewer createViewer() throws PartInitException { if (viewerClassName == null) throw new PartInitException( "DiagramViewer contributor class was not specified in editor extension's class attribute viewer-argument. contributor is '" + viewerContributor + "'"); try { Bundle b = Platform.getBundle(viewerContributor); if (b == null) throw new PartInitException("DiagramViewer '" + viewerClassName + "' contributor bundle '" + viewerContributor + "' was not found in the platform."); Class clazz = b.loadClass(viewerClassName); if (!DiagramViewer.class.isAssignableFrom(clazz)) throw new PartInitException("DiagramViewer class '" + viewerClassName + "' is not assignable to " + DiagramViewer.class + "."); Constructor ctor = clazz.getConstructor(); return (DiagramViewer) ctor.newInstance(); } catch (Exception e) { throw new PartInitException("Failed to instantiate DiagramViewer implementation '" + viewerClassName + "' from bundle '" + viewerContributor + "'. See exception for details.", e); } } @Override public IResourceEditorInput getResourceInput() { return viewer.getResourceInput(); } @Override public IResourceEditorInput2 getResourceInput2() { return viewer.getResourceInput2(); } public DiagramViewer getViewer() { return viewer; } public Resource getRuntimeResource() { DiagramViewer viewer = this.viewer; return viewer != null ? viewer.getRuntime() : null; } public Resource getInputResource() { DiagramViewer viewer = this.viewer; return viewer != null ? viewer.getInputResource() : null; } @Override public void doSave(IProgressMonitor monitor) { } @Override public void doSaveAs() { } @Override public boolean isDirty() { return false; } @Override public boolean isSaveAsAllowed() { return false; } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(input); viewer = createViewer(); // selectionProvider MUST be created and attached to the workbench site: // 1. only once during the life-cycle of this editor part // 2. in SWT UI thread // 3. at least before returning from #createPartControl swt = SWTThread.getThreadAccess(PlatformUI.getWorkbench().getDisplay()); selectionProvider = createSelectionProvider(); viewer.init(this, site, input, diagramContainer, selectionProvider); getSite().getPage().addPartListener(this); support = new ResourceEditorSupport(this, viewer.getInputValidator()); support.activateValidation(); } @Override public void createPartControl(Composite parent) { this.parent = parent; initializeViewer(); } private void initializeViewer() { parent.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); viewer.createPartControl(parent); // It is possible that something goes wrong and the parent gets disposed already if(parent.isDisposed()) return; parent.layout(true); } @Override public void setFocus() { if (viewer != null) viewer.setFocus(); } /** * Override this to customize the kind of selection provider created for * this {@link DiagramEditor}. * * @return the selection provider to set for the site */ protected WorkbenchSelectionProvider createSelectionProvider() { return new DiagramViewerSelectionProvider(swt, getSite(), diagramContainer); } @SuppressWarnings("unchecked") public T getAdapter(Class adapter) { if (adapter == DiagramViewer.class) return (T) viewer; if (viewer == null) return (T) super.getAdapter(adapter); Object result = viewer.getAdapter(adapter); if (result != null) return (T) result; return super.getAdapter(adapter); } @Override public void dispose() { getSite().getPage().removePartListener(this); if (support != null) { support.dispose(); support = null; } DISPOSING_POLICY.removeDisposer(disposer); tryDisposeViewer(); super.dispose(); } @Override public void doSetPartName(String name) { setPartName(name); } @Override public void doSetTitleToolTip(String name) { setTitleToolTip(name); } // BEGIN: IPartListener2 implementation @Override public void partActivated(IWorkbenchPartReference partRef) { } @Override public void partBroughtToTop(IWorkbenchPartReference partRef) { } @Override public void partClosed(IWorkbenchPartReference partRef) { } @Override public void partDeactivated(IWorkbenchPartReference partRef) { } @Override public void partOpened(IWorkbenchPartReference partRef) { } /** * Disposes of the diagram viewer if not already disposed. */ @Override public void partHidden(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (this.equals(part)) { DISPOSING_POLICY.addDisposer(disposer); } } private static final DisposingPolicy DISPOSING_POLICY = new DisposingPolicy(); private Runnable disposer = () -> tryDisposeViewer(); private void tryDisposeViewer() { if (viewer != null) { Composite viewerComposite = viewer.getComposite(); viewer.dispose(); viewer = null; if (viewerComposite != null) { viewerComposite.dispose(); } } } /** * Initializes the diagram viewer if not already initialized. */ @Override public void partVisible(IWorkbenchPartReference partRef) { IWorkbenchPart part = partRef.getPart(false); if (this.equals(part)) { DISPOSING_POLICY.removeDisposer(disposer); if (viewer == null) { try { viewer = createViewer(); viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider); initializeViewer(); } catch (PartInitException e) { // This should never happen! ErrorLogger.defaultLogError(e); } } } } @Override public void partInputChanged(IWorkbenchPartReference partRef) { } // END: IPartListener2 implementation /** * Reinitialize this diagram editor from scratch. * *

Must be invoked from the SWT thread.

*/ public void reinitializeViewer() { if (viewer != null) { DISPOSING_POLICY.removeDisposer(disposer); tryDisposeViewer(); try { viewer = createViewer(); viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider); initializeViewer(); } catch (PartInitException e) { // This should never happen! ErrorLogger.defaultLogError(e); } } } /** * Reinitializes all {@link DiagramEditor} instances in all workbench windows for which * the specified predicate returns true. * *

Must be invoked from the SWT thread.

* * @param predicate * tester for editor inputs */ public static void reinitializeDiagram(Predicate predicate) { for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) { for (IWorkbenchPage page : window.getPages()) { for (IEditorReference editorRef : page.getEditorReferences()) { try { IEditorInput input = editorRef.getEditorInput(); if (predicate.test(input)) { IEditorPart editor = editorRef.getEditor(false); if (editor instanceof DiagramEditor) ((DiagramEditor) editor).reinitializeViewer(); } } catch (PartInitException e) { ErrorLogger.defaultLogError(e); } } } } } /** * Reinitializes all DiagramEditor instances in all workbench windows that have * the specified diagram as their input. * *

Must be invoked from the SWT thread.

* * @param diagram * the diagram resource for which to reinitialize all DiagramEditors * for */ public static void reinitializeDiagram(Resource diagram) { reinitializeDiagram(input -> input instanceof IResourceEditorInput && ((IResourceEditorInput) input).getResource().equals(diagram)); } }