blob: 4e3a05fd953969f17ea96c8991eedf300f916d62 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2018 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.r.objectbrowser;
import static org.eclipse.statet.ecommons.ui.actions.UIActions.ADDITIONS_GROUP_ID;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler2;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.handlers.CollapseAllHandler;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.menus.UIElement;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.statet.ecommons.collections.FastList;
import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
import org.eclipse.statet.ecommons.ts.core.Tool;
import org.eclipse.statet.ecommons.ts.core.ToolRunnable;
import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
import org.eclipse.statet.ecommons.ui.actions.SearchContributionItem;
import org.eclipse.statet.ecommons.ui.components.StatusInfo;
import org.eclipse.statet.ecommons.ui.dialogs.DialogUtils;
import org.eclipse.statet.ecommons.ui.util.ColumnHoverManager;
import org.eclipse.statet.ecommons.ui.util.ColumnHoverStickyManager;
import org.eclipse.statet.ecommons.ui.util.ColumnWidgetTokenOwner;
import org.eclipse.statet.ecommons.ui.util.InformationDispatchHandler;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.PostSelectionProviderProxy;
import org.eclipse.statet.ecommons.ui.util.StatusLineMessageManager;
import org.eclipse.statet.ecommons.ui.util.TreeSelectionProxy;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ecommons.ui.workbench.WorkbenchUIUtils;
import org.eclipse.statet.base.ui.StatetImages;
import org.eclipse.statet.internal.r.ui.RUIPlugin;
import org.eclipse.statet.internal.r.ui.rtools.RunPrintInR;
import org.eclipse.statet.ltk.core.IElementName;
import org.eclipse.statet.ltk.ui.IElementNameProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorCommandIds;
import org.eclipse.statet.ltk.ui.sourceediting.assist.IInfoHover;
import org.eclipse.statet.ltk.ui.util.ViewerDragSupport;
import org.eclipse.statet.nico.core.runtime.ToolProcess;
import org.eclipse.statet.nico.core.util.IToolProvider;
import org.eclipse.statet.nico.core.util.IToolRetargetable;
import org.eclipse.statet.nico.ui.IToolRegistry;
import org.eclipse.statet.nico.ui.IToolRegistryListener;
import org.eclipse.statet.nico.ui.NicoUI;
import org.eclipse.statet.nico.ui.ToolSessionUIData;
import org.eclipse.statet.nico.ui.actions.ToolRetargetableHandler;
import org.eclipse.statet.r.console.core.AbstractRDataRunnable;
import org.eclipse.statet.r.console.core.IRDataAdapter;
import org.eclipse.statet.r.console.core.RConsoleTool;
import org.eclipse.statet.r.console.core.RProcess;
import org.eclipse.statet.r.console.core.RProcessREnvironment;
import org.eclipse.statet.r.console.core.RWorkspace;
import org.eclipse.statet.r.core.data.CombinedRElement;
import org.eclipse.statet.r.core.model.RElementComparator;
import org.eclipse.statet.r.core.model.RElementName;
import org.eclipse.statet.r.ui.RLabelProvider;
import org.eclipse.statet.r.ui.rtool.RElementInfoHoverCreator;
import org.eclipse.statet.r.ui.rtool.RElementInfoTask;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RReference;
public class ObjectBrowserView extends ViewPart implements IToolProvider {
private static final String FILTER_USERSPACEONLY_SETTINGS_KEY = "filter.only_userspace.enabled"; //$NON-NLS-1$
private static final String FILTER_INTERNALINCLUDE_SETTINGS_KEY = "filter.include_internal.enabled"; //$NON-NLS-1$
private static final String SORT_BYTYPE_SETTINGS_KEY = "sort.by_name.enabled"; //$NON-NLS-1$
static final RElementComparator ELEMENTNAME_COMPARATOR = new RElementComparator();
private static final ViewerComparator TYPE_COMPARATOR = new SortByTypeComparator();
private static final String OPEN_COMMAND_ID = "org.eclipse.jdt.ui.edit.text.java.open.editor"; //$NON-NLS-1$
private class RefreshWorkspaceR extends AbstractRDataRunnable {
public RefreshWorkspaceR() {
super("r/objectbrowser/refreshWorkspace.force", "Update Object Browser"); //$NON-NLS-1$
}
@Override
public boolean changed(final int event, final Tool tool) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
@Override
protected void run(final IRDataAdapter r,
final IProgressMonitor monitor) throws CoreException {
boolean current;
synchronized (ObjectBrowserView.this.processLock) {
current = (r.getTool() != getTool());
}
final IWorkbenchSiteProgressService progressService = getViewSite().getService(IWorkbenchSiteProgressService.class);
if (current && progressService != null) {
progressService.incrementBusy();
}
try {
ObjectBrowserView.this.fInputUpdater.forceOnWorkspaceChange();
r.refreshWorkspaceData(RWorkspace.REFRESH_COMPLETE, monitor);
}
finally {
if (current && progressService != null) {
progressService.decrementBusy();
}
}
}
}
private class RefreshHandler extends ToolRetargetableHandler {
private final ElementUpdater fUpdater = new ElementUpdater(IWorkbenchCommandConstants.FILE_REFRESH);
public RefreshHandler() {
super(ObjectBrowserView.this, getSite());
init();
}
@Override
protected Object doExecute(final ExecutionEvent event) {
scheduleUpdateAll();
return null;
}
@Override
protected void doRefresh() {
super.doRefresh();
this.fUpdater.schedule();
}
}
private class FilterUserspaceHandler extends AbstractHandler implements IElementUpdater {
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
ObjectBrowserView.this.fFilterUserspace = !ObjectBrowserView.this.fFilterUserspace;
ObjectBrowserView.this.fSettings.put(FILTER_USERSPACEONLY_SETTINGS_KEY, ObjectBrowserView.this.fFilterUserspace);
ObjectBrowserView.this.fInputUpdater.forceUpdate(ObjectBrowserView.this.process);
ObjectBrowserView.this.fInputUpdater.schedule();
return null;
}
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.fFilterUserspace);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
}
private class FilterInternalHandler extends AbstractHandler implements IElementUpdater {
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
ObjectBrowserView.this.fFilterIncludeInternal = !ObjectBrowserView.this.fFilterIncludeInternal;
ObjectBrowserView.this.fSettings.put(FILTER_INTERNALINCLUDE_SETTINGS_KEY, ObjectBrowserView.this.fFilterIncludeInternal);
updateFilter();
return null;
}
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.fFilterIncludeInternal);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
}
private class SortByTypeHandler extends AbstractHandler implements IElementUpdater {
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
ObjectBrowserView.this.fSortByType = !ObjectBrowserView.this.fSortByType;
ObjectBrowserView.this.fSettings.put(SORT_BYTYPE_SETTINGS_KEY, ObjectBrowserView.this.fSortByType);
updateSorter();
return null;
}
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.fSortByType);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
}
private class TreeElementSelection extends TreeSelectionProxy implements IElementNameProvider {
public TreeElementSelection(final ITreeSelection selection) {
super(selection);
}
@Override
public IElementName getElementName(final Object selectionElement) {
if (selectionElement instanceof TreePath) {
return getFQElementName((TreePath) selectionElement);
}
return null;
}
}
private class TreeSelectionProvider extends PostSelectionProviderProxy {
public TreeSelectionProvider() {
super(ObjectBrowserView.this.fTreeViewer);
}
@Override
protected ISelection getSelection(final ISelection originalSelection) {
return new TreeElementSelection((ITreeSelection) originalSelection);
}
}
private class HoverManager extends ColumnHoverManager {
HoverManager() {
super(ObjectBrowserView.this.fTreeViewer, ObjectBrowserView.this.fTokenOwner, new RElementInfoHoverCreator(IInfoHover.MODE_TOOLTIP));
final ColumnHoverStickyManager stickyManager = new ColumnHoverStickyManager(ObjectBrowserView.this.fTokenOwner, this);
getInternalAccessor().setInformationControlReplacer(stickyManager);
}
@Override
protected Object prepareHoverInformation(final ViewerCell cell) {
final TreePath treePath = cell.getViewerRow().getTreePath();
final RElementName elementName = getFQElementName(treePath);
if (elementName != null && elementName.getScope() != null) {
return elementName;
}
return null;
}
@Override
protected Object getHoverInformation(final Object element) {
if (element instanceof RElementName && ObjectBrowserView.this.process != null) {
final RElementInfoTask updater = new RElementInfoTask((RElementName) element);
return updater.load(getTool(), getSubjectControl(), ObjectBrowserView.this);
}
return null;
}
}
private TreeViewer fTreeViewer;
private TreeSelectionProvider fTreeSelectionProvider;
private ColumnWidgetTokenOwner fTokenOwner;
private Clipboard fClipboard;
private StatusLineMessageManager statusLine;
private IDialogSettings fSettings;
final Object processLock = new Object();
private RProcess process; // note: we write only in ui thread
private IToolRegistryListener fToolRegistryListener;
private final FastList<IToolRetargetable> fToolListenerList= new FastList<>(IToolRetargetable.class);
private final RefreshWorkspaceR fManualRefreshRunnable = new RefreshWorkspaceR();
private final ContentJob fInputUpdater = new ContentJob(this);
private ContentProvider contentProvider;
private boolean fFilterUserspace;
private boolean fFilterIncludeInternal;
private String fFilterText;
private boolean fSortByType;
private SearchContributionItem fSearchTextItem;
private boolean fFilterUserspaceActivated;
private CopyElementNameHandler fCopyElementNameHandler;
private DeleteHandler fDeleteElementHandler;
private PrintHandler fPrintElementHandler;
private IHandler2 fOpenInEditorHandler;
private Object fCurrentInfoObject;
private ColumnHoverManager fHoveringController;
private AbstractHandler fSearchStartHandler;
private HandlerContributionItem fRefreshToolbarItem;
private HandlerContributionItem fRefreshMenuItem;
private boolean fRefreshDirtyIndicator;
public ObjectBrowserView() {
}
@Override
public void init(final IViewSite site, final IMemento memento) throws PartInitException {
super.init(site, memento);
this.statusLine = new StatusLineMessageManager(site.getActionBars().getStatusLineManager());
this.fSettings = DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
this.fFilterUserspaceActivated = this.fFilterUserspace = this.fSettings.getBoolean(FILTER_USERSPACEONLY_SETTINGS_KEY);
this.fFilterIncludeInternal = this.fSettings.getBoolean(FILTER_INTERNALINCLUDE_SETTINGS_KEY);
this.fSortByType = this.fSettings.getBoolean(SORT_BYTYPE_SETTINGS_KEY);
}
@Override
public void saveState(final IMemento memento) {
super.saveState(memento);
this.fSettings = DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
}
@Override
public void createPartControl(final Composite parent) {
parent.setLayout(LayoutUtils.newSashGrid());
this.fTreeViewer = new TreeViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
this.fTreeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
this.fTreeViewer.setUseHashlookup(true);
this.fTreeSelectionProvider = new TreeSelectionProvider();
this.fTreeViewer.setLabelProvider(new RLabelProvider(RLabelProvider.COUNT) {
@Override
protected String getEnvCountInfo(final RProcessREnvironment envir) {
final StringBuilder textBuilder = getTextBuilder();
textBuilder.append(" ("); //$NON-NLS-1$
final ContentInput input = ObjectBrowserView.this.contentProvider.getInput();
if (input != null && input.hasEnvFilter()) {
final Object[] children = input.getEnvFilterChildren(envir);
if (children != null) {
textBuilder.append(children.length);
}
else {
textBuilder.append('-');
}
textBuilder.append('/');
}
textBuilder.append(envir.getLength());
textBuilder.append(')');
return textBuilder.toString();
}
});
this.fTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(final DoubleClickEvent event) {
final IStructuredSelection selection = (IStructuredSelection) event.getSelection();
if (selection.size() != 1) {
return;
}
final Object element = selection.getFirstElement();
if (element instanceof RObject) {
final RObject object = (RObject) element;
switch (object.getRObjectType()) {
case RObject.TYPE_ENVIRONMENT:
case RObject.TYPE_LIST:
case RObject.TYPE_DATAFRAME:
case RObject.TYPE_S4OBJECT:
case RObject.TYPE_REFERENCE:
ObjectBrowserView.this.fTreeViewer.setExpandedState(element, !ObjectBrowserView.this.fTreeViewer.getExpandedState(element));
}
}
}
});
this.fTreeSelectionProvider.addPostSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
updateSelectionInfo((ITreeSelection) event.getSelection());
}
});
this.contentProvider = new ContentProvider();
this.fTreeViewer.setContentProvider(this.contentProvider);
updateSorter();
this.fTreeViewer.setInput(this);
this.fTokenOwner = new ColumnWidgetTokenOwner(this.fTreeViewer);
this.fHoveringController = new HoverManager();
this.fHoveringController.setSizeConstraints(100, 12, false, true);
this.fHoveringController.install(this.fTreeViewer.getTree());
createActions();
hookContextMenu();
getSite().setSelectionProvider(this.fTreeSelectionProvider);
final ViewerDragSupport dragSupport = new ViewerDragSupport(this.fTreeViewer);
dragSupport.addDragSourceListener(new ViewerDragSupport.TextDragSourceListener(this.fTreeViewer));
dragSupport.init();
// listen on console changes
final IToolRegistry toolRegistry = NicoUI.getToolRegistry();
this.fToolRegistryListener = new IToolRegistryListener() {
@Override
public void toolSessionActivated(final ToolSessionUIData sessionData) {
final ToolProcess process = sessionData.getProcess();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
connect(process);
}
});
}
@Override
public void toolTerminated(final ToolSessionUIData sessionData) {
final ToolProcess process = sessionData.getProcess();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (process == getTool()) {
connect(null);
}
}
});
}
};
toolRegistry.addListener(this.fToolRegistryListener, getViewSite().getPage());
connect(toolRegistry.getActiveToolSession(getViewSite().getPage()).getProcess());
}
TreeViewer getViewer() {
return this.fTreeViewer;
}
private void createActions() {
final IHandlerService handlerService = getSite().getService(IHandlerService.class);
final IContextService contexts = getSite().getService(IContextService.class);
contexts.activateContext("org.eclipse.statet.workbench.contexts.StructuredElementViewer"); //$NON-NLS-1$
final RefreshHandler refreshHandler = new RefreshHandler();
handlerService.activateHandler(IWorkbenchCommandConstants.FILE_REFRESH, refreshHandler);
final CollapseAllHandler collapseAllHandler = new CollapseAllHandler(this.fTreeViewer);
handlerService.activateHandler(CollapseAllHandler.COMMAND_ID, collapseAllHandler);
this.fCopyElementNameHandler = new CopyElementNameHandler(this);
handlerService.activateHandler(ISourceEditorCommandIds.COPY_ELEMENT_NAME, this.fCopyElementNameHandler);
handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_COPY, this.fCopyElementNameHandler);
this.fDeleteElementHandler = new DeleteHandler(this);
handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_DELETE, this.fDeleteElementHandler);
this.fPrintElementHandler = new PrintHandler(this);
handlerService.activateHandler(RunPrintInR.COMMAND_ID, this.fPrintElementHandler);
final InformationDispatchHandler infoHandler = new InformationDispatchHandler(this.fTokenOwner);
handlerService.activateHandler(InformationDispatchHandler.COMMAND_ID, infoHandler);
this.fOpenInEditorHandler = new OpenInEditorHandler();
handlerService.activateHandler(OPEN_COMMAND_ID, this.fOpenInEditorHandler);
this.fSearchTextItem = new SearchContributionItem("search.text", //$NON-NLS-1$
SearchContributionItem.VIEW_TOOLBAR, true ) {
@Override
protected void search() {
ObjectBrowserView.this.fFilterText = getText();
updateFilter();
}
};
this.fSearchTextItem.setToolTip("Filter Elements");
this.fSearchTextItem.setSizeControl(this.fTreeViewer.getControl().getParent());
this.fSearchTextItem.setResultControl(this.fTreeViewer.getTree());
this.fSearchStartHandler = new AbstractHandler() {
@Override
public Object execute(final ExecutionEvent arg0) {
ObjectBrowserView.this.fSearchTextItem.show();
return null;
}
};
handlerService.activateHandler(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, this.fSearchStartHandler);
// add next/previous handler
final ToggleAutoRefreshHandler autoRefreshHandler = new ToggleAutoRefreshHandler(this);
final IActionBars actionBars = getViewSite().getActionBars();
final IMenuManager viewMenu = actionBars.getMenuManager();
final IToolBarManager viewToolbar = actionBars.getToolBarManager();
viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Filter.OnlyUserspace", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
null, null, null,
"Show non-&package Variables only", null, null,
HandlerContributionItem.STYLE_CHECK, null, false),
new FilterUserspaceHandler() ));
viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Filter.IncludeInternal", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
null, null, null,
"Show &Internal Variables ('.*')", null, null,
HandlerContributionItem.STYLE_CHECK, null, false),
new FilterInternalHandler() ));
viewMenu.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Sort.ByType", HandlerContributionItem.NO_COMMAND_ID, null, //$NON-NLS-1$
null, null, null,
"Sort by &Type", null, null,
HandlerContributionItem.STYLE_CHECK, null, false),
new SortByTypeHandler() ));
viewMenu.add(new Separator());
final HandlerContributionItem autoRefreshItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
null, HandlerContributionItem.NO_COMMAND_ID, null,
null, null, null,
"Refresh &automatically", null, null,
HandlerContributionItem.STYLE_CHECK, null, false),
autoRefreshHandler );
viewMenu.add(autoRefreshItem);
this.fRefreshMenuItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
null, IWorkbenchCommandConstants.FILE_REFRESH, null,
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
"&Refresh", null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
refreshHandler );
viewMenu.add(this.fRefreshMenuItem);
viewMenu.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
autoRefreshItem.update();
}
});
viewToolbar.add(this.fSearchTextItem);
viewToolbar.add(new Separator());
this.fRefreshToolbarItem = new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Refresh", IWorkbenchCommandConstants.FILE_REFRESH, null, //$NON-NLS-1$
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
refreshHandler);
this.fRefreshToolbarItem.setVisible(false);
viewToolbar.add(this.fRefreshToolbarItem);
viewToolbar.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Collapse.All", CollapseAllHandler.COMMAND_ID, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
collapseAllHandler));
}
void updateAutoRefresh(final boolean enabled) {
if (this.process == null || this.process.isTerminated()) {
return;
}
if (enabled) {
updateDirty(false);
}
else {
updateDirty(this.process.getWorkspaceData().isRObjectDBDirty());
}
if (this.fRefreshToolbarItem.isVisible() != enabled) {
return;
}
this.fRefreshToolbarItem.setVisible(!enabled);
final IContributionManager manager = this.fRefreshToolbarItem.getParent();
manager.update(true);
this.fSearchTextItem.resize();
}
void updateDirty(final boolean enabled) {
if (this.fRefreshDirtyIndicator == enabled) {
return;
}
final ImageDescriptor icon = (enabled) ?
RUIPlugin.getInstance().getImageRegistry().getDescriptor(RUIPlugin.IMG_LOCTOOL_REFRESH_RECOMMENDED) :
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH);
this.fRefreshToolbarItem.setIcon(icon);
this.fRefreshMenuItem.setIcon(icon);
this.fRefreshDirtyIndicator = enabled;
}
private void hookContextMenu() {
final MenuManager menuManager = new MenuManager("ContextMenu", //$NON-NLS-1$
"org.eclipse.statet.r.menus.RObjectBrowserContextMenu" ); //$NON-NLS-1$
menuManager.setRemoveAllWhenShown(true);
menuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager m) {
contextMenuAboutToShow(m);
}
});
final Menu contextMenu = menuManager.createContextMenu(this.fTreeViewer.getTree());
this.fTreeViewer.getTree().setMenu(contextMenu);
getSite().registerContextMenu(menuManager, this.fTreeViewer);
}
private void contextMenuAboutToShow(final IMenuManager m) {
m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
null, OPEN_COMMAND_ID, null,
null, null, null,
"Open in Data &Viewer", null, null,
HandlerContributionItem.STYLE_PUSH, null, true), this.fOpenInEditorHandler));
m.add(new Separator("edit"));
m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Copy.ElementName", ISourceEditorCommandIds.COPY_ELEMENT_NAME, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
this.fCopyElementNameHandler));
m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
"Delete", IWorkbenchCommandConstants.EDIT_DELETE, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
this.fDeleteElementHandler));
m.add(new Separator());
m.add(new HandlerContributionItem(new CommandContributionItemParameter(getSite(),
null, RunPrintInR.COMMAND_ID, null,
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false),
this.fPrintElementHandler));
m.add(new Separator(ADDITIONS_GROUP_ID));
}
/** May only be called in UI thread */
public void connect(final ToolProcess tool) {
if (this.process == tool) {
return;
}
final RProcess oldProcess = this.process;
if (oldProcess != null) {
oldProcess.getWorkspaceData().removePropertyListener(this.fInputUpdater);
}
synchronized (this.processLock) {
this.process = (RProcess)
((tool != null && tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID)) ?
tool : null);
}
if (this.fHoveringController != null) {
this.fHoveringController.stop();
}
if (oldProcess != null) {
clearInfo();
oldProcess.getQueue().remove(new ToolRunnable[] { this.fManualRefreshRunnable });
}
if (!UIAccess.isOkToUse(this.fTreeViewer)) {
return;
}
for (final IToolRetargetable listener : this.fToolListenerList.toArray()) {
listener.setTool(this.process);
}
this.fInputUpdater.forceUpdate(this.process);
if (this.process != null) {
this.process.getWorkspaceData().addPropertyListener(this.fInputUpdater);
updateAutoRefresh(this.process.getWorkspaceData().isAutoRefreshEnabled());
}
this.fInputUpdater.schedule();
}
@Override
public RProcess getTool() {
return this.process;
}
public ITreeSelection getSelection() {
return (ITreeSelection) this.fTreeViewer.getSelection();
}
public boolean getShowInternal() {
return this.fFilterIncludeInternal;
}
public boolean getShowConsenseUserspace() {
return this.fFilterUserspace;
}
public String getSearchText() {
return this.fFilterText;
}
@Override
public void addToolRetargetable(final IToolRetargetable action) {
this.fToolListenerList.add(action);
}
@Override
public void removeToolRetargetable(final IToolRetargetable action) {
this.fToolListenerList.remove(action);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter(final Class<T> adapterType) {
if (adapterType == Tool.class) {
return (T) this.process;
}
return super.getAdapter(adapterType);
}
private void scheduleUpdateAll() {
if (this.process != null) {
this.process.getQueue().add(this.fManualRefreshRunnable);
}
}
/** May only be called in UI thread (called by update job) */
void updateViewer(final List<RProcessREnvironment> updateEnvirs, final ContentInput input) {
if (!UIAccess.isOkToUse(this.fTreeViewer)) {
return;
}
this.fHoveringController.stop();
this.contentProvider.setInput(input);
final boolean changed = input.processChanged;
ISelection selection = null;
if (changed) {
input.processChanged = false;
}
/*else*/ if (input.showCondensedUserspace != this.fFilterUserspaceActivated) {
// If filter is changed, we have to retain the selection manually
selection = this.fTreeViewer.getSelection();
}
this.fFilterUserspaceActivated = input.showCondensedUserspace;
final Set<RReference> previousReferences = this.contentProvider.resetUsedReferences();
if (updateEnvirs != null) {
for (final RProcessREnvironment entry : updateEnvirs) {
this.fTreeViewer.refresh(entry, true);
}
if (!previousReferences.isEmpty()) {
final Set<RReference> usedReferences = this.contentProvider.getUsedReferences();
ITER_REFS: for (final RReference reference : previousReferences) {
if (!usedReferences.contains(reference)) {
// Update the envir copy in the viewer, if it refers to an updated envir
for (final RProcessREnvironment entry : updateEnvirs) {
if (entry.getHandle() == reference.getHandle()) {
this.fTreeViewer.refresh(reference, true);
// reference is readded automatically to new set, if necessary
continue ITER_REFS;
}
}
// Keep/readd the reference, if it refers to an envir in the search path
for (final RProcessREnvironment entry : input.searchEnvirs) {
if (entry.getHandle() == reference.getHandle()) {
usedReferences.add(reference);
continue ITER_REFS;
}
}
}
}
}
}
else {
this.fTreeViewer.refresh(true);
}
// Adapt and set selection
if (selection != null && !selection.isEmpty()) {
final ITreeSelection s = (ITreeSelection) selection;
final TreePath[] paths = s.getPaths();
int j = 0;
for (int i = 0; i < paths.length; i++) {
final TreePath oldPath = paths[i];
final int count = oldPath.getSegmentCount();
final int shift = (input.showCondensedUserspace) ? -1 : +1;
if (count + shift < 1) {
continue;
}
final Object[] newPath = new Object[count + shift];
for (int k = (shift == -1) ? +1 : 0; k < count; k++) {
newPath[k + shift] = oldPath.getSegment(k);
}
if (shift == +1) {
newPath[0] = ContentProvider.getCombinedRElement(newPath[1]).getModelParent();
}
paths[j++] = new TreePath(newPath);
}
this.fTreeViewer.setSelection(new TreeSelection(
(j < paths.length) ? Arrays.copyOf(paths, j) : paths),
true );
}
// Expand Global_Env, if it is a new process and has valid input
EXPAND_GLOBALENV : if (changed && !input.showCondensedUserspace
&& input.rootElements != null && input.rootElements.length > 0) {
for (final Object element : input.rootElements) {
if (this.fTreeViewer.getExpandedState(element)) {
break EXPAND_GLOBALENV;
}
}
this.fTreeViewer.expandToLevel(new TreePath(new Object[] { input.rootElements[0] }), 1);
}
}
private void updateFilter() {
this.fInputUpdater.schedule();
}
private void updateSorter() {
this.fTreeViewer.setComparator(this.fSortByType ? TYPE_COMPARATOR : null);
}
private void updateSelectionInfo(final ITreeSelection selection) {
final Object previousInfoObject = this.fCurrentInfoObject;
this.fCurrentInfoObject = null;
final RProcess tool = getTool();
if (tool == null) {
return;
}
if (selection.size() == 1) {
this.fCurrentInfoObject = selection.getFirstElement();
final TreePath treePath = selection.getPaths()[0];
final IElementName elementName = getFQElementName(treePath);
final String name = (elementName != null) ? elementName.getDisplayName() : null;
if (name != null) {
if (this.fCurrentInfoObject.equals(previousInfoObject)) {
clearInfo();
}
this.statusLine.setSelectionMessage(new StatusInfo(IStatus.OK,
NLS.bind("{0} \u2012 {1}", name, tool.getLabel(Tool.DEFAULT_LABEL)) )); //$NON-NLS-1$
return;
}
}
clearInfo();
if (selection.size() > 1) {
this.statusLine.setSelectionMessage(new StatusInfo(IStatus.OK,
NLS.bind("{0} items selected", selection.size()) ));
return;
}
this.statusLine.setSelectionMessage(null);
}
private void clearInfo() {
}
/**
* Returns a shared clipboard resource, which can be used by actions of this view.
*
* @return a clipboard object.
*/
Clipboard getClipboard() {
if (this.fClipboard == null) {
this.fClipboard = new Clipboard(Display.getCurrent());
}
return this.fClipboard;
}
StatusLineMessageManager getStatusLine() {
return this.statusLine;
}
@Override
public void setFocus() {
this.fTreeViewer.getControl().setFocus();
}
@Override
public void dispose() {
if (this.fToolRegistryListener != null) {
NicoUI.getToolRegistry().removeListener(this.fToolRegistryListener);
this.fToolRegistryListener = null;
}
if (this.process != null) {
this.process.getWorkspaceData().removePropertyListener(this.fInputUpdater);
this.process.getQueue().remove(new ToolRunnable[] { this.fManualRefreshRunnable });
}
this.fInputUpdater.cancel();
if (this.fHoveringController != null) {
this.fHoveringController.dispose();
this.fHoveringController = null;
}
if (this.fSearchStartHandler != null) {
this.fSearchStartHandler.dispose();
this.fSearchStartHandler = null;
}
if (this.fCopyElementNameHandler != null) {
this.fCopyElementNameHandler.dispose();
this.fCopyElementNameHandler = null;
}
if (this.fPrintElementHandler != null) {
this.fPrintElementHandler.dispose();
this.fPrintElementHandler = null;
}
for (final IToolRetargetable listener : this.fToolListenerList.toArray()) {
listener.setTool(null);
}
super.dispose();
if (this.fClipboard != null) {
this.fClipboard.dispose();
this.fClipboard = null;
}
}
public RElementName getFQElementName(final TreePath treePath) {
if (treePath.getSegmentCount() == 0) {
return null;
}
int segmentIdx = 0;
if (!this.fFilterUserspaceActivated) {
if (treePath.getSegmentCount() == 1) { // search path item
return ContentProvider.getCombinedRElement(treePath.getFirstSegment()).getElementName();
}
else { // main name at 1
segmentIdx = 1;
}
}
final List<RElementName> names= new ArrayList<>(treePath.getSegmentCount() - segmentIdx + 1);
final CombinedRElement first= ContentProvider.getCombinedRElement(treePath.getSegment(segmentIdx++));
names.add(first.getModelParent().getElementName());
names.add(first.getElementName());
while (segmentIdx < treePath.getSegmentCount()) {
final Object object= treePath.getSegment(segmentIdx++);
if (object instanceof ElementPartition) {
continue;
}
final CombinedRElement rElement= ContentProvider.getCombinedRElement(object);
names.add(rElement.getElementName());
}
return RElementName.create(names);
}
}