/******************************************************************************* * 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.selectionview; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; import java.util.TreeSet; import java.util.function.Consumer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IWorkbenchPartSite; import org.simantics.utils.ObjectUtils; /** *

* See PropertyPage for more information on what can be extended or * reimplemented from the superclasses. In addition to those, subclasses may * extend or reimplement the following methods: *

*

* * @author Tuukka Lehtonen * * @see StandardProperties */ public class StandardPropertyPage extends PropertyPage { private final Set contexts; private StandardProperties tabs; /** * For updating the view title when the active tab changes. */ //private transient ISelection lastSelection; private transient Consumer lastTitleCallback; /** * Initializes a new standard property page for the specified workbench part * without loading the page configuration from any extensible contexts. * * @param site the workbench part site that contains this page or * null if there is no site, i.e. the page is within a * dialog or a plain shell. */ public StandardPropertyPage(IWorkbenchPartSite site) { this(site, new TreeSet()); } /** * Initializes a new standard property page for the specified workbench part * and specifies which contexts should be searched for * {@link SelectionProcessor} and {@link PropertyTabContributor} bindings to * initialize the page. * * @param site the workbench part site that contains this page or * null if there is no site, i.e. the page is within a * dialog or a plain shell. * @param contexts the contexts to search bindings for to initialize the * property page */ public StandardPropertyPage(IWorkbenchPartSite site, Set contexts) { super(site); this.contexts = contexts; } protected SelectionProcessor getSelectionProcessor() { return new StandardSelectionProcessor(); } protected Set getContexts() { return contexts; } @SuppressWarnings("unchecked") @Override protected void createPageControls(Composite parent) { SelectionProcessor sp = getSelectionProcessor(); @SuppressWarnings("rawtypes") Collection sps = null; if (sp != null) sps = Collections.singleton(sp); tabs = new StandardProperties(sourceSite, getSite(), parent, SWT.NONE, getContexts(), sps) { @Override protected void activeTabChanged(TabChangeEvent event) { StandardPropertyPage.this.activeTabChanged(event); } @Override protected ComparableTabContributor selectInitialTab(Collection selectionContents, Collection contributions) { return StandardPropertyPage.this.selectInitialTab(selectionContents, contributions); } @Override protected int getTabFolderStyle() { return StandardPropertyPage.this.getTabFolderStyle(); } }; tab = tabs; tab.createControl(tabs, getSessionContext()); getSite().setSelectionProvider(tabs.getSelectionProvider()); fillToolBar(getSite().getActionBars().getToolBarManager()); fillDropDownMenu(getSite().getActionBars().getMenuManager()); } @Override public void updatePartName(ISelection forSelection, Consumer updateCallback) { if(!visible) { updateCallback.accept("Selection"); return; } //this.lastSelection = forSelection; this.lastTitleCallback = updateCallback; IPropertyTab tab = tabs.getActiveTab(); //System.out.println("updatePartName(" + forSelection + ", " + updateCallback + "): " + tab); if (tab instanceof IPropertyTab2) { //System.out.println("invoking tab.updatePartName for tab " + tab); // TabbedPropertyTable implementation reacts to events with several subsequent runnables. // This causes the part name update to be run before the input is set. Hence we have to set the input here. // TLe: this is broken since there is no guarantee that forSelection // is the proper input for this property tab whose input may well be // a transformed version of the original workbench selection. //((IPropertyTab2) tab).setInput(getSessionContext(), forSelection, false); ((IPropertyTab2) tab).updatePartName(updateCallback); } else { //System.out.println("using default updatePartName for tab " + tab); super.updatePartName(forSelection, updateCallback); } } /** * @return */ protected int getTabFolderStyle() { return SWT.BOTTOM | SWT.FLAT; } /** * A simple tab label based implementation for restoring the last selected * tab as the initial tab selection when the property page contents are * reset. It statically stores the last selected tab names for * {@value #MAX_SELECTED_TABS_REMEMBERED} last different ordered tab name * combinations. * * @param selectionContents * @param contributions * @return the previously selected tab contribution for a matching list of tabs */ protected ComparableTabContributor selectInitialTab(Collection selectionContents, Collection contributions) { tabSelection = TabSelection.make(contributions); for (Iterator it = previouslySelectedTabs.iterator(); it.hasNext();) { TabSelection t = it.next(); if (t.equals(tabSelection)) { tabSelection = t; // Move selection to front of list. it.remove(); previouslySelectedTabs.addFirst(t); for (ComparableTabContributor contrib : contributions) { if (ObjectUtils.objectEquals(contrib.getLabel(), t.selected)) { return contrib; } } // selection must have been null, select first tab anyway. return null; } } tabSelection.setSelected(contributions.iterator().next().getLabel()); previouslySelectedTabs.addFirst(tabSelection); while (previouslySelectedTabs.size() > MAX_SELECTED_TABS_REMEMBERED) previouslySelectedTabs.removeLast(); return null; } protected void activeTabChanged(TabChangeEvent event) { //System.out.println("active tab changed: " + event); if (tabSelection != null) tabSelection.setSelected(event.getNewTabLabel()); IPropertyTab tab = event.getNewTab(); if (tab instanceof IPropertyTab2) { //System.out.println("invoking tab.updatePartName for tab " + tab); if(lastTitleCallback != null) ((IPropertyTab2) tab).updatePartName(lastTitleCallback); } } /** * The maximum number of previous initial tab selections to store. See * {@link #previouslySelectedTabs} and {@link #tabSelection}. */ private static final int MAX_SELECTED_TABS_REMEMBERED = 20; /** * An LRU managed list of previously existing tab selections. The list is * static but only used from the SWT display thread so no synchronization is * necessary. */ private static LinkedList previouslySelectedTabs = new LinkedList(); /** * The selection of tabs this property page is currently showing. Also * contains the currently selected tab name, which is updated by * {@link #activeTabChanged(TabChangeEvent)} and * {@link #selectInitialTab(Collection, Collection)}. */ private TabSelection tabSelection; /** * A class for representing a set of tabs by their labels and a marking the previously selected one. */ static class TabSelection { private final String[] tabLabels; private String selected; public static TabSelection make(Collection contribs) { String[] labels = new String[contribs.size()]; int i = 0; for (ComparableTabContributor contrib : contribs) labels[i++] = contrib.getLabel(); return new TabSelection(labels); } public TabSelection(String... tabLabels) { this.tabLabels = tabLabels; } public String getSelected() { return selected; } /** * Ensures that the selected tab is among the set of different tab * labels. Otherwise the set request is ignored. * * @param label */ public void setSelected(String label) { for (String l : tabLabels) { if (l.equals(label)) { this.selected = label; break; } } } @Override public int hashCode() { return Arrays.hashCode(tabLabels); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TabSelection other = (TabSelection) obj; return Arrays.equals(tabLabels, other.tabLabels); } @Override public String toString() { return getClass().getName() + "[" + Arrays.toString(tabLabels) + " - " + selected + "]"; } } }