/******************************************************************************* * 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.Iterator; import java.util.List; import java.util.function.Consumer; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSource; 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.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchSite; import org.simantics.browsing.ui.GraphExplorer; import org.simantics.browsing.ui.common.ColumnKeys; import org.simantics.browsing.ui.common.EvaluatorData; import org.simantics.browsing.ui.common.EvaluatorData.Evaluator; import org.simantics.browsing.ui.common.EvaluatorDataImpl; import org.simantics.browsing.ui.common.comparators.AlphanumericComparatorFactory; import org.simantics.browsing.ui.common.processors.ComparableFactoryResolver; import org.simantics.browsing.ui.common.processors.ComparableSelectorQueryProcessor; import org.simantics.browsing.ui.common.processors.FilterSelectionRequestQueryProcessor; import org.simantics.browsing.ui.common.processors.ImageDecoratorFactoryResolver; import org.simantics.browsing.ui.common.processors.ImagerFactoryResolver; import org.simantics.browsing.ui.common.processors.LabelDecoratorFactoryResolver; import org.simantics.browsing.ui.common.processors.LabelerFactoryResolver; import org.simantics.browsing.ui.common.processors.UserSelectedComparableFactoryQueryProcessor; import org.simantics.browsing.ui.common.processors.UserSelectedViewpointFactoryQueryProcessor; import org.simantics.browsing.ui.common.processors.ViewpointFactoryResolver; import org.simantics.browsing.ui.common.views.IFilterArea; import org.simantics.browsing.ui.common.views.IFilterAreaProvider; import org.simantics.browsing.ui.common.views.PropertyTableConstants; import org.simantics.browsing.ui.graph.impl.ArrayPropertyLabelerFactory; import org.simantics.browsing.ui.graph.impl.ArrayPropertyValueViewpointFactory; import org.simantics.browsing.ui.graph.impl.AsyncReadGraphDataSource; import org.simantics.browsing.ui.graph.impl.PropertyViewpointFactory; import org.simantics.browsing.ui.graph.impl.ReadGraphDataSource; import org.simantics.browsing.ui.graph.impl.RelationContextLabelerFactory; import org.simantics.browsing.ui.graph.impl.ResourceProperty; import org.simantics.browsing.ui.graph.impl.StringRepresentationLabelerFactory; import org.simantics.browsing.ui.swt.ArrayPropertyImagerFactory; import org.simantics.browsing.ui.swt.ComparatorSelector; import org.simantics.browsing.ui.swt.ContextMenuInitializer; import org.simantics.browsing.ui.swt.GraphExplorerFactory; import org.simantics.browsing.ui.swt.PropertyTableUtil; import org.simantics.browsing.ui.swt.RootFilterArea; import org.simantics.browsing.ui.swt.TypesQueryProcessor; import org.simantics.browsing.ui.swt.ViewpointSelector; import org.simantics.db.AsyncReadGraph; import org.simantics.db.ReadGraph; import org.simantics.db.RelationContext; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.Statement; import org.simantics.db.common.ResourceArray; import org.simantics.db.management.ISessionContext; import org.simantics.ui.dnd.BasicDragSource; import org.simantics.ui.dnd.SessionContainer; import org.simantics.ui.utils.ResourceAdaptionUtils; import org.simantics.utils.ObjectUtils; import org.simantics.utils.ui.LayoutUtils; /** *

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

*

* * @author Tuukka Lehtonen */ public class PropertyTable extends Composite implements IFilterAreaProvider, IPropertyTab2 { protected IWorkbenchSite site; protected GraphExplorer explorer; protected Object dragSource; protected ISessionContext sessionContext; protected ISelection currentInput; protected UserSelectedComparableFactoryQueryProcessor userSelectedComparableFactoryQueryProcessor; protected UserSelectedViewpointFactoryQueryProcessor userSelectedViewpointFactoryQueryProcessor; protected FilterSelectionRequestQueryProcessor filterSelectionRequestQueryProcessor; protected LocalResourceManager resourceManager; private RootFilterArea filterArea; private IContributionItem filterAreaContribution; private IActionBars actionBars; public PropertyTable(IWorkbenchSite site, Composite parent, int style) { super(parent, style); this.site = site; //System.out.println("created " + this + " " + System.identityHashCode(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(PropertyTable.this)); tableDisposed(); PropertyTableUtil.setupDragSource(dragSource, null); PropertyTable.this.site = null; explorer = null; dragSource = null; sessionContext = null; currentInput = null; userSelectedComparableFactoryQueryProcessor = null; userSelectedViewpointFactoryQueryProcessor = null; filterSelectionRequestQueryProcessor = null; resourceManager.dispose(); } }); } /** * Override to perform actions when this property table is disposed. * Remember to call super. */ protected void tableDisposed() { getDisplay().asyncExec(new Runnable() { @Override public void run() { if (filterAreaContribution != null) { actionBars.getToolBarManager().remove(filterAreaContribution); filterAreaContribution.dispose(); filterAreaContribution = null; actionBars.updateActionBars(); } } }); } @Override public Control getControl() { return this; } @Override public void createControl(final Composite parent, ISessionContext context) { explorer = createExplorerControl(parent); dragSource = createDragSource(explorer); Control control = explorer.getControl(); userSelectedComparableFactoryQueryProcessor = new UserSelectedComparableFactoryQueryProcessor(); userSelectedViewpointFactoryQueryProcessor = new UserSelectedViewpointFactoryQueryProcessor(); filterSelectionRequestQueryProcessor = new FilterSelectionRequestQueryProcessor(); parent.setLayout(LayoutUtils.createNoBorderGridLayout(2, false)); GridDataFactory.fillDefaults().grab(true, true).span(2,1).applyTo(control); addControls(parent); setSessionContext(context); } protected void addControls(Composite parent) { //createSelectors(parent); //createFilterArea(parent); } protected void createSelectors(Composite parent) { Control control = explorer.getControl(); ComparatorSelector comparatorSelector = new ComparatorSelector(explorer, userSelectedComparableFactoryQueryProcessor, parent, SWT.READ_ONLY); comparatorSelector.moveAbove(control); ViewpointSelector viewpointSelector = new ViewpointSelector(explorer, userSelectedViewpointFactoryQueryProcessor, parent, SWT.READ_ONLY); viewpointSelector.moveAbove(control); } protected void createFilterArea(IActionBars actionBars) { this.filterAreaContribution = new ContributionItem("filterArea") { ToolItem item; @Override public boolean isDynamic() { return true; } @Override public final void fill(ToolBar parent, int index) { Control control = createControl(parent); item = new ToolItem(parent, SWT.SEPARATOR, index); item.setControl(control); item.setWidth(computeWidth(control)); } protected Control createControl(Composite parent) { filterArea = new RootFilterArea(explorer, filterSelectionRequestQueryProcessor, parent, SWT.NONE); return filterArea; } protected int computeWidth(Control control) { // FIXME: better size control? return control.computeSize(150, SWT.DEFAULT, true).x; } @Override public void dispose() { if (filterArea != null) { filterArea.dispose(); filterArea = null; } if (item != null) { item.dispose(); item = null; } } }; this.actionBars = actionBars; actionBars.getToolBarManager().add(filterAreaContribution); } protected void createFilterArea(Composite parent) { Control control = explorer.getControl(); filterArea = new RootFilterArea(explorer, filterSelectionRequestQueryProcessor, parent, SWT.NONE); filterArea.moveAbove(control); } protected GraphExplorer createExplorerControl(Composite parent) { GraphExplorer e = GraphExplorerFactory.getInstance().create(parent, SWT.FULL_SELECTION); Control c = e.getControl(); ISelectionProvider selectionProvider = (ISelectionProvider)e.getAdapter(ISelectionProvider.class); new ContextMenuInitializer("#PropertiesPopup").createContextMenu(c, selectionProvider, site); if (c instanceof Tree) ((Tree) c).setLinesVisible(true); e.setColumns(PropertyTableConstants.NORMAL_COLUMNS); return e; } /** * Override to customize drag source initialization. This default * implementation creates a {@link BasicDragSource}. The drag source is * initialized when the active database session is set. * * @param explorer * @return the object representing the drag source. If the object implements * {@link SessionContainer}, its * {@link SessionContainer#setSession(Session)} will be invoked * every time the active database session changes. */ protected Object createDragSource(GraphExplorer explorer) { Control c = explorer.getControl(); DragSource existingSource = (DragSource) c.getData(DND.DRAG_SOURCE_KEY); if (existingSource != null) return existingSource; return PropertyTableUtil.createResourceDragSource(explorer); } protected void setupDragSource(ISessionContext sessionContext) { PropertyTableUtil.setupDragSource(dragSource, sessionContext); } @Override public ISelectionProvider getSelectionProvider() { GraphExplorer e = explorer; if (e != null) { IPostSelectionProvider selectionProvider = (IPostSelectionProvider)e.getAdapter(IPostSelectionProvider.class); return selectionProvider; } return null; } @Override public void requestFocus() { GraphExplorer e = explorer; if (e != null) e.setFocus(); } @Override public IFilterArea getFilterArea() { return filterArea; } protected ISessionContext getSessionContext() { return sessionContext; } protected Session getSession() { return sessionContext != null ? sessionContext.getSession() : null; } @Override public void setInput(ISessionContext context, ISelection selection, boolean force) { if (isDisposed()) return; if (sessionContext == null) return; // Check if this is a duplicate of the previous selection to reduce unnecessary flicker. if (!force && ObjectUtils.objectEquals(currentInput, selection)) return; //Resource[] rs = ResourceAdaptionUtils.toResources(selection); Object[] rs = toObjects(selection); // System.out.println("resources: " + Arrays.toString(rs)); if (rs.length == 0) { explorer.setRoot(GraphExplorer.EMPTY_INPUT); } else { if (rs.length == 1) { explorer.setRoot(rs[0]); } else { explorer.setRoot(rs); } } currentInput = selection; } protected Object[] toObjects(ISelection s) { ResourceArray[] ras = ResourceAdaptionUtils.toResourceArrays(s); if (ras.length > 0) return ras; Resource[] rs = ResourceAdaptionUtils.toResources(s); if (rs.length > 0) return rs; if (!(s instanceof IStructuredSelection)) return new Object[0]; IStructuredSelection ss = (IStructuredSelection) s; List result = new ArrayList(); for (Iterator iterator = ss.iterator(); iterator.hasNext();) result.add(iterator.next()); return result.toArray(); } public void setSessionContext(ISessionContext sessionContext) { if (isDisposed()) return; // System.out.println("setSessionContext: " + sessionContext + " (" + this + ")"); if (this.sessionContext == sessionContext) return; this.sessionContext = sessionContext; Session session = sessionContext != null ? sessionContext.getSession() : null; reinitializeExplorer(explorer, sessionContext, session); } protected void reinitializeExplorer(GraphExplorer explorer, ISessionContext sessionContext, Session session) { if (isDisposed()) return; setupDragSource(sessionContext); // TODO: synchronize the explorer somehow to make sure nothing running in its background is trying to modify the explorer during this process EvaluatorData data = new EvaluatorDataImpl(); if (session != null) { explorer.setDataSource(new AsyncReadGraphDataSource(session)); explorer.setDataSource(new ReadGraphDataSource(session)); Evaluator resourceEvaluator = data.newEvaluator() .addViewpoint(new PropertyViewpointFactory(), 1.0) .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY), 2.0) .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY, true), 1.0) .addLabeler(new StringRepresentationLabelerFactory(ColumnKeys.PROPERTY), 1.0); Evaluator statementEvaluator = data.newEvaluator() .addViewpoint(new PropertyViewpointFactory(), 1.0) .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY), 2.0) .addComparator(new AlphanumericComparatorFactory(ColumnKeys.PROPERTY, true), 1.0); Evaluator relationContextEvaluator = data.newEvaluator() .addLabeler(new RelationContextLabelerFactory(), 1.0); Evaluator propertyEvaluator = data.newEvaluator() .addViewpoint(new ArrayPropertyValueViewpointFactory(), 1.0) .addLabeler(new ArrayPropertyLabelerFactory(), 1.0) .addImager(new ArrayPropertyImagerFactory(), 1.0); data.addEvaluator(Resource.class, resourceEvaluator); data.addEvaluator(ResourceArray.class, resourceEvaluator); data.addEvaluator(Statement.class, statementEvaluator); data.addEvaluator(RelationContext.class, relationContextEvaluator); data.addEvaluator(ResourceProperty.class, propertyEvaluator); } else { explorer.removeDataSource(AsyncReadGraph.class); explorer.removeDataSource(ReadGraph.class); } explorer.setProcessor(new ComparableFactoryResolver(data)); explorer.setProcessor(new ViewpointFactoryResolver(data)); explorer.setProcessor(new LabelerFactoryResolver(data)); explorer.setProcessor(new ImagerFactoryResolver(data)); explorer.setProcessor(new LabelDecoratorFactoryResolver(data)); explorer.setProcessor(new ImageDecoratorFactoryResolver(data)); explorer.setPrimitiveProcessor(new TypesQueryProcessor()); explorer.setPrimitiveProcessor(userSelectedViewpointFactoryQueryProcessor); explorer.setProcessor(new ComparableSelectorQueryProcessor()); explorer.setPrimitiveProcessor(userSelectedComparableFactoryQueryProcessor); explorer.setPrimitiveProcessor(filterSelectionRequestQueryProcessor); // Make sure the explorer gets reset. explorer.setRoot(GraphExplorer.EMPTY_INPUT); } @Override public void updatePartName(Consumer updateCallback) { ISelection forSelection = currentInput; if (forSelection instanceof IStructuredSelection) { IStructuredSelection s = (IStructuredSelection) forSelection; int size = s.size(); if (size == 1) { Object obj = s.getFirstElement(); if (obj != null) updateCallback.accept(obj.toString()); else updateCallback.accept("Properties"); } else { updateCallback.accept("Properties [" + size + "]"); } } else { updateCallback.accept("Properties"); } } }