1 /*******************************************************************************
2 * Copyright (c) 2012 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.diagramEditor;
14 import java.lang.reflect.Constructor;
16 import org.eclipse.core.runtime.IConfigurationElement;
17 import org.eclipse.core.runtime.IExecutableExtension;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.Platform;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.ui.IEditorInput;
23 import org.eclipse.ui.IEditorSite;
24 import org.eclipse.ui.IPartListener2;
25 import org.eclipse.ui.IWorkbenchPart;
26 import org.eclipse.ui.IWorkbenchPartReference;
27 import org.eclipse.ui.PartInitException;
28 import org.eclipse.ui.PlatformUI;
29 import org.eclipse.ui.part.EditorPart;
30 import org.osgi.framework.Bundle;
31 import org.simantics.db.Resource;
32 import org.simantics.diagram.ui.WorkbenchSelectionProvider;
33 import org.simantics.g2d.diagram.IDiagram;
34 import org.simantics.modeling.ui.diagramEditor.DiagramViewer.DiagramViewerHost;
35 import org.simantics.ui.workbench.IResourceEditorInput;
36 import org.simantics.ui.workbench.IResourceEditorInput2;
37 import org.simantics.ui.workbench.IResourceEditorPart2;
38 import org.simantics.ui.workbench.ResourceEditorSupport;
39 import org.simantics.utils.DataContainer;
40 import org.simantics.utils.threads.IThreadWorkQueue;
41 import org.simantics.utils.threads.SWTThread;
42 import org.simantics.utils.ui.ErrorLogger;
45 * A class for diagram editor parts that contains logic for destruction and
46 * (re)initialization of the actual diagram viewer and its controls during the
47 * life cycle of this editor part.
50 * To use this class in an editor part extension, define the following in the
51 * <code>class</code> attribute of the extension:
54 * class="org.simantics.modeling.ui.diagramEditor.DiagramEditor:viewer=%VIEWER%"
57 * where <code>%VIEWER%</code> is the name of the class that either is or
58 * extends {@link org.simantics.modeling.ui.diagramEditor.DiagramViewer}. The
59 * <code>viewer</code> argument tells {@link DiagramEditor} where to find the
60 * initializer for the diagram editor controls. The initializer must have a
61 * default constructor.
64 * This class is not intended to be extended by clients. Customizations should
65 * be performed through the viewer class.
67 * @author Tuukka Lehtonen
68 * @author Antti Villberg
70 public class DiagramEditor extends EditorPart implements IResourceEditorPart2, IPartListener2, DiagramViewerHost, IExecutableExtension {
73 * The {@value #ARG_VIEWER} argument for this editor part class tells the
74 * name of the class to use for initializing the diagram viewer, i.e.
75 * {@link #viewer}. The class is instantiated through reflection using the
76 * class loader of the bundle named {@link #viewerContributor}.
78 * @see #setInitializationData(IConfigurationElement, String, Object)
80 public static final String ARG_VIEWER = "viewer";
82 private Composite parent;
84 private String viewerContributor;
85 private String viewerClassName;
87 private ResourceEditorSupport support;
88 private DiagramViewer viewer;
91 * Used for distributing the reference to the IDiagram eventually loaded by
92 * the diagram viewer to both the diagram viewer and
93 * {@link #createSelectionProvider()}. {@link DiagramViewerLoadJob} is what
94 * ultimately does the actual loading and sets this container's value.
95 * @see #createSelectionProvider()
96 * @see DiagramViewerLoadJob
98 protected DataContainer<IDiagram> diagramContainer = new DataContainer<IDiagram>();
99 protected IThreadWorkQueue swt;
100 protected WorkbenchSelectionProvider selectionProvider;
103 * Reads the class arguments from the string in the data argument.
105 * @see org.eclipse.ui.part.EditorPart#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
106 * java.lang.String, java.lang.Object)
107 * @see #createViewer()
110 public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
111 super.setInitializationData(cfig, propertyName, data);
113 if (data instanceof String) {
114 viewerContributor = cfig.getContributor().getName();
116 String[] parameters = ((String) data).split(";");
118 for (String parameter : parameters) {
119 String[] keyValue = parameter.split("=");
120 if (keyValue.length > 2) {
121 ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: " + data, null);
124 String key = keyValue[0];
125 String value = keyValue.length > 1 ? keyValue[1] : "";
127 if (ARG_VIEWER.equals(key)) {
128 viewerClassName = value;
134 protected DiagramViewer createViewer() throws PartInitException {
135 if (viewerClassName == null)
136 throw new PartInitException(
137 "DiagramViewer contributor class was not specified in editor extension's class attribute viewer-argument. contributor is '"
138 + viewerContributor + "'");
141 Bundle b = Platform.getBundle(viewerContributor);
143 throw new PartInitException("DiagramViewer '" + viewerClassName + "' contributor bundle '"
144 + viewerContributor + "' was not found in the platform.");
146 Class<?> clazz = b.loadClass(viewerClassName);
147 if (!DiagramViewer.class.isAssignableFrom(clazz))
148 throw new PartInitException("DiagramViewer class '" + viewerClassName + "' is not assignable to "
149 + DiagramViewer.class + ".");
151 Constructor<?> ctor = clazz.getConstructor();
152 return (DiagramViewer) ctor.newInstance();
153 } catch (Exception e) {
154 throw new PartInitException("Failed to instantiate DiagramViewer implementation '" + viewerClassName
155 + "' from bundle '" + viewerContributor + "'. See exception for details.", e);
160 public IResourceEditorInput getResourceInput() {
161 return viewer.getResourceInput();
165 public IResourceEditorInput2 getResourceInput2() {
166 return viewer.getResourceInput2();
169 public DiagramViewer getViewer() {
173 public Resource getRuntimeResource() {
174 DiagramViewer viewer = this.viewer;
175 return viewer != null ? viewer.getRuntime() : null;
178 public Resource getInputResource() {
179 DiagramViewer viewer = this.viewer;
180 return viewer != null ? viewer.getInputResource() : null;
184 public void doSave(IProgressMonitor monitor) {
188 public void doSaveAs() {
192 public boolean isDirty() {
197 public boolean isSaveAsAllowed() {
202 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
206 viewer = createViewer();
208 // selectionProvider MUST be created and attached to the workbench site:
209 // 1. only once during the life-cycle of this editor part
210 // 2. in SWT UI thread
211 // 3. at least before returning from #createPartControl
212 swt = SWTThread.getThreadAccess(PlatformUI.getWorkbench().getDisplay());
213 selectionProvider = createSelectionProvider();
215 viewer.init(this, site, input, diagramContainer, selectionProvider);
217 getSite().getPage().addPartListener(this);
219 support = new ResourceEditorSupport(this, viewer.getInputValidator());
220 support.activateValidation();
224 public void createPartControl(Composite parent) {
225 this.parent = parent;
229 private void initializeViewer() {
230 parent.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
231 viewer.createPartControl(parent);
232 // It is possible that something goes wrong and the parent gets disposed already
233 if(parent.isDisposed()) return;
238 public void setFocus() {
244 * Override this to customize the kind of selection provider created for
245 * this {@link DiagramEditor}.
247 * @return the selection provider to set for the site
249 protected WorkbenchSelectionProvider createSelectionProvider() {
250 return new DiagramViewerSelectionProvider(swt, getSite(), diagramContainer);
253 @SuppressWarnings("unchecked")
254 public <T> T getAdapter(Class<T> adapter) {
255 if (adapter == DiagramViewer.class)
258 return (T) super.getAdapter(adapter);
260 Object result = viewer.getAdapter(adapter);
263 return super.getAdapter(adapter);
267 public void dispose() {
268 getSite().getPage().removePartListener(this);
270 if (support != null) {
275 DISPOSING_POLICY.removeDisposer(disposer);
282 public void doSetPartName(String name) {
287 public void doSetTitleToolTip(String name) {
288 setTitleToolTip(name);
291 // BEGIN: IPartListener2 implementation
294 public void partActivated(IWorkbenchPartReference partRef) {
298 public void partBroughtToTop(IWorkbenchPartReference partRef) {
302 public void partClosed(IWorkbenchPartReference partRef) {
306 public void partDeactivated(IWorkbenchPartReference partRef) {
310 public void partOpened(IWorkbenchPartReference partRef) {
314 * Disposes of the diagram viewer if not already disposed.
317 public void partHidden(IWorkbenchPartReference partRef) {
318 IWorkbenchPart part = partRef.getPart(false);
319 if (this.equals(part)) {
320 DISPOSING_POLICY.addDisposer(disposer);
324 private static final DisposingPolicy DISPOSING_POLICY =
325 new DisposingPolicy();
327 private Runnable disposer = new Runnable() {
334 private void tryDisposeViewer() {
335 if (viewer != null) {
336 Composite viewerComposite = viewer.getComposite();
339 if (viewerComposite != null) {
340 viewerComposite.dispose();
346 * Initializes the diagram viewer if not already initialized.
349 public void partVisible(IWorkbenchPartReference partRef) {
350 IWorkbenchPart part = partRef.getPart(false);
351 if (this.equals(part)) {
352 DISPOSING_POLICY.removeDisposer(disposer);
353 if (viewer == null) {
355 viewer = createViewer();
356 viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
358 } catch (PartInitException e) {
359 // This should never happen!
360 ErrorLogger.defaultLogError(e);
367 public void partInputChanged(IWorkbenchPartReference partRef) {
370 // END: IPartListener2 implementation