blob: 4c73438cead8be969fa038d4aae1fb7fdf998cbe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2007 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
*******************************************************************************/
package org.eclipse.ui.navigator;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
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.CommonNavigatorManager;
import org.eclipse.ui.internal.navigator.NavigatorContentService;
import org.eclipse.ui.internal.navigator.NavigatorPlugin;
import org.eclipse.ui.internal.navigator.extensions.LinkHelperService;
import org.eclipse.ui.part.ISetSelectionTarget;
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 lifecylce 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 are not expected to subclass CommonNavigator. Clients that wish to
* define their own custom extensible navigator view need to specify an instance
* of the <b>org.eclipse.ui.views</b> extension point:
*
* <pre>
*
* &lt;extension
* point=&quot;org.eclipse.ui.views&quot;&gt;
* &lt;view
* name=&quot;My Custom View&quot;
* icon=&quot;relative/path/to/icon.gif&quot;
* category=&quot;org.acme.mycategory&quot;
* class=&quot;org.eclipse.ui.navigator.CommonNavigator&quot;
* id=&quot;org.acme.MyCustomNavigatorID&quot;&gt;
* &lt;/view&gt;
* &lt;/extension&gt;
*
* </pre>
*
* </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>
* This class may be instantiated; it is not intended to be subclassed.
* </p>
* @noextend This class is not intended to be subclassed by clients.
*
* @since 3.2
*/
public class CommonNavigator extends ViewPart implements ISetSelectionTarget, ISaveablePart, ISaveablesSource, IShowInTarget {
private static final Class INAVIGATOR_CONTENT_SERVICE = INavigatorContentService.class;
private static final Class COMMON_VIEWER_CLASS = CommonViewer.class;
private static final Class ISHOW_IN_TARGET_CLASS = IShowInTarget.class;
private static final String HELP_CONTEXT = NavigatorPlugin.PLUGIN_ID + ".common_navigator"; //$NON-NLS-1$
/**
* <p>
* Used to track changes to the {@link #isLinkingEnabled}&nbsp;property.
* </p>
*/
public static final int IS_LINKING_ENABLED_PROPERTY = 1;
private CommonViewer commonViewer;
private CommonNavigatorManager commonManager;
private ActionGroup commonActionGroup;
private IMemento memento;
private boolean isLinkingEnabled = false;
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)
*/
public void createPartControl(Composite aParent) {
commonViewer = createCommonViewer(aParent);
try {
commonViewer.getControl().setRedraw(false);
INavigatorFilterService filterService = commonViewer
.getNavigatorContentService().getFilterService();
ViewerFilter[] visibleFilters = filterService.getVisibleFilters(true);
for (int i = 0; i < visibleFilters.length; i++) {
commonViewer.addFilter(visibleFilters[i]);
}
commonViewer.setSorter(new CommonViewerSorter());
/*
* make sure input is set after sorters and filters to avoid unnecessary
* refreshes
*/
commonViewer.setInput(getInitialInput());
getSite().setSelectionProvider(commonViewer);
updateTitle();
} finally {
commonViewer.getControl().setRedraw(true);
}
/*
* 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());
ISaveablesLifecycleListener saveablesLifecycleListener = new ISaveablesLifecycleListener() {
ISaveablesLifecycleListener siteSaveablesLifecycleListener = (ISaveablesLifecycleListener) getSite()
.getService(ISaveablesLifecycleListener.class);
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() {
public void selectionChanged(SelectionChangedEvent event) {
firePropertyChange(PROP_DIRTY);
}});
PlatformUI.getWorkbench().getHelpSystem().setHelp(commonViewer.getControl(), HELP_CONTEXT);
}
/**
* <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()
*/
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)
*/
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)
*/
public void saveState(IMemento aMemento) {
aMemento.putInteger(LINKING_ENABLED, (isLinkingEnabled) ? 1 : 0);
super.saveState(aMemento);
commonManager.saveState(aMemento);
commonViewer.getNavigatorContentService().saveState(aMemento);
}
/**
* <p>
* Force the workbench to focus on the Common Navigator tree.
* </p>
*
* @see org.eclipse.ui.part.WorkbenchPart#setFocus()
*/
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)
*/
public void selectReveal(ISelection selection) {
if (commonViewer != null) {
if(selection instanceof IStructuredSelection) {
Object[] newSelection = ((IStructuredSelection)selection).toArray();
Object[] expandedElements = commonViewer.getExpandedElements();
Object[] newExpandedElements = new Object[newSelection.length + expandedElements.length];
System.arraycopy(expandedElements, 0, newExpandedElements, 0, expandedElements.length);
System.arraycopy(newSelection, 0, newExpandedElements, expandedElements.length, newSelection.length);
commonViewer.setExpandedElements(newExpandedElements);
}
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)}&nbsp;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
*/
public Object getAdapter(Class adapter) {
if (adapter == COMMON_VIEWER_CLASS) {
return getCommonViewer();
} else if (adapter == INAVIGATOR_CONTENT_SERVICE) {
return getCommonViewer().getNavigatorContentService();
} else if ( adapter == ISHOW_IN_TARGET_CLASS) {
return this;
}
return super.getAdapter(adapter);
}
/**
* @return The Navigator Content Service which populates this instance of
* Common Navigator
*/
public NavigatorActionService getNavigatorActionService() {
return commonManager.getNavigatorActionService();
}
/**
* <p>
* Constructs and returns 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.
* <p>
*
* @param aParent
* A composite parent to contain the Common Viewer
* @return An initialized instance of CommonViewer
*/
protected CommonViewer createCommonViewer(Composite aParent) {
CommonViewer aViewer = new CommonViewer(getViewSite().getId(), aParent,
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
initListeners(aViewer);
aViewer.getNavigatorContentService().restoreState(memento);
return aViewer;
}
/**
* <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() {
public void doubleClick(DoubleClickEvent event) {
try {
handleDoubleClick(event);
} catch (RuntimeException re) {
re.printStackTrace();
}
}
});
}
/**
* <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}&nbsp; 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
*/
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());
}
/**
* @return The initial input for the viewer. Defaults to
* getSite().getPage().getInput()
*/
protected IAdaptable getInitialInput() {
return getSite().getPage().getInput();
}
/**
* <p>
* Updates the title text and title tool tip. Called whenever the input of
* the viewer changes.
* </p>
*/
protected void updateTitle() {
if (commonViewer == null) {
return;
}
Object input = commonViewer.getInput();
String viewName = getConfigurationElement().getAttribute("name"); //$NON-NLS-1$
// IWorkingSet workingSet = workingSetFilter.getWorkingSet();
if (input == null) {
setPartName(viewName);
setTitleToolTip(""); //$NON-NLS-1$
} else {
String inputToolTip = getFrameToolTipText(input);
setPartName(viewName);
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>
*/
protected String getFrameToolTipText(Object anElement) {
if (commonViewer != null) {
return ((ILabelProvider) commonViewer.getLabelProvider())
.getText(anElement);
}
return ""; //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablesSource#getSaveables()
*/
public Saveable[] getSaveables() {
return getNavigatorContentService().getSaveablesService().getSaveables();
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
*/
public Saveable[] getActiveSaveables() {
return getNavigatorContentService().getSaveablesService().getActiveSaveables();
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
public void doSave(IProgressMonitor monitor) {
// Ignore. This method is not called because CommonNavigator implements
// ISaveablesSource. All saves will go through the ISaveablesSource /
// Saveable protocol.
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#doSaveAs()
*/
public void doSaveAs() {
// ignore
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isDirty()
*/
public boolean isDirty() {
Saveable[] saveables = getSaveables();
for (int i = 0; i < saveables.length; i++) {
if(saveables[i].isDirty()) {
return true;
}
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
*/
public boolean isSaveAsAllowed() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
*/
public boolean isSaveOnCloseNeeded() {
return isDirty();
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.IShowInTarget#show(org.eclipse.ui.part.ShowInContext)
*/
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;
}
private synchronized LinkHelperService getLinkHelperService() {
if (linkService == null)
linkService = new LinkHelperService((NavigatorContentService)getCommonViewer().getNavigatorContentService());
return linkService;
}
}