blob: a5bd6a5162cadcf2303dff1648483e53472b40e8 [file] [log] [blame]
package org.eclipse.team.internal.ccvs.ui;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.ImageData;
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.CVSTag;
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.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
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;
/**
* Performs the decoration calculation for elements made available via the decoration notifier.
*/
public class CVSDecorationRunnable implements Runnable {
// Images cached for better performance
private static ImageDescriptor dirty;
private static ImageDescriptor checkedIn;
private static ImageDescriptor checkedOut;
private static ImageDescriptor merged;
// Provides resources to be decorated and is notified when decoration has been calculated
private IDecorationNotifier notifier;
// Remember the non posted decorated resources
List resources = new ArrayList();
List decorations = new ArrayList();
private final static int NUM_TO_BATCH = 50;
static {
dirty = new CachedImageDescriptor(TeamImages.getImageDescriptor(ISharedImages.IMG_DIRTY_OVR));
checkedIn = new CachedImageDescriptor(TeamImages.getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR));
checkedOut = new CachedImageDescriptor(TeamImages.getImageDescriptor(ISharedImages.IMG_CHECKEDOUT_OVR));
merged = new CachedImageDescriptor(CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_MERGED));
}
/*
* Define a cached image descriptor which only creates the image data once
*/
public static class CachedImageDescriptor extends ImageDescriptor {
ImageDescriptor descriptor;
ImageData data;
public CachedImageDescriptor(ImageDescriptor descriptor) {
this.descriptor = descriptor;
}
public ImageData getImageData() {
if (data == null) {
data = descriptor.getImageData();
}
return data;
}
}
/* package */
CVSDecorationRunnable(IDecorationNotifier notifier) {
this.notifier = notifier;
}
/*
* @see Runnable#run()
*/
public void run() {
while (true) {
// will block if there are no resources to be decorated
IResource resource = notifier.next();
// if next() returned null, we are done and should shut down.
if (resource == null) {
return;
}
CVSDecoration decoration = decorate(resource);
// notify that decoration is ready
if(decoration!=null) {
resources.add(resource);
decorations.add(decoration);
if(!resources.isEmpty() && (notifier.remaining()==0 || resources.size() >= NUM_TO_BATCH)) {
notifier.decorated((IResource[])resources.toArray(new IResource[resources.size()]),
(CVSDecoration[])decorations.toArray(new CVSDecoration[decorations.size()]));
resources.clear();
decorations.clear();
}
}
}
}
public CVSDecoration decorate(IResource resource) {
// it is possible that the resource to be decorated is no longer associated
// with a CVS provider. This could happen if the team nature was removed
// between the time the decoration event was posted to the thread and the time
// the thread processes the decoration.
RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject(), CVSProviderPlugin.getTypeId());
if(!resource.exists() || provider==null) {
return null;
}
// if the resource is ignored return an empty decoration. This will
// force a decoration update event and clear the existing CVS decoration.
ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
try {
if(cvsResource.isIgnored()) {
return new CVSDecoration();
}
} catch (CVSException e) {
// The was an exception in isIgnored. Don't decorate
return new CVSDecoration();
}
// determine a if resource has outgoing changes (e.g. is dirty).
IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
boolean isDirty = false;
boolean computeDeepDirtyCheck = store.getBoolean(ICVSUIConstants.PREF_CALCULATE_DIRTY);
int type = resource.getType();
if(type == IResource.FILE || computeDeepDirtyCheck) {
isDirty = CVSDecorator.isDirty(resource);
}
// compute decorations
CVSDecoration decoration = computeTextLabelFor(resource, isDirty);
decoration.setOverlays(computeLabelOverlaysFor(resource, isDirty, (CVSTeamProvider)provider));
return decoration;
}
public static CVSDecoration computeTextLabelFor(IResource resource, boolean isDirty) {
try {
Map bindings = new HashMap(3);
String format = ""; //$NON-NLS-1$
IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
IPath resourceLocation = resource.getLocation();
int type = resource.getType();
// if the resource does not have a location then return. This can happen if the resource
// has been deleted after we where asked to decorate it.
if(resourceLocation==null) {
return new CVSDecoration(format, bindings, null);
}
if(type==IResource.FOLDER) {
format = store.getString(ICVSUIConstants.PREF_FOLDERTEXT_DECORATION);
} else if(type==IResource.PROJECT) {
format = store.getString(ICVSUIConstants.PREF_PROJECTTEXT_DECORATION);
} else {
format = store.getString(ICVSUIConstants.PREF_FILETEXT_DECORATION);
}
if(isDirty) {
bindings.put(CVSDecoratorConfiguration.DIRTY_FLAG, store.getString(ICVSUIConstants.PREF_DIRTY_FLAG));
}
CVSTag tag = getTagToShow(resource);
if(tag != null) {
bindings.put(CVSDecoratorConfiguration.RESOURCE_TAG, tag.getName());
}
if(type != IResource.FILE) {
ICVSFolder folder = CVSWorkspaceRoot.getCVSFolderFor((IContainer) resource);
FolderSyncInfo folderInfo = folder.getFolderSyncInfo();
if (folderInfo != null) {
ICVSRepositoryLocation location = CVSProviderPlugin.getProvider().getRepository(folderInfo.getRoot());
bindings.put(CVSDecoratorConfiguration.REMOTELOCATION_HOST, location.getHost());
bindings.put(CVSDecoratorConfiguration.REMOTELOCATION_METHOD, location.getMethod().getName());
bindings.put(CVSDecoratorConfiguration.REMOTELOCATION_USER, location.getUsername());
bindings.put(CVSDecoratorConfiguration.REMOTELOCATION_ROOT, location.getRootDirectory());
bindings.put(CVSDecoratorConfiguration.REMOTELOCATION_REPOSITORY, folderInfo.getRepository());
}
} else {
format = store.getString(ICVSUIConstants.PREF_FILETEXT_DECORATION);
ICVSFile file = CVSWorkspaceRoot.getCVSFileFor((IFile) resource);
ResourceSyncInfo fileInfo = file.getSyncInfo();
if (fileInfo != null) {
if(fileInfo.isAdded()) {
bindings.put(CVSDecoratorConfiguration.ADDED_FLAG, store.getString(ICVSUIConstants.PREF_ADDED_FLAG));
} else {
bindings.put(CVSDecoratorConfiguration.FILE_REVISION, fileInfo.getRevision());
}
KSubstOption option = fileInfo.getKeywordMode() != null ?
fileInfo.getKeywordMode() :
KSubstOption.fromFile((IFile) resource);
bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, option.getShortDisplayText());
} else {
// only show the type that cvs will use when comitting the file
KSubstOption option = KSubstOption.fromFile((IFile) resource);
bindings.put(CVSDecoratorConfiguration.FILE_KEYWORD, option.getShortDisplayText());
}
}
return new CVSDecoration(format, bindings, null);
} catch (CVSException e) {
CVSUIPlugin.log(e.getStatus());
return new CVSDecoration();
}
}
/**
* Only show the tag if the resources tag is different than the parents. Or else, tag
* names will clutter the text decorations.
*/
protected static CVSTag getTagToShow(IResource resource) throws CVSException {
ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
CVSTag tag = null;
if(cvsResource.isFolder()) {
FolderSyncInfo folderInfo = ((ICVSFolder)cvsResource).getFolderSyncInfo();
if(folderInfo != null) {
tag = folderInfo.getTag();
}
} else {
ResourceSyncInfo info = ((ICVSFile)cvsResource).getSyncInfo();
if(info != null) {
tag = info.getTag();
}
}
ICVSFolder parent = cvsResource.getParent();
if(parent != null && tag != null) {
FolderSyncInfo parentInfo = parent.getFolderSyncInfo();
if(parentInfo != null) {
CVSTag parentTag = parentInfo.getTag();
parentTag = (parentTag == null ? CVSTag.DEFAULT : parentTag);
tag = (tag == null ? CVSTag.DEFAULT : tag);
// must compare tags by name because CVS doesn't do a good job of
// using T and N prefixes for folders and files.
if( parentTag.getName().equals(tag.getName())) {
tag = null;
}
}
}
return tag;
}
public static List computeLabelOverlaysFor(IResource resource, boolean isDirty, CVSTeamProvider provider) {
List overlays = new ArrayList(3);
IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore();
boolean showDirty = store.getBoolean(ICVSUIConstants.PREF_SHOW_DIRTY_DECORATION);
boolean showHasRemote = store.getBoolean(ICVSUIConstants.PREF_SHOW_HASREMOTE_DECORATION);
boolean showAdded = store.getBoolean(ICVSUIConstants.PREF_SHOW_ADDED_DECORATION);
if (showAdded && resource.getType() == IResource.FILE) {
try {
IPath location = resource.getLocation();
if(location!=null) {
ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor((IFile) resource);
ResourceSyncInfo info = cvsFile.getSyncInfo();
// show merged icon if file has been merged but has not been edited (e.g. on commit it will be ignored)
if(info!=null && info.isNeedsMerge(cvsFile.getTimeStamp())) {
overlays.add(merged);
// show added icon if file has been added locally.
} else if(info!=null && info.isAdded()) {
overlays.add(checkedOut);
}
}
} catch (CVSException e) {
CVSUIPlugin.log(e.getStatus());
return null;
}
}
// show outgoing arrow
if(showDirty && isDirty) {
overlays.add(dirty);
}
// show remote icon
if (showHasRemote && provider.hasRemote(resource)) {
overlays.add(checkedIn);
}
if(overlays.isEmpty()) {
return null;
} else {
return overlays;
}
}
}