1 /*******************************************************************************
2 * Copyright (c) 2012, 2017 Association for Decentralized Information Management
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
12 *******************************************************************************/
13 package org.simantics.modeling.ui.diagramEditor;
15 import java.lang.reflect.Constructor;
17 import java.util.function.Predicate;
19 import org.eclipse.core.runtime.IConfigurationElement;
20 import org.eclipse.core.runtime.IExecutableExtension;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.Platform;
23 import org.eclipse.osgi.util.NLS;
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.ui.IEditorInput;
27 import org.eclipse.ui.IEditorPart;
28 import org.eclipse.ui.IEditorReference;
29 import org.eclipse.ui.IEditorSite;
30 import org.eclipse.ui.IPartListener2;
31 import org.eclipse.ui.IWorkbenchPage;
32 import org.eclipse.ui.IWorkbenchPart;
33 import org.eclipse.ui.IWorkbenchPartReference;
34 import org.eclipse.ui.IWorkbenchWindow;
35 import org.eclipse.ui.PartInitException;
36 import org.eclipse.ui.PlatformUI;
37 import org.eclipse.ui.part.EditorPart;
38 import org.osgi.framework.Bundle;
39 import org.simantics.db.Resource;
40 import org.simantics.diagram.ui.WorkbenchSelectionProvider;
41 import org.simantics.g2d.diagram.IDiagram;
42 import org.simantics.modeling.ui.diagramEditor.DiagramViewer.DiagramViewerHost;
43 import org.simantics.ui.workbench.IResourceEditorInput;
44 import org.simantics.ui.workbench.IResourceEditorInput2;
45 import org.simantics.ui.workbench.IResourceEditorPart2;
46 import org.simantics.ui.workbench.ResourceEditorSupport;
47 import org.simantics.utils.DataContainer;
48 import org.simantics.utils.threads.IThreadWorkQueue;
49 import org.simantics.utils.threads.SWTThread;
50 import org.simantics.utils.ui.ErrorLogger;
53 * A class for diagram editor parts that contains logic for destruction and
54 * (re)initialization of the actual diagram viewer and its controls during the
55 * life cycle of this editor part.
58 * To use this class in an editor part extension, define the following in the
59 * <code>class</code> attribute of the extension:
62 * class="org.simantics.modeling.ui.diagramEditor.DiagramEditor:viewer=%VIEWER%"
65 * where <code>%VIEWER%</code> is the name of the class that either is or
66 * extends {@link org.simantics.modeling.ui.diagramEditor.DiagramViewer}. The
67 * <code>viewer</code> argument tells {@link DiagramEditor} where to find the
68 * initializer for the diagram editor controls. The initializer must have a
69 * default constructor.
72 * This class is not intended to be extended by clients. Customizations should
73 * be performed through the viewer class.
75 * @author Tuukka Lehtonen
76 * @author Antti Villberg
78 public class DiagramEditor extends EditorPart implements IResourceEditorPart2, IPartListener2, DiagramViewerHost, IExecutableExtension {
81 * The {@value #ARG_VIEWER} argument for this editor part class tells the
82 * name of the class to use for initializing the diagram viewer, i.e.
83 * {@link #viewer}. The class is instantiated through reflection using the
84 * class loader of the bundle named {@link #viewerContributor}.
86 * @see #setInitializationData(IConfigurationElement, String, Object)
88 public static final String ARG_VIEWER = "viewer"; //$NON-NLS-1$
90 private Composite parent;
92 private String viewerContributor;
93 private String viewerClassName;
95 private ResourceEditorSupport support;
96 private DiagramViewer viewer;
99 * Used for distributing the reference to the IDiagram eventually loaded by
100 * the diagram viewer to both the diagram viewer and
101 * {@link #createSelectionProvider()}. {@link DiagramViewerLoadJob} is what
102 * ultimately does the actual loading and sets this container's value.
103 * @see #createSelectionProvider()
104 * @see DiagramViewerLoadJob
106 protected DataContainer<IDiagram> diagramContainer = new DataContainer<IDiagram>();
107 protected IThreadWorkQueue swt;
108 protected WorkbenchSelectionProvider selectionProvider;
111 * Reads the class arguments from the string in the data argument.
113 * @see org.eclipse.ui.part.EditorPart#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
114 * java.lang.String, java.lang.Object)
115 * @see #createViewer()
118 public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
119 super.setInitializationData(cfig, propertyName, data);
121 if (data instanceof String) {
122 viewerContributor = cfig.getContributor().getName();
124 String[] parameters = ((String) data).split(";"); //$NON-NLS-1$
126 for (String parameter : parameters) {
127 String[] keyValue = parameter.split("="); //$NON-NLS-1$
128 if (keyValue.length > 2) {
129 ErrorLogger.defaultLogWarning(NLS.bind(Messages.DiagramEditor_InvalidParameter, parameter, data), null);
132 String key = keyValue[0];
133 String value = keyValue.length > 1 ? keyValue[1] : ""; //$NON-NLS-1$
135 if (ARG_VIEWER.equals(key)) {
136 viewerClassName = value;
142 protected DiagramViewer createViewer() throws PartInitException {
143 if (viewerClassName == null)
144 throw new PartInitException(
145 "DiagramViewer contributor class was not specified in editor extension's class attribute viewer-argument. contributor is '" //$NON-NLS-1$
146 + viewerContributor + "'"); //$NON-NLS-1$
149 Bundle b = Platform.getBundle(viewerContributor);
151 throw new PartInitException("DiagramViewer '" + viewerClassName + "' contributor bundle '" //$NON-NLS-1$ //$NON-NLS-2$
152 + viewerContributor + "' was not found in the platform."); //$NON-NLS-1$
154 Class<?> clazz = b.loadClass(viewerClassName);
155 if (!DiagramViewer.class.isAssignableFrom(clazz))
156 throw new PartInitException("DiagramViewer class '" + viewerClassName + "' is not assignable to " //$NON-NLS-1$ //$NON-NLS-2$
157 + DiagramViewer.class + "."); //$NON-NLS-1$
159 Constructor<?> ctor = clazz.getConstructor();
160 return (DiagramViewer) ctor.newInstance();
161 } catch (Exception e) {
162 throw new PartInitException("Failed to instantiate DiagramViewer implementation '" + viewerClassName //$NON-NLS-1$
163 + "' from bundle '" + viewerContributor + "'. See exception for details.", e); //$NON-NLS-1$ //$NON-NLS-2$
168 public IResourceEditorInput getResourceInput() {
169 return viewer.getResourceInput();
173 public IResourceEditorInput2 getResourceInput2() {
174 return viewer.getResourceInput2();
177 public DiagramViewer getViewer() {
181 public Resource getRuntimeResource() {
182 DiagramViewer viewer = this.viewer;
183 return viewer != null ? viewer.getRuntime() : null;
186 public Resource getInputResource() {
187 DiagramViewer viewer = this.viewer;
188 return viewer != null ? viewer.getInputResource() : null;
192 public void doSave(IProgressMonitor monitor) {
196 public void doSaveAs() {
200 public boolean isDirty() {
205 public boolean isSaveAsAllowed() {
210 public void init(IEditorSite site, IEditorInput input) throws PartInitException {
214 viewer = createViewer();
216 // selectionProvider MUST be created and attached to the workbench site:
217 // 1. only once during the life-cycle of this editor part
218 // 2. in SWT UI thread
219 // 3. at least before returning from #createPartControl
220 swt = SWTThread.getThreadAccess(PlatformUI.getWorkbench().getDisplay());
221 selectionProvider = createSelectionProvider();
223 viewer.init(this, site, input, diagramContainer, selectionProvider);
225 getSite().getPage().addPartListener(this);
227 support = new ResourceEditorSupport(this, viewer.getInputValidator());
228 support.activateValidation();
232 public void createPartControl(Composite parent) {
233 this.parent = parent;
237 private void initializeViewer() {
238 parent.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
239 viewer.createPartControl(parent);
240 // It is possible that something goes wrong and the parent gets disposed already
241 if(parent.isDisposed()) return;
246 public void setFocus() {
252 * Override this to customize the kind of selection provider created for
253 * this {@link DiagramEditor}.
255 * @return the selection provider to set for the site
257 protected WorkbenchSelectionProvider createSelectionProvider() {
258 return new DiagramViewerSelectionProvider(swt, getSite(), diagramContainer);
261 @SuppressWarnings("unchecked")
262 public <T> T getAdapter(Class<T> adapter) {
263 if (adapter == DiagramViewer.class)
266 return (T) super.getAdapter(adapter);
268 Object result = viewer.getAdapter(adapter);
271 return super.getAdapter(adapter);
275 public void dispose() {
276 getSite().getPage().removePartListener(this);
278 if (support != null) {
283 DISPOSING_POLICY.removeDisposer(disposer);
290 public void doSetPartName(String name) {
295 public void doSetTitleToolTip(String name) {
296 setTitleToolTip(name);
299 // BEGIN: IPartListener2 implementation
302 public void partActivated(IWorkbenchPartReference partRef) {
306 public void partBroughtToTop(IWorkbenchPartReference partRef) {
310 public void partClosed(IWorkbenchPartReference partRef) {
314 public void partDeactivated(IWorkbenchPartReference partRef) {
318 public void partOpened(IWorkbenchPartReference partRef) {
322 * Disposes of the diagram viewer if not already disposed.
325 public void partHidden(IWorkbenchPartReference partRef) {
326 IWorkbenchPart part = partRef.getPart(false);
327 if (this.equals(part)) {
328 DISPOSING_POLICY.addDisposer(disposer);
332 private static final DisposingPolicy DISPOSING_POLICY =
333 new DisposingPolicy();
335 private Runnable disposer = () -> tryDisposeViewer();
337 private void tryDisposeViewer() {
338 if (viewer != null) {
339 Composite viewerComposite = viewer.getComposite();
342 if (viewerComposite != null) {
343 viewerComposite.dispose();
349 * Initializes the diagram viewer if not already initialized.
352 public void partVisible(IWorkbenchPartReference partRef) {
353 IWorkbenchPart part = partRef.getPart(false);
354 if (this.equals(part)) {
355 DISPOSING_POLICY.removeDisposer(disposer);
356 if (viewer == null) {
358 viewer = createViewer();
359 viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
361 } catch (PartInitException e) {
362 // This should never happen!
363 ErrorLogger.defaultLogError(e);
370 public void partInputChanged(IWorkbenchPartReference partRef) {
373 // END: IPartListener2 implementation
376 * Reinitialize this diagram editor from scratch.
378 * <p>Must be invoked from the SWT thread.</p>
380 public void reinitializeViewer() {
381 if (viewer != null) {
382 DISPOSING_POLICY.removeDisposer(disposer);
385 viewer = createViewer();
386 viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
388 } catch (PartInitException e) {
389 // This should never happen!
390 ErrorLogger.defaultLogError(e);
396 * Reinitializes all {@link DiagramEditor} instances in all workbench windows for which
397 * the specified predicate returns <code>true</code>.
399 * <p>Must be invoked from the SWT thread.</p>
402 * tester for editor inputs
404 public static void reinitializeDiagram(Predicate<IEditorInput> predicate) {
405 for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
406 for (IWorkbenchPage page : window.getPages()) {
407 for (IEditorReference editorRef : page.getEditorReferences()) {
409 IEditorInput input = editorRef.getEditorInput();
410 if (predicate.test(input)) {
411 IEditorPart editor = editorRef.getEditor(false);
412 if (editor instanceof DiagramEditor)
413 ((DiagramEditor) editor).reinitializeViewer();
415 } catch (PartInitException e) {
416 ErrorLogger.defaultLogError(e);
424 * Reinitializes all DiagramEditor instances in all workbench windows that have
425 * the specified <code>diagram</code> as their input.
427 * <p>Must be invoked from the SWT thread.</p>
430 * the diagram resource for which to reinitialize all DiagramEditors
433 public static void reinitializeDiagram(Resource diagram) {
434 reinitializeDiagram(input -> input instanceof IResourceEditorInput
435 && ((IResourceEditorInput) input).getResource().equals(diagram));
439 * Reinitializes all DiagramEditor instances in all workbench windows that have
440 * the specified <code>diagrams</code> as their input.
442 * <p>Must be invoked from the SWT thread.</p>
445 * collection of diagram resources for which to reinitialize all DiagramEditors
448 public static void reinitializeDiagram(Set<Resource> diagrams) {
449 reinitializeDiagram(input -> input instanceof IResourceEditorInput
450 && diagrams.contains(((IResourceEditorInput) input).getResource()));