]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/VariablesPageView.java
cfa988b88c22300d918229d8e46f6da32e4d1ede
[simantics/platform.git] / bundles / org.simantics.browsing.ui.platform / src / org / simantics / browsing / ui / platform / VariablesPageView.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.browsing.ui.platform;
13
14 import org.eclipse.jface.resource.ImageDescriptor;
15 import org.eclipse.jface.resource.JFaceResources;
16 import org.eclipse.jface.resource.LocalResourceManager;
17 import org.eclipse.jface.resource.ResourceManager;
18 import org.eclipse.jface.viewers.ISelection;
19 import org.eclipse.swt.widgets.Composite;
20 import org.eclipse.swt.widgets.Display;
21 import org.eclipse.ui.IEditorPart;
22 import org.eclipse.ui.IMemento;
23 import org.eclipse.ui.IPropertyListener;
24 import org.eclipse.ui.ISelectionListener;
25 import org.eclipse.ui.IViewPart;
26 import org.eclipse.ui.IViewSite;
27 import org.eclipse.ui.IWorkbenchPage;
28 import org.eclipse.ui.IWorkbenchPart;
29 import org.eclipse.ui.PartInitException;
30 import org.eclipse.ui.contexts.IContextService;
31 import org.eclipse.ui.part.IContributedContentsView;
32 import org.eclipse.ui.part.IPage;
33 import org.eclipse.ui.part.PageBook;
34 import org.eclipse.ui.part.PageBookView;
35 import org.simantics.browsing.ui.swt.IVariablesPage;
36 import org.simantics.db.management.ISessionContextProvider;
37 import org.simantics.selectionview.PropertyPage;
38 import org.simantics.ui.SimanticsUI;
39 import org.simantics.ui.workbench.IPropertyPage;
40 import org.simantics.ui.workbench.ResourceInput;
41 import org.simantics.utils.ui.BundleUtils;
42
43 /**
44  * This is a version of the standard eclipse <code>PropertySheet</code> view a
45  * graph database access twist. It presents a property view based the active
46  * workbench part and the active part's current selection.
47  * 
48  * <p>
49  * To get a property page for your own view or editor part you can do one of the
50  * following:
51  * 
52  * <ol>
53  * <li>Implement getAdapter for your view or editor part as follows:
54  * 
55  * <pre>
56  * Object getAdapter(Class c) {
57  *     if (c == ISessionContextProvider.class)
58  *         // Get ISessionContextProvider from somewhere, i.e. SimanticsUI
59  *     if (c == IPropertyPage.class)
60  *         return new PropertyPage(getSite(), this);
61  * }
62  * </pre>
63  * 
64  * This method also allows customization of the actual property page control
65  * that gets created. <code>PropertyPage</code> serves as a good starting
66  * point for your own version.</li>
67  * <li>Make the workbench part implement the marker interface
68  * <code>IStandardPropertyPage</code> which will make this view do the above
69  * automatically without implementing <code>getAdapter</code>.</li>
70  * </ol>
71  * 
72  * @author Tuukka Lehtonen
73  * 
74  * @see IStandardPropertyPage
75  * @see IPropertyPage
76  * @see PropertyPage
77  */
78 public class VariablesPageView extends PageBookView implements ISelectionListener {
79
80     private static final String VARIABLES_VIEW_CONTEXT = "org.simantics.browsing.ui.variables";
81
82     private static final String     PROP_PINNED                = "pinned";
83
84     protected static final long     SELECTION_CHANGE_THRESHOLD = 500;
85
86     private ISessionContextProvider contextProvider;
87
88     /**
89      * The initial selection when the property sheet opens
90      */
91     private ISelection              bootstrapSelection;
92
93     /**
94      * A flag for indicating whether or not this view will only use the
95      * bootstrap selection and and IPropertyPage source instead of listening to
96      * the input constantly.
97      */
98     private final boolean                 bootstrapOnly = false;
99
100     private IMemento                memento;
101
102     private boolean                 pinSelection = false;
103
104     private IWorkbenchPart          lastPart;
105     private ISelection              lastSelection;
106
107     private ResourceManager         resourceManager;
108
109     private ImageDescriptor         notPinned;
110     private ImageDescriptor         pinned;
111
112     @Override
113     public void createPartControl(Composite parent) {
114         super.createPartControl(parent);
115
116         this.resourceManager = new LocalResourceManager(JFaceResources.getResources());
117         notPinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple.png");
118         pinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple_pinned.png");
119
120         IContextService cs = (IContextService) getSite().getService(IContextService.class);
121         cs.activateContext(VARIABLES_VIEW_CONTEXT);
122     }
123
124     /*
125      * (non-Javadoc)
126      * 
127      * @see org.eclipse.ui.part.PageBookView#getAdapter(java.lang.Class)
128      */
129     @SuppressWarnings("unchecked")
130     @Override
131     public <T> T getAdapter(Class<T> adapter) {
132         if (adapter == IContributedContentsView.class) {
133             // This makes it possible to duplicate a PropertyPageView with another
134             // secondary ID and make it show the same property page that was showing
135             // in the original property page view.
136             return (T) new IContributedContentsView() {
137                 @Override
138                 public IWorkbenchPart getContributingPart() {
139                     return getContributingEditor();
140                 }
141             };
142         }
143         if (adapter == ISessionContextProvider.class)
144             return (T) contextProvider;
145         return super.getAdapter(adapter);
146     }
147
148     /**
149      * Returns the editor which contributed the current
150      * page to this view.
151      *
152      * @return the editor which contributed the current page
153      * or <code>null</code> if no editor contributed the current page
154      */
155     private IWorkbenchPart getContributingEditor() {
156         return getCurrentContributingPart();
157     }
158
159     /* (non-Javadoc)
160      * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
161      */
162     @Override
163     public void init(IViewSite site, IMemento memento) throws PartInitException {
164         this.memento = memento;
165         init(site);
166     }
167
168     /*
169      * (non-Javadoc)
170      * 
171      * @see org.eclipse.ui.part.PageBookView#init(org.eclipse.ui.IViewSite)
172      */
173     @Override
174     public void init(IViewSite site) throws PartInitException {
175         String secondaryId = site.getSecondaryId();
176         if (secondaryId != null) {
177             ResourceInput input = ResourceInput.unmarshall(secondaryId);
178             if (input != null) {
179                 //bootstrapOnly = true;
180             }
181         }
182
183         //System.out.println("PPV init: " + this);
184         super.init(site);
185
186         // This prevents the Properties view from providing a selection to other
187         // workbench parts, thus making them lose their selections which is not
188         // desirable.
189         site.setSelectionProvider(null);
190
191         contextProvider = SimanticsUI.getSessionContextProvider();
192
193         if (!bootstrapOnly) {
194             site.getPage().addSelectionListener(immediateSelectionListener);
195             site.getPage().addPostSelectionListener(this);
196         }
197     }
198
199     @Override
200     public void saveState(IMemento memento) {
201         if (this.memento != null) {
202             memento.putMemento(this.memento);
203         }
204     }
205
206     /* (non-Javadoc)
207      * Method declared on IWorkbenchPart.
208      */
209     @Override
210     public void dispose() {
211         //System.out.println("PPV dispose: " + this);
212         // Dispose of this before nullifying contextProvider because this
213         // dispose may just need the context provider - at least PropertyPage
214         // disposal will.
215         super.dispose();
216
217         if (lastPart != null)
218             lastPart.removePropertyListener(partPropertyListener);
219
220         contextProvider = null;
221
222         // Remove ourselves as a workbench selection listener.
223         if (!bootstrapOnly) {
224             getSite().getPage().removePostSelectionListener(this);
225             getSite().getPage().removeSelectionListener(immediateSelectionListener);
226         }
227
228         if (resourceManager != null) {
229             resourceManager.dispose();
230             resourceManager = null;
231         }
232     }
233
234     @Override
235     protected IPage createDefaultPage(PageBook book) {
236         /*
237         MessagePage page = new MessagePage();
238         initPage(page);
239         page.createControl(book);
240         page.setMessage(Messages.PropertyPageView_noPropertiesAvailable);
241         return page;
242          */
243
244         VariablesPage page = new VariablesPage(getSite(), this);
245         page.setAdapter(this);
246         initPage(page);
247         page.createControl(book);
248         //System.out.println("PPV create default page: " + page);
249         return page;
250
251     }
252
253     @Override
254     protected PageRec doCreatePage(IWorkbenchPart part) {
255
256         return null;
257
258 //        // NOTE: If the default page should be shown, this method must return null.
259 //        if (part == null)
260 //            return null;
261 //
262 //        //System.out.println("PPV try to create page for part: " + (part != null ? part.getTitle() : null));
263 //
264 //        VariablesPage page = new VariablesPage(getSite(), this);
265 //
266 //        // Make sure that the adaptation is provided by this class
267 //        // in order not to leave the ISessionContextProvider adaptability
268 //        // up to clients to implement.
269 //        page.setAdapter(this);
270 //        //System.out.println("PPV created page: " + page);
271 //        if (page instanceof IPageBookViewPage) {
272 //            initPage((IPageBookViewPage) page);
273 //        }
274 //        page.createControl(getPageBook());
275 //        //System.out.println("PPV created page control: " + page.getControl());
276 //        return new PageRec(part, page);
277
278     }
279
280     @Override
281     protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
282         //System.out.println("PPV destroy page for part: " + part.getTitle());
283
284         IPropertyPage page = (IPropertyPage) pageRecord.page;
285         page.dispose();
286         pageRecord.dispose();
287     }
288
289     @Override
290     protected IWorkbenchPart getBootstrapPart() {
291         IWorkbenchPage page = getSite().getPage();
292         if (page != null) {
293             bootstrapSelection = page.getSelection();
294             return page.getActivePart();
295         }
296         return null;
297     }
298
299     @Override
300     protected boolean isImportant(IWorkbenchPart part) {
301         // If selection is pinned, part activations are not important.
302         if (pinSelection)
303             return false;
304         // Ignore self, try all others.
305         return part != this;
306     }
307
308     /**
309      * The <code>PropertySheet</code> implementation of this
310      * <code>IPartListener</code> method first sees if the active part is an
311      * <code>IContributedContentsView</code> adapter and if so, asks it for
312      * its contributing part.
313      */
314     @Override
315     public void partActivated(IWorkbenchPart part) {
316 //        if (bootstrapSelection == null && bootstrapOnly)
317 //            return;
318
319         // Look for a declaratively-contributed adapter - including not yet
320         // loaded adapter factories.
321         // See bug 86362 [PropertiesView] Can not access AdapterFactory, when
322         // plugin is not loaded.
323         IWorkbenchPart source = getSourcePart(part);
324         //System.out.println("PPV part activated: " + part  + ",src " + source + ",view " + this + " bss: " + bootstrapSelection + " pin " + pinSelection);
325         super.partActivated(source);
326
327         // When the view is first opened, pass the selection to the page
328         if (bootstrapSelection != null) {
329             IPage page = getCurrentPage();
330             if (page instanceof IPropertyPage) {
331                 IPropertyPage ppage = (IPropertyPage) page;
332                 // FIXME: should this pass source or part ??
333                 ppage.selectionChanged(part, bootstrapSelection);
334             }
335             bootstrapSelection = null;
336         }
337     }
338
339
340     @Override
341     protected void partHidden(IWorkbenchPart part) {
342         // Make sure that pinned view is not hidden when the editor is hidden
343         if(!pinSelection)
344             super.partHidden(part);
345     }
346
347     long lastSelectionChangeTime = -1000;
348
349     ISelectionListener immediateSelectionListener = new ISelectionListener() {
350         @Override
351         public void selectionChanged(IWorkbenchPart part, ISelection selection) {
352             // Check that enough time has passed since the last selection change.
353             long time = System.currentTimeMillis();
354             long delta = time - lastSelectionChangeTime;
355             lastSelectionChangeTime = time;
356
357             //System.out.println("time delta: " + delta + " : " + selection);
358
359             if (delta > SELECTION_CHANGE_THRESHOLD) {
360                 VariablesPageView.this.selectionChanged(part, selection);
361             }
362         }
363     };
364
365     public ISelection getLastSelection() {
366         return lastSelection;
367     }
368
369     /*
370      * (non-Javadoc)
371      * 
372      * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
373      *      org.eclipse.jface.viewers.ISelection)
374      */
375     @Override
376     public void selectionChanged(IWorkbenchPart part, ISelection sel) {
377         // we ignore our own selection or null selection
378         if (part == this || sel == null) {
379             return;
380         }
381         // ignore workbench selections when pinned also
382         if (pinSelection)
383             return;
384
385         // pass the selection change to the page
386         //System.out.println("PPV selection changed: " + sel + " " + this);
387         IPage page = getCurrentPage();
388         if (page instanceof IVariablesPage) {
389             IVariablesPage ppage = (IVariablesPage) page;
390             ppage.selectionChanged(part, sel);
391
392             // Make sure that the part name is not updated unnecessarily because
393             // of immediate and post selection listeners.
394             boolean sameSelection = false;
395             if (part == lastPart) {
396                 if (sel != null && sel.equals(lastSelection))
397                     sameSelection = true;
398             }
399
400             if (lastPart != null) {
401                 lastPart.removePropertyListener(partPropertyListener);
402             }
403             lastPart = part;
404             lastSelection = sel;
405             if (lastPart != null) {
406                 lastPart.addPropertyListener(partPropertyListener);
407             }
408
409             if (!sameSelection) {
410                 final Display d = getSite().getShell().getDisplay();
411                 ppage.updatePartName(sel, parameter -> {
412                     if (!d.isDisposed())
413                         d.asyncExec(() -> doSetPartName(parameter));
414                 });
415             }
416         }
417     }
418
419     void doSetPartName(String partName) {
420         // Is the page view disposed ??
421         if (contextProvider == null)
422             return;
423         if (partName == null) {
424             // Return to default
425             partName = "Selection";
426         }
427         setPartName(partName);
428     }
429
430     public boolean isWorkbenchSelectionPinned() {
431         return pinSelection;
432     }
433
434     public void pinWorkbenchSelection(boolean pin) {
435         if (pin == pinSelection)
436             return;
437
438         pinSelection = pin;
439         setPartProperty(PROP_PINNED, Boolean.toString(pin));
440
441         if (pin) {
442             setTitleImage(resourceManager.createImage(pinned));
443         } else {
444             setTitleImage(resourceManager.createImage(notPinned));
445         }
446         updateContentDescription(pin, lastPart);
447         // Since lastPart is another PropertyView, we do not want to listen it's changes (At least current implementation is done so)
448         if (lastPart != null) {
449             lastPart.removePropertyListener(partPropertyListener);
450         }
451         lastPart = null;
452
453     }
454
455     IWorkbenchPart getSourcePart(IWorkbenchPart part) {
456         IContributedContentsView view = (IContributedContentsView) part.getAdapter(IContributedContentsView.class);
457         if (view != null) {
458             IWorkbenchPart source = view.getContributingPart();
459             if (source != null)
460                 return source;
461         }
462         return part;
463     }
464
465     private void updateContentDescription(boolean selectionPinned, IWorkbenchPart sourcePart) {
466         if (selectionPinned) {
467             if (sourcePart == null) {
468                 setContentDescription("No selection");
469             } else {
470                 sourcePart = getSourcePart(sourcePart);
471
472                 StringBuilder desc = new StringBuilder("Selection from ");
473                 if (sourcePart instanceof IEditorPart)
474                     desc.append("editor ");
475                 if (sourcePart instanceof IViewPart)
476                     desc.append("view ");
477                 desc.append('\'');
478                 desc.append(sourcePart.getTitle());
479                 desc.append('\'');
480
481                 setContentDescription(desc.toString());
482             }
483         } else {
484             setContentDescription("");
485         }
486     }
487
488     IPropertyListener partPropertyListener = new IPropertyListener() {
489         @Override
490         public void propertyChanged(Object source, int propId) {
491             if (propId == IWorkbenchPart.PROP_TITLE) {
492                 updateContentDescription(pinSelection, lastPart);
493             }
494         }
495     };
496
497 }