Lightweight decorators
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoration.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoration.java
deleted file mode 100644
index a9f9cd7..0000000
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoration.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.eclipse.team.internal.ccvs.ui;
-
-/*
- * (c) Copyright IBM Corp. 2000, 2002.
- * All Rights Reserved.
- */
- 
-import java.util.List;
-import java.util.Map;
-
-public class CVSDecoration {
-	
-	private String format;
-	private Map bindings;
-	private List overlays;
-	private int[] locations;
-	
-	public CVSDecoration() {
-		this(null, null, null, null);
-	}
-
-	public CVSDecoration(String format, Map bindings, List overlays, int[] locations) {
-		setFormat(format);
-		setBindings(bindings);
-		setOverlays(overlays);
-		setLocations(locations);
-	}
-	
-	public int hashCode() {
-		return overlays.hashCode();
-	}
-	
-	public boolean equals(Object o) {
-		if (!(o instanceof CVSDecoration)) return false;
-		return overlays.equals(((CVSDecoration)o).overlays);
-	}
-	/**
-	 * Gets the overlays.
-	 * @return Returns a List
-	 */
-	public List getOverlays() {
-		return overlays;
-	}
-
-	/**
-	 * Sets the overlays.
-	 * @param overlays The overlays to set
-	 */
-	public void setOverlays(List overlays) {
-		this.overlays = overlays;
-	}
-
-	/**
-	 * Gets the substitutions.
-	 * @return Returns a String[]
-	 */
-	public Map getBindings() {
-		return bindings;
-	}
-
-	/**
-	 * Sets the substitutions.
-	 * @param substitutions The substitutions to set
-	 */
-	public void setBindings(Map bindings) {
-		this.bindings = bindings;
-	}
-
-	/**
-	 * Gets the textBinding.
-	 * @return Returns a String
-	 */
-	public String getFormat() {
-		return format;
-	}
-
-	/**
-	 * Sets the textBinding.
-	 * @param textBinding The textBinding to set
-	 */
-	public void setFormat(String format) {
-		this.format = format;
-	}			
-	/**
-	 * Gets the locations.
-	 * @return Returns a int[]
-	 */
-	public int[] getLocations() {
-		return locations;
-	}
-
-	/**
-	 * Sets the locations.
-	 * @param locations The locations to set
-	 */
-	public void setLocations(int[] locations) {
-		this.locations = locations;
-	}
-}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
deleted file mode 100644
index 94d99ac..0000000
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecorator.java
+++ /dev/null
@@ -1,335 +0,0 @@
-package org.eclipse.team.internal.ccvs.ui;
-
-/*
- * (c) Copyright IBM Corp. 2000, 2001.
- * All Rights Reserved.
- */
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-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.resources.IResourceDeltaVisitor;
-import org.eclipse.core.resources.IResourceVisitor;
-import org.eclipse.core.resources.ResourcesPlugin;
-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.Status;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.jface.viewers.ILabelDecorator;
-import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.jface.viewers.LabelProviderChangedEvent;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.team.core.RepositoryProvider;
-import org.eclipse.team.internal.ccvs.core.CVSException;
-import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
-import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
-import org.eclipse.team.internal.ccvs.core.ICVSFile;
-import org.eclipse.team.internal.ccvs.core.ICVSFolder;
-import org.eclipse.team.internal.ccvs.core.ICVSResource;
-import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
-import org.eclipse.team.internal.ccvs.core.ICVSRunnable;
-import org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener;
-import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
-import org.eclipse.ui.IDecoratorManager;
-
-/**
- * Classes registered with the workbench decoration extension point. The <code>CVSDecorationRunnable</code> class
- * actually calculates the decoration, while this class is responsible for listening to the following sources
- * for indications that the decorators need updating:
- * <ul>
- * 	<li>workbench label requests (decorateText/decorateImage)
- * 	<li>workspace resource change events (resourceChanged)
- * 	<li>cvs state changes (resourceStateChanged)
- * </ul>
- * <p>
- * [Note: There are several optimization that can be implemented in this class: (1) cache something
- * so that computing the dirty state of containers does not imply traversal of all children, (2) improve
- * the queue used between the decorator and the decorator runnable such that priority can be
- * given to visible elements when decoration requests are made.]
- */
-public class CVSDecorator implements IResourceChangeListener, IResourceStateChangeListener {
-	
-	// Resources that need an icon and text computed for display to the user
-	private List decoratorNeedsUpdating = new ArrayList();
-
-	// When decorations are computed they are added to this cache via decorated() method
-	private Map cache = Collections.synchronizedMap(new HashMap());
-
-	// Updater thread, computes decoration labels and images
-	private Thread decoratorUpdateThread;
-
-	private boolean shutdown = false;
-
-	private OverlayIconCache iconCache = new OverlayIconCache();
-	
-	// Keep track of deconfigured projects
-	private Set deconfiguredProjects = new HashSet();
-	
-	/*
-	 * Blanket refresh the displaying of our decorator.
-	 */ 
-	public static void refresh() {
-/*		CVSDecorator activeDecorator = getActiveCVSDecorator();
-		
-		if(activeDecorator == null)
-			return;	//nothing to do, our decorator isn't active
-		activeDecorator.clearCache();  //clear the cache of previous decorations so we can compute them anew
-		
-		//update all displaying of our decorator;
-		activeDecorator.fireLabelProviderChanged(new LabelProviderChangedEvent(activeDecorator));
-*/	}
-	
-	/*
-	 * Answers null if a provider does not exist or the provider is not a CVS provider. These resources
-	 * will be ignored by the decorator.
-	 */
-	private static CVSTeamProvider getCVSProviderFor(IResource resource) {
-		RepositoryProvider p = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
-		if (p == null) {
-			return null;
-		}
-		return (CVSTeamProvider) p;
-	}	
-
-
-	public CVSDecorator() {
-		// thread that calculates the decoration for a resource
-//		decoratorUpdateThread = new Thread(new CVSDecorationRunnable(this), "CVS"); //$NON-NLS-1$
-//		decoratorUpdateThread.start();
-//		CVSProviderPlugin.addResourceStateChangeListener(this);
-//		ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_AUTO_BUILD);
-	}
-
-	/*
-	 * Handle resource changes and project description changes
-	 * 
-	 * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent)
-	 */
-	public void resourceChanged(IResourceChangeEvent event) {
-		try {
-			final List changedResources = new ArrayList();
-			event.getDelta().accept(new IResourceDeltaVisitor() {
-				public boolean visit(IResourceDelta delta) throws CoreException {
-					IResource resource = delta.getResource();
-					
-					if(resource.getType()==IResource.ROOT) {
-						// continue with the delta
-						return true;
-					}
-					
-					if (resource.getType() == IResource.PROJECT) {
-						// deconfigure if appropriate (see CVSDecorator#projectDeconfigured(IProject))
-						// do this even if there is a provider (this is required since old projects may still have a cvs nature)
-						if (deconfiguredProjects.contains(resource)) {
-							deconfiguredProjects.remove(resource);
-							refresh((IProject)resource);
-						}
-						// If there is no CVS provider, don't continue
-						if (RepositoryProvider.getProvider((IProject)resource, CVSProviderPlugin.getTypeId()) == null) {
-							return false;
-						}
-					}
-					
-					switch (delta.getKind()) {
-						case IResourceDelta.REMOVED:
-							// remove the cached decoration for any removed resource
-							cache.remove(resource);
-							break;
-						case IResourceDelta.CHANGED:
-							// for changed resources we have to update the decoration
-							changedResources.add(resource);	
-					}
-					
-					return true;
-				}
-			});
-			resourceStateChanged((IResource[])changedResources.toArray(new IResource[changedResources.size()]));
-			changedResources.clear();	
-		} catch (CoreException e) {
-			CVSProviderPlugin.log(e.getStatus());
-		}
-	}
-	/*
-	 * @see IResourceStateChangeListener#resourceStateChanged(IResource[])
-	 */
-	public void resourceStateChanged(IResource[] changedResources) {
-		// add depth first so that update thread processes parents first.
-		//System.out.println(">> State Change Event");
-		List resources = new ArrayList();
-		List noProviderResources = new ArrayList();
-		for (int i = 0; i < changedResources.length; i++) {
-			// ignore subtrees that aren't associated with a provider, this can happen on import
-			// of a new project to CVS.
-			IResource resource = changedResources[i];
-			if (getCVSProviderFor(resource) == null) {
-				// post a changed event but forget any cached information about this resource
-				noProviderResources.add(resource);
-			}
-			resources.addAll(computeParents(resource));
-		}
-		
-//		addResourcesToBeDecorated((IResource[]) resources.toArray(new IResource[resources.size()]));
-		
-		// post label events for resources that cannot or should not be decorated by CVS
-		if(!noProviderResources.isEmpty()) {
-			List resourcesToUpdate = new ArrayList();
-			for (Iterator it = resources.iterator(); it.hasNext();) {
-				IResource element = (IResource) it.next();
-				resourcesToUpdate.add(element);
-			}
-//			postLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate.toArray()));
-		}
-	}
-
-	private void clearCache() {
-		cache.clear();
-	}
-	
-	public void refresh(IProject project) {
-		final List resources = new ArrayList();
-		try {
-			project.accept(new IResourceVisitor() {
-				public boolean visit(IResource resource) {
-					resources.add(resource);
-					return true;
-				}
-			});
-			resourceStateChanged((IResource[]) resources.toArray(new IResource[resources.size()]));	
-		} catch (CoreException e) {
-		}
-	}
-
-	private List computeParents(IResource resource) {
-		IResource current = resource;
-		List resources = new ArrayList();
-		if(CVSUIPlugin.getPlugin().getPreferenceStore().getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY)) {
-			while (current.getType() != IResource.ROOT) {
-				resources.add(current);
-				current = current.getParent();
-			}
-		} else {
-			resources.add(current);			
-		}
-		return resources;
-	}
-	
-	private void shutdown() {
-		shutdown = true;
-		// Wake the thread up if it is asleep.
-		synchronized (this) {
-			notifyAll();
-		}
-		try {
-			// Wait for the decorator thread to finish before returning.
-			decoratorUpdateThread.join();
-		} catch (InterruptedException e) {
-		}
-	}
-		
-	/**
-	 * @see IResourceStateChangeListener#projectConfigured(IProject)
-	 */
-	public void projectConfigured(IProject project) {
-		refresh(project);
-	}
-
-	/**
-	 * @see IResourceStateChangeListener#projectDeconfigured(IProject)
-	 */
-	public void projectDeconfigured(IProject project) {
-		// Unfortunately, the nature is still associated with the project at this point.
-		// Therefore, we will remember that the project has been deconfigured and we will 
-		// refresh the decorators in the resource delta listener
-		deconfiguredProjects.add(project);
-	}
-	
-	public static boolean isDirty(final ICVSFile cvsFile) {
-		try {
-			final boolean[] isDirty = new boolean[] {false};
-			cvsFile.getParent().run(new ICVSRunnable() {
-				public void run(IProgressMonitor monitor) throws CVSException {
-					// file is dirty or file has been merged by an update
-					if(!cvsFile.isIgnored()) {
-						isDirty[0] = cvsFile.isModified();
-					}
-				}
-			}, ICVSFolder.READ_ONLY, null);
-			return isDirty[0];
-		} catch (CVSException e) {
-			//if we get an error report it to the log but assume dirty
-			CVSUIPlugin.log(e.getStatus());
-			return true;
-		}
-	}
-
-	public static boolean isDirty(IFile file) {
-		return isDirty(CVSWorkspaceRoot.getCVSFileFor(file));
-	}
-
-	public static boolean isDirty(IResource resource) {
-		
-		// No need to decorate non-existant resources
-		if (!resource.exists()) return false;
-		
-		if(resource.getType() == IResource.FILE) {
-			return isDirty((IFile) resource);
-		}
-		
-		final CVSException DECORATOR_EXCEPTION = new CVSException(new Status(IStatus.OK, "id", 1, "", null)); //$NON-NLS-1$ //$NON-NLS-2$
-		try {
-			ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
-			cvsResource.accept(new ICVSResourceVisitor() {
-				public void visitFile(ICVSFile file) throws CVSException {
-					if(isDirty(file)) {
-						throw DECORATOR_EXCEPTION;
-					}
-				}
-				public void visitFolder(ICVSFolder folder) throws CVSException {
-					if(!folder.exists()) {
-						if (folder.isCVSFolder()) {
-							// The folder may contain outgoing file deletions
-							folder.acceptChildren(this);
-						}
-						return;
-					}
-					if (!folder.isCVSFolder() && !folder.isIgnored()) {
-						// new resource, show as dirty
-						throw DECORATOR_EXCEPTION;
-					}
-					folder.acceptChildren(this);
-				}
-			});
-		} catch (CVSException e) {
-			//if our exception was caught, we know there's a dirty child
-			return e == DECORATOR_EXCEPTION;
-		}
-		return false;
-	}
-	/**
-	 * This method is used to indicate whether a particular resource is a member of
-	 * a project that is in the process of being deconfigured. Such resources should not
-	 * be decorated.
-	 */
-	/* package */ boolean isMemberDeconfiguredProject(IResource resource) {
-		if (deconfiguredProjects.isEmpty()) return false;
-		return deconfiguredProjects.contains(resource.getProject());
-	}
-
-}
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
index f13ab58..5110f5a 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSDecoratorPreferencesPage.java
@@ -351,7 +351,7 @@
 		
 		store.setValue(ICVSUIConstants.PREF_SHOW_SYNCINFO_AS_TEXT, showSyncInfoInLabel.getSelection());		
 		
-		CVSDecorator.refresh();
+		CVSLightweightDecorator.refresh();
 		
 		CVSUIPlugin.getPlugin().savePluginPreferences();
 		return true;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSLightweightDecorator.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSLightweightDecorator.java
index d25c742..5f78820 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSLightweightDecorator.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSLightweightDecorator.java
@@ -5,21 +5,34 @@
  * All Rights Reserved.
  */
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.core.resources.IContainer;
 import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
 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.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.IDecoration;
 import org.eclipse.jface.viewers.ILightweightLabelDecorator;
 import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
 import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.internal.ccvs.core.CVSException;
 import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
@@ -29,16 +42,19 @@
 import org.eclipse.team.internal.ccvs.core.ICVSFolder;
 import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
 import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.ICVSRunnable;
+import org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener;
 import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
 import org.eclipse.team.ui.ISharedImages;
 import org.eclipse.team.ui.TeamImages;
+import org.eclipse.ui.IDecoratorManager;
 
 public class CVSLightweightDecorator
 	extends LabelProvider
-	implements ILightweightLabelDecorator {
+	implements ILightweightLabelDecorator, IResourceChangeListener, IResourceStateChangeListener {
 
 	// Images cached for better performance
 	private static ImageDescriptor dirty;
@@ -72,16 +88,44 @@
 		merged = new CachedImageDescriptor(CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_MERGED));
 		newResource = new CachedImageDescriptor(CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_QUESTIONABLE));
 	}
-		
-/*	public CVSLightweightDecorator() {
+
+	public CVSLightweightDecorator() {
 		CVSProviderPlugin.addResourceStateChangeListener(this);
 		
 		ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_AUTO_BUILD);
 	}
-*/
+
 	// Keep track of deconfigured projects
 	private Set deconfiguredProjects = new HashSet();
 
+	public static boolean isDirty(final ICVSResource cvsFile) {
+		try {
+			final boolean[] isDirty = new boolean[] {false};
+			cvsFile.getParent().run(new ICVSRunnable() {
+				public void run(IProgressMonitor monitor) throws CVSException {
+					// file is dirty or file has been merged by an update
+					if(!cvsFile.isIgnored()) {
+						isDirty[0] = cvsFile.isModified();
+					}
+				}
+			}, ICVSFolder.READ_ONLY, null);
+			return isDirty[0];
+		} catch (CVSException e) {
+			//if we get an error report it to the log but assume dirty
+			CVSUIPlugin.log(e.getStatus());
+			return true;
+		}
+	}
+
+	public static boolean isDirty(IResource resource) {
+
+		// No need to decorate non-existant resources
+		if (!resource.exists()) return false;
+
+		return isDirty(CVSWorkspaceRoot.getCVSResourceFor(resource));
+
+	}
+	
 	/*
 	 * Answers null if a provider does not exist or the provider is not a CVS provider. These resources
 	 * will be ignored by the decorator.
@@ -148,7 +192,7 @@
 			store.getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY);
 		int type = resource.getType();
 		if (type == IResource.FILE || computeDeepDirtyCheck) {
-			isDirty = CVSDecorator.isDirty(resource);
+			isDirty = CVSLightweightDecorator.isDirty(resource);
 		}
 		
 		decorateTextLabel(resource, decoration, isDirty, true);
@@ -349,4 +393,157 @@
 		return null;
 
 	}
+
+	/*
+	 * Add resource and its parents to the List
+	 */
+	 
+	private void addWithParents(IResource resource, List resources) {
+		IResource current = resource;
+
+		while (current.getType() != IResource.ROOT) {
+			resources.add(current);
+			current = current.getParent();
+		}
+	}
+	
+	/*
+	 * Return the CVSLightweightDecorator instance that is currently enabled.
+	 * Return null if we don't have a decorator or its not enabled.
+	 */	 
+	/* package */ static CVSLightweightDecorator getActiveCVSDecorator() {
+		IDecoratorManager manager = CVSUIPlugin.getPlugin().getWorkbench().getDecoratorManager();
+		if (manager.getEnabled(CVSUIPlugin.DECORATOR_ID))
+			return (CVSLightweightDecorator) manager.getLightweightLabelDecorator(CVSUIPlugin.DECORATOR_ID);
+		return null;
+	}
+	/*
+	 * Perform a blanket refresh of all CVS decorations
+	 */
+	 public static void refresh() {
+		CVSLightweightDecorator activeDecorator = getActiveCVSDecorator();
+
+		if(activeDecorator == null)
+			return;	//nothing to do, our decorator isn't active
+
+		//update all displaying of our decorator;
+		activeDecorator.fireLabelProviderChanged(new LabelProviderChangedEvent(activeDecorator));
+	}
+
+	/*
+	 * Update the decorators for every resource in project
+	 */
+	 
+	public void refresh(IProject project) {
+		final List resources = new ArrayList();
+		try {
+			project.accept(new IResourceVisitor() {
+				public boolean visit(IResource resource) {
+					resources.add(resource);
+					return true;
+				}
+			});
+			postLabelEvent(new LabelProviderChangedEvent(this, resources.toArray()));
+		} catch (CoreException e) {
+			CVSProviderPlugin.log(e.getStatus());
+		}
+	}
+	
+	/**
+	 * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceSyncInfoChanged(org.eclipse.core.resources.IResource[])
+	 */
+	public void resourceSyncInfoChanged(IResource[] changedResources) {
+		resourceStateChanged(changedResources);
+	}
+	
+	/**
+	 * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceModificationStateChanged(org.eclipse.core.resources.IResource[])
+	 */
+	public void resourceModified(IResource[] changedResources) {
+		resourceStateChanged(changedResources);
+	}
+	
+	/**
+	 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+	 */
+	public void resourceChanged(IResourceChangeEvent event) {
+		try {
+			final List changedResources = new ArrayList();
+			event.getDelta().accept(new IResourceDeltaVisitor() {
+				public boolean visit(IResourceDelta delta) throws CoreException {
+					IResource resource = delta.getResource();
+
+					if(resource.getType() == IResource.ROOT) {
+						// continue with the delta
+						return true;
+					}
+
+					if (resource.getType() == IResource.PROJECT) {
+						// deconfigure if appropriate (see CVSDecorator#projectDeconfigured(IProject))
+						// do this even if there is a provider (this is required since old projects may still have a cvs nature)
+						if (deconfiguredProjects.contains(resource)) {
+							deconfiguredProjects.remove(resource);
+							refresh((IProject)resource);
+						}
+						// If there is no CVS provider, don't continue
+						if (RepositoryProvider.getProvider((IProject)resource, CVSProviderPlugin.getTypeId()) == null) {
+							return false;
+						}
+					}
+
+					return true;
+				}
+			});
+			resourceStateChanged((IResource[])changedResources.toArray(new IResource[changedResources.size()]));
+			changedResources.clear();
+		} catch (CoreException e) {
+			CVSProviderPlugin.log(e.getStatus());
+		}
+	}
+	/**
+	 * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#resourceStateChanged(org.eclipse.core.resources.IResource[])
+	 */
+	public void resourceStateChanged(IResource[] changedResources) {
+		// add depth first so that update thread processes parents first.
+		//System.out.println(">> State Change Event");
+		List resourcesToUpdate = new ArrayList();
+
+		boolean showingDeepDirtyIndicators = CVSUIPlugin.getPlugin().getPreferenceStore().getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY);
+
+		for (int i = 0; i < changedResources.length; i++) {
+			IResource resource = changedResources[i];
+
+			if(showingDeepDirtyIndicators) {
+				addWithParents(resource, resourcesToUpdate);
+			} else {
+				resourcesToUpdate.add(resource);
+			}
+		}
+
+		postLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate.toArray()));
+	}
+	
+	/**
+	 * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#projectConfigured(org.eclipse.core.resources.IProject)
+	 */
+	public void projectConfigured(IProject project) {
+	}
+	/**
+	 * @see org.eclipse.team.internal.ccvs.core.IResourceStateChangeListener#projectDeconfigured(org.eclipse.core.resources.IProject)
+	 */
+	public void projectDeconfigured(IProject project) {
+	}
+
+	/**
+	 * Post the label event to the UI thread
+	 *
+	 * @param events  the events to post
+	 */
+	private void postLabelEvent(final LabelProviderChangedEvent event) {
+		Display.getDefault().asyncExec(new Runnable() {
+			public void run() {
+				fireLabelProviderChanged(event);
+			}
+		});
+	}
 }
\ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPreferencesPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPreferencesPage.java
index db3d2d3..5a0a3d2 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPreferencesPage.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSPreferencesPage.java
@@ -370,7 +370,7 @@
 		
 		// changing the default keyword substitution mode for text files may affect
 		// information displayed in the decorators
-		if (! oldKSubst.equals(newKSubst)) CVSDecorator.refresh();
+		if (! oldKSubst.equals(newKSubst)) CVSLightweightDecorator.refresh();
 		
 		CVSUIPlugin.getPlugin().savePluginPreferences();
 		return true;
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
index 1ab8904..9f7622d 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/CVSUIPlugin.java
Binary files differ
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSAction.java
index 721b658..3e16902 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSAction.java
@@ -42,7 +42,7 @@
 import org.eclipse.team.internal.ccvs.core.ILogEntry;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
 import org.eclipse.team.internal.ccvs.ui.AvoidableMessageDialog;
-import org.eclipse.team.internal.ccvs.ui.CVSDecorator;
+import org.eclipse.team.internal.ccvs.ui.CVSLightweightDecorator;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
 import org.eclipse.team.internal.ccvs.ui.Policy;
@@ -425,7 +425,7 @@
 	public static IPromptCondition getOverwriteLocalChangesPrompt() {
 		return new IPromptCondition() {
 			public boolean needsPrompt(IResource resource) {
-				return CVSDecorator.isDirty(resource);
+				return CVSLightweightDecorator.isDirty(resource);
 			}
 			public String promptMessage(IResource resource) {
 				return Policy.bind("ReplaceWithAction.localChanges", resource.getName());//$NON-NLS-1$
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java
index cdb0a27..c41d91d 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/TagAction.java
@@ -29,7 +29,7 @@
 import org.eclipse.team.internal.ccvs.core.CVSTeamProvider;
 import org.eclipse.team.internal.ccvs.core.ICVSFolder;
 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
-import org.eclipse.team.internal.ccvs.ui.CVSDecorator;
+import org.eclipse.team.internal.ccvs.ui.CVSLightweightDecorator;
 import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
 import org.eclipse.team.internal.ccvs.ui.Policy;
 import org.eclipse.team.internal.ccvs.ui.TagAsVersionDialog;
@@ -148,7 +148,7 @@
 	protected IPromptCondition getPromptCondition() {
 		return new IPromptCondition() {
 			public boolean needsPrompt(IResource resource) {
-				return CVSDecorator.isDirty(resource);
+				return CVSLightweightDecorator.isDirty(resource);
 			}
 			public String promptMessage(IResource resource) {
 				return Policy.bind("TagAction.uncommittedChanges", resource.getName());//$NON-NLS-1$
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
index 4f86e4e..5ee14d4 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/sync/CVSCatchupReleaseViewer.java
@@ -231,7 +231,8 @@
 			public void dispose() {
 				iconCache.disposeAll();
 				oldProvider.dispose();
-				conflictImage.dispose();
+				if(conflictImage != null)	
+					conflictImage.dispose();
 			}
 			
 			public Image getImage(Object element) {