/******************************************************************************* * Copyright (c) 2007, 2010 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 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.swt.widgets.Display; 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.PartInitException; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.part.IContributedContentsView; import org.eclipse.ui.part.IPage; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.part.PageBookView; import org.simantics.browsing.ui.swt.IVariablesPage; import org.simantics.db.management.ISessionContextProvider; import org.simantics.selectionview.PropertyPage; import org.simantics.ui.SimanticsUI; import org.simantics.ui.workbench.IPropertyPage; import org.simantics.ui.workbench.ResourceInput; import org.simantics.utils.ui.BundleUtils; /** * This is a version of the standard eclipse PropertySheet view a * graph database access twist. It presents a property view based the active * workbench part and the active part's current selection. * *

* To get a property page for your own view or editor part you can do one of the * following: * *

    *
  1. Implement getAdapter for your view or editor part as follows: * *
     * Object getAdapter(Class c) {
     *     if (c == ISessionContextProvider.class)
     *         // Get ISessionContextProvider from somewhere, i.e. SimanticsUI
     *     if (c == IPropertyPage.class)
     *         return new PropertyPage(getSite(), this);
     * }
     * 
    * * This method also allows customization of the actual property page control * that gets created. PropertyPage serves as a good starting * point for your own version.
  2. *
  3. Make the workbench part implement the marker interface * IStandardPropertyPage which will make this view do the above * automatically without implementing getAdapter.
  4. *
* * @author Tuukka Lehtonen * * @see IStandardPropertyPage * @see IPropertyPage * @see PropertyPage */ public class VariablesPageView extends PageBookView implements ISelectionListener { private static final String VARIABLES_VIEW_CONTEXT = "org.simantics.browsing.ui.variables"; private static final String PROP_PINNED = "pinned"; protected static final long SELECTION_CHANGE_THRESHOLD = 500; 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 ResourceManager resourceManager; private ImageDescriptor notPinned; private ImageDescriptor pinned; @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(VARIABLES_VIEW_CONTEXT); } /* * (non-Javadoc) * * @see org.eclipse.ui.part.PageBookView#getAdapter(java.lang.Class) */ @SuppressWarnings("unchecked") @Override public T 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 (T) new IContributedContentsView() { @Override public IWorkbenchPart getContributingPart() { return getContributingEditor(); } }; } if (adapter == ISessionContextProvider.class) return (T) contextProvider; return super.getAdapter(adapter); } /** * Returns the editor which contributed the current * page to this view. * * @return the editor which contributed the current page * or null 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 = SimanticsUI.getSessionContextProvider(); if (!bootstrapOnly) { site.getPage().addSelectionListener(immediateSelectionListener); site.getPage().addPostSelectionListener(this); } } @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(this); getSite().getPage().removeSelectionListener(immediateSelectionListener); } 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; */ VariablesPage page = new VariablesPage(getSite(), this); page.setAdapter(this); initPage(page); page.createControl(book); //System.out.println("PPV create default page: " + page); return page; } @Override protected PageRec doCreatePage(IWorkbenchPart part) { return null; // // 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)); // // VariablesPage page = new VariablesPage(getSite(), this); // // // Make sure that the adaptation is provided by this class // // in order not to leave the ISessionContextProvider adaptability // // up to clients to implement. // page.setAdapter(this); // //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); } @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; } @Override protected boolean isImportant(IWorkbenchPart part) { // If selection is pinned, part activations are not important. if (pinSelection) return false; // Ignore self, try all others. return part != this; } /** * The PropertySheet implementation of this * IPartListener method first sees if the active part is an * IContributedContentsView 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); } bootstrapSelection = null; } } @Override protected void partHidden(IWorkbenchPart part) { // Make sure that pinned view is not hidden when the editor is hidden if(!pinSelection) super.partHidden(part); } long lastSelectionChangeTime = -1000; ISelectionListener immediateSelectionListener = new ISelectionListener() { @Override public void selectionChanged(IWorkbenchPart part, ISelection selection) { // Check that enough time has passed since the last selection change. long time = System.currentTimeMillis(); long delta = time - lastSelectionChangeTime; lastSelectionChangeTime = time; //System.out.println("time delta: " + delta + " : " + selection); if (delta > SELECTION_CHANGE_THRESHOLD) { VariablesPageView.this.selectionChanged(part, selection); } } }; public ISelection getLastSelection() { return lastSelection; } /* * (non-Javadoc) * * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart, * org.eclipse.jface.viewers.ISelection) */ @Override public void selectionChanged(IWorkbenchPart part, ISelection sel) { // we ignore our own selection or null selection if (part == this || sel == null) { return; } // ignore workbench selections when pinned also if (pinSelection) return; // pass the selection change to the page //System.out.println("PPV selection changed: " + sel + " " + this); IPage page = getCurrentPage(); if (page instanceof IVariablesPage) { IVariablesPage ppage = (IVariablesPage) page; ppage.selectionChanged(part, sel); // Make sure that the part name is not updated unnecessarily because // of immediate and post selection listeners. boolean sameSelection = false; if (part == lastPart) { if (sel != null && sel.equals(lastSelection)) sameSelection = true; } if (lastPart != null) { lastPart.removePropertyListener(partPropertyListener); } lastPart = part; lastSelection = sel; if (lastPart != null) { lastPart.addPropertyListener(partPropertyListener); } if (!sameSelection) { final Display d = getSite().getShell().getDisplay(); ppage.updatePartName(sel, parameter -> { if (!d.isDisposed()) d.asyncExec(() -> 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); } } }; }