X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2FdiagramEditor%2FDiagramEditor.java;fp=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2FdiagramEditor%2FDiagramEditor.java;h=759a03e7a196a21046c38e9029dedb51da84ba49;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramEditor.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramEditor.java new file mode 100644 index 000000000..759a03e7a --- /dev/null +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramEditor.java @@ -0,0 +1,372 @@ +/******************************************************************************* + * Copyright (c) 2012 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.modeling.ui.diagramEditor; + +import java.lang.reflect.Constructor; + +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.IEditorSite; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +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 = new Runnable() { + @Override + public void run() { + 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 + +}