X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.selectionview%2Fsrc%2Forg%2Fsimantics%2Fselectionview%2FTabbedPropertyTable.java;h=70a0c573a8cb95073c039d06996e2d6b412236b2;hb=HEAD;hp=4f7ae3edcf976e8520b272d466480de3e992b869;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.selectionview/src/org/simantics/selectionview/TabbedPropertyTable.java b/bundles/org.simantics.selectionview/src/org/simantics/selectionview/TabbedPropertyTable.java index 4f7ae3edc..70a0c573a 100644 --- a/bundles/org.simantics.selectionview/src/org/simantics/selectionview/TabbedPropertyTable.java +++ b/bundles/org.simantics.selectionview/src/org/simantics/selectionview/TabbedPropertyTable.java @@ -1,558 +1,558 @@ -/******************************************************************************* - * 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.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.TreeSet; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -import org.eclipse.jface.layout.GridDataFactory; -import org.eclipse.jface.layout.GridLayoutFactory; -import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.resource.LocalResourceManager; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.ISelectionProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.ui.IWorkbenchPartSite; -import org.eclipse.ui.part.IPageSite; -import org.simantics.browsing.ui.common.ErrorLogger; -import org.simantics.browsing.ui.swt.TabbedPropertyPage; -import org.simantics.db.ReadGraph; -import org.simantics.db.common.request.ReadRequest; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.management.ISessionContext; -import org.simantics.ui.SimanticsUI; -import org.simantics.utils.ObjectUtils; -import org.simantics.utils.datastructures.MapList; -import org.simantics.utils.ui.ISelectionUtils; -import org.simantics.utils.ui.jface.BasePostSelectionProvider; - -/** - *

- * Subclasses may extend or reimplement the following methods as required: - *

- *

- * - * @author Tuukka Lehtonen - */ -public class TabbedPropertyTable extends Composite implements IPropertyTab { - - public static final SelectionProcessor DEFAULT_SELECTION_PROCESSOR = new SelectionProcessor() { - @Override - public Collection process(Object selection, Object object) { - return Collections.emptyList(); - } - }; - - @SuppressWarnings("rawtypes") - private SelectionProcessor selectionProcessor = DEFAULT_SELECTION_PROCESSOR; - - private Composite baseComposite; - - private final List tabs = new CopyOnWriteArrayList(); - - private final AtomicInteger activeTab = new AtomicInteger(-1); - - protected IWorkbenchPartSite sourceSite; - - protected IPageSite pageSite; - - protected ISelection currentSelection; - - /** - * The selection provider set for the page site. - */ - protected BasePostSelectionProvider pageSelectionProvider = new BasePostSelectionProvider() { - - /** - * For preventing infinite recursion, not that it should happen. - */ - private AtomicBoolean settingSelection = new AtomicBoolean(); - - /** - * Overridden like this because pageSelectionProvider is published to - * the workbench as the page site selection provider and therefore it - * possible that its {@link ISelectionProvider#setSelection(ISelection)} - * method is invoked externally and we need to propagate the selection - * to the underlying active tab and its selection provider instead of - * setting pageSelectionProvider's selection to anything. - */ - @Override - public void setSelection(ISelection selection) { - if (settingSelection.compareAndSet(false, true)) { - IPropertyTab table = getActiveTab(); - if (table != null && table.getSelectionProvider() != null) - table.getSelectionProvider().setSelection(selection); - settingSelection.set(false); - } else { - ErrorLogger.defaultLogWarning("Possible BUG: prevented recursive attempt to set selection for " - + TabbedPropertyTable.this.toString(), new Exception("trace")); - } - } - }; - -// protected ISelectionChangedListener debugPageSelectionListener = new ISelectionChangedListener() { -// { -// pageSelectionProvider.addSelectionChangedListener(this); -// } -// @Override -// public void selectionChanged(SelectionChangedEvent event) { -// System.out.println("page selection change: " + event); -// System.out.println(" provider: " + event.getSelectionProvider()); -// System.out.println(" selection: " + event.getSelection()); -// } -// }; - - protected ISelectionChangedListener activeTabSelectionListener = new ISelectionChangedListener() { - @Override - public void selectionChanged(SelectionChangedEvent event) { -// System.out.println("active tab selection change: " + event); -// System.out.println(" provider: " + event.getSelectionProvider()); -// System.out.println(" selection: " + event.getSelection()); - ISelection s = event.getSelection(); - // This is a workaround to avert calling pageSelectionProvider.setSelection here. - pageSelectionProvider.setSelectionWithoutFiring(s); - pageSelectionProvider.fireSelection(s); - pageSelectionProvider.firePostSelection(s); - } - }; - - protected LocalResourceManager resourceManager; - - public TabbedPropertyTable(IWorkbenchPartSite site, IPageSite pageSite, Composite parent, int style) { - super(parent, style); - if (site == null) - throw new IllegalArgumentException("null source site"); - if (pageSite == null) - throw new IllegalArgumentException("null page site"); - this.sourceSite = site; - this.pageSite = pageSite; - GridLayoutFactory.fillDefaults().applyTo(this); - - resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay())); - - addListener(SWT.Dispose, new Listener() { - @Override - public void handleEvent(Event event) { - //System.out.println("DISPOSING " + this + " " + System.identityHashCode(TabbedPropertyTable.this)); - activeTab.set(-1); - tabs.clear(); - - currentSelection = null; - if (currentListener != null) - currentListener.dispose(); - - TabbedPropertyTable.this.pageSite = null; - TabbedPropertyTable.this.sourceSite = null; - resourceManager.dispose(); - - } - }); - } - - @SuppressWarnings("rawtypes") - protected void setSelectionProcessor(SelectionProcessor selectionProcessor) { - this.selectionProcessor = selectionProcessor; - } - - @Override - public void createControl(Composite parent, ISessionContext context) { - createBaseComposite(parent, null); - } - - class InputListener implements org.simantics.db.procedure.Listener> { - - final private Consumer> inputCallback; - private boolean disposed = false; - - public InputListener(Consumer> inputCallback) { - this.inputCallback = inputCallback; - } - - @Override - public void exception(Throwable t) { - ErrorLogger.defaultLogError(t); - } - - @Override - public void execute(Collection result) { - inputCallback.accept(result); - } - - @Override - public boolean isDisposed() { - return disposed || TabbedPropertyTable.this.isDisposed(); - } - - public void dispose() { - disposed = true; - } - - } - - InputListener currentListener = null; - - /** - * Must be invoked from the SWT UI thread. - * - * @param selection the new selection - * @param force true to force the resetting of the new input - * even if it is the same as the previous one. - */ - @Override - @SuppressWarnings("unchecked") - public void setInput(ISessionContext context, ISelection selection, boolean force) { - //System.out.println(hashCode() + "# TabbedPropertyTable.setInput(" + selection + ", " + force + ")"); - if (isDisposed()) - return; - if (context == null) - return; - - // Check if this is a duplicate of the previous selection to reduce unnecessary flicker. - if (!force && ObjectUtils.objectEquals(currentSelection, selection)) - return; - -// System.out.println("[3] setInput " + selection + ", force=" + force); - currentSelection = selection; - - if (selectionProcessor != null) { - - if (currentListener != null) currentListener.dispose(); - - final Collection contents = ISelectionUtils.convertSelection(selection); - if (contents.isEmpty()) - return; - - currentListener = new InputListener(inputCallback(contents, context)); - - // NOTE: must be an anonymous read to guarantee that each request - // will always be performed and not taken from DB caches. - context.getSession().asyncRequest(new ReadRequest() { - - @Override - public void run(ReadGraph graph) throws DatabaseException { - - graph.syncRequest(new UniqueRead>() { - @Override - public Collection perform(ReadGraph graph) throws DatabaseException { - //System.out.println("TabbedPropertyTable.setInput.perform(" + contents + ")"); - return selectionProcessor.process(contents, graph); - } - }, currentListener); - - } - }); - - } - } - - protected Consumer> inputCallback(final Collection selectionContents, final ISessionContext sessionContext) { - return new Consumer>() { - @Override - public void accept(final Collection contribs) { - - if (isDisposed()) - return; -// if (contribs.isEmpty()) -// return; - - SimanticsUI.asyncExecSWT(TabbedPropertyTable.this, new Runnable() { - - public void run() { - - //System.out.println(Thread.currentThread() + " inputCallback: " + input); - //System.out.println("is TabbedPropertyTable " + this + " visible: " + isVisible()); - if (!isVisible()) { - // Set current selection to null to force update when the - // page becomes visible - currentSelection = null; - return; - } - - createBaseComposite(TabbedPropertyTable.this, new TabbedPropertyPage(sourceSite.getPart()) { - - /** - * The selection providers for the current set of property tabs. - */ - ISelectionProvider[] tabSelectionProviders = { null }; - - @Override - protected void initializePageSwitching() { - // Overridden to prevent TabbedPropertyPage - // from initializing PageSwitcher - } - - @Override - protected int getContainerStyle() { - return TabbedPropertyTable.this.getTabFolderStyle(); - } - - @Override - protected void pageChange(int newPageIndex) { - int oldActiveTab = activeTab.getAndSet(newPageIndex); - //System.out.println(Thread.currentThread() + " page changed: from " + oldActiveTab + " to " + newPageIndex); - super.pageChange(newPageIndex); - - // Remove possible old selection listeners from the hidden tab. - ISelection oldSelection = null; - IPropertyTab oldTab = null; - if (oldActiveTab > -1) { - oldTab = tabs.get(oldActiveTab); - ISelectionProvider pv = oldTab.getSelectionProvider(); - if (pv != null) { - oldSelection = pv.getSelection(); - pv.removeSelectionChangedListener(activeTabSelectionListener); - } - } - - // Attach selection listeners to the activated tab if possible. - ISelection activeSelection = null; - IPropertyTab activeTab = getActiveTab(); - if (activeTab != null) { - ISelectionProvider pv = activeTab.getSelectionProvider(); - if (pv != null) { - activeSelection = pv.getSelection(); - pv.addSelectionChangedListener(activeTabSelectionListener); - } - } - - String oldLabel = null; - String newLabel = null; - if (oldActiveTab > -1) - oldLabel = getPageText(oldActiveTab); - if (newPageIndex > -1) - newLabel = getPageText(newPageIndex); - activeTabChanged(new TabChangeEvent(oldTab, oldLabel, activeTab, newLabel)); - - // This is a workaround to avert calling pageSelectionProvider.setSelection here. - pageSelectionProvider.setSelectionWithoutFiring(activeSelection); - if (!ObjectUtils.objectEquals(oldSelection, activeSelection)) { - pageSelectionProvider.fireSelection(activeSelection); - pageSelectionProvider.firePostSelection(activeSelection); - } - } - - @Override - protected void createPages() { - - // 1. loop through a list of possible contributors - // 2. list the ones that can contribute, in priority order told by the contributions themselves - // 3. add pages in priority order - // 4. initialize pages - - // Categorize contributions by id - MapList ml = new MapList(); - for (Object o : contribs) { - if (o instanceof ComparableTabContributor) { - ComparableTabContributor c = (ComparableTabContributor) o; - ml.add(c.getId(), c); - } else { - System.out.println("WARNING: SelectionProcessor produced an unusable contribution to TabbedPropertyTable: " + o); - } - } - - // For each id take the greatest (id) contribution - Collection contributions = new TreeSet(); - for(String key : ml.getKeys()) { - TreeSet ts = new TreeSet(ml.getValuesUnsafe(key)); - ComparableTabContributor contrib = ts.first(); - contributions.add(contrib); - } - - // Sort contributions by id - List contributionList = new ArrayList(contributions); - - if (contributions.isEmpty()) { - Composite empty = createEmptyControl(getContainer(), SWT.NONE); - addPage(empty, "No properties to show", null); - } else { - // Set single selection provider into pageSite - // that is dispatched every time the selection - // in the property view changes, be it because - // the selection changed in the current tab or - // because the active tab, i.e. the selection - // provider is changed. - pageSite.setSelectionProvider(pageSelectionProvider); - - // Allocate space for each tab to specify - // its own selection provider. - tabSelectionProviders = new ISelectionProvider[contributions.size()]; - int index = 0; - - for (ComparableTabContributor cc : contributionList) { - Composite middleware = new Composite(getContainer(), SWT.NONE); - GridLayoutFactory.fillDefaults().applyTo(middleware); - - Control control = null; - - // Create a wrapper for pageSite to make it possible - // for each tab contribution to specify its own - // selection provider. - final int tabIndex = index++; - IPageSite siteWrapper = new PageSiteProxy(pageSite) { - @Override - public void setSelectionProvider(ISelectionProvider provider) { - tabSelectionProviders[tabIndex] = provider; - if(pageSelectionProvider != null && provider != null) - pageSelectionProvider.setAndFireNonEqualSelection(provider.getSelection()); - } - @Override - public ISelectionProvider getSelectionProvider() { - return tabSelectionProviders[tabIndex]; - } - }; - - IPropertyTab tab = cc.create(middleware, siteWrapper, sessionContext, cc.getInput()); - if (tab != null) { - tabs.add(tab); - control = tab.getControl(); - if(control != null && !control.isDisposed()) GridDataFactory.fillDefaults().grab(true, true).applyTo(control); - } else { - control = createEmptyControl(middleware, SWT.NONE); -// Button b = new Button(middleware, SWT.BORDER); -// GridDataFactory.fillDefaults().grab(true, true).applyTo(b); -// b.setText("FOO"); - } - - addPage(middleware, cc.getLabel(), cc.getImage()); - - if (tab != null) { - Object input = cc.getInput(); - tab.setInput(sessionContext, new StructuredSelection(input), false); - } - } - - ComparableTabContributor cc = TabbedPropertyTable.this.selectInitialTab(selectionContents, contributionList); - if (cc != null) { - int activePage = contributionList.indexOf(cc); - if (activePage != -1) - setActivePage(activePage); - } - } - } - - Composite createEmptyControl(Composite parent, int flags) { - Composite empty = new Composite(parent, flags); - GridLayoutFactory.fillDefaults().applyTo(empty); - return empty; - } - }); - layout(); - } - }); - } - }; - } - - @Override - public Control getControl() { - return this; - } - - @Override - public void requestFocus() { - IPropertyTab table = getActiveTab(); - if (table != null) - table.requestFocus(); - } - - public IPropertyTab getActiveTab() { - int index = activeTab.get(); - if (index < 0 || index >= tabs.size()) - return null; - IPropertyTab tab = tabs.get(index); - return tab; - } - - protected Composite createBaseComposite(final Composite parent, TabbedPropertyPage page) { - // 1. dispose the previous UI container - disposeAll(); - - // 2. construct new base for the tabbed property UI - baseComposite = new Composite(parent, SWT.NONE); - - // -// parent.setBackground(getDisplay().getSystemColor(SWT.COLOR_RED)); -// baseComposite.setBackground(getDisplay().getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND)); - // - - if (page != null) - page.createPartControl(baseComposite); - - GridLayoutFactory.fillDefaults().applyTo(parent); - GridDataFactory.fillDefaults().grab(true, true).applyTo(baseComposite); - baseComposite.setLayout(new FillLayout()); - - return baseComposite; - } - - private void disposeAll() { - if (baseComposite != null && !baseComposite.isDisposed()) { - baseComposite.dispose(); - baseComposite = null; - } - activeTab.set(-1); - for (IPropertyTab tab : tabs) - tab.dispose(); - tabs.clear(); - } - - @Override - public ISelectionProvider getSelectionProvider() { - return pageSelectionProvider; - } - - /** - * Override to perform actions when the active tab changes. - * - * @param previousTab - * @param activeTab - */ - protected void activeTabChanged(TabChangeEvent event) { - } - - /** - * @param selectionContents - * @param contributions - * @return - */ - protected ComparableTabContributor selectInitialTab(Collection selectionContents, Collection contributions) { - return null; - } - - /** - * @return - */ - protected int getTabFolderStyle() { - return SWT.NONE; - } - -} +/******************************************************************************* + * 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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.TreeSet; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.part.IPageSite; +import org.simantics.browsing.ui.common.ErrorLogger; +import org.simantics.browsing.ui.swt.TabbedPropertyPage; +import org.simantics.db.ReadGraph; +import org.simantics.db.common.request.ReadRequest; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.ui.SimanticsUI; +import org.simantics.utils.ObjectUtils; +import org.simantics.utils.datastructures.MapList; +import org.simantics.utils.ui.ISelectionUtils; +import org.simantics.utils.ui.jface.BasePostSelectionProvider; + +/** + *

+ * Subclasses may extend or reimplement the following methods as required: + *

    + *
  • createBaseComposite - reimplement to customize how the + * composite is constructed that is the basis of the whole tab container. + *
+ *

+ * + * @author Tuukka Lehtonen + */ +public class TabbedPropertyTable extends Composite implements IPropertyTab { + + public static final SelectionProcessor DEFAULT_SELECTION_PROCESSOR = new SelectionProcessor() { + @Override + public Collection process(Object selection, Object object) { + return Collections.emptyList(); + } + }; + + @SuppressWarnings("rawtypes") + private SelectionProcessor selectionProcessor = DEFAULT_SELECTION_PROCESSOR; + + private Composite baseComposite; + + private final List tabs = new CopyOnWriteArrayList(); + + private final AtomicInteger activeTab = new AtomicInteger(-1); + + protected IWorkbenchPartSite sourceSite; + + protected IPageSite pageSite; + + protected ISelection currentSelection; + + /** + * The selection provider set for the page site. + */ + protected BasePostSelectionProvider pageSelectionProvider = new BasePostSelectionProvider() { + + /** + * For preventing infinite recursion, not that it should happen. + */ + private AtomicBoolean settingSelection = new AtomicBoolean(); + + /** + * Overridden like this because pageSelectionProvider is published to + * the workbench as the page site selection provider and therefore it + * possible that its {@link ISelectionProvider#setSelection(ISelection)} + * method is invoked externally and we need to propagate the selection + * to the underlying active tab and its selection provider instead of + * setting pageSelectionProvider's selection to anything. + */ + @Override + public void setSelection(ISelection selection) { + if (settingSelection.compareAndSet(false, true)) { + IPropertyTab table = getActiveTab(); + if (table != null && table.getSelectionProvider() != null) + table.getSelectionProvider().setSelection(selection); + settingSelection.set(false); + } else { + ErrorLogger.defaultLogWarning("Possible BUG: prevented recursive attempt to set selection for " + + TabbedPropertyTable.this.toString(), new Exception("trace")); + } + } + }; + +// protected ISelectionChangedListener debugPageSelectionListener = new ISelectionChangedListener() { +// { +// pageSelectionProvider.addSelectionChangedListener(this); +// } +// @Override +// public void selectionChanged(SelectionChangedEvent event) { +// System.out.println("page selection change: " + event); +// System.out.println(" provider: " + event.getSelectionProvider()); +// System.out.println(" selection: " + event.getSelection()); +// } +// }; + + protected ISelectionChangedListener activeTabSelectionListener = new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { +// System.out.println("active tab selection change: " + event); +// System.out.println(" provider: " + event.getSelectionProvider()); +// System.out.println(" selection: " + event.getSelection()); + ISelection s = event.getSelection(); + // This is a workaround to avert calling pageSelectionProvider.setSelection here. + pageSelectionProvider.setSelectionWithoutFiring(s); + pageSelectionProvider.fireSelection(s); + pageSelectionProvider.firePostSelection(s); + } + }; + + protected LocalResourceManager resourceManager; + + public TabbedPropertyTable(IWorkbenchPartSite site, IPageSite pageSite, Composite parent, int style) { + super(parent, style); + if (site == null) + throw new IllegalArgumentException("null source site"); + if (pageSite == null) + throw new IllegalArgumentException("null page site"); + this.sourceSite = site; + this.pageSite = pageSite; + GridLayoutFactory.fillDefaults().applyTo(this); + + resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay())); + + addListener(SWT.Dispose, new Listener() { + @Override + public void handleEvent(Event event) { + //System.out.println("DISPOSING " + this + " " + System.identityHashCode(TabbedPropertyTable.this)); + activeTab.set(-1); + tabs.clear(); + + currentSelection = null; + if (currentListener != null) + currentListener.dispose(); + + TabbedPropertyTable.this.pageSite = null; + TabbedPropertyTable.this.sourceSite = null; + resourceManager.dispose(); + + } + }); + } + + @SuppressWarnings("rawtypes") + protected void setSelectionProcessor(SelectionProcessor selectionProcessor) { + this.selectionProcessor = selectionProcessor; + } + + @Override + public void createControl(Composite parent, ISessionContext context) { + createBaseComposite(parent, null); + } + + class InputListener implements org.simantics.db.procedure.Listener> { + + final private Consumer> inputCallback; + private boolean disposed = false; + + public InputListener(Consumer> inputCallback) { + this.inputCallback = inputCallback; + } + + @Override + public void exception(Throwable t) { + ErrorLogger.defaultLogError(t); + } + + @Override + public void execute(Collection result) { + inputCallback.accept(result); + } + + @Override + public boolean isDisposed() { + return disposed || TabbedPropertyTable.this.isDisposed(); + } + + public void dispose() { + disposed = true; + } + + } + + InputListener currentListener = null; + + /** + * Must be invoked from the SWT UI thread. + * + * @param selection the new selection + * @param force true to force the resetting of the new input + * even if it is the same as the previous one. + */ + @Override + @SuppressWarnings("unchecked") + public void setInput(ISessionContext context, ISelection selection, boolean force) { + //System.out.println(hashCode() + "# TabbedPropertyTable.setInput(" + selection + ", " + force + ")"); + if (isDisposed()) + return; + if (context == null) + return; + + // Check if this is a duplicate of the previous selection to reduce unnecessary flicker. + if (!force && ObjectUtils.objectEquals(currentSelection, selection)) + return; + +// System.out.println("[3] setInput " + selection + ", force=" + force); + currentSelection = selection; + + if (selectionProcessor != null) { + + if (currentListener != null) currentListener.dispose(); + + final Collection contents = ISelectionUtils.convertSelection(selection); + if (contents.isEmpty()) + return; + + currentListener = new InputListener(inputCallback(contents, context)); + + // NOTE: must be an anonymous read to guarantee that each request + // will always be performed and not taken from DB caches. + context.getSession().asyncRequest(new ReadRequest() { + + @Override + public void run(ReadGraph graph) throws DatabaseException { + + graph.syncRequest(new UniqueRead>() { + @Override + public Collection perform(ReadGraph graph) throws DatabaseException { + //System.out.println("TabbedPropertyTable.setInput.perform(" + contents + ")"); + return selectionProcessor.process(contents, graph); + } + }, currentListener); + + } + }); + + } + } + + protected Consumer> inputCallback(final Collection selectionContents, final ISessionContext sessionContext) { + return new Consumer>() { + @Override + public void accept(final Collection contribs) { + + if (isDisposed()) + return; +// if (contribs.isEmpty()) +// return; + + SimanticsUI.asyncExecSWT(TabbedPropertyTable.this, new Runnable() { + + public void run() { + + //System.out.println(Thread.currentThread() + " inputCallback: " + input); + //System.out.println("is TabbedPropertyTable " + this + " visible: " + isVisible()); + if (!isVisible()) { + // Set current selection to null to force update when the + // page becomes visible + currentSelection = null; + return; + } + + createBaseComposite(TabbedPropertyTable.this, new TabbedPropertyPage(sourceSite.getPart()) { + + /** + * The selection providers for the current set of property tabs. + */ + ISelectionProvider[] tabSelectionProviders = { null }; + + @Override + protected void initializePageSwitching() { + // Overridden to prevent TabbedPropertyPage + // from initializing PageSwitcher + } + + @Override + protected int getContainerStyle() { + return TabbedPropertyTable.this.getTabFolderStyle(); + } + + @Override + protected void pageChange(int newPageIndex) { + int oldActiveTab = activeTab.getAndSet(newPageIndex); + //System.out.println(Thread.currentThread() + " page changed: from " + oldActiveTab + " to " + newPageIndex); + super.pageChange(newPageIndex); + + // Remove possible old selection listeners from the hidden tab. + ISelection oldSelection = null; + IPropertyTab oldTab = null; + if (oldActiveTab > -1) { + oldTab = tabs.get(oldActiveTab); + ISelectionProvider pv = oldTab.getSelectionProvider(); + if (pv != null) { + oldSelection = pv.getSelection(); + pv.removeSelectionChangedListener(activeTabSelectionListener); + } + } + + // Attach selection listeners to the activated tab if possible. + ISelection activeSelection = null; + IPropertyTab activeTab = getActiveTab(); + if (activeTab != null) { + ISelectionProvider pv = activeTab.getSelectionProvider(); + if (pv != null) { + activeSelection = pv.getSelection(); + pv.addSelectionChangedListener(activeTabSelectionListener); + } + } + + String oldLabel = null; + String newLabel = null; + if (oldActiveTab > -1) + oldLabel = getPageText(oldActiveTab); + if (newPageIndex > -1) + newLabel = getPageText(newPageIndex); + activeTabChanged(new TabChangeEvent(oldTab, oldLabel, activeTab, newLabel)); + + // This is a workaround to avert calling pageSelectionProvider.setSelection here. + pageSelectionProvider.setSelectionWithoutFiring(activeSelection); + if (!ObjectUtils.objectEquals(oldSelection, activeSelection)) { + pageSelectionProvider.fireSelection(activeSelection); + pageSelectionProvider.firePostSelection(activeSelection); + } + } + + @Override + protected void createPages() { + + // 1. loop through a list of possible contributors + // 2. list the ones that can contribute, in priority order told by the contributions themselves + // 3. add pages in priority order + // 4. initialize pages + + // Categorize contributions by id + MapList ml = new MapList(); + for (Object o : contribs) { + if (o instanceof ComparableTabContributor) { + ComparableTabContributor c = (ComparableTabContributor) o; + ml.add(c.getId(), c); + } else { + System.out.println("WARNING: SelectionProcessor produced an unusable contribution to TabbedPropertyTable: " + o); + } + } + + // For each id take the greatest (id) contribution + Collection contributions = new TreeSet(); + for(String key : ml.getKeys()) { + TreeSet ts = new TreeSet(ml.getValuesUnsafe(key)); + ComparableTabContributor contrib = ts.first(); + contributions.add(contrib); + } + + // Sort contributions by id + List contributionList = new ArrayList(contributions); + + if (contributions.isEmpty()) { + Composite empty = createEmptyControl(getContainer(), SWT.NONE); + addPage(empty, "No properties to show", null); + } else { + // Set single selection provider into pageSite + // that is dispatched every time the selection + // in the property view changes, be it because + // the selection changed in the current tab or + // because the active tab, i.e. the selection + // provider is changed. + pageSite.setSelectionProvider(pageSelectionProvider); + + // Allocate space for each tab to specify + // its own selection provider. + tabSelectionProviders = new ISelectionProvider[contributions.size()]; + int index = 0; + + for (ComparableTabContributor cc : contributionList) { + Composite middleware = new Composite(getContainer(), SWT.NONE); + GridLayoutFactory.fillDefaults().applyTo(middleware); + + Control control = null; + + // Create a wrapper for pageSite to make it possible + // for each tab contribution to specify its own + // selection provider. + final int tabIndex = index++; + IPageSite siteWrapper = new PageSiteProxy(pageSite) { + @Override + public void setSelectionProvider(ISelectionProvider provider) { + tabSelectionProviders[tabIndex] = provider; + if(pageSelectionProvider != null && provider != null) + pageSelectionProvider.setAndFireNonEqualSelection(provider.getSelection()); + } + @Override + public ISelectionProvider getSelectionProvider() { + return tabSelectionProviders[tabIndex]; + } + }; + + IPropertyTab tab = cc.create(middleware, siteWrapper, sessionContext, cc.getInput()); + if (tab != null) { + tabs.add(tab); + control = tab.getControl(); + if(control != null && !control.isDisposed()) GridDataFactory.fillDefaults().grab(true, true).applyTo(control); + } else { + control = createEmptyControl(middleware, SWT.NONE); +// Button b = new Button(middleware, SWT.BORDER); +// GridDataFactory.fillDefaults().grab(true, true).applyTo(b); +// b.setText("FOO"); + } + + addPage(middleware, cc.getLabel(), cc.getImage()); + + if (tab != null) { + Object input = cc.getInput(); + tab.setInput(sessionContext, new StructuredSelection(input), false); + } + } + + ComparableTabContributor cc = TabbedPropertyTable.this.selectInitialTab(selectionContents, contributionList); + if (cc != null) { + int activePage = contributionList.indexOf(cc); + if (activePage != -1) + setActivePage(activePage); + } + } + } + + Composite createEmptyControl(Composite parent, int flags) { + Composite empty = new Composite(parent, flags); + GridLayoutFactory.fillDefaults().applyTo(empty); + return empty; + } + }); + layout(); + } + }); + } + }; + } + + @Override + public Control getControl() { + return this; + } + + @Override + public void requestFocus() { + IPropertyTab table = getActiveTab(); + if (table != null) + table.requestFocus(); + } + + public IPropertyTab getActiveTab() { + int index = activeTab.get(); + if (index < 0 || index >= tabs.size()) + return null; + IPropertyTab tab = tabs.get(index); + return tab; + } + + protected Composite createBaseComposite(final Composite parent, TabbedPropertyPage page) { + // 1. dispose the previous UI container + disposeAll(); + + // 2. construct new base for the tabbed property UI + baseComposite = new Composite(parent, SWT.NONE); + + // +// parent.setBackground(getDisplay().getSystemColor(SWT.COLOR_RED)); +// baseComposite.setBackground(getDisplay().getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND)); + // + + if (page != null) + page.createPartControl(baseComposite); + + GridLayoutFactory.fillDefaults().applyTo(parent); + GridDataFactory.fillDefaults().grab(true, true).applyTo(baseComposite); + baseComposite.setLayout(new FillLayout()); + + return baseComposite; + } + + private void disposeAll() { + if (baseComposite != null && !baseComposite.isDisposed()) { + baseComposite.dispose(); + baseComposite = null; + } + activeTab.set(-1); + for (IPropertyTab tab : tabs) + tab.dispose(); + tabs.clear(); + } + + @Override + public ISelectionProvider getSelectionProvider() { + return pageSelectionProvider; + } + + /** + * Override to perform actions when the active tab changes. + * + * @param previousTab + * @param activeTab + */ + protected void activeTabChanged(TabChangeEvent event) { + } + + /** + * @param selectionContents + * @param contributions + * @return + */ + protected ComparableTabContributor selectInitialTab(Collection selectionContents, Collection contributions) { + return null; + } + + /** + * @return + */ + protected int getTabFolderStyle() { + return SWT.NONE; + } + +}