-/*******************************************************************************\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 java.util.HashSet;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.WeakHashMap;\r
-import java.util.function.Consumer;\r
-\r
-import org.eclipse.core.runtime.IConfigurationElement;\r
-import org.eclipse.core.runtime.IExtension;\r
-import org.eclipse.core.runtime.IExtensionPoint;\r
-import org.eclipse.core.runtime.IExtensionRegistry;\r
-import org.eclipse.core.runtime.RegistryFactory;\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.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.IWorkbenchPart3;\r
-import org.eclipse.ui.PartInitException;\r
-import org.eclipse.ui.PlatformUI;\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.IPageBookViewPage;\r
-import org.eclipse.ui.part.PageBook;\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.threads.SWTThread;\r
-import org.simantics.utils.threads.Throttler;\r
-import org.simantics.utils.ui.BundleUtils;\r
-import org.simantics.utils.ui.SWTUtils;\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 == IPropertyPage.class) {\r
- * // Get the browse contexts to use from somewhere\r
- * Set<String> browseContexts = Collections.singleton("...");\r
- * return new StandardPropertyPage(getSite(), browseContexts);\r
- * }\r
- * return super.getAdapter(c);\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 point\r
- * 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 PropertyPageView extends PageBookView implements ISelectionListener, IContributedContentsView {\r
-\r
- /**\r
- * Extension point used to modify behavior of the view\r
- */\r
- private static final String EXT_POINT = "org.eclipse.ui.propertiesView"; //$NON-NLS-1$\r
-\r
- private static final String PROPERTY_VIEW_CONTEXT = "org.simantics.modeling.ui.properties";\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
- private final Map<IWorkbenchPart, ISelection> lastSelections = new WeakHashMap<IWorkbenchPart, ISelection>();\r
-\r
- private ResourceManager resourceManager;\r
-\r
- private ImageDescriptor notPinned;\r
- private ImageDescriptor pinned;\r
-\r
- /**\r
- * Set of workbench parts, which should not be used as a source for PropertySheet\r
- */\r
- private Set<String> ignoredViews;\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(PROPERTY_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
- 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
- PropertyPage page = new PropertyPage(getSite());\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
- // 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
- // Try to get a property page.\r
- IPropertyPage page = (IPropertyPage) part.getAdapter(IPropertyPage.class);\r
- if (page != null) {\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
- return null;\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
- private boolean isPropertyView(IWorkbenchPart part) {\r
- boolean ignore = false;\r
-\r
- if (part instanceof IWorkbenchPart3) {\r
- IWorkbenchPart3 part3 = (IWorkbenchPart3) part;\r
- ignore = Boolean.parseBoolean(part3.getPartProperty(PROP_PINNED));\r
- }\r
-\r
- // See org.simantics.modeling.ui.actions.DuplicatePinnedViewHandler\r
-// ignore |= part.getSite().getId().endsWith("Pinned");\r
- String thisId = getSite().getId();\r
- String otherId = part.getSite().getId();\r
- //System.out.println(thisId + " - " + otherId);\r
- ignore |= otherId.startsWith(thisId);\r
-\r
- return this == part || ignore;\r
- }\r
-\r
- private Set<String> getIgnoredViews() {\r
- if (ignoredViews == null) {\r
- ignoredViews = new HashSet<String>();\r
- IExtensionRegistry registry = RegistryFactory.getRegistry();\r
- IExtensionPoint ep = registry.getExtensionPoint(EXT_POINT);\r
- if (ep != null) {\r
- IExtension[] extensions = ep.getExtensions();\r
- for (int i = 0; i < extensions.length; i++) {\r
- IConfigurationElement[] elements = extensions[i].getConfigurationElements();\r
- for (int j = 0; j < elements.length; j++) {\r
- if ("excludeSources".equalsIgnoreCase(elements[j].getName())) { //$NON-NLS-1$\r
- String id = elements[j].getAttribute("id"); //$NON-NLS-1$\r
- if (id != null)\r
- ignoredViews.add(id);\r
- }\r
- }\r
- }\r
- }\r
- }\r
- return ignoredViews;\r
- }\r
-\r
- private boolean isViewIgnored(String partID) {\r
- return getIgnoredViews().contains(partID);\r
- }\r
-\r
- @Override\r
- protected boolean isImportant(IWorkbenchPart part) {\r
- String partID = part.getSite().getId();\r
- //System.out.println("isImportant(" + partID + ")");\r
- return !isWorkbenchSelectionPinned() && !isPropertyView(part) && !isViewIgnored(partID);\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
- updatePartName(ppage, bootstrapSelection);\r
- }\r
- bootstrapSelection = null;\r
- }\r
- }\r
-\r
-\r
- @Override\r
- public void partClosed(IWorkbenchPart part) {\r
- // Make sure that pinned view is not reset even if its originating\r
- // editor is closed.\r
- if (!pinSelection)\r
- super.partClosed(part);\r
- }\r
-\r
- @Override\r
- protected void partHidden(IWorkbenchPart part) {\r
- // Fast views are quite unusable if this code is enabled.\r
-\r
- // Make sure that pinned view is not hidden when the editor is hidden\r
-// if(!pinSelection)\r
-// super.partHidden(part);\r
- }\r
-\r
- ISelectionListener immediateSelectionListener = new ISelectionListener() {\r
- \r
- private Throttler throttler = new Throttler(SWTThread.getThreadAccess(PlatformUI.getWorkbench().getDisplay()), 500, 3);\r
- \r
- @Override\r
- public void selectionChanged(final IWorkbenchPart part, final ISelection selection) {\r
- \r
- // Do not process selections from self\r
- if(PropertyPageView.this == part) return;\r
- \r
- throttler.schedule(new Runnable() {\r
-\r
- @Override\r
- public void run() {\r
- PropertyPageView.this.doSelectionChanged(part, selection); \r
- }\r
- \r
- });\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
- doSelectionChanged(part, sel);\r
- }\r
-\r
- /**\r
- * @param part\r
- * @param sel\r
- * @return <code>true</code> if the changed selection affected the view,\r
- * <code>false</code> otherwise\r
- */\r
- boolean doSelectionChanged(IWorkbenchPart part, ISelection sel) {\r
- \r
- // we ignore our own selection or null selection\r
- if (isPropertyView(part) || sel == null) {\r
- return false;\r
- }\r
- // ignore workbench selections when pinned also\r
- if (pinSelection)\r
- return false;\r
-\r
- // pass the selection change to the page\r
- part = getSourcePart(part);\r
- IPage page = getCurrentPage();\r
- //System.out.println("PPV selection changed to (" + part + ", " + sel + "): " + page);\r
- if (page instanceof IPropertyPage) {\r
- IPropertyPage ppage = (IPropertyPage) page;\r
-\r
- // Prevent parts that do not contribute a property page from messing\r
- // up the contents/title of the currently active property page.\r
- PageRec pageRec = getPageRec(part);\r
- if (pageRec == null || pageRec.page != page)\r
- return false;\r
-\r
- // Make sure that the part name is not updated unnecessarily because\r
- // of immediate and post selection listeners.\r
- ISelection lastPartSelection = lastSelections.get(part);\r
- //System.out.println(" LAST PART SELECTION(" + part + "): " + lastPartSelection);\r
- boolean sameSelection = lastPartSelection != null && sel.equals(lastPartSelection);\r
-\r
- if (lastPart != null) {\r
- lastPart.removePropertyListener(partPropertyListener);\r
- }\r
- lastPart = part;\r
- lastSelection = sel;\r
- lastSelections.put(part, sel);\r
- if (lastPart != null) {\r
- lastPart.addPropertyListener(partPropertyListener);\r
- }\r
-\r
- updatePartName(ppage, sel);\r
- if (!sameSelection) {\r
- ppage.selectionChanged(part, sel);\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- void updatePartName(IPropertyPage ppage, ISelection sel) {\r
- ppage.updatePartName(sel, partNameUpdateCallback);\r
- }\r
-\r
- Consumer<String> partNameUpdateCallback = parameter -> {\r
- // This check is not safe - there might be a burst of changes incoming\r
- //if (getPartName().equals(parameter)) return;\r
- //System.out.println("partNameUpdateCallback : " + parameter);\r
- SWTUtils.asyncExec(getPageBook(), new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!getPageBook().isDisposed()) {\r
- if (getPartName().equals(parameter)) return;\r
- //System.out.println("doSetParameterName : " + parameter);\r
- 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
- @Override\r
- public IWorkbenchPart getContributingPart() {\r
- return lastPart;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.platform;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPart3;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.contexts.IContextService;
+import org.eclipse.ui.part.IContributedContentsView;
+import org.eclipse.ui.part.IPage;
+import org.eclipse.ui.part.IPageBookViewPage;
+import org.eclipse.ui.part.PageBook;
+import org.simantics.Simantics;
+import org.simantics.db.management.ISessionContextProvider;
+import org.simantics.selectionview.PropertyPage;
+import org.simantics.ui.workbench.IPropertyPage;
+import org.simantics.ui.workbench.ResourceInput;
+import org.simantics.utils.ui.BundleUtils;
+import org.simantics.utils.ui.SWTUtils;
+
+/**
+ * This is a version of the standard eclipse <code>PropertySheet</code> view a
+ * graph database access twist. It presents a property view based the active
+ * workbench part and the active part's current selection.
+ *
+ * <p>
+ * To get a property page for your own view or editor part you can do one of the
+ * following:
+ *
+ * <ol>
+ * <li>Implement getAdapter for your view or editor part as follows:
+ *
+ * <pre>
+ * Object getAdapter(Class c) {
+ * if (c == IPropertyPage.class) {
+ * // Get the browse contexts to use from somewhere
+ * Set<String> browseContexts = Collections.singleton("...");
+ * return new StandardPropertyPage(getSite(), browseContexts);
+ * }
+ * return super.getAdapter(c);
+ * }
+ * </pre>
+ *
+ * This method also allows customization of the actual property page control
+ * that gets created. <code>PropertyPage</code> serves as a good starting point
+ * for your own version.</li>
+ * <li>Make the workbench part implement the marker interface
+ * <code>IStandardPropertyPage</code> which will make this view do the above
+ * automatically without implementing <code>getAdapter</code>.</li>
+ * </ol>
+ *
+ * @author Tuukka Lehtonen
+ *
+ * @see IStandardPropertyPage
+ * @see IPropertyPage
+ * @see PropertyPage
+ */
+public class PropertyPageView extends PageBookView implements IContributedContentsView {
+
+ private static final String PROPERTY_VIEW_CONTEXT = "org.simantics.modeling.ui.properties";
+
+ private static final String PROP_PINNED = "pinned";
+
+ private ISessionContextProvider contextProvider;
+
+ /**
+ * The initial selection when the property sheet opens
+ */
+ private ISelection bootstrapSelection;
+
+ /**
+ * A flag for indicating whether or not this view will only use the
+ * bootstrap selection and and IPropertyPage source instead of listening to
+ * the input constantly.
+ */
+ private final boolean bootstrapOnly = false;
+
+ private IMemento memento;
+
+ private boolean pinSelection = false;
+
+ private IWorkbenchPart lastPart;
+ private ISelection lastSelection;
+ private final Map<IWorkbenchPart, ISelection> lastSelections = new WeakHashMap<>();
+
+ private ResourceManager resourceManager;
+
+ private ImageDescriptor notPinned;
+ private ImageDescriptor pinned;
+
+ /**
+ * The workbench post-selection listener for this view that changes the
+ * input of the pagebook page for the part which changed its selection.
+ */
+ private ISelectionListener selectionListener = this::doSelectionChanged;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
+
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources());
+ notPinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple.png");
+ pinned = BundleUtils.getImageDescriptorFromPlugin("org.simantics.browsing.ui.common", "icons/table_multiple_pinned.png");
+
+ IContextService cs = (IContextService) getSite().getService(IContextService.class);
+ cs.activateContext(PROPERTY_VIEW_CONTEXT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.part.PageBookView#getAdapter(java.lang.Class)
+ */
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ if (adapter == IContributedContentsView.class) {
+ // This makes it possible to duplicate a PropertyPageView with another
+ // secondary ID and make it show the same property page that was showing
+ // in the original property page view.
+ return new IContributedContentsView() {
+ @Override
+ public IWorkbenchPart getContributingPart() {
+ return getContributingEditor();
+ }
+ };
+ }
+ return super.getAdapter(adapter);
+ }
+
+ /**
+ * Returns the editor which contributed the current
+ * page to this view.
+ *
+ * @return the editor which contributed the current page
+ * or <code>null</code> if no editor contributed the current page
+ */
+ private IWorkbenchPart getContributingEditor() {
+ return getCurrentContributingPart();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, org.eclipse.ui.IMemento)
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento) throws PartInitException {
+ this.memento = memento;
+ init(site);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.part.PageBookView#init(org.eclipse.ui.IViewSite)
+ */
+ @Override
+ public void init(IViewSite site) throws PartInitException {
+ String secondaryId = site.getSecondaryId();
+ if (secondaryId != null) {
+ ResourceInput input = ResourceInput.unmarshall(secondaryId);
+ if (input != null) {
+ //bootstrapOnly = true;
+ }
+ }
+
+ //System.out.println("PPV init: " + this);
+ super.init(site);
+
+ // This prevents the Properties view from providing a selection to other
+ // workbench parts, thus making them lose their selections which is not
+ // desirable.
+ site.setSelectionProvider(null);
+
+ contextProvider = Simantics.getSessionContextProvider();
+
+ if (!bootstrapOnly) {
+ site.getPage().addPostSelectionListener(selectionListener);
+ }
+ }
+
+ @Override
+ public void saveState(IMemento memento) {
+ if (this.memento != null) {
+ memento.putMemento(this.memento);
+ }
+ }
+
+ /* (non-Javadoc)
+ * Method declared on IWorkbenchPart.
+ */
+ @Override
+ public void dispose() {
+ //System.out.println("PPV dispose: " + this);
+ // Dispose of this before nullifying contextProvider because this
+ // dispose may just need the context provider - at least PropertyPage
+ // disposal will.
+ super.dispose();
+
+ if (lastPart != null)
+ lastPart.removePropertyListener(partPropertyListener);
+
+ contextProvider = null;
+
+ // Remove ourselves as a workbench selection listener.
+ if (!bootstrapOnly) {
+ getSite().getPage().removePostSelectionListener(selectionListener);
+ }
+
+ if (resourceManager != null) {
+ resourceManager.dispose();
+ resourceManager = null;
+ }
+ }
+
+ @Override
+ protected IPage createDefaultPage(PageBook book) {
+ /*
+ MessagePage page = new MessagePage();
+ initPage(page);
+ page.createControl(book);
+ page.setMessage(Messages.PropertyPageView_noPropertiesAvailable);
+ return page;
+ */
+
+ PropertyPage page = new PropertyPage(getSite());
+ initPage(page);
+ page.createControl(book);
+ //System.out.println("PPV create default page: " + page);
+ return page;
+
+ }
+
+ @Override
+ protected PageRec doCreatePage(IWorkbenchPart part) {
+ // NOTE: If the default page should be shown, this method must return null.
+ if (part == null)
+ return null;
+
+ //System.out.println("PPV try to create page for part: " + (part != null ? part.getTitle() : null));
+
+ // Try to get a property page.
+ IPropertyPage page = (IPropertyPage) part.getAdapter(IPropertyPage.class);
+ if (page != null) {
+ //System.out.println("PPV created page: " + page);
+ if (page instanceof IPageBookViewPage) {
+ initPage((IPageBookViewPage) page);
+ }
+ page.createControl(getPageBook());
+ //System.out.println("PPV created page control: " + page.getControl());
+ return new PageRec(part, page);
+ }
+ return null;
+ }
+
+ @Override
+ protected void doDestroyPage(IWorkbenchPart part, PageRec pageRecord) {
+ //System.out.println("PPV destroy page for part: " + part.getTitle());
+
+ IPropertyPage page = (IPropertyPage) pageRecord.page;
+ page.dispose();
+ pageRecord.dispose();
+ }
+
+ @Override
+ protected IWorkbenchPart getBootstrapPart() {
+ IWorkbenchPage page = getSite().getPage();
+ if (page != null) {
+ bootstrapSelection = page.getSelection();
+ return page.getActivePart();
+ }
+ return null;
+ }
+
+ private boolean isPropertyView(IWorkbenchPart part) {
+ boolean ignore = false;
+
+ if (part instanceof IWorkbenchPart3) {
+ IWorkbenchPart3 part3 = (IWorkbenchPart3) part;
+ ignore = Boolean.parseBoolean(part3.getPartProperty(PROP_PINNED));
+ }
+
+ // See org.simantics.modeling.ui.actions.DuplicatePinnedViewHandler
+// ignore |= part.getSite().getId().endsWith("Pinned");
+ String thisId = getSite().getId();
+ String otherId = part.getSite().getId();
+ //System.out.println(thisId + " - " + otherId);
+ ignore |= otherId.startsWith(thisId);
+
+ return this == part || ignore;
+ }
+
+ @Override
+ protected boolean isImportant(IWorkbenchPart part) {
+ //System.out.println("isImportant(" + part.getSite().getId() + ")");
+ return !isWorkbenchSelectionPinned() && !isPropertyView(part);
+ }
+
+ /**
+ * The <code>PropertySheet</code> implementation of this
+ * <code>IPartListener</code> method first sees if the active part is an
+ * <code>IContributedContentsView</code> adapter and if so, asks it for
+ * its contributing part.
+ */
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+// if (bootstrapSelection == null && bootstrapOnly)
+// return;
+
+ // Look for a declaratively-contributed adapter - including not yet
+ // loaded adapter factories.
+ // See bug 86362 [PropertiesView] Can not access AdapterFactory, when
+ // plugin is not loaded.
+ IWorkbenchPart source = getSourcePart(part);
+ //System.out.println("PPV part activated: " + part + ",src " + source + ",view " + this + " bss: " + bootstrapSelection + " pin " + pinSelection);
+ super.partActivated(source);
+
+ // When the view is first opened, pass the selection to the page
+ if (bootstrapSelection != null) {
+ IPage page = getCurrentPage();
+ if (page instanceof IPropertyPage) {
+ IPropertyPage ppage = (IPropertyPage) page;
+ // FIXME: should this pass source or part ??
+ ppage.selectionChanged(part, bootstrapSelection);
+ updatePartName(ppage, bootstrapSelection);
+ }
+ bootstrapSelection = null;
+ }
+ }
+
+
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ // Make sure that pinned view is not reset even if its originating
+ // editor is closed.
+ if (!pinSelection)
+ super.partClosed(part);
+ }
+
+ @Override
+ protected void partHidden(IWorkbenchPart part) {
+ // Fast views are quite unusable if this code is enabled.
+
+ // Make sure that pinned view is not hidden when the editor is hidden
+// if(!pinSelection)
+// super.partHidden(part);
+ }
+
+ public ISelection getLastSelection() {
+ return lastSelection;
+ }
+
+ public void selectionChanged(IWorkbenchPart part, ISelection sel) {
+ doSelectionChanged(part, sel);
+ }
+
+ /**
+ * @param part
+ * @param sel
+ * @return <code>true</code> if the changed selection affected the view,
+ * <code>false</code> otherwise
+ */
+ boolean doSelectionChanged(IWorkbenchPart part, ISelection sel) {
+ // we ignore our own selection or null selection
+ if (isPropertyView(part) || sel == null) {
+ return false;
+ }
+ // ignore workbench selections when pinned also
+ if (pinSelection)
+ return false;
+
+ // pass the selection change to the page
+ part = getSourcePart(part);
+ IPage page = getCurrentPage();
+ //System.out.println("PPV selection changed to (" + part + ", " + sel + "): " + page);
+ if (page instanceof IPropertyPage) {
+ IPropertyPage ppage = (IPropertyPage) page;
+
+ // Prevent parts that do not contribute a property page from messing
+ // up the contents/title of the currently active property page.
+ PageRec pageRec = getPageRec(part);
+ if (pageRec == null || pageRec.page != page)
+ return false;
+
+ // Make sure that the part name is not updated unnecessarily because
+ // of immediate and post selection listeners.
+ ISelection lastPartSelection = lastSelections.get(part);
+ //System.out.println(" LAST PART SELECTION(" + part + "): " + lastPartSelection);
+ boolean sameSelection = lastPartSelection != null && sel.equals(lastPartSelection);
+
+ if (lastPart != null) {
+ lastPart.removePropertyListener(partPropertyListener);
+ }
+ lastPart = part;
+ lastSelection = sel;
+ lastSelections.put(part, sel);
+ if (lastPart != null) {
+ lastPart.addPropertyListener(partPropertyListener);
+ }
+
+ if (!sameSelection) {
+ updatePartName(ppage, sel);
+ ppage.selectionChanged(part, sel);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void updatePartName(IPropertyPage ppage, ISelection sel) {
+ ppage.updatePartName(sel, partNameUpdateCallback);
+ }
+
+ Consumer<String> partNameUpdateCallback = parameter -> {
+ // This check is not safe - there might be a burst of changes incoming
+ //if (getPartName().equals(parameter)) return;
+ //System.out.println("partNameUpdateCallback : " + parameter);
+ SWTUtils.asyncExec(getPageBook(), () -> {
+ if (!getPageBook().isDisposed()) {
+ if (getPartName().equals(parameter)) return;
+ //System.out.println("doSetParameterName : " + parameter);
+ doSetPartName(parameter);
+ }
+ });
+ };
+
+ void doSetPartName(String partName) {
+ // Is the page view disposed ??
+ if (contextProvider == null)
+ return;
+ if (partName == null) {
+ // Return to default
+ partName = "Selection";
+ }
+ setPartName(partName);
+ }
+
+ public boolean isWorkbenchSelectionPinned() {
+ return pinSelection;
+ }
+
+ public void pinWorkbenchSelection(boolean pin) {
+ if (pin == pinSelection)
+ return;
+
+ pinSelection = pin;
+ setPartProperty(PROP_PINNED, Boolean.toString(pin));
+
+ if (pin) {
+ setTitleImage(resourceManager.createImage(pinned));
+ } else {
+ setTitleImage(resourceManager.createImage(notPinned));
+ }
+ updateContentDescription(pin, lastPart);
+ // Since lastPart is another PropertyView, we do not want to listen it's changes (At least current implementation is done so)
+ if (lastPart != null) {
+ lastPart.removePropertyListener(partPropertyListener);
+ }
+ lastPart = null;
+
+ }
+
+ IWorkbenchPart getSourcePart(IWorkbenchPart part) {
+ IContributedContentsView view = (IContributedContentsView) part.getAdapter(IContributedContentsView.class);
+ if (view != null) {
+ IWorkbenchPart source = view.getContributingPart();
+ if (source != null)
+ return source;
+ }
+ return part;
+ }
+
+ private void updateContentDescription(boolean selectionPinned, IWorkbenchPart sourcePart) {
+ if (selectionPinned) {
+ if (sourcePart == null) {
+ setContentDescription("No selection");
+ } else {
+ sourcePart = getSourcePart(sourcePart);
+
+ StringBuilder desc = new StringBuilder("Selection from ");
+ if (sourcePart instanceof IEditorPart)
+ desc.append("editor ");
+ if (sourcePart instanceof IViewPart)
+ desc.append("view ");
+ desc.append('\'');
+ desc.append(sourcePart.getTitle());
+ desc.append('\'');
+
+ setContentDescription(desc.toString());
+ }
+ } else {
+ setContentDescription("");
+ }
+ }
+
+ IPropertyListener partPropertyListener = new IPropertyListener() {
+ @Override
+ public void propertyChanged(Object source, int propId) {
+ if (propId == IWorkbenchPart.PROP_TITLE) {
+ updateContentDescription(pinSelection, lastPart);
+ }
+ }
+ };
+
+ @Override
+ public IWorkbenchPart getContributingPart() {
+ return lastPart;
+ }
+
+}