]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.views.swt/src/org/simantics/views/swt/ModelledView.java
92396f421de9d37e0b9673d8989d3ca894db64eb
[simantics/platform.git] / bundles / org.simantics.views.swt / src / org / simantics / views / swt / ModelledView.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 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  *******************************************************************************/
12 package org.simantics.views.swt;
13
14 import org.eclipse.core.runtime.IConfigurationElement;
15 import org.eclipse.jface.layout.GridDataFactory;
16 import org.eclipse.jface.layout.GridLayoutFactory;
17 import org.eclipse.jface.viewers.ISelection;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.graphics.Point;
20 import org.eclipse.swt.widgets.Composite;
21 import org.eclipse.swt.widgets.Display;
22 import org.eclipse.swt.widgets.Event;
23 import org.eclipse.swt.widgets.Listener;
24 import org.eclipse.ui.IPartListener2;
25 import org.eclipse.ui.IWorkbenchPage;
26 import org.eclipse.ui.IWorkbenchPart;
27 import org.eclipse.ui.IWorkbenchPartReference;
28 import org.eclipse.ui.IWorkbenchSite;
29 import org.simantics.Simantics;
30 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport;
31 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl;
32 import org.simantics.db.ReadGraph;
33 import org.simantics.db.Resource;
34 import org.simantics.db.VirtualGraph;
35 import org.simantics.db.WriteGraph;
36 import org.simantics.db.common.request.WriteRequest;
37 import org.simantics.db.common.request.WriteResultRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.exception.ServiceNotFoundException;
40 import org.simantics.db.layer0.util.RemoverUtil;
41 import org.simantics.db.layer0.variable.Variable;
42 import org.simantics.db.management.ISessionContext;
43 import org.simantics.db.request.Read;
44 import org.simantics.layer0.Layer0;
45 import org.simantics.scenegraph.ontology.ScenegraphResources;
46 import org.simantics.scl.runtime.function.Function1;
47 import org.simantics.ui.workbench.IPropertyPage;
48 import org.simantics.utils.ui.ErrorLogger;
49 import org.simantics.utils.ui.jface.BasePostSelectionProvider;
50 import org.simantics.views.swt.client.base.SWTRoot;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * To use this class, first model your view contents in .pgraph files according
56  * to the Browsing.pgraph ontology. After that there are two ways to put your
57  * configuration to use by defining a new view extension:
58  * <ol>
59  * <li>Set view extension class to
60  * <code>org.simantics.browsing.ui.swt.ModelledView:configurationURI=ConfigURI</code>
61  * , where ConfigURI is the URI of your view configuration.</li>
62  * <li>Extend this class and override at least {@link #configurationURI()} to
63  * define the URI from which the configuration for the view is found. Set view
64  * extension class to the created class.</li>
65  * </ol>
66  * 
67  * @author Antti Villberg
68  */
69 public class ModelledView extends SimanticsView implements IPartListener2 {
70
71     private static final Logger LOGGER = LoggerFactory.getLogger(ModelledView.class);
72
73     public static final int TIME_BEFORE_DISPOSE_WHEN_HIDDEN = 30000; // ms
74     
75     private static final boolean           DEBUG             = false;
76
77     protected Resource                     runtime;
78     protected String                       configurationURI;
79
80     protected SWTRoot                      root;
81
82     protected Variable                     viewVariable;
83
84     protected Function1<Variable, Boolean> onInputChanged    = null;
85
86     protected SWTViewLoaderProcess         loader;
87
88     protected Composite                    body;
89
90     protected Composite                    container;
91
92     protected ModelledSupport              support;
93
94     private BasePostSelectionProvider      selectionProvider = new BasePostSelectionProvider() {
95         @Override
96         public void setSelection(ISelection selection) {
97             super.setAndFireNonEqualSelection(selection);
98             super.firePostSelection(selection);
99         }
100     };
101
102     protected String configurationURI() {
103         return configurationURI;
104     }
105
106     @Override
107     protected WidgetSupportImpl createSupport() {
108         try {
109             runtime = Simantics.getSession().sync(
110                     new WriteResultRequest<Resource>(Simantics.getSession().getService(VirtualGraph.class)) {
111                         @Override
112                         public Resource perform(WriteGraph graph) throws DatabaseException {
113                             Layer0 L0 = Layer0.getInstance(graph);
114                             ScenegraphResources SG = ScenegraphResources.getInstance(graph);
115                             Resource runtime = graph.newResource();
116                             graph.claim(runtime, L0.InstanceOf, null, SG.Runtime);
117                             return runtime;
118                         }
119                     });
120         } catch (ServiceNotFoundException | DatabaseException e) {
121             LOGGER.error("Failed to initialize modelled view database runtime support structures", e);
122         }
123
124         support = new ModelledSupport(this);
125
126         return support;
127     }
128
129     public void fireInput() {
130         if (onInputChanged != null)
131             onInputChanged.apply(viewVariable);
132     }
133
134     @Override
135     public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
136         super.setInitializationData(cfig, propertyName, data);
137         if (data instanceof String) {
138             String[] parameters = ((String) data).split(";");
139
140             for (String parameter : parameters) {
141                 String[] keyValue = parameter.split("=");
142                 if (keyValue.length > 2) {
143                     ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: "
144                             + data, null);
145                     continue;
146                 }
147                 String key = keyValue[0];
148                 String value = keyValue.length > 1 ? keyValue[1] : "";
149
150                 if ("configurationURI".equals(key)) {
151                     configurationURI = value;
152                 }
153             }
154         }
155     }
156
157     protected void doCreateControls(boolean load) {
158         if (DEBUG)
159             System.out.println(this + " doCreateControls(" + load + ")");
160
161         if (container == null) {
162             GridLayoutFactory.fillDefaults().applyTo(body);
163             container = new Composite(body, SWT.NONE);
164             GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
165             GridLayoutFactory.fillDefaults().applyTo(container);
166         }
167
168         if (load) {
169             try {
170                 loader = new SWTViewLoaderProcess(this, getSite(), getClass().getSimpleName());
171
172                 viewVariable = loader.getVariable(Simantics.getSession(), configurationURI(), runtime);
173
174                 onInputChanged = Simantics.getSession().syncRequest(new Read<Function1<Variable, Boolean>>() {
175
176                     @Override
177                     public Function1<Variable, Boolean> perform(ReadGraph graph) throws DatabaseException {
178                         return viewVariable.getPossiblePropertyValue(graph, "onInputChanged");
179                     }
180
181                 });
182
183                 root = loader.load(Simantics.getSession(), viewVariable);
184                 root.createControls(container);
185                 root.getControl().addListener(SWT.Dispose, new Listener() {
186
187                     final SWTViewLoaderProcess oldLoader = ModelledView.this.loader;
188
189                     @Override
190                     public void handleEvent(Event event) {
191
192                         if (oldLoader != null && !oldLoader.isDisposed())
193                             oldLoader.dispose();
194
195                     }
196
197                 });
198
199                 body.layout(true);
200
201             } catch (DatabaseException e) {
202                 LOGGER.error("Failed to create modelled SWT view controls", e);
203             }
204         }
205     }
206
207     @Override
208     protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) {
209         this.body = body;
210
211         // Only create controls if the part is TRULY visible.
212         // Fast view parts seem to cause calls to createPartControl even
213         // when the part is hidden in reality
214         if (DEBUG)
215             System.out.println(this + ": createControls( visible=" + site.getPage().isPartVisible(this) + " )");
216         doCreateControls(true);
217
218         getSite().setSelectionProvider(selectionProvider);
219         getSite().getPage().addPartListener(this);
220     }
221
222     protected void inputChanged(IWorkbenchPart provider, Object input) {
223         // Do not accept selections from self
224         if (provider == this)
225             return;
226         applySessionContext(getSessionContext());
227     }
228
229     @Override
230     public void setFocus() {
231         if (root != null && !root.isNodeDisposed())
232             root.setFocus();
233     }
234
235     public void setVisible(boolean value) {
236         if (root != null && !root.isNodeDisposed())
237             root.setVisible(value);
238     }
239
240     @Override
241     public void dispose() {
242
243         disposeRuntime(runtime);
244
245         IWorkbenchSite site = getSite();
246         if (site != null) {
247             IWorkbenchPage page = site.getPage();
248             if (page != null) {
249                 page.removePartListener(this);
250             }
251         }
252
253         if (root != null) {
254             root.cleanup();
255             root = null;
256         }
257         if (loader != null) {
258             loader.dispose();
259             loader = null;
260         }
261         if (support != null) {
262             support.dispose();
263             support = null;
264         }
265
266         super.dispose();
267         
268     }
269
270     protected void disposeRuntime(Resource runtime) {
271         final Resource rt = this.runtime;
272         this.runtime = null;
273         if (rt == null)
274             return;
275
276         try {
277             Simantics.getSession().sync(new WriteRequest(Simantics.getSession().getService(VirtualGraph.class)) {
278                 @Override
279                 public void perform(WriteGraph graph) throws DatabaseException {
280                     RemoverUtil.remove(graph, rt);
281                 }
282             });
283         } catch (ServiceNotFoundException | DatabaseException e) {
284             LOGGER.error("Failed to dispose of modelled view database runtime support structures", e);
285         }
286     }
287
288     @Override
289     public void partActivated(IWorkbenchPartReference partRef) {
290         if (DEBUG) {
291             IWorkbenchPart part = partRef.getPart(false);
292             if (this.equals(part)) {
293                 System.out.println(this + ": ACTIVATED ( loader=" + loader + ", visible="
294                         + getSite().getPage().isPartVisible(part) + " )");
295             }
296         }
297     }
298
299     @Override
300     public void partBroughtToTop(IWorkbenchPartReference partRef) {
301         if (DEBUG) {
302             IWorkbenchPart part = partRef.getPart(false);
303             if (this.equals(part)) {
304                 System.out.println(this + ": BROUGHT TO TOP ( loader=" + loader + ", visible="
305                         + getSite().getPage().isPartVisible(part) + " )");
306             }
307         }
308     }
309
310     @Override
311     public void partClosed(IWorkbenchPartReference partRef) {
312         if (DEBUG) {
313             IWorkbenchPart part = partRef.getPart(false);
314             if (this.equals(part)) {
315                 System.out.println(this + ": CLOSED ( loader=" + loader + ", visible="
316                         + getSite().getPage().isPartVisible(part) + " )");
317             }
318         }
319     }
320
321     @Override
322     public void partDeactivated(IWorkbenchPartReference partRef) {
323         IWorkbenchPart part = partRef.getPart(false);
324         if (this.equals(part)) {
325             if (DEBUG)
326                 System.out.println(this + ": DEACTIVATED ( loader=" + loader + ", visible="
327                         + getSite().getPage().isPartVisible(part) + " )");
328             // clearExisting();
329         }
330     }
331
332     @Override
333     public void partOpened(IWorkbenchPartReference partRef) {
334         if (DEBUG) {
335             IWorkbenchPart part = partRef.getPart(false);
336             if (this.equals(part)) {
337                 System.out.println(this + ": OPENED ( loader=" + loader + ", visible="
338                         + getSite().getPage().isPartVisible(part) + " )");
339             }
340         }
341     }
342
343     @Override
344     public void partInputChanged(IWorkbenchPartReference partRef) {
345     }
346
347     @Override
348     public void partHidden(IWorkbenchPartReference partRef) {
349         IWorkbenchPart part = partRef.getPart(false);
350         if (this.equals(part)) {
351             if (DEBUG)
352                 System.out.println(this + ": HID ( loader=" + loader + ", visible="
353                         + getSite().getPage().isPartVisible(part) + " )");
354             clearExisting();
355         }
356     }
357
358     @Override
359     public void partVisible(IWorkbenchPartReference partRef) {
360         IWorkbenchPart part = partRef.getPart(false);
361         if (this.equals(part)) {
362             if (DEBUG)
363                 System.out.println(this + ": MADE VISIBLE ( loader=" + loader + ", visible="
364                         + getSite().getPage().isPartVisible(part) + " )");
365             createControlsIfNecessary(false);
366         }
367     }
368
369     private void createControlsIfNecessary(boolean forceContainerRepaint) {
370         // Cancel potential dispose before creating controls
371         reallyClearExisting = false;
372         if (loader == null) {
373             doCreateControls(true);
374             body.layout(true);
375
376             if (forceContainerRepaint) {
377                 container.layout(true);
378                 Point size = container.getSize();
379                 container.redraw(0, 0, size.x, size.y, true);
380                 container.update();
381             }
382         }
383     }
384
385     // Can be used to cancel already scheduled dispose
386     volatile boolean reallyClearExisting = false;
387
388     Runnable clearExisting = new Runnable() {
389
390         @Override
391         public void run() {
392             if(!reallyClearExisting)
393                 return;
394             viewVariable = null;
395             onInputChanged = null;
396
397             if (loader != null) {
398                 loader.dispose();
399                 loader = null;
400             }
401             if (container != null) {
402
403                 final Composite oldContainer = container;
404                 Display.getCurrent().asyncExec(new Runnable() {
405                     @Override
406                     public void run() {
407                         if (!oldContainer.isDisposed())
408                             oldContainer.dispose();
409                     }
410                 });
411
412                 if (!container.isDisposed())
413                     GridDataFactory.fillDefaults().exclude(true).applyTo(container);
414                 container = null;
415
416             }
417
418             root = null;
419         }
420         
421     };
422
423     private void clearExisting() {
424         Display.getCurrent().timerExec(TIME_BEFORE_DISPOSE_WHEN_HIDDEN, clearExisting);
425         
426         // Do this after scheduling the runnable, because otherwise already scheduled runnable could 
427         // get the flag.
428         reallyClearExisting = true;
429     }
430
431     @Override
432     protected IPropertyPage getPropertyPage() {
433         return null;
434     }
435
436 }