/*******************************************************************************
* 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
}