blob: e8bc05da61c5259e5df7f96ca0d4ea26cdea3ce4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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.views.markers.internal;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.resources.mapping.ResourceMappingContext;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.help.HelpSystem;
import org.eclipse.help.IContext;
import org.eclipse.help.IContextProvider;
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.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.events.TreeAdapter;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ContributionItemFactory;
import org.eclipse.ui.actions.SelectionProviderAction;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.operations.RedoActionHandler;
import org.eclipse.ui.operations.UndoActionHandler;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.MarkerTransfer;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.preferences.ViewPreferencesAction;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.views.markers.internal.MarkerAdapter.MarkerCategory;
import org.eclipse.ui.views.tasklist.ITaskListResourceAdapter;
/**
* MarkerView is the abstract super class of the marker based views.
*
*/
public abstract class MarkerView extends TableView {
private static final String TAG_SELECTION = "selection"; //$NON-NLS-1$
private static final String TAG_MARKER = "marker"; //$NON-NLS-1$
private static final String TAG_RESOURCE = "resource"; //$NON-NLS-1$
private static final String TAG_ID = "id"; //$NON-NLS-1$
private static final String TAG_FILTERS_SECTION = "filters"; //$NON-NLS-1$
private static final String TAG_FILTER_ENTRY = "filter"; //$NON-NLS-1$
private static final String MENU_FILTERS_GROUP = "group.filter";//$NON-NLS-1$
private static final String MENU_SHOW_IN_GROUP = "group.showIn";//$NON-NLS-1$
// Section from a 3.1 or earlier workbench
private static final String OLD_FILTER_SECTION = "filter"; //$NON-NLS-1$
static final Object MARKER_UPDATE_FAMILY = new Object();
class MarkerProcessJob extends Job {
/**
* Create a new instance of the receiver.
*/
MarkerProcessJob() {
super(MarkerMessages.MarkerView_processUpdates);
setSystem(true);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
*/
protected IStatus run(IProgressMonitor monitor) {
updateForContentsRefresh(monitor);
return Status.OK_STATUS;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.progress.WorkbenchJob#shouldRun()
*/
public boolean shouldRun() {
// Do not run if the change came in before there is a viewer
return PlatformUI.isWorkbenchRunning();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
*/
public boolean belongsTo(Object family) {
return MARKER_UPDATE_FAMILY == family;
}
}
MarkerProcessJob markerProcessJob = new MarkerProcessJob();
private class UpdateJob extends WorkbenchJob {
private class MarkerDescriptor {
String description;
String folder;
String resource;
int line;
MarkerDescriptor(ConcreteMarker marker) {
description = marker.getDescription();
folder = marker.getFolder();
resource = marker.getResourceName();
line = marker.getLine();
}
boolean isEquivalentTo(ConcreteMarker marker) {
return marker.getDescription().equals(description)
&& marker.getFolder().equals(folder)
&& marker.getResourceName().equals(resource)
&& marker.getLine() == line;
}
}
private Collection categoriesToExpand = new HashSet();
private Collection preservedSelection = new ArrayList();
UpdateJob() {
super(MarkerMessages.MarkerView_queueing_updates);
setSystem(true);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
*/
public IStatus runInUIThread(IProgressMonitor monitor) {
if (getViewer().getControl().isDisposed()) {
return Status.CANCEL_STATUS;
}
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
getViewer().refresh(true);
Tree tree = getTree();
if (tree != null && !tree.isDisposed()) {
updateStatusMessage();
updateTitle();
// Expand all if the list is small
if (getCurrentMarkers().getSize() < 20) {
getViewer().expandAll();
} else {// Reexpand the old categories
MarkerCategory[] categories = getMarkerAdapter()
.getCategories();
if (categories == null)
categoriesToExpand.clear();
else {
if (categories.length == 1) {// Expand if there is
// only
// one
getViewer().expandAll();
categoriesToExpand.clear();
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
categoriesToExpand.add(categories[0].getName());
} else {
Collection newCategories = new HashSet();
for (int i = 0; i < categories.length; i++) {
if (monitor.isCanceled())
return Status.CANCEL_STATUS;
MarkerCategory category = categories[i];
if (categoriesToExpand.contains(category
.getName())) {
getViewer().expandToLevel(category,
AbstractTreeViewer.ALL_LEVELS);
newCategories.add(category.getName());
}
}
categoriesToExpand = newCategories;
}
}
}
}
if (preservedSelection.size() > 0) {
Collection newSelection = new ArrayList();
ConcreteMarker[] markers = getCurrentMarkers().toArray();
for (int i = 0; i < markers.length; i++) {
Iterator preserved = preservedSelection.iterator();
while (preserved.hasNext()) {
MarkerDescriptor next = (MarkerDescriptor) preserved
.next();
if (next.isEquivalentTo(markers[i])) {
newSelection.add(markers[i]);
continue;
}
}
}
getViewer().setSelection(
new StructuredSelection(newSelection.toArray()), true);
preservedSelection.clear();
}
if (getViewer().getTree().getItemCount() > 0)
getViewer().getTree().setTopItem(
getViewer().getTree().getItem(0));
return Status.OK_STATUS;
}
/**
* Add the category to the list of expanded categories.
*
* @param category
*/
public void addExpandedCategory(MarkerCategory category) {
categoriesToExpand.add(category.getName());
}
/**
* Remove the category from the list of expanded ones.
*
* @param category
*/
public void removeExpandedCategory(MarkerCategory category) {
categoriesToExpand.remove(category.getName());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
*/
public boolean belongsTo(Object family) {
return family == MARKER_UPDATE_FAMILY;
}
/**
* Preserve the selection for reselection after the next update.
*
* @param selection
*/
public void saveSelection(ISelection selection) {
preservedSelection.clear();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structured = (IStructuredSelection) selection;
Iterator iterator = structured.iterator();
while (iterator.hasNext()) {
MarkerNode next = (MarkerNode) iterator.next();
if (next.isConcrete()) {
preservedSelection.add(new MarkerDescriptor(next
.getConcreteRepresentative()));
}
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.progress.WorkbenchJob#shouldRun()
*/
public boolean shouldRun() {
return !getMarkerAdapter().isBuilding();
}
}
private UpdateJob updateJob = new UpdateJob();
// A private field for keeping track of the number of markers
// before the busy testing started
private int preBusyMarkers = 0;
protected Object[] focusElements;
private Clipboard clipboard;
IResourceChangeListener markerUpdateListener = new IResourceChangeListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
if (!hasMarkerDelta(event))
return;
if (event.getType() == IResourceChangeEvent.POST_BUILD) {
scheduleMarkerUpdate(Util.SHORT_DELAY);
return;
}
// After 30 seconds do updates anyways
IWorkbenchSiteProgressService progressService = getProgressService();
if (progressService == null)
markerProcessJob.schedule(Util.LONG_DELAY);
else
getProgressService()
.schedule(markerProcessJob, Util.LONG_DELAY);
}
/**
* Returns whether or not the given even contains marker deltas for this
* view.
*
* @param event
* the resource change event
* @return <code>true</code> if the event contains at least one
* relevant marker delta
* @since 3.3
*/
private boolean hasMarkerDelta(IResourceChangeEvent event) {
String[] markerTypes = getMarkerTypes();
for (int i = 0; i < markerTypes.length; i++) {
if (event.findMarkerDeltas(markerTypes[i], true).length > 0) {
return true;
}
}
return false;
}
};
private class ContextProvider implements IContextProvider {
public int getContextChangeMask() {
return SELECTION;
}
public IContext getContext(Object target) {
String contextId = null;
// See if there is a context registered for the current selection
ConcreteMarker marker = getSelectedConcreteMarker();
if (marker != null) {
contextId = IDE.getMarkerHelpRegistry().getHelp(
marker.getMarker());
}
if (contextId == null) {
contextId = getStaticContextId();
}
return HelpSystem.getContext(contextId);
}
/**
* Return the currently selected concrete marker or <code>null</code>
* if there isn't one.
*
* @return ConcreteMarker
*/
private ConcreteMarker getSelectedConcreteMarker() {
IStructuredSelection selection = (IStructuredSelection) getViewer()
.getSelection();
if (selection.isEmpty())
return null;
if (selection.getFirstElement() instanceof ConcreteMarker)
return (ConcreteMarker) selection.getFirstElement();
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.help.IContextProvider#getSearchExpression(java.lang.Object)
*/
public String getSearchExpression(Object target) {
return null;
}
}
private ContextProvider contextProvider = new ContextProvider();
protected ActionCopyMarker copyAction;
protected ActionPasteMarker pasteAction;
protected SelectionProviderAction revealAction;
protected SelectionProviderAction openAction;
protected SelectionProviderAction deleteAction;
protected SelectionProviderAction selectAllAction;
protected SelectionProviderAction propertiesAction;
protected UndoActionHandler undoAction;
protected RedoActionHandler redoAction;
private ISelectionListener focusListener = new ISelectionListener() {
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
MarkerView.this.focusSelectionChanged(part, selection);
}
};
private int totalMarkers = 0;
private MarkerFilter[] markerFilters = new MarkerFilter[0];
// A cache of the enabled filters
private MarkerFilter[] enabledFilters = null;
private MenuManager filtersMenu;
private MenuManager showInMenu;
private IPropertyChangeListener workingSetListener;
private MarkerAdapter adapter;
private IPropertyChangeListener preferenceListener;
/**
* Create a new instance of the receiver,
*/
public MarkerView() {
super();
preferenceListener = new IPropertyChangeListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(getFiltersPreferenceName())) {
loadFiltersPreferences();
clearEnabledFilters();
refreshForFocusUpdate();
}
}
};
IDEWorkbenchPlugin.getDefault().getPreferenceStore()
.addPropertyChangeListener(preferenceListener);
}
/**
* Get the current markers for the receiver.
*
* @return MarkerList
*/
public MarkerList getCurrentMarkers() {
return getMarkerAdapter().getCurrentMarkers();
}
/**
* Get the marker adapter for the receiver.
*
* @return MarkerAdapter
*/
protected MarkerAdapter getMarkerAdapter() {
return adapter;
}
/**
* Update for the change in the contents.
*
* @param monitor
*/
public void updateForContentsRefresh(IProgressMonitor monitor) {
updateJob.cancel();
getMarkerAdapter().buildAllMarkers(monitor);
getProgressService().schedule(updateJob);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite,
* org.eclipse.ui.IMemento)
*/
public void init(IViewSite site, IMemento memento) throws PartInitException {
super.init(site, memento);
IWorkbenchSiteProgressService progressService = getProgressService();
if (progressService != null) {
getProgressService().showBusyForFamily(
ResourcesPlugin.FAMILY_MANUAL_BUILD);
getProgressService().showBusyForFamily(
ResourcesPlugin.FAMILY_AUTO_BUILD);
getProgressService().showBusyForFamily(MARKER_UPDATE_FAMILY);
}
loadFiltersPreferences();
}
/**
* Load the filters preference.
*/
private void loadFiltersPreferences() {
String preference = IDEWorkbenchPlugin.getDefault()
.getPreferenceStore().getString(getFiltersPreferenceName());
if (preference.equals(IPreferenceStore.STRING_DEFAULT_DEFAULT)) {
createDefaultFilter();
return;
}
StringReader reader = new StringReader(preference);
try {
restoreFilters(XMLMemento.createReadRoot(reader));
} catch (WorkbenchException e) {
IDEWorkbenchPlugin.log(e.getLocalizedMessage(), e);
}
}
/**
* Update for filter changes. Save the preference and clear the enabled
* cache.
*/
void updateForFilterChanges() {
XMLMemento memento = XMLMemento.createWriteRoot(TAG_FILTERS_SECTION);
writeFiltersSettings(memento);
StringWriter writer = new StringWriter();
try {
memento.save(writer);
} catch (IOException e) {
IDEWorkbenchPlugin.getDefault().getLog().log(Util.errorStatus(e));
}
IDEWorkbenchPlugin.getDefault().getPreferenceStore().putValue(
getFiltersPreferenceName(), writer.toString());
IDEWorkbenchPlugin.getDefault().savePluginPreferences();
clearEnabledFilters();
refreshFilterMenu();
refreshViewer();
}
/**
* Write the filter settings to the memento.
*
* @param memento
*/
protected void writeFiltersSettings(XMLMemento memento) {
MarkerFilter[] filters = getUserFilters();
for (int i = 0; i < filters.length; i++) {
IMemento child = memento.createChild(TAG_FILTER_ENTRY, filters[i]
.getName());
filters[i].saveFilterSettings(child);
}
}
/**
* Get the name of the filters preference for instances of the receiver.
*
* @return String
*/
abstract String getFiltersPreferenceName();
/**
* Restore the filters from the mimento.
*
* @param memento
*/
void restoreFilters(IMemento memento) {
IMemento[] sections = null;
if (memento != null) {
sections = memento.getChildren(TAG_FILTER_ENTRY);
}
if (sections == null) {
// Check if we have an old filter setting around
IDialogSettings mainSettings = getDialogSettings();
IDialogSettings filtersSection = mainSettings
.getSection(OLD_FILTER_SECTION);
if (filtersSection != null) {
MarkerFilter markerFilter = createFilter(MarkerMessages.MarkerFilter_defaultFilterName);
markerFilter.restoreFilterSettings(filtersSection);
setFilters(new MarkerFilter[] { markerFilter });
}
} else {
MarkerFilter[] newFilters = new MarkerFilter[sections.length];
for (int i = 0; i < sections.length; i++) {
newFilters[i] = createFilter(sections[i].getID());
newFilters[i].restoreState(sections[i]);
}
setFilters(newFilters);
}
if (markerFilters.length == 0) {// Make sure there is at least a default
createDefaultFilter();
}
}
/**
* Create a default filter for the receiver.
*
*/
private void createDefaultFilter() {
MarkerFilter filter = createFilter(MarkerMessages.MarkerFilter_defaultFilterName);
setFilters(new MarkerFilter[] { filter });
}
/**
* Create a filter called name.
*
* @param name
* @return MarkerFilter
*/
protected abstract MarkerFilter createFilter(String name);
/**
* Return the memento tag for the receiver.
*
* @return String
*/
protected abstract String getSectionTag();
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
clipboard = new Clipboard(parent.getDisplay());
super.createPartControl(parent);
initDragAndDrop();
getSite().getPage().addSelectionListener(focusListener);
focusSelectionChanged(getSite().getPage().getActivePart(), getSite()
.getPage().getSelection());
PlatformUI.getWorkbench().getWorkingSetManager()
.addPropertyChangeListener(getWorkingSetListener());
// Set help on the view itself
getViewer().getControl().addHelpListener(new HelpListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.HelpListener#helpRequested(org.eclipse.swt.events.HelpEvent)
*/
public void helpRequested(HelpEvent e) {
IContext context = contextProvider.getContext(getViewer()
.getControl());
PlatformUI.getWorkbench().getHelpSystem().displayHelp(context);
}
});
// Hook up to the resource changes after all widget have been created
ResourcesPlugin.getWorkspace().addResourceChangeListener(
markerUpdateListener,
IResourceChangeEvent.POST_CHANGE
| IResourceChangeEvent.PRE_BUILD
| IResourceChangeEvent.POST_BUILD);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class adaptable) {
if (adaptable.equals(IContextProvider.class)) {
return contextProvider;
}
if (adaptable.equals(IShowInSource.class)) {
return new IShowInSource() {
public ShowInContext getShowInContext() {
ISelection selection = getViewer().getSelection();
if (!(selection instanceof IStructuredSelection)) {
return null;
}
IStructuredSelection structured = (IStructuredSelection) selection;
Iterator markerIterator = structured.iterator();
List newSelection = new ArrayList();
while (markerIterator.hasNext()) {
ConcreteMarker element = (ConcreteMarker) markerIterator
.next();
newSelection.add(element.getResource());
}
return new ShowInContext(getViewer().getInput(),
new StructuredSelection(newSelection));
}
};
}
return super.getAdapter(adaptable);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.markers.internal.TableView#viewerSelectionChanged(org.eclipse.jface.viewers.IStructuredSelection)
*/
protected void viewerSelectionChanged(IStructuredSelection selection) {
Object[] rawSelection = selection.toArray();
List markers = new ArrayList();
for (int idx = 0; idx < rawSelection.length; idx++) {
if (rawSelection[idx] instanceof ConcreteMarker)
markers.add(((ConcreteMarker) rawSelection[idx]).getMarker());
}
setSelection(new StructuredSelection(markers));
updateStatusMessage(selection);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#dispose()
*/
public void dispose() {
super.dispose();
cancelJobs();
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
markerUpdateListener);
PlatformUI.getWorkbench().getWorkingSetManager()
.removePropertyChangeListener(workingSetListener);
IDEWorkbenchPlugin.getDefault().getPreferenceStore()
.removePropertyChangeListener(preferenceListener);
getSite().getPage().removeSelectionListener(focusListener);
// dispose of selection provider actions (may not have been created yet
// if createPartControls was never called)
if (openAction != null) {
openAction.dispose();
copyAction.dispose();
selectAllAction.dispose();
deleteAction.dispose();
revealAction.dispose();
propertiesAction.dispose();
undoAction.dispose();
redoAction.dispose();
clipboard.dispose();
}
if (showInMenu != null) {
showInMenu.dispose();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#createActions()
*/
protected void createActions() {
revealAction = new ActionRevealMarker(this, getViewer());
openAction = new ActionOpenMarker(this, getViewer());
copyAction = new ActionCopyMarker(this, getViewer());
copyAction.setClipboard(clipboard);
copyAction.setProperties(getSortingFields());
pasteAction = new ActionPasteMarker(this, getViewer(), getMarkerName());
pasteAction.setClipboard(clipboard);
pasteAction.setPastableTypes(getMarkerTypes());
deleteAction = new ActionRemoveMarker(this, getViewer(),
getMarkerName());
selectAllAction = new ActionSelectAll(this);
propertiesAction = new ActionMarkerProperties(this, getViewer(),
getMarkerName());
IUndoContext undoContext = getUndoContext();
undoAction = new UndoActionHandler(getSite(), undoContext);
redoAction = new RedoActionHandler(getSite(), undoContext);
super.createActions();
setFilterAction(new FiltersAction(this));
setPreferencesAction(new ViewPreferencesAction() {
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.preferences.ViewPreferencesAction#openViewPreferencesDialog()
*/
public void openViewPreferencesDialog() {
openPreferencesDialog(getMarkerEnablementPreferenceName(),
getMarkerLimitPreferenceName());
}
});
}
/**
* Open a dialog to set the preferences.
*
* @param markerEnablementPreferenceName
* @param markerLimitPreferenceName
*/
private void openPreferencesDialog(String markerEnablementPreferenceName,
String markerLimitPreferenceName) {
Dialog dialog = new MarkerViewPreferenceDialog(getSite()
.getWorkbenchWindow().getShell(),
markerEnablementPreferenceName, markerLimitPreferenceName,
MarkerMessages.MarkerPreferences_DialogTitle);
if (dialog.open() == Window.OK) {
refreshViewer();
}
}
/**
* Get the name of the marker enablement preference.
*
* @return String
*/
abstract String getMarkerLimitPreferenceName();
abstract String[] getMarkerTypes();
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#initToolBar(org.eclipse.jface.action.IToolBarManager)
*/
protected void initToolBar(IToolBarManager tbm) {
tbm.add(deleteAction);
tbm.add(getFilterAction());
tbm.update(false);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#registerGlobalActions(org.eclipse.ui.IActionBars)
*/
protected void registerGlobalActions(IActionBars actionBars) {
copyAction.setActionDefinitionId("org.eclipse.ui.edit.copy"); //$NON-NLS-1$
pasteAction.setActionDefinitionId("org.eclipse.ui.edit.paste"); //$NON-NLS-1$
deleteAction.setActionDefinitionId("org.eclipse.ui.edit.delete"); //$NON-NLS-1$
selectAllAction.setActionDefinitionId("org.eclipse.ui.edit.selectAll"); //$NON-NLS-1$
propertiesAction
.setActionDefinitionId("org.eclipse.ui.file.properties"); //$NON-NLS-1$
undoAction.setActionDefinitionId("org.eclipse.ui.edit.undo"); //$NON-NLS-1$
redoAction.setActionDefinitionId("org.eclipse.ui.edit.redo"); //$NON-NLS-1$
actionBars.setGlobalActionHandler(ActionFactory.COPY.getId(),
copyAction);
actionBars.setGlobalActionHandler(ActionFactory.PASTE.getId(),
pasteAction);
actionBars.setGlobalActionHandler(ActionFactory.DELETE.getId(),
deleteAction);
actionBars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
selectAllAction);
actionBars.setGlobalActionHandler(ActionFactory.PROPERTIES.getId(),
propertiesAction);
actionBars.setGlobalActionHandler(ActionFactory.UNDO.getId(),
undoAction);
actionBars.setGlobalActionHandler(ActionFactory.REDO.getId(),
redoAction);
}
protected void initDragAndDrop() {
int operations = DND.DROP_COPY;
Transfer[] transferTypes = new Transfer[] {
MarkerTransfer.getInstance(), TextTransfer.getInstance() };
DragSourceListener listener = new DragSourceAdapter() {
public void dragSetData(DragSourceEvent event) {
performDragSetData(event);
}
public void dragFinished(DragSourceEvent event) {
}
};
getViewer().addDragSupport(operations, transferTypes, listener);
}
/**
* The user is attempting to drag marker data. Add the appropriate data to
* the event depending on the transfer type.
*/
private void performDragSetData(DragSourceEvent event) {
if (MarkerTransfer.getInstance().isSupportedType(event.dataType)) {
event.data = getSelectedMarkers();
return;
}
if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
List selection = ((IStructuredSelection) getViewer().getSelection())
.toList();
try {
IMarker[] markers = new IMarker[selection.size()];
selection.toArray(markers);
if (markers != null) {
event.data = copyAction.createMarkerReport(markers);
}
} catch (ArrayStoreException e) {
}
}
}
/**
* Get the array of selected markers.
*
* @return IMarker[]
*/
private IMarker[] getSelectedMarkers() {
Object[] selection = ((IStructuredSelection) getViewer().getSelection())
.toArray();
ArrayList markers = new ArrayList();
for (int i = 0; i < selection.length; i++) {
if (selection[i] instanceof ConcreteMarker) {
markers.add(((ConcreteMarker) selection[i]).getMarker());
}
}
return (IMarker[]) markers.toArray(new IMarker[markers.size()]);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#fillContextMenu(org.eclipse.jface.action.IMenuManager)
*/
protected void fillContextMenu(IMenuManager manager) {
if (manager == null) {
return;
}
manager.add(openAction);
createShowInMenu(manager);
manager.add(new Separator());
manager.add(copyAction);
pasteAction.updateEnablement();
manager.add(pasteAction);
if (canBeEditable()) {
manager.add(deleteAction);
}
manager.add(selectAllAction);
fillContextMenuAdditions(manager);
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
manager.add(new Separator());
manager.add(propertiesAction);
}
/**
* Return whether or not any of the types in the receiver can be editable.
*
* @return <code>true</code> if it is possible to have an editable marker
* in this view.
*/
boolean canBeEditable() {
return true;
}
/**
* Fill the context menu for the receiver.
*
* @param manager
*/
abstract void fillContextMenuAdditions(IMenuManager manager);
/**
* Get the filters for the receiver.
*
* @return MarkerFilter[]
*/
protected final MarkerFilter[] getUserFilters() {
return markerFilters;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#handleOpenEvent(org.eclipse.jface.viewers.OpenEvent)
*/
protected void handleOpenEvent(OpenEvent event) {
if (openAction.isEnabled()) {
openAction.run();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.internal.tableview.TableView#saveSelection(org.eclipse.ui.IMemento)
*/
protected void saveSelection(IMemento memento) {
IStructuredSelection selection = (IStructuredSelection) getViewer()
.getSelection();
IMemento selectionMem = memento.createChild(TAG_SELECTION);
for (Iterator iterator = selection.iterator(); iterator.hasNext();) {
Object next = iterator.next();
if (!(next instanceof ConcreteMarker)) {
continue;
}
ConcreteMarker marker = (ConcreteMarker) next;
IMemento elementMem = selectionMem.createChild(TAG_MARKER);
elementMem.putString(TAG_RESOURCE, marker.getMarker().getResource()
.getFullPath().toString());
elementMem.putString(TAG_ID, String.valueOf(marker.getMarker()
.getId()));
}
}
protected abstract String[] getRootTypes();
/**
* @param part
* @param selection
*/
protected void focusSelectionChanged(IWorkbenchPart part,
ISelection selection) {
List selectedElements = new ArrayList();
if (part instanceof IEditorPart) {
IEditorPart editor = (IEditorPart) part;
IFile file = ResourceUtil.getFile(editor.getEditorInput());
if (file == null) {
IEditorInput editorInput = editor.getEditorInput();
if (editorInput != null) {
Object mapping = editorInput
.getAdapter(ResourceMapping.class);
if (mapping != null) {
selectedElements.add(mapping);
}
}
} else {
selectedElements.add(file);
}
} else {
if (selection instanceof IStructuredSelection) {
for (Iterator iterator = ((IStructuredSelection) selection)
.iterator(); iterator.hasNext();) {
Object object = iterator.next();
if (object instanceof IAdaptable) {
ITaskListResourceAdapter taskListResourceAdapter;
Object adapter = ((IAdaptable) object)
.getAdapter(ITaskListResourceAdapter.class);
if (adapter != null
&& adapter instanceof ITaskListResourceAdapter) {
taskListResourceAdapter = (ITaskListResourceAdapter) adapter;
} else {
taskListResourceAdapter = DefaultMarkerResourceAdapter
.getDefault();
}
IResource resource = taskListResourceAdapter
.getAffectedResource((IAdaptable) object);
if (resource == null) {
Object mapping = ((IAdaptable) object)
.getAdapter(ResourceMapping.class);
if (mapping != null) {
selectedElements.add(mapping);
}
} else {
selectedElements.add(resource);
}
}
}
}
}
updateFocusMarkers(selectedElements.toArray());
}
/**
* Update the focus resources of the filters.
*
* @param elements
*/
protected final void updateFilterSelection(Object[] elements) {
Collection resourceCollection = new ArrayList();
for (int i = 0; i < elements.length; i++) {
if (elements[i] instanceof IResource) {
resourceCollection.add(elements[i]);
} else {
addResources(resourceCollection,
((ResourceMapping) elements[i]));
}
}
IResource[] resources = new IResource[resourceCollection.size()];
resourceCollection.toArray(resources);
for (int i = 0; i < markerFilters.length; i++) {
markerFilters[i].setFocusResource(resources);
}
Iterator systemFilters = MarkerSupportRegistry.getInstance()
.getRegisteredFilters().iterator();
while (systemFilters.hasNext()) {
MarkerFilter filter = (MarkerFilter) systemFilters.next();
filter.setFocusResource(resources);
}
}
/**
* Add the resources for the mapping to resources.
*
* @param resources
* @param mapping
*/
private void addResources(Collection resources, ResourceMapping mapping) {
try {
ResourceTraversal[] traversals = mapping.getTraversals(
ResourceMappingContext.LOCAL_CONTEXT,
new NullProgressMonitor());
for (int i = 0; i < traversals.length; i++) {
ResourceTraversal traversal = traversals[i];
IResource[] result = traversal.getResources();
for (int j = 0; j < result.length; j++) {
resources.add(result[j]);
}
}
} catch (CoreException e) {
Util.log(e);
return;
}
}
protected abstract String getStaticContextId();
/**
* Update the focus markers for the supplied elements.
*
* @param elements
*/
void updateFocusMarkers(Object[] elements) {
boolean updateNeeded = updateNeeded(focusElements, elements);
if (updateNeeded) {
focusElements = elements;
refreshForFocusUpdate();
}
}
private boolean updateNeeded(Object[] oldElements, Object[] newElements) {
// determine if an update if refiltering is required
MarkerFilter[] filters = getEnabledFilters();
boolean updateNeeded = false;
for (int i = 0; i < filters.length; i++) {
MarkerFilter filter = filters[i];
if (!filter.isEnabled()) {
continue;
}
int onResource = filter.getOnResource();
if (onResource == MarkerFilter.ON_ANY
|| onResource == MarkerFilter.ON_WORKING_SET) {
continue;
}
if (newElements == null || newElements.length < 1) {
continue;
}
if (oldElements == null || oldElements.length < 1) {
return true;
}
if (Arrays.equals(oldElements, newElements)) {
continue;
}
if (onResource == MarkerFilter.ON_ANY_IN_SAME_CONTAINER) {
Collection oldProjects = MarkerFilter
.getProjectsAsCollection(oldElements);
Collection newProjects = MarkerFilter
.getProjectsAsCollection(newElements);
if (oldProjects.size() == newProjects.size()) {
if (newProjects.containsAll(oldProjects)) {
continue;
}
}
return true;
}
updateNeeded = true;// We are updating as there is nothing to stop
// us
}
return updateNeeded;
}
void updateTitle() {
String status = Util.EMPTY_STRING;
int filteredCount = getCurrentMarkers().getItemCount();
int totalCount = getTotalMarkers();
if (filteredCount == totalCount) {
status = NLS.bind(MarkerMessages.filter_itemsMessage, new Integer(
totalCount));
} else {
status = NLS.bind(MarkerMessages.filter_matchedMessage,
new Integer(filteredCount), new Integer(totalCount));
}
setContentDescription(status);
}
/**
* Updates the message displayed in the status line. This method is invoked
* in the following cases:
* <ul>
* <li>when this view is first created</li>
* <li>when new elements are added</li>
* <li>when something is deleted</li>
* <li>when the filters change</li>
* </ul>
* <p>
* By default, this method calls
* <code>updateStatusMessage(IStructuredSelection)</code> with the current
* selection or <code>null</code>. Classes wishing to override this
* functionality, should just override the method
* <code>updateStatusMessage(IStructuredSelection)</code>.
* </p>
*/
protected void updateStatusMessage() {
ISelection selection = getViewer().getSelection();
if (selection instanceof IStructuredSelection) {
updateStatusMessage((IStructuredSelection) selection);
} else {
updateStatusMessage(null);
}
}
/**
* Updates that message displayed in the status line. If the selection
* parameter is <code>null</code> or its size is 0, the status area is
* blanked out. If only 1 marker is selected, the status area is updated
* with the contents of the message attribute of this marker. In other cases
* (more than one marker is selected) the status area indicates how many
* items have been selected.
* <p>
* This method may be overwritten.
* </p>
* <p>
* This method is called whenever a selection changes in this view.
* </p>
*
* @param selection
* a valid selection or <code>null</code>
*/
protected void updateStatusMessage(IStructuredSelection selection) {
String message = ""; //$NON-NLS-1$
if (selection == null || selection.size() == 0) {
// Show stats on all items in the view
message = updateSummaryVisible();
} else if (selection.size() == 1) {
// Use the Message attribute of the marker
Object first = selection.getFirstElement();
if (first instanceof ConcreteMarker) {
message = ((ConcreteMarker) first).getDescription();
}
} else if (selection.size() > 1) {
// Show stats on only those items in the selection
message = updateSummarySelected(selection);
}
getViewSite().getActionBars().getStatusLineManager()
.setMessage(message);
}
/**
* @param selection
* @return the summary status message
*/
protected String updateSummarySelected(IStructuredSelection selection) {
// Show how many items selected
return MessageFormat.format(
MarkerMessages.marker_statusSummarySelected,
new Object[] { new Integer(selection.size()) });
}
/**
* @return the update summary
*/
protected String updateSummaryVisible() {
return ""; //$NON-NLS-1$
}
/**
* Open a dialog on the filters
*
*/
public final void openFiltersDialog() {
DialogMarkerFilter dialog = createFiltersDialog();
if (dialog.open() == Window.OK) {
MarkerFilter[] result = dialog.getFilters();
if (result == null) {
return;
}
if (result.length == 0) {
setFilters(new MarkerFilter[] { createFilter(MarkerMessages.MarkerFilter_defaultFilterName) });
} else {
setFilters(result);
}
updateForFilterChanges();
}
}
/**
* Refresh the contents of the viewer.
*/
public void refreshViewer() {
scheduleMarkerUpdate(Util.SHORT_DELAY);
}
/**
* Set the filters to newFilters.
*
* @param newFilters
*/
void setFilters(MarkerFilter[] newFilters) {
markerFilters = newFilters;
}
/**
* Clear the cache of enabled filters.
*
*/
void clearEnabledFilters() {
enabledFilters = null;
}
/**
* Refresh the contents of the filter sub menu.
*/
private void refreshFilterMenu() {
if (filtersMenu == null) {
return;
}
filtersMenu.removeAll();
MarkerFilter[] filters = getAllFilters();
for (int i = 0; i < filters.length; i++) {
filtersMenu.add(new FilterEnablementAction(filters[i], this));
}
}
/**
* Open a filter dialog on the receiver.
*/
protected abstract DialogMarkerFilter createFiltersDialog();
/**
* Given a selection of IMarker, reveals the corresponding elements in the
* viewer
*
* @param structuredSelection
* @param reveal
*/
public void setSelection(IStructuredSelection structuredSelection,
boolean reveal) {
TreeViewer viewer = getViewer();
List newSelection = new ArrayList(structuredSelection.size());
for (Iterator i = structuredSelection.iterator(); i.hasNext();) {
Object next = i.next();
if (next instanceof IMarker) {
ConcreteMarker marker = getCurrentMarkers().getMarker(
(IMarker) next);
if (marker != null) {
newSelection.add(marker);
}
}
}
if (viewer != null) {
viewer.setSelection(new StructuredSelection(newSelection), reveal);
}
}
protected MarkerList getVisibleMarkers() {
return getCurrentMarkers();
}
/**
* Returns the total number of markers. Should not be called while the
* marker list is still updating.
*
* @return the total number of markers in the workspace (including
* everything that doesn't pass the filters)
*/
int getTotalMarkers() {
// The number of visible markers should never exceed the total number of
// markers in
// the workspace. If this assertation fails, it probably indicates some
// sort of concurrency problem
// (most likely, getTotalMarkers was called while we were still
// computing the marker lists)
// Assert.isTrue(totalMarkers >= currentMarkers.getItemCount());
return totalMarkers;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.part.WorkbenchPart#showBusy(boolean)
*/
public void showBusy(boolean busy) {
super.showBusy(busy);
if (busy) {
preBusyMarkers = totalMarkers;
} else {// Only bold if there has been a change in count
if (totalMarkers != preBusyMarkers) {
getProgressService().warnOfContentChange();
}
}
}
/**
* Get the filters that are currently enabled.
*
* @return MarkerFilter[]
*/
MarkerFilter[] getEnabledFilters() {
if (enabledFilters == null) {
Collection filters = findEnabledFilters();
enabledFilters = new MarkerFilter[filters.size()];
filters.toArray(enabledFilters);
}
return enabledFilters;
}
/**
* Find the filters enabled in the view.
*
* @return Collection of MarkerFilter
*/
protected Collection findEnabledFilters() {
MarkerFilter[] allFilters = getAllFilters();
ArrayList filters = new ArrayList(0);
for (int i = 0; i < allFilters.length; i++) {
if (allFilters[i].isEnabled()) {
filters.add(allFilters[i]);
}
}
return filters;
}
/**
* Get all of the filters applied to the receiver.
*
* @return MarkerFilter[]
*/
MarkerFilter[] getAllFilters() {
return getUserFilters();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.markers.internal.TableView#addDropDownContributions(org.eclipse.jface.action.IMenuManager)
*/
void addDropDownContributions(IMenuManager menu) {
super.addDropDownContributions(menu);
menu.add(new Separator(MENU_FILTERS_GROUP));
// Don't add in the filters until they are set
filtersMenu = new MenuManager(MarkerMessages.filtersSubMenu_title);
refreshFilterMenu();
menu.appendToGroup(MENU_FILTERS_GROUP, filtersMenu);
}
/**
* Create the show in menu if there is a single selection.
*
* @param menu
*/
void createShowInMenu(IMenuManager menu) {
ISelection selection = getViewer().getSelection();
if (!(selection instanceof IStructuredSelection)) {
return;
}
IStructuredSelection structured = (IStructuredSelection) selection;
if (!Util.isSingleConcreteSelection(structured)) {
return;
}
menu.add(new Separator(MENU_SHOW_IN_GROUP));
// Don't add in the filters until they are set
String showInLabel = IDEWorkbenchMessages.Workbench_showIn;
IBindingService bindingService = (IBindingService) PlatformUI
.getWorkbench().getAdapter(IBindingService.class);
if (bindingService != null) {
String keyBinding = bindingService
.getBestActiveBindingFormattedFor("org.eclipse.ui.navigate.showInQuickMenu"); //$NON-NLS-1$
if (keyBinding != null) {
showInLabel += '\t' + keyBinding;
}
}
showInMenu = new MenuManager(showInLabel);
showInMenu.add(ContributionItemFactory.VIEWS_SHOW_IN
.create(getViewSite().getWorkbenchWindow()));
menu.appendToGroup(MENU_SHOW_IN_GROUP, showInMenu);
}
/**
* Refresh the marker counts
*
* @param monitor
*/
void refreshMarkerCounts(IProgressMonitor monitor) {
monitor.subTask(MarkerMessages.MarkerView_refreshing_counts);
try {
totalMarkers = MarkerList.compute(getMarkerTypes()).length;
} catch (CoreException e) {
Util.log(e);
return;
}
}
/**
* Returns the marker limit or -1 if unlimited
*
* @return int
*/
int getMarkerLimit() {
// If limits are enabled return it. Otherwise return -1
if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(
getMarkerEnablementPreferenceName())) {
return IDEWorkbenchPlugin.getDefault().getPreferenceStore().getInt(
getMarkerLimitPreferenceName());
}
return -1;
}
/**
* Get the name of the marker limit preference.
*
* @return String
*/
abstract String getMarkerEnablementPreferenceName();
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.markers.internal.TableView#createViewerInput()
*/
Object createViewerInput() {
adapter = new MarkerAdapter(this);
return adapter;
}
/**
* Add a listener for the end of the update.
*
* @param listener
*/
public void addUpdateFinishListener(IJobChangeListener listener) {
updateJob.addJobChangeListener(listener);
}
/**
* Remove a listener for the end of the update.
*
* @param listener
*/
public void removeUpdateFinishListener(IJobChangeListener listener) {
updateJob.removeJobChangeListener(listener);
}
/**
* Create a listener for working set changes.
*
* @return IPropertyChangeListener
*/
private IPropertyChangeListener getWorkingSetListener() {
workingSetListener = new IPropertyChangeListener() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
clearEnabledFilters();
refreshViewer();
}
};
return workingSetListener;
}
/**
* Schedule an update of the markers with a delay of time
*
* @param time
*/
void scheduleMarkerUpdate(int time) {
cancelJobs();
getProgressService().schedule(markerProcessJob, time);
}
/**
* Cancel the pending jobs in the receiver.
*/
private void cancelJobs() {
markerProcessJob.cancel();
updateJob.cancel();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.ui.views.markers.internal.TableView#createTree(org.eclipse.swt.widgets.Composite)
*/
protected Tree createTree(Composite parent) {
Tree tree = super.createTree(parent);
tree.addTreeListener(new TreeAdapter() {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.TreeAdapter#treeCollapsed(org.eclipse.swt.events.TreeEvent)
*/
public void treeCollapsed(TreeEvent e) {
updateJob.removeExpandedCategory((MarkerCategory) e.item
.getData());
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.TreeAdapter#treeExpanded(org.eclipse.swt.events.TreeEvent)
*/
public void treeExpanded(TreeEvent e) {
updateJob
.addExpandedCategory((MarkerCategory) e.item.getData());
}
});
return tree;
}
/**
* The focus elements have changed. Update accordingly.
*/
private void refreshForFocusUpdate() {
if (focusElements != null) {
updateFilterSelection(focusElements);
refreshViewer();
}
}
/**
* Save the current selection in the update for reselection after update.
*/
protected void preserveSelection() {
updateJob.saveSelection(getViewer().getSelection());
}
/**
* Return the string name of the specific type of marker shown in this view.
*/
protected abstract String getMarkerName();
/**
* Return the undo context associated with operations performed in this
* view. By default, return the workspace undo context. Subclasses should
* override if a more specific undo context should be used.
*/
protected IUndoContext getUndoContext() {
return (IUndoContext) ResourcesPlugin.getWorkspace().getAdapter(
IUndoContext.class);
}
}