Signed-off-by: Tor Arne Vestbø <torarnv@xxxxxxxxx> --- org.spearce.egit.ui/icons/ovr/assumevalid.gif | Bin 64 -> 0 bytes org.spearce.egit.ui/icons/ovr/conflict.gif | Bin 64 -> 164 bytes org.spearce.egit.ui/icons/ovr/pending_add.gif | Bin 64 -> 0 bytes org.spearce.egit.ui/icons/ovr/pending_remove.gif | Bin 111 -> 0 bytes org.spearce.egit.ui/icons/ovr/staged.gif | Bin 0 -> 114 bytes org.spearce.egit.ui/icons/ovr/staged_added.gif | Bin 0 -> 114 bytes org.spearce.egit.ui/icons/ovr/staged_removed.gif | Bin 0 -> 114 bytes .../egit/ui/PluginPreferenceInitializer.java | 2 + .../src/org/spearce/egit/ui/UIIcons.java | 13 +- .../src/org/spearce/egit/ui/UIPreferences.java | 4 + .../src/org/spearce/egit/ui/UIText.java | 14 +- .../decorators/GitLightweightDecorator.java | 284 ++++++++++++++++---- .../internal/decorators/IDecoratableResource.java | 39 +++ .../preferences/GitDecoratorPreferencePage.java | 131 ++++++++-- .../src/org/spearce/egit/ui/uitext.properties | 14 +- 15 files changed, 427 insertions(+), 74 deletions(-) delete mode 100644 org.spearce.egit.ui/icons/ovr/assumevalid.gif delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_add.gif delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_remove.gif create mode 100644 org.spearce.egit.ui/icons/ovr/staged.gif create mode 100644 org.spearce.egit.ui/icons/ovr/staged_added.gif create mode 100644 org.spearce.egit.ui/icons/ovr/staged_removed.gif diff --git a/org.spearce.egit.ui/icons/ovr/assumevalid.gif b/org.spearce.egit.ui/icons/ovr/assumevalid.gif deleted file mode 100644 index c7262ed4e3f9437a51806f70fbc851e3a6f951d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 zcmZ?wbhEHbWM|-D_{abP{~H+o{|7M?f3h$#FfcRdfH)v|1}4Ed_6CP$GF;`pupzry Lj9oc|fx#L8z0(dE diff --git a/org.spearce.egit.ui/icons/ovr/conflict.gif b/org.spearce.egit.ui/icons/ovr/conflict.gif index b444be94a2d09561b212138b1514d5c07610cc07..186345711bb5443b440c4265fe62b099cce5def0 100644 GIT binary patch literal 164 zcmZ?wbhEHbWM|-DIKseS;8EwAJi|S8rcc&9znlfG$<y4^W_f1J^~{*<m%Cuej;o77 z=*sb{&yHVx`snqS|6ssCG*JA>!pOiN$e;t#0kV^URWU)OPmsZ3MW&ETlG9;j{ud8T Wc$>JmHcJQuD+C104Qdczum%A1Q!l9i literal 64 zcmZ?wbhEHbWM|-D_{hM}z`*dI0SXj<vM@3*Ffr(W_#k-(Cc!EF3&kHON*Qck_+rIG KhIgS14AuZ~{td7I diff --git a/org.spearce.egit.ui/icons/ovr/pending_add.gif b/org.spearce.egit.ui/icons/ovr/pending_add.gif deleted file mode 100644 index f2306024b2872e50db4143a790bca93189ef8d5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 zcmZ?wbhEHbWM|-Dn8?6je1?HxCd2>#{}~t<6o0a?iZC!S=m6OaAbAER!723%B?Oj5 R<v1{ie-xgi8_2+54FJ_W4pRUC diff --git a/org.spearce.egit.ui/icons/ovr/pending_remove.gif b/org.spearce.egit.ui/icons/ovr/pending_remove.gif deleted file mode 100644 index 4ecce038e66f904af8345f88c2c007706a4bc3d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111 zcmZ?wbhEHbWM|-D*v!E2;Qr&bwzj&uy5i#Ew6wIy$jG3eAU{99|Ns9pU;~OjSy)AY sT6I8@ATt<PWC|uQx@~OiE8rFvPw;68lw@ATcdKBBRM#03Aw~vk0Ht*t9{>OV diff --git a/org.spearce.egit.ui/icons/ovr/staged.gif b/org.spearce.egit.ui/icons/ovr/staged.gif new file mode 100644 index 0000000000000000000000000000000000000000..dc0b8c01673b32fde0f9b11c8302ad1ca0a1e902 GIT binary patch literal 114 zcmZ?wbhEHb<Y3@n*v!E2>(@D15q3p!E=^fpC2=k-SzZ$rK|38WV-+D!6DfNgu|P{% zPh+VN3xogv|1&T!DE?$&WMJT9&;hc6CU7t?u*hFHX*qrG>5bPI{QDmDwl|kFq+1-m RA=-DKlJ_A?g`5k6H30u&A6)<d literal 0 HcmV?d00001 diff --git a/org.spearce.egit.ui/icons/ovr/staged_added.gif b/org.spearce.egit.ui/icons/ovr/staged_added.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2b87e274667c441897d0853e6a437ba5487f51c GIT binary patch literal 114 zcmZ?wbhEHb<Y3@n*v!E2>(@D15q3p!E=^fpC2=k-SzZ$rK|38WV-+D!6DfNgu|P{% zPh+VN3xogv|1&T!DE?$&WMJT9&;hc6CU7t?u*hFHX*qrG>5bPI{QC}7o_(m`FiYy3 RK~byn9^Qv66>=^N)&TbU9;*NV literal 0 HcmV?d00001 diff --git a/org.spearce.egit.ui/icons/ovr/staged_removed.gif b/org.spearce.egit.ui/icons/ovr/staged_removed.gif new file mode 100644 index 0000000000000000000000000000000000000000..b6b4911d9eba72838d54fbcf6109eaf3836bf0db GIT binary patch literal 114 zcmZ?wbhEHb<Y3@n*v!E2>(@D15q3p!E=^fpC2=k-SzZ$rK|38WV-+D!6DfNgu|P{% zPh+VN3xogv|1&T!DE?$&WMJT9&;hc6CU7t?u*hFHX*qrG>5bPI{QC~|wl_0qWLX?O RA=-DKlJ_A?g`5k6H304c9%KLj literal 0 HcmV?d00001 diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java index 7465444..ef886cf 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java @@ -44,6 +44,8 @@ public void initializeDefaultPreferences() { UIText.DecoratorPreferencesPage_projectFormatDefault); prefs.setDefault(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, true); prefs.setDefault(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON, true); + prefs.setDefault(UIPreferences.DECORATOR_SHOW_STAGED_ICON, true); + prefs.setDefault(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON, true); w = new int[] { 500, 500 }; UIPreferences.setDefault(prefs, diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java index 0cead29..302637e 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java @@ -18,11 +18,15 @@ * Icons for the the Eclipse plugin. Mostly decorations. */ public class UIIcons { + + /** Decoration for resource in the index but not yet committed. */ + public static final ImageDescriptor OVR_STAGED; + /** Decoration for resource added to index but not yet committed. */ - public static final ImageDescriptor OVR_PENDING_ADD; + public static final ImageDescriptor OVR_STAGED_ADD; /** Decoration for resource removed from the index but not commit. */ - public static final ImageDescriptor OVR_PENDING_REMOVE; + public static final ImageDescriptor OVR_STAGED_REMOVE; /** Decoration for resource not being tracked by Git */ public static final ImageDescriptor OVR_UNTRACKED; @@ -75,8 +79,9 @@ static { base = init(); - OVR_PENDING_ADD = map("ovr/pending_add.gif"); - OVR_PENDING_REMOVE = map("ovr/pending_remove.gif"); + OVR_STAGED = map("ovr/staged.gif"); + OVR_STAGED_ADD = map("ovr/staged_added.gif"); + OVR_STAGED_REMOVE = map("ovr/staged_removed.gif"); OVR_UNTRACKED = map("ovr/untracked.gif"); OVR_CONFLICT = map("ovr/conflict.gif"); OVR_ASSUMEVALID = map("ovr/assumevalid.gif"); diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java index 7916cea..409b335 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java @@ -64,6 +64,10 @@ public final static String DECORATOR_SHOW_TRACKED_ICON = "decorator_show_tracked_icon"; /** */ public final static String DECORATOR_SHOW_UNTRACKED_ICON = "decorator_show_untracked_icon"; + /** */ + public final static String DECORATOR_SHOW_STAGED_ICON = "decorator_show_staged_icon"; + /** */ + public final static String DECORATOR_SHOW_CONFLICTS_ICON = "decorator_show_conflicts_icon"; /** * Get the preference values associated with a fixed integer array. diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java index fe62d0a..b62ca4c 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java @@ -953,12 +953,18 @@ public static String DecoratorPreferencesPage_generalTabFolder; /** */ - public static String DecoratorPreferencesPage_nameResourceVariable; + public static String DecoratorPreferencesPage_bindingResourceName; /** */ public static String DecoratorPreferencesPage_bindingBranchName; /** */ + public static String DecoratorPreferencesPage_bindingDirtyFlag; + + /** */ + public static String DecoratorPreferencesPage_bindingStagedFlag; + + /** */ public static String DecoratorPreferencesPage_selectFormats; /** */ @@ -979,6 +985,12 @@ /** */ public static String DecoratorPreferencesPage_iconsShowUntracked; + /** */ + public static String DecoratorPreferencesPage_iconsShowStaged; + + /** */ + public static String DecoratorPreferencesPage_iconsShowConflicts; + static { initializeMessages(UIText.class.getPackage().getName() + ".uitext", UIText.class); diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java index b20070a..aa6a261 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java @@ -60,9 +60,12 @@ import org.spearce.egit.ui.UIIcons; import org.spearce.egit.ui.UIPreferences; import org.spearce.egit.ui.UIText; +import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged; import org.spearce.jgit.dircache.DirCache; +import org.spearce.jgit.dircache.DirCacheEntry; import org.spearce.jgit.dircache.DirCacheIterator; import org.spearce.jgit.lib.Constants; +import org.spearce.jgit.lib.FileMode; import org.spearce.jgit.lib.IndexChangedEvent; import org.spearce.jgit.lib.ObjectId; import org.spearce.jgit.lib.RefsChangedEvent; @@ -200,6 +203,18 @@ public void decorate(Object element, IDecoration decoration) { private boolean ignored = false; + private boolean dirty = false; + + private boolean conflicts = false; + + private Staged staged = Staged.NOT_STAGED; + + static final int T_HEAD = 0; + + static final int T_INDEX = 1; + + static final int T_WORKSPACE = 2; + public DecoratableResourceAdapter(IResource resourceToWrap) throws IOException { resource = resourceToWrap; @@ -207,59 +222,161 @@ public DecoratableResourceAdapter(IResource resourceToWrap) repository = mapping.getRepository(); headId = repository.resolve(Constants.HEAD); - initializeValues(); + switch (resource.getType()) { + case IResource.FILE: + extractFileProperties(); + break; + case IResource.FOLDER: + extractContainerProperties(); + break; + case IResource.PROJECT: + extractProjectProperties(); + break; + } } - /** - * Initialize the various values that are used for making decoration - * decisions later on. - * - * We might as well pre-load these now, instead of using lazy - * initialization, because they are all read by the decorator when - * building variable bindings and computing the preferred overlay. - * - * @throws IOException - */ - private void initializeValues() throws IOException { - - // Resolve current branch - branch = repository.getBranch(); + private void extractFileProperties() throws IOException { + TreeWalk treeWalk = createHeadVsIndexTreeWalk(); + if (treeWalk == null) + return; - // Resolve tracked state - if (getType() == IResource.PROJECT) { + if (treeWalk.next()) tracked = true; + else + return; + + // TODO: Also read ignores from .git/info/excludes et al. + if (Team.isIgnoredHint(resource)) { + ignored = true; + return; + } + + final DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX, + DirCacheIterator.class); + final DirCacheEntry indexEntry = indexIterator != null ? indexIterator + .getDirCacheEntry() + : null; + + if (indexEntry == null) { + staged = Staged.REMOVED; } else { - final TreeWalk treeWalk = new TreeWalk(repository); - - Set<String> repositoryPaths = Collections.singleton(mapping - .getRepoRelativePath(resource)); - if (!(repositoryPaths.isEmpty() || repositoryPaths.contains(""))) { - treeWalk.setFilter(PathFilterGroup - .createFromStrings(repositoryPaths)); - treeWalk.setRecursive(treeWalk.getFilter() - .shouldBeRecursive()); - treeWalk.reset(); - - if (headId != null) - treeWalk.addTree(new RevWalk(repository) - .parseTree(headId)); - else - treeWalk.addTree(new EmptyTreeIterator()); + if (indexEntry.isAssumeValid()) { + dirty = false; + } else if (indexEntry.getStage() > 0) { + conflicts = true; + } else if (treeWalk.getRawMode(T_HEAD) == FileMode.MISSING + .getBits()) { + staged = Staged.ADDED; + } else { + long indexEntryLastModified = indexEntry.getLastModified(); + long resourceLastModified = resource.getLocalTimeStamp(); + + // C-Git under Windows stores timestamps with 1-seconds + // resolution, so we need to check to see if this is the + // case here, and possibly fix the timestamp of the resource + // to match the resolution of the index. + if (indexEntryLastModified % 1000 == 0) { + resourceLastModified -= resourceLastModified % 1000; + } + + if (resourceLastModified != indexEntryLastModified) { + // TODO: Consider doing a content check here, to rule + // out false positives, as we might get mismatch between + // timestamps, even if the content is the same + dirty = true; + } - treeWalk.addTree(new DirCacheIterator(DirCache - .read(repository))); - if (treeWalk.next()) { - tracked = true; + if (treeWalk.getRawMode(T_HEAD) != treeWalk + .getRawMode(T_INDEX) + || !treeWalk.idEqual(T_HEAD, T_INDEX)) { + staged = Staged.MODIFIED; } } } - // Resolve ignored state (currently only reads the global Eclipse - // ignores) + } + + private void extractContainerProperties() throws IOException { + TreeWalk treeWalk = createHeadVsIndexTreeWalk(); + if (treeWalk == null) + return; + + if (treeWalk.next()) + tracked = true; + else + return; + // TODO: Also read ignores from .git/info/excludes et al. if (Team.isIgnoredHint(resource)) { ignored = true; + return; } + + // TODO: Compute dirty state for folder, using ContainerTreeIterator + // and ContainerDiffFilter + + } + + private void extractProjectProperties() throws IOException { + branch = repository.getBranch(); + tracked = true; + + // TODO: Compute dirty state for folder, using ContainerTreeIterator + // and ContainerDiffFilter + + } + + /** + * Adds a filter to the specified tree walk limiting the results to only + * those matching the resource specified by + * <code>resourceToFilterBy</code> + * <p> + * If the resource does not exists in the current repository, or it has + * an empty path (it is the project itself), the filter is not added, + * and the method returns <code>null</code>. + * + * @param treeWalk + * the tree walk to add the filter to + * @param resourceToFilterBy + * the resource to filter by + * + * @return <code>true</code> if the filter could be added, + * <code>false</code> otherwise + */ + private boolean addResourceFilter(final TreeWalk treeWalk, + final IResource resourceToFilterBy) { + Set<String> repositoryPaths = Collections.singleton(mapping + .getRepoRelativePath(resourceToFilterBy)); + if (repositoryPaths.isEmpty() || repositoryPaths.contains("")) + return false; + + treeWalk.setFilter(PathFilterGroup + .createFromStrings(repositoryPaths)); + return true; + } + + /** + * Helper method to create a new tree walk between HEAD and the index. + * + * @return the created tree walk, or null if it could not be created + * @throws IOException + * if there were errors when creating the tree walk + */ + private TreeWalk createHeadVsIndexTreeWalk() throws IOException { + final TreeWalk treeWalk = new TreeWalk(repository); + if (!addResourceFilter(treeWalk, resource)) + return null; + + treeWalk.setRecursive(treeWalk.getFilter().shouldBeRecursive()); + treeWalk.reset(); + + if (headId != null) + treeWalk.addTree(new RevWalk(repository).parseTree(headId)); + else + treeWalk.addTree(new EmptyTreeIterator()); + + treeWalk.addTree(new DirCacheIterator(DirCache.read(repository))); + return treeWalk; } public String getName() { @@ -281,6 +398,18 @@ public boolean isTracked() { public boolean isIgnored() { return ignored; } + + public boolean isDirty() { + return dirty; + } + + public Staged staged() { + return staged; + } + + public boolean hasConflicts() { + return conflicts; + } } /** @@ -298,6 +427,12 @@ public boolean isIgnored() { /** */ public static final String BINDING_BRANCH_NAME = "branch"; //$NON-NLS-1$ + /** */ + public static final String BINDING_DIRTY_FLAG = "dirty"; //$NON-NLS-1$ + + /** */ + public static final String BINDING_STAGED_FLAG = "staged"; //$NON-NLS-1$ + private IPreferenceStore store; /** @@ -325,10 +460,23 @@ public ImageData getImageData() { private static ImageDescriptor untrackedImage; + private static ImageDescriptor stagedImage; + + private static ImageDescriptor stagedAddedImage; + + private static ImageDescriptor stagedRemovedImage; + + private static ImageDescriptor conflictImage; + static { trackedImage = new CachedImageDescriptor(TeamImages .getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR)); untrackedImage = new CachedImageDescriptor(UIIcons.OVR_UNTRACKED); + stagedImage = new CachedImageDescriptor(UIIcons.OVR_STAGED); + stagedAddedImage = new CachedImageDescriptor(UIIcons.OVR_STAGED_ADD); + stagedRemovedImage = new CachedImageDescriptor( + UIIcons.OVR_STAGED_REMOVE); + conflictImage = new CachedImageDescriptor(UIIcons.OVR_CONFLICT); } /** @@ -354,6 +502,9 @@ public DecorationHelper(IPreferenceStore preferencesStore) { */ public void decorate(IDecoration decoration, IDecoratableResource resource) { + if (resource.isIgnored()) + return; + decorateText(decoration, resource); decorateIcons(decoration, resource); } @@ -379,22 +530,44 @@ private void decorateText(IDecoration decoration, Map<String, String> bindings = new HashMap<String, String>(); bindings.put(BINDING_RESOURCE_NAME, resource.getName()); bindings.put(BINDING_BRANCH_NAME, resource.getBranch()); + bindings.put(BINDING_DIRTY_FLAG, resource.isDirty() ? ">" : null); + bindings.put(BINDING_STAGED_FLAG, + resource.staged() != Staged.NOT_STAGED ? "*" : null); decorate(decoration, format, bindings); } private void decorateIcons(IDecoration decoration, IDecoratableResource resource) { - if (resource.isIgnored()) - return; + ImageDescriptor overlay = null; if (resource.isTracked()) { if (store.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON)) - decoration.addOverlay(trackedImage); - } else if (store - .getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) { - decoration.addOverlay(untrackedImage); + overlay = trackedImage; + + // Staged overrides tracked + Staged staged = resource.staged(); + if (store.getBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON) + && staged != Staged.NOT_STAGED) { + if (staged == Staged.ADDED) + overlay = stagedAddedImage; + else if (staged == Staged.REMOVED) + overlay = stagedRemovedImage; + else + overlay = stagedImage; + } + + // Conflicts override everything + if (store.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON) + && resource.hasConflicts()) + overlay = conflictImage; + + } else if (store.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) { + overlay = untrackedImage; } + + // Overlays can only be added once, so do it at the end + decoration.addOverlay(overlay); } /** @@ -411,7 +584,7 @@ private void decorateIcons(IDecoration decoration, * values */ public static void decorate(IDecoration decoration, String format, - Map bindings) { + Map<String, String> bindings) { StringBuffer prefix = new StringBuffer(); StringBuffer suffix = new StringBuffer(); StringBuffer output = prefix; @@ -426,6 +599,15 @@ public static void decorate(IDecoration decoration, String format, String key = format.substring(end + 1, start); String s; + // Allow users to override the binding + if (key.indexOf(':') > -1) { + String[] keyAndBinding = key.split(":", 2); + key = keyAndBinding[0]; + if (keyAndBinding.length > 1 + && bindings.get(key) != null) + bindings.put(key, keyAndBinding[1]); + } + // We use the BINDING_RESOURCE_NAME key to determine if // we are doing the prefix or suffix. The name isn't // actually part of either. @@ -433,7 +615,7 @@ public static void decorate(IDecoration decoration, String format, output = suffix; s = null; } else { - s = (String) bindings.get(key); + s = bindings.get(key); } if (s != null) { @@ -522,6 +704,14 @@ public void resourceChanged(IResourceChangeEvent event) { public boolean visit(IResourceDelta delta) throws CoreException { final IResource resource = delta.getResource(); + // If the resource is not part of a project under Git + // revision control + final RepositoryMapping mapping = RepositoryMapping + .getMapping(resource); + if (mapping == null) { + // Ignore the change + return true; + } if (resource.getType() == IResource.ROOT) { // Continue with the delta return true; diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java index f144214..d2b6b5c 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java @@ -16,6 +16,22 @@ public interface IDecoratableResource { /** + * Set of possible staging states for a resource + */ + public enum Staged { + /** Represents a resource that is not staged */ + NOT_STAGED, + /** Represents a resource that has been modified */ + MODIFIED, + /** Represents a resource that is added to Git */ + ADDED, + /** Represents a resource that is removed from Git */ + REMOVED, + /** Represents a resource that has been renamed */ + RENAMED + } + + /** * Gets the type of the resource as defined by {@link IResource} * * @return the type of the resource @@ -51,4 +67,27 @@ * @return whether or not the resource is ignored */ boolean isIgnored(); + + /** + * Returns whether or not the resource has changes that are not staged + * + * @return whether or not the resource is dirty + */ + boolean isDirty(); + + /** + * Returns the staged state of the resource + * + * The set of allowed values are defined by the <code>Staged</code> enum + * + * @return the staged state of the resource + */ + Staged staged(); + + /** + * Returns whether or not the resource has merge conflicts + * + * @return whether or not the resource has merge conflicts + */ + boolean hasConflicts(); } diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java index 2cbf07a..74bc209 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java @@ -56,6 +56,7 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; @@ -72,8 +73,9 @@ import org.spearce.egit.ui.UIPreferences; import org.spearce.egit.ui.UIText; import org.spearce.egit.ui.internal.SWTUtils; -import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper; import org.spearce.egit.ui.internal.decorators.IDecoratableResource; +import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper; +import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged; /** * Preference page for customizing Git label decorations @@ -93,6 +95,10 @@ private Button showUntracked; + private Button showStaged; + + private Button showConflicts; + private Preview fPreview; private static final Collection PREVIEW_FILESYSTEM_ROOT; @@ -101,16 +107,29 @@ static { final PreviewResource project = new PreviewResource( - "Project", IResource.PROJECT, "master", true, false); //$NON-NLS-1$1 + "Project", IResource.PROJECT, "master", true, false, false, Staged.NOT_STAGED, false); //$NON-NLS-1$1 final ArrayList<PreviewResource> children = new ArrayList<PreviewResource>(); + + children.add(new PreviewResource( + "folder", IResource.FOLDER, null, true, false, false, Staged.NOT_STAGED, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "tracked.txt", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false)); //$NON-NLS-1$ children.add(new PreviewResource( - "folder", IResource.FOLDER, null, true, false)); //$NON-NLS-1$ + "untracked.txt", IResource.FILE, null, false, false, false, Staged.NOT_STAGED, false)); //$NON-NLS-1$ children.add(new PreviewResource( - "file.txt", IResource.FILE, null, true, false)); //$NON-NLS-1$ + "ignored.txt", IResource.FILE, null, false, true, false, Staged.NOT_STAGED, false)); //$NON-NLS-1$ children.add(new PreviewResource( - "untracked.txt", IResource.FILE, null, false, false)); //$NON-NLS-1$ + "dirty.txt", IResource.FILE, null, true, false, true, Staged.NOT_STAGED, false)); //$NON-NLS-1$ children.add(new PreviewResource( - "ignored.txt", IResource.FILE, null, false, true)); //$NON-NLS-1$ + "staged.txt", IResource.FILE, null, true, false, false, Staged.MODIFIED, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "partially-staged.txt", IResource.FILE, null, true, false, true, Staged.MODIFIED, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "added.txt", IResource.FILE, null, true, false, false, Staged.ADDED, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "removed.txt", IResource.FILE, null, true, false, false, Staged.REMOVED, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "conflict.txt", IResource.FILE, null, true, false, true, Staged.NOT_STAGED, true)); //$NON-NLS-1$ project.children = children; PREVIEW_FILESYSTEM_ROOT = Collections.singleton(project); } @@ -179,6 +198,7 @@ private Control createGeneralDecoratorPage(Composite parent) { Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); + layout.marginHeight = 10; composite.setLayout(layout); GridData data = new GridData(); data.horizontalAlignment = GridData.FILL; @@ -206,22 +226,29 @@ private Control createTextDecoratorPage(Composite parent) { data.horizontalAlignment = GridData.FILL; fileTextGroup.setLayoutData(data); + int labelWidth = convertWidthInCharsToPixels(Math.max( + UIText.DecoratorPreferencesPage_fileFormatLabel.length(), + Math.max(UIText.DecoratorPreferencesPage_folderFormatLabel + .length(), + UIText.DecoratorPreferencesPage_projectFormatLabel + .length()))); + TextPair format = createFormatEditorControl(fileTextGroup, UIText.DecoratorPreferencesPage_fileFormatLabel, UIText.DecoratorPreferencesPage_addVariablesAction, - getFileBindingDescriptions()); + getFileBindingDescriptions(), labelWidth); fileTextFormat = format.t1; format = createFormatEditorControl(fileTextGroup, UIText.DecoratorPreferencesPage_folderFormatLabel, UIText.DecoratorPreferencesPage_addVariablesAction, - getFolderBindingDescriptions()); + getFolderBindingDescriptions(), labelWidth); folderTextFormat = format.t1; format = createFormatEditorControl(fileTextGroup, UIText.DecoratorPreferencesPage_projectFormatLabel, UIText.DecoratorPreferencesPage_addVariablesAction, - getProjectBindingDescriptions()); + getProjectBindingDescriptions(), labelWidth); projectTextFormat = format.t1; return fileTextGroup; @@ -230,6 +257,7 @@ private Control createTextDecoratorPage(Composite parent) { private Control createIconDecoratorPage(Composite parent) { Composite imageGroup = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); + layout.marginHeight = 10; imageGroup.setLayout(layout); GridData data = new GridData(); data.horizontalAlignment = GridData.FILL; @@ -239,17 +267,27 @@ private Control createIconDecoratorPage(Composite parent) { UIText.DecoratorPreferencesPage_iconsShowTracked); showUntracked = SWTUtils.createCheckBox(imageGroup, UIText.DecoratorPreferencesPage_iconsShowUntracked); + showStaged = SWTUtils.createCheckBox(imageGroup, + UIText.DecoratorPreferencesPage_iconsShowStaged); + showConflicts = SWTUtils.createCheckBox(imageGroup, + UIText.DecoratorPreferencesPage_iconsShowConflicts); return imageGroup; } private TextPair createFormatEditorControl(Composite composite, - String title, String buttonText, final Map supportedBindings) { + String title, String buttonText, final Map supportedBindings, + int labelWidth) { - SWTUtils.createLabel(composite, title); + Label label = SWTUtils.createLabel(composite, title); + GridData labelGridData = new GridData(); + labelGridData.widthHint = labelWidth; + label.setLayoutData(labelGridData); Text format = new Text(composite, SWT.BORDER); - format.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + GridData textGridData = new GridData(GridData.FILL_HORIZONTAL); + textGridData.widthHint = 200; + format.setLayoutData(textGridData); format.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { updatePreview(); @@ -293,6 +331,10 @@ private void initializeValues() { .getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON)); showUntracked.setSelection(store .getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)); + showStaged.setSelection(store + .getBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON)); + showConflicts.setSelection(store + .getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)); SelectionListener selectionListener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { @@ -303,6 +345,8 @@ public void widgetSelected(SelectionEvent e) { computeDeepDirtyState.addSelectionListener(selectionListener); showTracked.addSelectionListener(selectionListener); showUntracked.addSelectionListener(selectionListener); + showStaged.addSelectionListener(selectionListener); + showConflicts.addSelectionListener(selectionListener); setValid(true); } @@ -354,6 +398,10 @@ private boolean performOk(IPreferenceStore store) { .getSelection()); store.setValue(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON, showUntracked.getSelection()); + store.setValue(UIPreferences.DECORATOR_SHOW_STAGED_ICON, showStaged + .getSelection()); + store.setValue(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON, + showConflicts.getSelection()); return true; } @@ -383,6 +431,11 @@ protected void performDefaults() { showUntracked .setSelection(store .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)); + showStaged.setSelection(store + .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON)); + showConflicts + .setSelection(store + .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)); } /** @@ -505,7 +558,11 @@ TextPair(Text t1, Text t2) { private Map getFileBindingDescriptions() { Map<String, String> bindings = new HashMap<String, String>(); bindings.put(DecorationHelper.BINDING_RESOURCE_NAME, - UIText.DecoratorPreferencesPage_nameResourceVariable); + UIText.DecoratorPreferencesPage_bindingResourceName); + bindings.put(DecorationHelper.BINDING_DIRTY_FLAG, + UIText.DecoratorPreferencesPage_bindingDirtyFlag); + bindings.put(DecorationHelper.BINDING_STAGED_FLAG, + UIText.DecoratorPreferencesPage_bindingStagedFlag); return bindings; } @@ -518,7 +575,11 @@ private Map getFileBindingDescriptions() { private Map getFolderBindingDescriptions() { Map<String, String> bindings = new HashMap<String, String>(); bindings.put(DecorationHelper.BINDING_RESOURCE_NAME, - UIText.DecoratorPreferencesPage_nameResourceVariable); + UIText.DecoratorPreferencesPage_bindingResourceName); + bindings.put(DecorationHelper.BINDING_DIRTY_FLAG, + UIText.DecoratorPreferencesPage_bindingDirtyFlag); + bindings.put(DecorationHelper.BINDING_STAGED_FLAG, + UIText.DecoratorPreferencesPage_bindingStagedFlag); return bindings; } @@ -531,7 +592,11 @@ private Map getFolderBindingDescriptions() { private Map getProjectBindingDescriptions() { Map<String, String> bindings = new HashMap<String, String>(); bindings.put(DecorationHelper.BINDING_RESOURCE_NAME, - UIText.DecoratorPreferencesPage_nameResourceVariable); + UIText.DecoratorPreferencesPage_bindingResourceName); + bindings.put(DecorationHelper.BINDING_DIRTY_FLAG, + UIText.DecoratorPreferencesPage_bindingDirtyFlag); + bindings.put(DecorationHelper.BINDING_STAGED_FLAG, + UIText.DecoratorPreferencesPage_bindingStagedFlag); bindings.put(DecorationHelper.BINDING_BRANCH_NAME, UIText.DecoratorPreferencesPage_bindingBranchName); return bindings; @@ -691,14 +756,25 @@ private PreviewDecoration getDecoration(Object element) { private boolean ignored; + private boolean dirty; + + private boolean conflicts; + + private Staged staged; + public PreviewResource(String name, int type, String branch, - boolean tracked, boolean ignored) { + boolean tracked, boolean ignored, boolean dirty, Staged staged, + boolean conflicts) { + this.name = name; this.branch = branch; this.type = type; this.children = Collections.EMPTY_LIST; this.tracked = tracked; this.ignored = ignored; + this.dirty = dirty; + this.staged = staged; + this.conflicts = conflicts; } public String getName() { @@ -720,6 +796,18 @@ public boolean isTracked() { public boolean isIgnored() { return ignored; } + + public boolean isDirty() { + return dirty; + } + + public Staged staged() { + return staged; + } + + public boolean hasConflicts() { + return conflicts; + } } private class PreviewDecoration implements IDecoration { @@ -736,12 +824,19 @@ public boolean isIgnored() { private Color foregroundColor; + /** + * Adds an icon overlay to the decoration + * <p> + * Copies the behavior of <code>DecorationBuilder</code> of only + * allowing the overlay to be set once. + */ public void addOverlay(ImageDescriptor overlayImage) { - overlay = overlayImage; + if (overlay == null) + overlay = overlayImage; } public void addOverlay(ImageDescriptor overlayImage, int quadrant) { - overlay = overlayImage; + addOverlay(overlayImage); } public void addPrefix(String prefix) { diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties index 05fdba9..2762d11 100644 --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties @@ -352,13 +352,15 @@ DecoratorPreferencesPage_preview=Preview: DecoratorPreferencesPage_fileFormatLabel=&Files: DecoratorPreferencesPage_folderFormatLabel=F&olders: DecoratorPreferencesPage_projectFormatLabel=&Projects: -DecoratorPreferencesPage_fileFormatDefault={name} -DecoratorPreferencesPage_folderFormatDefault={name} -DecoratorPreferencesPage_projectFormatDefault={name} [{branch}] +DecoratorPreferencesPage_fileFormatDefault={dirty:>} {name} +DecoratorPreferencesPage_folderFormatDefault={dirty:>} {name} +DecoratorPreferencesPage_projectFormatDefault={dirty:>} {name} [{branch}] DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or disable Git decorations. DecoratorPreferencesPage_generalTabFolder=&General -DecoratorPreferencesPage_nameResourceVariable=name of the resource being decorated +DecoratorPreferencesPage_bindingResourceName=name of the resource being decorated DecoratorPreferencesPage_bindingBranchName=current branch of the project +DecoratorPreferencesPage_bindingDirtyFlag=flag indicating whether or not the resource is dirty +DecoratorPreferencesPage_bindingStagedFlag=flag indicating whether or not the resource is staged DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and project text labels: DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format: DecoratorPreferencesPage_textLabel=T&ext Decorations @@ -366,3 +368,7 @@ DecoratorPreferencesPage_iconLabel=&Icon Decorations DecoratorPreferencesPage_iconsShowTracked=Tracked resources DecoratorPreferencesPage_iconsShowUntracked=Untracked resources +Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources. + +DecoratorPreferencesPage_iconsShowStaged=Staged resources +DecoratorPreferencesPage_iconsShowConflicts=Conflicting resources -- 1.6.1.2.309.g2ea3 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html