[EGIT PATCH v2 12/12] Implement label decorations for folders and projects

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The option "Re-decorate ancestors..." controls if parents of a
re-decorated resource also should be updated, for example to
to signal that the containing folder is now concidered dirty.

The option "Maximum number of levels..." controls how deep the
container decoration algorithm will recurse when trying to
determine the state (dirty, staged, etc.) of a container.

Signed-off-by: Tor Arne Vestbø <torarnv@xxxxxxxxx>
---
 .../egit/ui/PluginPreferenceInitializer.java       |    4 +-
 .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +-
 .../src/org/spearce/egit/ui/UIText.java            |   14 +-
 .../decorators/DecoratableResourceAdapter.java     |  391 ++++++++++++++++++++
 .../decorators/GitLightweightDecorator.java        |  300 ++-------------
 .../preferences/GitDecoratorPreferencePage.java    |   91 ++++-
 .../src/org/spearce/egit/ui/uitext.properties      |    7 +-
 7 files changed, 528 insertions(+), 283 deletions(-)
 create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java

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 a3196f4..8d617e9 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
@@ -35,7 +35,9 @@ public void initializeDefaultPreferences() {
 		prefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);
 		prefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);
 
-		prefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);
+		prefs.setDefault(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS, true);
+		prefs.setDefault(UIPreferences.DECORATOR_RECURSIVE_LIMIT,
+				Integer.MAX_VALUE);
 		prefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,
 				UIText.DecoratorPreferencesPage_fileFormatDefault);
 		prefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,
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 e812716..bbc36be 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
@@ -53,7 +53,9 @@
 	public final static String THEME_CommitMessageFont = "org.spearce.egit.ui.CommitMessageFont";
 
 	/** */
-	public final static String DECORATOR_CALCULATE_DIRTY = "decorator_calculate_dirty";
+	public final static String DECORATOR_RECOMPUTE_ANCESTORS = "decorator_recompute_ancestors";
+	/** */
+	public final static String DECORATOR_RECURSIVE_LIMIT = "decorator_recursive_limit";
 	/** */
 	public final static String DECORATOR_FILETEXT_DECORATION = "decorator_filetext_decoration";
 	/** */
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 7e26337..bd64ca1 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
@@ -935,7 +935,19 @@
 	public static String DecoratorPreferencesPage_addVariablesAction;
 
 	/** */
-	public static String DecoratorPreferencesPage_computeDeep;
+	public static String DecoratorPreferencesPage_recomputeAncestorDecorations;
+
+	/** */
+	public static String DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip;
+
+	/** */
+	public static String DecoratorPreferencesPage_computeRecursiveLimit;
+
+	/** */
+	public static String DecoratorPreferencesPage_computeRecursiveLimitTooltip;
+
+	/** */
+	public static String DecoratorPreferencesPage_invalidInput;
 
 	/** */
 	public static String DecoratorPreferencesPage_description;
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
new file mode 100644
index 0000000..e2fe54b
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
@@ -0,0 +1,391 @@
+/*******************************************************************************
+ * Copyright (C) 2007, IBM Corporation and others
+ * Copyright (C) 2007, Dave Watson <dwatson@xxxxxxxxxxxx>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@xxxxxxxxxx>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@xxxxxxxxxxx>
+ * Copyright (C) 2008, Google Inc.
+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@xxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+
+package org.spearce.egit.ui.internal.decorators;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.team.core.Team;
+import org.spearce.egit.core.ContainerTreeIterator;
+import org.spearce.egit.core.ContainerTreeIterator.ResourceEntry;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.egit.ui.Activator;
+import org.spearce.egit.ui.UIPreferences;
+import org.spearce.jgit.dircache.DirCache;
+import org.spearce.jgit.dircache.DirCacheEntry;
+import org.spearce.jgit.dircache.DirCacheIterator;
+import org.spearce.jgit.errors.IncorrectObjectTypeException;
+import org.spearce.jgit.errors.MissingObjectException;
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.FileMode;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.revwalk.RevWalk;
+import org.spearce.jgit.treewalk.EmptyTreeIterator;
+import org.spearce.jgit.treewalk.TreeWalk;
+import org.spearce.jgit.treewalk.filter.AndTreeFilter;
+import org.spearce.jgit.treewalk.filter.PathFilterGroup;
+import org.spearce.jgit.treewalk.filter.TreeFilter;
+
+class DecoratableResourceAdapter implements IDecoratableResource {
+
+	private final IResource resource;
+
+	private final RepositoryMapping mapping;
+
+	private final Repository repository;
+
+	private final ObjectId headId;
+
+	private final IPreferenceStore store;
+
+	private String branch = "";
+
+	private boolean tracked = false;
+
+	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;
+
+	@SuppressWarnings("fallthrough")
+	public DecoratableResourceAdapter(IResource resourceToWrap)
+			throws IOException {
+		resource = resourceToWrap;
+		mapping = RepositoryMapping.getMapping(resource);
+		repository = mapping.getRepository();
+		headId = repository.resolve(Constants.HEAD);
+
+		store = Activator.getDefault().getPreferenceStore();
+
+		// TODO: Add option to shorten branch name to 6 chars if it's a SHA
+		branch = repository.getBranch();
+
+		TreeWalk treeWalk = createThreeWayTreeWalk();
+		if (treeWalk == null)
+			return;
+
+		switch (resource.getType()) {
+		case IResource.FILE:
+			if (!treeWalk.next())
+				return;
+			extractResourceProperties(treeWalk);
+			break;
+		case IResource.PROJECT:
+			tracked = true;
+		case IResource.FOLDER:
+			extractContainerProperties(treeWalk);
+			break;
+		}
+	}
+
+	private void extractResourceProperties(TreeWalk treeWalk) {
+		final ContainerTreeIterator workspaceIterator = treeWalk.getTree(
+				T_WORKSPACE, ContainerTreeIterator.class);
+		final ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator
+				.getResourceEntry() : null;
+
+		if (resourceEntry == null)
+			return;
+
+		if (isIgnored(resourceEntry.getResource())) {
+			ignored = true;
+			return;
+		}
+
+		final int mHead = treeWalk.getRawMode(T_HEAD);
+		final int mIndex = treeWalk.getRawMode(T_INDEX);
+
+		if (mHead == FileMode.MISSING.getBits()
+				&& mIndex == FileMode.MISSING.getBits())
+			return;
+
+		tracked = true;
+
+		if (mHead == FileMode.MISSING.getBits()) {
+			staged = Staged.ADDED;
+		} else if (mIndex == FileMode.MISSING.getBits()) {
+			staged = Staged.REMOVED;
+		} else if (mHead != mIndex
+				|| (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual(
+						T_HEAD, T_INDEX))) {
+			staged = Staged.MODIFIED;
+		} else {
+			staged = Staged.NOT_STAGED;
+		}
+
+		final DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX,
+				DirCacheIterator.class);
+		final DirCacheEntry indexEntry = indexIterator != null ? indexIterator
+				.getDirCacheEntry() : null;
+
+		if (indexEntry == null)
+			return;
+
+		if (indexEntry.getStage() > 0)
+			conflicts = true;
+
+		if (indexEntry.isAssumeValid()) {
+			dirty = false;
+			assumeValid = true;
+		} else {
+			if (!timestampMatches(indexEntry, resourceEntry))
+				dirty = true;
+
+			// 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.
+		}
+	}
+
+	private class RecursiveStateFilter extends TreeFilter {
+
+		private int filesChecked = 0;
+
+		private int targetDepth = -1;
+
+		private final int recurseLimit;
+
+		public RecursiveStateFilter() {
+			recurseLimit = store
+					.getInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT);
+		}
+
+		@Override
+		public boolean include(TreeWalk treeWalk)
+				throws MissingObjectException, IncorrectObjectTypeException,
+				IOException {
+
+			if (treeWalk.getFileMode(T_HEAD) == FileMode.MISSING
+					&& treeWalk.getFileMode(T_INDEX) == FileMode.MISSING)
+				return false;
+
+			if (FileMode.TREE.equals(treeWalk.getRawMode(T_WORKSPACE)))
+				return shouldRecurse(treeWalk);
+
+			// Backup current state so far
+			Staged wasStaged = staged;
+			boolean wasDirty = dirty;
+			boolean hadConflicts = conflicts;
+
+			extractResourceProperties(treeWalk);
+			filesChecked++;
+
+			// Merge results with old state
+			ignored = false;
+			assumeValid = false;
+			dirty = wasDirty || dirty;
+			conflicts = hadConflicts || conflicts;
+			if (staged != wasStaged && filesChecked > 1)
+				staged = Staged.MODIFIED;
+
+			return false;
+		}
+
+		private boolean shouldRecurse(TreeWalk treeWalk) {
+			final ContainerTreeIterator workspaceIterator = treeWalk.getTree(
+					T_WORKSPACE, ContainerTreeIterator.class);
+			final ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator
+					.getResourceEntry()
+					: null;
+			IResource visitingResource = resourceEntry.getResource();
+
+			if (targetDepth == -1) {
+				if (visitingResource.equals(resource)
+						|| visitingResource.getParent().equals(resource))
+					targetDepth = treeWalk.getDepth();
+				else
+					return true;
+			}
+
+			if ((treeWalk.getDepth() - targetDepth) >= recurseLimit) {
+				if (visitingResource.equals(resource))
+					extractResourceProperties(treeWalk);
+
+				return false;
+			}
+
+			return true;
+		}
+
+		@Override
+		public TreeFilter clone() {
+			RecursiveStateFilter clone = new RecursiveStateFilter();
+			clone.filesChecked = this.filesChecked;
+			return clone;
+		}
+
+		@Override
+		public boolean shouldBeRecursive() {
+			return true;
+		}
+	}
+
+	private void extractContainerProperties(TreeWalk treeWalk) throws IOException {
+
+		if (isIgnored(resource)) {
+			ignored = true;
+			return;
+		}
+
+		treeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter(),
+				new RecursiveStateFilter()));
+		treeWalk.setRecursive(true);
+
+		treeWalk.next();
+	}
+
+	/**
+	 * 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, no filter is
+	 * added and the method returns <code>false</code>. If the resource is a
+	 * project, no filter is added, but the operation is considered a success.
+	 *
+	 * @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())
+			return false;
+
+		if (repositoryPaths.contains(""))
+			return true; // Project filter
+
+		treeWalk.setFilter(PathFilterGroup.createFromStrings(repositoryPaths));
+		return true;
+	}
+
+	/**
+	 * Helper method to create a new tree walk between the repository, the
+	 * index, and the working tree.
+	 *
+	 * @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 createThreeWayTreeWalk() throws IOException {
+		final TreeWalk treeWalk = new TreeWalk(repository);
+		if (!addResourceFilter(treeWalk, resource))
+			return null;
+
+		treeWalk.setRecursive(treeWalk.getFilter().shouldBeRecursive());
+		treeWalk.reset();
+
+		// Repository
+		if (headId != null)
+			treeWalk.addTree(new RevWalk(repository).parseTree(headId));
+		else
+			treeWalk.addTree(new EmptyTreeIterator());
+
+		// Index
+		treeWalk.addTree(new DirCacheIterator(DirCache.read(repository)));
+
+		// Working directory
+		IProject project = resource.getProject();
+		IWorkspace workspace = resource.getWorkspace();
+		if (repository.getWorkDir().equals(project.getLocation().toFile()))
+			treeWalk.addTree(new ContainerTreeIterator(project));
+		else
+			treeWalk.addTree(new ContainerTreeIterator(workspace.getRoot()));
+
+		// TODO: Add fallback for projects with the repository more than
+		// one parent up, for example by using a stack of DummyIterators
+
+		return treeWalk;
+	}
+
+	private static boolean timestampMatches(DirCacheEntry indexEntry,
+			ResourceEntry resourceEntry) {
+		long tIndex = indexEntry.getLastModified();
+		long tWorkspaceResource = resourceEntry.getLastModified();
+
+
+		// 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 (tIndex % 1000 == 0) {
+			return tIndex == (tWorkspaceResource - (tWorkspaceResource % 1000));
+		} else {
+			return tIndex == tWorkspaceResource;
+		}
+	}
+
+	private static boolean isIgnored(IResource resource) {
+		// TODO: Also read ignores from .git/info/excludes et al.
+		return Team.isIgnoredHint(resource);
+	}
+
+	public String getName() {
+		return resource.getName();
+	}
+
+	public int getType() {
+		return resource.getType();
+	}
+
+	public String getBranch() {
+		return branch;
+	}
+
+	public boolean isTracked() {
+		return tracked;
+	}
+
+	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;
+	}
+}
\ No newline at end of file
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 1e95369..d9de3a4 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
@@ -14,7 +14,6 @@
 package org.spearce.egit.ui.internal.decorators;
 
 import java.io.IOException;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -42,7 +41,6 @@
 import org.eclipse.osgi.util.TextProcessor;
 import org.eclipse.swt.graphics.ImageData;
 import org.eclipse.swt.widgets.Display;
-import org.eclipse.team.core.Team;
 import org.eclipse.team.ui.ISharedImages;
 import org.eclipse.team.ui.TeamImages;
 import org.eclipse.team.ui.TeamUI;
@@ -58,21 +56,11 @@
 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;
 import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.lib.RepositoryChangedEvent;
 import org.spearce.jgit.lib.RepositoryListener;
-import org.spearce.jgit.revwalk.RevWalk;
-import org.spearce.jgit.treewalk.EmptyTreeIterator;
-import org.spearce.jgit.treewalk.TreeWalk;
-import org.spearce.jgit.treewalk.filter.PathFilterGroup;
 
 /**
  * Supplies annotations for displayed resources
@@ -151,6 +139,15 @@ public void decorate(Object element, IDecoration decoration) {
 		if (resource == null)
 			return;
 
+		// Don't decorate if the workbench is not running
+		if (!PlatformUI.isWorkbenchRunning())
+			return;
+
+		// Don't decorate if UI plugin is not running
+		Activator activator = Activator.getDefault();
+		if (activator == null)
+			return;
+
 		// Don't decorate the workspace root
 		if (resource.getType() == IResource.ROOT)
 			return;
@@ -169,11 +166,6 @@ public void decorate(Object element, IDecoration decoration) {
 		if (mapping.getRepoRelativePath(resource) == null)
 			return;
 
-		// Don't decorate if UI plugin is not running
-		Activator activator = Activator.getDefault();
-		if (activator == null)
-			return;
-
 		try {
 			DecorationHelper helper = new DecorationHelper(activator
 					.getPreferenceStore());
@@ -184,238 +176,6 @@ public void decorate(Object element, IDecoration decoration) {
 		}
 	}
 
-	private class DecoratableResourceAdapter implements IDecoratableResource {
-
-		private final IResource resource;
-
-		private final RepositoryMapping mapping;
-
-		private final Repository repository;
-
-		private final ObjectId headId;
-
-		private String branch = "";
-
-		private boolean tracked = false;
-
-		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;
-			mapping = RepositoryMapping.getMapping(resource);
-			repository = mapping.getRepository();
-			headId = repository.resolve(Constants.HEAD);
-
-			switch (resource.getType()) {
-			case IResource.FILE:
-				extractFileProperties();
-				break;
-			case IResource.FOLDER:
-				extractContainerProperties();
-				break;
-			case IResource.PROJECT:
-				extractProjectProperties();
-				break;
-			}
-		}
-
-		private void extractFileProperties() 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;
-			}
-
-			final DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX,
-					DirCacheIterator.class);
-			final DirCacheEntry indexEntry = indexIterator != null ? indexIterator
-					.getDirCacheEntry()
-					: null;
-
-			if (indexEntry == null) {
-				staged = Staged.REMOVED;
-			} else {
-				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;
-					}
-
-					if (treeWalk.getRawMode(T_HEAD) != treeWalk
-							.getRawMode(T_INDEX)
-							|| !treeWalk.idEqual(T_HEAD, T_INDEX)) {
-						staged = Staged.MODIFIED;
-					}
-				}
-			}
-
-		}
-
-		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() {
-			return resource.getName();
-		}
-
-		public int getType() {
-			return resource.getType();
-		}
-
-		public String getBranch() {
-			return branch;
-		}
-
-		public boolean isTracked() {
-			return tracked;
-		}
-
-		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;
-		}
-	}
-
 	/**
 	 * Helper class for doing resource decoration, based on the given
 	 * preferences
@@ -570,11 +330,13 @@ else if (staged == Staged.REMOVED)
 				}
 
 				// Conflicts override everything
-				if (store.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)
+				if (store
+						.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)
 						&& resource.hasConflicts())
 					overlay = conflictImage;
 
-			} else if (store.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {
+			} else if (store
+					.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {
 				overlay = untrackedImage;
 			}
 
@@ -714,6 +476,14 @@ public void resourceChanged(IResourceChangeEvent event) {
 		try { // Compute the changed resources by looking at the delta
 			event.getDelta().accept(new IResourceDeltaVisitor() {
 				public boolean visit(IResourceDelta delta) throws CoreException {
+
+					// If the file has changed but not in a way that we care
+					// about (e.g. marker changes to files) then ignore
+					if (delta.getKind() == IResourceDelta.CHANGED
+							&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {
+						return true;
+					}
+
 					final IResource resource = delta.getResource();
 
 					// If the resource is not part of a project under Git
@@ -724,6 +494,7 @@ public boolean visit(IResourceDelta delta) throws CoreException {
 						// Ignore the change
 						return true;
 					}
+
 					if (resource.getType() == IResource.ROOT) {
 						// Continue with the delta
 						return true;
@@ -735,33 +506,32 @@ public boolean visit(IResourceDelta delta) throws CoreException {
 							return false;
 					}
 
-					// If the file has changed but not in a way that we care
-					// about
-					// (e.g. marker changes to files) then ignore the change
-					if (delta.getKind() == IResourceDelta.CHANGED
-							&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {
-						return true;
-					}
-
 					// All seems good, schedule the resource for update
 					resourcesToUpdate.add(resource);
-					return true;
+
+					if (delta.getKind() == IResourceDelta.CHANGED
+							&& (delta.getFlags() & IResourceDelta.OPEN) > 1)
+						return false; // Don't recurse when opening projects
+					else
+						return true;
 				}
 			}, true /* includePhantoms */);
 		} catch (final CoreException e) {
 			handleException(null, e);
 		}
 
-		// If deep decorator calculation is enabled in the preferences we
-		// walk the ancestor tree of each of the changed resources and add
+		if (resourcesToUpdate.isEmpty())
+			return;
+
+		// If ancestor-decoration is enabled in the preferences we walk
+		// the ancestor tree of each of the changed resources and add
 		// their parents to the update set
 		final IPreferenceStore store = Activator.getDefault()
 				.getPreferenceStore();
-		if (store.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY)) {
+		if (store.getBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS)) {
 			final IResource[] changedResources = resourcesToUpdate
 					.toArray(new IResource[resourcesToUpdate.size()]);
-			for (int i = 0; i < changedResources.length; i++) {
-				IResource current = changedResources[i];
+			for (IResource current : changedResources) {
 				while (current.getType() != IResource.ROOT) {
 					current = current.getParent();
 					resourcesToUpdate.add(current);
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 f72ceb7..1c0f82e 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
@@ -51,12 +51,14 @@
 import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 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.Scale;
 import org.eclipse.swt.widgets.TabFolder;
 import org.eclipse.swt.widgets.TabItem;
 import org.eclipse.swt.widgets.Text;
@@ -76,6 +78,7 @@
 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
  */
@@ -88,7 +91,9 @@
 
 	private Text projectTextFormat;
 
-	private Button computeDeepDirtyState;
+	private Button recomputeAncestorDecorations;
+
+	private Scale containerRecurseLimit;
 
 	private Button showTracked;
 
@@ -108,11 +113,11 @@
 
 	static {
 		final PreviewResource project = new PreviewResource(
-				"Project", IResource.PROJECT, "master", true, false, false, Staged.NOT_STAGED, false, false); //$NON-NLS-1$1
+				"Project", IResource.PROJECT, "master", true, false, true, 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$
+						"folder", IResource.FOLDER, null, true, false, true, 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(
@@ -194,14 +199,68 @@ public void propertyChange(PropertyChangeEvent event) {
 
 	private Control createGeneralDecoratorPage(Composite parent) {
 		Composite composite = SWTUtils.createHVFillComposite(parent,
-				SWTUtils.MARGINS_DEFAULT);
+				SWTUtils.MARGINS_DEFAULT, 1);
 
-		computeDeepDirtyState = SWTUtils.createCheckBox(composite,
-				UIText.DecoratorPreferencesPage_computeDeep);
+		recomputeAncestorDecorations = SWTUtils.createCheckBox(composite,
+				UIText.DecoratorPreferencesPage_recomputeAncestorDecorations);
+		recomputeAncestorDecorations
+				.setToolTipText(UIText.DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip);
+
+		SWTUtils.createLabel(composite,
+				UIText.DecoratorPreferencesPage_computeRecursiveLimit);
+		containerRecurseLimit = createLabeledScaleControl(composite);
+		containerRecurseLimit
+				.setToolTipText(UIText.DecoratorPreferencesPage_computeRecursiveLimitTooltip);
 
 		return composite;
 	}
 
+	private Scale createLabeledScaleControl(Composite parent) {
+
+		final int[] values = new int[] { 0, 1, 2, 3, 5, 10, 15, 20, 50, 100,
+				Integer.MAX_VALUE };
+
+		Composite composite = SWTUtils.createHVFillComposite(parent,
+				SWTUtils.MARGINS_DEFAULT);
+
+		Composite labels = SWTUtils.createHVFillComposite(composite,
+				SWTUtils.MARGINS_NONE, values.length);
+		GridLayout labelsLayout = (GridLayout) labels.getLayout();
+		labelsLayout.makeColumnsEqualWidth = true;
+		labelsLayout.horizontalSpacing = 0;
+		labels.setLayoutData(SWTUtils.createGridData(-1, -1, SWT.FILL,
+				SWT.FILL, false, false));
+
+		for (int i = 0; i < values.length; ++i) {
+			Label label = SWTUtils.createLabel(labels, "" + values[i]);
+			if (i == 0) {
+				label.setAlignment(SWT.LEFT);
+				label.setText("Off");
+			} else if (i == values.length - 1) {
+				label.setAlignment(SWT.RIGHT);
+				label.setText("Inf.");
+			} else {
+				label.setAlignment(SWT.CENTER);
+			}
+		}
+
+		final Scale scale = new Scale(composite, SWT.HORIZONTAL);
+		scale.setLayoutData(SWTUtils.createHVFillGridData());
+		scale.setMaximum(values.length - 1);
+		scale.setMinimum(0);
+		scale.setIncrement(1);
+		scale.setPageIncrement(1);
+
+		scale.addListener(SWT.Selection, new Listener() {
+			public void handleEvent(Event event) {
+				// Workaround for GTK treating the slider as stepless
+				scale.setSelection(scale.getSelection());
+			}
+		});
+
+		return scale;
+	}
+
 	/**
 	 * Creates the controls for the first tab folder
 	 * 
@@ -301,8 +360,10 @@ public void handleEvent(Event event) {
 	private void initializeValues() {
 		final IPreferenceStore store = getPreferenceStore();
 
-		computeDeepDirtyState.setSelection(store
-				.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
+		recomputeAncestorDecorations.setSelection(store
+				.getBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS));
+		containerRecurseLimit.setSelection(store
+				.getInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT));
 
 		fileTextFormat.setText(store
 				.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
@@ -328,7 +389,6 @@ public void widgetSelected(SelectionEvent e) {
 			}
 		};
 
-		computeDeepDirtyState.addSelectionListener(selectionListener);
 		showTracked.addSelectionListener(selectionListener);
 		showUntracked.addSelectionListener(selectionListener);
 		showStaged.addSelectionListener(selectionListener);
@@ -371,8 +431,10 @@ public boolean performOk() {
 	 */
 	private boolean performOk(IPreferenceStore store) {
 
-		store.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY,
-				computeDeepDirtyState.getSelection());
+		store.setValue(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS,
+				recomputeAncestorDecorations.getSelection());
+		store.setValue(UIPreferences.DECORATOR_RECURSIVE_LIMIT,
+				containerRecurseLimit.getSelection());
 
 		store.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,
 				fileTextFormat.getText());
@@ -403,8 +465,11 @@ protected void performDefaults() {
 		super.performDefaults();
 		IPreferenceStore store = getPreferenceStore();
 
-		computeDeepDirtyState.setSelection(store
-				.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
+		recomputeAncestorDecorations
+				.setSelection(store
+						.getDefaultBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS));
+		containerRecurseLimit.setSelection(store
+				.getDefaultInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT));
 
 		fileTextFormat.setText(store
 				.getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
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 e9a2321..847cb2c 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
@@ -350,9 +350,12 @@ Decorator_exceptionMessage=Errors occurred while applying Git decorations to res
 
 DecoratorPreferencesPage_addVariablesTitle=Add Variables
 DecoratorPreferencesPage_addVariablesAction=Add &Variables...
-DecoratorPreferencesPage_computeDeep=Include &ancestors when re-decorating changed resources
+DecoratorPreferencesPage_recomputeAncestorDecorations=Re-decorate &ancestors when decorating changed resources
+DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip=Enabling this option will cause the ancestor-tree of any updated resources to also be re-decorated (minor performance impact).
+DecoratorPreferencesPage_computeRecursiveLimit=Maximum number of levels to recurse for container decoration:
+DecoratorPreferencesPage_computeRecursiveLimitTooltip=This value determines the maximum number of levels that will be recursed for each container decoration before bailing out. Increasing this number will improve accuracy for container decoration, but has a performance impact for large projects.
 DecoratorPreferencesPage_description=Shows Git specific information on resources in projects under version control.
-
+DecoratorPreferencesPage_invalidInput=''{0}'' is not a valid input
 DecoratorPreferencesPage_decorationSettings=Decoration &settings:
 DecoratorPreferencesPage_preview=Preview:
 DecoratorPreferencesPage_fileFormatLabel=&Files:
-- 
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

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux