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