blob: 760675ac59f76057a32ed6cb4885cb9f9948a25b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.tasklist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.internal.views.tasklist.TaskListMessages;
/**
* Task list content provider returns elements that should be
* in the task list depending on the selection mode.
* It goes directly to the marker manager and retreives
* tasks and problems.
*/
class TaskListContentProvider implements IStructuredContentProvider,
IResourceChangeListener {
private static final int TASKS = 0;
private static final int ERRORS = 1;
private static final int WARNINGS = 2;
private static final int INFOS = 3;
private TaskList taskList;
private TableViewer viewer;
private IResource input;
/* cached counts of tasks, errors, warnings and infos for the visible
* markers, maintained incrementally */
private int[] visibleMarkerCounts = null;
/* cached count of all markers in workspace matching supported root types
* (tasks & problems), maintained incrementally */
private int totalMarkerCount = -1;
/**
* The constructor.
*/
public TaskListContentProvider(TaskList taskList) {
this.taskList = taskList;
this.viewer = taskList.getTableViewer();
}
private boolean getFilterOnMarkerLimit() {
return taskList.getFilter().getFilterOnMarkerLimit();
}
private int getMarkerLimit() {
return taskList.getFilter().getMarkerLimit();
}
private boolean isMarkerLimitExceeded() {
return taskList.isMarkerLimitExceeded();
}
private void setMarkerLimitExceeded(boolean markerLimitExceeded) {
taskList.setMarkerLimitExceeded(markerLimitExceeded);
}
/**
* Returns a one-line string containing a summary of the number
* of visible tasks and problems.
*/
public String getStatusSummaryVisible() {
if (visibleMarkerCounts == null) {
return ""; //$NON-NLS-1$
}
return NLS.bind(TaskListMessages.TaskList_statusSummaryVisible,new Integer(sum(visibleMarkerCounts)),
getStatusSummaryBreakdown(visibleMarkerCounts));
}
/**
* Returns a one-line string containing a summary of the number
* of selected tasks and problems.
*
* @param selection the current selection
*/
public String getStatusSummarySelected(IStructuredSelection selection) {
int[] selectedMarkerCounts = getMarkerCounts(selection.toList());
return NLS.bind(TaskListMessages.TaskList_statusSummarySelected, new Integer(sum(selectedMarkerCounts)),
getStatusSummaryBreakdown(selectedMarkerCounts) );
}
/**
* Returns a one-line string containing a summary of the number of
* given tasks, errors, warnings, and infos.
*/
private String getStatusSummaryBreakdown(int[] counts) {
return NLS.bind(
TaskListMessages.TaskList_statusSummaryBreakdown,
new Object []{
new Integer(counts[TASKS]),
new Integer(counts[ERRORS]),
new Integer(counts[WARNINGS]),
new Integer(counts[INFOS])});
}
/**
* Returns a one-line string containing a summary of the number items
* being shown by the filter, for display in the title bar.
*/
public String getTitleSummary() {
if (visibleMarkerCounts == null) {
return ""; //$NON-NLS-1$
}
int visibleMarkerCount = sum(visibleMarkerCounts);
TasksFilter filter = taskList.getFilter();
if (filter.isShowingAll()) {
return NLS.bind(TaskListMessages.TaskList_titleSummaryUnfiltered, new Integer(visibleMarkerCount));
} else {
return NLS.bind(TaskListMessages.TaskList_titleSummaryFiltered, new Integer(visibleMarkerCount),
new Integer(getTotalMarkerCount()));
}
}
/**
* Returns the sum of the given counts.
*/
private int sum(int[] counts) {
int sum = 0;
for (int i = 0, l = counts.length; i < l; ++i) {
sum += counts[i];
}
return sum;
}
/**
* Returns the count of all markers in the workspace which can be shown in
* the task list. This is computed once, then maintained incrementally by
* the delta processing.
*/
private int getTotalMarkerCount() {
if (totalMarkerCount == -1) {
totalMarkerCount = 0;
try {
IResource root = taskList.getWorkspace().getRoot();
IMarker[] markers = root.findMarkers(null, true,
IResource.DEPTH_INFINITE);
for (int i = 0; i < markers.length; ++i) {
if (isRootType(markers[i])) {
++totalMarkerCount;
}
}
} catch (CoreException e) {
// shouldn't happen
}
}
return totalMarkerCount;
}
/**
* Returns whether the given marker is a subtype of one of the root types.
*/
private boolean isRootType(IMarker marker) {
String[] rootTypes = TasksFilter.ROOT_TYPES;
for (int i = 0, l = rootTypes.length; i < l; ++i) {
if (MarkerUtil.isMarkerType(marker, rootTypes[i])) {
return true;
}
}
return false;
}
/**
* Returns the markers to show in the task list.
*/
private IMarker[] getMarkers() throws CoreException {
IResource[] resources = taskList.getResources();
int l = resources.length;
IResource resource;
boolean bExists = false;
for (int i = 0; i < l; i++) {
resource = resources[i];
if (resource != null && resource.exists()) {
bExists = true;
break;
}
}
if (!bExists) {
return new IMarker[0];
}
if (taskList.showOwnerProject()) {
IResource[] projectResources = new IResource[l];
IResource project;
for (int i = 0; i < l; i++) {
resource = resources[i];
if (resource != null) {
project = resource.getProject();
if (project != null) {
projectResources[i] = project;
} else {
projectResources[i] = resource;
}
}
}
resources = projectResources;
}
int depth = taskList.getResourceDepth();
TasksFilter filter = taskList.getFilter();
Set set = new HashSet();
for (int i = 0; i < l; i++) {
resource = resources[i];
if (resource != null) {
IMarker[] markers = resource.findMarkers(null, true, depth);
for (int j = 0; j < markers.length; ++j) {
IMarker marker = markers[j];
if (filter.select(marker)) {
set.add(marker);
}
}
}
}
IMarker[] result = new IMarker[set.size()];
set.toArray(result);
return result;
}
/**
* Returns the number of tasks, errors, warnings, infos
* in the given markers.
*/
private int[] getMarkerCounts(List markers) {
int[] markerCounts = new int[4];
Iterator iterator = markers.iterator();
while (iterator.hasNext()) {
IMarker marker = (IMarker) iterator.next();
if (MarkerUtil.isMarkerType(marker, IMarker.PROBLEM)) {
switch (MarkerUtil.getSeverity(marker)) {
case IMarker.SEVERITY_ERROR:
++markerCounts[ERRORS];
break;
case IMarker.SEVERITY_WARNING:
++markerCounts[WARNINGS];
break;
case IMarker.SEVERITY_INFO:
++markerCounts[INFOS];
break;
}
} else if (MarkerUtil.isMarkerType(marker, IMarker.TASK)) {
++markerCounts[TASKS];
}
}
return markerCounts;
}
/**
* Updates the marker counts for the given delta.
* Assumptions:
* - the delta is either an addition or a removal
* - problem severities don't change
*/
private void updateMarkerCounts(IMarkerDelta markerDelta, int difference) {
if (visibleMarkerCounts == null)
return;
if (markerDelta.isSubtypeOf(IMarker.PROBLEM)) {
int severity = markerDelta.getAttribute(IMarker.SEVERITY,
IMarker.SEVERITY_WARNING);
switch (severity) {
case IMarker.SEVERITY_ERROR:
visibleMarkerCounts[ERRORS] += difference;
break;
case IMarker.SEVERITY_WARNING:
visibleMarkerCounts[WARNINGS] += difference;
break;
case IMarker.SEVERITY_INFO:
visibleMarkerCounts[INFOS] += difference;
break;
}
} else if (markerDelta.isSubtypeOf(IMarker.TASK))
visibleMarkerCounts[TASKS] += difference;
}
/**
* Updates the viewer given the lists of added, removed, and changes
* markers. This is called inside an syncExec.
*/
private void updateViewer(List additions, List removals, List changes) {
// The widget may have been destroyed by the time this is run.
// Check for this and do nothing if so.
Control ctrl = viewer.getControl();
if (ctrl == null || ctrl.isDisposed()) {
return;
}
//update the viewer based on the marker changes.
//process removals before additions, to avoid multiple equal elements in
//the viewer
if (removals.size() > 0) {
// Cancel any open cell editor. We assume that the one being edited
// is the one being removed.
viewer.cancelEditing();
viewer.remove(removals.toArray());
}
if (additions.size() > 0) {
viewer.add(additions.toArray());
}
if (changes.size() > 0) {
viewer.update(changes.toArray(), null);
}
}
/**
* The visual part that is using this content provider is about
* to be disposed. Deallocate all allocated SWT resources.
*/
public void dispose() {
if (input != null) {
input.getWorkspace().removeResourceChangeListener(this);
input = null;
}
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
if (this.input != null) {
this.input.getWorkspace().removeResourceChangeListener(this);
}
this.input = (IResource) newInput;
if (this.input != null) {
this.input.getWorkspace().addResourceChangeListener(this,
IResourceChangeEvent.POST_CHANGE);
}
this.viewer = (TableViewer) viewer;
}
/**
* Returns all the markers that should be shown for
* the current settings.
*/
public Object[] getElements(Object parent) {
try {
IMarker[] markers = getMarkers();
this.visibleMarkerCounts = getMarkerCounts(Arrays.asList(markers));
if (getFilterOnMarkerLimit() && markers.length > getMarkerLimit()) {
if (!isMarkerLimitExceeded()) {
setMarkerLimitExceeded(true);
viewer.getControl().getDisplay().syncExec(new Runnable() {
public void run() {
viewer.refresh();
}
});
}
return new IMarker[0];
} else {
if (isMarkerLimitExceeded()) {
setMarkerLimitExceeded(false);
viewer.getControl().getDisplay().syncExec(new Runnable() {
public void run() {
viewer.refresh();
}
});
}
return markers;
}
} catch (CoreException e) {
return new IMarker[0];
}
}
/**
* The workbench has changed. Process the delta and issue updates to the
* viewer, inside the UI thread.
*
* @see IResourceChangeListener#resourceChanged
*/
public void resourceChanged(final IResourceChangeEvent event) {
/*
* gather all marker changes from the delta. be sure to do this in the
* calling thread, as the delta is destroyed when this method returns
*/
IMarkerDelta[] markerDeltas = event.findMarkerDeltas(null, true);
if (markerDeltas == null)
return;
int oldTotal = totalMarkerCount;
final List additions = new ArrayList();
final List removals = new ArrayList();
final List changes = new ArrayList();
for (int i = 0; i < markerDeltas.length; i++) {
IMarkerDelta markerDelta = markerDeltas[i];
if (markerDelta == null)
continue;
int iKind = markerDelta.getKind();
for (int j = 0; j < TasksFilter.ROOT_TYPES.length; j++) {
if (markerDelta.isSubtypeOf(TasksFilter.ROOT_TYPES[j])) {
/*
* Updates the total count of markers given the applicable
* marker deltas.
*/
if (totalMarkerCount != -1) {
switch (iKind) {
case IResourceDelta.ADDED:
totalMarkerCount++;
break;
case IResourceDelta.REMOVED:
totalMarkerCount--;
break;
}
}
/*
* Partition the marker deltas into one of the three given
* lists depending on
* the type of delta (add, remove, or change).
* The resulting lists contain the corresponding markers,
* not the deltas.
* Deltas which are not under the current focus resource are
* discarded.
* This also updates the marker counts.
*/
IResource resource = markerDelta.getResource();
if (resource == null)
continue;
boolean affectedBy = taskList.checkResource(resource)
&& taskList.getFilter().select(markerDelta);
if (affectedBy) {
IMarker marker = markerDelta.getMarker();
switch (iKind) {
case IResourceDelta.ADDED:
additions.add(marker);
updateMarkerCounts(markerDelta, +1);
break;
case IResourceDelta.REMOVED:
removals.add(marker);
updateMarkerCounts(markerDelta, -1);
break;
case IResourceDelta.CHANGED:
changes.add(marker);
/*
* Assume attribute changes don't affect marker
* counts. This is only true if problem severities
* can't change.
*/
break;
}
}
break;
}
}
}
if (oldTotal == totalMarkerCount
&& additions.size() + removals.size() + changes.size() == 0) {
// no changes to markers that we care about
return;
}
/*
* do the required viewer updates in the UI thread need to use syncExec;
* see 1G95PU8: ITPUI:WIN2000 - Changing task description flashes old
* description
*/
viewer.getControl().getDisplay().syncExec(new Runnable() {
public void run() {
if (getFilterOnMarkerLimit()
&& sum(visibleMarkerCounts) > getMarkerLimit()) {
if (!isMarkerLimitExceeded()) {
setMarkerLimitExceeded(true);
viewer.refresh();
}
} else if (taskList.isMarkerLimitExceeded()) {
setMarkerLimitExceeded(false);
viewer.refresh();
} else {
updateViewer(additions, removals, changes);
}
/* Update the task list's status message.
* XXX: Quick and dirty solution here.
* Would be better to have a separate model for the tasks and
* have both the content provider and the task list register for
* updates. XXX: Do this inside the syncExec, since we're
* talking to status line widget.
*/
taskList.markersChanged();
}
});
}
}