]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.selectionview/src/org/simantics/selectionview/PropertyTable.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.selectionview / src / org / simantics / selectionview / PropertyTable.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.selectionview;
13
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.function.Consumer;
18
19 import org.eclipse.jface.action.ContributionItem;
20 import org.eclipse.jface.action.IContributionItem;
21 import org.eclipse.jface.layout.GridDataFactory;
22 import org.eclipse.jface.resource.JFaceResources;
23 import org.eclipse.jface.resource.LocalResourceManager;
24 import org.eclipse.jface.viewers.IPostSelectionProvider;
25 import org.eclipse.jface.viewers.ISelection;
26 import org.eclipse.jface.viewers.ISelectionProvider;
27 import org.eclipse.jface.viewers.IStructuredSelection;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.dnd.DND;
30 import org.eclipse.swt.dnd.DragSource;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Event;
34 import org.eclipse.swt.widgets.Listener;
35 import org.eclipse.swt.widgets.ToolBar;
36 import org.eclipse.swt.widgets.ToolItem;
37 import org.eclipse.swt.widgets.Tree;
38 import org.eclipse.ui.IActionBars;
39 import org.eclipse.ui.IWorkbenchSite;
40 import org.simantics.browsing.ui.GraphExplorer;
41 import org.simantics.browsing.ui.common.ColumnKeys;
42 import org.simantics.browsing.ui.common.EvaluatorData;
43 import org.simantics.browsing.ui.common.EvaluatorData.Evaluator;
44 import org.simantics.browsing.ui.common.EvaluatorDataImpl;
45 import org.simantics.browsing.ui.common.comparators.AlphanumericComparatorFactory;
46 import org.simantics.browsing.ui.common.processors.ComparableFactoryResolver;
47 import org.simantics.browsing.ui.common.processors.ComparableSelectorQueryProcessor;
48 import org.simantics.browsing.ui.common.processors.FilterSelectionRequestQueryProcessor;
49 import org.simantics.browsing.ui.common.processors.ImageDecoratorFactoryResolver;
50 import org.simantics.browsing.ui.common.processors.ImagerFactoryResolver;
51 import org.simantics.browsing.ui.common.processors.LabelDecoratorFactoryResolver;
52 import org.simantics.browsing.ui.common.processors.LabelerFactoryResolver;
53 import org.simantics.browsing.ui.common.processors.UserSelectedComparableFactoryQueryProcessor;
54 import org.simantics.browsing.ui.common.processors.UserSelectedViewpointFactoryQueryProcessor;
55 import org.simantics.browsing.ui.common.processors.ViewpointFactoryResolver;
56 import org.simantics.browsing.ui.common.views.IFilterArea;
57 import org.simantics.browsing.ui.common.views.IFilterAreaProvider;
58 import org.simantics.browsing.ui.common.views.PropertyTableConstants;
59 import org.simantics.browsing.ui.graph.impl.ArrayPropertyLabelerFactory;
60 import org.simantics.browsing.ui.graph.impl.ArrayPropertyValueViewpointFactory;
61 import org.simantics.browsing.ui.graph.impl.AsyncReadGraphDataSource;
62 import org.simantics.browsing.ui.graph.impl.PropertyViewpointFactory;
63 import org.simantics.browsing.ui.graph.impl.ReadGraphDataSource;
64 import org.simantics.browsing.ui.graph.impl.RelationContextLabelerFactory;
65 import org.simantics.browsing.ui.graph.impl.ResourceProperty;
66 import org.simantics.browsing.ui.graph.impl.StringRepresentationLabelerFactory;
67 import org.simantics.browsing.ui.swt.ArrayPropertyImagerFactory;
68 import org.simantics.browsing.ui.swt.ComparatorSelector;
69 import org.simantics.browsing.ui.swt.ContextMenuInitializer;
70 import org.simantics.browsing.ui.swt.GraphExplorerFactory;
71 import org.simantics.browsing.ui.swt.PropertyTableUtil;
72 import org.simantics.browsing.ui.swt.RootFilterArea;
73 import org.simantics.browsing.ui.swt.TypesQueryProcessor;
74 import org.simantics.browsing.ui.swt.ViewpointSelector;
75 import org.simantics.db.AsyncReadGraph;
76 import org.simantics.db.ReadGraph;
77 import org.simantics.db.RelationContext;
78 import org.simantics.db.Resource;
79 import org.simantics.db.Session;
80 import org.simantics.db.Statement;
81 import org.simantics.db.common.ResourceArray;
82 import org.simantics.db.management.ISessionContext;
83 import org.simantics.ui.dnd.BasicDragSource;
84 import org.simantics.ui.dnd.SessionContainer;
85 import org.simantics.ui.utils.ResourceAdaptionUtils;
86 import org.simantics.utils.ObjectUtils;
87 import org.simantics.utils.ui.LayoutUtils;
88
89
90 /**
91  * <p>
92  * Subclasses may extend or reimplement the following methods as required:
93  * <ul>
94  *   <li><code>createExplorerControl</code> - reimplement to customize the way the IGraphExplorer control is created</li>
95  *   <li><code>createControl</code> - reimplement to customize the way other supplementary controls get created</li>
96  *   <li><code>createDragSource</code> - reimplement to customize the kind of DragSource this control provides</li>
97  * </ul>
98  * </p>
99  * 
100  * @author Tuukka Lehtonen
101  */
102 public class PropertyTable extends Composite implements IFilterAreaProvider, IPropertyTab2 {
103
104     protected IWorkbenchSite                              site;
105     protected GraphExplorer                               explorer;
106     protected Object                                      dragSource;
107     protected ISessionContext                             sessionContext;
108     protected ISelection                                  currentInput;
109
110     protected UserSelectedComparableFactoryQueryProcessor userSelectedComparableFactoryQueryProcessor;
111     protected UserSelectedViewpointFactoryQueryProcessor  userSelectedViewpointFactoryQueryProcessor;
112     protected FilterSelectionRequestQueryProcessor        filterSelectionRequestQueryProcessor;
113
114     protected LocalResourceManager resourceManager;
115
116     private RootFilterArea filterArea;
117     private IContributionItem filterAreaContribution;
118     private IActionBars actionBars;
119
120     public PropertyTable(IWorkbenchSite site, Composite parent, int style) {
121         super(parent, style);
122         this.site = site;
123         //System.out.println("created " + this + " " + System.identityHashCode(this));
124         resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay()));
125
126         addListener(SWT.Dispose, new Listener() {
127             @Override
128             public void handleEvent(Event event) {
129                 //System.out.println("DISPOSING " + this + " " + System.identityHashCode(PropertyTable.this));
130                 tableDisposed();
131
132                 PropertyTableUtil.setupDragSource(dragSource, null);
133
134                 PropertyTable.this.site = null;
135                 explorer = null;
136                 dragSource = null;
137                 sessionContext = null;
138                 currentInput = null;
139
140                 userSelectedComparableFactoryQueryProcessor = null;
141                 userSelectedViewpointFactoryQueryProcessor = null;
142                 filterSelectionRequestQueryProcessor = null;
143
144                 resourceManager.dispose();
145             }
146         });
147     }
148
149     /**
150      * Override to perform actions when this property table is disposed.
151      * Remember to call super.
152      */
153     protected void tableDisposed() {
154         getDisplay().asyncExec(new Runnable() {
155             @Override
156             public void run() {
157                 if (filterAreaContribution != null) {
158                     actionBars.getToolBarManager().remove(filterAreaContribution);
159                     filterAreaContribution.dispose();
160                     filterAreaContribution = null;
161                     actionBars.updateActionBars();
162                 }
163             }
164         });
165     }
166
167     @Override
168     public Control getControl() {
169         return this;
170     }
171
172     @Override
173     public void createControl(final Composite parent, ISessionContext context) {
174         explorer = createExplorerControl(parent);
175         dragSource = createDragSource(explorer);
176
177         Control control = explorer.getControl();
178
179         userSelectedComparableFactoryQueryProcessor = new UserSelectedComparableFactoryQueryProcessor();
180         userSelectedViewpointFactoryQueryProcessor = new UserSelectedViewpointFactoryQueryProcessor();
181         filterSelectionRequestQueryProcessor = new FilterSelectionRequestQueryProcessor();
182
183         parent.setLayout(LayoutUtils.createNoBorderGridLayout(2, false));
184         GridDataFactory.fillDefaults().grab(true, true).span(2,1).applyTo(control);
185
186         addControls(parent);
187         setSessionContext(context);
188     }
189
190     protected void addControls(Composite parent) {
191         //createSelectors(parent);
192         //createFilterArea(parent);
193     }
194
195     protected void createSelectors(Composite parent) {
196         Control control = explorer.getControl();
197         ComparatorSelector comparatorSelector = new ComparatorSelector(explorer, userSelectedComparableFactoryQueryProcessor, parent, SWT.READ_ONLY);
198         comparatorSelector.moveAbove(control);
199         ViewpointSelector viewpointSelector = new ViewpointSelector(explorer, userSelectedViewpointFactoryQueryProcessor, parent, SWT.READ_ONLY);
200         viewpointSelector.moveAbove(control);
201     }
202
203     protected void createFilterArea(IActionBars actionBars) {
204         this.filterAreaContribution = new ContributionItem("filterArea") {
205             ToolItem item;
206             @Override
207             public boolean isDynamic() {
208                 return true;
209             }
210             @Override
211             public final void fill(ToolBar parent, int index) {
212                 Control control = createControl(parent);
213                 item = new ToolItem(parent, SWT.SEPARATOR, index);
214                 item.setControl(control);
215                 item.setWidth(computeWidth(control));
216             }
217             protected Control createControl(Composite parent) {
218                 filterArea = new RootFilterArea(explorer, filterSelectionRequestQueryProcessor, parent, SWT.NONE);
219                 return filterArea;
220             }
221             protected int computeWidth(Control control) {
222                 // FIXME: better size control?
223                 return control.computeSize(150, SWT.DEFAULT, true).x;
224             }
225             @Override
226             public void dispose() {
227                 if (filterArea != null) {
228                     filterArea.dispose();
229                     filterArea = null;
230                 }
231                 if (item != null) {
232                     item.dispose();
233                     item = null;
234                 }
235             }
236         };
237         this.actionBars = actionBars;
238         actionBars.getToolBarManager().add(filterAreaContribution);
239     }
240
241     protected void createFilterArea(Composite parent) {
242         Control control = explorer.getControl();
243         filterArea = new RootFilterArea(explorer, filterSelectionRequestQueryProcessor, parent, SWT.NONE);
244         filterArea.moveAbove(control);
245     }
246
247     protected GraphExplorer createExplorerControl(Composite parent) {
248         GraphExplorer e = GraphExplorerFactory.getInstance().create(parent, SWT.FULL_SELECTION);
249         Control c = e.getControl();
250         ISelectionProvider selectionProvider = (ISelectionProvider)e.getAdapter(ISelectionProvider.class);
251         new ContextMenuInitializer("#PropertiesPopup").createContextMenu(c, selectionProvider, site);
252         if (c instanceof Tree)
253             ((Tree) c).setLinesVisible(true);
254         e.setColumns(PropertyTableConstants.NORMAL_COLUMNS);
255         return e;
256     }
257
258     /**
259      * Override to customize drag source initialization. This default
260      * implementation creates a {@link BasicDragSource}. The drag source is
261      * initialized when the active database session is set.
262      * 
263      * @param explorer
264      * @return the object representing the drag source. If the object implements
265      *         {@link SessionContainer}, its
266      *         {@link SessionContainer#setSession(Session)} will be invoked
267      *         every time the active database session changes.
268      */
269     protected Object createDragSource(GraphExplorer explorer) {
270         Control c = explorer.getControl();
271         DragSource existingSource = (DragSource) c.getData(DND.DRAG_SOURCE_KEY);
272         if (existingSource != null)
273             return existingSource;
274         return PropertyTableUtil.createResourceDragSource(explorer);
275     }
276
277     protected void setupDragSource(ISessionContext sessionContext) {
278         PropertyTableUtil.setupDragSource(dragSource, sessionContext);
279     }
280
281     @Override
282     public ISelectionProvider getSelectionProvider() {
283         GraphExplorer e = explorer;
284         if (e != null) {
285             IPostSelectionProvider selectionProvider = (IPostSelectionProvider)e.getAdapter(IPostSelectionProvider.class);
286             return selectionProvider;
287         }
288         return null;
289     }
290
291     @Override
292     public void requestFocus() {
293         GraphExplorer e = explorer;
294         if (e != null)
295             e.setFocus();
296     }
297
298     @Override
299     public IFilterArea getFilterArea() {
300         return filterArea;
301     }
302
303     protected ISessionContext getSessionContext() {
304         return sessionContext;
305     }
306
307     protected Session getSession() {
308         return sessionContext != null ? sessionContext.getSession() : null;
309     }
310
311     @Override
312     public void setInput(ISessionContext context, ISelection selection, boolean force) {
313         if (isDisposed())
314             return;
315         if (sessionContext == null)
316             return;
317
318         // Check if this is a duplicate of the previous selection to reduce unnecessary flicker.
319         if (!force && ObjectUtils.objectEquals(currentInput, selection))
320             return;
321
322         //Resource[] rs = ResourceAdaptionUtils.toResources(selection);
323         Object[] rs = toObjects(selection);
324 //      System.out.println("resources: " + Arrays.toString(rs));
325
326         if (rs.length == 0) {
327             explorer.setRoot(GraphExplorer.EMPTY_INPUT);
328         } else {
329             if (rs.length == 1) {
330                 explorer.setRoot(rs[0]);
331             } else {
332                 explorer.setRoot(rs);
333             }
334         }
335
336         currentInput = selection;
337     }
338
339     protected Object[] toObjects(ISelection s) {
340         ResourceArray[] ras = ResourceAdaptionUtils.toResourceArrays(s);
341         if (ras.length > 0)
342             return ras;
343         Resource[] rs = ResourceAdaptionUtils.toResources(s);
344         if (rs.length > 0)
345             return rs;
346
347         if (!(s instanceof IStructuredSelection))
348             return new Object[0];
349         IStructuredSelection ss = (IStructuredSelection) s;
350         List<Object> result = new ArrayList<Object>();
351         for (Iterator<?> iterator = ss.iterator(); iterator.hasNext();)
352             result.add(iterator.next());
353         return result.toArray();
354     }
355
356     public void setSessionContext(ISessionContext sessionContext) {
357         if (isDisposed())
358             return;
359
360 //        System.out.println("setSessionContext: " + sessionContext + " (" + this + ")");
361         if (this.sessionContext == sessionContext)
362             return;
363         this.sessionContext = sessionContext;
364         Session session = sessionContext != null ? sessionContext.getSession() : null;
365         reinitializeExplorer(explorer, sessionContext, session);
366     }
367
368     protected void reinitializeExplorer(GraphExplorer explorer, ISessionContext sessionContext, Session session) {
369         if (isDisposed())
370             return;
371
372         setupDragSource(sessionContext);
373
374         // TODO: synchronize the explorer somehow to make sure nothing running in its background is trying to modify the explorer during this process
375
376         EvaluatorData data = new EvaluatorDataImpl();
377
378         if (session != null) {
379             explorer.setDataSource(new AsyncReadGraphDataSource(session));
380             explorer.setDataSource(new ReadGraphDataSource(session));
381
382             Evaluator resourceEvaluator = data.newEvaluator()
383             .addViewpoint(new PropertyViewpointFactory(), 1.0)
384             .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY), 2.0)
385             .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY, true), 1.0)
386             .addLabeler(new StringRepresentationLabelerFactory(ColumnKeys.PROPERTY), 1.0);
387
388             Evaluator statementEvaluator = data.newEvaluator()
389             .addViewpoint(new PropertyViewpointFactory(), 1.0)
390             .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY), 2.0)
391             .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY, true), 1.0);
392
393             Evaluator relationContextEvaluator = data.newEvaluator()
394             .addLabeler(new RelationContextLabelerFactory(), 1.0);
395
396             Evaluator propertyEvaluator = data.newEvaluator()
397             .addViewpoint(new ArrayPropertyValueViewpointFactory(), 1.0)
398             .addLabeler(new ArrayPropertyLabelerFactory(), 1.0)
399             .addImager(new ArrayPropertyImagerFactory(), 1.0);
400
401             data.addEvaluator(Resource.class, resourceEvaluator);
402             data.addEvaluator(ResourceArray.class, resourceEvaluator);
403             data.addEvaluator(Statement.class, statementEvaluator);
404             data.addEvaluator(RelationContext.class, relationContextEvaluator);
405             data.addEvaluator(ResourceProperty.class, propertyEvaluator);
406
407         } else {
408             explorer.removeDataSource(AsyncReadGraph.class);
409             explorer.removeDataSource(ReadGraph.class);
410         }
411
412         explorer.setProcessor(new ComparableFactoryResolver(data));
413         explorer.setProcessor(new ViewpointFactoryResolver(data));
414         explorer.setProcessor(new LabelerFactoryResolver(data));
415         explorer.setProcessor(new ImagerFactoryResolver(data));
416         explorer.setProcessor(new LabelDecoratorFactoryResolver(data));
417         explorer.setProcessor(new ImageDecoratorFactoryResolver(data));
418         explorer.setPrimitiveProcessor(new TypesQueryProcessor());
419
420         explorer.setPrimitiveProcessor(userSelectedViewpointFactoryQueryProcessor);
421         explorer.setProcessor(new ComparableSelectorQueryProcessor());
422         explorer.setPrimitiveProcessor(userSelectedComparableFactoryQueryProcessor);
423         explorer.setPrimitiveProcessor(filterSelectionRequestQueryProcessor);
424
425         // Make sure the explorer gets reset.
426         explorer.setRoot(GraphExplorer.EMPTY_INPUT);
427     }
428
429     @Override
430     public void updatePartName(Consumer<String> updateCallback) {
431         ISelection forSelection = currentInput;
432         if (forSelection instanceof IStructuredSelection) {
433             IStructuredSelection s = (IStructuredSelection) forSelection;
434             int size = s.size();
435             if (size == 1) {
436                 Object obj = s.getFirstElement();
437                 if (obj != null)
438                     updateCallback.accept(obj.toString());
439                 else
440                     updateCallback.accept("Properties");
441             } else {
442                 updateCallback.accept("Properties [" + size + "]");
443             }
444         } else {
445             updateCallback.accept("Properties");
446         }
447     }
448
449 }