blob: 389323561b5e0da057568bc8cefaa2fe9cc3456b [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 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.ui.IWorkbenchCommandConstants.NAVIGATE_COLLAPSE_ALL;
import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert;
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.Objects;
import java.util.Set;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
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.ILabelProvider;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
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.Control;
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.ui.services.IServiceLocator;
import org.eclipse.statet.jcommons.collections.CopyOnWriteIdentityListSet;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener;
import org.eclipse.statet.jcommons.ts.core.ActiveToolListener.ActiveToolEvent;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolProvider;
import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolRegistry;
import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolRegistryListener;
import org.eclipse.statet.ecommons.ts.ui.workbench.WorkbenchToolSessionData;
import org.eclipse.statet.ecommons.ui.actions.HandlerCollection;
import org.eclipse.statet.ecommons.ui.actions.HandlerContributionItem;
import org.eclipse.statet.ecommons.ui.actions.SearchContributionItem;
import org.eclipse.statet.ecommons.ui.actions.UIActions;
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.TreeSelectionProxy;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ecommons.ui.util.ViewActionUtil;
import org.eclipse.statet.ecommons.ui.workbench.ContextHandlers;
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.ElementName;
import org.eclipse.statet.ltk.ui.ElementNameProvider;
import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditorCommandIds;
import org.eclipse.statet.ltk.ui.sourceediting.assist.InfoHover;
import org.eclipse.statet.ltk.ui.util.ViewerDragSupport;
import org.eclipse.statet.nico.ui.NicoUI;
import org.eclipse.statet.nico.ui.actions.AbstractToolHandler;
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.rtool.CopyRElementHandler;
import org.eclipse.statet.r.ui.rtool.PrintRElementHandler;
import org.eclipse.statet.r.ui.rtool.RElementInfoHoverCreator;
import org.eclipse.statet.r.ui.rtool.RElementInfoTask;
import org.eclipse.statet.r.ui.rtool.RElementViewerDragSourceListener;
import org.eclipse.statet.r.ui.util.CopyRElementNameHandler;
import org.eclipse.statet.r.ui.util.RElementInputContentProvider;
import org.eclipse.statet.r.ui.util.RElementInputLabelProvider;
import org.eclipse.statet.r.ui.util.RElementInputUtils;
import org.eclipse.statet.rj.data.RReference;
@NonNullByDefault
public class ObjectBrowserView extends ViewPart implements ToolProvider {
private static final String REFRESH_COMMAND_ID= IWorkbenchCommandConstants.FILE_REFRESH;
private static final String OPEN_COMMAND_ID= "org.eclipse.jdt.ui.edit.text.java.open.editor"; //$NON-NLS-1$
private static final String PRINT_COMMAND_ID= RunPrintInR.COMMAND_ID;
private static final String FILTER_ONLY_USERSPACE_COMMAND_ID= "Filter.OnlyUserspace";
private static final String FILTER_INCLUDE_INTERNAL_COMMAND_ID= "Filter.IncludeInternal";
private static final String FILTER_ONLY_USERSPACE_SETTINGS_KEY= "Filter.OnlyUserspace.enabled"; //$NON-NLS-1$
private static final String FILTER_INCLUDE_INTERNAL_SETTINGS_KEY= "Filter.IncludeInternal.enabled"; //$NON-NLS-1$
private static final String SORT_BY_TYPE_SETTINGS_KEY= "Sort.ByType.enabled"; //$NON-NLS-1$
static final RElementComparator ELEMENTNAME_COMPARATOR= new RElementComparator();
private static final ViewerComparator TYPE_COMPARATOR= new SortByTypeComparator();
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 ProgressMonitor m) throws StatusException {
boolean current;
synchronized (ObjectBrowserView.this.sourceLock) {
current= (r.getTool() != getTool());
}
final IWorkbenchSiteProgressService progressService= getViewSite().getService(IWorkbenchSiteProgressService.class);
if (current && progressService != null) {
progressService.incrementBusy();
}
try {
ObjectBrowserView.this.inputUpdater.forceOnWorkspaceChange();
r.refreshWorkspaceData(RWorkspace.REFRESH_COMPLETE, m);
}
finally {
if (current && progressService != null) {
progressService.decrementBusy();
}
}
}
}
private class RefreshHandler extends AbstractToolHandler<RProcess> {
public RefreshHandler() {
super(RConsoleTool.TYPE, null, ObjectBrowserView.this, getSite());
init();
}
protected void refreshElements() {
WorkbenchUIUtils.refreshCommandElements(REFRESH_COMMAND_ID, this, null);
}
@Override
protected @Nullable Object execute(final RProcess tool, final ExecutionEvent event) {
scheduleUpdateAll();
return null;
}
}
private class FilterUserspaceHandler extends AbstractHandler implements IElementUpdater {
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.filterUserspace);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
@Override
public @Nullable Object execute(final ExecutionEvent event) {
ObjectBrowserView.this.filterUserspace= !ObjectBrowserView.this.filterUserspace;
ObjectBrowserView.this.settings.put(FILTER_ONLY_USERSPACE_SETTINGS_KEY, ObjectBrowserView.this.filterUserspace);
ObjectBrowserView.this.inputUpdater.forceUpdate(ObjectBrowserView.this.process);
ObjectBrowserView.this.inputUpdater.schedule();
return null;
}
}
private class FilterInternalHandler extends AbstractHandler implements IElementUpdater {
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.filterIncludeInternal);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
@Override
public @Nullable Object execute(final ExecutionEvent event) {
ObjectBrowserView.this.filterIncludeInternal= !ObjectBrowserView.this.filterIncludeInternal;
ObjectBrowserView.this.settings.put(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY, ObjectBrowserView.this.filterIncludeInternal);
updateFilter();
return null;
}
}
private class SortByTypeHandler extends AbstractHandler implements IElementUpdater {
@Override
public void updateElement(final UIElement element, final Map parameters) {
WorkbenchUIUtils.aboutToUpdateCommandsElements(this, element);
try {
element.setChecked(ObjectBrowserView.this.sortByType);
}
finally {
WorkbenchUIUtils.finalizeUpdateCommandsElements(this);
}
}
@Override
public @Nullable Object execute(final ExecutionEvent event) {
ObjectBrowserView.this.sortByType= !ObjectBrowserView.this.sortByType;
ObjectBrowserView.this.settings.put(SORT_BY_TYPE_SETTINGS_KEY, ObjectBrowserView.this.sortByType);
updateSorter();
return null;
}
}
private class TreeElementSelection extends TreeSelectionProxy implements ElementNameProvider {
public TreeElementSelection(final ITreeSelection selection) {
super(selection);
}
@Override
public @Nullable ElementName getElementName(final Object selectionElement) {
if (selectionElement instanceof TreePath) {
return getFQElementName((TreePath) selectionElement);
}
return null;
}
}
private class TreeSelectionProvider extends PostSelectionProviderProxy {
public TreeSelectionProvider() {
super(ObjectBrowserView.this.treeViewer);
}
@Override
protected ISelection getSelection(final ISelection originalSelection) {
return new TreeElementSelection((ITreeSelection) originalSelection);
}
}
private class HoverManager extends ColumnHoverManager {
HoverManager() {
super(ObjectBrowserView.this.treeViewer, ObjectBrowserView.this.tokenOwner, new RElementInfoHoverCreator(InfoHover.MODE_TOOLTIP));
final ColumnHoverStickyManager stickyManager= new ColumnHoverStickyManager(ObjectBrowserView.this.tokenOwner, this);
getInternalAccessor().setInformationControlReplacer(stickyManager);
}
@Override
protected @Nullable 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 @Nullable 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 IDialogSettings settings;
final Object sourceLock= new Object();
private WorkbenchToolRegistryListener toolRegistryListener;
private @Nullable RProcess process; // note: we write only in ui thread
private final CopyOnWriteIdentityListSet<ActiveToolListener> toolListeners= new CopyOnWriteIdentityListSet<>();
private TreeViewer treeViewer;
private ColumnWidgetTokenOwner tokenOwner;
private Clipboard clipboard;
private final ContentJob inputUpdater= new ContentJob(this);
private boolean isUpdating;
private final RefreshWorkspaceR manualRefreshRunnable= new RefreshWorkspaceR();
private boolean filterUserspace;
private boolean filterIncludeInternal;
private String filterText;
private boolean sortByType;
private SearchContributionItem searchTextItem;
private RElementInputContentProvider<ContentInput> inputContentProvider;
private boolean filterUserspaceActivated;
private ViewActionUtil actionUtil;
private ContextHandlers handlers;
private @Nullable Object currentInfoObject;
private ColumnHoverManager hoveringController;
private HandlerContributionItem refreshToolbarItem;
private HandlerContributionItem refreshMenuItem;
private boolean refreshDirtyIndicator;
public ObjectBrowserView() {
}
@Override
public void dispose() {
if (this.toolRegistryListener != null) {
NicoUI.getToolRegistry().removeListener(this.toolRegistryListener);
this.toolRegistryListener= null;
}
setTool(null, false);
this.inputUpdater.cancel();
if (this.hoveringController != null) {
this.hoveringController.dispose();
this.hoveringController= null;
}
if (this.handlers != null) {
this.handlers.dispose();
this.handlers= null;
}
super.dispose();
if (this.clipboard != null) {
this.clipboard.dispose();
this.clipboard= null;
}
}
@Override
public void init(final IViewSite site, final IMemento memento) throws PartInitException {
super.init(site, memento);
this.settings= DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
this.filterUserspaceActivated= this.filterUserspace= this.settings.getBoolean(FILTER_ONLY_USERSPACE_SETTINGS_KEY);
this.filterIncludeInternal= this.settings.getBoolean(FILTER_INCLUDE_INTERNAL_SETTINGS_KEY);
this.sortByType= this.settings.getBoolean(SORT_BY_TYPE_SETTINGS_KEY);
}
@Override
public void saveState(final IMemento memento) {
super.saveState(memento);
this.settings= DialogUtils.getDialogSettings(RUIPlugin.getInstance(), "ObjectBrowser");
}
@Override
public void createPartControl(final Composite parent) {
parent.setLayout(LayoutUtils.newSashGrid());
this.treeViewer= new TreeViewer(parent, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
this.treeViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final IPostSelectionProvider treeSelectionProvider= new TreeSelectionProvider();
treeSelectionProvider.addPostSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
updateSelectionInfo((ITreeSelection) event.getSelection());
}
});
this.treeViewer.setLabelProvider(new RElementInputLabelProvider());
this.treeViewer.setUseHashlookup(true);
this.inputContentProvider= new RElementInputContentProvider();
this.treeViewer.setContentProvider(this.inputContentProvider);
updateSorter();
this.treeViewer.setInput(this);
this.tokenOwner= new ColumnWidgetTokenOwner(this.treeViewer);
this.hoveringController= new HoverManager();
this.hoveringController.setSizeConstraints(100, 12, false, true);
this.hoveringController.install(this.treeViewer.getTree());
final IViewSite site= getViewSite();
site.setSelectionProvider(treeSelectionProvider);
this.actionUtil= new ViewActionUtil(this);
this.handlers= new ContextHandlers(site.getService(IHandlerService.class));
initActions(site, this.handlers);
contributeToActionBars(site, site.getActionBars(), this.handlers);
hookContextMenu();
// listen on console changes
final WorkbenchToolRegistry toolRegistry= NicoUI.getToolRegistry();
this.toolRegistryListener= new WorkbenchToolRegistryListener() {
@Override
public void toolSessionActivated(final WorkbenchToolSessionData sessionData) {
final Tool tool= sessionData.getTool();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
setTool(tool, true);
}
});
}
@Override
public void toolTerminated(final WorkbenchToolSessionData sessionData) {
final Tool tool= sessionData.getTool();
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
if (tool == getTool()) {
setTool(null, true);
}
}
});
}
};
toolRegistry.addListener(this.toolRegistryListener, getViewSite().getPage());
setTool(toolRegistry.getActiveToolSession(getViewSite().getPage()).getTool(), true);
}
TreeViewer getViewer() {
return this.treeViewer;
}
protected void initActions(final IServiceLocator serviceLocator, final ContextHandlers handlers) {
final IContextService contexts= serviceLocator.getService(IContextService.class);
contexts.activateContext("org.eclipse.statet.workbench.contexts.StructuredElementViewer"); //$NON-NLS-1$
handlers.addActivate(REFRESH_COMMAND_ID, new RefreshHandler());
final CopyRElementHandler copyHandler= new CopyRElementHandler(this.actionUtil,
(ILabelProvider) this.treeViewer.getLabelProvider() );
handlers.addActivate(IWorkbenchCommandConstants.EDIT_COPY, copyHandler);
handlers.addActivate(ISourceEditorCommandIds.COPY_ELEMENT_NAME,
new CopyRElementNameHandler(this.actionUtil) );
handlers.addActivate(IWorkbenchCommandConstants.EDIT_DELETE,
new DeleteHandler(this) );
final ViewerDragSupport dragSupport= new ViewerDragSupport(this.treeViewer);
dragSupport.addDragSourceListener(new RElementViewerDragSourceListener(
copyHandler, this.treeViewer ));
dragSupport.init();
handlers.addActivate(OPEN_COMMAND_ID,
new OpenInEditorHandler() );
handlers.addActivate(PRINT_COMMAND_ID,
new PrintRElementHandler(this.actionUtil) );
handlers.addActivate(InformationDispatchHandler.COMMAND_ID,
new InformationDispatchHandler(this.tokenOwner) );
handlers.add(FILTER_INCLUDE_INTERNAL_COMMAND_ID, new FilterInternalHandler());
handlers.add(FILTER_ONLY_USERSPACE_COMMAND_ID, new FilterUserspaceHandler());
handlers.addActivate(NAVIGATE_COLLAPSE_ALL, new CollapseAllHandler(this.treeViewer));
RElementInputUtils.addDoubleClickExpansion(this.treeViewer);
this.searchTextItem= new SearchContributionItem("search.text", //$NON-NLS-1$
SearchContributionItem.VIEW_TOOLBAR, true ) {
@Override
protected void search() {
ObjectBrowserView.this.filterText= getText();
updateFilter();
}
};
this.searchTextItem.setToolTip("Filter Elements");
this.searchTextItem.setSizeControl(this.treeViewer.getControl().getParent());
this.searchTextItem.setResultControl(this.treeViewer.getTree());
handlers.addActivate(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE,
new AbstractHandler() {
@Override
public Object execute(final ExecutionEvent arg0) {
ObjectBrowserView.this.searchTextItem.show();
return null;
}
});
// add next/previous handler
}
protected void contributeToActionBars(final IServiceLocator serviceLocator,
final IActionBars actionBars, final HandlerCollection handlers) {
final IMenuManager menuManager= actionBars.getMenuManager();
final IToolBarManager toolbarManager= actionBars.getToolBarManager();
final ToggleAutoRefreshHandler autoRefreshHandler= new ToggleAutoRefreshHandler(this);
menuManager.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"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 ),
nonNullAssert(handlers.get(FILTER_ONLY_USERSPACE_COMMAND_ID)) ));
menuManager.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, HandlerContributionItem.NO_COMMAND_ID, null,
null, null, null,
"Show &Internal Variables ('.*')", null, null,
HandlerContributionItem.STYLE_CHECK, null, false ),
nonNullAssert(handlers.get(FILTER_INCLUDE_INTERNAL_COMMAND_ID)) ));
menuManager.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"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() ));
menuManager.add(new Separator());
final HandlerContributionItem autoRefreshItem= new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, HandlerContributionItem.NO_COMMAND_ID, null,
null, null, null,
"Refresh &automatically", null, null,
HandlerContributionItem.STYLE_CHECK, null, false ),
autoRefreshHandler );
menuManager.add(autoRefreshItem);
this.refreshMenuItem= new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"Refresh", REFRESH_COMMAND_ID, null, //$NON-NLS-1$
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
"&Refresh", null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers );
menuManager.add(this.refreshMenuItem);
menuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(final IMenuManager manager) {
autoRefreshItem.update();
}
});
toolbarManager.add(this.searchTextItem);
toolbarManager.add(new Separator());
this.refreshToolbarItem= new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"Refresh", REFRESH_COMMAND_ID, null, //$NON-NLS-1$
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH), StatetImages.getDescriptor(StatetImages.TOOLD_REFRESH), null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers );
this.refreshToolbarItem.setVisible(false);
toolbarManager.add(this.refreshToolbarItem);
toolbarManager.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, NAVIGATE_COLLAPSE_ALL, null,
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers ));
}
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.refreshToolbarItem.isVisible() != enabled) {
return;
}
this.refreshToolbarItem.setVisible(!enabled);
final IContributionManager manager= this.refreshToolbarItem.getParent();
manager.update(true);
this.searchTextItem.resize();
}
void updateDirty(final boolean enabled) {
if (this.refreshDirtyIndicator == enabled) {
return;
}
final ImageDescriptor icon= nonNullAssert((enabled) ?
RUIPlugin.getInstance().getImageRegistry().getDescriptor(RUIPlugin.IMG_LOCTOOL_REFRESH_RECOMMENDED) :
StatetImages.getDescriptor(StatetImages.TOOL_REFRESH) );
this.refreshToolbarItem.setIcon(icon);
this.refreshMenuItem.setIcon(icon);
this.refreshDirtyIndicator= 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(this::fillContextMenu);
final Menu contextMenu= menuManager.createContextMenu(this.treeViewer.getTree());
this.treeViewer.getTree().setMenu(contextMenu);
getSite().registerContextMenu(menuManager, this.treeViewer);
}
private void fillContextMenu(final IMenuManager m) {
final IServiceLocator serviceLocator= getSite();
final ContextHandlers handlers= this.handlers;
m.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, OPEN_COMMAND_ID, null,
null, null, null,
"Open in Data &Viewer", null, null,
HandlerContributionItem.STYLE_PUSH, null, true ),
handlers ));
m.add(new Separator(UIActions.EDIT_GROUP_ID));
m.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"Copy", IWorkbenchCommandConstants.EDIT_COPY, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers ));
m.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"Copy.ElementName", ISourceEditorCommandIds.COPY_ELEMENT_NAME, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers ));
m.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
"Delete", IWorkbenchCommandConstants.EDIT_DELETE, null, //$NON-NLS-1$
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers ));
m.add(new Separator());
m.add(new HandlerContributionItem(
new CommandContributionItemParameter(serviceLocator,
null, PRINT_COMMAND_ID, null,
null, null, null,
null, null, null,
HandlerContributionItem.STYLE_PUSH, null, false ),
handlers ));
m.add(new Separator(ADDITIONS_GROUP_ID));
}
@Override
public void setFocus() {
this.treeViewer.getControl().setFocus();
}
@Override
public @Nullable RProcess getTool() {
return this.process;
}
@Override
public void addToolListener(final ActiveToolListener action) {
this.toolListeners.add(action);
}
@Override
public void removeToolListener(final ActiveToolListener action) {
this.toolListeners.remove(action);
}
/** UI thread only */
private void setTool(final @Nullable Tool tool, final boolean update) {
final RProcess process= (tool != null
&& tool.isProvidingFeatureSet(RConsoleTool.R_DATA_FEATURESET_ID)
&& !tool.isTerminated() ) ?
(RProcess)tool : null;
if (this.process == tool) {
return;
}
final RProcess oldProcess= this.process;
if (oldProcess != null) {
oldProcess.getWorkspaceData().removePropertyListener(this.inputUpdater);
}
synchronized (this.sourceLock) {
this.process= process;
}
if (this.hoveringController != null) {
this.hoveringController.stop();
}
clearActionInfo();
if (oldProcess != null) {
oldProcess.getQueue().remove(this.manualRefreshRunnable);
}
final ActiveToolEvent event= new ActiveToolEvent(ActiveToolEvent.TOOL_ACTIVATED, process);
for (final ActiveToolListener listener : this.toolListeners) {
listener.onToolChanged(event);
}
this.inputUpdater.forceUpdate(process);
if (process != null) {
process.getWorkspaceData().addPropertyListener(this.inputUpdater);
updateAutoRefresh(process.getWorkspaceData().isAutoRefreshEnabled());
}
if (update) {
this.inputUpdater.schedule();
}
}
public ITreeSelection getSelection() {
return (ITreeSelection) this.treeViewer.getSelection();
}
public boolean getShowCondensedUserspace() {
return this.filterUserspace;
}
public boolean getFilterIncludeInternal() {
return this.filterIncludeInternal;
}
public String getFilterSearchText() {
return this.filterText;
}
private void scheduleUpdateAll() {
if (this.process != null) {
this.process.getQueue().add(this.manualRefreshRunnable);
}
}
/** UI thread only (called by update job) */
void updateView(final @Nullable ContentInput input,
final @Nullable List<RProcessREnvironment> updateEnvirs) {
if (!UIAccess.isOkToUse(this.treeViewer)) {
return;
}
this.isUpdating= true;
this.hoveringController.stop();
final ContentInput prevInput= this.inputContentProvider.getInput();
this.inputContentProvider.setInput(input);
final boolean processChanged= Objects.equals(
(input != null) ? input.getSource() : null,
(prevInput != null) ? prevInput.getSource() : null );
ISelection selection= null;
// If filter is changed, we have to retain the selection manually
if (input != null && !processChanged && input.isFilterUserspace() != this.filterUserspaceActivated) {
selection= this.treeViewer.getSelection();
}
this.filterUserspaceActivated= (input != null) ? input.isFilterUserspace() : false;
final Set<RReference> previousReferences= this.inputContentProvider.resetUsedReferences();
if (input != null && updateEnvirs != null) {
for (final RProcessREnvironment entry : updateEnvirs) {
this.treeViewer.refresh(entry, true);
}
if (!previousReferences.isEmpty()) {
final Set<RReference> usedReferences= this.inputContentProvider.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.treeViewer.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.treeViewer.refresh(true);
}
// Adapt and set selection
if (input != null && 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.isFilterUserspace()) ? -1 : +1;
if (count + shift < 1) {
continue;
}
final Object[] newPath= new @NonNull Object[count + shift];
for (int k= (shift == -1) ? +1 : 0; k < count; k++) {
newPath[k + shift]= oldPath.getSegment(k);
}
if (shift == +1) {
newPath[0]= RElementInputContentProvider.getCombinedRElement(newPath[1]).getModelParent();
}
paths[j++]= new TreePath(newPath);
}
this.treeViewer.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 (input != null && processChanged && !input.isFilterUserspace()) {
final CombinedRElement[] rootElements= input.getRootElements();
if (rootElements != null && rootElements.length > 0) {
for (final Object element : rootElements) {
if (this.treeViewer.getExpandedState(element)) {
break EXPAND_GLOBALENV;
}
}
this.treeViewer.expandToLevel(new TreePath(new Object[] { rootElements[0] }), 1);
}
}
this.isUpdating= false;
updateSelectionInfo((ITreeSelection) this.actionUtil.getSelectionProvider().getSelection());
}
private void updateFilter() {
this.inputUpdater.schedule();
}
private void updateSorter() {
this.treeViewer.setComparator((this.sortByType) ? TYPE_COMPARATOR : null);
}
private void clearActionInfo() {
this.actionUtil.getStatusLine().clearAll();
}
private void updateSelectionInfo(final ITreeSelection selection) {
if (this.isUpdating) {
return;
}
Object infoObject= null;
String message= null;
final RProcess tool= getTool();
if (tool != null && !selection.isEmpty()) {
if (selection.size() == 1) {
final TreePath treePath= selection.getPaths()[0];
final ElementName elementName= getFQElementName(treePath);
final String name= (elementName != null) ? elementName.getDisplayName() : null;
if (name != null) {
infoObject= selection.getFirstElement();
message= name;
}
}
else {
message= NLS.bind("{0} items selected", selection.size());
}
if (message != null) {
message= NLS.bind("{0} \u2012 {1}", message, tool.getLabel(Tool.DEFAULT_LABEL)); //$NON-NLS-1$
}
}
if (infoObject == null || !infoObject.equals(this.currentInfoObject)) {
clearActionInfo();
}
this.currentInfoObject= infoObject;
this.actionUtil.getStatusLine().setSelectionMessage(
(message != null) ? new StatusInfo(IStatus.OK, message) : null );
}
public @Nullable RElementName getFQElementName(final TreePath treePath) {
if (treePath.getSegmentCount() == 0) {
return null;
}
int segmentIdx= 0;
if (!this.filterUserspaceActivated) {
if (treePath.getSegmentCount() == 1) { // search path item
return RElementInputContentProvider.getCombinedRElement(treePath.getFirstSegment()).getElementName();
}
else { // main name at 1
segmentIdx= 1;
}
}
final List<RElementName> names= new ArrayList<>(treePath.getSegmentCount() - segmentIdx + 1);
final CombinedRElement first= RElementInputContentProvider.getCombinedRElement(treePath.getSegment(segmentIdx++));
names.add(first.getModelParent().getElementName());
names.add(first.getElementName());
while (segmentIdx < treePath.getSegmentCount()) {
final Object segment= treePath.getSegment(segmentIdx++);
if (segment instanceof ElementPartition) {
continue;
}
final CombinedRElement rElement= RElementInputContentProvider.getCombinedRElement(segment);
names.add(rElement.getElementName());
}
return RElementName.create(names);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter(final Class<T> adapterType) {
if (adapterType == Control.class) {
return (T) this.treeViewer.getControl();
}
if (adapterType == Tool.class) {
return (T) this.process;
}
return super.getAdapter(adapterType);
}
}