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