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