/*******************************************************************************
* 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.platform;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.part.IPageBookViewPage;
import org.eclipse.ui.part.Page;
import org.simantics.browsing.ui.GraphExplorer;
import org.simantics.browsing.ui.swt.IVariablesPage;
import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite;
import org.simantics.browsing.ui.swt.widgets.GraphExplorerComposite.InputSource;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
import org.simantics.db.common.ResourceArray;
import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.AdaptionException;
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.db.procedure.Listener;
import org.simantics.db.request.Read;
import org.simantics.ui.utils.ResourceAdaptionUtils;
import org.simantics.utils.ui.AdaptionUtils;
import org.simantics.utils.ui.ErrorLogger;
/**
*
* Subclasses may extend or reimplement the following methods as required:
*
* createPageControls
- to create the page's controls
* getControl
- to retrieve the page's control
* setFocus
- implement to accept focus
* sourceSelectionChanged
- puts the incoming ISelection into use in this page
* sourcePartClosed
- cleans up the page controls after a current selection source part has been closed
* dispose
- extend to provide additional cleanup
* setActionBars
- reimplement to make contributions
* makeContributions
- this method exists to support previous versions
* setActionBars
- this method exists to support previous versions
* init
- extend to provide additional setup
* sessionContextChanged
- reimplement to take actions when the source database session changes
*
*
*
* @author Tuukka Lehtonen
*/
public class VariablesPage extends Page implements IPageBookViewPage, IVariablesPage {
protected static final int MAX_SELECTION_LENGTH_TO_SHOW = 5;
protected ISessionContext sessionContext;
protected GraphExplorerComposite explorer;
/**
* @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 VariablesPage(IWorkbenchPartSite site) {
this.site = site;
}
/**
* @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 adapter must provide an adapter for
* ISessionContextProvider.class
*/
public VariablesPage(IWorkbenchPartSite site, IAdaptable adapter) {
this(site);
setAdapter(adapter);
}
@Override
public void dispose() {
// Stop listening for title changes.
if (currentPartNameListener != null)
currentPartNameListener.dispose();
if (adapter != null) {
ISessionContextProvider contextProvider = getSessionContextProvider();
contextProvider.removeContextChangedListener(contextChangeListener);
}
if (sourcePart != null) {
sourcePart.getSite().getPage().removePartListener(partListener);
sourcePart = null;
}
site = null;
adapter = null;
explorer = null;
sessionContext = null;
}
protected ISessionContextProvider getSessionContextProvider() {
return (ISessionContextProvider) getAdapter().getAdapter(ISessionContextProvider.class);
}
protected ISessionContext getSessionContext() {
return sessionContext;
}
@Override
public final void createControl(Composite parent) {
createPageControls(parent);
// Attach to current session context provider to keep the UI intact even
// when the current UI session changes.
ISessionContextProvider contextProvider = getSessionContextProvider();
contextProvider.addContextChangedListener(contextChangeListener);
setSessionContext(contextProvider.getSessionContext());
}
/**
* Override to customize the UI component created on this page.
*
* @param parent
*/
protected void createPageControls(Composite parent) {
Map args = new HashMap();
Set browseContexts = new HashSet();
browseContexts.add("org.simantics.browsing.ui.graph.variablesView");
args.put("browseContexts", browseContexts);
explorer = new GraphExplorerComposite(args, site, parent, SWT.NONE);
explorer.setInputSource(new InputSource() {
@Override
public Object get(ISessionContext ctx, Object selection) {
final Resource input = AdaptionUtils.adaptToSingle(selection, Resource.class);
if(input == null) {
return GraphExplorer.EMPTY_INPUT;
}
final VariablePrefix prefix = AdaptionUtils.adaptToSingle(selection, VariablePrefix.class);
if(prefix == null) {
return new VariablesInput(null, input);
} else {
return new VariablesInput(prefix.getPrefix(), input);
}
}
});
}
/**
* @param newContext
*/
protected final void setSessionContext(ISessionContext newContext) {
ISessionContext oldContext = this.sessionContext;
this.sessionContext = newContext;
// System.out.println("AbstractPropertyPage.setSessionContext: " + oldContext + " -> " + newContext);
sessionContextChanged(oldContext, newContext);
}
/**
* @param oldContext
* @param newContext
*/
protected void sessionContextChanged(ISessionContext oldContext, ISessionContext newContext) {
explorer.applySessionContext(newContext);
// explorer.setSessionContext(newContext);
}
protected ISessionContextChangedListener contextChangeListener = new ISessionContextChangedListener() {
@Override
public void sessionContextChanged(SessionContextChangedEvent event) {
setSessionContext(event.getNewValue());
}
};
@Override
public Control getControl() {
return (explorer != null) ? explorer : null;
}
@Override
public ISelection getSelection() {
// if (!explorer.isDisposed()) {
// return explorer.getSelection();
// }
return null;
}
/**
* Sets focus to a part in the page.
* @see org.eclipse.ui.part.Page#setFocus()
*/
@Override
public void setFocus() {
// if (explorer != null && !explorer.isDisposed()) {
// explorer.requestFocus();
// }
}
protected void sourcePartClosed(IWorkbenchPart part) {
// if (!explorer.isDisposed()) {
// explorer.setInput(StructuredSelection.EMPTY, false);
// }
}
protected void sourceSelectionChanged(ISelection selection) {
if (!explorer.isDisposed()) {
explorer.setInput(selection, false);
}
}
static class PartNameListener implements Listener {
private boolean disposed = false;
private final Consumer updateCallback;
public PartNameListener(Consumer updateCallback) {
assert updateCallback != null;
this.updateCallback = updateCallback;
}
public void dispose() {
disposed = true;
}
@Override
public boolean isDisposed() {
return disposed;
}
@Override
public void execute(String result) {
//System.out.println("part name changed: " + result);
updateCallback.accept(result);
}
@Override
public void exception(Throwable t) {
ErrorLogger.defaultLogError(t);
}
}
PartNameListener currentPartNameListener = null;
@Override
public void updatePartName(final ISelection forSelection, Consumer updateCallback) {
PartNameListener oldListener = currentPartNameListener;
PartNameListener newListener = new PartNameListener(updateCallback);
if (oldListener != null)
oldListener.dispose();
if (sessionContext != null) {
sessionContext.getSession().asyncRequest(new Read() {
@Override
public String perform(ReadGraph graph) throws DatabaseException {
return computeTitle(graph, forSelection);
}
}, newListener);
}
}
protected static String safeGetName(ReadGraph g, Resource r) throws DatabaseException {
try {
return g.adapt(r, String.class);
} catch (AdaptionException e) {
return NameUtils.getSafeName(g, r);
}
}
protected String computeTitle(ReadGraph graph, ISelection selection) throws DatabaseException {
boolean sameTypes = true;
try {
final ResourceArray[] ras = ResourceAdaptionUtils.toResourceArrays(selection);
if (ras.length == 0)
return null;
// Check if all the input resource are of the same type.
Collection types = null;
for (ResourceArray ra : ras) {
if (ra.isEmpty()) {
return null;
}
if (types == null) {
types = graph.getPrincipalTypes(ra.resources[0]);
} else {
Collection ts = graph.getPrincipalTypes(ra.resources[0]);
ts.removeAll(types);
if (!ts.isEmpty()) {
sameTypes = false;
break;
}
}
}
if (sameTypes) {
// If the resource no longer exists, provide default name only.
if (!graph.hasStatement(ras[0].resources[0])) {
return null;
}
String name = safeGetName(graph, ras[0].resources[0]);
if (ras.length > 1)
name += " [" + ras.length +"]";
return name;
} else {
Collection names = new ArrayList(ras.length);
boolean truncate = ras.length > MAX_SELECTION_LENGTH_TO_SHOW;
int end = Math.min(ras.length, MAX_SELECTION_LENGTH_TO_SHOW);
int missing = ras.length - end;
for (int i = 0; i < end; ++i) {
// If the resource no longer exists, provide default name only.
if (!graph.hasStatement(ras[i].resources[0]))
continue;
names.add(safeGetName(graph, ras[i].resources[0]));
}
if (names.isEmpty()) {
return null;
}
if (truncate)
names.add("+ " + missing + " more...");
String name = names.toString();
return name;
}
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
/**
* Part listener which cleans up this page when the source part is closed.
* This is hooked only when there is a source part.
*/
private class PartListener implements IPartListener {
public void partActivated(IWorkbenchPart part) {
}
public void partBroughtToTop(IWorkbenchPart part) {
}
public void partClosed(IWorkbenchPart part) {
if (sourcePart == part) {
sourcePart = null;
sourcePartClosed(part);
}
}
public void partDeactivated(IWorkbenchPart part) {
}
public void partOpened(IWorkbenchPart part) {
}
}
private PartListener partListener = new PartListener();
protected IWorkbenchPartSite site;
private IWorkbenchPart sourcePart;
protected IAdaptable adapter;
@Override
public void setAdapter(IAdaptable adapter) {
assert adapter != null;
this.adapter = adapter;
}
public IAdaptable getAdapter() {
assert adapter != null;
return adapter;
}
@Override
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (getControl() == null) {
return;
}
if (sourcePart != null) {
sourcePart.getSite().getPage().removePartListener(partListener);
sourcePart = null;
}
// change the viewer input since the workbench selection has changed.
sourceSelectionChanged(selection);
sourcePart = part;
if (sourcePart != null) {
sourcePart.getSite().getPage().addPartListener(partListener);
}
}
}