]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramEditor.java
Reinitialize visible diagrams after shared library migration
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagramEditor / DiagramEditor.java
1 /*******************************************************************************
2  * Copyright (c) 2012, 2017 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *     Semantum Oy - #7586
12  *******************************************************************************/
13 package org.simantics.modeling.ui.diagramEditor;
14
15 import java.lang.reflect.Constructor;
16 import java.util.Set;
17 import java.util.function.Predicate;
18
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.swt.SWT;
24 import org.eclipse.swt.widgets.Composite;
25 import org.eclipse.ui.IEditorInput;
26 import org.eclipse.ui.IEditorPart;
27 import org.eclipse.ui.IEditorReference;
28 import org.eclipse.ui.IEditorSite;
29 import org.eclipse.ui.IPartListener2;
30 import org.eclipse.ui.IWorkbenchPage;
31 import org.eclipse.ui.IWorkbenchPart;
32 import org.eclipse.ui.IWorkbenchPartReference;
33 import org.eclipse.ui.IWorkbenchWindow;
34 import org.eclipse.ui.PartInitException;
35 import org.eclipse.ui.PlatformUI;
36 import org.eclipse.ui.part.EditorPart;
37 import org.osgi.framework.Bundle;
38 import org.simantics.db.Resource;
39 import org.simantics.diagram.ui.WorkbenchSelectionProvider;
40 import org.simantics.g2d.diagram.IDiagram;
41 import org.simantics.modeling.ui.diagramEditor.DiagramViewer.DiagramViewerHost;
42 import org.simantics.ui.workbench.IResourceEditorInput;
43 import org.simantics.ui.workbench.IResourceEditorInput2;
44 import org.simantics.ui.workbench.IResourceEditorPart2;
45 import org.simantics.ui.workbench.ResourceEditorSupport;
46 import org.simantics.utils.DataContainer;
47 import org.simantics.utils.threads.IThreadWorkQueue;
48 import org.simantics.utils.threads.SWTThread;
49 import org.simantics.utils.ui.ErrorLogger;
50
51 /**
52  * A class for diagram editor parts that contains logic for destruction and
53  * (re)initialization of the actual diagram viewer and its controls during the
54  * life cycle of this editor part.
55  * 
56  * <p>
57  * To use this class in an editor part extension, define the following in the
58  * <code>class</code> attribute of the extension:
59  * 
60  * <pre>
61  * class="org.simantics.modeling.ui.diagramEditor.DiagramEditor:viewer=%VIEWER%"
62  * </pre>
63  * 
64  * where <code>%VIEWER%</code> is the name of the class that either is or
65  * extends {@link org.simantics.modeling.ui.diagramEditor.DiagramViewer}. The
66  * <code>viewer</code> argument tells {@link DiagramEditor} where to find the
67  * initializer for the diagram editor controls. The initializer must have a
68  * default constructor.
69  * 
70  * <p>
71  * This class is not intended to be extended by clients. Customizations should
72  * be performed through the viewer class.
73  * 
74  * @author Tuukka Lehtonen
75  * @author Antti Villberg
76  */
77 public class DiagramEditor extends EditorPart implements IResourceEditorPart2, IPartListener2, DiagramViewerHost, IExecutableExtension {
78
79     /**
80      * The {@value #ARG_VIEWER} argument for this editor part class tells the
81      * name of the class to use for initializing the diagram viewer, i.e.
82      * {@link #viewer}. The class is instantiated through reflection using the
83      * class loader of the bundle named {@link #viewerContributor}.
84      * 
85      * @see #setInitializationData(IConfigurationElement, String, Object)
86      */
87     public static final String    ARG_VIEWER = "viewer";
88
89     private Composite             parent;
90
91     private String                viewerContributor;
92     private String                viewerClassName;
93
94     private ResourceEditorSupport support;
95     private DiagramViewer         viewer;
96
97     /**
98      * Used for distributing the reference to the IDiagram eventually loaded by
99      * the diagram viewer to both the diagram viewer and
100      * {@link #createSelectionProvider()}. {@link DiagramViewerLoadJob} is what
101      * ultimately does the actual loading and sets this container's value.
102      * @see #createSelectionProvider()
103      * @see DiagramViewerLoadJob
104      */
105     protected DataContainer<IDiagram>    diagramContainer = new DataContainer<IDiagram>();
106     protected IThreadWorkQueue           swt;
107     protected WorkbenchSelectionProvider selectionProvider;
108
109     /**
110      * Reads the class arguments from the string in the data argument.
111      * 
112      * @see org.eclipse.ui.part.EditorPart#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
113      *      java.lang.String, java.lang.Object)
114      * @see #createViewer()
115      */
116     @Override
117     public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
118         super.setInitializationData(cfig, propertyName, data);
119
120         if (data instanceof String) {
121             viewerContributor = cfig.getContributor().getName();
122
123             String[] parameters = ((String) data).split(";");
124
125             for (String parameter : parameters) {
126                 String[] keyValue = parameter.split("=");
127                 if (keyValue.length > 2) {
128                     ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: " + data, null);
129                     continue;
130                 }
131                 String key = keyValue[0];
132                 String value = keyValue.length > 1 ? keyValue[1] : "";
133
134                 if (ARG_VIEWER.equals(key)) {
135                     viewerClassName = value;
136                 } 
137             }
138         }
139     }
140
141     protected DiagramViewer createViewer() throws PartInitException {
142         if (viewerClassName == null)
143             throw new PartInitException(
144                     "DiagramViewer contributor class was not specified in editor extension's class attribute viewer-argument. contributor is '"
145                             + viewerContributor + "'");
146
147         try {
148             Bundle b = Platform.getBundle(viewerContributor);
149             if (b == null)
150                 throw new PartInitException("DiagramViewer '" + viewerClassName + "' contributor bundle '"
151                         + viewerContributor + "' was not found in the platform.");
152
153             Class<?> clazz = b.loadClass(viewerClassName);
154             if (!DiagramViewer.class.isAssignableFrom(clazz))
155                 throw new PartInitException("DiagramViewer class '" + viewerClassName + "' is not assignable to "
156                         + DiagramViewer.class + ".");
157
158             Constructor<?> ctor = clazz.getConstructor();
159             return (DiagramViewer) ctor.newInstance();
160         } catch (Exception e) {
161             throw new PartInitException("Failed to instantiate DiagramViewer implementation '" + viewerClassName
162                     + "' from bundle '" + viewerContributor + "'. See exception for details.", e);
163         }
164     }
165
166     @Override
167     public IResourceEditorInput getResourceInput() {
168         return viewer.getResourceInput();
169     }
170
171     @Override
172     public IResourceEditorInput2 getResourceInput2() {
173         return viewer.getResourceInput2();
174     }
175
176     public DiagramViewer getViewer() {
177         return viewer;
178     }
179
180     public Resource getRuntimeResource() {
181         DiagramViewer viewer = this.viewer;
182         return viewer != null ? viewer.getRuntime() : null;
183     }
184     
185     public Resource getInputResource() {
186         DiagramViewer viewer = this.viewer;
187         return viewer != null ? viewer.getInputResource() : null;
188     }
189
190     @Override
191     public void doSave(IProgressMonitor monitor) {
192     }
193
194     @Override
195     public void doSaveAs() {
196     }
197
198     @Override
199     public boolean isDirty() {
200         return false;
201     }
202
203     @Override
204     public boolean isSaveAsAllowed() {
205         return false;
206     }
207
208     @Override
209     public void init(IEditorSite site, IEditorInput input) throws PartInitException {
210         setSite(site);
211         setInput(input);
212
213         viewer = createViewer();
214
215         // selectionProvider MUST be created and attached to the workbench site:
216         //   1. only once during the life-cycle of this editor part
217         //   2. in SWT UI thread
218         //   3. at least before returning from #createPartControl
219         swt = SWTThread.getThreadAccess(PlatformUI.getWorkbench().getDisplay());
220         selectionProvider = createSelectionProvider();
221
222         viewer.init(this, site, input, diagramContainer, selectionProvider);
223
224         getSite().getPage().addPartListener(this);
225
226         support = new ResourceEditorSupport(this, viewer.getInputValidator());
227         support.activateValidation();
228     }
229
230     @Override
231     public void createPartControl(Composite parent) {
232         this.parent = parent;
233         initializeViewer();
234     }
235
236     private void initializeViewer() {
237         parent.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
238         viewer.createPartControl(parent);
239         // It is possible that something goes wrong and the parent gets disposed already
240         if(parent.isDisposed()) return;
241         parent.layout(true);
242     }
243
244     @Override
245     public void setFocus() {
246         if (viewer != null)
247             viewer.setFocus();
248     }
249
250     /**
251      * Override this to customize the kind of selection provider created for
252      * this {@link DiagramEditor}.
253      * 
254      * @return the selection provider to set for the site
255      */
256     protected WorkbenchSelectionProvider createSelectionProvider() {
257         return new DiagramViewerSelectionProvider(swt, getSite(), diagramContainer);
258     }
259
260     @SuppressWarnings("unchecked")
261     public <T> T getAdapter(Class<T> adapter) {
262         if (adapter == DiagramViewer.class)
263             return (T) viewer;
264         if (viewer == null)
265             return (T) super.getAdapter(adapter);
266
267         Object result = viewer.getAdapter(adapter);
268         if (result != null)
269             return (T) result;
270         return super.getAdapter(adapter);
271     }
272
273     @Override
274     public void dispose() {
275         getSite().getPage().removePartListener(this);
276
277         if (support != null) {
278             support.dispose();
279             support = null;
280         }
281
282         DISPOSING_POLICY.removeDisposer(disposer);
283         tryDisposeViewer();
284
285         super.dispose();
286     }
287
288     @Override
289     public void doSetPartName(String name) {
290         setPartName(name);
291     }
292
293     @Override
294     public void doSetTitleToolTip(String name) {
295         setTitleToolTip(name);
296     }
297
298     // BEGIN: IPartListener2 implementation
299
300     @Override
301     public void partActivated(IWorkbenchPartReference partRef) {
302     }
303
304     @Override
305     public void partBroughtToTop(IWorkbenchPartReference partRef) {
306     }
307
308     @Override
309     public void partClosed(IWorkbenchPartReference partRef) {
310     }
311
312     @Override
313     public void partDeactivated(IWorkbenchPartReference partRef) {
314     }
315
316     @Override
317     public void partOpened(IWorkbenchPartReference partRef) {
318     }
319
320     /**
321      * Disposes of the diagram viewer if not already disposed.
322      */
323     @Override
324     public void partHidden(IWorkbenchPartReference partRef) {
325         IWorkbenchPart part = partRef.getPart(false);
326         if (this.equals(part)) {
327             DISPOSING_POLICY.addDisposer(disposer);
328         }
329     }
330     
331     private static final DisposingPolicy DISPOSING_POLICY = 
332             new DisposingPolicy();
333
334     private Runnable disposer = () -> tryDisposeViewer();
335
336     private void tryDisposeViewer() {
337         if (viewer != null) {
338             Composite viewerComposite = viewer.getComposite();
339             viewer.dispose();
340             viewer = null;
341             if (viewerComposite != null) {
342                 viewerComposite.dispose();
343             }
344         }
345     }
346
347     /**
348      * Initializes the diagram viewer if not already initialized.
349      */
350     @Override
351     public void partVisible(IWorkbenchPartReference partRef) {
352         IWorkbenchPart part = partRef.getPart(false);
353         if (this.equals(part)) {
354             DISPOSING_POLICY.removeDisposer(disposer);
355             if (viewer == null) {
356                 try {
357                     viewer = createViewer();
358                     viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
359                     initializeViewer();
360                 } catch (PartInitException e) {
361                     // This should never happen!
362                     ErrorLogger.defaultLogError(e);
363                 }
364             }
365         }
366     }
367
368     @Override
369     public void partInputChanged(IWorkbenchPartReference partRef) {
370     }
371
372     // END: IPartListener2 implementation
373
374     /**
375      * Reinitialize this diagram editor from scratch.
376      * 
377      * <p>Must be invoked from the SWT thread.</p>
378      */
379     public void reinitializeViewer() {
380         if (viewer != null) {
381             DISPOSING_POLICY.removeDisposer(disposer);
382             tryDisposeViewer();
383             try {
384                 viewer = createViewer();
385                 viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
386                 initializeViewer();
387             } catch (PartInitException e) {
388                 // This should never happen!
389                 ErrorLogger.defaultLogError(e);
390             }
391         }
392     }
393
394     /**
395      * Reinitializes all {@link DiagramEditor} instances in all workbench windows for which
396      * the specified predicate returns <code>true</code>.
397      * 
398      * <p>Must be invoked from the SWT thread.</p>
399      * 
400      * @param predicate
401      *            tester for editor inputs
402      */
403     public static void reinitializeDiagram(Predicate<IEditorInput> predicate) {
404         for (IWorkbenchWindow window : PlatformUI.getWorkbench().getWorkbenchWindows()) {
405             for (IWorkbenchPage page : window.getPages()) {
406                 for (IEditorReference editorRef : page.getEditorReferences()) {
407                     try {
408                         IEditorInput input = editorRef.getEditorInput();
409                         if (predicate.test(input)) {
410                             IEditorPart editor = editorRef.getEditor(false);
411                             if (editor instanceof DiagramEditor)
412                                 ((DiagramEditor) editor).reinitializeViewer();
413                         }
414                     } catch (PartInitException e) {
415                         ErrorLogger.defaultLogError(e);
416                     }
417                 }
418             }
419         }
420     }
421
422     /**
423      * Reinitializes all DiagramEditor instances in all workbench windows that have
424      * the specified <code>diagram</code> as their input.
425      * 
426      * <p>Must be invoked from the SWT thread.</p>
427      * 
428      * @param diagram
429      *            the diagram resource for which to reinitialize all DiagramEditors
430      *            for
431      */
432     public static void reinitializeDiagram(Resource diagram) {
433         reinitializeDiagram(input -> input instanceof IResourceEditorInput
434                 && ((IResourceEditorInput) input).getResource().equals(diagram));
435     }
436
437     /**
438      * Reinitializes all DiagramEditor instances in all workbench windows that have
439      * the specified <code>diagrams</code> as their input.
440      * 
441      * <p>Must be invoked from the SWT thread.</p>
442      * 
443      * @param diagrams
444      *            collection of diagram resources for which to reinitialize all DiagramEditors
445      *            for
446      */
447     public static void reinitializeDiagram(Set<Resource> diagrams) {
448         reinitializeDiagram(input -> input instanceof IResourceEditorInput
449                 && diagrams.contains(((IResourceEditorInput) input).getResource()));
450     }
451
452 }