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