/******************************************************************************* * 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.views.swt; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.jface.action.IMenuManager; 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.dnd.DND; import org.eclipse.swt.dnd.DragSource; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; 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.IWorkbenchSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.part.ViewPart; import org.simantics.browsing.ui.BuiltinKeys; 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.node.IDropTargetNode; import org.simantics.browsing.ui.graph.impl.GraphInputSources; import org.simantics.browsing.ui.graph.impl.SessionContextInputSource; import org.simantics.browsing.ui.swt.ContextMenuInitializer; import org.simantics.browsing.ui.swt.DefaultKeyListener; import org.simantics.browsing.ui.swt.DefaultMouseListener; import org.simantics.browsing.ui.swt.DefaultSelectionDataResolver; import org.simantics.browsing.ui.swt.GraphExplorerFactory; import org.simantics.browsing.ui.swt.IContextMenuInitializer; import org.simantics.browsing.ui.swt.ViewArgumentUtils; import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupportImpl; import org.simantics.db.Disposable; 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.ProjectKeys; import org.simantics.ui.SimanticsUI; import org.simantics.ui.dnd.LocalObjectTransfer; import org.simantics.ui.dnd.LocalSelectionDragSourceListener; import org.simantics.ui.dnd.NoImageDragSourceEffect; 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.HintContext; import org.simantics.utils.datastructures.hints.HintListenerAdapter; import org.simantics.utils.datastructures.hints.HintTracker; import org.simantics.utils.datastructures.hints.IHintContext; 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; /** * @author Antti Villberg * @deprecated in favor of org.simantics.views.swt.ModelledView */ public abstract class SimanticsView extends ViewPart { private final WidgetSupportImpl widgetSupport = createSupport(); protected IHintContext factoryHints = new HintContext(); protected LocalResourceManager resourceManager; protected ISelectionListener workbenchSelectionListener; protected Composite parent; private Map args; private ISessionContextProvider contextProvider; private ISessionContext sessionContext; protected IMemento memento; private IHintTracker sessionContextTracker = new SessionContextProjectTracker(); private SessionContextInputSource inputSource = GraphInputSources.projectSource(); private DisposeState disposeState = DisposeState.Alive; protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() { @Override public void sessionContextChanged(SessionContextChangedEvent event) { sessionContext = event.getNewValue(); sessionContextTracker.track(sessionContext); } }; protected Set getBrowseContexts() { return Collections.emptySet(); } protected WidgetSupportImpl createSupport() { return new WidgetSupportImpl(); } abstract protected void createControls(Composite body, IWorkbenchSite site, ISessionContext context, WidgetSupport support); protected void activateUiContexts() { Collection contexts = getUiContexts(); if (!contexts.isEmpty()) { IContextService cs = (IContextService) getSite().getService(IContextService.class); for (String context : contexts) cs.activateContext(context); } } protected Transfer[] getAcceptedDataTypes() { return new Transfer[] { LocalObjectTransfer.getTransfer(), FileTransfer.getInstance() }; } protected void handleDrop(Object data, NodeContext target) { if (target != null) { Object input = target.getConstant(BuiltinKeys.INPUT); //System.out.println("DROPPED " + data + " ON " + target); if (input instanceof IDropTargetNode) ((IDropTargetNode) input).drop(data); } } protected Object createDragSource(GraphExplorer explorer) { ISelectionProvider selectionProvider = (ISelectionProvider) explorer.getAdapter(ISelectionProvider.class); DragSourceListener listener = new LocalSelectionDragSourceListener(selectionProvider); Control control = explorer.getControl(); DragSource source = new DragSource(control, DND.DROP_LINK | DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT); source.setTransfer(new Transfer[] {LocalObjectTransfer.getTransfer()}); source.addDragListener(listener); source.setDragSourceEffect(new NoImageDragSourceEffect(control)); return listener; } /** * @return the set of org.eclipse.ui.context contexts to * activate for this view site */ protected Set getUiContexts() { return Collections.emptySet(); } /** * The default hint tracker that will be active if * {@link SimanticsViewBase#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); } } protected void setSessionContextTracker(IHintTracker tracker) { this.sessionContextTracker = tracker; } public void setInputSource(SessionContextInputSource source) { this.inputSource = source; } protected final 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()); parent.setLayout(LayoutUtils.createNoBorderGridLayout(1, false)); setWorkbenchListeners(); activateUiContexts(); createControls(parent, getSite(), getSessionContext(), widgetSupport); 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() { removeWorkbenchListeners(); disposeState = DisposeState.Disposing; try { if (inputSource instanceof Disposable) { ((Disposable) inputSource).dispose(); } //System.out.println(this + ".GraphExplorerViewBase.dispose()"); if (contextProvider != null) { contextProvider.removeContextChangedListener(contextChangeListener); contextProvider = null; } sessionContextTracker.untrack(); resourceManager.dispose(); if (widgetSupport instanceof Disposable) ((Disposable) widgetSupport).dispose(); resourceManager = null; args = null; sessionContext = null; parent = null; super.dispose(); } finally { disposeState = DisposeState.Disposed; } } @Override public void 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: 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)); } /** * 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; } } 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 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; } // 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 + "(SimanticsView).applySessionContext(context=" + context + ")"); if (disposeState != DisposeState.Alive) return false; if(this.sessionContext == null && context == null) return false; this.sessionContext = context; Object input = getInputSource().get(context); //System.out.println(this + "(SimanticsView).applySessionContext(input=" + input + ")"); 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; //System.out.println(this + "(SimanticsView).applySessionContext(new input=" + currentInput + ")"); widgetSupport.fireInput(context, 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("unchecked") void searchControl(Control control, Class c, Set result) { if(c.isInstance(control)) result.add((T)control); if(control instanceof Composite) { for(Control child : ((Composite)control).getChildren()) { searchControl(child, c, result); } } } @SuppressWarnings("unchecked") @Override public T getAdapter(Class adapter) { if(ISessionContextProvider.class == adapter) return (T) getSessionContextProvider(); else if(IPropertyPage.class == adapter) return (T) getPropertyPage(); else if(GraphExplorer.class == adapter) { Set composites = new HashSet<>(); searchControl(parent, GraphExplorerComposite.class, composites); if(composites.size() == 1) { GraphExplorerComposite gec = composites.iterator().next(); return (T) gec.getExplorer(); } return null; } return super.getAdapter(adapter); } abstract protected IPropertyPage getPropertyPage(); // protected IPropertyPage getPropertyPage() { // return new StandardPropertyPage(getSite(), getBrowseContexts()); // } }