]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagramEditor/DiagramEditor.java
Externalize strings
[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.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;
51
52 /**
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.
56  * 
57  * <p>
58  * To use this class in an editor part extension, define the following in the
59  * <code>class</code> attribute of the extension:
60  * 
61  * <pre>
62  * class="org.simantics.modeling.ui.diagramEditor.DiagramEditor:viewer=%VIEWER%"
63  * </pre>
64  * 
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.
70  * 
71  * <p>
72  * This class is not intended to be extended by clients. Customizations should
73  * be performed through the viewer class.
74  * 
75  * @author Tuukka Lehtonen
76  * @author Antti Villberg
77  */
78 public class DiagramEditor extends EditorPart implements IResourceEditorPart2, IPartListener2, DiagramViewerHost, IExecutableExtension {
79
80     /**
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}.
85      * 
86      * @see #setInitializationData(IConfigurationElement, String, Object)
87      */
88     public static final String    ARG_VIEWER = "viewer"; //$NON-NLS-1$
89
90     private Composite             parent;
91
92     private String                viewerContributor;
93     private String                viewerClassName;
94
95     private ResourceEditorSupport support;
96     private DiagramViewer         viewer;
97
98     /**
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
105      */
106     protected DataContainer<IDiagram>    diagramContainer = new DataContainer<IDiagram>();
107     protected IThreadWorkQueue           swt;
108     protected WorkbenchSelectionProvider selectionProvider;
109
110     /**
111      * Reads the class arguments from the string in the data argument.
112      * 
113      * @see org.eclipse.ui.part.EditorPart#setInitializationData(org.eclipse.core.runtime.IConfigurationElement,
114      *      java.lang.String, java.lang.Object)
115      * @see #createViewer()
116      */
117     @Override
118     public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
119         super.setInitializationData(cfig, propertyName, data);
120
121         if (data instanceof String) {
122             viewerContributor = cfig.getContributor().getName();
123
124             String[] parameters = ((String) data).split(";"); //$NON-NLS-1$
125
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); 
130                     continue;
131                 }
132                 String key = keyValue[0];
133                 String value = keyValue.length > 1 ? keyValue[1] : ""; //$NON-NLS-1$
134
135                 if (ARG_VIEWER.equals(key)) {
136                     viewerClassName = value;
137                 } 
138             }
139         }
140     }
141
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$
147
148         try {
149             Bundle b = Platform.getBundle(viewerContributor);
150             if (b == null)
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$
153
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$
158
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$
164         }
165     }
166
167     @Override
168     public IResourceEditorInput getResourceInput() {
169         return viewer.getResourceInput();
170     }
171
172     @Override
173     public IResourceEditorInput2 getResourceInput2() {
174         return viewer.getResourceInput2();
175     }
176
177     public DiagramViewer getViewer() {
178         return viewer;
179     }
180
181     public Resource getRuntimeResource() {
182         DiagramViewer viewer = this.viewer;
183         return viewer != null ? viewer.getRuntime() : null;
184     }
185     
186     public Resource getInputResource() {
187         DiagramViewer viewer = this.viewer;
188         return viewer != null ? viewer.getInputResource() : null;
189     }
190
191     @Override
192     public void doSave(IProgressMonitor monitor) {
193     }
194
195     @Override
196     public void doSaveAs() {
197     }
198
199     @Override
200     public boolean isDirty() {
201         return false;
202     }
203
204     @Override
205     public boolean isSaveAsAllowed() {
206         return false;
207     }
208
209     @Override
210     public void init(IEditorSite site, IEditorInput input) throws PartInitException {
211         setSite(site);
212         setInput(input);
213
214         viewer = createViewer();
215
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();
222
223         viewer.init(this, site, input, diagramContainer, selectionProvider);
224
225         getSite().getPage().addPartListener(this);
226
227         support = new ResourceEditorSupport(this, viewer.getInputValidator());
228         support.activateValidation();
229     }
230
231     @Override
232     public void createPartControl(Composite parent) {
233         this.parent = parent;
234         initializeViewer();
235     }
236
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;
242         parent.layout(true);
243     }
244
245     @Override
246     public void setFocus() {
247         if (viewer != null)
248             viewer.setFocus();
249     }
250
251     /**
252      * Override this to customize the kind of selection provider created for
253      * this {@link DiagramEditor}.
254      * 
255      * @return the selection provider to set for the site
256      */
257     protected WorkbenchSelectionProvider createSelectionProvider() {
258         return new DiagramViewerSelectionProvider(swt, getSite(), diagramContainer);
259     }
260
261     @SuppressWarnings("unchecked")
262     public <T> T getAdapter(Class<T> adapter) {
263         if (adapter == DiagramViewer.class)
264             return (T) viewer;
265         if (viewer == null)
266             return (T) super.getAdapter(adapter);
267
268         Object result = viewer.getAdapter(adapter);
269         if (result != null)
270             return (T) result;
271         return super.getAdapter(adapter);
272     }
273
274     @Override
275     public void dispose() {
276         getSite().getPage().removePartListener(this);
277
278         if (support != null) {
279             support.dispose();
280             support = null;
281         }
282
283         DISPOSING_POLICY.removeDisposer(disposer);
284         tryDisposeViewer();
285
286         super.dispose();
287     }
288
289     @Override
290     public void doSetPartName(String name) {
291         setPartName(name);
292     }
293
294     @Override
295     public void doSetTitleToolTip(String name) {
296         setTitleToolTip(name);
297     }
298
299     // BEGIN: IPartListener2 implementation
300
301     @Override
302     public void partActivated(IWorkbenchPartReference partRef) {
303     }
304
305     @Override
306     public void partBroughtToTop(IWorkbenchPartReference partRef) {
307     }
308
309     @Override
310     public void partClosed(IWorkbenchPartReference partRef) {
311     }
312
313     @Override
314     public void partDeactivated(IWorkbenchPartReference partRef) {
315     }
316
317     @Override
318     public void partOpened(IWorkbenchPartReference partRef) {
319     }
320
321     /**
322      * Disposes of the diagram viewer if not already disposed.
323      */
324     @Override
325     public void partHidden(IWorkbenchPartReference partRef) {
326         IWorkbenchPart part = partRef.getPart(false);
327         if (this.equals(part)) {
328             DISPOSING_POLICY.addDisposer(disposer);
329         }
330     }
331     
332     private static final DisposingPolicy DISPOSING_POLICY = 
333             new DisposingPolicy();
334
335     private Runnable disposer = () -> tryDisposeViewer();
336
337     private void tryDisposeViewer() {
338         if (viewer != null) {
339             Composite viewerComposite = viewer.getComposite();
340             viewer.dispose();
341             viewer = null;
342             if (viewerComposite != null) {
343                 viewerComposite.dispose();
344             }
345         }
346     }
347
348     /**
349      * Initializes the diagram viewer if not already initialized.
350      */
351     @Override
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) {
357                 try {
358                     viewer = createViewer();
359                     viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
360                     initializeViewer();
361                 } catch (PartInitException e) {
362                     // This should never happen!
363                     ErrorLogger.defaultLogError(e);
364                 }
365             }
366         }
367     }
368
369     @Override
370     public void partInputChanged(IWorkbenchPartReference partRef) {
371     }
372
373     // END: IPartListener2 implementation
374
375     /**
376      * Reinitialize this diagram editor from scratch.
377      * 
378      * <p>Must be invoked from the SWT thread.</p>
379      */
380     public void reinitializeViewer() {
381         if (viewer != null) {
382             DISPOSING_POLICY.removeDisposer(disposer);
383             tryDisposeViewer();
384             try {
385                 viewer = createViewer();
386                 viewer.init(this, getEditorSite(), getEditorInput(), diagramContainer, selectionProvider);
387                 initializeViewer();
388             } catch (PartInitException e) {
389                 // This should never happen!
390                 ErrorLogger.defaultLogError(e);
391             }
392         }
393     }
394
395     /**
396      * Reinitializes all {@link DiagramEditor} instances in all workbench windows for which
397      * the specified predicate returns <code>true</code>.
398      * 
399      * <p>Must be invoked from the SWT thread.</p>
400      * 
401      * @param predicate
402      *            tester for editor inputs
403      */
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()) {
408                     try {
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();
414                         }
415                     } catch (PartInitException e) {
416                         ErrorLogger.defaultLogError(e);
417                     }
418                 }
419             }
420         }
421     }
422
423     /**
424      * Reinitializes all DiagramEditor instances in all workbench windows that have
425      * the specified <code>diagram</code> as their input.
426      * 
427      * <p>Must be invoked from the SWT thread.</p>
428      * 
429      * @param diagram
430      *            the diagram resource for which to reinitialize all DiagramEditors
431      *            for
432      */
433     public static void reinitializeDiagram(Resource diagram) {
434         reinitializeDiagram(input -> input instanceof IResourceEditorInput
435                 && ((IResourceEditorInput) input).getResource().equals(diagram));
436     }
437
438     /**
439      * Reinitializes all DiagramEditor instances in all workbench windows that have
440      * the specified <code>diagrams</code> as their input.
441      * 
442      * <p>Must be invoked from the SWT thread.</p>
443      * 
444      * @param diagrams
445      *            collection of diagram resources for which to reinitialize all DiagramEditors
446      *            for
447      */
448     public static void reinitializeDiagram(Set<Resource> diagrams) {
449         reinitializeDiagram(input -> input instanceof IResourceEditorInput
450                 && diagrams.contains(((IResourceEditorInput) input).getResource()));
451     }
452
453 }