- Dirty - Staged - Conflict - Assume valid Signed-off-by: Tor Arne Vestbø <torarnv@xxxxxxxxx> --- org.spearce.egit.ui/icons/ovr/assume_valid.gif | Bin 0 -> 85 bytes org.spearce.egit.ui/icons/ovr/assumevalid.gif | Bin 64 -> 0 bytes org.spearce.egit.ui/icons/ovr/conflict.gif | Bin 64 -> 194 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 -> 169 bytes org.spearce.egit.ui/icons/ovr/staged_removed.gif | Bin 0 -> 176 bytes .../egit/ui/PluginPreferenceInitializer.java | 3 + .../src/org/spearce/egit/ui/UIIcons.java | 15 +- .../src/org/spearce/egit/ui/UIPreferences.java | 6 + .../src/org/spearce/egit/ui/UIText.java | 17 +- .../decorators/GitLightweightDecorator.java | 299 +++++++++++++++++--- .../internal/decorators/IDecoratableResource.java | 46 +++ .../preferences/GitDecoratorPreferencePage.java | 152 +++++++++-- .../src/org/spearce/egit/ui/uitext.properties | 15 +- 16 files changed, 477 insertions(+), 76 deletions(-) create mode 100644 org.spearce.egit.ui/icons/ovr/assume_valid.gif 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/assume_valid.gif b/org.spearce.egit.ui/icons/ovr/assume_valid.gif new file mode 100644 index 0000000000000000000000000000000000000000..b6d7167cb38b147aa0fcc115121f3166276f6688 GIT binary patch literal 85 zcmZ?wbhEHb<Y3@pSj5Wk|Nno6&}sGa_b)s3!M=V!hzkaaKUo+V7+4u}Ks=CI24<;< gU94*oY!sY|O?Vb;E(m>L;^NQ~(4hBvvkHSX0J)kQGXMYp literal 0 HcmV?d00001 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..fce456a49a0d6b19feaef4e1fb8615d8083c674c 100644 GIT binary patch literal 194 zcmZ?wbhEHb<YwSvIKsfNSXBI}k=aul+s7VW3poWA@dz#iA)&<rB8vn>o~Y|S);7GW zV|+_e`Ie;0T?OrX8YZiZHCF1XE!R_9uBNn9O6ro5(lQ147ctTQ!GHl3DE?$&WMGhD z&;bd9>||iIU!ayIII}ZDEm63-?}`D3QE8g^;`e8!hE6+n_+o~G)HE-)gh(9`Kkf%@ d8(7v&JK{arFoJ8^;WC#KQvzQ|Td^=$0|4|&KIZ@c 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..c65d16a191f259e0feb55c9244e7545c9063f2ee GIT binary patch literal 169 zcmZ?wbhEHb<Y3@nI3mv=<D+lc6C~rUFXwNhSM8ymWTTpFZ&2l?k?oi~MbDtt!(w8P zX13k6n|3NGHU_nBa)HL3YfROWEiL<k^{d^EU9;|4ZK_}8(Y4mhqBp>NLa2<7LGmO+ zwPfqgH75W6|5yCU!pOiN&!EEq1Ry&ZSk)e=_dO9$3EpXP>xYb&Yrys66%*4FPo$Ud U`8i&abQEg7#HJw7%D`X^09=zc+W-In 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..8aaadfb9b9300594a3448986650b82841398287c GIT binary patch literal 176 zcmZ?wbhEHb<Y3@nI3mX2@9!TQ8ygf96c`wooSa-&SLf&FS5i_E8XDTx)|Q{29~>Oq z-rk;)l2TY$7#SHE5D<`;mlqcoS6p2D;Qr&HqN4x*|1;nQia%Kx85qPEbU;c#b~3Q) zE>P`DcJ&Dn4ViXC=E@Y0u&)L|A2*0x3h8BCs@CD4#4P3#=;_K}%6y8ku!Vua8UVS) BGb{iA 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..a3196f4 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,9 @@ 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); + prefs.setDefault(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_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 4c0d189..952816c 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; @@ -84,11 +88,12 @@ 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"); + OVR_ASSUMEVALID = map("ovr/assume_valid.gif"); ELCL16_FIND = map("elcl16/find.gif"); ELCL16_NEXT = map("elcl16/next.gif"); ELCL16_PREVIOUS = map("elcl16/previous.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..e812716 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,12 @@ 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"; + /** */ + public final static String DECORATOR_SHOW_ASSUME_VALID_ICON = "decorator_show_assume_valid_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 60e4eaa..7e26337 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 @@ -968,12 +968,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; /** */ @@ -994,6 +1000,15 @@ /** */ public static String DecoratorPreferencesPage_iconsShowUntracked; + /** */ + public static String DecoratorPreferencesPage_iconsShowStaged; + + /** */ + public static String DecoratorPreferencesPage_iconsShowConflicts; + + /** */ + public static String DecoratorPreferencesPage_iconsShowAssumeValid; + 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..c23ce24 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,20 @@ public void decorate(Object element, IDecoration decoration) { private boolean ignored = false; + private boolean dirty = false; + + private boolean conflicts = false; + + private boolean assumeValid = 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 +224,162 @@ 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; + assumeValid = true; + } 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 +401,22 @@ public boolean isTracked() { public boolean isIgnored() { return ignored; } + + public boolean isDirty() { + return dirty; + } + + public Staged staged() { + return staged; + } + + public boolean hasConflicts() { + return conflicts; + } + + public boolean isAssumeValid() { + return assumeValid; + } } /** @@ -298,6 +434,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 +467,26 @@ public ImageData getImageData() { private static ImageDescriptor untrackedImage; + private static ImageDescriptor stagedImage; + + private static ImageDescriptor stagedAddedImage; + + private static ImageDescriptor stagedRemovedImage; + + private static ImageDescriptor conflictImage; + + private static ImageDescriptor assumeValidImage; + 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); + assumeValidImage = new CachedImageDescriptor(UIIcons.OVR_ASSUMEVALID); } /** @@ -354,6 +512,9 @@ public DecorationHelper(IPreferenceStore preferencesStore) { */ public void decorate(IDecoration decoration, IDecoratableResource resource) { + if (resource.isIgnored()) + return; + decorateText(decoration, resource); decorateIcons(decoration, resource); } @@ -379,22 +540,49 @@ 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; + + if (store + .getBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON) + && resource.isAssumeValid()) + overlay = assumeValidImage; + + // 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 +599,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 +614,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 +630,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 +719,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..b864a10 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,34 @@ * @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(); + + /** + * Returns whether or not the resource is assumed valid + * + * @return whether or not the resource is assumed valid + */ + boolean isAssumeValid(); } 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 7b637e3..f72ceb7 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 @@ -55,6 +55,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; @@ -71,8 +72,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 @@ -94,22 +96,43 @@ private Preview preview; + private Button showStaged; + + private Button showConflicts; + + private Button showAssumeValid; + private static final Collection PREVIEW_FILESYSTEM_ROOT; private static IPropertyChangeListener themeListener; 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, 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, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "tracked.txt", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false, 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, 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, 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, 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, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "partially-staged.txt", IResource.FILE, null, true, false, true, Staged.MODIFIED, false, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "added.txt", IResource.FILE, null, true, false, false, Staged.ADDED, false, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "removed.txt", IResource.FILE, null, true, false, false, Staged.REMOVED, false, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "conflict.txt", IResource.FILE, null, true, false, true, Staged.NOT_STAGED, true, false)); //$NON-NLS-1$ + children.add(new PreviewResource( + "assume-valid.txt", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false, true)); //$NON-NLS-1$ project.children = children; PREVIEW_FILESYSTEM_ROOT = Collections.singleton(project); } @@ -190,22 +213,29 @@ private Control createTextDecoratorPage(Composite parent) { Composite fileTextGroup = SWTUtils.createHVFillComposite(parent, SWTUtils.MARGINS_DEFAULT, 3); + 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; @@ -219,17 +249,29 @@ 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); + showAssumeValid = SWTUtils.createCheckBox(imageGroup, + UIText.DecoratorPreferencesPage_iconsShowAssumeValid); 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(); @@ -273,16 +315,25 @@ 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)); + showAssumeValid.setSelection(store + .getBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON)); SelectionListener selectionListener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { - fPreview.refresh(); + preview.refresh(); } }; computeDeepDirtyState.addSelectionListener(selectionListener); showTracked.addSelectionListener(selectionListener); showUntracked.addSelectionListener(selectionListener); + showStaged.addSelectionListener(selectionListener); + showConflicts.addSelectionListener(selectionListener); + showAssumeValid.addSelectionListener(selectionListener); setValid(true); } @@ -334,6 +385,12 @@ 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()); + store.setValue(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON, + showAssumeValid.getSelection()); return true; } @@ -363,6 +420,14 @@ 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)); + showAssumeValid + .setSelection(store + .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON)); } /** @@ -473,7 +538,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; } @@ -486,7 +555,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; } @@ -499,7 +572,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; @@ -658,14 +735,28 @@ private PreviewDecoration getDecoration(Object element) { private boolean ignored; + private boolean dirty; + + private boolean conflicts; + + private Staged staged; + + private boolean assumeValid; + public PreviewResource(String name, int type, String branch, - boolean tracked, boolean ignored) { + boolean tracked, boolean ignored, boolean dirty, Staged staged, + boolean conflicts, boolean assumeValid) { + 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; + this.assumeValid = assumeValid; } public String getName() { @@ -687,6 +778,22 @@ public boolean isTracked() { public boolean isIgnored() { return ignored; } + + public boolean isDirty() { + return dirty; + } + + public Staged staged() { + return staged; + } + + public boolean hasConflicts() { + return conflicts; + } + + public boolean isAssumeValid() { + return assumeValid; + } } private class PreviewDecoration implements IDecoration { @@ -703,12 +810,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 9940177..e9a2321 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 @@ -358,17 +358,24 @@ 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 DecoratorPreferencesPage_iconLabel=&Icon Decorations DecoratorPreferencesPage_iconsShowTracked=Tracked resources DecoratorPreferencesPage_iconsShowUntracked=Untracked resources +DecoratorPreferencesPage_iconsShowStaged=Staged resources +DecoratorPreferencesPage_iconsShowConflicts=Conflicting resources +DecoratorPreferencesPage_iconsShowAssumeValid=Assumed unchanged resources + +Decorator_exceptionMessage=Errors occurred while applying Git decorations to 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