| /*=============================================================================# |
| # 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); |
| } |
| |
| } |