X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FGraphExplorerViewBase.java;fp=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FGraphExplorerViewBase.java;h=f748656a5ba10f484a1dd9701f7c4f24b185c7b7;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerViewBase.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerViewBase.java new file mode 100644 index 000000000..f748656a5 --- /dev/null +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/GraphExplorerViewBase.java @@ -0,0 +1,544 @@ +/******************************************************************************* + * 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.swt; + +import java.util.Map; + +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IViewSite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.part.ViewPart; +import org.simantics.browsing.ui.Column; +import org.simantics.browsing.ui.GraphExplorer; +import org.simantics.browsing.ui.NodeContext; +import org.simantics.browsing.ui.common.ColumnKeys; +import org.simantics.browsing.ui.common.views.IViewArguments; +import org.simantics.browsing.ui.graph.impl.SessionContextInputSource; +import org.simantics.db.Session; +import org.simantics.db.common.request.Queries; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.management.ISessionContextChangedListener; +import org.simantics.db.management.ISessionContextProvider; +import org.simantics.db.management.SessionContextChangedEvent; +import org.simantics.project.IProject; +import org.simantics.project.ProjectKeys; +import org.simantics.ui.SimanticsUI; +import org.simantics.ui.dnd.BasicDragSource; +import org.simantics.ui.dnd.SessionContainer; +import org.simantics.ui.workbench.IPropertyPage; +import org.simantics.utils.ObjectUtils; +import org.simantics.utils.datastructures.Function; +import org.simantics.utils.datastructures.disposable.DisposeState; +import org.simantics.utils.datastructures.hints.HintListenerAdapter; +import org.simantics.utils.datastructures.hints.HintTracker; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintListener; +import org.simantics.utils.datastructures.hints.IHintObservable; +import org.simantics.utils.datastructures.hints.IHintTracker; +import org.simantics.utils.ui.LayoutUtils; + +/** + * An abstract base Eclipse workbench ViewPart for use in situations where a + * tree-based GraphExplorer graph model browser is needed. + * + *

+ * Override to customize behavior: + *

+ *

+ * + *

+ * You can invoke the following methods from within + * {@link #createControls(Composite)} to customize how the view keeps track of + * the active ISessionContext and how the view resolves the input object of the + * GraphExplorer control. + *

+ *

+ * + * @author Tuukka Lehtonen + * @deprecated in favor of org.simantics.views.swt.ModelledView + */ +public abstract class GraphExplorerViewBase extends ViewPart { + + /** + * The default hint tracker that will be active if + * {@link GraphExplorerViewBase#setSessionContextTracker(IHintTracker) is + * not called. + */ + public class SessionContextProjectTracker extends HintTracker { + public SessionContextProjectTracker() { + IHintListener activeProjectListener = new HintListenerAdapter() { + @Override + public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { + applySessionContext(getSessionContext()); + } + }; + addKeyHintListener(ProjectKeys.KEY_PROJECT, activeProjectListener); + } + } + + /** + * The default implementation of a {@link SessionContextInputSource} that + * will be active if + * {@link GraphExplorerViewBase#setInputSource(SessionContextInputSource)} + * is not called. + */ + public class SessionContextProjectSource implements SessionContextInputSource { + /** + * Returns the input object used by this GraphExplorer view. This object + * will be the starting point for all content shown in this explorer tree. + * + * @param sessionContext a valid database session context + * @return the root object of the graph explorer tree or null + * to indicate that there is no input and nothing should be shown. + */ + @Override + public Object get(ISessionContext ctx) { + if (ctx == null) + return GraphExplorer.EMPTY_INPUT; + + String inputId = getViewArguments().get(IViewArguments.INPUT); + if (inputId != null) { + try { + return ctx.getSession().syncRequest(Queries.resource(inputId)); + } catch (DatabaseException e) { + // Ok, the view argument was invalid. Just continue to the next + // method. + } + } + + Object input = GraphExplorer.EMPTY_INPUT; + IProject project2 = ctx.getHint(ProjectKeys.KEY_PROJECT); + if (project2 != null) + input = project2.get(); + return input; + } + + @Override + public IWorkbenchPart getProvider() { + return null; + } + + } + + protected LocalResourceManager resourceManager; + + protected ISelectionListener workbenchSelectionListener; + + protected Composite parent; + + protected GraphExplorer explorer; + + protected IMenuManager menuManager; + + private Map args; + + private ISessionContextProvider contextProvider; + + private ISessionContext sessionContext; + + private Object dragSource; + + protected IMemento memento; + + private IHintTracker sessionContextTracker = new SessionContextProjectTracker(); + + private SessionContextInputSource inputSource = new SessionContextProjectSource(); + + private DisposeState disposeState = DisposeState.Alive; + + protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() { + @Override + public void sessionContextChanged(SessionContextChangedEvent event) { + sessionContext = event.getNewValue(); + sessionContextTracker.track(sessionContext); + } + }; + + protected void setSessionContextTracker(IHintTracker tracker) { + this.sessionContextTracker = tracker; + } + + protected void setInputSource(SessionContextInputSource source) { + this.inputSource = source; + } + + protected SessionContextInputSource getInputSource() { + return inputSource; + } + + protected Map getViewArguments() { + return args; + } + + protected DisposeState getDisposeState() { + return disposeState; + } + + public ISessionContext getSessionContext() { + return sessionContext; + } + + public ISessionContextProvider getSessionContextProvider() { + return contextProvider; + } + + @Override + public void createPartControl(Composite parent) { + this.parent = parent; + this.resourceManager = new LocalResourceManager(JFaceResources.getResources(parent.getDisplay())); + + contextProvider = SimanticsUI.getSessionContextProvider(getViewSite().getWorkbenchWindow()); + createControls(parent); + attachToSession(); + } + + /** + * Invoked when this viewpart is disposed. Unhooks the view from its + * ISessionContextProvider. Overriding is allowed but super.dispose() must + * be called. + * + * @see org.eclipse.ui.part.WorkbenchPart#dispose() + */ + @Override + public void dispose() { + disposeState = DisposeState.Disposing; + try { + //System.out.println(this + ".GraphExplorerViewBase.dispose()"); + if (contextProvider != null) { + contextProvider.removeContextChangedListener(contextChangeListener); + contextProvider = null; + } + sessionContextTracker.untrack(); + resourceManager.dispose(); + resourceManager = null; + args = null; + explorer = null; + sessionContext = null; + dragSource = null; + parent = null; + super.dispose(); + } finally { + disposeState = DisposeState.Disposed; + } + } + + @Override + public void setFocus() { + if (explorer != null && !explorer.isDisposed()) + explorer.setFocus(); + } + + @Override + public void init(IViewSite site) throws PartInitException { + super.init(site); + this.args = ViewArgumentUtils.parseViewArguments(this); + } + + @Override + public void init(IViewSite site, IMemento memento) throws PartInitException { + super.init(site, memento); + this.args = ViewArgumentUtils.parseViewArguments(this); + this.memento = memento; + } + + @Override + public void saveState(IMemento memento) { + if (this.memento != null) { + memento.putMemento(this.memento); + } +// if (explorer != null) +// explorer.saveState(memento); + } + + protected void setWorkbenchListeners() { + if (workbenchSelectionListener == null) { + + ISelectionProvider selectionProvider = (ISelectionProvider)explorer.getAdapter(ISelectionProvider.class); + + getSite().setSelectionProvider(selectionProvider); + + // Listen to the workbench selection also to propagate it to + // the explorer also. + workbenchSelectionListener = new DefaultExplorerSelectionListener(this, explorer); + getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(workbenchSelectionListener); + } + } + + protected void removeWorkbenchListeners() { + // Remember to remove the installed workbench selection listener + if (workbenchSelectionListener != null) { + getSite().getWorkbenchWindow().getSelectionService().removePostSelectionListener(workbenchSelectionListener); + workbenchSelectionListener = null; + + getSite().setSelectionProvider(null); + } + } + + protected final void attachToSession() { + // Track active ISessionContext changes + //contextProvider = SimanticsUI.getSessionContextProvider(getViewSite().getWorkbenchWindow()); + contextProvider.addContextChangedListener(contextChangeListener); + + // Start tracking the current session context for input changes. + // This will/must cause applySessionContext to get called. + // Doing the applySessionContext initialization this way + // instead of directly calling it will also make sure that + // applySessionContext is only called once when first initialized, + // and not twice like with the direct invocation. + this.sessionContext = contextProvider.getSessionContext(); + sessionContextTracker.track(sessionContext); + } + + // ///////////////////////////////////////////////////////////////////////// + // Override / implement these: + +// /** +// * Returns an ID that is used for persisting a GraphExplorer instance. +// * +// * Used for restoreState(IMemento) and +// * restoreState(IMemento) in OntologyExplorer. Must be unique +// * within a workbench part. +// * +// * @return a unique name for this particular graph explorer view used for +// * saving and restoring the state of this view part +// */ +// public String getExplorerName() { +// return "GraphExplorerViewBase"; +// } + + protected Column[] getColumns() { + return null; + } + + /** + * Override this method to add controls to the view part. This is invoked + * before attaching the view part to a database session. + * + * @param parent + */ + protected void createControls(Composite parent) { + + parent.setLayout(LayoutUtils.createNoBorderGridLayout(1, false)); + + // Initialize explorer control. + explorer = createExplorerControl(parent); + + ISelectionProvider selectionProvider = (ISelectionProvider)explorer.getAdapter(ISelectionProvider.class); + Control control = explorer.getControl(); + + Column[] columns = getColumns(); + if(columns != null) + explorer.setColumns(columns); + + GridDataFactory.fillDefaults().grab(true, true).applyTo(control); + + // Initialize context menu if an initializer is provided. + IContextMenuInitializer cmi = getContextMenuInitializer(); + if (cmi != null) { + menuManager = cmi.createContextMenu(control, selectionProvider, getSite()); + } + + // Initialize DND. + dragSource = createDragSource(explorer); + + // Listeners are only added once per listener, not every time the + // session context changes. + addListeners(explorer, menuManager); + } + + /** + * Override this method and provide a proper context menu initializer if you + * want to have this base class initialize one for you. + * + * @return the initializer to be used by {@link #createControls(Composite)} + */ + protected IContextMenuInitializer getContextMenuInitializer() { + String contextMenuId = getContextMenuId(); + if(contextMenuId != null) { + return new ContextMenuInitializer(contextMenuId); + } else { + return null; + } + } + + /** + * @return the ID of the context menu to initialize for this this graph + * explorer view or null to not initialize a context + * menu + */ + protected String getContextMenuId() { + return null; + } + + protected int getStyle() { + return SWT.MULTI; + } + + /** + * @param parent + * @return + */ + protected GraphExplorer createExplorerControl(Composite parent) { + return GraphExplorerFactory.getInstance() + .selectionDataResolver(new DefaultSelectionDataResolver()) + .create(parent, getStyle()); + } + + /** + * 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) { + ISelectionProvider selectionProvider = (ISelectionProvider)explorer.getAdapter(ISelectionProvider.class); + Control control = explorer.getControl(); + return new BasicDragSource(selectionProvider, control, null); + } + + protected void setupDragSource(Session session) { + if (dragSource instanceof SessionContainer) { + ((SessionContainer) dragSource).setSession(session); + } + } + + /** + * Override to customize the addition of listeners a newly created + * GraphExplorer. + * + * @param explorer + */ + protected void addListeners(GraphExplorer explorer, IMenuManager menuManager) { + addSelectionInputListeners(explorer, menuManager); + } + + protected void addSelectionInputListeners(GraphExplorer explorer, IMenuManager menuManager) { + // Consider ENTER presses to simulate mouse left button double clicks + explorer.addListener(new DefaultKeyListener(contextProvider, explorer, new Function() { + @Override + public String[] execute(Object... obj) { + return new String[] { getEditingColumn((NodeContext) obj[0]) }; + } + })); + // Default double click handling + explorer.addListener(new DefaultMouseListener(explorer)); + } + + protected String getEditingColumn(NodeContext context) { + return ColumnKeys.SINGLE; + } + + /** + * Override to customize the initialization of the content provision and + * presentation of a GraphExplorer. This is called every time the input + * database session changes. + * + * @param explorer + * @param context may be null if there is no session + */ + protected void initializeExplorer(final GraphExplorer explorer, ISessionContext context) { + setupDragSource((context != null) ? context.getSession() : null); + } + + // Needed for preventing unnecessary re-initialization of the explorer with the same input. + private Object currentInput; + + protected boolean isImportantInput(Object previousInput, Object input) { + return !ObjectUtils.objectEquals(previousInput, input); + } + + /** + * Invoke this to reinitialize the explorer and reset its input. The input + * will be resolved from the specified ISessionContext based on the + * {@link SessionContextInputSource} that is currently in use. If the input + * is identical to the previous input, nothing will be done. + * + * @param context + */ + protected final boolean applySessionContext(ISessionContext context) { + // If control is not alive anymore, do nothing. +// System.out.println(this + ": applySessionContext(" + context + "), explorer=" + explorer); + if (disposeState != DisposeState.Alive) + return false; + + this.sessionContext = context; + Object input = inputSource.get(context); + if (!isImportantInput(currentInput, input)) + return false; + +// System.out.println(this + ": initializeExplorer(" + explorer + ", " + context + ")"); + initializeExplorer(explorer, context); +// System.out.println(this + ": setRoot(" + input + ")"); + explorer.setRoot(input); + + currentInput = input; + + // Start tracking the session context. + // + // If this is not the same session that is currently tracked, it will + // cause IHintListeners of the sessionContextTracker to fire. + // For this we need the above input equality (identity) checking. + // This is here just to make sure that we are tracking the correct + // session context. + sessionContextTracker.track(sessionContext); + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public Object getAdapter(Class adapter) { + + if (GraphExplorer.class == adapter) + return explorer; + else if(ISessionContextProvider.class == adapter) + return getSessionContextProvider(); + else if(IPropertyPage.class == adapter) + return getPropertyPage(); + + return super.getAdapter(adapter); + + } + + protected IPropertyPage getPropertyPage() { + return null; + } + + + +}