001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.views.tasklist;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.List;
017: import java.util.Set;
018:
019: import org.eclipse.core.resources.IMarker;
020: import org.eclipse.core.resources.IMarkerDelta;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.resources.IResourceChangeEvent;
023: import org.eclipse.core.resources.IResourceChangeListener;
024: import org.eclipse.core.resources.IResourceDelta;
025: import org.eclipse.core.runtime.CoreException;
026: import org.eclipse.jface.viewers.IStructuredContentProvider;
027: import org.eclipse.jface.viewers.IStructuredSelection;
028: import org.eclipse.jface.viewers.TableViewer;
029: import org.eclipse.jface.viewers.Viewer;
030: import org.eclipse.osgi.util.NLS;
031: import org.eclipse.swt.widgets.Control;
032: import org.eclipse.ui.internal.views.tasklist.TaskListMessages;
033:
034: /**
035: * Task list content provider returns elements that should be
036: * in the task list depending on the selection mode.
037: * It goes directly to the marker manager and retreives
038: * tasks and problems.
039: */
040: class TaskListContentProvider implements IStructuredContentProvider,
041: IResourceChangeListener {
042:
043: private static final int TASKS = 0;
044:
045: private static final int ERRORS = 1;
046:
047: private static final int WARNINGS = 2;
048:
049: private static final int INFOS = 3;
050:
051: private TaskList taskList;
052:
053: private TableViewer viewer;
054:
055: private IResource input;
056:
057: /* cached counts of tasks, errors, warnings and infos for the visible
058: * markers, maintained incrementally */
059: private int[] visibleMarkerCounts = null;
060:
061: /* cached count of all markers in workspace matching supported root types
062: * (tasks & problems), maintained incrementally */
063: private int totalMarkerCount = -1;
064:
065: /**
066: * The constructor.
067: */
068: public TaskListContentProvider(TaskList taskList) {
069: this .taskList = taskList;
070: this .viewer = taskList.getTableViewer();
071: }
072:
073: private boolean getFilterOnMarkerLimit() {
074: return taskList.getFilter().getFilterOnMarkerLimit();
075: }
076:
077: private int getMarkerLimit() {
078: return taskList.getFilter().getMarkerLimit();
079: }
080:
081: private boolean isMarkerLimitExceeded() {
082: return taskList.isMarkerLimitExceeded();
083: }
084:
085: private void setMarkerLimitExceeded(boolean markerLimitExceeded) {
086: taskList.setMarkerLimitExceeded(markerLimitExceeded);
087: }
088:
089: /**
090: * Returns a one-line string containing a summary of the number
091: * of visible tasks and problems.
092: */
093: public String getStatusSummaryVisible() {
094: if (visibleMarkerCounts == null) {
095: return ""; //$NON-NLS-1$
096: }
097:
098: return NLS.bind(TaskListMessages.TaskList_statusSummaryVisible,
099: new Integer(sum(visibleMarkerCounts)),
100: getStatusSummaryBreakdown(visibleMarkerCounts));
101: }
102:
103: /**
104: * Returns a one-line string containing a summary of the number
105: * of selected tasks and problems.
106: *
107: * @param selection the current selection
108: */
109: public String getStatusSummarySelected(
110: IStructuredSelection selection) {
111: int[] selectedMarkerCounts = getMarkerCounts(selection.toList());
112: return NLS.bind(
113: TaskListMessages.TaskList_statusSummarySelected,
114: new Integer(sum(selectedMarkerCounts)),
115: getStatusSummaryBreakdown(selectedMarkerCounts));
116: }
117:
118: /**
119: * Returns a one-line string containing a summary of the number of
120: * given tasks, errors, warnings, and infos.
121: */
122: private String getStatusSummaryBreakdown(int[] counts) {
123: return NLS.bind(
124: TaskListMessages.TaskList_statusSummaryBreakdown,
125: new Object[] { new Integer(counts[TASKS]),
126: new Integer(counts[ERRORS]),
127: new Integer(counts[WARNINGS]),
128: new Integer(counts[INFOS]) });
129: }
130:
131: /**
132: * Returns a one-line string containing a summary of the number items
133: * being shown by the filter, for display in the title bar.
134: */
135: public String getTitleSummary() {
136: if (visibleMarkerCounts == null) {
137: return ""; //$NON-NLS-1$
138: }
139:
140: int visibleMarkerCount = sum(visibleMarkerCounts);
141: TasksFilter filter = taskList.getFilter();
142:
143: if (filter.isShowingAll()) {
144: return NLS.bind(
145: TaskListMessages.TaskList_titleSummaryUnfiltered,
146: new Integer(visibleMarkerCount));
147: } else {
148: return NLS.bind(
149: TaskListMessages.TaskList_titleSummaryFiltered,
150: new Integer(visibleMarkerCount), new Integer(
151: getTotalMarkerCount()));
152: }
153: }
154:
155: /**
156: * Returns the sum of the given counts.
157: */
158: private int sum(int[] counts) {
159: int sum = 0;
160:
161: for (int i = 0, l = counts.length; i < l; ++i) {
162: sum += counts[i];
163: }
164:
165: return sum;
166: }
167:
168: /**
169: * Returns the count of all markers in the workspace which can be shown in
170: * the task list. This is computed once, then maintained incrementally by
171: * the delta processing.
172: */
173: private int getTotalMarkerCount() {
174: if (totalMarkerCount == -1) {
175: totalMarkerCount = 0;
176:
177: try {
178: IResource root = taskList.getWorkspace().getRoot();
179: IMarker[] markers = root.findMarkers(null, true,
180: IResource.DEPTH_INFINITE);
181:
182: for (int i = 0; i < markers.length; ++i) {
183: if (isRootType(markers[i])) {
184: ++totalMarkerCount;
185: }
186: }
187: } catch (CoreException e) {
188: // shouldn't happen
189: }
190: }
191:
192: return totalMarkerCount;
193: }
194:
195: /**
196: * Returns whether the given marker is a subtype of one of the root types.
197: */
198: private boolean isRootType(IMarker marker) {
199: String[] rootTypes = TasksFilter.ROOT_TYPES;
200:
201: for (int i = 0, l = rootTypes.length; i < l; ++i) {
202: if (MarkerUtil.isMarkerType(marker, rootTypes[i])) {
203: return true;
204: }
205: }
206:
207: return false;
208: }
209:
210: /**
211: * Returns the markers to show in the task list.
212: */
213: private IMarker[] getMarkers() throws CoreException {
214: IResource[] resources = taskList.getResources();
215: int l = resources.length;
216: IResource resource;
217: boolean bExists = false;
218:
219: for (int i = 0; i < l; i++) {
220: resource = resources[i];
221:
222: if (resource != null && resource.exists()) {
223: bExists = true;
224: break;
225: }
226: }
227:
228: if (!bExists) {
229: return new IMarker[0];
230: }
231:
232: if (taskList.showOwnerProject()) {
233: IResource[] projectResources = new IResource[l];
234: IResource project;
235:
236: for (int i = 0; i < l; i++) {
237: resource = resources[i];
238:
239: if (resource != null) {
240: project = resource.getProject();
241:
242: if (project != null) {
243: projectResources[i] = project;
244: } else {
245: projectResources[i] = resource;
246: }
247: }
248: }
249:
250: resources = projectResources;
251: }
252:
253: int depth = taskList.getResourceDepth();
254: TasksFilter filter = taskList.getFilter();
255: Set set = new HashSet();
256:
257: for (int i = 0; i < l; i++) {
258: resource = resources[i];
259:
260: if (resource != null) {
261: IMarker[] markers = resource.findMarkers(null, true,
262: depth);
263:
264: for (int j = 0; j < markers.length; ++j) {
265: IMarker marker = markers[j];
266:
267: if (filter.select(marker)) {
268: set.add(marker);
269: }
270: }
271: }
272: }
273:
274: IMarker[] result = new IMarker[set.size()];
275: set.toArray(result);
276: return result;
277: }
278:
279: /**
280: * Returns the number of tasks, errors, warnings, infos
281: * in the given markers.
282: */
283: private int[] getMarkerCounts(List markers) {
284: int[] markerCounts = new int[4];
285: Iterator iterator = markers.iterator();
286:
287: while (iterator.hasNext()) {
288: IMarker marker = (IMarker) iterator.next();
289:
290: if (MarkerUtil.isMarkerType(marker, IMarker.PROBLEM)) {
291: switch (MarkerUtil.getSeverity(marker)) {
292: case IMarker.SEVERITY_ERROR:
293: ++markerCounts[ERRORS];
294: break;
295: case IMarker.SEVERITY_WARNING:
296: ++markerCounts[WARNINGS];
297: break;
298: case IMarker.SEVERITY_INFO:
299: ++markerCounts[INFOS];
300: break;
301: }
302: } else if (MarkerUtil.isMarkerType(marker, IMarker.TASK)) {
303: ++markerCounts[TASKS];
304: }
305: }
306:
307: return markerCounts;
308: }
309:
310: /**
311: * Updates the marker counts for the given delta.
312: * Assumptions:
313: * - the delta is either an addition or a removal
314: * - problem severities don't change
315: */
316: private void updateMarkerCounts(IMarkerDelta markerDelta,
317: int difference) {
318: if (visibleMarkerCounts == null) {
319: return;
320: }
321:
322: if (markerDelta.isSubtypeOf(IMarker.PROBLEM)) {
323: int severity = markerDelta.getAttribute(IMarker.SEVERITY,
324: IMarker.SEVERITY_WARNING);
325:
326: switch (severity) {
327: case IMarker.SEVERITY_ERROR:
328: visibleMarkerCounts[ERRORS] += difference;
329: break;
330: case IMarker.SEVERITY_WARNING:
331: visibleMarkerCounts[WARNINGS] += difference;
332: break;
333: case IMarker.SEVERITY_INFO:
334: visibleMarkerCounts[INFOS] += difference;
335: break;
336: }
337: } else if (markerDelta.isSubtypeOf(IMarker.TASK)) {
338: visibleMarkerCounts[TASKS] += difference;
339: }
340: }
341:
342: /**
343: * Updates the viewer given the lists of added, removed, and changes
344: * markers. This is called inside an syncExec.
345: */
346: private void updateViewer(List additions, List removals,
347: List changes) {
348:
349: // The widget may have been destroyed by the time this is run.
350: // Check for this and do nothing if so.
351: Control ctrl = viewer.getControl();
352:
353: if (ctrl == null || ctrl.isDisposed()) {
354: return;
355: }
356:
357: //update the viewer based on the marker changes.
358: //process removals before additions, to avoid multiple equal elements in
359: //the viewer
360: if (removals.size() > 0) {
361:
362: // Cancel any open cell editor. We assume that the one being edited
363: // is the one being removed.
364: viewer.cancelEditing();
365: viewer.remove(removals.toArray());
366: }
367:
368: if (additions.size() > 0) {
369: viewer.add(additions.toArray());
370: }
371:
372: if (changes.size() > 0) {
373: viewer.update(changes.toArray(), null);
374: }
375: }
376:
377: /**
378: * The visual part that is using this content provider is about
379: * to be disposed. Deallocate all allocated SWT resources.
380: */
381: public void dispose() {
382: if (input != null) {
383: input.getWorkspace().removeResourceChangeListener(this );
384: input = null;
385: }
386: }
387:
388: public void inputChanged(Viewer viewer, Object oldInput,
389: Object newInput) {
390: if (this .input != null) {
391: this .input.getWorkspace()
392: .removeResourceChangeListener(this );
393: }
394:
395: this .input = (IResource) newInput;
396:
397: if (this .input != null) {
398: this .input.getWorkspace().addResourceChangeListener(this ,
399: IResourceChangeEvent.POST_CHANGE);
400: }
401:
402: this .viewer = (TableViewer) viewer;
403: }
404:
405: /**
406: * Returns all the markers that should be shown for
407: * the current settings.
408: */
409: public Object[] getElements(Object parent) {
410: try {
411: IMarker[] markers = getMarkers();
412: this .visibleMarkerCounts = getMarkerCounts(Arrays
413: .asList(markers));
414:
415: if (getFilterOnMarkerLimit()
416: && markers.length > getMarkerLimit()) {
417: if (!isMarkerLimitExceeded()) {
418: setMarkerLimitExceeded(true);
419:
420: viewer.getControl().getDisplay().syncExec(
421: new Runnable() {
422: public void run() {
423: viewer.refresh();
424: }
425: });
426: }
427:
428: return new IMarker[0];
429: } else {
430: if (isMarkerLimitExceeded()) {
431: setMarkerLimitExceeded(false);
432:
433: viewer.getControl().getDisplay().syncExec(
434: new Runnable() {
435: public void run() {
436: viewer.refresh();
437: }
438: });
439: }
440:
441: return markers;
442: }
443: } catch (CoreException e) {
444: return new IMarker[0];
445: }
446: }
447:
448: /**
449: * The workbench has changed. Process the delta and issue updates to the
450: * viewer, inside the UI thread.
451: *
452: * @see IResourceChangeListener#resourceChanged
453: */
454: public void resourceChanged(final IResourceChangeEvent event) {
455: /*
456: * gather all marker changes from the delta. be sure to do this in the
457: * calling thread, as the delta is destroyed when this method returns
458: */
459: IMarkerDelta[] markerDeltas = event
460: .findMarkerDeltas(null, true);
461:
462: if (markerDeltas == null) {
463: return;
464: }
465:
466: int oldTotal = totalMarkerCount;
467: final List additions = new ArrayList();
468: final List removals = new ArrayList();
469: final List changes = new ArrayList();
470:
471: for (int i = 0; i < markerDeltas.length; i++) {
472: IMarkerDelta markerDelta = markerDeltas[i];
473:
474: if (markerDelta == null) {
475: continue;
476: }
477:
478: int iKind = markerDelta.getKind();
479:
480: for (int j = 0; j < TasksFilter.ROOT_TYPES.length; j++) {
481: if (markerDelta.isSubtypeOf(TasksFilter.ROOT_TYPES[j])) {
482:
483: /*
484: * Updates the total count of markers given the applicable
485: * marker deltas.
486: */
487: if (totalMarkerCount != -1) {
488: switch (iKind) {
489: case IResourceDelta.ADDED:
490: totalMarkerCount++;
491: break;
492: case IResourceDelta.REMOVED:
493: totalMarkerCount--;
494: break;
495: }
496: }
497:
498: /*
499: * Partition the marker deltas into one of the three given
500: * lists depending on
501: * the type of delta (add, remove, or change).
502: * The resulting lists contain the corresponding markers,
503: * not the deltas.
504: * Deltas which are not under the current focus resource are
505: * discarded.
506: * This also updates the marker counts.
507: */
508:
509: IResource resource = markerDelta.getResource();
510:
511: if (resource == null) {
512: continue;
513: }
514:
515: boolean affectedBy = taskList
516: .checkResource(resource)
517: && taskList.getFilter().select(markerDelta);
518:
519: if (affectedBy) {
520: IMarker marker = markerDelta.getMarker();
521:
522: switch (iKind) {
523: case IResourceDelta.ADDED:
524: additions.add(marker);
525: updateMarkerCounts(markerDelta, +1);
526: break;
527: case IResourceDelta.REMOVED:
528: removals.add(marker);
529: updateMarkerCounts(markerDelta, -1);
530: break;
531: case IResourceDelta.CHANGED:
532: changes.add(marker);
533: /*
534: * Assume attribute changes don't affect marker
535: * counts. This is only true if problem severities
536: * can't change.
537: */
538: break;
539: }
540: }
541:
542: break;
543: }
544: }
545: }
546:
547: if (oldTotal == totalMarkerCount
548: && additions.size() + removals.size() + changes.size() == 0) {
549: // no changes to markers that we care about
550: return;
551: }
552:
553: /*
554: * do the required viewer updates in the UI thread need to use syncExec;
555: * see 1G95PU8: ITPUI:WIN2000 - Changing task description flashes old
556: * description
557: */
558: viewer.getControl().getDisplay().syncExec(new Runnable() {
559: public void run() {
560: if (getFilterOnMarkerLimit()
561: && sum(visibleMarkerCounts) > getMarkerLimit()) {
562: if (!isMarkerLimitExceeded()) {
563: setMarkerLimitExceeded(true);
564: viewer.refresh();
565: }
566: } else if (taskList.isMarkerLimitExceeded()) {
567: setMarkerLimitExceeded(false);
568: viewer.refresh();
569: } else {
570: updateViewer(additions, removals, changes);
571: }
572:
573: /* Update the task list's status message.
574: * XXX: Quick and dirty solution here.
575: * Would be better to have a separate model for the tasks and
576: * have both the content provider and the task list register for
577: * updates. XXX: Do this inside the syncExec, since we're
578: * talking to status line widget.
579: */
580: taskList.markersChanged();
581: }
582: });
583: }
584: }
|