/******************************************************************************* * Copyright (c) 2003, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * anton.leherbauer@windriver.com - bug 220599 make CommonNavigator a ShowInSource * rob.stryker@jboss.com - bug 250198 Slight changes for easier subclassing *******************************************************************************/ package org.eclipse.ui.navigator; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.PerformanceStats; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IMemento; import org.eclipse.ui.ISaveablePart; import org.eclipse.ui.ISaveablesLifecycleListener; import org.eclipse.ui.ISaveablesSource; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.Saveable; import org.eclipse.ui.SaveablesLifecycleEvent; import org.eclipse.ui.actions.ActionGroup; import org.eclipse.ui.internal.navigator.CommonNavigatorActionGroup; import org.eclipse.ui.internal.navigator.NavigatorContentService; import org.eclipse.ui.internal.navigator.NavigatorPlugin; import org.eclipse.ui.internal.navigator.NavigatorSafeRunnable; import org.eclipse.ui.part.ISetSelectionTarget; import org.eclipse.ui.part.IShowInSource; import org.eclipse.ui.part.IShowInTarget; import org.eclipse.ui.part.ShowInContext; import org.eclipse.ui.part.ViewPart; /** * <p> * This class provides the IViewPart for the Common Navigator framework in the * Eclipse workbench. This class also serves as the backbone for navigational * viewers. The following types are used by this class to render the Common * Navigator: * <ul> * <li> * <p> * {@link org.eclipse.ui.navigator.CommonViewer}: The viewer that renders the * extensible tree. Creates and manages the lifecycle of the Navigator Content * Service (described below). * </p> * </li> * <li> * <p> * {@link org.eclipse.ui.navigator.NavigatorActionService}: Manages instances of * {@link org.eclipse.ui.navigator.CommonActionProvider}s provided by individual * extensions and content extensions. * </p> * </li> * <li> * <p> * {@link org.eclipse.ui.navigator.INavigatorContentService}: Manages instances * of Navigator Content Extensions. Instances are created as needed, and * disposed of upon the disposal of the Navigator Content Service. * </p> * </li> * </ul> * * <p> * Clients that wish to define their own custom extensible navigator view using * CommonNavigator need to specify an instance of the * <b>org.eclipse.ui.views</b> extension point: * * <pre> * * <extension * point="org.eclipse.ui.views"> * <view * name="My Custom View" * icon="relative/path/to/icon.gif" * category="org.acme.mycategory" * class="org.eclipse.ui.navigator.CommonNavigator" * id="org.acme.MyCustomNavigatorID"> * </view> * </extension> * * </pre> * * </p> * * <p> * CommonNavigator gets its initial input (during initialization) from the * Workbench by calling getSite().getPage().getInput(). This is done in * {@link #getInitialInput()}. Clients may create a subclass of CommonNavigator * to provide their own means of getting the initial input. Or they may access * the {@link CommonViewer} and set its input directly after startup. * </p> * * <p> * In the IDE scenario, the default page input is IWorkspaceRoot, in the RCP * scenario it is null and can be configured in the WorkbenchAdvisor. * </p> * * <p> * Clients that wish to extend the view menu provided via the * <b>org.eclipse.ui.popupMenu</b>s extension may specify the the * <i>popupMenuId</i> specified by <b>org.eclipse.ui.navigator.viewer</b> (or a * nested <b>popupMenu</b> element) of their target viewer as their target menu * id. * </p> * * <p> * This class may be instantiated or subclassed * </p> * * @since 3.2 */ public class CommonNavigator extends ViewPart implements ISetSelectionTarget, ISaveablePart, ISaveablesSource, IShowInTarget { private static final String PERF_CREATE_PART_CONTROL= "org.eclipse.ui.navigator/perf/explorer/createPartControl"; //$NON-NLS-1$ private static final String HELP_CONTEXT = NavigatorPlugin.PLUGIN_ID + ".common_navigator"; //$NON-NLS-1$ /** * <p> * Used to track changes to the {@link #isLinkingEnabled} property. * </p> * * Make sure this does not conflict with anything in IWorkbenchPartConstants. * */ public static final int IS_LINKING_ENABLED_PROPERTY = 0x10000; private CommonViewer commonViewer; private CommonNavigatorManager commonManager; private ActionGroup commonActionGroup; /** * To allow {@link #createCommonViewer(Composite)} to be subclassed * * @since 3.4 */ protected IMemento memento; private boolean isLinkingEnabled; private String LINKING_ENABLED = "CommonNavigator.LINKING_ENABLED"; //$NON-NLS-1$ private LinkHelperService linkService; /** * */ public CommonNavigator() { super(); } /** * <p> * Create the CommonViewer part control and setup the default providers as * necessary. * </p> * * * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite aParent) { final PerformanceStats stats= PerformanceStats.getStats(PERF_CREATE_PART_CONTROL, this); stats.startRun(); commonViewer = createCommonViewer(aParent); commonViewer.setCommonNavigator(this); try { commonViewer.getControl().setRedraw(false); INavigatorFilterService filterService = commonViewer .getNavigatorContentService().getFilterService(); ViewerFilter[] visibleFilters = filterService.getVisibleFilters(true); for (ViewerFilter visibleFilter : visibleFilters) { commonViewer.addFilter(visibleFilter); } commonViewer.setSorter(new CommonViewerSorter()); /* * make sure input is set after sorters and filters to avoid unnecessary * refreshes */ commonViewer.setInput(getInitialInput()); getSite().setSelectionProvider(commonViewer); setPartName(getConfigurationElement().getAttribute("name")); //$NON-NLS-1$ } finally { commonViewer.getControl().setRedraw(true); } commonViewer.createFrameList(); /* * Create the CommonNavigatorManager last because information about the * state of the CommonNavigator is required for the initialization of * the CommonNavigatorManager */ commonManager = createCommonManager(); if (memento != null) { commonViewer.getNavigatorContentService().restoreState(memento); } commonActionGroup = createCommonActionGroup(); commonActionGroup.fillActionBars(getViewSite().getActionBars()); if (memento != null && commonActionGroup instanceof IMementoAware) { ((IMementoAware) commonActionGroup).restoreState(memento); } ISaveablesLifecycleListener saveablesLifecycleListener = new ISaveablesLifecycleListener() { ISaveablesLifecycleListener siteSaveablesLifecycleListener = getSite() .getService(ISaveablesLifecycleListener.class); @Override public void handleLifecycleEvent(SaveablesLifecycleEvent event) { if (event.getEventType() == SaveablesLifecycleEvent.DIRTY_CHANGED) { firePropertyChange(PROP_DIRTY); } siteSaveablesLifecycleListener.handleLifecycleEvent(event); } }; commonViewer.getNavigatorContentService() .getSaveablesService().init(this, getCommonViewer(), saveablesLifecycleListener); commonViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { firePropertyChange(PROP_DIRTY); }}); String helpContext = commonViewer.getNavigatorContentService().getViewerDescriptor().getHelpContext(); if (helpContext == null) helpContext = HELP_CONTEXT; PlatformUI.getWorkbench().getHelpSystem().setHelp(commonViewer.getControl(), helpContext); stats.endRun(); } /** * <p> * Updates the title text and title tool tip. Called whenever the input of * the viewer changes. * </p> * @since 3.4 */ public void updateTitle() { Object input = commonViewer.getInput(); if (input == null) { setTitleToolTip(""); //$NON-NLS-1$ } else { String inputToolTip = getFrameToolTipText(input); setTitleToolTip(inputToolTip); } } /** * <p> * Returns the tool tip text for the given element. Used as the tool tip * text for the current frame, and for the view title tooltip. * </p> * @param anElement * @return the tool tip text * @since 3.4 * */ public String getFrameToolTipText(Object anElement) { if (commonViewer == null) return ""; //$NON-NLS-1$ return ((ILabelProvider) commonViewer.getLabelProvider()) .getText(anElement); } /** * <p> * Note: This method is for internal use only. Clients should not call this * method. * </p> * <p> * This method will be invoked when the DisposeListener is notified of the * disposal of the Eclipse view part. * </p> * * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override public void dispose() { if (commonManager != null) { commonManager.dispose(); } if (commonActionGroup != null) { commonActionGroup.dispose(); } super.dispose(); } /** * <p> * Note: This method is for internal use only. Clients should not call this * method. * </p> * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite, * org.eclipse.ui.IMemento) */ @Override public void init(IViewSite aSite, IMemento aMemento) throws PartInitException { super.init(aSite, aMemento); memento = aMemento; if (memento != null) { Integer linkingEnabledInteger = memento.getInteger(LINKING_ENABLED); setLinkingEnabled(((linkingEnabledInteger != null) ? linkingEnabledInteger .intValue() == 1 : false)); } } /** * * <p> * Note: This method is for internal use only. Clients should not call this * method. * </p> * * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento) */ @Override public void saveState(IMemento aMemento) { aMemento.putInteger(LINKING_ENABLED, (isLinkingEnabled) ? 1 : 0); super.saveState(aMemento); commonManager.saveState(aMemento); commonViewer.getNavigatorContentService().saveState(aMemento); if (commonActionGroup instanceof IMementoAware) { ((IMementoAware) commonActionGroup).saveState(aMemento); } } /** * <p> * Force the workbench to focus on the Common Navigator tree. * </p> * * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ @Override public void setFocus() { if (commonViewer != null) { commonViewer.getTree().setFocus(); } } /** * <p> * Set the selection to the Common Navigator tree, and expand nodes if * necessary. Use caution when invoking this method as it can cause * Navigator Content Extensions to load, thus causing plugin activation. * </p> * * @see org.eclipse.ui.part.ISetSelectionTarget#selectReveal(org.eclipse.jface.viewers.ISelection) */ @Override public void selectReveal(ISelection selection) { if (commonViewer != null) { commonViewer.setSelection(selection, true); } } /** * <p> * Linking is handled by an action which listens for * changes to the {@link CommonNavigator#IS_LINKING_ENABLED_PROPERTY}. * Custom implementations that wish to override this functionality, need to * override the action used by the default ActionGroup and listen for * changes to the above property. * * @param toEnableLinking * True enables linking the current selection with open editors */ public final void setLinkingEnabled(boolean toEnableLinking) { isLinkingEnabled = toEnableLinking; firePropertyChange(IS_LINKING_ENABLED_PROPERTY); } /** * @return Whether linking the current selection with open editors is * enabled. */ public final boolean isLinkingEnabled() { return isLinkingEnabled; } /** * <p> * Provides access to the commonViewer used by the current CommonNavigator. * The field will not be valid until after * {@link #init(IViewSite, IMemento)} has been called by the * Workbench. * </p> * * @return The (already created) instance of Common Viewer. */ public CommonViewer getCommonViewer() { return commonViewer; } /** * @return The Navigator Content Service which populates this instance of * Common Navigator */ public INavigatorContentService getNavigatorContentService() { return getCommonViewer().getNavigatorContentService(); } /** * Returns an object which is an instance of the given class * associated with this object. Returns <code>null</code> if * no such object can be found. * * @param adapter the adapter class to look up * @return a object castable to the given class, * or <code>null</code> if this object does not * have an adapter for the given class */ @Override @SuppressWarnings("unchecked") public <T> T getAdapter(Class<T> adapter) { if (adapter == CommonViewer.class) { return (T) getCommonViewer(); } else if (adapter == INavigatorContentService.class) { return (T) getCommonViewer().getNavigatorContentService(); } else if (adapter == IShowInTarget.class) { return (T) this; } else if (adapter == IShowInSource.class) { return (T) getShowInSource(); } return super.getAdapter(adapter); } /** * Returns the <code>IShowInSource</code> for this view. */ private IShowInSource getShowInSource() { return new IShowInSource() { @Override public ShowInContext getShowInContext() { return new ShowInContext(getCommonViewer().getInput(), getCommonViewer().getSelection()); } }; } /** * @return The Navigator Content Service which populates this instance of * Common Navigator */ public NavigatorActionService getNavigatorActionService() { return commonManager.getNavigatorActionService(); } /** * Creates and initializes an instance of {@link CommonViewer}. The ID of * the Eclipse view part will be used to create the viewer. The ID is * important as some extensions indicate they should only be used with a * particular viewer ID. * * @param aParent * A composite parent to contain the Common Viewer * @return An initialized instance of CommonViewer */ protected CommonViewer createCommonViewer(Composite aParent) { CommonViewer aViewer = createCommonViewerObject(aParent); initListeners(aViewer); aViewer.getNavigatorContentService().restoreState(memento); return aViewer; } /** * Constructs and returns an instance of {@link CommonViewer}. The ID of * the Eclipse view part will be used to create the viewer. * * Override this method if you want a subclass of the CommonViewer * * @param aParent * A composite parent to contain the CommonViewer * @return An instance of CommonViewer * @since 3.4 */ protected CommonViewer createCommonViewerObject(Composite aParent) { return new CommonViewer(getViewSite().getId(), aParent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); } /** * <p> * Adds the listeners to the Common Viewer. * </p> * * @param viewer * The viewer * @since 2.0 */ protected void initListeners(TreeViewer viewer) { viewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(final DoubleClickEvent event) { SafeRunner.run(new NavigatorSafeRunnable() { @Override public void run() throws Exception { handleDoubleClick(event); } }); } }); } /** * <p> * Note: This method is for internal use only. Clients should not call this * method. * </p> * * @param anEvent * Supplied by the DoubleClick listener. */ protected void handleDoubleClick(DoubleClickEvent anEvent) { IAction openHandler = getViewSite().getActionBars().getGlobalActionHandler(ICommonActionConstants.OPEN); if(openHandler == null) { IStructuredSelection selection = (IStructuredSelection) anEvent .getSelection(); Object element = selection.getFirstElement(); TreeViewer viewer = getCommonViewer(); if (viewer.isExpandable(element)) { viewer.setExpandedState(element, !viewer.getExpandedState(element)); } } } /** * <p> * The Common Navigator Manager handles the setup of the Common Navigator * Menu, manages updates to the ActionBars from * {@link CommonActionProvider}  extensions as the user's selection * changes, and also updates the status bar based on the current selection. * * @return The Common Navigator Manager class which handles menu population * and ActionBars * * @since 3.4 */ protected CommonNavigatorManager createCommonManager() { return new CommonNavigatorManager(this, memento); } /** * <p> * The ActionGroup is used to populate the ActionBars of Common Navigator * View Part, and the returned implementation will have an opportunity to * fill the ActionBars of the view as soon as it is created. ({@link ActionGroup#fillActionBars(org.eclipse.ui.IActionBars)}. * </p> * <p> * The default implementation returns an action group which will add the * following actions: * <ul> * <li> * <p> * Link with editor support. Allows the user to toggling linking the current * selection with the active editors. * </p> * <li> * <p> * Collapse all. Collapses all expanded nodes. * </p> * <li> * <p> * Select Filters. Provides access to the "Select Filters" dialog that * allows users to enable/disable filters and also the Content Extension * activations. * </p> * </ul> * * @return The Action Group to be associated with the Common Navigator View * Part. */ protected ActionGroup createCommonActionGroup() { return new CommonNavigatorActionGroup(this, commonViewer, getLinkHelperService()); } /** * Used to provide the initial input for the {@link CommonViewer}. By default * getSite().getPage().getInput() is used. Subclass this to return your desired * input. * * @return The initial input for the viewer. Defaults to * getSite().getPage().getInput() * @since 3.4 */ protected Object getInitialInput() { return getSite().getPage().getInput(); } @Override public Saveable[] getSaveables() { return getNavigatorContentService().getSaveablesService().getSaveables(); } @Override public Saveable[] getActiveSaveables() { return getNavigatorContentService().getSaveablesService().getActiveSaveables(); } @Override public void doSave(IProgressMonitor monitor) { // Ignore. This method is not called because CommonNavigator implements // ISaveablesSource. All saves will go through the ISaveablesSource / // Saveable protocol. } @Override public void doSaveAs() { // ignore } @Override public boolean isDirty() { Saveable[] saveables = getSaveables(); for (Saveable saveable : saveables) { if(saveable.isDirty()) { return true; } } return false; } @Override public boolean isSaveAsAllowed() { return false; } @Override public boolean isSaveOnCloseNeeded() { return isDirty(); } @Override public boolean show(ShowInContext context) { IStructuredSelection selection = getSelection(context); if (selection != null && !selection.isEmpty()) { selectReveal(selection); return true; } return false; } private IStructuredSelection getSelection(ShowInContext context) { if (context == null) return StructuredSelection.EMPTY; ISelection selection = context.getSelection(); if (selection != null && !selection.isEmpty() && selection instanceof IStructuredSelection) return (IStructuredSelection)selection; Object input = context.getInput(); if (input instanceof IEditorInput) { LinkHelperService lhs = getLinkHelperService(); return lhs.getSelectionFor((IEditorInput) input); } if (input != null) { return new StructuredSelection(input); } return StructuredSelection.EMPTY; } /** * @since 3.4 */ protected synchronized LinkHelperService getLinkHelperService() { if (linkService == null) linkService = new LinkHelperService((NavigatorContentService)getCommonViewer().getNavigatorContentService()); return linkService; } /** * @since 3.4 */ protected IMemento getMemento() { return memento; } /** * @param mode * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.4 */ public void setRootMode(int mode) { // For subclassing } /** * @return the root mode * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.4 */ public int getRootMode() { // For subclassing return 0; } /** * @param label * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.4 */ public void setWorkingSetLabel(String label) { // For subclassing } /** * @return the working set label * @noreference This method is not intended to be referenced by clients. * @nooverride This method is not intended to be re-implemented or extended by clients. * @since 3.4 */ public String getWorkingSetLabel() { // For subclassing return null; } }