--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.browsing.ui.platform;\r
+\r
+import org.eclipse.jface.resource.ImageDescriptor;\r
+import org.eclipse.jface.resource.JFaceResources;\r
+import org.eclipse.jface.resource.LocalResourceManager;\r
+import org.eclipse.jface.resource.ResourceManager;\r
+import org.eclipse.jface.viewers.ISelection;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.ui.IEditorPart;\r
+import org.eclipse.ui.IMemento;\r
+import org.eclipse.ui.IPropertyListener;\r
+import org.eclipse.ui.ISelectionListener;\r
+import org.eclipse.ui.IViewPart;\r
+import org.eclipse.ui.IViewSite;\r
+import org.eclipse.ui.IWorkbenchPage;\r
+import org.eclipse.ui.IWorkbenchPart;\r
+import org.eclipse.ui.PartInitException;\r
+import org.eclipse.ui.contexts.IContextService;\r
+import org.eclipse.ui.part.IContributedContentsView;\r
+import org.eclipse.ui.part.IPage;\r
+import org.eclipse.ui.part.PageBook;\r
+import org.eclipse.ui.part.PageBookView;\r
+import org.simantics.browsing.ui.swt.IVariablesPage;\r
+import org.simantics.db.management.ISessionContextProvider;\r
+import org.simantics.selectionview.PropertyPage;\r
+import org.simantics.ui.SimanticsUI;\r
+import org.simantics.ui.workbench.IPropertyPage;\r
+import org.simantics.ui.workbench.ResourceInput;\r
+import org.simantics.utils.ui.BundleUtils;\r
+\r
+/**\r
+ * This is a version of the standard eclipse <code>PropertySheet</code> view a\r
+ * graph database access twist. It presents a property view based the active\r
+ * workbench part and the active part's current selection.\r
+ * \r
+ * <p>\r
+ * To get a property page for your own view or editor part you can do one of the\r
+ * following:\r
+ * \r
+ * <ol>\r
+ * <li>Implement getAdapter for your view or editor part as follows:\r
+ * \r
+ * <pre>\r
+ * Object getAdapter(Class c) {\r
+ * if (c == ISessionContextProvider.class)\r
+ * // Get ISessionContextProvider from somewhere, i.e. SimanticsUI\r
+ * if (c == IPropertyPage.class)\r
+ * return new PropertyPage(getSite(), this);\r
+ * }\r
+ * </pre>\r
+ * \r
+ * This method also allows customization of the actual property page control\r
+ * that gets created. <code>PropertyPage</code> serves as a good starting\r
+ * point for your own version.</li>\r
+ * <li>Make the workbench part implement the marker interface\r
+ * <code>IStandardPropertyPage</code> which will make this view do the above\r
+ * automatically without implementing <code>getAdapter</code>.</li>\r
+ * </ol>\r
+ * \r
+ * @author Tuukka Lehtonen\r
+ * \r
+ * @see IStandardPropertyPage\r
+ * @see IPropertyPage\r
+ * @see PropertyPage\r
+ */\r
+public class VariablesPageView extends PageBookView implements ISelectionListener {\r
+\r
+ private static final String VARIABLES_VIEW_CONTEXT = "org.simantics.browsing.ui.variables";\r
+\r
+ private static final String PROP_PINNED = "pinned";\r
+\r
+ protected static final long SELECTION_CHANGE_THRESHOLD = 500;\r
+\r
+ private ISessionContextProvider contextProvider;\r
+\r
+ /**\r
+ * The initial selection when the property sheet opens\r
+ */\r
+ private ISelection bootstrapSelection;\r
+\r
+ /**\r
+ * A flag for indicating whether or not this view will only use the\r
+ * bootstrap selection and and IPropertyPage source instead of listening to\r
+ * the input constantly.\r
+ */\r
+ private final boolean bootstrapOnly = false;\r
+\r
+ private IMemento memento;\r
+\r
+ private boolean pinSelection = false;\r
+\r
+ private IWorkbenchPart lastPart;\r
+ private ISelection lastSelection;\r
+\r
+ private ResourceManager resourceManager;\r
+\r
+ private ImageDescriptor notPinned;\r
+ private ImageDescriptor pinned;\r
+\r
+ @Override\r
+ public void createPartControl(Composite parent) {\r
+ super.createPartControl(parent);\r
+\r
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources());\r
+ notPinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple.png");\r
+ pinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple_pinned.png");\r
+\r
+ IContextService cs = (IContextService) getSite().getService(IContextService.class);\r
+ cs.activateContext(VARIABLES_VIEW_CONTEXT);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see org.eclipse.ui.part.PageBookView#getAdapter(java.lang.Class)\r
+ */\r
+ @SuppressWarnings({ "rawtypes" })\r
+ @Override\r
+ public Object getAdapter(Class adapter) {\r
+ if (adapter == IContributedContentsView.class) {\r
+ // This makes it possible to duplicate a PropertyPageView with another\r
+ // secondary ID and make it show the same property page that was showing\r
+ // in the original property page view.\r
+ return new IContributedContentsView() {\r
+ @Override\r
+ public IWorkbenchPart getContributingPart() {\r
+ return getContributingEditor();\r
+ }\r
+ };\r
+ }\r
+ if (adapter == ISessionContextProvider.class)\r
+ return contextProvider;\r
+ return super.getAdapter(adapter);\r
+ }\r
+\r
+ /**\r
+ * Returns the editor which contributed the current\r
+ * page to this view.\r
+ *\r
+ * @return the editor which contributed the current page\r
+ * or <code>null</code> if no editor contributed the current page\r
+ */\r
+ private IWorkbenchPart getContributingEditor() {\r
+ return getCurrentContributingPart();\r
+ }\r
+\r
+ /* (non-Javadoc)\r
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)\r
+ */\r
+ @Override\r
+ public void init(IViewSite site, IMemento memento) throws PartInitException {\r
+ this.memento = memento;\r
+ init(site);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see org.eclipse.ui.part.PageBookView#init(org.eclipse.ui.IViewSite)\r
+ */\r
+ @Override\r
+ public void init(IViewSite site) throws PartInitException {\r
+ String secondaryId = site.getSecondaryId();\r
+ if (secondaryId != null) {\r
+ ResourceInput input = ResourceInput.unmarshall(secondaryId);\r
+ if (input != null) {\r
+ //bootstrapOnly = true;\r
+ }\r
+ }\r
+\r
+ //System.out.println("PPV init: " + this);\r
+ super.init(site);\r
+\r
+ // This prevents the Properties view from providing a selection to other\r
+ // workbench parts, thus making them lose their selections which is not\r
+ // desirable.\r
+ site.setSelectionProvider(null);\r
+\r
+ contextProvider = SimanticsUI.getSessionContextProvider();\r
+\r
+ if (!bootstrapOnly) {\r
+ site.getPage().addSelectionListener(immediateSelectionListener);\r
+ site.getPage().addPostSelectionListener(this);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void saveState(IMemento memento) {\r
+ if (this.memento != null) {\r
+ memento.putMemento(this.memento);\r
+ }\r
+ }\r
+\r
+ /* (non-Javadoc)\r
+ * Method declared on IWorkbenchPart.\r
+ */\r
+ @Override\r
+ public void dispose() {\r
+ //System.out.println("PPV dispose: " + this);\r
+ // Dispose of this before nullifying contextProvider because this\r
+ // dispose may just need the context provider - at least PropertyPage\r
+ // disposal will.\r
+ super.dispose();\r
+\r
+ if (lastPart != null)\r
+ lastPart.removePropertyListener(partPropertyListener);\r
+\r
+ contextProvider = null;\r
+\r
+ // Remove ourselves as a workbench selection listener.\r
+ if (!bootstrapOnly) {\r
+ getSite().getPage().removePostSelectionListener(this);\r
+ getSite().getPage().removeSelectionListener(immediateSelectionListener);\r
+ }\r
+\r
+ if (resourceManager != null) {\r
+ resourceManager.dispose();\r
+ resourceManager = null;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected IPage createDefaultPage(PageBook book) {\r
+ /*\r
+ MessagePage page = new MessagePage();\r
+ initPage(page);\r
+ page.createControl(book);\r
+ page.setMessage(Messages.PropertyPageView_noPropertiesAvailable);\r
+ return page;\r
+ */\r
+\r
+ VariablesPage page = new VariablesPage(getSite(), this);\r
+ page.setAdapter(this);\r
+ initPage(page);\r
+ page.createControl(book);\r
+ //System.out.println("PPV create default page: " + page);\r
+ return page;\r
+\r
+ }\r
+\r
+ @Override\r
+ protected PageRec doCreatePage(IWorkbenchPart part) {\r
+\r
+ return null;\r
+\r
+// // NOTE: If the default page should be shown, this method must return null.\r
+// if (part == null)\r
+// return null;\r
+//\r
+// //System.out.println("PPV try to create page for part: " + (part != null ? part.getTitle() : null));\r
+//\r
+// VariablesPage page = new VariablesPage(getSite(), this);\r
+//\r
+// // Make sure that the adaptation is provided by this class\r
+// // in order not to leave the ISessionContextProvider adaptability\r
+// // up to clients to implement.\r
+// page.setAdapter(this);\r
+// //System.out.println("PPV created page: " + page);\r
+// if (page instanceof IPageBookViewPage) {\r
+// initPage((IPageBookViewPage) page);\r
+// }\r
+// page.createControl(getPageBook());\r
+// //System.out.println("PPV created page control: " + page.getControl());\r
+// return new PageRec(part, page);\r
+\r
+ }\r
+\r
+ @Override\r
+ protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {\r
+ //System.out.println("PPV destroy page for part: " + part.getTitle());\r
+\r
+ IPropertyPage page = (IPropertyPage) pageRecord.page;\r
+ page.dispose();\r
+ pageRecord.dispose();\r
+ }\r
+\r
+ @Override\r
+ protected IWorkbenchPart getBootstrapPart() {\r
+ IWorkbenchPage page = getSite().getPage();\r
+ if (page != null) {\r
+ bootstrapSelection = page.getSelection();\r
+ return page.getActivePart();\r
+ }\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ protected boolean isImportant(IWorkbenchPart part) {\r
+ // If selection is pinned, part activations are not important.\r
+ if (pinSelection)\r
+ return false;\r
+ // Ignore self, try all others.\r
+ return part != this;\r
+ }\r
+\r
+ /**\r
+ * The <code>PropertySheet</code> implementation of this\r
+ * <code>IPartListener</code> method first sees if the active part is an\r
+ * <code>IContributedContentsView</code> adapter and if so, asks it for\r
+ * its contributing part.\r
+ */\r
+ @Override\r
+ public void partActivated(IWorkbenchPart part) {\r
+// if (bootstrapSelection == null && bootstrapOnly)\r
+// return;\r
+\r
+ // Look for a declaratively-contributed adapter - including not yet\r
+ // loaded adapter factories.\r
+ // See bug 86362 [PropertiesView] Can not access AdapterFactory, when\r
+ // plugin is not loaded.\r
+ IWorkbenchPart source = getSourcePart(part);\r
+ //System.out.println("PPV part activated: " + part + ",src " + source + ",view " + this + " bss: " + bootstrapSelection + " pin " + pinSelection);\r
+ super.partActivated(source);\r
+\r
+ // When the view is first opened, pass the selection to the page\r
+ if (bootstrapSelection != null) {\r
+ IPage page = getCurrentPage();\r
+ if (page instanceof IPropertyPage) {\r
+ IPropertyPage ppage = (IPropertyPage) page;\r
+ // FIXME: should this pass source or part ??\r
+ ppage.selectionChanged(part, bootstrapSelection);\r
+ }\r
+ bootstrapSelection = null;\r
+ }\r
+ }\r
+\r
+\r
+ @Override\r
+ protected void partHidden(IWorkbenchPart part) {\r
+ // Make sure that pinned view is not hidden when the editor is hidden\r
+ if(!pinSelection)\r
+ super.partHidden(part);\r
+ }\r
+\r
+ long lastSelectionChangeTime = -1000;\r
+\r
+ ISelectionListener immediateSelectionListener = new ISelectionListener() {\r
+ @Override\r
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {\r
+ // Check that enough time has passed since the last selection change.\r
+ long time = System.currentTimeMillis();\r
+ long delta = time - lastSelectionChangeTime;\r
+ lastSelectionChangeTime = time;\r
+\r
+ //System.out.println("time delta: " + delta + " : " + selection);\r
+\r
+ if (delta > SELECTION_CHANGE_THRESHOLD) {\r
+ VariablesPageView.this.selectionChanged(part, selection);\r
+ }\r
+ }\r
+ };\r
+\r
+ public ISelection getLastSelection() {\r
+ return lastSelection;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,\r
+ * org.eclipse.jface.viewers.ISelection)\r
+ */\r
+ @Override\r
+ public void selectionChanged(IWorkbenchPart part, ISelection sel) {\r
+ // we ignore our own selection or null selection\r
+ if (part == this || sel == null) {\r
+ return;\r
+ }\r
+ // ignore workbench selections when pinned also\r
+ if (pinSelection)\r
+ return;\r
+\r
+ // pass the selection change to the page\r
+ //System.out.println("PPV selection changed: " + sel + " " + this);\r
+ IPage page = getCurrentPage();\r
+ if (page instanceof IVariablesPage) {\r
+ IVariablesPage ppage = (IVariablesPage) page;\r
+ ppage.selectionChanged(part, sel);\r
+\r
+ // Make sure that the part name is not updated unnecessarily because\r
+ // of immediate and post selection listeners.\r
+ boolean sameSelection = false;\r
+ if (part == lastPart) {\r
+ if (sel != null && sel.equals(lastSelection))\r
+ sameSelection = true;\r
+ }\r
+\r
+ if (lastPart != null) {\r
+ lastPart.removePropertyListener(partPropertyListener);\r
+ }\r
+ lastPart = part;\r
+ lastSelection = sel;\r
+ if (lastPart != null) {\r
+ lastPart.addPropertyListener(partPropertyListener);\r
+ }\r
+\r
+ if (!sameSelection) {\r
+ final Display d = getSite().getShell().getDisplay();\r
+ ppage.updatePartName(sel, parameter -> {\r
+ if (!d.isDisposed())\r
+ d.asyncExec(() -> doSetPartName(parameter));\r
+ });\r
+ }\r
+ }\r
+ }\r
+\r
+ void doSetPartName(String partName) {\r
+ // Is the page view disposed ??\r
+ if (contextProvider == null)\r
+ return;\r
+ if (partName == null) {\r
+ // Return to default\r
+ partName = "Selection";\r
+ }\r
+ setPartName(partName);\r
+ }\r
+\r
+ public boolean isWorkbenchSelectionPinned() {\r
+ return pinSelection;\r
+ }\r
+\r
+ public void pinWorkbenchSelection(boolean pin) {\r
+ if (pin == pinSelection)\r
+ return;\r
+\r
+ pinSelection = pin;\r
+ setPartProperty(PROP_PINNED, Boolean.toString(pin));\r
+\r
+ if (pin) {\r
+ setTitleImage(resourceManager.createImage(pinned));\r
+ } else {\r
+ setTitleImage(resourceManager.createImage(notPinned));\r
+ }\r
+ updateContentDescription(pin, lastPart);\r
+ // Since lastPart is another PropertyView, we do not want to listen it's changes (At least current implementation is done so)\r
+ if (lastPart != null) {\r
+ lastPart.removePropertyListener(partPropertyListener);\r
+ }\r
+ lastPart = null;\r
+\r
+ }\r
+\r
+ IWorkbenchPart getSourcePart(IWorkbenchPart part) {\r
+ IContributedContentsView view = (IContributedContentsView) part.getAdapter(IContributedContentsView.class);\r
+ if (view != null) {\r
+ IWorkbenchPart source = view.getContributingPart();\r
+ if (source != null)\r
+ return source;\r
+ }\r
+ return part;\r
+ }\r
+\r
+ private void updateContentDescription(boolean selectionPinned, IWorkbenchPart sourcePart) {\r
+ if (selectionPinned) {\r
+ if (sourcePart == null) {\r
+ setContentDescription("No selection");\r
+ } else {\r
+ sourcePart = getSourcePart(sourcePart);\r
+\r
+ StringBuilder desc = new StringBuilder("Selection from ");\r
+ if (sourcePart instanceof IEditorPart)\r
+ desc.append("editor ");\r
+ if (sourcePart instanceof IViewPart)\r
+ desc.append("view ");\r
+ desc.append('\'');\r
+ desc.append(sourcePart.getTitle());\r
+ desc.append('\'');\r
+\r
+ setContentDescription(desc.toString());\r
+ }\r
+ } else {\r
+ setContentDescription("");\r
+ }\r
+ }\r
+\r
+ IPropertyListener partPropertyListener = new IPropertyListener() {\r
+ @Override\r
+ public void propertyChanged(Object source, int propId) {\r
+ if (propId == IWorkbenchPart.PROP_TITLE) {\r
+ updateContentDescription(pinSelection, lastPart);\r
+ }\r
+ }\r
+ };\r
+\r
+}\r