]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.views.swt/src/org/simantics/views/swt/ModelledView.java
Fixed multiple issues causing dangling references to discarded queries
[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.common.ErrorLogger;
31 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport;
32 import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl;
33 import org.simantics.db.ReadGraph;
34 import org.simantics.db.Resource;
35 import org.simantics.db.VirtualGraph;
36 import org.simantics.db.WriteGraph;
37 import org.simantics.db.common.request.WriteRequest;
38 import org.simantics.db.common.request.WriteResultRequest;
39 import org.simantics.db.exception.DatabaseException;
40 import org.simantics.db.exception.RuntimeDatabaseException;
41 import org.simantics.db.layer0.util.RemoverUtil;
42 import org.simantics.db.layer0.variable.Variable;
43 import org.simantics.db.management.ISessionContext;
44 import org.simantics.db.request.Read;
45 import org.simantics.layer0.Layer0;
46 import org.simantics.scenegraph.ontology.ScenegraphResources;
47 import org.simantics.scl.runtime.function.Function1;
48 import org.simantics.ui.workbench.IPropertyPage;
49 import org.simantics.utils.ui.jface.ActiveSelectionProvider;
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     ActiveSelectionProvider                selectionProvider = new ActiveSelectionProvider() {
95         @Override
96         public void setSelection(ISelection selection) {
97             super.setSelection(selection);
98         }
99     };
100
101     protected String configurationURI() {
102         return configurationURI;
103     }
104
105     @Override
106     protected WidgetSupportImpl createSupport() {
107
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 (RuntimeDatabaseException | DatabaseException e) {
121             LOGGER.error("Failed to initialize modelled widget support runtime scenegraph", e);
122         }
123
124         support = new ModelledSupport(this);
125
126         return support;
127
128     }
129
130     public void fireInput() {
131         if (onInputChanged != null)
132             onInputChanged.apply(viewVariable);
133     }
134
135     @Override
136     public void setInitializationData(IConfigurationElement cfig, String propertyName, Object data) {
137         super.setInitializationData(cfig, propertyName, data);
138         if (data instanceof String) {
139             String[] parameters = ((String) data).split(";");
140
141             for (String parameter : parameters) {
142                 String[] keyValue = parameter.split("=");
143                 if (keyValue.length > 2) {
144                     ErrorLogger.defaultLogWarning("Invalid parameter '" + parameter + ". Complete view argument: "
145                             + data, null);
146                     continue;
147                 }
148                 String key = keyValue[0];
149                 String value = keyValue.length > 1 ? keyValue[1] : "";
150
151                 if ("configurationURI".equals(key)) {
152                     configurationURI = value;
153                 }
154             }
155         }
156     }
157
158     protected void doCreateControls(boolean load) {
159         if (DEBUG)
160             System.out.println(this + " doCreateControls(" + load + ")");
161
162         if (container == null) {
163             GridLayoutFactory.fillDefaults().applyTo(body);
164             container = new Composite(body, SWT.NONE);
165             GridDataFactory.fillDefaults().grab(true, true).applyTo(container);
166             GridLayoutFactory.fillDefaults().applyTo(container);
167         }
168
169         if (load) {
170
171             try {
172
173                 loader = new SWTViewLoaderProcess(this, getSite(), getClass().getSimpleName());
174
175                 viewVariable = loader.getVariable(Simantics.getSession(), configurationURI(), runtime);
176
177                 onInputChanged = Simantics.getSession().syncRequest(new Read<Function1<Variable, Boolean>>() {
178
179                     @Override
180                     public Function1<Variable, Boolean> perform(ReadGraph graph) throws DatabaseException {
181                         return viewVariable.getPossiblePropertyValue(graph, "onInputChanged");
182                     }
183
184                 });
185
186                 root = loader.load(Simantics.getSession(), viewVariable);
187                 root.createControls(container);
188                 root.getControl().addListener(SWT.Dispose, new Listener() {
189
190                     final SWTViewLoaderProcess oldLoader = ModelledView.this.loader;
191
192                     @Override
193                     public void handleEvent(Event event) {
194
195                         if (oldLoader != null && !oldLoader.isDisposed())
196                             oldLoader.dispose();
197
198                     }
199
200                 });
201
202                 body.layout(true);
203
204                 getSite().setSelectionProvider(selectionProvider);
205
206             } catch (DatabaseException e) {
207
208                 LOGGER.error("Failed to create modelled controls", e);
209
210             }
211
212         }
213
214     }
215
216     @Override
217     protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support) {
218         this.body = body;
219
220         // Only create controls if the part is TRULY visible.
221         // Fast view parts seem to cause calls to createPartControl even
222         // when the part is hidden in reality
223         boolean visible = site.getPage().isPartVisible(this);
224         if (DEBUG)
225             System.out.println(this + ": createControls( visible=" + site.getPage().isPartVisible(this) + " )");
226         doCreateControls(true);
227
228         getSite().setSelectionProvider(selectionProvider);
229         getSite().getPage().addPartListener(this);
230
231     }
232
233     protected void inputChanged(IWorkbenchPart provider, Object input) {
234         // Do not accept selections from self
235         if (provider == this)
236             return;
237         applySessionContext(getSessionContext());
238     }
239
240     @Override
241     public void setFocus() {
242         if (root != null && !root.isNodeDisposed())
243             root.setFocus();
244     }
245
246     public void setVisible(boolean value) {
247         if (root != null && !root.isNodeDisposed())
248             root.setVisible(value);
249     }
250
251     @Override
252     public void dispose() {
253
254         disposeRuntime(runtime);
255
256         IWorkbenchSite site = getSite();
257         if (site != null) {
258             IWorkbenchPage page = site.getPage();
259             if (page != null) {
260                 page.removePartListener(this);
261             }
262         }
263
264         if (root != null) {
265             root.cleanup();
266             root = null;
267         }
268         if (loader != null) {
269             loader.dispose();
270             loader = null;
271         }
272         if (support != null) {
273             support.dispose();
274             support = null;
275         }
276
277         super.dispose();
278         
279     }
280
281     protected void disposeRuntime(Resource runtime) {
282         final Resource rt = this.runtime;
283         this.runtime = null;
284         if (rt == null)
285             return;
286
287         try {
288             Simantics.getSession().sync(new WriteRequest(Simantics.getSession().getService(VirtualGraph.class)) {
289                 @Override
290                 public void perform(WriteGraph graph) throws DatabaseException {
291                     RemoverUtil.remove(graph, rt);
292                 }
293             });
294         } catch (RuntimeDatabaseException | DatabaseException e) {
295             LOGGER.error("Failed to dispose of the modelled widget support runtime scenegraph", e);
296         }
297     }
298
299     @Override
300     public void partActivated(IWorkbenchPartReference partRef) {
301         if (DEBUG) {
302             IWorkbenchPart part = partRef.getPart(false);
303             if (this.equals(part)) {
304                 System.out.println(this + ": ACTIVATED ( loader=" + loader + ", visible="
305                         + getSite().getPage().isPartVisible(part) + " )");
306             }
307         }
308     }
309
310     @Override
311     public void partBroughtToTop(IWorkbenchPartReference partRef) {
312         if (DEBUG) {
313             IWorkbenchPart part = partRef.getPart(false);
314             if (this.equals(part)) {
315                 System.out.println(this + ": BROUGHT TO TOP ( loader=" + loader + ", visible="
316                         + getSite().getPage().isPartVisible(part) + " )");
317             }
318         }
319     }
320
321     @Override
322     public void partClosed(IWorkbenchPartReference partRef) {
323         if (DEBUG) {
324             IWorkbenchPart part = partRef.getPart(false);
325             if (this.equals(part)) {
326                 System.out.println(this + ": CLOSED ( loader=" + loader + ", visible="
327                         + getSite().getPage().isPartVisible(part) + " )");
328             }
329         }
330     }
331
332     @Override
333     public void partDeactivated(IWorkbenchPartReference partRef) {
334         IWorkbenchPart part = partRef.getPart(false);
335         if (this.equals(part)) {
336             if (DEBUG)
337                 System.out.println(this + ": DEACTIVATED ( loader=" + loader + ", visible="
338                         + getSite().getPage().isPartVisible(part) + " )");
339             // clearExisting();
340         }
341     }
342
343     @Override
344     public void partOpened(IWorkbenchPartReference partRef) {
345         if (DEBUG) {
346             IWorkbenchPart part = partRef.getPart(false);
347             if (this.equals(part)) {
348                 System.out.println(this + ": OPENED ( loader=" + loader + ", visible="
349                         + getSite().getPage().isPartVisible(part) + " )");
350             }
351         }
352     }
353
354     @Override
355     public void partInputChanged(IWorkbenchPartReference partRef) {
356     }
357
358     @Override
359     public void partHidden(IWorkbenchPartReference partRef) {
360         IWorkbenchPart part = partRef.getPart(false);
361         if (this.equals(part)) {
362             if (DEBUG)
363                 System.out.println(this + ": HID ( loader=" + loader + ", visible="
364                         + getSite().getPage().isPartVisible(part) + " )");
365             clearExisting();
366         }
367     }
368
369     @Override
370     public void partVisible(IWorkbenchPartReference partRef) {
371         IWorkbenchPart part = partRef.getPart(false);
372         if (this.equals(part)) {
373             if (DEBUG)
374                 System.out.println(this + ": MADE VISIBLE ( loader=" + loader + ", visible="
375                         + getSite().getPage().isPartVisible(part) + " )");
376             createControlsIfNecessary(false);
377         }
378     }
379
380     private void createControlsIfNecessary(boolean forceContainerRepaint) {
381         // Cancel potential dispose before creating controls
382         reallyClearExisting = false;
383         if (loader == null) {
384             doCreateControls(true);
385             body.layout(true);
386
387             if (forceContainerRepaint) {
388                 container.layout(true);
389                 Point size = container.getSize();
390                 container.redraw(0, 0, size.x, size.y, true);
391                 container.update();
392             }
393         }
394     }
395     
396     // Can be used to cancel already scheduled dispose
397     volatile boolean reallyClearExisting = false;
398     
399     Runnable clearExisting = new Runnable() {
400
401         @Override
402         public void run() {
403             if(!reallyClearExisting)
404                 return;
405             viewVariable = null;
406             onInputChanged = null;
407
408             if (loader != null) {
409                 loader.dispose();
410                 loader = null;
411             }
412             if (container != null) {
413
414                 final Composite oldContainer = container;
415                 Display.getCurrent().asyncExec(new Runnable() {
416                     @Override
417                     public void run() {
418                         if (!oldContainer.isDisposed())
419                             oldContainer.dispose();
420                     }
421                 });
422
423                 if (!container.isDisposed())
424                     GridDataFactory.fillDefaults().exclude(true).applyTo(container);
425                 container = null;
426
427             }
428
429             root = null;
430         }
431         
432     };
433
434     private void clearExisting() {
435         Display.getCurrent().timerExec(TIME_BEFORE_DISPOSE_WHEN_HIDDEN, clearExisting);
436         
437         // Do this after scheduling the runnable, because otherwise already scheduled runnable could 
438         // get the flag.
439         reallyClearExisting = true;
440     }
441
442     @Override
443     protected IPropertyPage getPropertyPage() {
444         return null;
445     }
446     
447 }