{"thread":{"id":"17728","subject":"[EGIT PATCH v2 06/12] Implement basic customizable label decorations with preferences","startedAt":"2009-02-11T18:40:02Z","lastAt":"2009-02-17T17:52:42Z","messageCount":21,"participants":["Tor Arne Vestbø","Robin Rosenberg"],"isPatch":true,"patchVersion":2,"patchTotal":12},"messages":[{"id":"104255","messageId":"1234377614-23798-1-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":null,"subject":"[EGIT PATCH v2 00/12] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:02Z","receivedAt":"2009-02-11T18:40:02Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"This series adds support for customizable label decorations, which\nis usefull for hiding selected decorations, or tweaking the format\nof the decoration text.\n\nChanges in v2:\n\n  - Fixed layout issues in preference dialog\n  - Icons replaced to match established conventions\n  - 'Assume unchanged' icon is back\n  - Added tooltips in the preference dialog\n  - 'Added and dirty' is now decorated appropriately\n  - Refactored container decorations. Now shares code path with\n    files for more accurate results, and to allow inheritance\n\nDecorations are edited using the new Team->Git->Label Decorations\npreference page, which is based off similar functionality from the\nexisting CVS and SVN team providers.\n\nIcons can be enabled and disabled individually, and text can be\ncustomized by reordering and editing the set of mapped variables.\nBoolean variables like 'dirty' and 'staged' can be customized by\npostfixing the variable name with a colon and a selected string\nthat should be insert if the variable evaluates to true.\n\nThe two general options control traversal of child and parent\nelements during decoration. The first, 'Also re-decorate...',\ncontrols whether or not ancestor elements of the current decorated\nelment will also be scheduled for re-recoration. The second, \n'Maximum number of levels...', controls how deep the container\ndecoration algorithm will recurse when trying to determine the\nstate (dirty, staged, etc.) of a container.\n\nTweaking these options will improve performance for large trees.\n\nThe code should work fairly well for most usecases, but I may have\nmissed cases where the decorations will fail misserably. If so, \nplease let me know. \n\nKnown issues are:\n\n  - If a project has a repository more than one level above the\n    project directory decorations will fail.\n\n  - When a Java resource is dirty, each package in the package\n    hierarcy will appear dirty, also when the layout is set\n    to 'flat', which can be confusing.\n\nThese are on my list for features to add on top of these series,\nbut I consider them non-blocking. This also goes for online help,\nwhich will be added when things stabilize some more.\n\nI've also sprinkled the code with TODOs where I found possible \nfuture improvments. One such improvment is performance, where for\nexample refactoring to use one shared status cache should help.\n\nTor Arne\n\n\n\nTor Arne VestbÃ¸ (12):\n  Add support code to handle plugin property changes\n  Use Set instead of array to keep track of change listeners\n  Add a specialized team exception for Git\n  Add new class ExceptionCollector for grouping exceptions\n  Add new class SWTUtils with helper-methods for creating controls\n  Implement basic customizable label decorations with preferences\n  Add binding for name of the current branch\n  Add icon decoration for tracked and untracked resources\n  Implement icon and text decorations of various resource states\n  Don't decorate every single resource on repository change\n  Expose the underlying resource entries in ContainerTreeIterator\n  Implement label decorations for folders and projects\n\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    5 +-\n .../spearce/egit/core/ContainerTreeIterator.java   |   23 +-\n .../src/org/spearce/egit/core/GitException.java    |  168 ++++\n .../core/internal/util/ExceptionCollector.java     |  128 +++\n .../spearce/egit/core/project/GitProjectData.java  |   40 +-\n org.spearce.egit.ui/icons/ovr/assume_valid.gif     |  Bin 0 -> 85 bytes\n org.spearce.egit.ui/icons/ovr/assumevalid.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/conflict.gif         |  Bin 64 -> 194 bytes\n org.spearce.egit.ui/icons/ovr/pending_add.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/pending_remove.gif   |  Bin 111 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/shared.gif           |  Bin 106 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/staged.gif           |  Bin 0 -> 114 bytes\n org.spearce.egit.ui/icons/ovr/staged_added.gif     |  Bin 0 -> 169 bytes\n org.spearce.egit.ui/icons/ovr/staged_removed.gif   |  Bin 0 -> 176 bytes\n org.spearce.egit.ui/icons/ovr/untracked.gif        |  Bin 0 -> 79 bytes\n org.spearce.egit.ui/plugin.properties              |    1 +\n org.spearce.egit.ui/plugin.xml                     |   12 +-\n .../src/org/spearce/egit/ui/Activator.java         |   68 ++\n .../egit/ui/PluginPreferenceInitializer.java       |   15 +\n .../src/org/spearce/egit/ui/UIIcons.java           |   21 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |   21 +\n .../src/org/spearce/egit/ui/UIText.java            |   99 ++-\n .../src/org/spearce/egit/ui/internal/SWTUtils.java |  595 ++++++++++++\n .../egit/ui/internal/actions/BranchAction.java     |    4 +-\n .../egit/ui/internal/actions/Disconnect.java       |    4 +-\n .../egit/ui/internal/actions/ResetAction.java      |    4 +-\n .../decorators/DecoratableResourceAdapter.java     |  391 ++++++++\n .../decorators/GitLightweightDecorator.java        |  653 ++++++++++++++\n .../internal/decorators/GitResourceDecorator.java  |  454 ----------\n .../internal/decorators/IDecoratableResource.java  |  100 ++\n .../preferences/GitDecoratorPreferencePage.java    |  949 ++++++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   38 +-\n .../src/org/spearce/jgit/treewalk/TreeWalk.java    |    9 +\n 33 files changed, 3310 insertions(+), 492 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/GitException.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/internal/util/ExceptionCollector.java\n create mode 100644 org.spearce.egit.ui/icons/ovr/assume_valid.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/assumevalid.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_add.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_remove.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/shared.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged_added.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged_removed.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/untracked.gif\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/SWTUtils.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n"},{"id":"104248","messageId":"1234377614-23798-2-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-1-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 01/12] Add support code to handle plugin property changes","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:03Z","receivedAt":"2009-02-11T18:40:03Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Signed-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../src/org/spearce/egit/ui/Activator.java         |   52 ++++++++++++++++++++\n 1 files changed, 52 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\nindex fced643..534c408 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\n@@ -10,9 +10,11 @@\n \n import java.net.Authenticator;\n import java.net.ProxySelector;\n+import java.util.ArrayList;\n import java.util.HashSet;\n import java.util.Iterator;\n import java.util.LinkedHashSet;\n+import java.util.List;\n import java.util.Set;\n \n import org.eclipse.core.net.proxy.IProxyService;\n@@ -27,6 +29,8 @@\n import org.eclipse.core.runtime.SubProgressMonitor;\n import org.eclipse.core.runtime.jobs.ISchedulingRule;\n import org.eclipse.core.runtime.jobs.Job;\n+import org.eclipse.jface.util.IPropertyChangeListener;\n+import org.eclipse.jface.util.PropertyChangeEvent;\n import org.eclipse.jsch.core.IJSchService;\n import org.eclipse.swt.graphics.Font;\n import org.eclipse.ui.plugin.AbstractUIPlugin;\n@@ -44,9 +48,24 @@\n  * This is a plugin singleton mostly controlling logging.\n  */\n public class Activator extends AbstractUIPlugin {\n+\n+\t/**\n+\t *  The one and only instance\n+\t */\n \tprivate static Activator plugin;\n \n \t/**\n+\t * Property listeners for plugin specific events\n+\t */\n+\tprivate static List<IPropertyChangeListener> propertyChangeListeners =\n+\t\tnew ArrayList<IPropertyChangeListener>(5);\n+\n+\t/**\n+\t * Property constant indicating the decorator configuration has changed.\n+\t */\n+\tpublic static final String DECORATORS_CHANGED = \"org.spearce.egit.ui.DECORATORS_CHANGED\"; //$NON-NLS-1$\n+\n+\t/**\n \t * @return the {@link Activator} singleton.\n \t */\n \tpublic static Activator getDefault() {\n@@ -167,6 +186,39 @@ private void setupRepoIndexRefresh() {\n \t\tRepository.addAnyRepositoryChangedListener(refreshJob);\n \t}\n \n+\t/**\n+\t * Register for changes made to Team properties.\n+\t * \n+\t * @param listener\n+\t *            The listener to register\n+\t */\n+\tpublic static synchronized void addPropertyChangeListener(\n+\t\t\tIPropertyChangeListener listener) {\n+\t\tpropertyChangeListeners.add(listener);\n+\t}\n+\n+\t/**\n+\t * Remove a Team property changes.\n+\t * \n+\t * @param listener\n+\t *            The listener to remove\n+\t */\n+\tpublic static synchronized void removePropertyChangeListener(\n+\t\t\tIPropertyChangeListener listener) {\n+\t\tpropertyChangeListeners.remove(listener);\n+\t}\n+\n+\t/**\n+\t * Broadcast a Team property change.\n+\t * \n+\t * @param event\n+\t *            The event to broadcast\n+\t */\n+\tpublic static synchronized void broadcastPropertyChange(PropertyChangeEvent event) {\n+\t\tfor (IPropertyChangeListener listener : propertyChangeListeners)\n+\t\t\tlistener.propertyChange(event);\n+\t}\n+\n \tstatic class RIRefresh extends Job implements RepositoryListener {\n \n \t\tRIRefresh() {\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104249","messageId":"1234377614-23798-3-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-2-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 02/12] Use Set instead of array to keep track of change listeners","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:04Z","receivedAt":"2009-02-11T18:40:04Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Also, add method for removing listeners.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../spearce/egit/core/project/GitProjectData.java  |   40 ++++++++++++-------\n 1 files changed, 25 insertions(+), 15 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\nindex db5f20b..b12a85f 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n@@ -52,7 +52,7 @@\n \n \tprivate static final Map<File, WeakReference> repositoryCache = new HashMap<File, WeakReference>();\n \n-\tprivate static RepositoryChangeListener[] repositoryChangeListeners = {};\n+\tprivate static Set<RepositoryChangeListener> repositoryChangeListeners = new HashSet<RepositoryChangeListener>();\n \n \t@SuppressWarnings(\"synthetic-access\")\n \tprivate static final IResourceChangeListener rcl = new RCL();\n@@ -112,16 +112,18 @@ public static synchronized void addRepositoryChangeListener(\n \t\t\tfinal RepositoryChangeListener objectThatCares) {\n \t\tif (objectThatCares == null)\n \t\t\tthrow new NullPointerException();\n-\t\tfor (int k = repositoryChangeListeners.length - 1; k >= 0; k--) {\n-\t\t\tif (repositoryChangeListeners[k] == objectThatCares)\n-\t\t\t\treturn;\n-\t\t}\n-\t\tfinal int p = repositoryChangeListeners.length;\n-\t\tfinal RepositoryChangeListener[] n;\n-\t\tn = new RepositoryChangeListener[p + 1];\n-\t\tSystem.arraycopy(repositoryChangeListeners, 0, n, 0, p);\n-\t\tn[p] = objectThatCares;\n-\t\trepositoryChangeListeners = n;\n+\t\trepositoryChangeListeners.add(objectThatCares);\n+\t}\n+\n+\t/**\n+\t * Remove a registered {@link RepositoryChangeListener}\n+\t * \n+\t * @param objectThatCares\n+\t *            The listener to remove\n+\t */\n+\tpublic static synchronized void removeRepositoryChangeListener(\n+\t\t\tfinal RepositoryChangeListener objectThatCares) {\n+\t\trepositoryChangeListeners.remove(objectThatCares);\n \t}\n \n \t/**\n@@ -131,13 +133,21 @@ public static synchronized void addRepositoryChangeListener(\n \t *            the repository which has had changes occur within it.\n \t */\n \tstatic void fireRepositoryChanged(final RepositoryMapping which) {\n-\t\tfinal RepositoryChangeListener[] e = getRepositoryChangeListeners();\n-\t\tfor (int k = e.length - 1; k >= 0; k--)\n-\t\t\te[k].repositoryChanged(which);\n+\t\tfor (RepositoryChangeListener listener : getRepositoryChangeListeners())\n+\t\t\tlistener.repositoryChanged(which);\n \t}\n \n+\t/**\n+\t * Get a copy of the current set of repository change listeners\n+\t * <p>\n+\t * The array has no references, so is safe for iteration and modification\n+\t * \n+\t * @return a copy of the current repository change listeners\n+\t */\n \tprivate static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {\n-\t\treturn repositoryChangeListeners;\n+\t\treturn repositoryChangeListeners\n+\t\t\t\t.toArray(new RepositoryChangeListener[repositoryChangeListeners\n+\t\t\t\t\t\t.size()]);\n \t}\n \n \t/**\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104250","messageId":"1234377614-23798-4-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-3-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 03/12] Add a specialized team exception for Git","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:05Z","receivedAt":"2009-02-11T18:40:05Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Copied largly from org.eclipse.team.internal.ccvs.core (CVS) and\norg.tigris.subversion.subclipse.core (SVN), and then cleaned up.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../src/org/spearce/egit/core/GitException.java    |  168 ++++++++++++++++++++\n 1 files changed, 168 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/GitException.java\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/GitException.java b/org.spearce.egit.core/src/org/spearce/egit/core/GitException.java\nnew file mode 100644\nindex 0000000..7217fb7\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitException.java\n@@ -0,0 +1,168 @@\n+/*******************************************************************************\n+ * Copyright (c) 2000, 2007 IBM Corporation and others.\n+ * Copyright (c) 2003, 2006 Subclipse project and others.\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.core;\n+\n+import java.io.IOException;\n+import java.lang.reflect.InvocationTargetException;\n+\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.runtime.*;\n+import org.eclipse.team.core.TeamException;\n+import org.eclipse.team.core.TeamStatus;\n+\n+/**\n+ * A checked exception representing a failure in the Git plugin.\n+ * <p>\n+ * Git exceptions contain a status object describing the cause of the exception.\n+ * </p>\n+ * \n+ * @see IStatus\n+ */\n+public class GitException extends TeamException {\n+\n+\tprivate static final long serialVersionUID = 1L;\n+\n+\t/**\n+\t * Constructs a new Git exception\n+\t * \n+\t * @param severity\n+\t * @param code\n+\t * @param message\n+\t * @param e\n+\t */\n+\tpublic GitException(int severity, int code, String message, Throwable e) {\n+\t\tsuper(new TeamStatus(severity, Activator.getPluginId(), code, message,\n+\t\t\t\te, null));\n+\t}\n+\n+\t/**\n+\t * Constructs a new Git exception\n+\t * \n+\t * @param severity\n+\t * @param code\n+\t * @param message\n+\t */\n+\tpublic GitException(int severity, int code, String message) {\n+\t\tthis(severity, code, message, null);\n+\t}\n+\n+\t/**\n+\t * Constructs a new Git exception\n+\t * \n+\t * @param message\n+\t * @param e\n+\t */\n+\tpublic GitException(String message, Throwable e) {\n+\t\tthis(IStatus.ERROR, UNABLE, message, e);\n+\t}\n+\n+\t/**\n+\t * Constructs a new Git exception\n+\t * \n+\t * @param message\n+\t */\n+\tpublic GitException(String message) {\n+\t\tthis(message, null);\n+\t}\n+\n+\t/**\n+\t * Constructs a new Git exception\n+\t * \n+\t * @param status\n+\t */\n+\tpublic GitException(IStatus status) {\n+\t\tsuper(status);\n+\t}\n+\n+\t/**\n+\t * Transform this exception into a CoreException\n+\t * \n+\t * @return the new CoreException\n+\t */\n+\tpublic CoreException toCoreException() {\n+\t\tIStatus status = getStatus();\n+\t\treturn new CoreException(new Status(status.getSeverity(), status\n+\t\t\t\t.getPlugin(), 0, status.getMessage(), this));\n+\t}\n+\n+\t/**\n+\t * Static helper method for creating a Git exception\n+\t * \n+\t * @param resource\n+\t * @param message\n+\t * @param e\n+\t * @return the created exception\n+\t */\n+\tpublic static GitException wrapException(IResource resource,\n+\t\t\tString message, CoreException e) {\n+\t\treturn new GitException(IStatus.ERROR, e.getStatus().getCode(),\n+\t\t\t\tmessage, e);\n+\t}\n+\n+\t/**\n+\t * Static helper method for creating a Git exception\n+\t * \n+\t * @param e\n+\t * @return the created exception\n+\t */\n+\tpublic static GitException wrapException(Exception e) {\n+\t\tThrowable t = e;\n+\t\tif (e instanceof InvocationTargetException) {\n+\t\t\tThrowable target = ((InvocationTargetException) e)\n+\t\t\t\t\t.getTargetException();\n+\t\t\tif (target instanceof GitException) {\n+\t\t\t\treturn (GitException) target;\n+\t\t\t}\n+\t\t\tt = target;\n+\t\t}\n+\n+\t\treturn new GitException(IStatus.ERROR, UNABLE,\n+\t\t\t\tt.getMessage() != null ? t.getMessage() : \"\", t); //$NON-NLS-1$\n+\t}\n+\n+\t/**\n+\t * Static helper method for creating a Git exception\n+\t * \n+\t * @param e\n+\t * @return the created exception\n+\t */\n+\tpublic static GitException wrapException(CoreException e) {\n+\t\tIStatus status = e.getStatus();\n+\t\tif (!status.isMultiStatus()) {\n+\t\t\tstatus = new TeamStatus(status.getSeverity(), Activator\n+\t\t\t\t\t.getPluginId(), status.getCode(), status.getMessage(), e,\n+\t\t\t\t\tnull);\n+\t\t}\n+\t\treturn new GitException(status);\n+\t}\n+\n+\t/**\n+\t * Static helper method for creating a Git exception\n+\t * \n+\t * @param e\n+\t * @return the created exception\n+\t */\n+\tpublic static GitException wrapException(IOException e) {\n+\t\treturn new GitException(IStatus.ERROR, IO_FAILED, e.getMessage(), e);\n+\t}\n+\n+\t/**\n+\t * Static helper method for creating a Git exception\n+\t * \n+\t * @param e\n+\t * @return the created exception\n+\t */\n+\tpublic static GitException wrapException(TeamException e) {\n+\t\tif (e instanceof GitException)\n+\t\t\treturn (GitException) e;\n+\t\telse\n+\t\t\treturn new GitException(e.getStatus());\n+\t}\n+}\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104259","messageId":"1234377614-23798-5-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-4-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 04/12] Add new class ExceptionCollector for grouping exceptions","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:06Z","receivedAt":"2009-02-11T18:40:06Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Copied from org.eclipse.team.internal.core\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    5 +-\n .../core/internal/util/ExceptionCollector.java     |  128 ++++++++++++++++++++\n 2 files changed, 131 insertions(+), 2 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/internal/util/ExceptionCollector.java\n\ndiff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF\nindex e13732b..20df15f 100644\n--- a/org.spearce.egit.core/META-INF/MANIFEST.MF\n+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF\n@@ -12,8 +12,9 @@ Require-Bundle: org.eclipse.core.runtime,\n  org.spearce.jgit,\n  org.eclipse.core.filesystem,\n  org.eclipse.ui\n-Export-Package: org.spearce.egit.core.internal.storage;x-friends:=\"org.spearce.egit.ui\",\n- org.spearce.egit.core,\n+Export-Package: org.spearce.egit.core,\n+ org.spearce.egit.core.internal.storage;x-friends:=\"org.spearce.egit.ui\",\n+ org.spearce.egit.core.internal.util;x-friends:=\"org.spearce.egit.ui\",\n  org.spearce.egit.core.op,\n  org.spearce.egit.core.project\n Bundle-ActivationPolicy: lazy\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/internal/util/ExceptionCollector.java b/org.spearce.egit.core/src/org/spearce/egit/core/internal/util/ExceptionCollector.java\nnew file mode 100644\nindex 0000000..d99d651\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/internal/util/ExceptionCollector.java\n@@ -0,0 +1,128 @@\n+/*******************************************************************************\n+ * Copyright (c) 2000, 2006 IBM Corporation and others.\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * which accompanies this distribution, and is available at\n+ * http://www.eclipse.org/legal/epl-v10.html\n+ *\n+ * Contributors:\n+ *     IBM Corporation - initial API and implementation\n+ *******************************************************************************/\n+package org.spearce.egit.core.internal.util;\n+\n+import java.util.ArrayList;\n+import java.util.Iterator;\n+import java.util.List;\n+\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.ILog;\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.MultiStatus;\n+import org.eclipse.core.runtime.Status;\n+\n+/**\n+ * Collects exceptions and can be configured to ignore duplicates exceptions.\n+ * Exceptions can be logged and a MultiStatus containing all collected\n+ * exceptions can be returned.\n+ * \n+ * @see org.eclipse.core.runtime.MultiStatus\n+ * @see org.eclipse.core.runtime.IStatus\n+ * \n+ * @since 3.0\n+ */\n+public class ExceptionCollector {\n+\n+\tprivate final List<IStatus> statuses = new ArrayList<IStatus>();\n+\n+\tprivate final String message;\n+\n+\tprivate final String pluginId;\n+\n+\tprivate final int severity;\n+\n+\tprivate final ILog log;\n+\n+\t/**\n+\t * Creates a collector and initializes the parameters for the top-level\n+\t * exception that would be returned from <code>getStatus</code> is\n+\t * exceptions are collected.\n+\t * \n+\t * @param message\n+\t *            a human-readable message, localized to the current locale\n+\t * @param pluginId\n+\t *            the unique identifier of the relevant plug-in\n+\t * @param severity\n+\t *            the severity; one of <code>OK</code>, <code>ERROR</code>,\n+\t *            <code>INFO</code>, or <code>WARNING</code>\n+\t * @param log\n+\t *            the log to output the exceptions to, or <code>null</code> if\n+\t *            exceptions should not be logged.\n+\t */\n+\tpublic ExceptionCollector(String message, String pluginId, int severity,\n+\t\t\tILog log) {\n+\t\tthis.message = message;\n+\t\tthis.pluginId = pluginId;\n+\t\tthis.severity = severity;\n+\t\tthis.log = log;\n+\t}\n+\n+\t/**\n+\t * Clears the exceptions collected.\n+\t */\n+\tpublic void clear() {\n+\t\tstatuses.clear();\n+\t}\n+\n+\t/**\n+\t * Returns a status that represents the exceptions collected. If the\n+\t * collector is empty <code>IStatus.OK</code> is returned. Otherwise a\n+\t * MultiStatus containing all collected exceptions is returned.\n+\t * \n+\t * @return a multistatus containing the exceptions collected or IStatus.OK\n+\t *         if the collector is empty.\n+\t */\n+\tpublic IStatus getStatus() {\n+\t\tif (statuses.isEmpty()) {\n+\t\t\treturn Status.OK_STATUS;\n+\t\t} else {\n+\t\t\tfinal MultiStatus multiStatus = new MultiStatus(pluginId, severity,\n+\t\t\t\t\tmessage, null);\n+\t\t\tfinal Iterator it = statuses.iterator();\n+\t\t\twhile (it.hasNext()) {\n+\t\t\t\tfinal IStatus status = (IStatus) it.next();\n+\t\t\t\tmultiStatus.merge(status);\n+\t\t\t}\n+\t\t\treturn multiStatus;\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Add this exception to the collector. If a log was specified in the\n+\t * constructor then the exception will be output to the log. You can\n+\t * retreive exceptions using <code>getStatus</code>.\n+\t * \n+\t * @param exception\n+\t *            the exception to collect\n+\t */\n+\tpublic void handleException(CoreException exception) {\n+\t\tif (log != null) {\n+\t\t\tlog.log(new Status(severity, pluginId, 0, message, exception));\n+\t\t}\n+\n+\t\t// Record each status individually to flatten the resulting multi-status\n+\t\tfinal IStatus exceptionStatus = exception.getStatus();\n+\n+\t\t// Wrap the exception so the stack trace is not lost.\n+\t\tfinal IStatus status = new Status(exceptionStatus.getSeverity(),\n+\t\t\t\texceptionStatus.getPlugin(), exceptionStatus.getCode(),\n+\t\t\t\texceptionStatus.getMessage(), exception);\n+\n+\t\trecordStatus(status);\n+\t\tfor (IStatus childStatus : status.getChildren())\n+\t\t\trecordStatus(childStatus);\n+\t}\n+\n+\tprivate void recordStatus(IStatus status) {\n+\t\tstatuses.add(status);\n+\t}\n+}\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104254","messageId":"1234377614-23798-6-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-5-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 05/12] Add new class SWTUtils with helper-methods for creating controls","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:07Z","receivedAt":"2009-02-11T18:40:07Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Copied verbatim from org.eclipse.team.internal.ui and documented\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../src/org/spearce/egit/ui/internal/SWTUtils.java |  595 ++++++++++++++++++++\n 1 files changed, 595 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/SWTUtils.java\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/SWTUtils.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/SWTUtils.java\nnew file mode 100644\nindex 0000000..fe65bbb\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/SWTUtils.java\n@@ -0,0 +1,595 @@\n+/*******************************************************************************\n+ * Copyright (c) 2000, 2005 IBM Corporation and others.\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * which accompanies this distribution, and is available at\n+ * http://www.eclipse.org/legal/epl-v10.html\n+ *\n+ * Contributors:\n+ *\t IBM Corporation - initial API and implementation\n+ *******************************************************************************/\n+\n+package org.spearce.egit.ui.internal;\n+\n+import org.eclipse.core.runtime.Assert;\n+import org.eclipse.jface.dialogs.Dialog;\n+import org.eclipse.jface.dialogs.IDialogConstants;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.graphics.FontMetrics;\n+import org.eclipse.swt.graphics.GC;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n+import org.eclipse.swt.widgets.*;\n+import org.eclipse.ui.dialogs.PreferenceLinkArea;\n+import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;\n+\n+/**\n+ * A collection of factory methods for creating common SWT controls\n+ */\n+public class SWTUtils {\n+\n+\t/** */\n+\tpublic static final int MARGINS_DEFAULT = -1;\n+\n+\t/** */\n+\tpublic static final int MARGINS_NONE = 0;\n+\n+\t/** */\n+\tpublic static final int MARGINS_DIALOG = 1;\n+\n+\t/**\n+\t * Creates a preference link which will open in the specified container\n+\t * \n+\t * @param container\n+\t * @param parent\n+\t * @param pageId\n+\t * @param text\n+\t * \n+\t * @return the created link\n+\t */\n+\tpublic static PreferenceLinkArea createPreferenceLink(\n+\t\t\tIWorkbenchPreferenceContainer container, Composite parent,\n+\t\t\tString pageId, String text) {\n+\t\tfinal PreferenceLinkArea area = new PreferenceLinkArea(parent,\n+\t\t\t\tSWT.NONE, pageId, text, container, null);\n+\t\treturn area;\n+\t}\n+\n+\t/**\n+\t * Creates a grid data with the specified metrics\n+\t * \n+\t * @param width\n+\t * @param height\n+\t * @param hFill\n+\t * @param vFill\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createGridData(int width, int height, boolean hFill,\n+\t\t\tboolean vFill) {\n+\t\treturn createGridData(width, height, hFill ? SWT.FILL : SWT.BEGINNING,\n+\t\t\t\tvFill ? SWT.FILL : SWT.CENTER, hFill, vFill);\n+\t}\n+\n+\t/**\n+\t * Creates a grid data with the specified metrics\n+\t * \n+\t * @param width\n+\t * @param height\n+\t * @param hAlign\n+\t * @param vAlign\n+\t * @param hGrab\n+\t * @param vGrab\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createGridData(int width, int height, int hAlign,\n+\t\t\tint vAlign, boolean hGrab, boolean vGrab) {\n+\t\tfinal GridData gd = new GridData(hAlign, vAlign, hGrab, vGrab);\n+\t\tgd.widthHint = width;\n+\t\tgd.heightHint = height;\n+\t\treturn gd;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal grid data with the default metrics\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createHFillGridData() {\n+\t\treturn createHFillGridData(1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal grid data with the specified span\n+\t * \n+\t * @param span\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createHFillGridData(int span) {\n+\t\tfinal GridData gd = createGridData(0, SWT.DEFAULT, SWT.FILL,\n+\t\t\t\tSWT.CENTER, true, false);\n+\t\tgd.horizontalSpan = span;\n+\t\treturn gd;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal fill composite with the specified margins\n+\t * \n+\t * @param parent\n+\t * @param margins\n+\t * \n+\t * @return the created composite\n+\t */\n+\tpublic static Composite createHFillComposite(Composite parent, int margins) {\n+\t\treturn createHFillComposite(parent, margins, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal fill composite with the specified margins and\n+\t * columns\n+\t * \n+\t * @param parent\n+\t * @param margins\n+\t * @param columns\n+\t * \n+\t * @return the created composite\n+\t */\n+\tpublic static Composite createHFillComposite(Composite parent, int margins,\n+\t\t\tint columns) {\n+\t\tfinal Composite composite = new Composite(parent, SWT.NONE);\n+\t\tcomposite.setFont(parent.getFont());\n+\t\tcomposite.setLayoutData(createHFillGridData());\n+\t\tcomposite.setLayout(createGridLayout(columns,\n+\t\t\t\tnew PixelConverter(parent), margins));\n+\t\treturn composite;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill composite with the specified margins\n+\t * \n+\t * @param parent\n+\t * @param margins\n+\t * \n+\t * @return the created composite\n+\t */\n+\tpublic static Composite createHVFillComposite(Composite parent, int margins) {\n+\t\treturn createHVFillComposite(parent, margins, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill composite with the specified margins\n+\t * and columns\n+\t * \n+\t * @param parent\n+\t * @param margins\n+\t * @param columns\n+\t * \n+\t * @return the created composite\n+\t */\n+\tpublic static Composite createHVFillComposite(Composite parent,\n+\t\t\tint margins, int columns) {\n+\t\tfinal Composite composite = new Composite(parent, SWT.NONE);\n+\t\tcomposite.setFont(parent.getFont());\n+\t\tcomposite.setLayoutData(createHVFillGridData());\n+\t\tcomposite.setLayout(createGridLayout(columns,\n+\t\t\t\tnew PixelConverter(parent), margins));\n+\t\treturn composite;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal fill group with the specified text and margins\n+\t * \n+\t * @param parent\n+\t * @param text\n+\t * @param margins\n+\t * @return the created group\n+\t */\n+\tpublic static Group createHFillGroup(Composite parent, String text,\n+\t\t\tint margins) {\n+\t\treturn createHFillGroup(parent, text, margins, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal fill group with the specified text, margins and rows\n+\t * \n+\t * @param parent\n+\t * @param text\n+\t * @param margins\n+\t * @param rows\n+\t * \n+\t * @return the created group\n+\t */\n+\tpublic static Group createHFillGroup(Composite parent, String text,\n+\t\t\tint margins, int rows) {\n+\t\tfinal Group group = new Group(parent, SWT.NONE);\n+\t\tgroup.setFont(parent.getFont());\n+\t\tgroup.setLayoutData(createHFillGridData());\n+\t\tif (text != null)\n+\t\t\tgroup.setText(text);\n+\t\tgroup.setLayout(createGridLayout(rows, new PixelConverter(parent),\n+\t\t\t\tmargins));\n+\t\treturn group;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill group with the specified text and\n+\t * margins\n+\t * \n+\t * @param parent\n+\t * @param text\n+\t * @param margins\n+\t * \n+\t * @return the created group\n+\t */\n+\tpublic static Group createHVFillGroup(Composite parent, String text,\n+\t\t\tint margins) {\n+\t\treturn createHVFillGroup(parent, text, margins, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill group with the specified text, margins\n+\t * and rows\n+\t * \n+\t * @param parent\n+\t * @param text\n+\t * @param margins\n+\t * @param rows\n+\t * \n+\t * @return the created group\n+\t */\n+\tpublic static Group createHVFillGroup(Composite parent, String text,\n+\t\t\tint margins, int rows) {\n+\t\tfinal Group group = new Group(parent, SWT.NONE);\n+\t\tgroup.setFont(parent.getFont());\n+\t\tgroup.setLayoutData(createHVFillGridData());\n+\t\tif (text != null)\n+\t\t\tgroup.setText(text);\n+\t\tgroup.setLayout(createGridLayout(rows, new PixelConverter(parent),\n+\t\t\t\tmargins));\n+\t\treturn group;\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill grid data with the default metrics\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createHVFillGridData() {\n+\t\treturn createHVFillGridData(1);\n+\t}\n+\n+\t/**\n+\t * Creates a horizontal/vertical fill grid data with the specified span\n+\t * \n+\t * @param span\n+\t * \n+\t * @return the created grid data\n+\t */\n+\tpublic static GridData createHVFillGridData(int span) {\n+\t\tfinal GridData gd = createGridData(0, 0, true, true);\n+\t\tgd.horizontalSpan = span;\n+\t\treturn gd;\n+\t}\n+\n+\t/**\n+\t * Creates a grid layout with the specified number of columns and the\n+\t * standard spacings.\n+\t * \n+\t * @param numColumns\n+\t *            the number of columns\n+\t * @param converter\n+\t *            the pixel converter\n+\t * @param margins\n+\t *            one of <code>MARGINS_DEFAULT</code>, <code>MARGINS_NONE</code>\n+\t *            or <code>MARGINS_DIALOG</code>.\n+\t * \n+\t * @return the created grid layout\n+\t */\n+\tpublic static GridLayout createGridLayout(int numColumns,\n+\t\t\tPixelConverter converter, int margins) {\n+\t\tAssert.isTrue(margins == MARGINS_DEFAULT || margins == MARGINS_NONE\n+\t\t\t\t|| margins == MARGINS_DIALOG);\n+\n+\t\tfinal GridLayout layout = new GridLayout(numColumns, false);\n+\t\tlayout.horizontalSpacing = converter\n+\t\t\t\t.convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);\n+\t\tlayout.verticalSpacing = converter\n+\t\t\t\t.convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);\n+\n+\t\tswitch (margins) {\n+\t\tcase MARGINS_NONE:\n+\t\t\tlayout.marginLeft = layout.marginRight = 0;\n+\t\t\tlayout.marginTop = layout.marginBottom = 0;\n+\t\t\tbreak;\n+\t\tcase MARGINS_DIALOG:\n+\t\t\tlayout.marginLeft = layout.marginRight = converter\n+\t\t\t\t\t.convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);\n+\t\t\tlayout.marginTop = layout.marginBottom = converter\n+\t\t\t\t\t.convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);\n+\t\t\tbreak;\n+\t\tcase MARGINS_DEFAULT:\n+\t\t\tlayout.marginLeft = layout.marginRight = layout.marginWidth;\n+\t\t\tlayout.marginTop = layout.marginBottom = layout.marginHeight;\n+\t\t}\n+\t\tlayout.marginWidth = layout.marginHeight = 0;\n+\t\treturn layout;\n+\t}\n+\n+\t/**\n+\t * Creates a label with the specified message\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * \n+\t * @return the created label\n+\t */\n+\tpublic static Label createLabel(Composite parent, String message) {\n+\t\treturn createLabel(parent, message, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a label with the specified message and span\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * @param span\n+\t * \n+\t * @return the created label\n+\t */\n+\tpublic static Label createLabel(Composite parent, String message, int span) {\n+\t\tfinal Label label = new Label(parent, SWT.WRAP);\n+\t\tif (message != null)\n+\t\t\tlabel.setText(message);\n+\t\tlabel.setLayoutData(createHFillGridData(span));\n+\t\treturn label;\n+\t}\n+\n+\t/**\n+\t * Creates a check box with the specified message\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * \n+\t * @return the created check box\n+\t */\n+\tpublic static Button createCheckBox(Composite parent, String message) {\n+\t\treturn createCheckBox(parent, message, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a check box with the specified message and span\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * @param span\n+\t * \n+\t * @return the created check box\n+\t */\n+\tpublic static Button createCheckBox(Composite parent, String message,\n+\t\t\tint span) {\n+\t\tfinal Button button = new Button(parent, SWT.CHECK);\n+\t\tbutton.setText(message);\n+\t\tbutton.setLayoutData(createHFillGridData(span));\n+\t\treturn button;\n+\t}\n+\n+\t/**\n+\t * Creates a radio button with the specified message\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * \n+\t * @return the created radio button\n+\t */\n+\tpublic static Button createRadioButton(Composite parent, String message) {\n+\t\treturn createRadioButton(parent, message, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a radio button with the specified message and span\n+\t * \n+\t * @param parent\n+\t * @param message\n+\t * @param span\n+\t * \n+\t * @return the created radio button\n+\t */\n+\tpublic static Button createRadioButton(Composite parent, String message,\n+\t\t\tint span) {\n+\t\tfinal Button button = new Button(parent, SWT.RADIO);\n+\t\tbutton.setText(message);\n+\t\tbutton.setLayoutData(createHFillGridData(span));\n+\t\treturn button;\n+\t}\n+\n+\t/**\n+\t * Creates a text control\n+\t * \n+\t * @param parent\n+\t * \n+\t * @return the created text control\n+\t */\n+\tpublic static Text createText(Composite parent) {\n+\t\treturn createText(parent, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a text control with the specified span\n+\t * \n+\t * @param parent\n+\t * @param span\n+\t * \n+\t * @return the created text control\n+\t */\n+\tpublic static Text createText(Composite parent, int span) {\n+\t\tfinal Text text = new Text(parent, SWT.SINGLE | SWT.BORDER);\n+\t\ttext.setLayoutData(createHFillGridData(span));\n+\t\treturn text;\n+\t}\n+\n+\t/**\n+\t * Creates a place holder with the specified height and span\n+\t * \n+\t * @param parent\n+\t * @param heightInChars\n+\t * @param span\n+\t * \n+\t * @return the created place holder\n+\t */\n+\tpublic static Control createPlaceholder(Composite parent,\n+\t\t\tint heightInChars, int span) {\n+\t\tAssert.isTrue(heightInChars > 0);\n+\t\tfinal Control placeHolder = new Composite(parent, SWT.NONE);\n+\t\tfinal GridData gd = new GridData(SWT.BEGINNING, SWT.TOP, false, false);\n+\t\tgd.heightHint = new PixelConverter(parent)\n+\t\t\t\t.convertHeightInCharsToPixels(heightInChars);\n+\t\tgd.horizontalSpan = span;\n+\t\tplaceHolder.setLayoutData(gd);\n+\t\treturn placeHolder;\n+\t}\n+\n+\t/**\n+\t * Creates a place holder with the specified height\n+\t * \n+\t * @param parent\n+\t * @param heightInChars\n+\t * @return the created place holder\n+\t */\n+\tpublic static Control createPlaceholder(Composite parent, int heightInChars) {\n+\t\treturn createPlaceholder(parent, heightInChars, 1);\n+\t}\n+\n+\t/**\n+\t * Creates a pixel converter\n+\t * \n+\t * @param control\n+\t * \n+\t * @return the created pixel converter\n+\t */\n+\tpublic static PixelConverter createDialogPixelConverter(Control control) {\n+\t\tDialog.applyDialogFont(control);\n+\t\treturn new PixelConverter(control);\n+\t}\n+\n+\t/**\n+\t * Calculates the size of the specified controls, using the specified\n+\t * converter\n+\t * \n+\t * @param converter\n+\t * @param controls\n+\t * \n+\t * @return the size of the control(s)\n+\t */\n+\tpublic static int calculateControlSize(PixelConverter converter,\n+\t\t\tControl[] controls) {\n+\t\treturn calculateControlSize(converter, controls, 0, controls.length - 1);\n+\t}\n+\n+\t/**\n+\t * Calculates the size of the specified subset of controls, using the\n+\t * specified converter\n+\t * \n+\t * @param converter\n+\t * @param controls\n+\t * @param start\n+\t * @param end\n+\t * \n+\t * @return the created control\n+\t */\n+\tpublic static int calculateControlSize(PixelConverter converter,\n+\t\t\tControl[] controls, int start, int end) {\n+\t\tint minimum = converter\n+\t\t\t\t.convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);\n+\t\tfor (int i = start; i <= end; i++) {\n+\t\t\tfinal int length = controls[i]\n+\t\t\t\t\t.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;\n+\t\t\tif (minimum < length)\n+\t\t\t\tminimum = length;\n+\t\t}\n+\t\treturn minimum;\n+\t}\n+\n+\t/**\n+\t * Equalizes the specified controls using the specified converter\n+\t * \n+\t * @param converter\n+\t * @param controls\n+\t */\n+\tpublic static void equalizeControls(PixelConverter converter,\n+\t\t\tControl[] controls) {\n+\t\tequalizeControls(converter, controls, 0, controls.length - 1);\n+\t}\n+\n+\t/**\n+\t * Equalizes the specified subset of controls using the specified converter\n+\t * \n+\t * @param converter\n+\t * @param controls\n+\t * @param start\n+\t * @param end\n+\t */\n+\tpublic static void equalizeControls(PixelConverter converter,\n+\t\t\tControl[] controls, int start, int end) {\n+\t\tfinal int size = calculateControlSize(converter, controls, start, end);\n+\t\tfor (int i = start; i <= end; i++) {\n+\t\t\tfinal Control button = controls[i];\n+\t\t\tif (button.getLayoutData() instanceof GridData) {\n+\t\t\t\t((GridData) button.getLayoutData()).widthHint = size;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Gets the width of the longest string in <code>strings</code>, using the\n+\t * specified pixel converter\n+\t * \n+\t * @param converter\n+\t * @param strings\n+\t * \n+\t * @return the width of the longest string\n+\t */\n+\tpublic static int getWidthInCharsForLongest(PixelConverter converter,\n+\t\t\tString[] strings) {\n+\t\tint minimum = 0;\n+\t\tfor (int i = 0; i < strings.length; i++) {\n+\t\t\tfinal int length = converter.convertWidthInCharsToPixels(strings[i]\n+\t\t\t\t\t.length());\n+\t\t\tif (minimum < length)\n+\t\t\t\tminimum = length;\n+\t\t}\n+\t\treturn minimum;\n+\t}\n+\n+\tprivate static class PixelConverter {\n+\n+\t\tprivate final FontMetrics fFontMetrics;\n+\n+\t\tpublic PixelConverter(Control control) {\n+\t\t\tGC gc = new GC(control);\n+\t\t\ttry {\n+\t\t\t\tgc.setFont(control.getFont());\n+\t\t\t\tfFontMetrics = gc.getFontMetrics();\n+\t\t\t} finally {\n+\t\t\t\tgc.dispose();\n+\t\t\t}\n+\t\t}\n+\n+\t\tpublic int convertHeightInCharsToPixels(int chars) {\n+\t\t\treturn Dialog.convertHeightInCharsToPixels(fFontMetrics, chars);\n+\t\t}\n+\n+\t\tpublic int convertHorizontalDLUsToPixels(int dlus) {\n+\t\t\treturn Dialog.convertHorizontalDLUsToPixels(fFontMetrics, dlus);\n+\t\t}\n+\n+\t\tpublic int convertVerticalDLUsToPixels(int dlus) {\n+\t\t\treturn Dialog.convertVerticalDLUsToPixels(fFontMetrics, dlus);\n+\t\t}\n+\n+\t\tpublic int convertWidthInCharsToPixels(int chars) {\n+\t\t\treturn Dialog.convertWidthInCharsToPixels(fFontMetrics, chars);\n+\t\t}\n+\t}\n+}\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104247","messageId":"1234377614-23798-7-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-6-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 06/12] Implement basic customizable label decorations with preferences","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:08Z","receivedAt":"2009-02-11T18:40:08Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Currently the only binding available is the resource name, but\nthis commit enables a framework for adding more bindings.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n org.spearce.egit.ui/plugin.properties              |    1 +\n org.spearce.egit.ui/plugin.xml                     |   12 +-\n .../src/org/spearce/egit/ui/Activator.java         |   16 +\n .../egit/ui/PluginPreferenceInitializer.java       |    8 +\n .../src/org/spearce/egit/ui/UIPreferences.java     |    9 +\n .../src/org/spearce/egit/ui/UIText.java            |   63 ++-\n .../egit/ui/internal/actions/BranchAction.java     |    4 +-\n .../egit/ui/internal/actions/Disconnect.java       |    4 +-\n .../egit/ui/internal/actions/ResetAction.java      |    4 +-\n .../decorators/GitLightweightDecorator.java        |  538 +++++++++++++++\n .../internal/decorators/GitResourceDecorator.java  |  454 -------------\n .../internal/decorators/IDecoratableResource.java  |   31 +\n .../preferences/GitDecoratorPreferencePage.java    |  702 ++++++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   25 +-\n 14 files changed, 1405 insertions(+), 466 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n\ndiff --git a/org.spearce.egit.ui/plugin.properties b/org.spearce.egit.ui/plugin.properties\nindex fa043f1..58b879f 100644\n--- a/org.spearce.egit.ui/plugin.properties\n+++ b/org.spearce.egit.ui/plugin.properties\n@@ -64,3 +64,4 @@ Theme_CommitMessageFont_description=This font is used to show a commit message.\n GitPreferences_name=Git\n GitPreferences_HistoryPreferencePage_name=History\n GitPreferences_WindowCachePreferencePage_name=Window Cache\n+GitPreferences_DecoratorPreferencePage_name=Label Decorations\ndiff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml\nindex 869108c..2f23559 100644\n--- a/org.spearce.egit.ui/plugin.xml\n+++ b/org.spearce.egit.ui/plugin.xml\n@@ -200,6 +200,14 @@\n \t\t  id=\"org.spearce.egit.ui.keyword.git\">\n \t    </keywordReference>\n       </page>\n+\t  <page name=\"%GitPreferences_DecoratorPreferencePage_name\"\n+\t    category=\"org.spearce.egit.ui.GitPreferences\"\n+            class=\"org.spearce.egit.ui.internal.preferences.GitDecoratorPreferencePage\"\n+\t    id=\"org.spearce.egit.ui.internal.preferences.GitDecoratorPreferencePage\" >\n+\t    <keywordReference\n+\t\t  id=\"org.spearce.egit.ui.keyword.git\">\n+\t    </keywordReference>\n+      </page>\n    </extension>\n \n    <extension point=\"org.eclipse.ui.propertyPages\">\n@@ -233,10 +241,10 @@\n             lightweight=\"true\"\n             adaptable=\"true\"\n             label=\"%Decorator_name\"\n-            class=\"org.spearce.egit.ui.internal.decorators.GitResourceDecorator\"\n+            class=\"org.spearce.egit.ui.internal.decorators.GitLightweightDecorator\"\n             state=\"true\"\n             location=\"BOTTOM_RIGHT\"\n-            id=\"org.spearce.egit.ui.internal.decorators.GitResourceDecorator\">\n+            id=\"org.spearce.egit.ui.internal.decorators.GitLightweightDecorator\">\n             <enablement>\n               <objectClass name=\"org.eclipse.core.resources.IResource\"/>\n             </enablement>\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\nindex 534c408..87f45d2 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java\n@@ -33,6 +33,7 @@\n import org.eclipse.jface.util.PropertyChangeEvent;\n import org.eclipse.jsch.core.IJSchService;\n import org.eclipse.swt.graphics.Font;\n+import org.eclipse.swt.widgets.Display;\n import org.eclipse.ui.plugin.AbstractUIPlugin;\n import org.eclipse.ui.themes.ITheme;\n import org.osgi.framework.BundleContext;\n@@ -80,6 +81,21 @@ public static String getPluginId() {\n \t}\n \n \t/**\n+\t * Returns the standard display to be used. The method first checks, if the\n+\t * thread calling this method has an associated display. If so, this display\n+\t * is returned. Otherwise the method returns the default display.\n+\t * \n+\t * @return the display to use\n+\t */\n+\tpublic static Display getStandardDisplay() {\n+\t\tDisplay display = Display.getCurrent();\n+\t\tif (display == null) {\n+\t\t\tdisplay = Display.getDefault();\n+\t\t}\n+\t\treturn display;\n+\t}\n+\n+\t/**\n \t * Instantiate an error exception.\n \t * \n \t * @param message\ndiff --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\nindex bb7381f..79c2665 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n@@ -35,6 +35,14 @@ public void initializeDefaultPreferences() {\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);\n \n+\t\tprefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n+\t\t\t\tUIText.DecoratorPreferencesPage_fileFormatDefault);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n+\t\t\t\tUIText.DecoratorPreferencesPage_folderFormatDefault);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n+\t\t\t\tUIText.DecoratorPreferencesPage_projectFormatDefault);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n+\n \t\tw = new int[] { 500, 500 };\n \t\tUIPreferences.setDefault(prefs,\n \t\t\t\tUIPreferences.RESOURCEHISTORY_GRAPH_SPLIT, w);\ndiff --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\nindex 5ab6b25..a6168a0 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n@@ -52,6 +52,15 @@\n \t/** */\n \tpublic final static String THEME_CommitMessageFont = \"org.spearce.egit.ui.CommitMessageFont\";\n \n+\t/** */\n+\tpublic final static String DECORATOR_CALCULATE_DIRTY = \"decorator_calculate_dirty\";\n+\t/** */\n+\tpublic final static String DECORATOR_FILETEXT_DECORATION = \"decorator_filetext_decoration\";\n+\t/** */\n+\tpublic final static String DECORATOR_FOLDERTEXT_DECORATION = \"decorator_foldertext_decoration\";\n+\t/** */\n+\tpublic final static String DECORATOR_PROJECTTEXT_DECORATION = \"decorator_projecttext_decoration\";\n+\n \t/**\n \t * Get the preference values associated with a fixed integer array.\n \t * \ndiff --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\nindex 249f2a0..a7ef408 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -446,9 +446,6 @@\n \tpublic static String RefSpecPage_annotatedTagsNoTags;\n \n \t/** */\n-\tpublic static String Decorator_failedLazyLoading;\n-\n-\t/** */\n \tpublic static String QuickDiff_failedLoading;\n \n \t/** */\n@@ -928,6 +925,66 @@\n \t/** */\n \tpublic static String HistoryPage_ShowAllVersionsForFolder;\n \n+\t/** */\n+\tpublic static String Decorator_exceptionMessage;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_addVariablesTitle;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_addVariablesAction;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_computeDeep;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_description;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_decorationSettings;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_preview;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_fileFormatLabel;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_folderFormatLabel;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_projectFormatLabel;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_fileFormatDefault;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_projectFormatDefault;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_folderFormatDefault;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_generalTabFolder;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_nameResourceVariable;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_selectFormats;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_selectVariablesToAdd;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_textLabel;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconLabel;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_labelDecorationsLink;\n+\n \tstatic {\n \t\tinitializeMessages(UIText.class.getPackage().getName() + \".uitext\",\n \t\t\t\tUIText.class);\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/BranchAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/BranchAction.java\nindex 7ca4d10..38ee3d8 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/BranchAction.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/BranchAction.java\n@@ -19,7 +19,7 @@\n import org.eclipse.jface.operation.IRunnableWithProgress;\n import org.eclipse.swt.widgets.Display;\n import org.spearce.egit.core.op.BranchOperation;\n-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;\n+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;\n import org.spearce.egit.ui.internal.dialogs.BranchSelectionDialog;\n import org.spearce.jgit.lib.Repository;\n \n@@ -56,7 +56,7 @@ public void run(final IProgressMonitor monitor)\n \t\t\t\tthrows InvocationTargetException {\n \t\t\t\t\ttry {\n \t\t\t\t\t\tnew BranchOperation(repository, refName).run(monitor);\n-\t\t\t\t\t\tGitResourceDecorator.refresh();\n+\t\t\t\t\t\tGitLightweightDecorator.refresh();\n \t\t\t\t\t} catch (final CoreException ce) {\n \t\t\t\t\t\tce.printStackTrace();\n \t\t\t\t\t\tDisplay.getDefault().asyncExec(new Runnable() {\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/Disconnect.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/Disconnect.java\nindex 18d6b4b..4201822 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/Disconnect.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/Disconnect.java\n@@ -13,7 +13,7 @@\n import org.eclipse.core.resources.IWorkspaceRunnable;\n import org.eclipse.jface.action.IAction;\n import org.spearce.egit.core.op.DisconnectProviderOperation;\n-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;\n+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;\n \n /**\n  *\tAction to disassociate a project from its Git repository.\n@@ -27,6 +27,6 @@ protected IWorkspaceRunnable createOperation(final IAction act,\n \t}\n \n \tprotected void postOperation() {\n-\t\tGitResourceDecorator.refresh();\n+\t\tGitLightweightDecorator.refresh();\n \t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ResetAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ResetAction.java\nindex b05cdd3..a329925 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ResetAction.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/ResetAction.java\n@@ -19,7 +19,7 @@\n import org.eclipse.jface.operation.IRunnableWithProgress;\n import org.spearce.egit.core.op.ResetOperation;\n import org.spearce.egit.core.op.ResetOperation.ResetType;\n-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;\n+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;\n import org.spearce.egit.ui.internal.dialogs.BranchSelectionDialog;\n import org.spearce.jgit.lib.Repository;\n \n@@ -55,7 +55,7 @@ public void run(final IProgressMonitor monitor)\n \t\t\t\t\tthrows InvocationTargetException {\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tnew ResetOperation(repository, refName, type).run(monitor);\n-\t\t\t\t\t\t\tGitResourceDecorator.refresh();\n+\t\t\t\t\t\t\tGitLightweightDecorator.refresh();\n \t\t\t\t\t\t} catch (CoreException ce) {\n \t\t\t\t\t\t\tce.printStackTrace();\n \t\t\t\t\t\t\tthrow new InvocationTargetException(ce);\ndiff --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\nnew file mode 100644\nindex 0000000..85b9173\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -0,0 +1,538 @@\n+/*******************************************************************************\n+ * Copyright (C) 2007, IBM Corporation and others\n+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>\n+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.ui.internal.decorators;\n+\n+import java.util.ArrayList;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Set;\n+\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IResourceChangeEvent;\n+import org.eclipse.core.resources.IResourceChangeListener;\n+import org.eclipse.core.resources.IResourceDelta;\n+import org.eclipse.core.resources.IResourceDeltaVisitor;\n+import org.eclipse.core.resources.IResourceVisitor;\n+import org.eclipse.core.resources.ResourcesPlugin;\n+import org.eclipse.core.resources.mapping.ResourceMapping;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IAdaptable;\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.jface.preference.IPreferenceStore;\n+import org.eclipse.jface.util.IPropertyChangeListener;\n+import org.eclipse.jface.util.PropertyChangeEvent;\n+import org.eclipse.jface.viewers.IDecoration;\n+import org.eclipse.jface.viewers.ILightweightLabelDecorator;\n+import org.eclipse.jface.viewers.LabelProvider;\n+import org.eclipse.jface.viewers.LabelProviderChangedEvent;\n+import org.eclipse.osgi.util.TextProcessor;\n+import org.eclipse.swt.widgets.Display;\n+import org.eclipse.team.ui.TeamUI;\n+import org.eclipse.ui.IContributorResourceAdapter;\n+import org.eclipse.ui.PlatformUI;\n+import org.spearce.egit.core.internal.util.ExceptionCollector;\n+import org.spearce.egit.core.project.GitProjectData;\n+import org.spearce.egit.core.project.RepositoryChangeListener;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIPreferences;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.lib.IndexChangedEvent;\n+import org.spearce.jgit.lib.RefsChangedEvent;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RepositoryChangedEvent;\n+import org.spearce.jgit.lib.RepositoryListener;\n+\n+/**\n+ * Supplies annotations for displayed resources\n+ * \n+ * This decorator provides annotations to indicate the status of each resource\n+ * when compared to <code>HEAD</code>, as well as the index in the relevant\n+ * repository.\n+ * \n+ * TODO: Add support for colors and font decoration\n+ */\n+public class GitLightweightDecorator extends LabelProvider implements\n+\t\tILightweightLabelDecorator, IPropertyChangeListener,\n+\t\tIResourceChangeListener, RepositoryChangeListener, RepositoryListener {\n+\n+\t/**\n+\t * Property constant pointing back to the extension point id of the\n+\t * decorator\n+\t */\n+\tpublic static final String DECORATOR_ID = \"org.spearce.egit.ui.internal.decorators.GitLightweightDecorator\"; //$NON-NLS-1$\n+\n+\t/**\n+\t * Bit-mask describing interesting changes for IResourceChangeListener\n+\t * events\n+\t */\n+\tprivate static int INTERESTING_CHANGES = IResourceDelta.CONTENT\n+\t\t\t| IResourceDelta.MOVED_FROM | IResourceDelta.MOVED_TO\n+\t\t\t| IResourceDelta.OPEN | IResourceDelta.REPLACED\n+\t\t\t| IResourceDelta.TYPE;\n+\n+\t/**\n+\t * Collector for keeping the error view from filling up with exceptions\n+\t */\n+\tprivate static ExceptionCollector exceptions = new ExceptionCollector(\n+\t\t\tUIText.Decorator_exceptionMessage, Activator.getPluginId(),\n+\t\t\tIStatus.ERROR, Activator.getDefault().getLog());\n+\n+\t/**\n+\t * Constructs a new Git resource decorator\n+\t */\n+\tpublic GitLightweightDecorator() {\n+\t\tTeamUI.addPropertyChangeListener(this);\n+\t\tActivator.addPropertyChangeListener(this);\n+\t\tPlatformUI.getWorkbench().getThemeManager().getCurrentTheme()\n+\t\t\t\t.addPropertyChangeListener(this);\n+\t\tRepository.addAnyRepositoryChangedListener(this);\n+\t\tGitProjectData.addRepositoryChangeListener(this);\n+\t\tResourcesPlugin.getWorkspace().addResourceChangeListener(this,\n+\t\t\t\tIResourceChangeEvent.POST_CHANGE);\n+\t}\n+\n+\t/*\n+\t * (non-Javadoc)\n+\t * \n+\t * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()\n+\t */\n+\t@Override\n+\tpublic void dispose() {\n+\t\tsuper.dispose();\n+\t\tPlatformUI.getWorkbench().getThemeManager().getCurrentTheme()\n+\t\t\t\t.removePropertyChangeListener(this);\n+\t\tTeamUI.removePropertyChangeListener(this);\n+\t\tActivator.removePropertyChangeListener(this);\n+\t\tRepository.removeAnyRepositoryChangedListener(this);\n+\t\tGitProjectData.removeRepositoryChangeListener(this);\n+\t\tResourcesPlugin.getWorkspace().removeResourceChangeListener(this);\n+\t}\n+\n+\t/**\n+\t * This method should only be called by the decorator thread.\n+\t * \n+\t * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,\n+\t *      org.eclipse.jface.viewers.IDecoration)\n+\t */\n+\tpublic void decorate(Object element, IDecoration decoration) {\n+\t\tfinal IResource resource = getResource(element);\n+\t\tif (resource == null)\n+\t\t\treturn;\n+\n+\t\t// Don't decorate the workspace root\n+\t\tif (resource.getType() == IResource.ROOT)\n+\t\t\treturn;\n+\n+\t\t// Don't decorate non-existing resources\n+\t\tif (!resource.exists() && !resource.isPhantom())\n+\t\t\treturn;\n+\n+\t\t// Make sure we're dealing with a Git project\n+\t\tfinal RepositoryMapping mapping = RepositoryMapping\n+\t\t\t\t.getMapping(resource);\n+\t\tif (mapping == null)\n+\t\t\treturn;\n+\n+\t\t// Cannot decorate linked resources\n+\t\tif (mapping.getRepoRelativePath(resource) == null)\n+\t\t\treturn;\n+\n+\t\t// Don't decorate if UI plugin is not running\n+\t\tActivator activator = Activator.getDefault();\n+\t\tif (activator == null)\n+\t\t\treturn;\n+\n+\t\tDecorationHelper helper = new DecorationHelper(activator\n+\t\t\t\t.getPreferenceStore());\n+\t\thelper.decorate(decoration, new DecoratableResourceAdapter(resource));\n+\t}\n+\n+\tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n+\n+\t\tprivate IResource resource;\n+\n+\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap) {\n+\t\t\tresource = resourceToWrap;\n+\t\t}\n+\n+\t\tpublic String getName() {\n+\t\t\treturn resource.getName();\n+\t\t}\n+\n+\t\tpublic int getType() {\n+\t\t\treturn resource.getType();\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Helper class for doing resource decoration, based on the given\n+\t * preferences\n+\t * \n+\t * Used for real-time decoration, as well as in the decorator preview\n+\t * preferences page\n+\t */\n+\tpublic static class DecorationHelper {\n+\n+\t\tprivate IPreferenceStore store;\n+\n+\t\t/** */\n+\t\tpublic static final String BINDING_RESOURCE_NAME = \"name\"; //$NON-NLS-1$\n+\n+\t\t/**\n+\t\t * Constructs a decorator using the rules from the given\n+\t\t * <code>preferencesStore</code>\n+\t\t * \n+\t\t * @param preferencesStore\n+\t\t *            the preferences store with the preferred decorator rules\n+\t\t */\n+\t\tpublic DecorationHelper(IPreferenceStore preferencesStore) {\n+\t\t\tstore = preferencesStore;\n+\t\t}\n+\n+\t\t/**\n+\t\t * Decorates the given <code>decoration</code> based on the state of the\n+\t\t * given <code>resource</code>, using the preferences passed when\n+\t\t * constructing this decoration helper.\n+\t\t * \n+\t\t * @param decoration\n+\t\t *            the decoration to decorate\n+\t\t * @param resource\n+\t\t *            the resource to retrieve state from\n+\t\t */\n+\t\tpublic void decorate(IDecoration decoration,\n+\t\t\t\tIDecoratableResource resource) {\n+\t\t\tString format = \"\";\n+\t\t\tswitch (resource.getType()) {\n+\t\t\tcase IResource.FILE:\n+\t\t\t\tformat = store\n+\t\t\t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION);\n+\t\t\t\tbreak;\n+\t\t\tcase IResource.FOLDER:\n+\t\t\t\tformat = store\n+\t\t\t\t\t\t.getString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION);\n+\t\t\t\tbreak;\n+\t\t\tcase IResource.PROJECT:\n+\t\t\t\tformat = store\n+\t\t\t\t\t\t.getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tMap<String, String> bindings = new HashMap<String, String>();\n+\t\t\tbindings.put(BINDING_RESOURCE_NAME, resource.getName());\n+\n+\t\t\tdecorate(decoration, format, bindings);\n+\t\t}\n+\n+\t\t/**\n+\t\t * Decorates the given <code>decoration</code>, using the given\n+\t\t * <code>format</code>, and mapped using <code>bindings</code>\n+\t\t * \n+\t\t * @param decoration\n+\t\t *            the decoration to decorate\n+\t\t * @param format\n+\t\t *            the format to base the decoration on\n+\t\t * @param bindings\n+\t\t *            the bindings between variables in the format and actual\n+\t\t *            values\n+\t\t */\n+\t\tpublic static void decorate(IDecoration decoration, String format,\n+\t\t\t\tMap bindings) {\n+\t\t\tStringBuffer prefix = new StringBuffer();\n+\t\t\tStringBuffer suffix = new StringBuffer();\n+\t\t\tStringBuffer output = prefix;\n+\n+\t\t\tint length = format.length();\n+\t\t\tint start = -1;\n+\t\t\tint end = length;\n+\t\t\twhile (true) {\n+\t\t\t\tif ((end = format.indexOf('{', start)) > -1) {\n+\t\t\t\t\toutput.append(format.substring(start + 1, end));\n+\t\t\t\t\tif ((start = format.indexOf('}', end)) > -1) {\n+\t\t\t\t\t\tString key = format.substring(end + 1, start);\n+\t\t\t\t\t\tString s;\n+\n+\t\t\t\t\t\t// We use the BINDING_RESOURCE_NAME key to determine if\n+\t\t\t\t\t\t// we are doing the prefix or suffix. The name isn't\n+\t\t\t\t\t\t// actually part of either.\n+\t\t\t\t\t\tif (key.equals(BINDING_RESOURCE_NAME)) {\n+\t\t\t\t\t\t\toutput = suffix;\n+\t\t\t\t\t\t\ts = null;\n+\t\t\t\t\t\t} else {\n+\t\t\t\t\t\t\ts = (String) bindings.get(key);\n+\t\t\t\t\t\t}\n+\n+\t\t\t\t\t\tif (s != null) {\n+\t\t\t\t\t\t\toutput.append(s);\n+\t\t\t\t\t\t} else {\n+\t\t\t\t\t\t\t// Support removing prefix character if binding is\n+\t\t\t\t\t\t\t// null\n+\t\t\t\t\t\t\tint curLength = output.length();\n+\t\t\t\t\t\t\tif (curLength > 0) {\n+\t\t\t\t\t\t\t\tchar c = output.charAt(curLength - 1);\n+\t\t\t\t\t\t\t\tif (c == ':' || c == '@') {\n+\t\t\t\t\t\t\t\t\toutput.deleteCharAt(curLength - 1);\n+\t\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t}\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\toutput.append(format.substring(end, length));\n+\t\t\t\t\t\tbreak;\n+\t\t\t\t\t}\n+\t\t\t\t} else {\n+\t\t\t\t\toutput.append(format.substring(start + 1, length));\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tString prefixString = prefix.toString().replaceAll(\"^\\\\s+\", \"\");\n+\t\t\tif (prefixString != null) {\n+\t\t\t\tdecoration.addPrefix(TextProcessor.process(prefixString,\n+\t\t\t\t\t\t\"()[].\")); //$NON-NLS-1$\n+\t\t\t}\n+\t\t\tString suffixString = suffix.toString().replaceAll(\"\\\\s+$\", \"\");\n+\t\t\tif (suffixString != null) {\n+\t\t\t\tdecoration.addSuffix(TextProcessor.process(suffixString,\n+\t\t\t\t\t\t\"()[].\")); //$NON-NLS-1$\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t// -------- Refresh handling --------\n+\n+\t/**\n+\t * Perform a blanket refresh of all decorations\n+\t */\n+\tpublic static void refresh() {\n+\t\tDisplay.getDefault().asyncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\tActivator.getDefault().getWorkbench().getDecoratorManager()\n+\t\t\t\t\t\t.update(DECORATOR_ID);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\t/**\n+\t * Callback for IPropertyChangeListener events\n+\t * \n+\t * If any of the relevant preferences has been changed we refresh all\n+\t * decorations (all projects and their resources).\n+\t * \n+\t * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)\n+\t */\n+\tpublic void propertyChange(PropertyChangeEvent event) {\n+\t\tfinal String prop = event.getProperty();\n+\t\t// If the property is of any interest to us\n+\t\tif (prop.equals(TeamUI.GLOBAL_IGNORES_CHANGED)\n+\t\t\t\t|| prop.equals(TeamUI.GLOBAL_FILE_TYPES_CHANGED)\n+\t\t\t\t|| prop.equals(Activator.DECORATORS_CHANGED)) {\n+\t\t\tpostLabelEvent(new LabelProviderChangedEvent(this, null /* all */));\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Callback for IResourceChangeListener events\n+\t * \n+\t * Schedules a refresh of the changed resource\n+\t * \n+\t * If the preference for computing deep dirty states has been set we walk\n+\t * the ancestor tree of the changed resource and update all parents as well.\n+\t * \n+\t * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)\n+\t */\n+\tpublic void resourceChanged(IResourceChangeEvent event) {\n+\t\tfinal Set<IResource> resourcesToUpdate = new HashSet<IResource>();\n+\n+\t\ttry { // Compute the changed resources by looking at the delta\n+\t\t\tevent.getDelta().accept(new IResourceDeltaVisitor() {\n+\t\t\t\tpublic boolean visit(IResourceDelta delta) throws CoreException {\n+\t\t\t\t\tfinal IResource resource = delta.getResource();\n+\n+\t\t\t\t\tif (resource.getType() == IResource.ROOT) {\n+\t\t\t\t\t\t// Continue with the delta\n+\t\t\t\t\t\treturn true;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tif (resource.getType() == IResource.PROJECT) {\n+\t\t\t\t\t\t// If the project is not accessible, don't process it\n+\t\t\t\t\t\tif (!resource.isAccessible())\n+\t\t\t\t\t\t\treturn false;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\t// If the file has changed but not in a way that we care\n+\t\t\t\t\t// about\n+\t\t\t\t\t// (e.g. marker changes to files) then ignore the change\n+\t\t\t\t\tif (delta.getKind() == IResourceDelta.CHANGED\n+\t\t\t\t\t\t\t&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {\n+\t\t\t\t\t\treturn true;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\t// All seems good, schedule the resource for update\n+\t\t\t\t\tresourcesToUpdate.add(resource);\n+\t\t\t\t\treturn true;\n+\t\t\t\t}\n+\t\t\t}, true /* includePhantoms */);\n+\t\t} catch (final CoreException e) {\n+\t\t\thandleException(null, e);\n+\t\t}\n+\n+\t\t// If deep decorator calculation is enabled in the preferences we\n+\t\t// walk the ancestor tree of each of the changed resources and add\n+\t\t// their parents to the update set\n+\t\tfinal IPreferenceStore store = Activator.getDefault()\n+\t\t\t\t.getPreferenceStore();\n+\t\tif (store.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY)) {\n+\t\t\tfinal IResource[] changedResources = resourcesToUpdate\n+\t\t\t\t\t.toArray(new IResource[resourcesToUpdate.size()]);\n+\t\t\tfor (int i = 0; i < changedResources.length; i++) {\n+\t\t\t\tIResource current = changedResources[i];\n+\t\t\t\twhile (current.getType() != IResource.ROOT) {\n+\t\t\t\t\tcurrent = current.getParent();\n+\t\t\t\t\tresourcesToUpdate.add(current);\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tpostLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate\n+\t\t\t\t.toArray()));\n+\t}\n+\n+\t/**\n+\t * Callback for RepositoryListener events\n+\t * \n+\t * We resolve the repository mapping for the changed repository and forward\n+\t * that to repositoryChanged(RepositoryMapping).\n+\t * \n+\t * @param e\n+\t *            The original change event\n+\t */\n+\tprivate void repositoryChanged(RepositoryChangedEvent e) {\n+\t\tfinal Set<RepositoryMapping> ms = new HashSet<RepositoryMapping>();\n+\t\tfor (final IProject p : ResourcesPlugin.getWorkspace().getRoot()\n+\t\t\t\t.getProjects()) {\n+\t\t\tfinal RepositoryMapping mapping = RepositoryMapping.getMapping(p);\n+\t\t\tif (mapping != null && mapping.getRepository() == e.getRepository())\n+\t\t\t\tms.add(mapping);\n+\t\t}\n+\t\tfor (final RepositoryMapping m : ms) {\n+\t\t\trepositoryChanged(m);\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * (non-Javadoc)\n+\t * \n+\t * @see\n+\t * org.spearce.jgit.lib.RepositoryListener#indexChanged(org.spearce.jgit\n+\t * .lib.IndexChangedEvent)\n+\t */\n+\tpublic void indexChanged(IndexChangedEvent e) {\n+\t\trepositoryChanged(e);\n+\t}\n+\n+\t/*\n+\t * (non-Javadoc)\n+\t * \n+\t * @see\n+\t * org.spearce.jgit.lib.RepositoryListener#refsChanged(org.spearce.jgit.\n+\t * lib.RefsChangedEvent)\n+\t */\n+\tpublic void refsChanged(RefsChangedEvent e) {\n+\t\trepositoryChanged(e);\n+\t}\n+\n+\t/**\n+\t * Callback for RepositoryChangeListener events, as well as\n+\t * RepositoryListener events via repositoryChanged()\n+\t * \n+\t * We resolve the project and schedule a refresh of each resource in the\n+\t * project.\n+\t * \n+\t * @see org.spearce.egit.core.project.RepositoryChangeListener#repositoryChanged(org.spearce.egit.core.project.RepositoryMapping)\n+\t */\n+\tpublic void repositoryChanged(RepositoryMapping mapping) {\n+\t\tfinal IProject project = mapping.getContainer().getProject();\n+\t\tif (project == null)\n+\t\t\treturn;\n+\n+\t\tfinal List<IResource> resources = new ArrayList<IResource>();\n+\t\ttry {\n+\t\t\tproject.accept(new IResourceVisitor() {\n+\t\t\t\tpublic boolean visit(IResource resource) {\n+\t\t\t\t\tresources.add(resource);\n+\t\t\t\t\treturn true;\n+\t\t\t\t}\n+\t\t\t});\n+\t\t\tpostLabelEvent(new LabelProviderChangedEvent(this, resources\n+\t\t\t\t\t.toArray()));\n+\t\t} catch (final CoreException e) {\n+\t\t\thandleException(project, e);\n+\t\t}\n+\t}\n+\n+\t// -------- Helper methods --------\n+\n+\tprivate static IResource getResource(Object element) {\n+\t\tif (element instanceof ResourceMapping) {\n+\t\t\telement = ((ResourceMapping) element).getModelObject();\n+\t\t}\n+\n+\t\tIResource resource = null;\n+\t\tif (element instanceof IResource) {\n+\t\t\tresource = (IResource) element;\n+\t\t} else if (element instanceof IAdaptable) {\n+\t\t\tfinal IAdaptable adaptable = (IAdaptable) element;\n+\t\t\tresource = (IResource) adaptable.getAdapter(IResource.class);\n+\t\t\tif (resource == null) {\n+\t\t\t\tfinal IContributorResourceAdapter adapter = (IContributorResourceAdapter) adaptable\n+\t\t\t\t\t\t.getAdapter(IContributorResourceAdapter.class);\n+\t\t\t\tif (adapter != null)\n+\t\t\t\t\tresource = adapter.getAdaptedResource(adaptable);\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn resource;\n+\t}\n+\n+\t/**\n+\t * Post the label event to the UI thread\n+\t * \n+\t * @param event\n+\t *            The event to post\n+\t */\n+\tprivate void postLabelEvent(final LabelProviderChangedEvent event) {\n+\t\tDisplay.getDefault().asyncExec(new Runnable() {\n+\t\t\tpublic void run() {\n+\t\t\t\tfireLabelProviderChanged(event);\n+\t\t\t}\n+\t\t});\n+\t}\n+\n+\t/**\n+\t * Handle exceptions that occur in the decorator. Exceptions are only logged\n+\t * for resources that are accessible (i.e. exist in an open project).\n+\t * \n+\t * @param resource\n+\t *            The resource that triggered the exception\n+\t * @param e\n+\t *            The exception that occurred\n+\t */\n+\tprivate static void handleException(IResource resource, CoreException e) {\n+\t\tif (resource == null || resource.isAccessible())\n+\t\t\texceptions.handleException(e);\n+\t}\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java\ndeleted file mode 100644\nindex f24b1eb..0000000\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java\n+++ /dev/null\n@@ -1,454 +0,0 @@\n-/*******************************************************************************\n- * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>\n- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n- * Copyright (C) 2008, Google Inc.\n- *\n- * All rights reserved. This program and the accompanying materials\n- * are made available under the terms of the Eclipse Public License v1.0\n- * See LICENSE for the full license text, also available.\n- *******************************************************************************/\n-package org.spearce.egit.ui.internal.decorators;\n-\n-import java.io.IOException;\n-import java.util.HashSet;\n-import java.util.Iterator;\n-import java.util.LinkedHashSet;\n-import java.util.Set;\n-\n-import org.eclipse.core.resources.IContainer;\n-import org.eclipse.core.resources.IFile;\n-import org.eclipse.core.resources.IProject;\n-import org.eclipse.core.resources.IResource;\n-import org.eclipse.core.resources.IResourceChangeEvent;\n-import org.eclipse.core.resources.IResourceChangeListener;\n-import org.eclipse.core.resources.IResourceDelta;\n-import org.eclipse.core.resources.IResourceDeltaVisitor;\n-import org.eclipse.core.resources.IResourceVisitor;\n-import org.eclipse.core.resources.ResourcesPlugin;\n-import org.eclipse.core.runtime.CoreException;\n-import org.eclipse.core.runtime.IAdaptable;\n-import org.eclipse.core.runtime.IProgressMonitor;\n-import org.eclipse.core.runtime.IStatus;\n-import org.eclipse.core.runtime.QualifiedName;\n-import org.eclipse.core.runtime.Status;\n-import org.eclipse.core.runtime.jobs.ISchedulingRule;\n-import org.eclipse.core.runtime.jobs.Job;\n-import org.eclipse.jface.viewers.IDecoration;\n-import org.eclipse.jface.viewers.ILightweightLabelDecorator;\n-import org.eclipse.jface.viewers.LabelProvider;\n-import org.eclipse.jface.viewers.LabelProviderChangedEvent;\n-import org.eclipse.swt.widgets.Display;\n-import org.eclipse.team.core.Team;\n-import org.eclipse.ui.IDecoratorManager;\n-import org.spearce.egit.core.project.GitProjectData;\n-import org.spearce.egit.core.project.RepositoryChangeListener;\n-import org.spearce.egit.core.project.RepositoryMapping;\n-import org.spearce.egit.ui.Activator;\n-import org.spearce.egit.ui.UIIcons;\n-import org.spearce.egit.ui.UIText;\n-import org.spearce.jgit.lib.Constants;\n-import org.spearce.jgit.lib.GitIndex;\n-import org.spearce.jgit.lib.IndexChangedEvent;\n-import org.spearce.jgit.lib.RefsChangedEvent;\n-import org.spearce.jgit.lib.Repository;\n-import org.spearce.jgit.lib.RepositoryChangedEvent;\n-import org.spearce.jgit.lib.RepositoryListener;\n-import org.spearce.jgit.lib.RepositoryState;\n-import org.spearce.jgit.lib.Tree;\n-import org.spearce.jgit.lib.TreeEntry;\n-import org.spearce.jgit.lib.GitIndex.Entry;\n-\n-/**\n- * Supplies annotations for displayed resources.\n- * <p>\n- * This decorator provides annotations to indicate the status of each resource\n- * when compared to <code>HEAD</code> as well as the index in the relevant\n- * repository.\n- * \n- * When either the index or the working directory is different from HEAD an\n- * indicator is set.\n- * \n- * </p>\n- */\n-public class GitResourceDecorator extends LabelProvider implements\n-\t\tILightweightLabelDecorator {\n-\n-\tstatic final String decoratorId = \"org.spearce.egit.ui.internal.decorators.GitResourceDecorator\";\n-\tstatic class ResCL extends Job implements IResourceChangeListener, RepositoryChangeListener, RepositoryListener {\n-\n-\t\tResCL() {\n-\t\t\tsuper(\"Git resource decorator trigger\");\n-\t\t}\n-\n-\t\tGitResourceDecorator getActiveDecorator() {\n-\t\t\tIDecoratorManager decoratorManager = Activator.getDefault()\n-\t\t\t\t\t.getWorkbench().getDecoratorManager();\n-\t\t\tif (decoratorManager.getEnabled(decoratorId))\n-\t\t\t\treturn (GitResourceDecorator) decoratorManager\n-\t\t\t\t\t\t.getLightweightLabelDecorator(decoratorId);\n-\t\t\treturn null;\n-\t\t}\n-\n-\t\tprivate Set<IResource> resources = new LinkedHashSet<IResource>();\n-\n-\t\tpublic void refsChanged(RefsChangedEvent e) {\n-\t\t\trepositoryChanged(e);\n-\t\t}\n-\n-\t\tpublic void indexChanged(IndexChangedEvent e) {\n-\t\t\trepositoryChanged(e);\n-\t\t}\n-\n-\t\tprivate void repositoryChanged(RepositoryChangedEvent e) {\n-\t\t\tSet<RepositoryMapping> ms = new HashSet<RepositoryMapping>();\n-\t\t\tfor (IProject p : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {\n-\t\t\t\tRepositoryMapping mapping = RepositoryMapping.getMapping(p);\n-\t\t\t\tif (mapping != null && mapping.getRepository() == e.getRepository())\n-\t\t\t\t\tms.add(mapping);\n-\t\t\t}\n-\t\t\tfor (RepositoryMapping m : ms) {\n-\t\t\t\trepositoryChanged(m);\n-\t\t\t}\n-\t\t}\n-\n-\t\tpublic void repositoryChanged(final RepositoryMapping which) {\n-\t\t\tsynchronized (resources) {\n-\t\t\t\tresources.add(which.getContainer());\n-\t\t\t}\n-\t\t\tschedule();\n-\t\t}\n-\n-\t\t@Override\n-\t\tprotected IStatus run(IProgressMonitor arg0) {\n-\t\t\ttry {\n-\t\t\t\tif (resources.size() > 0) {\n-\t\t\t\t\tIResource m;\n-\t\t\t\t\tsynchronized(resources) {\n-\t\t\t\t\t\tIterator<IResource> i = resources.iterator();\n-\t\t\t\t\t\tm = i.next();\n-\t\t\t\t\t\ti.remove();\n-\n-\t\t\t\t\t\twhile (!m.isAccessible()) {\n-\t\t\t\t\t\t\tif (!i.hasNext())\n-\t\t\t\t\t\t\t\treturn Status.OK_STATUS;\n-\t\t\t\t\t\t\tm = i.next();\n-\t\t\t\t\t\t\ti.remove();\n-\t\t\t\t\t\t}\n-\n-\t\t\t\t\t\tif (resources.size() > 0)\n-\t\t\t\t\t\t\tschedule();\n-\t\t\t\t\t}\n-\t\t\t\t\tISchedulingRule markerRule = m.getWorkspace().getRuleFactory().markerRule(m);\n-\t\t\t\t\tgetJobManager().beginRule(markerRule, arg0);\n-\t\t\t\t\ttry {\n-\t\t\t\t\t\tm.accept(new IResourceVisitor() {\n-\t\t\t\t\t\t\tpublic boolean visit(IResource resource) throws CoreException {\n-\t\t\t\t\t\t\t\tGitResourceDecorator decorator = getActiveDecorator();\n-\t\t\t\t\t\t\t\tif (decorator != null)\n-\t\t\t\t\t\t\t\t\tdecorator.clearDecorationState(resource);\n-\t\t\t\t\t\t\t\treturn true;\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t},\n-\t\t\t\t\t\tIResource.DEPTH_INFINITE,\n-\t\t\t\t\t\ttrue);\n-\t\t\t\t\t} finally {\n-\t\t\t\t\t\tgetJobManager().endRule(markerRule);\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t\treturn Status.OK_STATUS;\n-\t\t\t} catch (Exception e) {\n-\t\t\t\t// We must be silent here or the UI will panic with lots of error messages\n-\t\t\t\tActivator.logError(\"Failed to trigger resource re-decoration\", e);\n-\t\t\t\treturn Status.OK_STATUS;\n-\t\t\t}\n-\t\t}\n-\n-\t\tpublic void resourceChanged(IResourceChangeEvent event) {\n-\t\t\tif (event.getType() != IResourceChangeEvent.POST_CHANGE) {\n-\t\t\t\treturn;\n-\t\t\t}\n-\t\t\ttry {\n-\t\t\t\tevent.getDelta().accept(new IResourceDeltaVisitor() {\n-\t\t\t\t\tpublic boolean visit(IResourceDelta delta)\n-\t\t\t\t\t\t\tthrows CoreException {\n-\t\t\t\t\t\tfor (IResource r = delta.getResource(); r.getType() != IResource.ROOT; r = r\n-\t\t\t\t\t\t\t\t.getParent()) {\n-\t\t\t\t\t\t\tsynchronized (resources) {\n-\t\t\t\t\t\t\t\tresources.add(r);\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\treturn true;\n-\t\t\t\t\t}\n-\t\t\t\t},\n-\t\t\t\ttrue\n-\t\t\t\t);\n-\t\t\t} catch (Exception e) {\n-\t\t\t\tActivator.logError(\"Problem during decorations. Stopped\", e);\n-\t\t\t}\n-\t\t\tschedule();\n-\t\t}\n-\n-\t\tvoid force() {\n-\t\t\tfor (IProject p : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {\n-\t\t\t\tsynchronized (resources) {\n-\t\t\t\t\tresources.add(p);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tschedule();\n-\t\t}\n-\t} // End ResCL\n-\n-\tvoid clearDecorationState(IResource r) throws CoreException {\n-\t\tif (r.isAccessible()) {\n-\t\t\tr.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, null);\n-\t\t\tfireLabelProviderChanged(new LabelProviderChangedEvent(this, r));\n-\t\t}\n-\t}\n-\n-\tstatic ResCL myrescl = new ResCL();\n-\n-\tstatic {\n-\t\tRepository.addAnyRepositoryChangedListener(myrescl);\n-\t\tGitProjectData.addRepositoryChangeListener(myrescl);\n-\t\tResourcesPlugin.getWorkspace().addResourceChangeListener(myrescl,\n-\t\t\t\tIResourceChangeEvent.POST_CHANGE);\n-\t}\n-\n-\t/**\n-\t * Request that the decorator be updated, to reflect any recent changes.\n-\t * <p>\n-\t * Can be invoked any any thread. If the current thread is not the UI\n-\t * thread, an async update will be scheduled.\n-\t * </p>\n-\t */\n-\tpublic static void refresh() {\n-\t\tmyrescl.force();\n-\t}\n-\n-\tprivate static IResource toIResource(final Object e) {\n-\t\tif (e instanceof IResource)\n-\t\t\treturn (IResource) e;\n-\t\tif (e instanceof IAdaptable) {\n-\t\t\tfinal Object c = ((IAdaptable) e).getAdapter(IResource.class);\n-\t\t\tif (c instanceof IResource)\n-\t\t\t\treturn (IResource) c;\n-\t\t}\n-\t\treturn null;\n-\t}\n-\n-\tstatic QualifiedName GITFOLDERDIRTYSTATEPROPERTY = new QualifiedName(\n-\t\t\t\"org.spearce.egit.ui.internal.decorators.GitResourceDecorator\",\n-\t\t\t\"dirty\");\n-\n-\tstatic final int UNCHANGED = 0;\n-\n-\tstatic final int CHANGED = 1;\n-\n-\tprivate Boolean isDirty(IResource rsrc) {\n-\t\ttry {\n-\t\t\tif (rsrc.getType() == IResource.FILE && Team.isIgnored((IFile)rsrc))\n-\t\t\t\treturn Boolean.FALSE;\n-\n-\t\t\tRepositoryMapping mapped = RepositoryMapping.getMapping(rsrc);\n-\t\t\tif (mapped != null) {\n-\t\t\t\tif (rsrc instanceof IContainer) {\n-\t\t\t\t\tfor (IResource r : ((IContainer) rsrc)\n-\t\t\t\t\t\t\t.members(IContainer.EXCLUDE_DERIVED)) {\n-\t\t\t\t\t\tBoolean f = isDirty(r);\n-\t\t\t\t\t\tif (f == null || f.booleanValue())\n-\t\t\t\t\t\t\treturn Boolean.TRUE;\n-\t\t\t\t\t}\n-\t\t\t\t\treturn Boolean.FALSE;\n-\t\t\t\t}\n-\n-\t\t\t\treturn Boolean.valueOf(mapped.isResourceChanged(rsrc));\n-\t\t\t}\n-\t\t\treturn null; // not mapped\n-\t\t} catch (CoreException e) {\n-\t\t\t// TODO Auto-generated catch block\n-\t\t\te.printStackTrace();\n-\t\t} catch (IOException e) {\n-\t\t\t// TODO Auto-generated catch block\n-\t\t\te.printStackTrace();\n-\t\t}\n-\t\treturn null;\n-\t}\n-\n-\tpublic void decorate(final Object element, final IDecoration decoration) {\n-\t\tfinal IResource rsrc = toIResource(element);\n-\t\tif (rsrc == null)\n-\t\t\treturn;\n-\n-\t\t// If the workspace has not been refreshed properly a resource might\n-\t\t// not actually exist, so we ignore these and do not decorate them\n-\t\tif (!rsrc.exists() && !rsrc.isPhantom()) {\n-\t\t\tActivator.trace(\"Tried to decorate non-existent resource \"+rsrc);\n-\t\t\treturn;\n-\t\t}\n-\n-\t\tRepositoryMapping mapped = RepositoryMapping.getMapping(rsrc);\n-\n-\t\t// TODO: How do I see a renamed resource?\n-\t\t// TODO: Even trickier: when a path change from being blob to tree?\n-\t\ttry {\n-\t\t\tif (mapped != null) {\n-\t\t\t\tRepository repository = mapped.getRepository();\n-\t\t\t\tGitIndex index = repository.getIndex();\n-\t\t\t\tString repoRelativePath = mapped.getRepoRelativePath(rsrc);\n-\n-\t\t\t\tif (repoRelativePath == null) {\n-\t\t\t\t\tActivator.trace(\"Cannot decorate linked resource \" + rsrc);\n-\t\t\t\t\treturn;\n-\t\t\t\t}\n-\n-\t\t\t\tTree headTree = repository.mapTree(Constants.HEAD);\n-\t\t\t\tTreeEntry blob = headTree!=null ? headTree.findBlobMember(repoRelativePath) : null;\n-\t\t\t\tEntry entry = index.getEntry(repoRelativePath);\n-\t\t\t\tif (entry == null) {\n-\t\t\t\t\tif (blob == null) {\n-\t\t\t\t\t\tif (rsrc instanceof IContainer) {\n-\t\t\t\t\t\t\tInteger df = (Integer) rsrc\n-\t\t\t\t\t\t\t\t\t.getSessionProperty(GITFOLDERDIRTYSTATEPROPERTY);\n-\t\t\t\t\t\t\tBoolean f = df == null ? isDirty(rsrc)\n-\t\t\t\t\t\t\t\t\t: Boolean.valueOf(df.intValue() == CHANGED);\n-\t\t\t\t\t\t\tif (f != null) {\n-\t\t\t\t\t\t\t\tif (f.booleanValue()) {\n-\t\t\t\t\t\t\t\t\tdecoration.addPrefix(\">\"); // Have not\n-\t\t\t\t\t\t\t\t\t// seen\n-\t\t\t\t\t\t\t\t\torState(rsrc, CHANGED);\n-\t\t\t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\t\t\torState(rsrc, UNCHANGED);\n-\t\t\t\t\t\t\t\t\t// decoration.addSuffix(\"=?\");\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\t\tdecoration.addSuffix(\" ?* \");\n-\t\t\t\t\t\t\t}\n-\n-\t\t\t\t\t\t\tif (rsrc instanceof IProject) {\n-\t\t\t\t\t\t\t\tRepository repo = mapped.getRepository();\n-\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\tString branch = repo.getBranch();\n-\t\t\t\t\t\t\t\t\tRepositoryState repositoryState = repo.getRepositoryState();\n-\t\t\t\t\t\t\t\t\tString statename;\n-\t\t\t\t\t\t\t\t\tif (repositoryState.equals(RepositoryState.SAFE))\n-\t\t\t\t\t\t\t\t\t\tstatename = \"\";\n-\t\t\t\t\t\t\t\t\telse\n-\t\t\t\t\t\t\t\t\t\tstatename = repositoryState.getDescription() + \" \";\n-\t\t\t\t\t\t\t\t\tdecoration.addSuffix(\" [Git \" + statename + \"@ \" + branch + \"]\");\n-\t\t\t\t\t\t\t\t} catch (IOException e) {\n-\t\t\t\t\t\t\t\t\te.printStackTrace();\n-\t\t\t\t\t\t\t\t\tdecoration.addSuffix(\" [Git ?]\");\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_SHARED);\n-\t\t\t\t\t\t\t}\n-\n-\t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\tif (Team.isIgnoredHint(rsrc)) {\n-\t\t\t\t\t\t\t\tdecoration.addSuffix(\"(ignored)\");\n-\t\t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\t\tdecoration.addPrefix(\">\");\n-\t\t\t\t\t\t\t\tdecoration.addSuffix(\"(untracked)\");\n-\t\t\t\t\t\t\t\torState(rsrc.getParent(), CHANGED);\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t}\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tif (!(rsrc instanceof IContainer)) {\n-\t\t\t\t\t\t\tdecoration.addSuffix(\"(deprecated)\"); // Will drop on\n-\t\t\t\t\t\t\t// commit\n-\t\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_PENDING_REMOVE);\n-\t\t\t\t\t\t\torState(rsrc.getParent(), CHANGED);\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t} else {\n-\t\t\t\t\tif (entry.getStage() != GitIndex.STAGE_0) {\n-\t\t\t\t\t\tdecoration.addSuffix(\"(conflict)\");\n-\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_CONFLICT);\n-\t\t\t\t\t\torState(rsrc.getParent(), CHANGED);\n-\t\t\t\t\t\treturn;\n-\t\t\t\t\t}\n-\n-\t\t\t\t\tif (blob == null) {\n-\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_PENDING_ADD);\n-\t\t\t\t\t\torState(rsrc.getParent(), CHANGED);\n-\t\t\t\t\t} else {\n-\n-\t\t\t\t\t\tif (entry.isAssumedValid()) {\n-\t\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_ASSUMEVALID);\n-\t\t\t\t\t\t\treturn;\n-\t\t\t\t\t\t}\n-\n-\t\t\t\t\t\tdecoration.addOverlay(UIIcons.OVR_SHARED);\n-\n-\t\t\t\t\t\tif (entry.isModified(mapped.getWorkDir(), true)) {\n-\t\t\t\t\t\t\tdecoration.addPrefix(\">\");\n-\t\t\t\t\t\t\tdecoration.addSuffix(\"(not updated)\");\n-\t\t\t\t\t\t\torState(rsrc.getParent(), CHANGED);\n-\t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\tif (!entry.getObjectId().equals(blob.getId()))\n-\t\t\t\t\t\t\t\tdecoration.addPrefix(\">\");\n-\t\t\t\t\t\t\telse\n-\t\t\t\t\t\t\t\tdecoration.addPrefix(\"\"); // set it to avoid further calls\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} catch (IOException e) {\n-\t\t\tdecoration.addSuffix(\"?\");\n-\t\t\t// If we throw an exception Eclipse will log the error and\n-\t\t\t// unregister us thereby preventing us from dragging down the\n-\t\t\t// entire workbench because we are crashing.\n-\t\t\t//\n-\t\t\tthrow new RuntimeException(UIText.Decorator_failedLazyLoading, e);\n-\t\t} catch (CoreException e) {\n-\t\t\tthrow new RuntimeException(UIText.Decorator_failedLazyLoading, e);\n-\t\t}\n-\t}\n-\n-\tprivate void orState(final IResource rsrc, int flag) {\n-\t\tif (rsrc == null || rsrc.getType() == IResource.ROOT) {\n-\t\t\treturn;\n-\t\t}\n-\n-\t\ttry {\n-\t\t\tInteger dirty = (Integer) rsrc.getSessionProperty(GITFOLDERDIRTYSTATEPROPERTY);\n-\t\t\tRunnable runnable = new Runnable() {\n-\t\t\t\tpublic void run() {\n-\t\t\t\t\t// Async could be called after a\n-\t\t\t\t\t// project is closed or a\n-\t\t\t\t\t// resource is deleted\n-\t\t\t\t\tif (!rsrc.isAccessible())\n-\t\t\t\t\t\treturn;\n-\t\t\t\t\tfireLabelProviderChanged(new LabelProviderChangedEvent(\n-\t\t\t\t\t\t\tGitResourceDecorator.this, rsrc));\n-\t\t\t\t}\n-\t\t\t};\n-\t\t\tif (dirty == null) {\n-\t\t\t\trsrc.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, new Integer(flag));\n-\t\t\t\torState(rsrc.getParent(), flag);\n-//\t\t\t\tif (Thread.currentThread() == Display.getDefault().getThread())\n-//\t\t\t\t\trunnable.run();\n-//\t\t\t\telse\n-\t\t\t\t\tDisplay.getDefault().asyncExec(runnable);\n-\t\t\t} else {\n-\t\t\t\tif ((dirty.intValue() | flag) != dirty.intValue()) {\n-\t\t\t\t\tdirty = new Integer(dirty.intValue() | flag);\n-\t\t\t\t\trsrc.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, dirty);\n-\t\t\t\t\torState(rsrc.getParent(), dirty.intValue());\n-//\t\t\t\t\tif (Thread.currentThread() == Display.getDefault().getThread())\n-//\t\t\t\t\t\trunnable.run();\n-//\t\t\t\t\telse\n-\t\t\t\t\t\tDisplay.getDefault().asyncExec(runnable);\n-\t\t\t\t}\n-\t\t\t}\n-\t\t} catch (CoreException e) {\n-\t\t\t// TODO Auto-generated catch block\n-\t\t\te.printStackTrace();\n-\t\t}\n-\t}\n-\t\n-\t@Override\n-\tpublic boolean isLabelProperty(Object element, String property) {\n-\t\treturn super.isLabelProperty(element, property);\n-\t}\n-}\ndiff --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\nnew file mode 100644\nindex 0000000..8d6c741\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n@@ -0,0 +1,31 @@\n+/*******************************************************************************\n+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.ui.internal.decorators;\n+\n+import org.eclipse.core.resources.IResource;\n+\n+/**\n+ * Represents the state of a resource that can be used as a basis for decoration\n+ */\n+public interface IDecoratableResource {\n+\n+\t/**\n+\t * Gets the type of the resource as defined by {@link IResource}\n+\t * \n+\t * @return the type of the resource\n+\t */\n+\tint getType();\n+\n+\t/**\n+\t * Gets the name of the resource\n+\t * \n+\t * @return the name of the resource\n+\t */\n+\tString getName();\n+}\ndiff --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\nnew file mode 100644\nindex 0000000..84203b7\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -0,0 +1,702 @@\n+/*******************************************************************************\n+ * Copyright (C) 2003, 2006 Subclipse project and others.\n+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.preferences;\n+\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.HashMap;\n+import java.util.Iterator;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.Observable;\n+import java.util.Observer;\n+\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.jface.dialogs.Dialog;\n+import org.eclipse.jface.dialogs.IDialogConstants;\n+import org.eclipse.jface.preference.IPreferenceStore;\n+import org.eclipse.jface.preference.PreferencePage;\n+import org.eclipse.jface.preference.PreferenceStore;\n+import org.eclipse.jface.resource.ImageDescriptor;\n+import org.eclipse.jface.resource.JFaceResources;\n+import org.eclipse.jface.resource.LocalResourceManager;\n+import org.eclipse.jface.resource.ResourceManager;\n+import org.eclipse.jface.util.IPropertyChangeListener;\n+import org.eclipse.jface.util.PropertyChangeEvent;\n+import org.eclipse.jface.viewers.DecorationContext;\n+import org.eclipse.jface.viewers.DecorationOverlayIcon;\n+import org.eclipse.jface.viewers.IDecoration;\n+import org.eclipse.jface.viewers.IDecorationContext;\n+import org.eclipse.jface.viewers.ILabelProvider;\n+import org.eclipse.jface.viewers.IStructuredContentProvider;\n+import org.eclipse.jface.viewers.ITreeContentProvider;\n+import org.eclipse.jface.viewers.LabelProvider;\n+import org.eclipse.jface.viewers.TreeViewer;\n+import org.eclipse.jface.viewers.Viewer;\n+import org.eclipse.jface.window.Window;\n+import org.eclipse.swt.SWT;\n+import org.eclipse.swt.events.ModifyEvent;\n+import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.graphics.Color;\n+import org.eclipse.swt.graphics.Font;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.widgets.Button;\n+import org.eclipse.swt.widgets.Composite;\n+import org.eclipse.swt.widgets.Control;\n+import org.eclipse.swt.widgets.Event;\n+import org.eclipse.swt.widgets.Listener;\n+import org.eclipse.swt.widgets.TabFolder;\n+import org.eclipse.swt.widgets.TabItem;\n+import org.eclipse.swt.widgets.Text;\n+import org.eclipse.swt.widgets.TreeItem;\n+import org.eclipse.ui.ISharedImages;\n+import org.eclipse.ui.IWorkbench;\n+import org.eclipse.ui.IWorkbenchPreferencePage;\n+import org.eclipse.ui.PlatformUI;\n+import org.eclipse.ui.dialogs.ListSelectionDialog;\n+import org.eclipse.ui.ide.IDE.SharedImages;\n+import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIPreferences;\n+import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.SWTUtils;\n+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;\n+import org.spearce.egit.ui.internal.decorators.IDecoratableResource;\n+\n+/**\n+ * Preference page for customizing Git label decorations\n+ */\n+public class GitDecoratorPreferencePage extends PreferencePage implements\n+\t\tIWorkbenchPreferencePage {\n+\n+\tprivate Text fileTextFormat;\n+\n+\tprivate Text folderTextFormat;\n+\n+\tprivate Text projectTextFormat;\n+\n+\tprivate Button showDirty;\n+\n+\tprivate Preview preview;\n+\n+\tprivate static final Collection PREVIEW_FILESYSTEM_ROOT;\n+\n+\tprivate static IPropertyChangeListener themeListener;\n+\n+\tstatic {\n+\t\tfinal PreviewResource project = new PreviewResource(\n+\t\t\t\t\"Project\", IResource.PROJECT); //$NON-NLS-1$1\n+\t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n+\t\tchildren.add(new PreviewResource(\"folder\", IResource.FOLDER)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\"file.txt\", IResource.FILE)); //$NON-NLS-1$\n+\t\tproject.children = children;\n+\t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n+\t}\n+\n+\t/**\n+\t * Constructs a decorator preference page\n+\t */\n+\tpublic GitDecoratorPreferencePage() {\n+\t\tsetDescription(UIText.DecoratorPreferencesPage_description);\n+\t}\n+\n+\t/**\n+\t * @see PreferencePage#createContents(Composite)\n+\t */\n+\tprotected Control createContents(Composite parent) {\n+\n+\t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n+\t\t\t\tSWTUtils.MARGINS_NONE);\n+\n+\t\tSWTUtils.createPreferenceLink(\n+\t\t\t\t(IWorkbenchPreferenceContainer) getContainer(), composite,\n+\t\t\t\t\"org.eclipse.ui.preferencePages.Decorators\",\n+\t\t\t\tUIText.DecoratorPreferencesPage_labelDecorationsLink); //$NON-NLS-1$\n+\n+\t\tTabFolder tabFolder = new TabFolder(composite, SWT.NONE);\n+\t\ttabFolder.setLayoutData(SWTUtils.createHVFillGridData());\n+\n+\t\tTabItem tabItem = new TabItem(tabFolder, SWT.NONE);\n+\t\ttabItem.setText(UIText.DecoratorPreferencesPage_generalTabFolder);\n+\t\ttabItem.setControl(createGeneralDecoratorPage(tabFolder));\n+\n+\t\ttabItem = new TabItem(tabFolder, SWT.NONE);\n+\t\ttabItem.setText(UIText.DecoratorPreferencesPage_textLabel);\n+\t\ttabItem.setControl(createTextDecoratorPage(tabFolder));\n+\n+\t\ttabItem = new TabItem(tabFolder, SWT.NONE);\n+\t\ttabItem.setText(UIText.DecoratorPreferencesPage_iconLabel);\n+\t\ttabItem.setControl(createIconDecoratorPage(tabFolder));\n+\n+\t\tinitializeValues();\n+\n+\t\tpreview = new Preview(composite);\n+\t\tpreview.refresh();\n+\n+\t\t// TODO: Add help text for this preference page\n+\n+\t\tthemeListener = new IPropertyChangeListener() {\n+\t\t\tpublic void propertyChange(PropertyChangeEvent event) {\n+\t\t\t\tpreview.refresh();\n+\t\t\t}\n+\t\t};\n+\t\tPlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(\n+\t\t\t\tthemeListener);\n+\n+\t\tDialog.applyDialogFont(parent);\n+\n+\t\treturn composite;\n+\t}\n+\n+\tprivate Control createGeneralDecoratorPage(Composite parent) {\n+\t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n+\t\t\t\tSWTUtils.MARGINS_DEFAULT);\n+\n+\t\tshowDirty = SWTUtils.createCheckBox(composite,\n+\t\t\t\tUIText.DecoratorPreferencesPage_computeDeep);\n+\n+\t\treturn composite;\n+\t}\n+\n+\t/**\n+\t * Creates the controls for the first tab folder\n+\t * \n+\t * @param parent\n+\t * \n+\t * @return the control\n+\t */\n+\tprivate Control createTextDecoratorPage(Composite parent) {\n+\t\tComposite fileTextGroup = SWTUtils.createHVFillComposite(parent,\n+\t\t\t\tSWTUtils.MARGINS_DEFAULT, 3);\n+\n+\t\tTextPair format = createFormatEditorControl(fileTextGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_fileFormatLabel,\n+\t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n+\t\t\t\tgetFileBindingDescriptions());\n+\t\tfileTextFormat = format.t1;\n+\n+\t\tformat = createFormatEditorControl(fileTextGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_folderFormatLabel,\n+\t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n+\t\t\t\tgetFolderBindingDescriptions());\n+\t\tfolderTextFormat = format.t1;\n+\n+\t\tformat = createFormatEditorControl(fileTextGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_projectFormatLabel,\n+\t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n+\t\t\t\tgetProjectBindingDescriptions());\n+\t\tprojectTextFormat = format.t1;\n+\n+\t\treturn fileTextGroup;\n+\t}\n+\n+\tprivate Control createIconDecoratorPage(Composite parent) {\n+\t\tComposite imageGroup = SWTUtils.createHVFillComposite(parent,\n+\t\t\t\tSWTUtils.MARGINS_DEFAULT, 2);\n+\n+\t\treturn imageGroup;\n+\t}\n+\n+\tprivate TextPair createFormatEditorControl(Composite composite,\n+\t\t\tString title, String buttonText, final Map supportedBindings) {\n+\n+\t\tSWTUtils.createLabel(composite, title);\n+\n+\t\tText format = new Text(composite, SWT.BORDER);\n+\t\tformat.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\n+\t\tformat.addModifyListener(new ModifyListener() {\n+\t\t\tpublic void modifyText(ModifyEvent e) {\n+\t\t\t\tupdatePreview();\n+\t\t\t}\n+\t\t});\n+\t\tButton b = new Button(composite, SWT.NONE);\n+\t\tb.setText(buttonText);\n+\t\tGridData data = new GridData();\n+\t\tdata.horizontalAlignment = GridData.FILL;\n+\t\tint widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);\n+\t\tdata.widthHint = Math.max(widthHint, b.computeSize(SWT.DEFAULT,\n+\t\t\t\tSWT.DEFAULT, true).x);\n+\t\tb.setLayoutData(data);\n+\t\tfinal Text formatToInsert = format;\n+\t\tb.addListener(SWT.Selection, new Listener() {\n+\t\t\tpublic void handleEvent(Event event) {\n+\t\t\t\taddVariables(formatToInsert, supportedBindings);\n+\t\t\t}\n+\t\t});\n+\n+\t\treturn new TextPair(format, null);\n+\t}\n+\n+\t/**\n+\t * Initializes states of the controls from the preference store.\n+\t */\n+\tprivate void initializeValues() {\n+\t\tfinal IPreferenceStore store = getPreferenceStore();\n+\n+\t\tfileTextFormat.setText(store\n+\t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n+\t\tfolderTextFormat.setText(store\n+\t\t\t\t.getString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));\n+\t\tprojectTextFormat.setText(store\n+\t\t\t\t.getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n+\n+\t\tshowDirty.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\n+\t\tsetValid(true);\n+\t}\n+\n+\t/**\n+\t * @see IWorkbenchPreferencePage#init(IWorkbench)\n+\t */\n+\tpublic void init(IWorkbench workbench) {\n+\t\t// No-op\n+\t}\n+\n+\t/**\n+\t * OK was clicked. Store the preferences to the plugin store\n+\t * \n+\t * @return whether it is okay to close the preference page\n+\t */\n+\tpublic boolean performOk() {\n+\t\tIPreferenceStore store = getPreferenceStore();\n+\t\tfinal boolean okToClose = performOk(store);\n+\t\tif (store.needsSaving()) {\n+\t\t\tActivator.getDefault().savePluginPreferences();\n+\t\t\tActivator.broadcastPropertyChange(new PropertyChangeEvent(this,\n+\t\t\t\t\tActivator.DECORATORS_CHANGED, null, null));\n+\t\t}\n+\t\treturn okToClose;\n+\t}\n+\n+\t/**\n+\t * Store the preferences to the given preference store\n+\t * \n+\t * @param store\n+\t *            the preference store to store the preferences to\n+\t * \n+\t * @return whether it operation succeeded\n+\t */\n+\tprivate boolean performOk(IPreferenceStore store) {\n+\n+\t\tstore.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n+\t\t\t\tfileTextFormat.getText());\n+\t\tstore.setValue(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n+\t\t\t\tfolderTextFormat.getText());\n+\t\tstore.setValue(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n+\t\t\t\tprojectTextFormat.getText());\n+\n+\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY, showDirty\n+\t\t\t\t.getSelection());\n+\n+\t\treturn true;\n+\t}\n+\n+\t/**\n+\t * Defaults was clicked. Restore the Git decoration preferences to their\n+\t * default values\n+\t */\n+\tprotected void performDefaults() {\n+\t\tsuper.performDefaults();\n+\t\tIPreferenceStore store = getPreferenceStore();\n+\n+\t\tfileTextFormat.setText(store\n+\t\t\t\t.getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n+\t\tfolderTextFormat\n+\t\t\t\t.setText(store\n+\t\t\t\t\t\t.getDefaultString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));\n+\t\tprojectTextFormat\n+\t\t\t\t.setText(store\n+\t\t\t\t\t\t.getDefaultString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n+\n+\t\tshowDirty.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t}\n+\n+\t/**\n+\t * Returns the preference store that belongs to the our plugin.\n+\t * \n+\t * This is important because we want to store our preferences separately\n+\t * from the desktop.\n+\t * \n+\t * @return the preference store for this plugin\n+\t */\n+\tprotected IPreferenceStore doGetPreferenceStore() {\n+\t\treturn Activator.getDefault().getPreferenceStore();\n+\t}\n+\n+\t/*\n+\t * (non-Javadoc)\n+\t * \n+\t * @see org.eclipse.jface.dialogs.DialogPage#dispose()\n+\t */\n+\tpublic void dispose() {\n+\t\tPlatformUI.getWorkbench().getThemeManager()\n+\t\t\t\t.removePropertyChangeListener(themeListener);\n+\t\tsuper.dispose();\n+\t}\n+\n+\t/**\n+\t * Adds another variable to the given target text\n+\t * \n+\t * A ListSelectionDialog pops up and allow the user to choose the variable,\n+\t * which is then inserted at current position in <code>text</code>\n+\t * \n+\t * @param target\n+\t *            the target to add the variable to\n+\t * @param bindings\n+\t *            the map of bindings\n+\t */\n+\tprivate void addVariables(Text target, Map bindings) {\n+\n+\t\tfinal List<StringPair> variables = new ArrayList<StringPair>(bindings\n+\t\t\t\t.size());\n+\n+\t\tILabelProvider labelProvider = new LabelProvider() {\n+\t\t\tpublic String getText(Object element) {\n+\t\t\t\treturn ((StringPair) element).s1\n+\t\t\t\t\t\t+ \" - \" + ((StringPair) element).s2; //$NON-NLS-1$\n+\t\t\t}\n+\t\t};\n+\n+\t\tIStructuredContentProvider contentsProvider = new IStructuredContentProvider() {\n+\t\t\tpublic Object[] getElements(Object inputElement) {\n+\t\t\t\treturn variables.toArray(new StringPair[variables.size()]);\n+\t\t\t}\n+\n+\t\t\tpublic void dispose() {\n+\t\t\t\t// No-op\n+\t\t\t}\n+\n+\t\t\tpublic void inputChanged(Viewer viewer, Object oldInput,\n+\t\t\t\t\tObject newInput) {\n+\t\t\t\t// No-op\n+\t\t\t}\n+\t\t};\n+\n+\t\tfor (Iterator it = bindings.keySet().iterator(); it.hasNext();) {\n+\t\t\tStringPair variable = new StringPair();\n+\t\t\tvariable.s1 = (String) it.next(); // variable\n+\t\t\tvariable.s2 = (String) bindings.get(variable.s1); // description\n+\t\t\tvariables.add(variable);\n+\t\t}\n+\n+\t\tListSelectionDialog dialog = new ListSelectionDialog(this.getShell(),\n+\t\t\t\tthis, contentsProvider, labelProvider,\n+\t\t\t\tUIText.DecoratorPreferencesPage_selectVariablesToAdd);\n+\t\tdialog.setTitle(UIText.DecoratorPreferencesPage_addVariablesTitle);\n+\t\tif (dialog.open() != Window.OK)\n+\t\t\treturn;\n+\n+\t\tObject[] result = dialog.getResult();\n+\n+\t\tfor (int i = 0; i < result.length; i++) {\n+\t\t\ttarget.insert(\"{\" + ((StringPair) result[i]).s1 + \"}\"); //$NON-NLS-1$ //$NON-NLS-2$\n+\t\t}\n+\t}\n+\n+\tclass StringPair {\n+\t\tString s1;\n+\n+\t\tString s2;\n+\t}\n+\n+\tclass TextPair {\n+\t\tTextPair(Text t1, Text t2) {\n+\t\t\tthis.t1 = t1;\n+\t\t\tthis.t2 = t2;\n+\t\t}\n+\n+\t\tText t1;\n+\n+\t\tText t2;\n+\t}\n+\n+\t/**\n+\t * Gets the map of bindings between variables and description, to use for\n+\t * the format editors for files\n+\t * \n+\t * @return the bindings\n+\t */\n+\tprivate Map getFileBindingDescriptions() {\n+\t\tMap<String, String> bindings = new HashMap<String, String>();\n+\t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n+\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\treturn bindings;\n+\t}\n+\n+\t/**\n+\t * Gets the map of bindings between variables and description, to use for\n+\t * the format editors for folders\n+\t * \n+\t * @return the bindings\n+\t */\n+\tprivate Map getFolderBindingDescriptions() {\n+\t\tMap<String, String> bindings = new HashMap<String, String>();\n+\t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n+\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\treturn bindings;\n+\t}\n+\n+\t/**\n+\t * Gets the map of bindings between variables and description, to use for\n+\t * the format editors for projects\n+\t * \n+\t * @return the bindings\n+\t */\n+\tprivate Map getProjectBindingDescriptions() {\n+\t\tMap<String, String> bindings = new HashMap<String, String>();\n+\t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n+\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\treturn bindings;\n+\t}\n+\n+\tprivate void updatePreview() {\n+\t\tif (preview != null)\n+\t\t\tpreview.refresh();\n+\t}\n+\n+\t/**\n+\t * Preview control for showing how changes in the dialog will affect\n+\t * decoration\n+\t */\n+\tprivate class Preview extends LabelProvider implements Observer,\n+\t\t\tITreeContentProvider {\n+\n+\t\tprivate final ResourceManager fImageCache;\n+\n+\t\tprivate final TreeViewer fViewer;\n+\n+\t\tprivate DecorationHelper fHelper;\n+\n+\t\tpublic Preview(Composite composite) {\n+\t\t\treloadDecorationHelper(); // Has to happen before the tree control\n+\t\t\t\t\t\t\t\t\t\t// is constructed\n+\t\t\tSWTUtils.createLabel(composite,\n+\t\t\t\t\tUIText.DecoratorPreferencesPage_preview);\n+\t\t\tfImageCache = new LocalResourceManager(JFaceResources\n+\t\t\t\t\t.getResources());\n+\n+\t\t\tfViewer = new TreeViewer(composite);\n+\t\t\tfViewer.getControl().setLayoutData(SWTUtils.createHVFillGridData());\n+\t\t\tfViewer.setContentProvider(this);\n+\t\t\tfViewer.setLabelProvider(this);\n+\t\t\tfViewer.setInput(PREVIEW_FILESYSTEM_ROOT);\n+\t\t\tfViewer.expandAll();\n+\t\t\tfHelper = new DecorationHelper(new PreferenceStore());\n+\t\t}\n+\n+\t\tprivate void reloadDecorationHelper() {\n+\t\t\tPreferenceStore store = new PreferenceStore();\n+\t\t\tperformOk(store);\n+\t\t\tfHelper = new DecorationHelper(store);\n+\t\t}\n+\n+\t\tpublic void refresh() {\n+\t\t\treloadDecorationHelper();\n+\t\t\tfViewer.refresh(true);\n+\t\t\tsetColorsAndFonts(fViewer.getTree().getItems());\n+\t\t}\n+\n+\t\t@SuppressWarnings(\"unused\")\n+\t\tprivate void setColorsAndFonts(TreeItem[] items) {\n+\t\t\t// TODO: Implement colors and fonts\n+\t\t}\n+\n+\t\tpublic void update(Observable o, Object arg) {\n+\t\t\trefresh();\n+\t\t}\n+\n+\t\tpublic Object[] getChildren(Object parentElement) {\n+\t\t\treturn ((PreviewResource) parentElement).children.toArray();\n+\t\t}\n+\n+\t\tpublic Object getParent(Object element) {\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\tpublic boolean hasChildren(Object element) {\n+\t\t\treturn !((PreviewResource) element).children.isEmpty();\n+\t\t}\n+\n+\t\tpublic Object[] getElements(Object inputElement) {\n+\t\t\treturn ((Collection) inputElement).toArray();\n+\t\t}\n+\n+\t\tpublic void dispose() {\n+\t\t\tfImageCache.dispose();\n+\t\t}\n+\n+\t\tpublic void inputChanged(Viewer viewer, Object oldInput, Object newInput) {\n+\t\t\t// No-op\n+\t\t}\n+\n+\t\tpublic Color getBackground(Object element) {\n+\t\t\treturn getDecoration(element).getBackgroundColor();\n+\t\t}\n+\n+\t\tpublic Color getForeground(Object element) {\n+\t\t\treturn getDecoration(element).getForegroundColor();\n+\t\t}\n+\n+\t\tpublic Font getFont(Object element) {\n+\t\t\treturn getDecoration(element).getFont();\n+\t\t}\n+\n+\t\tpublic String getText(Object element) {\n+\t\t\tfinal PreviewDecoration decoration = getDecoration(element);\n+\t\t\tfinal StringBuffer buffer = new StringBuffer();\n+\t\t\tfinal String prefix = decoration.getPrefix();\n+\t\t\tif (prefix != null)\n+\t\t\t\tbuffer.append(prefix);\n+\t\t\tbuffer.append(((PreviewResource) element).getName());\n+\t\t\tfinal String suffix = decoration.getSuffix();\n+\t\t\tif (suffix != null)\n+\t\t\t\tbuffer.append(suffix);\n+\t\t\treturn buffer.toString();\n+\t\t}\n+\n+\t\tpublic Image getImage(Object element) {\n+\t\t\tfinal String s;\n+\t\t\tswitch (((PreviewResource) element).type) {\n+\t\t\tcase IResource.PROJECT:\n+\t\t\t\ts = SharedImages.IMG_OBJ_PROJECT;\n+\t\t\t\tbreak;\n+\t\t\tcase IResource.FOLDER:\n+\t\t\t\ts = ISharedImages.IMG_OBJ_FOLDER;\n+\t\t\t\tbreak;\n+\t\t\tdefault:\n+\t\t\t\ts = ISharedImages.IMG_OBJ_FILE;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tfinal Image baseImage = PlatformUI.getWorkbench().getSharedImages()\n+\t\t\t\t\t.getImage(s);\n+\t\t\tfinal ImageDescriptor overlay = getDecoration(element).getOverlay();\n+\t\t\tif (overlay == null)\n+\t\t\t\treturn baseImage;\n+\t\t\ttry {\n+\t\t\t\treturn fImageCache.createImage(new DecorationOverlayIcon(\n+\t\t\t\t\t\tbaseImage, overlay, IDecoration.BOTTOM_RIGHT));\n+\t\t\t} catch (Exception e) {\n+\t\t\t\tActivator.logError(e.getMessage(), e);\n+\t\t\t}\n+\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\tprivate PreviewDecoration getDecoration(Object element) {\n+\t\t\tPreviewDecoration decoration = new PreviewDecoration();\n+\t\t\tfHelper.decorate(decoration, (PreviewResource) element);\n+\t\t\treturn decoration;\n+\t\t}\n+\t}\n+\n+\tprivate static class PreviewResource implements IDecoratableResource {\n+\t\tpublic final String name;\n+\n+\t\tpublic final int type;\n+\n+\t\tpublic Collection children;\n+\n+\t\tpublic PreviewResource(String name, int type) {\n+\t\t\tthis.name = name;\n+\t\t\tthis.type = type;\n+\t\t\tthis.children = Collections.EMPTY_LIST;\n+\t\t}\n+\n+\t\tpublic String getName() {\n+\t\t\treturn name;\n+\t\t}\n+\n+\t\tpublic int getType() {\n+\t\t\treturn type;\n+\t\t}\n+\t}\n+\n+\tprivate class PreviewDecoration implements IDecoration {\n+\n+\t\tprivate List<String> prefixes = new ArrayList<String>();\n+\n+\t\tprivate List<String> suffixes = new ArrayList<String>();\n+\n+\t\tprivate ImageDescriptor overlay = null;\n+\n+\t\tprivate Font font;\n+\n+\t\tprivate Color backgroundColor;\n+\n+\t\tprivate Color foregroundColor;\n+\n+\t\tpublic void addOverlay(ImageDescriptor overlayImage) {\n+\t\t\toverlay = overlayImage;\n+\t\t}\n+\n+\t\tpublic void addOverlay(ImageDescriptor overlayImage, int quadrant) {\n+\t\t\toverlay = overlayImage;\n+\t\t}\n+\n+\t\tpublic void addPrefix(String prefix) {\n+\t\t\tprefixes.add(prefix);\n+\t\t}\n+\n+\t\tpublic void addSuffix(String suffix) {\n+\t\t\tsuffixes.add(suffix);\n+\t\t}\n+\n+\t\tpublic IDecorationContext getDecorationContext() {\n+\t\t\treturn new DecorationContext();\n+\t\t}\n+\n+\t\tpublic void setBackgroundColor(Color color) {\n+\t\t\tbackgroundColor = color;\n+\t\t}\n+\n+\t\tpublic void setForegroundColor(Color color) {\n+\t\t\tforegroundColor = color;\n+\t\t}\n+\n+\t\tpublic void setFont(Font font) {\n+\t\t\tthis.font = font;\n+\t\t}\n+\n+\t\tpublic ImageDescriptor getOverlay() {\n+\t\t\treturn overlay;\n+\t\t}\n+\n+\t\tpublic String getPrefix() {\n+\t\t\tStringBuffer sb = new StringBuffer();\n+\t\t\tfor (Iterator<String> iter = prefixes.iterator(); iter.hasNext();) {\n+\t\t\t\tsb.append(iter.next());\n+\t\t\t}\n+\t\t\treturn sb.toString();\n+\t\t}\n+\n+\t\tpublic String getSuffix() {\n+\t\t\tStringBuffer sb = new StringBuffer();\n+\t\t\tfor (Iterator<String> iter = suffixes.iterator(); iter.hasNext();) {\n+\t\t\t\tsb.append(iter.next());\n+\t\t\t}\n+\t\t\treturn sb.toString();\n+\t\t}\n+\n+\t\tpublic Font getFont() {\n+\t\t\treturn font;\n+\t\t}\n+\n+\t\tpublic Color getBackgroundColor() {\n+\t\t\treturn backgroundColor;\n+\t\t}\n+\n+\t\tpublic Color getForegroundColor() {\n+\t\t\treturn foregroundColor;\n+\t\t}\n+\t}\n+}\ndiff --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\nindex c1d9db6..4660983 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -169,7 +169,6 @@ RefSpecPage_annotatedTagsAutoFollow=Automatically follow tags if we fetch the th\n RefSpecPage_annotatedTagsFetchTags=Always fetch tags, even if we do not have the thing it points at\n RefSpecPage_annotatedTagsNoTags=Never fetch tags, even if we have the thing it points at\n \n-Decorator_failedLazyLoading=Resource decorator failed to load tree contents on demand.\n QuickDiff_failedLoading=Quick diff failed to obtain file data.\n \n ResourceHistory_toggleCommentWrap=Wrap Comments\n@@ -346,3 +345,27 @@ BranchSelectionDialog_ResetTypeMixed=&Mixed (working directory unmodified)\n BranchSelectionDialog_ResetTypeSoft=&Soft (Index and working directory unmodified)\n BranchSelectionDialog_Tags=Tags\n BranchSelectionDialog_Refs=&Refs\n+\n+Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources.\n+\n+DecoratorPreferencesPage_addVariablesTitle=Add Variables\n+DecoratorPreferencesPage_addVariablesAction=Add &Variables...\n+DecoratorPreferencesPage_computeDeep=Include &ancestors when re-decorating changed resources\n+DecoratorPreferencesPage_description=Shows Git specific information on resources in projects under version control.\n+\n+DecoratorPreferencesPage_decorationSettings=Decoration &settings:\n+DecoratorPreferencesPage_preview=Preview:\n+DecoratorPreferencesPage_fileFormatLabel=&Files:\n+DecoratorPreferencesPage_folderFormatLabel=F&olders:\n+DecoratorPreferencesPage_projectFormatLabel=&Projects:\n+DecoratorPreferencesPage_fileFormatDefault={name}\n+DecoratorPreferencesPage_folderFormatDefault={name}\n+DecoratorPreferencesPage_projectFormatDefault={name}\n+DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or disable Git decorations.\n+DecoratorPreferencesPage_generalTabFolder=&General\n+DecoratorPreferencesPage_nameResourceVariable=name of the resource being decorated\n+DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and project text labels:\n+DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:\n+DecoratorPreferencesPage_textLabel=T&ext Decorations\n+DecoratorPreferencesPage_iconLabel=&Icon Decorations\n+\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104258","messageId":"1234377614-23798-8-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-7-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 07/12] Add binding for name of the current branch","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:09Z","receivedAt":"2009-02-11T18:40:09Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"This is an example of how to add more bindings to the\ndecoration preferences, and how they are implemented in\nthe decorator.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    3 ++\n .../decorators/GitLightweightDecorator.java        |   26 ++++++++++++++++---\n .../internal/decorators/IDecoratableResource.java  |    8 ++++++\n .../preferences/GitDecoratorPreferencePage.java    |   21 +++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +-\n 5 files changed, 50 insertions(+), 11 deletions(-)\n\ndiff --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\nindex a7ef408..345c66b 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -971,6 +971,9 @@\n \tpublic static String DecoratorPreferencesPage_nameResourceVariable;\n \n \t/** */\n+\tpublic static String DecoratorPreferencesPage_bindingBranchName;\n+\n+\t/** */\n \tpublic static String DecoratorPreferencesPage_selectFormats;\n \n \t/** */\ndiff --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\nindex 85b9173..265d5a3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -13,6 +13,7 @@\n \n package org.spearce.egit.ui.internal.decorators;\n \n+import java.io.IOException;\n import java.util.ArrayList;\n import java.util.HashMap;\n import java.util.HashSet;\n@@ -44,6 +45,7 @@\n import org.eclipse.team.ui.TeamUI;\n import org.eclipse.ui.IContributorResourceAdapter;\n import org.eclipse.ui.PlatformUI;\n+import org.spearce.egit.core.GitException;\n import org.spearce.egit.core.internal.util.ExceptionCollector;\n import org.spearce.egit.core.project.GitProjectData;\n import org.spearce.egit.core.project.RepositoryChangeListener;\n@@ -157,17 +159,26 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (activator == null)\n \t\t\treturn;\n \n-\t\tDecorationHelper helper = new DecorationHelper(activator\n-\t\t\t\t.getPreferenceStore());\n-\t\thelper.decorate(decoration, new DecoratableResourceAdapter(resource));\n+\t\ttry {\n+\t\t\tDecorationHelper helper = new DecorationHelper(activator\n+\t\t\t\t\t.getPreferenceStore());\n+\t\t\thelper.decorate(decoration,\n+\t\t\t\t\tnew DecoratableResourceAdapter(resource));\n+\t\t} catch (IOException e) {\n+\t\t\thandleException(resource, GitException.wrapException(e));\n+\t\t}\n \t}\n \n \tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n \n \t\tprivate IResource resource;\n+\t\tprivate String branch;\n \n-\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap) {\n+\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap) throws IOException {\n \t\t\tresource = resourceToWrap;\n+\t\t\tRepositoryMapping mapping = RepositoryMapping.getMapping(resource);\n+\t\t\tRepository repository = mapping.getRepository();\n+\t\t\tbranch = repository.getBranch();\n \t\t}\n \n \t\tpublic String getName() {\n@@ -177,6 +188,10 @@ public String getName() {\n \t\tpublic int getType() {\n \t\t\treturn resource.getType();\n \t\t}\n+\n+\t\tpublic String getBranch() {\n+\t\t\treturn branch;\n+\t\t}\n \t}\n \n \t/**\n@@ -192,6 +207,8 @@ public int getType() {\n \n \t\t/** */\n \t\tpublic static final String BINDING_RESOURCE_NAME = \"name\"; //$NON-NLS-1$\n+\t\t/** */\n+\t\tpublic static final String BINDING_BRANCH_NAME = \"branch\"; //$NON-NLS-1$\n \n \t\t/**\n \t\t * Constructs a decorator using the rules from the given\n@@ -234,6 +251,7 @@ public void decorate(IDecoration decoration,\n \n \t\t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\t\tbindings.put(BINDING_RESOURCE_NAME, resource.getName());\n+\t\t\tbindings.put(BINDING_BRANCH_NAME, resource.getBranch());\n \n \t\t\tdecorate(decoration, format, bindings);\n \t\t}\ndiff --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\nindex 8d6c741..6b36e0e 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n@@ -28,4 +28,12 @@\n \t * @return the name of the resource\n \t */\n \tString getName();\n+\n+\t/**\n+\t * Gets the current branch of the resource if applicable\n+\t * \n+\t * @return the name of the current branch, or <code>null</code> if not\n+\t *         applicable\n+\t */\n+\tString getBranch();\n }\ndiff --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\nindex 84203b7..b7d737c 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -93,10 +93,10 @@\n \n \tstatic {\n \t\tfinal PreviewResource project = new PreviewResource(\n-\t\t\t\t\"Project\", IResource.PROJECT); //$NON-NLS-1$1\n+\t\t\t\t\"Project\", IResource.PROJECT, \"master\"); //$NON-NLS-1$1\n \t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n-\t\tchildren.add(new PreviewResource(\"folder\", IResource.FOLDER)); //$NON-NLS-1$\n-\t\tchildren.add(new PreviewResource(\"file.txt\", IResource.FILE)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\"folder\", IResource.FOLDER, null)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\"file.txt\", IResource.FILE, null)); //$NON-NLS-1$\n \t\tproject.children = children;\n \t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n \t}\n@@ -456,6 +456,8 @@ private Map getProjectBindingDescriptions() {\n \t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n \t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\tbindings.put(DecorationHelper.BINDING_BRANCH_NAME,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingBranchName);\n \t\treturn bindings;\n \t}\n \n@@ -478,8 +480,8 @@ private void updatePreview() {\n \t\tprivate DecorationHelper fHelper;\n \n \t\tpublic Preview(Composite composite) {\n-\t\t\treloadDecorationHelper(); // Has to happen before the tree control\n-\t\t\t\t\t\t\t\t\t\t// is constructed\n+\t\t\t// Has to happen before the tree control is constructed\n+\t\t\treloadDecorationHelper();\n \t\t\tSWTUtils.createLabel(composite,\n \t\t\t\t\tUIText.DecoratorPreferencesPage_preview);\n \t\t\tfImageCache = new LocalResourceManager(JFaceResources\n@@ -602,12 +604,15 @@ private PreviewDecoration getDecoration(Object element) {\n \tprivate static class PreviewResource implements IDecoratableResource {\n \t\tpublic final String name;\n \n+\t\tpublic final String branch;\n+\n \t\tpublic final int type;\n \n \t\tpublic Collection children;\n \n-\t\tpublic PreviewResource(String name, int type) {\n+\t\tpublic PreviewResource(String name, int type, String branch) {\n \t\t\tthis.name = name;\n+\t\t\tthis.branch = branch;\n \t\t\tthis.type = type;\n \t\t\tthis.children = Collections.EMPTY_LIST;\n \t\t}\n@@ -619,6 +624,10 @@ public String getName() {\n \t\tpublic int getType() {\n \t\t\treturn type;\n \t\t}\n+\n+\t\tpublic String getBranch() {\n+\t\t\treturn branch;\n+\t\t}\n \t}\n \n \tprivate class PreviewDecoration implements IDecoration {\ndiff --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\nindex 4660983..d050be9 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -360,10 +360,11 @@ DecoratorPreferencesPage_folderFormatLabel=F&olders:\n DecoratorPreferencesPage_projectFormatLabel=&Projects:\n DecoratorPreferencesPage_fileFormatDefault={name}\n DecoratorPreferencesPage_folderFormatDefault={name}\n-DecoratorPreferencesPage_projectFormatDefault={name}\n+DecoratorPreferencesPage_projectFormatDefault={name} [{branch}]\n DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or disable Git decorations.\n DecoratorPreferencesPage_generalTabFolder=&General\n DecoratorPreferencesPage_nameResourceVariable=name of the resource being decorated\n+DecoratorPreferencesPage_bindingBranchName=current branch of the project\n DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and project text labels:\n DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:\n DecoratorPreferencesPage_textLabel=T&ext Decorations\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104252","messageId":"1234377614-23798-9-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-8-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 08/12] Add icon decoration for tracked and untracked resources","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:10Z","receivedAt":"2009-02-11T18:40:10Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Can be enabled/disabled in the preferences\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n org.spearce.egit.ui/icons/ovr/shared.gif           |  Bin 106 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/untracked.gif        |  Bin 0 -> 79 bytes\n .../egit/ui/PluginPreferenceInitializer.java       |    4 +-\n .../src/org/spearce/egit/ui/UIIcons.java           |    6 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +\n .../src/org/spearce/egit/ui/UIText.java            |    6 +\n .../decorators/GitLightweightDecorator.java        |  162 ++++++++++++++++++--\n .../internal/decorators/IDecoratableResource.java  |   15 ++\n .../preferences/GitDecoratorPreferencePage.java    |   89 +++++++++--\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 10 files changed, 259 insertions(+), 29 deletions(-)\n delete mode 100644 org.spearce.egit.ui/icons/ovr/shared.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/untracked.gif\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/shared.gif b/org.spearce.egit.ui/icons/ovr/shared.gif\ndeleted file mode 100644\nindex eb71a3c742e133c2ed61c958e237c71e0f7cb6aa..0000000000000000000000000000000000000000\nGIT binary patch\nliteral 0\nHcmV?d00001\n\nliteral 106\nzcmZ?wbhEHbWM|-D*v!E2|KHF5k5c|0Q~7_){{ITmE8U{M-dx?8EBgQce+Fzo@h1x-\np15m3DND^cQ1B<x8$xa82iYrMhmeJo^<v0pdEtw8K%o1d<1^~aoAP)cl\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/untracked.gif b/org.spearce.egit.ui/icons/ovr/untracked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..45ca32060700d71abcb88bbcdb1000d503fe11cf\nGIT binary patch\nliteral 79\nzcmZ?wbhEHbWM|-DSj51<z}U+mGMhnsAy57};pVIV!2l?%_>+Z^fq{)d2gqgssbydm\nbiP#m!(lAAgdxkboj%vJ&f(EaJDuXouU6m8v\n\nliteral 0\nHcmV?d00001\n\ndiff --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\nindex 79c2665..7465444 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n@@ -35,13 +35,15 @@ public void initializeDefaultPreferences() {\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);\n \n+\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_fileFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_folderFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_projectFormatDefault);\n-\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON, true);\n \n \t\tw = new int[] { 500, 500 };\n \t\tUIPreferences.setDefault(prefs,\ndiff --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\nindex ced186e..4c0d189 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n@@ -24,8 +24,8 @@\n \t/** Decoration for resource removed from the index but not commit. */\n \tpublic static final ImageDescriptor OVR_PENDING_REMOVE;\n \n-\t/** Decoration for resource tracked and committed in git. */\n-\tpublic static final ImageDescriptor OVR_SHARED;\n+\t/** Decoration for resource not being tracked by Git */\n+\tpublic static final ImageDescriptor OVR_UNTRACKED;\n \n \t/** Decoration for tracked resource with a merge conflict.  */\n \tpublic static final ImageDescriptor OVR_CONFLICT;\n@@ -86,7 +86,7 @@\n \t\tbase = init();\n \t\tOVR_PENDING_ADD = map(\"ovr/pending_add.gif\");\n \t\tOVR_PENDING_REMOVE = map(\"ovr/pending_remove.gif\");\n-\t\tOVR_SHARED = map(\"ovr/shared.gif\");\n+\t\tOVR_UNTRACKED = map(\"ovr/untracked.gif\");\n \t\tOVR_CONFLICT = map(\"ovr/conflict.gif\");\n \t\tOVR_ASSUMEVALID = map(\"ovr/assumevalid.gif\");\n \t\tELCL16_FIND = map(\"elcl16/find.gif\");\ndiff --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\nindex a6168a0..7916cea 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n@@ -60,6 +60,10 @@\n \tpublic final static String DECORATOR_FOLDERTEXT_DECORATION = \"decorator_foldertext_decoration\";\n \t/** */\n \tpublic final static String DECORATOR_PROJECTTEXT_DECORATION = \"decorator_projecttext_decoration\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_TRACKED_ICON = \"decorator_show_tracked_icon\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_UNTRACKED_ICON = \"decorator_show_untracked_icon\";\n \n \t/**\n \t * Get the preference values associated with a fixed integer array.\ndiff --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\nindex 345c66b..60e4eaa 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -988,6 +988,12 @@\n \t/** */\n \tpublic static String DecoratorPreferencesPage_labelDecorationsLink;\n \n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowTracked;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowUntracked;\n+\n \tstatic {\n \t\tinitializeMessages(UIText.class.getPackage().getName() + \".uitext\",\n \t\t\t\tUIText.class);\ndiff --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\nindex 265d5a3..b20070a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -15,6 +15,7 @@\n \n import java.io.IOException;\n import java.util.ArrayList;\n+import java.util.Collections;\n import java.util.HashMap;\n import java.util.HashSet;\n import java.util.List;\n@@ -34,6 +35,7 @@\n import org.eclipse.core.runtime.IAdaptable;\n import org.eclipse.core.runtime.IStatus;\n import org.eclipse.jface.preference.IPreferenceStore;\n+import org.eclipse.jface.resource.ImageDescriptor;\n import org.eclipse.jface.util.IPropertyChangeListener;\n import org.eclipse.jface.util.PropertyChangeEvent;\n import org.eclipse.jface.viewers.IDecoration;\n@@ -41,7 +43,11 @@\n import org.eclipse.jface.viewers.LabelProvider;\n import org.eclipse.jface.viewers.LabelProviderChangedEvent;\n import org.eclipse.osgi.util.TextProcessor;\n+import org.eclipse.swt.graphics.ImageData;\n import org.eclipse.swt.widgets.Display;\n+import org.eclipse.team.core.Team;\n+import org.eclipse.team.ui.ISharedImages;\n+import org.eclipse.team.ui.TeamImages;\n import org.eclipse.team.ui.TeamUI;\n import org.eclipse.ui.IContributorResourceAdapter;\n import org.eclipse.ui.PlatformUI;\n@@ -51,13 +57,22 @@\n import org.spearce.egit.core.project.RepositoryChangeListener;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheIterator;\n+import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.IndexChangedEvent;\n+import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.RefsChangedEvent;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.RepositoryChangedEvent;\n import org.spearce.jgit.lib.RepositoryListener;\n+import org.spearce.jgit.revwalk.RevWalk;\n+import org.spearce.jgit.treewalk.EmptyTreeIterator;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n \n /**\n  * Supplies annotations for displayed resources\n@@ -144,7 +159,7 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (!resource.exists() && !resource.isPhantom())\n \t\t\treturn;\n \n-\t\t// Make sure we're dealing with a Git project\n+\t\t// Make sure we're dealing with a project under Git revision control\n \t\tfinal RepositoryMapping mapping = RepositoryMapping\n \t\t\t\t.getMapping(resource);\n \t\tif (mapping == null)\n@@ -171,14 +186,80 @@ public void decorate(Object element, IDecoration decoration) {\n \n \tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n \n-\t\tprivate IResource resource;\n-\t\tprivate String branch;\n+\t\tprivate final IResource resource;\n \n-\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap) throws IOException {\n+\t\tprivate final RepositoryMapping mapping;\n+\n+\t\tprivate final Repository repository;\n+\n+\t\tprivate final ObjectId headId;\n+\n+\t\tprivate String branch = \"\";\n+\n+\t\tprivate boolean tracked = false;\n+\n+\t\tprivate boolean ignored = false;\n+\n+\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n+\t\t\t\tthrows IOException {\n \t\t\tresource = resourceToWrap;\n-\t\t\tRepositoryMapping mapping = RepositoryMapping.getMapping(resource);\n-\t\t\tRepository repository = mapping.getRepository();\n+\t\t\tmapping = RepositoryMapping.getMapping(resource);\n+\t\t\trepository = mapping.getRepository();\n+\t\t\theadId = repository.resolve(Constants.HEAD);\n+\n+\t\t\tinitializeValues();\n+\t\t}\n+\n+\t\t/**\n+\t\t * Initialize the various values that are used for making decoration\n+\t\t * decisions later on.\n+\t\t * \n+\t\t * We might as well pre-load these now, instead of using lazy\n+\t\t * initialization, because they are all read by the decorator when\n+\t\t * building variable bindings and computing the preferred overlay.\n+\t\t * \n+\t\t * @throws IOException\n+\t\t */\n+\t\tprivate void initializeValues() throws IOException {\n+\n+\t\t\t// Resolve current branch\n \t\t\tbranch = repository.getBranch();\n+\n+\t\t\t// Resolve tracked state\n+\t\t\tif (getType() == IResource.PROJECT) {\n+\t\t\t\ttracked = true;\n+\t\t\t} else {\n+\t\t\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n+\n+\t\t\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n+\t\t\t\t\t\t.getRepoRelativePath(resource));\n+\t\t\t\tif (!(repositoryPaths.isEmpty() || repositoryPaths.contains(\"\"))) {\n+\t\t\t\t\ttreeWalk.setFilter(PathFilterGroup\n+\t\t\t\t\t\t\t.createFromStrings(repositoryPaths));\n+\t\t\t\t\ttreeWalk.setRecursive(treeWalk.getFilter()\n+\t\t\t\t\t\t\t.shouldBeRecursive());\n+\t\t\t\t\ttreeWalk.reset();\n+\n+\t\t\t\t\tif (headId != null)\n+\t\t\t\t\t\ttreeWalk.addTree(new RevWalk(repository)\n+\t\t\t\t\t\t\t\t.parseTree(headId));\n+\t\t\t\t\telse\n+\t\t\t\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n+\n+\t\t\t\t\ttreeWalk.addTree(new DirCacheIterator(DirCache\n+\t\t\t\t\t\t\t.read(repository)));\n+\t\t\t\t\tif (treeWalk.next()) {\n+\t\t\t\t\t\ttracked = true;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t// Resolve ignored state (currently only reads the global Eclipse\n+\t\t\t// ignores)\n+\t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n+\t\t\tif (Team.isIgnoredHint(resource)) {\n+\t\t\t\tignored = true;\n+\t\t\t}\n \t\t}\n \n \t\tpublic String getName() {\n@@ -192,6 +273,14 @@ public int getType() {\n \t\tpublic String getBranch() {\n \t\t\treturn branch;\n \t\t}\n+\n+\t\tpublic boolean isTracked() {\n+\t\t\treturn tracked;\n+\t\t}\n+\n+\t\tpublic boolean isIgnored() {\n+\t\t\treturn ignored;\n+\t\t}\n \t}\n \n \t/**\n@@ -203,13 +292,45 @@ public String getBranch() {\n \t */\n \tpublic static class DecorationHelper {\n \n-\t\tprivate IPreferenceStore store;\n-\n \t\t/** */\n \t\tpublic static final String BINDING_RESOURCE_NAME = \"name\"; //$NON-NLS-1$\n+\n \t\t/** */\n \t\tpublic static final String BINDING_BRANCH_NAME = \"branch\"; //$NON-NLS-1$\n \n+\t\tprivate IPreferenceStore store;\n+\n+\t\t/**\n+\t\t * Define a cached image descriptor which only creates the image data\n+\t\t * once\n+\t\t */\n+\t\tprivate static class CachedImageDescriptor extends ImageDescriptor {\n+\t\t\tImageDescriptor descriptor;\n+\n+\t\t\tImageData data;\n+\n+\t\t\tpublic CachedImageDescriptor(ImageDescriptor descriptor) {\n+\t\t\t\tthis.descriptor = descriptor;\n+\t\t\t}\n+\n+\t\t\tpublic ImageData getImageData() {\n+\t\t\t\tif (data == null) {\n+\t\t\t\t\tdata = descriptor.getImageData();\n+\t\t\t\t}\n+\t\t\t\treturn data;\n+\t\t\t}\n+\t\t}\n+\n+\t\tprivate static ImageDescriptor trackedImage;\n+\n+\t\tprivate static ImageDescriptor untrackedImage;\n+\n+\t\tstatic {\n+\t\t\ttrackedImage = new CachedImageDescriptor(TeamImages\n+\t\t\t\t\t.getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR));\n+\t\t\tuntrackedImage = new CachedImageDescriptor(UIIcons.OVR_UNTRACKED);\n+\t\t}\n+\n \t\t/**\n \t\t * Constructs a decorator using the rules from the given\n \t\t * <code>preferencesStore</code>\n@@ -233,6 +354,12 @@ public DecorationHelper(IPreferenceStore preferencesStore) {\n \t\t */\n \t\tpublic void decorate(IDecoration decoration,\n \t\t\t\tIDecoratableResource resource) {\n+\t\t\tdecorateText(decoration, resource);\n+\t\t\tdecorateIcons(decoration, resource);\n+\t\t}\n+\n+\t\tprivate void decorateText(IDecoration decoration,\n+\t\t\t\tIDecoratableResource resource) {\n \t\t\tString format = \"\";\n \t\t\tswitch (resource.getType()) {\n \t\t\tcase IResource.FILE:\n@@ -256,9 +383,24 @@ public void decorate(IDecoration decoration,\n \t\t\tdecorate(decoration, format, bindings);\n \t\t}\n \n+\t\tprivate void decorateIcons(IDecoration decoration,\n+\t\t\t\tIDecoratableResource resource) {\n+\t\t\tif (resource.isIgnored())\n+\t\t\t\treturn;\n+\n+\t\t\tif (resource.isTracked()) {\n+\t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON))\n+\t\t\t\t\tdecoration.addOverlay(trackedImage);\n+\t\t\t} else if (store\n+\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n+\t\t\t\tdecoration.addOverlay(untrackedImage);\n+\t\t\t}\n+\t\t}\n+\n \t\t/**\n-\t\t * Decorates the given <code>decoration</code>, using the given\n-\t\t * <code>format</code>, and mapped using <code>bindings</code>\n+\t\t * Decorates the given <code>decoration</code>, using the specified text\n+\t\t * <code>format</code>, and mapped using the variable bindings from\n+\t\t * <code>bindings</code>\n \t\t * \n \t\t * @param decoration\n \t\t *            the decoration to decorate\ndiff --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\nindex 6b36e0e..f144214 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n@@ -36,4 +36,19 @@\n \t *         applicable\n \t */\n \tString getBranch();\n+\n+\t/**\n+\t * Returns whether or not the resource is tracked by Git\n+\t * \n+\t * @return whether or not the resource is tracked by Git\n+\t */\n+\tboolean isTracked();\n+\n+\t/**\n+\t * Returns whether or not the resource is ignored, either by a global team\n+\t * ignore in Eclipse, or by .git/info/exclude et al.\n+\t * \n+\t * @return whether or not the resource is ignored\n+\t */\n+\tboolean isIgnored();\n }\ndiff --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\nindex b7d737c..7b637e3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -44,6 +44,9 @@\n import org.eclipse.swt.SWT;\n import org.eclipse.swt.events.ModifyEvent;\n import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.events.SelectionListener;\n import org.eclipse.swt.graphics.Color;\n import org.eclipse.swt.graphics.Font;\n import org.eclipse.swt.graphics.Image;\n@@ -83,7 +86,11 @@\n \n \tprivate Text projectTextFormat;\n \n-\tprivate Button showDirty;\n+\tprivate Button computeDeepDirtyState;\n+\n+\tprivate Button showTracked;\n+\n+\tprivate Button showUntracked;\n \n \tprivate Preview preview;\n \n@@ -93,10 +100,16 @@\n \n \tstatic {\n \t\tfinal PreviewResource project = new PreviewResource(\n-\t\t\t\t\"Project\", IResource.PROJECT, \"master\"); //$NON-NLS-1$1\n+\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false); //$NON-NLS-1$1\n \t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n-\t\tchildren.add(new PreviewResource(\"folder\", IResource.FOLDER, null)); //$NON-NLS-1$\n-\t\tchildren.add(new PreviewResource(\"file.txt\", IResource.FILE, null)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"folder\", IResource.FOLDER, null, true, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"file.txt\", IResource.FILE, null, true, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"untracked.txt\", IResource.FILE, null, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"ignored.txt\", IResource.FILE, null, false, true)); //$NON-NLS-1$\n \t\tproject.children = children;\n \t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n \t}\n@@ -160,7 +173,7 @@ private Control createGeneralDecoratorPage(Composite parent) {\n \t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n \t\t\t\tSWTUtils.MARGINS_DEFAULT);\n \n-\t\tshowDirty = SWTUtils.createCheckBox(composite,\n+\t\tcomputeDeepDirtyState = SWTUtils.createCheckBox(composite,\n \t\t\t\tUIText.DecoratorPreferencesPage_computeDeep);\n \n \t\treturn composite;\n@@ -202,6 +215,11 @@ private Control createIconDecoratorPage(Composite parent) {\n \t\tComposite imageGroup = SWTUtils.createHVFillComposite(parent,\n \t\t\t\tSWTUtils.MARGINS_DEFAULT, 2);\n \n+\t\tshowTracked = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowTracked);\n+\t\tshowUntracked = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowUntracked);\n+\n \t\treturn imageGroup;\n \t}\n \n@@ -241,6 +259,9 @@ public void handleEvent(Event event) {\n \tprivate void initializeValues() {\n \t\tfinal IPreferenceStore store = getPreferenceStore();\n \n+\t\tcomputeDeepDirtyState.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\n \t\tfileTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n \t\tfolderTextFormat.setText(store\n@@ -248,8 +269,20 @@ private void initializeValues() {\n \t\tprojectTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n \n-\t\tshowDirty.setSelection(store\n-\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\tshowTracked.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));\n+\t\tshowUntracked.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n+\n+\t\tSelectionListener selectionListener = new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tfPreview.refresh();\n+\t\t\t}\n+\t\t};\n+\n+\t\tcomputeDeepDirtyState.addSelectionListener(selectionListener);\n+\t\tshowTracked.addSelectionListener(selectionListener);\n+\t\tshowUntracked.addSelectionListener(selectionListener);\n \n \t\tsetValid(true);\n \t}\n@@ -287,6 +320,9 @@ public boolean performOk() {\n \t */\n \tprivate boolean performOk(IPreferenceStore store) {\n \n+\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY,\n+\t\t\t\tcomputeDeepDirtyState.getSelection());\n+\n \t\tstore.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tfileTextFormat.getText());\n \t\tstore.setValue(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n@@ -294,8 +330,10 @@ private boolean performOk(IPreferenceStore store) {\n \t\tstore.setValue(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n \t\t\t\tprojectTextFormat.getText());\n \n-\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY, showDirty\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, showTracked\n \t\t\t\t.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON,\n+\t\t\t\tshowUntracked.getSelection());\n \n \t\treturn true;\n \t}\n@@ -308,6 +346,9 @@ protected void performDefaults() {\n \t\tsuper.performDefaults();\n \t\tIPreferenceStore store = getPreferenceStore();\n \n+\t\tcomputeDeepDirtyState.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\n \t\tfileTextFormat.setText(store\n \t\t\t\t.getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n \t\tfolderTextFormat\n@@ -317,8 +358,11 @@ protected void performDefaults() {\n \t\t\t\t.setText(store\n \t\t\t\t\t\t.getDefaultString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n \n-\t\tshowDirty.setSelection(store\n-\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\tshowTracked.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));\n+\t\tshowUntracked\n+\t\t\t\t.setSelection(store\n+\t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n \t}\n \n \t/**\n@@ -602,19 +646,26 @@ private PreviewDecoration getDecoration(Object element) {\n \t}\n \n \tprivate static class PreviewResource implements IDecoratableResource {\n-\t\tpublic final String name;\n+\t\tprivate final String name;\n+\n+\t\tprivate final String branch;\n \n-\t\tpublic final String branch;\n+\t\tprivate final int type;\n \n-\t\tpublic final int type;\n+\t\tprivate Collection children;\n \n-\t\tpublic Collection children;\n+\t\tprivate boolean tracked;\n \n-\t\tpublic PreviewResource(String name, int type, String branch) {\n+\t\tprivate boolean ignored;\n+\n+\t\tpublic PreviewResource(String name, int type, String branch,\n+\t\t\t\tboolean tracked, boolean ignored) {\n \t\t\tthis.name = name;\n \t\t\tthis.branch = branch;\n \t\t\tthis.type = type;\n \t\t\tthis.children = Collections.EMPTY_LIST;\n+\t\t\tthis.tracked = tracked;\n+\t\t\tthis.ignored = ignored;\n \t\t}\n \n \t\tpublic String getName() {\n@@ -628,6 +679,14 @@ public int getType() {\n \t\tpublic String getBranch() {\n \t\t\treturn branch;\n \t\t}\n+\n+\t\tpublic boolean isTracked() {\n+\t\t\treturn tracked;\n+\t\t}\n+\n+\t\tpublic boolean isIgnored() {\n+\t\t\treturn ignored;\n+\t\t}\n \t}\n \n \tprivate class PreviewDecoration implements IDecoration {\ndiff --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\nindex d050be9..9940177 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -369,4 +369,6 @@ DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and\n DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:\n DecoratorPreferencesPage_textLabel=T&ext Decorations\n DecoratorPreferencesPage_iconLabel=&Icon Decorations\n+DecoratorPreferencesPage_iconsShowTracked=Tracked resources\n+DecoratorPreferencesPage_iconsShowUntracked=Untracked resources\n \n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104251","messageId":"1234377614-23798-10-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-9-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 09/12] Implement icon and text decorations of various resource states","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:11Z","receivedAt":"2009-02-11T18:40:11Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"  - Dirty\n  - Staged\n  - Conflict\n  - Assume valid\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n org.spearce.egit.ui/icons/ovr/assume_valid.gif     |  Bin 0 -> 85 bytes\n org.spearce.egit.ui/icons/ovr/assumevalid.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/conflict.gif         |  Bin 64 -> 194 bytes\n org.spearce.egit.ui/icons/ovr/pending_add.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/pending_remove.gif   |  Bin 111 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/staged.gif           |  Bin 0 -> 114 bytes\n org.spearce.egit.ui/icons/ovr/staged_added.gif     |  Bin 0 -> 169 bytes\n org.spearce.egit.ui/icons/ovr/staged_removed.gif   |  Bin 0 -> 176 bytes\n .../egit/ui/PluginPreferenceInitializer.java       |    3 +\n .../src/org/spearce/egit/ui/UIIcons.java           |   15 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    6 +\n .../src/org/spearce/egit/ui/UIText.java            |   17 +-\n .../decorators/GitLightweightDecorator.java        |  299 +++++++++++++++++---\n .../internal/decorators/IDecoratableResource.java  |   46 +++\n .../preferences/GitDecoratorPreferencePage.java    |  152 +++++++++--\n .../src/org/spearce/egit/ui/uitext.properties      |   15 +-\n 16 files changed, 477 insertions(+), 76 deletions(-)\n create mode 100644 org.spearce.egit.ui/icons/ovr/assume_valid.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/assumevalid.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_add.gif\n delete mode 100644 org.spearce.egit.ui/icons/ovr/pending_remove.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged_added.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/staged_removed.gif\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/assume_valid.gif b/org.spearce.egit.ui/icons/ovr/assume_valid.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..b6d7167cb38b147aa0fcc115121f3166276f6688\nGIT binary patch\nliteral 85\nzcmZ?wbhEHb<Y3@pSj5Wk|Nno6&}sGa_b)s3!M=V!hzkaaKUo+V7+4u}Ks=CI24<;<\ngU94*oY!sY|O?Vb;E(m>L;^NQ~(4hBvvkHSX0J)kQGXMYp\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/assumevalid.gif b/org.spearce.egit.ui/icons/ovr/assumevalid.gif\ndeleted file mode 100644\nindex c7262ed4e3f9437a51806f70fbc851e3a6f951d3..0000000000000000000000000000000000000000\nGIT binary patch\nliteral 0\nHcmV?d00001\n\nliteral 64\nzcmZ?wbhEHbWM|-D_{abP{~H+o{|7M?f3h$#FfcRdfH)v|1}4Ed_6CP$GF;`pupzry\nLj9oc|fx#L8z0(dE\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/conflict.gif b/org.spearce.egit.ui/icons/ovr/conflict.gif\nindex b444be94a2d09561b212138b1514d5c07610cc07..fce456a49a0d6b19feaef4e1fb8615d8083c674c 100644\nGIT binary patch\nliteral 194\nzcmZ?wbhEHb<YwSvIKsfNSXBI}k=aul+s7VW3poWA@dz#iA)&<rB8vn>o~Y|S);7GW\nzV|+_e`Ie;0T?OrX8YZiZHCF1XE!R_9uBNn9O6ro5(lQ147ctTQ!GHl3DE?$&WMGhD\nz&;bd9>||iIU!ayIII}ZDEm63-?}`D3QE8g^;`e8!hE6+n_+o~G)HE-)gh(9`Kkf%@\nd8(7v&JK{arFoJ8^;WC#KQvzQ|Td^=$0|4|&KIZ@c\n\nliteral 64\nzcmZ?wbhEHbWM|-D_{hM}z`*dI0SXj<vM@3*Ffr(W_#k-(Cc!EF3&kHON*Qck_+rIG\nKhIgS14AuZ~{td7I\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/pending_add.gif b/org.spearce.egit.ui/icons/ovr/pending_add.gif\ndeleted file mode 100644\nindex f2306024b2872e50db4143a790bca93189ef8d5f..0000000000000000000000000000000000000000\nGIT binary patch\nliteral 0\nHcmV?d00001\n\nliteral 64\nzcmZ?wbhEHbWM|-Dn8?6je1?HxCd2>#{}~t<6o0a?iZC!S=m6OaAbAER!723%B?Oj5\nR<v1{ie-xgi8_2+54FJ_W4pRUC\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/pending_remove.gif b/org.spearce.egit.ui/icons/ovr/pending_remove.gif\ndeleted file mode 100644\nindex 4ecce038e66f904af8345f88c2c007706a4bc3d0..0000000000000000000000000000000000000000\nGIT binary patch\nliteral 0\nHcmV?d00001\n\nliteral 111\nzcmZ?wbhEHbWM|-D*v!E2;Qr&bwzj&uy5i#Ew6wIy$jG3eAU{99|Ns9pU;~OjSy)AY\nsT6I8@ATt<PWC|uQx@~OiE8rFvPw;68lw@ATcdKBBRM#03Aw~vk0Ht*t9{>OV\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/staged.gif b/org.spearce.egit.ui/icons/ovr/staged.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..dc0b8c01673b32fde0f9b11c8302ad1ca0a1e902\nGIT binary patch\nliteral 114\nzcmZ?wbhEHb<Y3@n*v!E2>(@D15q3p!E=^fpC2=k-SzZ$rK|38WV-+D!6DfNgu|P{%\nzPh+VN3xogv|1&T!DE?$&WMJT9&;hc6CU7t?u*hFHX*qrG>5bPI{QDmDwl|kFq+1-m\nRA=-DKlJ_A?g`5k6H30u&A6)<d\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/staged_added.gif b/org.spearce.egit.ui/icons/ovr/staged_added.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..c65d16a191f259e0feb55c9244e7545c9063f2ee\nGIT binary patch\nliteral 169\nzcmZ?wbhEHb<Y3@nI3mv=<D+lc6C~rUFXwNhSM8ymWTTpFZ&2l?k?oi~MbDtt!(w8P\nzX13k6n|3NGHU_nBa)HL3YfROWEiL<k^{d^EU9;|4ZK_}8(Y4mhqBp>NLa2<7LGmO+\nzwPfqgH75W6|5yCU!pOiN&!EEq1Ry&ZSk)e=_dO9$3EpXP>xYb&Yrys66%*4FPo$Ud\nU`8i&abQEg7#HJw7%D`X^09=zc+W-In\n\nliteral 0\nHcmV?d00001\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/staged_removed.gif b/org.spearce.egit.ui/icons/ovr/staged_removed.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..8aaadfb9b9300594a3448986650b82841398287c\nGIT binary patch\nliteral 176\nzcmZ?wbhEHb<Y3@nI3mX2@9!TQ8ygf96c`wooSa-&SLf&FS5i_E8XDTx)|Q{29~>Oq\nz-rk;)l2TY$7#SHE5D<`;mlqcoS6p2D;Qr&HqN4x*|1;nQia%Kx85qPEbU;c#b~3Q)\nzE>P`DcJ&Dn4ViXC=E@Y0u&)L|A2*0x3h8BCs@CD4#4P3#=;_K}%6y8ku!Vua8UVS)\nBGb{iA\n\nliteral 0\nHcmV?d00001\n\ndiff --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\nindex 7465444..a3196f4 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n@@ -44,6 +44,9 @@ public void initializeDefaultPreferences() {\n \t\t\t\tUIText.DecoratorPreferencesPage_projectFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, true);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_STAGED_ICON, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON, true);\n \n \t\tw = new int[] { 500, 500 };\n \t\tUIPreferences.setDefault(prefs,\ndiff --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\nindex 4c0d189..952816c 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n@@ -18,11 +18,15 @@\n  * Icons for the the Eclipse plugin. Mostly decorations.\n  */\n public class UIIcons {\n+\n+\t/** Decoration for resource in the index but not yet committed. */\n+\tpublic static final ImageDescriptor OVR_STAGED;\n+\n \t/** Decoration for resource added to index but not yet committed. */\n-\tpublic static final ImageDescriptor OVR_PENDING_ADD;\n+\tpublic static final ImageDescriptor OVR_STAGED_ADD;\n \n \t/** Decoration for resource removed from the index but not commit. */\n-\tpublic static final ImageDescriptor OVR_PENDING_REMOVE;\n+\tpublic static final ImageDescriptor OVR_STAGED_REMOVE;\n \n \t/** Decoration for resource not being tracked by Git */\n \tpublic static final ImageDescriptor OVR_UNTRACKED;\n@@ -84,11 +88,12 @@\n \n \tstatic {\n \t\tbase = init();\n-\t\tOVR_PENDING_ADD = map(\"ovr/pending_add.gif\");\n-\t\tOVR_PENDING_REMOVE = map(\"ovr/pending_remove.gif\");\n+\t\tOVR_STAGED = map(\"ovr/staged.gif\");\n+\t\tOVR_STAGED_ADD = map(\"ovr/staged_added.gif\");\n+\t\tOVR_STAGED_REMOVE = map(\"ovr/staged_removed.gif\");\n \t\tOVR_UNTRACKED = map(\"ovr/untracked.gif\");\n \t\tOVR_CONFLICT = map(\"ovr/conflict.gif\");\n-\t\tOVR_ASSUMEVALID = map(\"ovr/assumevalid.gif\");\n+\t\tOVR_ASSUMEVALID = map(\"ovr/assume_valid.gif\");\n \t\tELCL16_FIND = map(\"elcl16/find.gif\");\n \t\tELCL16_NEXT = map(\"elcl16/next.gif\");\n \t\tELCL16_PREVIOUS = map(\"elcl16/previous.gif\");\ndiff --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\nindex 7916cea..e812716 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n@@ -64,6 +64,12 @@\n \tpublic final static String DECORATOR_SHOW_TRACKED_ICON = \"decorator_show_tracked_icon\";\n \t/** */\n \tpublic final static String DECORATOR_SHOW_UNTRACKED_ICON = \"decorator_show_untracked_icon\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_STAGED_ICON = \"decorator_show_staged_icon\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_CONFLICTS_ICON = \"decorator_show_conflicts_icon\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_ASSUME_VALID_ICON = \"decorator_show_assume_valid_icon\";\n \n \t/**\n \t * Get the preference values associated with a fixed integer array.\ndiff --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\nindex 60e4eaa..7e26337 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -968,12 +968,18 @@\n \tpublic static String DecoratorPreferencesPage_generalTabFolder;\n \n \t/** */\n-\tpublic static String DecoratorPreferencesPage_nameResourceVariable;\n+\tpublic static String DecoratorPreferencesPage_bindingResourceName;\n \n \t/** */\n \tpublic static String DecoratorPreferencesPage_bindingBranchName;\n \n \t/** */\n+\tpublic static String DecoratorPreferencesPage_bindingDirtyFlag;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_bindingStagedFlag;\n+\n+\t/** */\n \tpublic static String DecoratorPreferencesPage_selectFormats;\n \n \t/** */\n@@ -994,6 +1000,15 @@\n \t/** */\n \tpublic static String DecoratorPreferencesPage_iconsShowUntracked;\n \n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowStaged;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowConflicts;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowAssumeValid;\n+\n \tstatic {\n \t\tinitializeMessages(UIText.class.getPackage().getName() + \".uitext\",\n \t\t\t\tUIText.class);\ndiff --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\nindex b20070a..c23ce24 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -60,9 +60,12 @@\n import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged;\n import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n import org.spearce.jgit.dircache.DirCacheIterator;\n import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n import org.spearce.jgit.lib.IndexChangedEvent;\n import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.RefsChangedEvent;\n@@ -200,6 +203,20 @@ public void decorate(Object element, IDecoration decoration) {\n \n \t\tprivate boolean ignored = false;\n \n+\t\tprivate boolean dirty = false;\n+\n+\t\tprivate boolean conflicts = false;\n+\n+\t\tprivate boolean assumeValid = false;\n+\n+\t\tprivate Staged staged = Staged.NOT_STAGED;\n+\n+\t\tstatic final int T_HEAD = 0;\n+\n+\t\tstatic final int T_INDEX = 1;\n+\n+\t\tstatic final int T_WORKSPACE = 2;\n+\n \t\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n \t\t\t\tthrows IOException {\n \t\t\tresource = resourceToWrap;\n@@ -207,59 +224,162 @@ public DecoratableResourceAdapter(IResource resourceToWrap)\n \t\t\trepository = mapping.getRepository();\n \t\t\theadId = repository.resolve(Constants.HEAD);\n \n-\t\t\tinitializeValues();\n+\t\t\tswitch (resource.getType()) {\n+\t\t\tcase IResource.FILE:\n+\t\t\t\textractFileProperties();\n+\t\t\t\tbreak;\n+\t\t\tcase IResource.FOLDER:\n+\t\t\t\textractContainerProperties();\n+\t\t\t\tbreak;\n+\t\t\tcase IResource.PROJECT:\n+\t\t\t\textractProjectProperties();\n+\t\t\t\tbreak;\n+\t\t\t}\n \t\t}\n \n-\t\t/**\n-\t\t * Initialize the various values that are used for making decoration\n-\t\t * decisions later on.\n-\t\t * \n-\t\t * We might as well pre-load these now, instead of using lazy\n-\t\t * initialization, because they are all read by the decorator when\n-\t\t * building variable bindings and computing the preferred overlay.\n-\t\t * \n-\t\t * @throws IOException\n-\t\t */\n-\t\tprivate void initializeValues() throws IOException {\n-\n-\t\t\t// Resolve current branch\n-\t\t\tbranch = repository.getBranch();\n+\t\tprivate void extractFileProperties() throws IOException {\n+\t\t\tTreeWalk treeWalk = createHeadVsIndexTreeWalk();\n+\t\t\tif (treeWalk == null)\n+\t\t\t\treturn;\n \n-\t\t\t// Resolve tracked state\n-\t\t\tif (getType() == IResource.PROJECT) {\n+\t\t\tif (treeWalk.next())\n \t\t\t\ttracked = true;\n+\t\t\telse\n+\t\t\t\treturn;\n+\n+\t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n+\t\t\tif (Team.isIgnoredHint(resource)) {\n+\t\t\t\tignored = true;\n+\t\t\t\treturn;\n+\t\t\t}\n+\n+\t\t\tfinal DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX,\n+\t\t\t\t\tDirCacheIterator.class);\n+\t\t\tfinal DirCacheEntry indexEntry = indexIterator != null ? indexIterator\n+\t\t\t\t\t.getDirCacheEntry()\n+\t\t\t\t\t: null;\n+\n+\t\t\tif (indexEntry == null) {\n+\t\t\t\tstaged = Staged.REMOVED;\n \t\t\t} else {\n-\t\t\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n-\n-\t\t\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n-\t\t\t\t\t\t.getRepoRelativePath(resource));\n-\t\t\t\tif (!(repositoryPaths.isEmpty() || repositoryPaths.contains(\"\"))) {\n-\t\t\t\t\ttreeWalk.setFilter(PathFilterGroup\n-\t\t\t\t\t\t\t.createFromStrings(repositoryPaths));\n-\t\t\t\t\ttreeWalk.setRecursive(treeWalk.getFilter()\n-\t\t\t\t\t\t\t.shouldBeRecursive());\n-\t\t\t\t\ttreeWalk.reset();\n-\n-\t\t\t\t\tif (headId != null)\n-\t\t\t\t\t\ttreeWalk.addTree(new RevWalk(repository)\n-\t\t\t\t\t\t\t\t.parseTree(headId));\n-\t\t\t\t\telse\n-\t\t\t\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n+\t\t\t\tif (indexEntry.isAssumeValid()) {\n+\t\t\t\t\tdirty = false;\n+\t\t\t\t\tassumeValid = true;\n+\t\t\t\t} else if (indexEntry.getStage() > 0) {\n+\t\t\t\t\tconflicts = true;\n+\t\t\t\t} else if (treeWalk.getRawMode(T_HEAD) == FileMode.MISSING\n+\t\t\t\t\t\t.getBits()) {\n+\t\t\t\t\tstaged = Staged.ADDED;\n+\t\t\t\t} else {\n+\t\t\t\t\tlong indexEntryLastModified = indexEntry.getLastModified();\n+\t\t\t\t\tlong resourceLastModified = resource.getLocalTimeStamp();\n+\n+\t\t\t\t\t// C-Git under Windows stores timestamps with 1-seconds\n+\t\t\t\t\t// resolution, so we need to check to see if this is the\n+\t\t\t\t\t// case here, and possibly fix the timestamp of the resource\n+\t\t\t\t\t// to match the resolution of the index.\n+\t\t\t\t\tif (indexEntryLastModified % 1000 == 0) {\n+\t\t\t\t\t\tresourceLastModified -= resourceLastModified % 1000;\n+\t\t\t\t\t}\n+\n+\t\t\t\t\tif (resourceLastModified != indexEntryLastModified) {\n+\t\t\t\t\t\t// TODO: Consider doing a content check here, to rule\n+\t\t\t\t\t\t// out false positives, as we might get mismatch between\n+\t\t\t\t\t\t// timestamps, even if the content is the same\n+\t\t\t\t\t\tdirty = true;\n+\t\t\t\t\t}\n \n-\t\t\t\t\ttreeWalk.addTree(new DirCacheIterator(DirCache\n-\t\t\t\t\t\t\t.read(repository)));\n-\t\t\t\t\tif (treeWalk.next()) {\n-\t\t\t\t\t\ttracked = true;\n+\t\t\t\t\tif (treeWalk.getRawMode(T_HEAD) != treeWalk\n+\t\t\t\t\t\t\t.getRawMode(T_INDEX)\n+\t\t\t\t\t\t\t|| !treeWalk.idEqual(T_HEAD, T_INDEX)) {\n+\t\t\t\t\t\tstaged = Staged.MODIFIED;\n \t\t\t\t\t}\n \t\t\t\t}\n \t\t\t}\n \n-\t\t\t// Resolve ignored state (currently only reads the global Eclipse\n-\t\t\t// ignores)\n+\t\t}\n+\n+\t\tprivate void extractContainerProperties() throws IOException {\n+\t\t\tTreeWalk treeWalk = createHeadVsIndexTreeWalk();\n+\t\t\tif (treeWalk == null)\n+\t\t\t\treturn;\n+\n+\t\t\tif (treeWalk.next())\n+\t\t\t\ttracked = true;\n+\t\t\telse\n+\t\t\t\treturn;\n+\n \t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n \t\t\tif (Team.isIgnoredHint(resource)) {\n \t\t\t\tignored = true;\n+\t\t\t\treturn;\n \t\t\t}\n+\n+\t\t\t// TODO: Compute dirty state for folder, using ContainerTreeIterator\n+\t\t\t// and ContainerDiffFilter\n+\n+\t\t}\n+\n+\t\tprivate void extractProjectProperties() throws IOException {\n+\t\t\tbranch = repository.getBranch();\n+\t\t\ttracked = true;\n+\n+\t\t\t// TODO: Compute dirty state for folder, using ContainerTreeIterator\n+\t\t\t// and ContainerDiffFilter\n+\n+\t\t}\n+\n+\t\t/**\n+\t\t * Adds a filter to the specified tree walk limiting the results to only\n+\t\t * those matching the resource specified by\n+\t\t * <code>resourceToFilterBy</code>\n+\t\t * <p>\n+\t\t * If the resource does not exists in the current repository, or it has\n+\t\t * an empty path (it is the project itself), the filter is not added,\n+\t\t * and the method returns <code>null</code>.\n+\t\t * \n+\t\t * @param treeWalk\n+\t\t *            the tree walk to add the filter to\n+\t\t * @param resourceToFilterBy\n+\t\t *            the resource to filter by\n+\t\t * \n+\t\t * @return <code>true</code> if the filter could be added,\n+\t\t *         <code>false</code> otherwise\n+\t\t */\n+\t\tprivate boolean addResourceFilter(final TreeWalk treeWalk,\n+\t\t\t\tfinal IResource resourceToFilterBy) {\n+\t\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n+\t\t\t\t\t.getRepoRelativePath(resourceToFilterBy));\n+\t\t\tif (repositoryPaths.isEmpty() || repositoryPaths.contains(\"\"))\n+\t\t\t\treturn false;\n+\n+\t\t\ttreeWalk.setFilter(PathFilterGroup\n+\t\t\t\t\t.createFromStrings(repositoryPaths));\n+\t\t\treturn true;\n+\t\t}\n+\n+\t\t/**\n+\t\t * Helper method to create a new tree walk between HEAD and the index.\n+\t\t * \n+\t\t * @return the created tree walk, or null if it could not be created\n+\t\t * @throws IOException\n+\t\t *             if there were errors when creating the tree walk\n+\t\t */\n+\t\tprivate TreeWalk createHeadVsIndexTreeWalk() throws IOException {\n+\t\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n+\t\t\tif (!addResourceFilter(treeWalk, resource))\n+\t\t\t\treturn null;\n+\n+\t\t\ttreeWalk.setRecursive(treeWalk.getFilter().shouldBeRecursive());\n+\t\t\ttreeWalk.reset();\n+\n+\t\t\tif (headId != null)\n+\t\t\t\ttreeWalk.addTree(new RevWalk(repository).parseTree(headId));\n+\t\t\telse\n+\t\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n+\n+\t\t\ttreeWalk.addTree(new DirCacheIterator(DirCache.read(repository)));\n+\t\t\treturn treeWalk;\n \t\t}\n \n \t\tpublic String getName() {\n@@ -281,6 +401,22 @@ public boolean isTracked() {\n \t\tpublic boolean isIgnored() {\n \t\t\treturn ignored;\n \t\t}\n+\n+\t\tpublic boolean isDirty() {\n+\t\t\treturn dirty;\n+\t\t}\n+\n+\t\tpublic Staged staged() {\n+\t\t\treturn staged;\n+\t\t}\n+\n+\t\tpublic boolean hasConflicts() {\n+\t\t\treturn conflicts;\n+\t\t}\n+\n+\t\tpublic boolean isAssumeValid() {\n+\t\t\treturn assumeValid;\n+\t\t}\n \t}\n \n \t/**\n@@ -298,6 +434,12 @@ public boolean isIgnored() {\n \t\t/** */\n \t\tpublic static final String BINDING_BRANCH_NAME = \"branch\"; //$NON-NLS-1$\n \n+\t\t/** */\n+\t\tpublic static final String BINDING_DIRTY_FLAG = \"dirty\"; //$NON-NLS-1$\n+\n+\t\t/** */\n+\t\tpublic static final String BINDING_STAGED_FLAG = \"staged\"; //$NON-NLS-1$\n+\n \t\tprivate IPreferenceStore store;\n \n \t\t/**\n@@ -325,10 +467,26 @@ public ImageData getImageData() {\n \n \t\tprivate static ImageDescriptor untrackedImage;\n \n+\t\tprivate static ImageDescriptor stagedImage;\n+\n+\t\tprivate static ImageDescriptor stagedAddedImage;\n+\n+\t\tprivate static ImageDescriptor stagedRemovedImage;\n+\n+\t\tprivate static ImageDescriptor conflictImage;\n+\n+\t\tprivate static ImageDescriptor assumeValidImage;\n+\n \t\tstatic {\n \t\t\ttrackedImage = new CachedImageDescriptor(TeamImages\n \t\t\t\t\t.getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR));\n \t\t\tuntrackedImage = new CachedImageDescriptor(UIIcons.OVR_UNTRACKED);\n+\t\t\tstagedImage = new CachedImageDescriptor(UIIcons.OVR_STAGED);\n+\t\t\tstagedAddedImage = new CachedImageDescriptor(UIIcons.OVR_STAGED_ADD);\n+\t\t\tstagedRemovedImage = new CachedImageDescriptor(\n+\t\t\t\t\tUIIcons.OVR_STAGED_REMOVE);\n+\t\t\tconflictImage = new CachedImageDescriptor(UIIcons.OVR_CONFLICT);\n+\t\t\tassumeValidImage = new CachedImageDescriptor(UIIcons.OVR_ASSUMEVALID);\n \t\t}\n \n \t\t/**\n@@ -354,6 +512,9 @@ public DecorationHelper(IPreferenceStore preferencesStore) {\n \t\t */\n \t\tpublic void decorate(IDecoration decoration,\n \t\t\t\tIDecoratableResource resource) {\n+\t\t\tif (resource.isIgnored())\n+\t\t\t\treturn;\n+\n \t\t\tdecorateText(decoration, resource);\n \t\t\tdecorateIcons(decoration, resource);\n \t\t}\n@@ -379,22 +540,49 @@ private void decorateText(IDecoration decoration,\n \t\t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\t\tbindings.put(BINDING_RESOURCE_NAME, resource.getName());\n \t\t\tbindings.put(BINDING_BRANCH_NAME, resource.getBranch());\n+\t\t\tbindings.put(BINDING_DIRTY_FLAG, resource.isDirty() ? \">\" : null);\n+\t\t\tbindings.put(BINDING_STAGED_FLAG,\n+\t\t\t\t\tresource.staged() != Staged.NOT_STAGED ? \"*\" : null);\n \n \t\t\tdecorate(decoration, format, bindings);\n \t\t}\n \n \t\tprivate void decorateIcons(IDecoration decoration,\n \t\t\t\tIDecoratableResource resource) {\n-\t\t\tif (resource.isIgnored())\n-\t\t\t\treturn;\n+\t\t\tImageDescriptor overlay = null;\n \n \t\t\tif (resource.isTracked()) {\n \t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON))\n-\t\t\t\t\tdecoration.addOverlay(trackedImage);\n-\t\t\t} else if (store\n-\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n-\t\t\t\tdecoration.addOverlay(untrackedImage);\n+\t\t\t\t\toverlay = trackedImage;\n+\n+\t\t\t\tif (store\n+\t\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON)\n+\t\t\t\t\t\t&& resource.isAssumeValid())\n+\t\t\t\t\toverlay = assumeValidImage;\n+\n+\t\t\t\t// Staged overrides tracked\n+\t\t\t\tStaged staged = resource.staged();\n+\t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON)\n+\t\t\t\t\t\t&& staged != Staged.NOT_STAGED) {\n+\t\t\t\t\tif (staged == Staged.ADDED)\n+\t\t\t\t\t\toverlay = stagedAddedImage;\n+\t\t\t\t\telse if (staged == Staged.REMOVED)\n+\t\t\t\t\t\toverlay = stagedRemovedImage;\n+\t\t\t\t\telse\n+\t\t\t\t\t\toverlay = stagedImage;\n+\t\t\t\t}\n+\n+\t\t\t\t// Conflicts override everything\n+\t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)\n+\t\t\t\t\t\t&& resource.hasConflicts())\n+\t\t\t\t\toverlay = conflictImage;\n+\n+\t\t\t} else if (store.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n+\t\t\t\toverlay = untrackedImage;\n \t\t\t}\n+\n+\t\t\t// Overlays can only be added once, so do it at the end\n+\t\t\tdecoration.addOverlay(overlay);\n \t\t}\n \n \t\t/**\n@@ -411,7 +599,7 @@ private void decorateIcons(IDecoration decoration,\n \t\t *            values\n \t\t */\n \t\tpublic static void decorate(IDecoration decoration, String format,\n-\t\t\t\tMap bindings) {\n+\t\t\t\tMap<String, String> bindings) {\n \t\t\tStringBuffer prefix = new StringBuffer();\n \t\t\tStringBuffer suffix = new StringBuffer();\n \t\t\tStringBuffer output = prefix;\n@@ -426,6 +614,15 @@ public static void decorate(IDecoration decoration, String format,\n \t\t\t\t\t\tString key = format.substring(end + 1, start);\n \t\t\t\t\t\tString s;\n \n+\t\t\t\t\t\t// Allow users to override the binding\n+\t\t\t\t\t\tif (key.indexOf(':') > -1) {\n+\t\t\t\t\t\t\tString[] keyAndBinding = key.split(\":\", 2);\n+\t\t\t\t\t\t\tkey = keyAndBinding[0];\n+\t\t\t\t\t\t\tif (keyAndBinding.length > 1\n+\t\t\t\t\t\t\t\t\t&& bindings.get(key) != null)\n+\t\t\t\t\t\t\t\tbindings.put(key, keyAndBinding[1]);\n+\t\t\t\t\t\t}\n+\n \t\t\t\t\t\t// We use the BINDING_RESOURCE_NAME key to determine if\n \t\t\t\t\t\t// we are doing the prefix or suffix. The name isn't\n \t\t\t\t\t\t// actually part of either.\n@@ -433,7 +630,7 @@ public static void decorate(IDecoration decoration, String format,\n \t\t\t\t\t\t\toutput = suffix;\n \t\t\t\t\t\t\ts = null;\n \t\t\t\t\t\t} else {\n-\t\t\t\t\t\t\ts = (String) bindings.get(key);\n+\t\t\t\t\t\t\ts = bindings.get(key);\n \t\t\t\t\t\t}\n \n \t\t\t\t\t\tif (s != null) {\n@@ -522,6 +719,14 @@ public void resourceChanged(IResourceChangeEvent event) {\n \t\t\t\tpublic boolean visit(IResourceDelta delta) throws CoreException {\n \t\t\t\t\tfinal IResource resource = delta.getResource();\n \n+\t\t\t\t\t// If the resource is not part of a project under Git\n+\t\t\t\t\t// revision control\n+\t\t\t\t\tfinal RepositoryMapping mapping = RepositoryMapping\n+\t\t\t\t\t\t\t.getMapping(resource);\n+\t\t\t\t\tif (mapping == null) {\n+\t\t\t\t\t\t// Ignore the change\n+\t\t\t\t\t\treturn true;\n+\t\t\t\t\t}\n \t\t\t\t\tif (resource.getType() == IResource.ROOT) {\n \t\t\t\t\t\t// Continue with the delta\n \t\t\t\t\t\treturn true;\ndiff --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\nindex f144214..b864a10 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n@@ -16,6 +16,22 @@\n public interface IDecoratableResource {\n \n \t/**\n+\t * Set of possible staging states for a resource\n+\t */\n+\tpublic enum Staged {\n+\t\t/** Represents a resource that is not staged */\n+\t\tNOT_STAGED,\n+\t\t/** Represents a resource that has been modified */\n+\t\tMODIFIED,\n+\t\t/** Represents a resource that is added to Git */\n+\t\tADDED,\n+\t\t/** Represents a resource that is removed from Git */\n+\t\tREMOVED,\n+\t\t/** Represents a resource that has been renamed */\n+\t\tRENAMED\n+\t}\n+\n+\t/**\n \t * Gets the type of the resource as defined by {@link IResource}\n \t * \n \t * @return the type of the resource\n@@ -51,4 +67,34 @@\n \t * @return whether or not the resource is ignored\n \t */\n \tboolean isIgnored();\n+\n+\t/**\n+\t * Returns whether or not the resource has changes that are not staged\n+\t * \n+\t * @return whether or not the resource is dirty\n+\t */\n+\tboolean isDirty();\n+\n+\t/**\n+\t * Returns the staged state of the resource\n+\t * \n+\t * The set of allowed values are defined by the <code>Staged</code> enum\n+\t * \n+\t * @return the staged state of the resource\n+\t */\n+\tStaged staged();\n+\n+\t/**\n+\t * Returns whether or not the resource has merge conflicts\n+\t * \n+\t * @return whether or not the resource has merge conflicts\n+\t */\n+\tboolean hasConflicts();\n+\n+\t/**\n+\t * Returns whether or not the resource is assumed valid\n+\t * \n+\t * @return whether or not the resource is assumed valid\n+\t */\n+\tboolean isAssumeValid();\n }\ndiff --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\nindex 7b637e3..f72ceb7 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -55,6 +55,7 @@\n import org.eclipse.swt.widgets.Composite;\n import org.eclipse.swt.widgets.Control;\n import org.eclipse.swt.widgets.Event;\n+import org.eclipse.swt.widgets.Label;\n import org.eclipse.swt.widgets.Listener;\n import org.eclipse.swt.widgets.TabFolder;\n import org.eclipse.swt.widgets.TabItem;\n@@ -71,8 +72,9 @@\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n import org.spearce.egit.ui.internal.SWTUtils;\n-import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;\n import org.spearce.egit.ui.internal.decorators.IDecoratableResource;\n+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;\n+import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged;\n \n /**\n  * Preference page for customizing Git label decorations\n@@ -94,22 +96,43 @@\n \n \tprivate Preview preview;\n \n+\tprivate Button showStaged;\n+\n+\tprivate Button showConflicts;\n+\n+\tprivate Button showAssumeValid;\n+\n \tprivate static final Collection PREVIEW_FILESYSTEM_ROOT;\n \n \tprivate static IPropertyChangeListener themeListener;\n \n \tstatic {\n \t\tfinal PreviewResource project = new PreviewResource(\n-\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false); //$NON-NLS-1$1\n+\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false, false, Staged.NOT_STAGED, false, false); //$NON-NLS-1$1\n \t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n+\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"folder\", IResource.FOLDER, null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"tracked.txt\", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n-\t\t\t\t\"folder\", IResource.FOLDER, null, true, false)); //$NON-NLS-1$\n+\t\t\t\t\t\t\"untracked.txt\", IResource.FILE, null, false, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n-\t\t\t\t\"file.txt\", IResource.FILE, null, true, false)); //$NON-NLS-1$\n+\t\t\t\t\t\t\"ignored.txt\", IResource.FILE, null, false, true, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n-\t\t\t\t\"untracked.txt\", IResource.FILE, null, false, false)); //$NON-NLS-1$\n+\t\t\t\t\t\t\"dirty.txt\", IResource.FILE, null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n-\t\t\t\t\"ignored.txt\", IResource.FILE, null, false, true)); //$NON-NLS-1$\n+\t\t\t\t\t\t\"staged.txt\", IResource.FILE, null, true, false, false, Staged.MODIFIED, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"partially-staged.txt\", IResource.FILE, null, true, false, true, Staged.MODIFIED, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"added.txt\", IResource.FILE, null, true, false, false, Staged.ADDED, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"removed.txt\", IResource.FILE, null, true, false, false, Staged.REMOVED, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"conflict.txt\", IResource.FILE, null, true, false, true, Staged.NOT_STAGED, true, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\t\t\"assume-valid.txt\", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false, true)); //$NON-NLS-1$\n \t\tproject.children = children;\n \t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n \t}\n@@ -190,22 +213,29 @@ private Control createTextDecoratorPage(Composite parent) {\n \t\tComposite fileTextGroup = SWTUtils.createHVFillComposite(parent,\n \t\t\t\tSWTUtils.MARGINS_DEFAULT, 3);\n \n+\t\tint labelWidth = convertWidthInCharsToPixels(Math.max(\n+\t\t\t\tUIText.DecoratorPreferencesPage_fileFormatLabel.length(),\n+\t\t\t\tMath.max(UIText.DecoratorPreferencesPage_folderFormatLabel\n+\t\t\t\t\t\t.length(),\n+\t\t\t\t\t\tUIText.DecoratorPreferencesPage_projectFormatLabel\n+\t\t\t\t\t\t\t\t.length())));\n+\n \t\tTextPair format = createFormatEditorControl(fileTextGroup,\n \t\t\t\tUIText.DecoratorPreferencesPage_fileFormatLabel,\n \t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n-\t\t\t\tgetFileBindingDescriptions());\n+\t\t\t\tgetFileBindingDescriptions(), labelWidth);\n \t\tfileTextFormat = format.t1;\n \n \t\tformat = createFormatEditorControl(fileTextGroup,\n \t\t\t\tUIText.DecoratorPreferencesPage_folderFormatLabel,\n \t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n-\t\t\t\tgetFolderBindingDescriptions());\n+\t\t\t\tgetFolderBindingDescriptions(), labelWidth);\n \t\tfolderTextFormat = format.t1;\n \n \t\tformat = createFormatEditorControl(fileTextGroup,\n \t\t\t\tUIText.DecoratorPreferencesPage_projectFormatLabel,\n \t\t\t\tUIText.DecoratorPreferencesPage_addVariablesAction,\n-\t\t\t\tgetProjectBindingDescriptions());\n+\t\t\t\tgetProjectBindingDescriptions(), labelWidth);\n \t\tprojectTextFormat = format.t1;\n \n \t\treturn fileTextGroup;\n@@ -219,17 +249,29 @@ private Control createIconDecoratorPage(Composite parent) {\n \t\t\t\tUIText.DecoratorPreferencesPage_iconsShowTracked);\n \t\tshowUntracked = SWTUtils.createCheckBox(imageGroup,\n \t\t\t\tUIText.DecoratorPreferencesPage_iconsShowUntracked);\n+\t\tshowStaged = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowStaged);\n+\t\tshowConflicts = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowConflicts);\n+\t\tshowAssumeValid = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowAssumeValid);\n \n \t\treturn imageGroup;\n \t}\n \n \tprivate TextPair createFormatEditorControl(Composite composite,\n-\t\t\tString title, String buttonText, final Map supportedBindings) {\n+\t\t\tString title, String buttonText, final Map supportedBindings,\n+\t\t\tint labelWidth) {\n \n-\t\tSWTUtils.createLabel(composite, title);\n+\t\tLabel label = SWTUtils.createLabel(composite, title);\n+\t\tGridData labelGridData = new GridData();\n+\t\tlabelGridData.widthHint = labelWidth;\n+\t\tlabel.setLayoutData(labelGridData);\n \n \t\tText format = new Text(composite, SWT.BORDER);\n-\t\tformat.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\n+\t\tGridData textGridData = new GridData(GridData.FILL_HORIZONTAL);\n+\t\ttextGridData.widthHint = 200;\n+\t\tformat.setLayoutData(textGridData);\n \t\tformat.addModifyListener(new ModifyListener() {\n \t\t\tpublic void modifyText(ModifyEvent e) {\n \t\t\t\tupdatePreview();\n@@ -273,16 +315,25 @@ private void initializeValues() {\n \t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));\n \t\tshowUntracked.setSelection(store\n \t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n+\t\tshowStaged.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON));\n+\t\tshowConflicts.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON));\n+\t\tshowAssumeValid.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON));\n \n \t\tSelectionListener selectionListener = new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(SelectionEvent e) {\n-\t\t\t\tfPreview.refresh();\n+\t\t\t\tpreview.refresh();\n \t\t\t}\n \t\t};\n \n \t\tcomputeDeepDirtyState.addSelectionListener(selectionListener);\n \t\tshowTracked.addSelectionListener(selectionListener);\n \t\tshowUntracked.addSelectionListener(selectionListener);\n+\t\tshowStaged.addSelectionListener(selectionListener);\n+\t\tshowConflicts.addSelectionListener(selectionListener);\n+\t\tshowAssumeValid.addSelectionListener(selectionListener);\n \n \t\tsetValid(true);\n \t}\n@@ -334,6 +385,12 @@ private boolean performOk(IPreferenceStore store) {\n \t\t\t\t.getSelection());\n \t\tstore.setValue(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON,\n \t\t\t\tshowUntracked.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_STAGED_ICON, showStaged\n+\t\t\t\t.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON,\n+\t\t\t\tshowConflicts.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON,\n+\t\t\t\tshowAssumeValid.getSelection());\n \n \t\treturn true;\n \t}\n@@ -363,6 +420,14 @@ protected void performDefaults() {\n \t\tshowUntracked\n \t\t\t\t.setSelection(store\n \t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n+\t\tshowStaged.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_STAGED_ICON));\n+\t\tshowConflicts\n+\t\t\t\t.setSelection(store\n+\t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON));\n+\t\tshowAssumeValid\n+\t\t\t\t.setSelection(store\n+\t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_ASSUME_VALID_ICON));\n \t}\n \n \t/**\n@@ -473,7 +538,11 @@ TextPair(Text t1, Text t2) {\n \tprivate Map getFileBindingDescriptions() {\n \t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n-\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingResourceName);\n+\t\tbindings.put(DecorationHelper.BINDING_DIRTY_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingDirtyFlag);\n+\t\tbindings.put(DecorationHelper.BINDING_STAGED_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingStagedFlag);\n \t\treturn bindings;\n \t}\n \n@@ -486,7 +555,11 @@ private Map getFileBindingDescriptions() {\n \tprivate Map getFolderBindingDescriptions() {\n \t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n-\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingResourceName);\n+\t\tbindings.put(DecorationHelper.BINDING_DIRTY_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingDirtyFlag);\n+\t\tbindings.put(DecorationHelper.BINDING_STAGED_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingStagedFlag);\n \t\treturn bindings;\n \t}\n \n@@ -499,7 +572,11 @@ private Map getFolderBindingDescriptions() {\n \tprivate Map getProjectBindingDescriptions() {\n \t\tMap<String, String> bindings = new HashMap<String, String>();\n \t\tbindings.put(DecorationHelper.BINDING_RESOURCE_NAME,\n-\t\t\t\tUIText.DecoratorPreferencesPage_nameResourceVariable);\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingResourceName);\n+\t\tbindings.put(DecorationHelper.BINDING_DIRTY_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingDirtyFlag);\n+\t\tbindings.put(DecorationHelper.BINDING_STAGED_FLAG,\n+\t\t\t\tUIText.DecoratorPreferencesPage_bindingStagedFlag);\n \t\tbindings.put(DecorationHelper.BINDING_BRANCH_NAME,\n \t\t\t\tUIText.DecoratorPreferencesPage_bindingBranchName);\n \t\treturn bindings;\n@@ -658,14 +735,28 @@ private PreviewDecoration getDecoration(Object element) {\n \n \t\tprivate boolean ignored;\n \n+\t\tprivate boolean dirty;\n+\n+\t\tprivate boolean conflicts;\n+\n+\t\tprivate Staged staged;\n+\n+\t\tprivate boolean assumeValid;\n+\n \t\tpublic PreviewResource(String name, int type, String branch,\n-\t\t\t\tboolean tracked, boolean ignored) {\n+\t\t\t\tboolean tracked, boolean ignored, boolean dirty, Staged staged,\n+\t\t\t\tboolean conflicts, boolean assumeValid) {\n+\n \t\t\tthis.name = name;\n \t\t\tthis.branch = branch;\n \t\t\tthis.type = type;\n \t\t\tthis.children = Collections.EMPTY_LIST;\n \t\t\tthis.tracked = tracked;\n \t\t\tthis.ignored = ignored;\n+\t\t\tthis.dirty = dirty;\n+\t\t\tthis.staged = staged;\n+\t\t\tthis.conflicts = conflicts;\n+\t\t\tthis.assumeValid = assumeValid; \n \t\t}\n \n \t\tpublic String getName() {\n@@ -687,6 +778,22 @@ public boolean isTracked() {\n \t\tpublic boolean isIgnored() {\n \t\t\treturn ignored;\n \t\t}\n+\n+\t\tpublic boolean isDirty() {\n+\t\t\treturn dirty;\n+\t\t}\n+\n+\t\tpublic Staged staged() {\n+\t\t\treturn staged;\n+\t\t}\n+\n+\t\tpublic boolean hasConflicts() {\n+\t\t\treturn conflicts;\n+\t\t}\n+\n+\t\tpublic boolean isAssumeValid() {\n+\t\t\treturn assumeValid;\n+\t\t}\n \t}\n \n \tprivate class PreviewDecoration implements IDecoration {\n@@ -703,12 +810,19 @@ public boolean isIgnored() {\n \n \t\tprivate Color foregroundColor;\n \n+\t\t/**\n+\t\t * Adds an icon overlay to the decoration\n+\t\t * <p>\n+\t\t * Copies the behavior of <code>DecorationBuilder</code> of only\n+\t\t * allowing the overlay to be set once.\n+\t\t */\n \t\tpublic void addOverlay(ImageDescriptor overlayImage) {\n-\t\t\toverlay = overlayImage;\n+\t\t\tif (overlay == null)\n+\t\t\t\toverlay = overlayImage;\n \t\t}\n \n \t\tpublic void addOverlay(ImageDescriptor overlayImage, int quadrant) {\n-\t\t\toverlay = overlayImage;\n+\t\t\taddOverlay(overlayImage);\n \t\t}\n \n \t\tpublic void addPrefix(String prefix) {\ndiff --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\nindex 9940177..e9a2321 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -358,17 +358,24 @@ DecoratorPreferencesPage_preview=Preview:\n DecoratorPreferencesPage_fileFormatLabel=&Files:\n DecoratorPreferencesPage_folderFormatLabel=F&olders:\n DecoratorPreferencesPage_projectFormatLabel=&Projects:\n-DecoratorPreferencesPage_fileFormatDefault={name}\n-DecoratorPreferencesPage_folderFormatDefault={name}\n-DecoratorPreferencesPage_projectFormatDefault={name} [{branch}]\n+DecoratorPreferencesPage_fileFormatDefault={dirty:>} {name}\n+DecoratorPreferencesPage_folderFormatDefault={dirty:>} {name}\n+DecoratorPreferencesPage_projectFormatDefault={dirty:>} {name} [{branch}]\n DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or disable Git decorations.\n DecoratorPreferencesPage_generalTabFolder=&General\n-DecoratorPreferencesPage_nameResourceVariable=name of the resource being decorated\n+DecoratorPreferencesPage_bindingResourceName=name of the resource being decorated\n DecoratorPreferencesPage_bindingBranchName=current branch of the project\n+DecoratorPreferencesPage_bindingDirtyFlag=flag indicating whether or not the resource is dirty\n+DecoratorPreferencesPage_bindingStagedFlag=flag indicating whether or not the resource is staged\n DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and project text labels:\n DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:\n DecoratorPreferencesPage_textLabel=T&ext Decorations\n DecoratorPreferencesPage_iconLabel=&Icon Decorations\n DecoratorPreferencesPage_iconsShowTracked=Tracked resources\n DecoratorPreferencesPage_iconsShowUntracked=Untracked resources\n+DecoratorPreferencesPage_iconsShowStaged=Staged resources\n+DecoratorPreferencesPage_iconsShowConflicts=Conflicting resources\n+DecoratorPreferencesPage_iconsShowAssumeValid=Assumed unchanged resources\n+\n+Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources.\n \n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104261","messageId":"1234377614-23798-11-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-10-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 10/12] Don't decorate every single resource on repository change","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:12Z","receivedAt":"2009-02-11T18:40:12Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Not all resources have corresponding labels that are visible,\nso by using LabelProviderChangedEvent() we ensure that only\nthe visible labels are refreshed.\n\nThe downside is that we lose project precition, so all\nprojects are included, but only visible labels in those\nprojects are re-decorated, so it is OK for now.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../decorators/GitLightweightDecorator.java        |   28 +++-----------------\n 1 files changed, 4 insertions(+), 24 deletions(-)\n\ndiff --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\nindex c23ce24..1e95369 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -14,11 +14,9 @@\n package org.spearce.egit.ui.internal.decorators;\n \n import java.io.IOException;\n-import java.util.ArrayList;\n import java.util.Collections;\n import java.util.HashMap;\n import java.util.HashSet;\n-import java.util.List;\n import java.util.Map;\n import java.util.Set;\n \n@@ -28,7 +26,6 @@\n import org.eclipse.core.resources.IResourceChangeListener;\n import org.eclipse.core.resources.IResourceDelta;\n import org.eclipse.core.resources.IResourceDeltaVisitor;\n-import org.eclipse.core.resources.IResourceVisitor;\n import org.eclipse.core.resources.ResourcesPlugin;\n import org.eclipse.core.resources.mapping.ResourceMapping;\n import org.eclipse.core.runtime.CoreException;\n@@ -697,7 +694,7 @@ public void propertyChange(PropertyChangeEvent event) {\n \t\tif (prop.equals(TeamUI.GLOBAL_IGNORES_CHANGED)\n \t\t\t\t|| prop.equals(TeamUI.GLOBAL_FILE_TYPES_CHANGED)\n \t\t\t\t|| prop.equals(Activator.DECORATORS_CHANGED)) {\n-\t\t\tpostLabelEvent(new LabelProviderChangedEvent(this, null /* all */));\n+\t\t\tpostLabelEvent(new LabelProviderChangedEvent(this));\n \t\t}\n \t}\n \n@@ -824,29 +821,12 @@ public void refsChanged(RefsChangedEvent e) {\n \t * Callback for RepositoryChangeListener events, as well as\n \t * RepositoryListener events via repositoryChanged()\n \t * \n-\t * We resolve the project and schedule a refresh of each resource in the\n-\t * project.\n-\t * \n \t * @see org.spearce.egit.core.project.RepositoryChangeListener#repositoryChanged(org.spearce.egit.core.project.RepositoryMapping)\n \t */\n \tpublic void repositoryChanged(RepositoryMapping mapping) {\n-\t\tfinal IProject project = mapping.getContainer().getProject();\n-\t\tif (project == null)\n-\t\t\treturn;\n-\n-\t\tfinal List<IResource> resources = new ArrayList<IResource>();\n-\t\ttry {\n-\t\t\tproject.accept(new IResourceVisitor() {\n-\t\t\t\tpublic boolean visit(IResource resource) {\n-\t\t\t\t\tresources.add(resource);\n-\t\t\t\t\treturn true;\n-\t\t\t\t}\n-\t\t\t});\n-\t\t\tpostLabelEvent(new LabelProviderChangedEvent(this, resources\n-\t\t\t\t\t.toArray()));\n-\t\t} catch (final CoreException e) {\n-\t\t\thandleException(project, e);\n-\t\t}\n+\t\t// Until we find a way to refresh visible labels within a project\n+\t\t// we have to use this blanket refresh that includes all projects.\n+\t\tpostLabelEvent(new LabelProviderChangedEvent(this));\n \t}\n \n \t// -------- Helper methods --------\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104260","messageId":"1234377614-23798-12-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-11-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 11/12] Expose the underlying resource entries in ContainerTreeIterator","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:13Z","receivedAt":"2009-02-11T18:40:13Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"This is needed if interacting with any of the Eclipse platform\nmethods that take IResources as arguments.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../spearce/egit/core/ContainerTreeIterator.java   |   23 +++++++++++++++++++-\n .../src/org/spearce/jgit/treewalk/TreeWalk.java    |    9 +++++++\n 2 files changed, 31 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java b/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\nindex 6d6b72e..2403252 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\n@@ -106,6 +106,15 @@ public AbstractTreeIterator createSubtreeIterator(final Repository db)\n \t\t\t\t\tConstants.TYPE_TREE);\n \t}\n \n+\t/**\n+\t * Get the ResourceEntry for the current entry.\n+\t * \n+\t * @return the current entry\n+\t */\n+\tpublic ResourceEntry getResourceEntry() {\n+\t\treturn (ResourceEntry) current();\n+\t}\n+\n \tprivate Entry[] entries() {\n \t\tfinal IResource[] all;\n \t\ttry {\n@@ -120,7 +129,10 @@ public AbstractTreeIterator createSubtreeIterator(final Repository db)\n \t\treturn r;\n \t}\n \n-\tstatic class ResourceEntry extends Entry {\n+\t/**\n+\t * Wrapper for a resource in the Eclipse workspace\n+\t */\n+\tstatic public class ResourceEntry extends Entry {\n \t\tfinal IResource rsrc;\n \n \t\tprivate final FileMode mode;\n@@ -195,6 +207,15 @@ public InputStream openInputStream() throws IOException {\n \t\t\tthrow new IOException(\"Not a regular file: \" + rsrc);\n \t\t}\n \n+\t\t/**\n+\t\t * Get the underlying resource of this entry.\n+\t\t * \n+\t\t * @return the underlying resource\n+\t\t */\n+\t\tpublic IResource getResource() {\n+\t\t\treturn rsrc;\n+\t\t}\n+\n \t\tprivate File asFile() {\n \t\t\treturn ((IFile) rsrc).getLocation().toFile();\n \t\t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\nindex 189fc86..ce247f2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n@@ -739,6 +739,15 @@ public int isPathPrefix(final byte[] p, final int pLen) {\n \t}\n \n \t/**\n+\t * Get the current subtree depth of this walker.\n+\t * \n+\t * @return the current subtree depth of this walker.\n+\t */\n+\tpublic int getDepth() {\n+\t\treturn depth;\n+\t}\n+\n+\t/**\n \t * Is the current entry a subtree?\n \t * <p>\n \t * This method is faster then testing the raw mode bits of all trees to see\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104256","messageId":"1234377614-23798-13-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-12-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH v2 12/12] Implement label decorations for folders and projects","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T18:40:14Z","receivedAt":"2009-02-11T18:40:14Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"The option \"Re-decorate ancestors...\" controls if parents of a\nre-decorated resource also should be updated, for example to\nto signal that the containing folder is now concidered dirty.\n\nThe option \"Maximum number of levels...\" controls how deep the\ncontainer decoration algorithm will recurse when trying to\ndetermine the state (dirty, staged, etc.) of a container.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../egit/ui/PluginPreferenceInitializer.java       |    4 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +-\n .../src/org/spearce/egit/ui/UIText.java            |   14 +-\n .../decorators/DecoratableResourceAdapter.java     |  391 ++++++++++++++++++++\n .../decorators/GitLightweightDecorator.java        |  300 ++-------------\n .../preferences/GitDecoratorPreferencePage.java    |   91 ++++-\n .../src/org/spearce/egit/ui/uitext.properties      |    7 +-\n 7 files changed, 528 insertions(+), 283 deletions(-)\n create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n\ndiff --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\nindex a3196f4..8d617e9 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n@@ -35,7 +35,9 @@ public void initializeDefaultPreferences() {\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);\n \n-\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_RECURSIVE_LIMIT,\n+\t\t\t\tInteger.MAX_VALUE);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_fileFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\ndiff --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\nindex e812716..bbc36be 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n@@ -53,7 +53,9 @@\n \tpublic final static String THEME_CommitMessageFont = \"org.spearce.egit.ui.CommitMessageFont\";\n \n \t/** */\n-\tpublic final static String DECORATOR_CALCULATE_DIRTY = \"decorator_calculate_dirty\";\n+\tpublic final static String DECORATOR_RECOMPUTE_ANCESTORS = \"decorator_recompute_ancestors\";\n+\t/** */\n+\tpublic final static String DECORATOR_RECURSIVE_LIMIT = \"decorator_recursive_limit\";\n \t/** */\n \tpublic final static String DECORATOR_FILETEXT_DECORATION = \"decorator_filetext_decoration\";\n \t/** */\ndiff --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\nindex 7e26337..bd64ca1 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -935,7 +935,19 @@\n \tpublic static String DecoratorPreferencesPage_addVariablesAction;\n \n \t/** */\n-\tpublic static String DecoratorPreferencesPage_computeDeep;\n+\tpublic static String DecoratorPreferencesPage_recomputeAncestorDecorations;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_computeRecursiveLimit;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_computeRecursiveLimitTooltip;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_invalidInput;\n \n \t/** */\n \tpublic static String DecoratorPreferencesPage_description;\ndiff --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\nnew file mode 100644\nindex 0000000..e2fe54b\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n@@ -0,0 +1,391 @@\n+/*******************************************************************************\n+ * Copyright (C) 2007, IBM Corporation and others\n+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>\n+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.ui.internal.decorators;\n+\n+import java.io.IOException;\n+import java.util.Collections;\n+import java.util.Set;\n+\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IWorkspace;\n+import org.eclipse.jface.preference.IPreferenceStore;\n+import org.eclipse.team.core.Team;\n+import org.spearce.egit.core.ContainerTreeIterator;\n+import org.spearce.egit.core.ContainerTreeIterator.ResourceEntry;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIPreferences;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n+import org.spearce.jgit.dircache.DirCacheIterator;\n+import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.errors.MissingObjectException;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.revwalk.RevWalk;\n+import org.spearce.jgit.treewalk.EmptyTreeIterator;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.AndTreeFilter;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n+import org.spearce.jgit.treewalk.filter.TreeFilter;\n+\n+class DecoratableResourceAdapter implements IDecoratableResource {\n+\n+\tprivate final IResource resource;\n+\n+\tprivate final RepositoryMapping mapping;\n+\n+\tprivate final Repository repository;\n+\n+\tprivate final ObjectId headId;\n+\n+\tprivate final IPreferenceStore store;\n+\n+\tprivate String branch = \"\";\n+\n+\tprivate boolean tracked = false;\n+\n+\tprivate boolean ignored = false;\n+\n+\tprivate boolean dirty = false;\n+\n+\tprivate boolean conflicts = false;\n+\n+\tprivate boolean assumeValid = false;\n+\n+\tprivate Staged staged = Staged.NOT_STAGED;\n+\n+\tstatic final int T_HEAD = 0;\n+\n+\tstatic final int T_INDEX = 1;\n+\n+\tstatic final int T_WORKSPACE = 2;\n+\n+\t@SuppressWarnings(\"fallthrough\")\n+\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n+\t\t\tthrows IOException {\n+\t\tresource = resourceToWrap;\n+\t\tmapping = RepositoryMapping.getMapping(resource);\n+\t\trepository = mapping.getRepository();\n+\t\theadId = repository.resolve(Constants.HEAD);\n+\n+\t\tstore = Activator.getDefault().getPreferenceStore();\n+\n+\t\t// TODO: Add option to shorten branch name to 6 chars if it's a SHA\n+\t\tbranch = repository.getBranch();\n+\n+\t\tTreeWalk treeWalk = createThreeWayTreeWalk();\n+\t\tif (treeWalk == null)\n+\t\t\treturn;\n+\n+\t\tswitch (resource.getType()) {\n+\t\tcase IResource.FILE:\n+\t\t\tif (!treeWalk.next())\n+\t\t\t\treturn;\n+\t\t\textractResourceProperties(treeWalk);\n+\t\t\tbreak;\n+\t\tcase IResource.PROJECT:\n+\t\t\ttracked = true;\n+\t\tcase IResource.FOLDER:\n+\t\t\textractContainerProperties(treeWalk);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tprivate void extractResourceProperties(TreeWalk treeWalk) {\n+\t\tfinal ContainerTreeIterator workspaceIterator = treeWalk.getTree(\n+\t\t\t\tT_WORKSPACE, ContainerTreeIterator.class);\n+\t\tfinal ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator\n+\t\t\t\t.getResourceEntry() : null;\n+\n+\t\tif (resourceEntry == null)\n+\t\t\treturn;\n+\n+\t\tif (isIgnored(resourceEntry.getResource())) {\n+\t\t\tignored = true;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal int mHead = treeWalk.getRawMode(T_HEAD);\n+\t\tfinal int mIndex = treeWalk.getRawMode(T_INDEX);\n+\n+\t\tif (mHead == FileMode.MISSING.getBits()\n+\t\t\t\t&& mIndex == FileMode.MISSING.getBits())\n+\t\t\treturn;\n+\n+\t\ttracked = true;\n+\n+\t\tif (mHead == FileMode.MISSING.getBits()) {\n+\t\t\tstaged = Staged.ADDED;\n+\t\t} else if (mIndex == FileMode.MISSING.getBits()) {\n+\t\t\tstaged = Staged.REMOVED;\n+\t\t} else if (mHead != mIndex\n+\t\t\t\t|| (mIndex != FileMode.TREE.getBits() && !treeWalk.idEqual(\n+\t\t\t\t\t\tT_HEAD, T_INDEX))) {\n+\t\t\tstaged = Staged.MODIFIED;\n+\t\t} else {\n+\t\t\tstaged = Staged.NOT_STAGED;\n+\t\t}\n+\n+\t\tfinal DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX,\n+\t\t\t\tDirCacheIterator.class);\n+\t\tfinal DirCacheEntry indexEntry = indexIterator != null ? indexIterator\n+\t\t\t\t.getDirCacheEntry() : null;\n+\n+\t\tif (indexEntry == null)\n+\t\t\treturn;\n+\n+\t\tif (indexEntry.getStage() > 0)\n+\t\t\tconflicts = true;\n+\n+\t\tif (indexEntry.isAssumeValid()) {\n+\t\t\tdirty = false;\n+\t\t\tassumeValid = true;\n+\t\t} else {\n+\t\t\tif (!timestampMatches(indexEntry, resourceEntry))\n+\t\t\t\tdirty = true;\n+\n+\t\t\t// TODO: Consider doing a content check here, to rule out false\n+\t\t\t// positives, as we might get mismatch between timestamps, even\n+\t\t\t// if the content is the same.\n+\t\t}\n+\t}\n+\n+\tprivate class RecursiveStateFilter extends TreeFilter {\n+\n+\t\tprivate int filesChecked = 0;\n+\n+\t\tprivate int targetDepth = -1;\n+\n+\t\tprivate final int recurseLimit;\n+\n+\t\tpublic RecursiveStateFilter() {\n+\t\t\trecurseLimit = store\n+\t\t\t\t\t.getInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT);\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic boolean include(TreeWalk treeWalk)\n+\t\t\t\tthrows MissingObjectException, IncorrectObjectTypeException,\n+\t\t\t\tIOException {\n+\n+\t\t\tif (treeWalk.getFileMode(T_HEAD) == FileMode.MISSING\n+\t\t\t\t\t&& treeWalk.getFileMode(T_INDEX) == FileMode.MISSING)\n+\t\t\t\treturn false;\n+\n+\t\t\tif (FileMode.TREE.equals(treeWalk.getRawMode(T_WORKSPACE)))\n+\t\t\t\treturn shouldRecurse(treeWalk);\n+\n+\t\t\t// Backup current state so far\n+\t\t\tStaged wasStaged = staged;\n+\t\t\tboolean wasDirty = dirty;\n+\t\t\tboolean hadConflicts = conflicts;\n+\n+\t\t\textractResourceProperties(treeWalk);\n+\t\t\tfilesChecked++;\n+\n+\t\t\t// Merge results with old state\n+\t\t\tignored = false;\n+\t\t\tassumeValid = false;\n+\t\t\tdirty = wasDirty || dirty;\n+\t\t\tconflicts = hadConflicts || conflicts;\n+\t\t\tif (staged != wasStaged && filesChecked > 1)\n+\t\t\t\tstaged = Staged.MODIFIED;\n+\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tprivate boolean shouldRecurse(TreeWalk treeWalk) {\n+\t\t\tfinal ContainerTreeIterator workspaceIterator = treeWalk.getTree(\n+\t\t\t\t\tT_WORKSPACE, ContainerTreeIterator.class);\n+\t\t\tfinal ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator\n+\t\t\t\t\t.getResourceEntry()\n+\t\t\t\t\t: null;\n+\t\t\tIResource visitingResource = resourceEntry.getResource();\n+\n+\t\t\tif (targetDepth == -1) {\n+\t\t\t\tif (visitingResource.equals(resource)\n+\t\t\t\t\t\t|| visitingResource.getParent().equals(resource))\n+\t\t\t\t\ttargetDepth = treeWalk.getDepth();\n+\t\t\t\telse\n+\t\t\t\t\treturn true;\n+\t\t\t}\n+\n+\t\t\tif ((treeWalk.getDepth() - targetDepth) >= recurseLimit) {\n+\t\t\t\tif (visitingResource.equals(resource))\n+\t\t\t\t\textractResourceProperties(treeWalk);\n+\n+\t\t\t\treturn false;\n+\t\t\t}\n+\n+\t\t\treturn true;\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic TreeFilter clone() {\n+\t\t\tRecursiveStateFilter clone = new RecursiveStateFilter();\n+\t\t\tclone.filesChecked = this.filesChecked;\n+\t\t\treturn clone;\n+\t\t}\n+\n+\t\t@Override\n+\t\tpublic boolean shouldBeRecursive() {\n+\t\t\treturn true;\n+\t\t}\n+\t}\n+\n+\tprivate void extractContainerProperties(TreeWalk treeWalk) throws IOException {\n+\n+\t\tif (isIgnored(resource)) {\n+\t\t\tignored = true;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\ttreeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter(),\n+\t\t\t\tnew RecursiveStateFilter()));\n+\t\ttreeWalk.setRecursive(true);\n+\n+\t\ttreeWalk.next();\n+\t}\n+\n+\t/**\n+\t * Adds a filter to the specified tree walk limiting the results to only\n+\t * those matching the resource specified by <code>resourceToFilterBy</code>\n+\t * <p>\n+\t * If the resource does not exists in the current repository, no filter is\n+\t * added and the method returns <code>false</code>. If the resource is a\n+\t * project, no filter is added, but the operation is considered a success.\n+\t *\n+\t * @param treeWalk\n+\t *            the tree walk to add the filter to\n+\t * @param resourceToFilterBy\n+\t *            the resource to filter by\n+\t *\n+\t * @return <code>true</code> if the filter could be added,\n+\t *         <code>false</code> otherwise\n+\t */\n+\tprivate boolean addResourceFilter(final TreeWalk treeWalk,\n+\t\t\tfinal IResource resourceToFilterBy) {\n+\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n+\t\t\t\t.getRepoRelativePath(resourceToFilterBy));\n+\t\tif (repositoryPaths.isEmpty())\n+\t\t\treturn false;\n+\n+\t\tif (repositoryPaths.contains(\"\"))\n+\t\t\treturn true; // Project filter\n+\n+\t\ttreeWalk.setFilter(PathFilterGroup.createFromStrings(repositoryPaths));\n+\t\treturn true;\n+\t}\n+\n+\t/**\n+\t * Helper method to create a new tree walk between the repository, the\n+\t * index, and the working tree.\n+\t *\n+\t * @return the created tree walk, or null if it could not be created\n+\t * @throws IOException\n+\t *             if there were errors when creating the tree walk\n+\t */\n+\tprivate TreeWalk createThreeWayTreeWalk() throws IOException {\n+\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n+\t\tif (!addResourceFilter(treeWalk, resource))\n+\t\t\treturn null;\n+\n+\t\ttreeWalk.setRecursive(treeWalk.getFilter().shouldBeRecursive());\n+\t\ttreeWalk.reset();\n+\n+\t\t// Repository\n+\t\tif (headId != null)\n+\t\t\ttreeWalk.addTree(new RevWalk(repository).parseTree(headId));\n+\t\telse\n+\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n+\n+\t\t// Index\n+\t\ttreeWalk.addTree(new DirCacheIterator(DirCache.read(repository)));\n+\n+\t\t// Working directory\n+\t\tIProject project = resource.getProject();\n+\t\tIWorkspace workspace = resource.getWorkspace();\n+\t\tif (repository.getWorkDir().equals(project.getLocation().toFile()))\n+\t\t\ttreeWalk.addTree(new ContainerTreeIterator(project));\n+\t\telse\n+\t\t\ttreeWalk.addTree(new ContainerTreeIterator(workspace.getRoot()));\n+\n+\t\t// TODO: Add fallback for projects with the repository more than\n+\t\t// one parent up, for example by using a stack of DummyIterators\n+\n+\t\treturn treeWalk;\n+\t}\n+\n+\tprivate static boolean timestampMatches(DirCacheEntry indexEntry,\n+\t\t\tResourceEntry resourceEntry) {\n+\t\tlong tIndex = indexEntry.getLastModified();\n+\t\tlong tWorkspaceResource = resourceEntry.getLastModified();\n+\n+\n+\t\t// C-Git under Windows stores timestamps with 1-seconds resolution,\n+\t\t// so we need to check to see if this is the case here, and possibly\n+\t\t// fix the timestamp of the resource to match the resolution of the\n+\t\t// index.\n+\t\tif (tIndex % 1000 == 0) {\n+\t\t\treturn tIndex == (tWorkspaceResource - (tWorkspaceResource % 1000));\n+\t\t} else {\n+\t\t\treturn tIndex == tWorkspaceResource;\n+\t\t}\n+\t}\n+\n+\tprivate static boolean isIgnored(IResource resource) {\n+\t\t// TODO: Also read ignores from .git/info/excludes et al.\n+\t\treturn Team.isIgnoredHint(resource);\n+\t}\n+\n+\tpublic String getName() {\n+\t\treturn resource.getName();\n+\t}\n+\n+\tpublic int getType() {\n+\t\treturn resource.getType();\n+\t}\n+\n+\tpublic String getBranch() {\n+\t\treturn branch;\n+\t}\n+\n+\tpublic boolean isTracked() {\n+\t\treturn tracked;\n+\t}\n+\n+\tpublic boolean isIgnored() {\n+\t\treturn ignored;\n+\t}\n+\n+\tpublic boolean isDirty() {\n+\t\treturn dirty;\n+\t}\n+\n+\tpublic Staged staged() {\n+\t\treturn staged;\n+\t}\n+\n+\tpublic boolean hasConflicts() {\n+\t\treturn conflicts;\n+\t}\n+\n+\tpublic boolean isAssumeValid() {\n+\t\treturn assumeValid;\n+\t}\n+}\n\\ No newline at end of file\ndiff --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\nindex 1e95369..d9de3a4 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -14,7 +14,6 @@\n package org.spearce.egit.ui.internal.decorators;\n \n import java.io.IOException;\n-import java.util.Collections;\n import java.util.HashMap;\n import java.util.HashSet;\n import java.util.Map;\n@@ -42,7 +41,6 @@\n import org.eclipse.osgi.util.TextProcessor;\n import org.eclipse.swt.graphics.ImageData;\n import org.eclipse.swt.widgets.Display;\n-import org.eclipse.team.core.Team;\n import org.eclipse.team.ui.ISharedImages;\n import org.eclipse.team.ui.TeamImages;\n import org.eclipse.team.ui.TeamUI;\n@@ -58,21 +56,11 @@\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged;\n-import org.spearce.jgit.dircache.DirCache;\n-import org.spearce.jgit.dircache.DirCacheEntry;\n-import org.spearce.jgit.dircache.DirCacheIterator;\n-import org.spearce.jgit.lib.Constants;\n-import org.spearce.jgit.lib.FileMode;\n import org.spearce.jgit.lib.IndexChangedEvent;\n-import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.RefsChangedEvent;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.RepositoryChangedEvent;\n import org.spearce.jgit.lib.RepositoryListener;\n-import org.spearce.jgit.revwalk.RevWalk;\n-import org.spearce.jgit.treewalk.EmptyTreeIterator;\n-import org.spearce.jgit.treewalk.TreeWalk;\n-import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n \n /**\n  * Supplies annotations for displayed resources\n@@ -151,6 +139,15 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (resource == null)\n \t\t\treturn;\n \n+\t\t// Don't decorate if the workbench is not running\n+\t\tif (!PlatformUI.isWorkbenchRunning())\n+\t\t\treturn;\n+\n+\t\t// Don't decorate if UI plugin is not running\n+\t\tActivator activator = Activator.getDefault();\n+\t\tif (activator == null)\n+\t\t\treturn;\n+\n \t\t// Don't decorate the workspace root\n \t\tif (resource.getType() == IResource.ROOT)\n \t\t\treturn;\n@@ -169,11 +166,6 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (mapping.getRepoRelativePath(resource) == null)\n \t\t\treturn;\n \n-\t\t// Don't decorate if UI plugin is not running\n-\t\tActivator activator = Activator.getDefault();\n-\t\tif (activator == null)\n-\t\t\treturn;\n-\n \t\ttry {\n \t\t\tDecorationHelper helper = new DecorationHelper(activator\n \t\t\t\t\t.getPreferenceStore());\n@@ -184,238 +176,6 @@ public void decorate(Object element, IDecoration decoration) {\n \t\t}\n \t}\n \n-\tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n-\n-\t\tprivate final IResource resource;\n-\n-\t\tprivate final RepositoryMapping mapping;\n-\n-\t\tprivate final Repository repository;\n-\n-\t\tprivate final ObjectId headId;\n-\n-\t\tprivate String branch = \"\";\n-\n-\t\tprivate boolean tracked = false;\n-\n-\t\tprivate boolean ignored = false;\n-\n-\t\tprivate boolean dirty = false;\n-\n-\t\tprivate boolean conflicts = false;\n-\n-\t\tprivate boolean assumeValid = false;\n-\n-\t\tprivate Staged staged = Staged.NOT_STAGED;\n-\n-\t\tstatic final int T_HEAD = 0;\n-\n-\t\tstatic final int T_INDEX = 1;\n-\n-\t\tstatic final int T_WORKSPACE = 2;\n-\n-\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n-\t\t\t\tthrows IOException {\n-\t\t\tresource = resourceToWrap;\n-\t\t\tmapping = RepositoryMapping.getMapping(resource);\n-\t\t\trepository = mapping.getRepository();\n-\t\t\theadId = repository.resolve(Constants.HEAD);\n-\n-\t\t\tswitch (resource.getType()) {\n-\t\t\tcase IResource.FILE:\n-\t\t\t\textractFileProperties();\n-\t\t\t\tbreak;\n-\t\t\tcase IResource.FOLDER:\n-\t\t\t\textractContainerProperties();\n-\t\t\t\tbreak;\n-\t\t\tcase IResource.PROJECT:\n-\t\t\t\textractProjectProperties();\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\n-\t\tprivate void extractFileProperties() throws IOException {\n-\t\t\tTreeWalk treeWalk = createHeadVsIndexTreeWalk();\n-\t\t\tif (treeWalk == null)\n-\t\t\t\treturn;\n-\n-\t\t\tif (treeWalk.next())\n-\t\t\t\ttracked = true;\n-\t\t\telse\n-\t\t\t\treturn;\n-\n-\t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n-\t\t\tif (Team.isIgnoredHint(resource)) {\n-\t\t\t\tignored = true;\n-\t\t\t\treturn;\n-\t\t\t}\n-\n-\t\t\tfinal DirCacheIterator indexIterator = treeWalk.getTree(T_INDEX,\n-\t\t\t\t\tDirCacheIterator.class);\n-\t\t\tfinal DirCacheEntry indexEntry = indexIterator != null ? indexIterator\n-\t\t\t\t\t.getDirCacheEntry()\n-\t\t\t\t\t: null;\n-\n-\t\t\tif (indexEntry == null) {\n-\t\t\t\tstaged = Staged.REMOVED;\n-\t\t\t} else {\n-\t\t\t\tif (indexEntry.isAssumeValid()) {\n-\t\t\t\t\tdirty = false;\n-\t\t\t\t\tassumeValid = true;\n-\t\t\t\t} else if (indexEntry.getStage() > 0) {\n-\t\t\t\t\tconflicts = true;\n-\t\t\t\t} else if (treeWalk.getRawMode(T_HEAD) == FileMode.MISSING\n-\t\t\t\t\t\t.getBits()) {\n-\t\t\t\t\tstaged = Staged.ADDED;\n-\t\t\t\t} else {\n-\t\t\t\t\tlong indexEntryLastModified = indexEntry.getLastModified();\n-\t\t\t\t\tlong resourceLastModified = resource.getLocalTimeStamp();\n-\n-\t\t\t\t\t// C-Git under Windows stores timestamps with 1-seconds\n-\t\t\t\t\t// resolution, so we need to check to see if this is the\n-\t\t\t\t\t// case here, and possibly fix the timestamp of the resource\n-\t\t\t\t\t// to match the resolution of the index.\n-\t\t\t\t\tif (indexEntryLastModified % 1000 == 0) {\n-\t\t\t\t\t\tresourceLastModified -= resourceLastModified % 1000;\n-\t\t\t\t\t}\n-\n-\t\t\t\t\tif (resourceLastModified != indexEntryLastModified) {\n-\t\t\t\t\t\t// TODO: Consider doing a content check here, to rule\n-\t\t\t\t\t\t// out false positives, as we might get mismatch between\n-\t\t\t\t\t\t// timestamps, even if the content is the same\n-\t\t\t\t\t\tdirty = true;\n-\t\t\t\t\t}\n-\n-\t\t\t\t\tif (treeWalk.getRawMode(T_HEAD) != treeWalk\n-\t\t\t\t\t\t\t.getRawMode(T_INDEX)\n-\t\t\t\t\t\t\t|| !treeWalk.idEqual(T_HEAD, T_INDEX)) {\n-\t\t\t\t\t\tstaged = Staged.MODIFIED;\n-\t\t\t\t\t}\n-\t\t\t\t}\n-\t\t\t}\n-\n-\t\t}\n-\n-\t\tprivate void extractContainerProperties() throws IOException {\n-\t\t\tTreeWalk treeWalk = createHeadVsIndexTreeWalk();\n-\t\t\tif (treeWalk == null)\n-\t\t\t\treturn;\n-\n-\t\t\tif (treeWalk.next())\n-\t\t\t\ttracked = true;\n-\t\t\telse\n-\t\t\t\treturn;\n-\n-\t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n-\t\t\tif (Team.isIgnoredHint(resource)) {\n-\t\t\t\tignored = true;\n-\t\t\t\treturn;\n-\t\t\t}\n-\n-\t\t\t// TODO: Compute dirty state for folder, using ContainerTreeIterator\n-\t\t\t// and ContainerDiffFilter\n-\n-\t\t}\n-\n-\t\tprivate void extractProjectProperties() throws IOException {\n-\t\t\tbranch = repository.getBranch();\n-\t\t\ttracked = true;\n-\n-\t\t\t// TODO: Compute dirty state for folder, using ContainerTreeIterator\n-\t\t\t// and ContainerDiffFilter\n-\n-\t\t}\n-\n-\t\t/**\n-\t\t * Adds a filter to the specified tree walk limiting the results to only\n-\t\t * those matching the resource specified by\n-\t\t * <code>resourceToFilterBy</code>\n-\t\t * <p>\n-\t\t * If the resource does not exists in the current repository, or it has\n-\t\t * an empty path (it is the project itself), the filter is not added,\n-\t\t * and the method returns <code>null</code>.\n-\t\t * \n-\t\t * @param treeWalk\n-\t\t *            the tree walk to add the filter to\n-\t\t * @param resourceToFilterBy\n-\t\t *            the resource to filter by\n-\t\t * \n-\t\t * @return <code>true</code> if the filter could be added,\n-\t\t *         <code>false</code> otherwise\n-\t\t */\n-\t\tprivate boolean addResourceFilter(final TreeWalk treeWalk,\n-\t\t\t\tfinal IResource resourceToFilterBy) {\n-\t\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n-\t\t\t\t\t.getRepoRelativePath(resourceToFilterBy));\n-\t\t\tif (repositoryPaths.isEmpty() || repositoryPaths.contains(\"\"))\n-\t\t\t\treturn false;\n-\n-\t\t\ttreeWalk.setFilter(PathFilterGroup\n-\t\t\t\t\t.createFromStrings(repositoryPaths));\n-\t\t\treturn true;\n-\t\t}\n-\n-\t\t/**\n-\t\t * Helper method to create a new tree walk between HEAD and the index.\n-\t\t * \n-\t\t * @return the created tree walk, or null if it could not be created\n-\t\t * @throws IOException\n-\t\t *             if there were errors when creating the tree walk\n-\t\t */\n-\t\tprivate TreeWalk createHeadVsIndexTreeWalk() throws IOException {\n-\t\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n-\t\t\tif (!addResourceFilter(treeWalk, resource))\n-\t\t\t\treturn null;\n-\n-\t\t\ttreeWalk.setRecursive(treeWalk.getFilter().shouldBeRecursive());\n-\t\t\ttreeWalk.reset();\n-\n-\t\t\tif (headId != null)\n-\t\t\t\ttreeWalk.addTree(new RevWalk(repository).parseTree(headId));\n-\t\t\telse\n-\t\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n-\n-\t\t\ttreeWalk.addTree(new DirCacheIterator(DirCache.read(repository)));\n-\t\t\treturn treeWalk;\n-\t\t}\n-\n-\t\tpublic String getName() {\n-\t\t\treturn resource.getName();\n-\t\t}\n-\n-\t\tpublic int getType() {\n-\t\t\treturn resource.getType();\n-\t\t}\n-\n-\t\tpublic String getBranch() {\n-\t\t\treturn branch;\n-\t\t}\n-\n-\t\tpublic boolean isTracked() {\n-\t\t\treturn tracked;\n-\t\t}\n-\n-\t\tpublic boolean isIgnored() {\n-\t\t\treturn ignored;\n-\t\t}\n-\n-\t\tpublic boolean isDirty() {\n-\t\t\treturn dirty;\n-\t\t}\n-\n-\t\tpublic Staged staged() {\n-\t\t\treturn staged;\n-\t\t}\n-\n-\t\tpublic boolean hasConflicts() {\n-\t\t\treturn conflicts;\n-\t\t}\n-\n-\t\tpublic boolean isAssumeValid() {\n-\t\t\treturn assumeValid;\n-\t\t}\n-\t}\n-\n \t/**\n \t * Helper class for doing resource decoration, based on the given\n \t * preferences\n@@ -570,11 +330,13 @@ else if (staged == Staged.REMOVED)\n \t\t\t\t}\n \n \t\t\t\t// Conflicts override everything\n-\t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)\n+\t\t\t\tif (store\n+\t\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_CONFLICTS_ICON)\n \t\t\t\t\t\t&& resource.hasConflicts())\n \t\t\t\t\toverlay = conflictImage;\n \n-\t\t\t} else if (store.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n+\t\t\t} else if (store\n+\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n \t\t\t\toverlay = untrackedImage;\n \t\t\t}\n \n@@ -714,6 +476,14 @@ public void resourceChanged(IResourceChangeEvent event) {\n \t\ttry { // Compute the changed resources by looking at the delta\n \t\t\tevent.getDelta().accept(new IResourceDeltaVisitor() {\n \t\t\t\tpublic boolean visit(IResourceDelta delta) throws CoreException {\n+\n+\t\t\t\t\t// If the file has changed but not in a way that we care\n+\t\t\t\t\t// about (e.g. marker changes to files) then ignore\n+\t\t\t\t\tif (delta.getKind() == IResourceDelta.CHANGED\n+\t\t\t\t\t\t\t&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {\n+\t\t\t\t\t\treturn true;\n+\t\t\t\t\t}\n+\n \t\t\t\t\tfinal IResource resource = delta.getResource();\n \n \t\t\t\t\t// If the resource is not part of a project under Git\n@@ -724,6 +494,7 @@ public boolean visit(IResourceDelta delta) throws CoreException {\n \t\t\t\t\t\t// Ignore the change\n \t\t\t\t\t\treturn true;\n \t\t\t\t\t}\n+\n \t\t\t\t\tif (resource.getType() == IResource.ROOT) {\n \t\t\t\t\t\t// Continue with the delta\n \t\t\t\t\t\treturn true;\n@@ -735,33 +506,32 @@ public boolean visit(IResourceDelta delta) throws CoreException {\n \t\t\t\t\t\t\treturn false;\n \t\t\t\t\t}\n \n-\t\t\t\t\t// If the file has changed but not in a way that we care\n-\t\t\t\t\t// about\n-\t\t\t\t\t// (e.g. marker changes to files) then ignore the change\n-\t\t\t\t\tif (delta.getKind() == IResourceDelta.CHANGED\n-\t\t\t\t\t\t\t&& (delta.getFlags() & INTERESTING_CHANGES) == 0) {\n-\t\t\t\t\t\treturn true;\n-\t\t\t\t\t}\n-\n \t\t\t\t\t// All seems good, schedule the resource for update\n \t\t\t\t\tresourcesToUpdate.add(resource);\n-\t\t\t\t\treturn true;\n+\n+\t\t\t\t\tif (delta.getKind() == IResourceDelta.CHANGED\n+\t\t\t\t\t\t\t&& (delta.getFlags() & IResourceDelta.OPEN) > 1)\n+\t\t\t\t\t\treturn false; // Don't recurse when opening projects\n+\t\t\t\t\telse\n+\t\t\t\t\t\treturn true;\n \t\t\t\t}\n \t\t\t}, true /* includePhantoms */);\n \t\t} catch (final CoreException e) {\n \t\t\thandleException(null, e);\n \t\t}\n \n-\t\t// If deep decorator calculation is enabled in the preferences we\n-\t\t// walk the ancestor tree of each of the changed resources and add\n+\t\tif (resourcesToUpdate.isEmpty())\n+\t\t\treturn;\n+\n+\t\t// If ancestor-decoration is enabled in the preferences we walk\n+\t\t// the ancestor tree of each of the changed resources and add\n \t\t// their parents to the update set\n \t\tfinal IPreferenceStore store = Activator.getDefault()\n \t\t\t\t.getPreferenceStore();\n-\t\tif (store.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY)) {\n+\t\tif (store.getBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS)) {\n \t\t\tfinal IResource[] changedResources = resourcesToUpdate\n \t\t\t\t\t.toArray(new IResource[resourcesToUpdate.size()]);\n-\t\t\tfor (int i = 0; i < changedResources.length; i++) {\n-\t\t\t\tIResource current = changedResources[i];\n+\t\t\tfor (IResource current : changedResources) {\n \t\t\t\twhile (current.getType() != IResource.ROOT) {\n \t\t\t\t\tcurrent = current.getParent();\n \t\t\t\t\tresourcesToUpdate.add(current);\ndiff --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\nindex f72ceb7..1c0f82e 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -51,12 +51,14 @@\n import org.eclipse.swt.graphics.Font;\n import org.eclipse.swt.graphics.Image;\n import org.eclipse.swt.layout.GridData;\n+import org.eclipse.swt.layout.GridLayout;\n import org.eclipse.swt.widgets.Button;\n import org.eclipse.swt.widgets.Composite;\n import org.eclipse.swt.widgets.Control;\n import org.eclipse.swt.widgets.Event;\n import org.eclipse.swt.widgets.Label;\n import org.eclipse.swt.widgets.Listener;\n+import org.eclipse.swt.widgets.Scale;\n import org.eclipse.swt.widgets.TabFolder;\n import org.eclipse.swt.widgets.TabItem;\n import org.eclipse.swt.widgets.Text;\n@@ -76,6 +78,7 @@\n import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;\n import org.spearce.egit.ui.internal.decorators.IDecoratableResource.Staged;\n \n+\n /**\n  * Preference page for customizing Git label decorations\n  */\n@@ -88,7 +91,9 @@\n \n \tprivate Text projectTextFormat;\n \n-\tprivate Button computeDeepDirtyState;\n+\tprivate Button recomputeAncestorDecorations;\n+\n+\tprivate Scale containerRecurseLimit;\n \n \tprivate Button showTracked;\n \n@@ -108,11 +113,11 @@\n \n \tstatic {\n \t\tfinal PreviewResource project = new PreviewResource(\n-\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false, false, Staged.NOT_STAGED, false, false); //$NON-NLS-1$1\n+\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false, true, Staged.NOT_STAGED, false, false); //$NON-NLS-1$1\n \t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n \n \t\tchildren.add(new PreviewResource(\n-\t\t\t\t\t\t\"folder\", IResource.FOLDER, null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n+\t\t\t\t\t\t\"folder\", IResource.FOLDER, null, true, false, true, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n \t\t\t\t\t\t\"tracked.txt\", IResource.FILE, null, true, false, false, Staged.NOT_STAGED, false, false)); //$NON-NLS-1$\n \t\tchildren.add(new PreviewResource(\n@@ -194,14 +199,68 @@ public void propertyChange(PropertyChangeEvent event) {\n \n \tprivate Control createGeneralDecoratorPage(Composite parent) {\n \t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n-\t\t\t\tSWTUtils.MARGINS_DEFAULT);\n+\t\t\t\tSWTUtils.MARGINS_DEFAULT, 1);\n \n-\t\tcomputeDeepDirtyState = SWTUtils.createCheckBox(composite,\n-\t\t\t\tUIText.DecoratorPreferencesPage_computeDeep);\n+\t\trecomputeAncestorDecorations = SWTUtils.createCheckBox(composite,\n+\t\t\t\tUIText.DecoratorPreferencesPage_recomputeAncestorDecorations);\n+\t\trecomputeAncestorDecorations\n+\t\t\t\t.setToolTipText(UIText.DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip);\n+\n+\t\tSWTUtils.createLabel(composite,\n+\t\t\t\tUIText.DecoratorPreferencesPage_computeRecursiveLimit);\n+\t\tcontainerRecurseLimit = createLabeledScaleControl(composite);\n+\t\tcontainerRecurseLimit\n+\t\t\t\t.setToolTipText(UIText.DecoratorPreferencesPage_computeRecursiveLimitTooltip);\n \n \t\treturn composite;\n \t}\n \n+\tprivate Scale createLabeledScaleControl(Composite parent) {\n+\n+\t\tfinal int[] values = new int[] { 0, 1, 2, 3, 5, 10, 15, 20, 50, 100,\n+\t\t\t\tInteger.MAX_VALUE };\n+\n+\t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n+\t\t\t\tSWTUtils.MARGINS_DEFAULT);\n+\n+\t\tComposite labels = SWTUtils.createHVFillComposite(composite,\n+\t\t\t\tSWTUtils.MARGINS_NONE, values.length);\n+\t\tGridLayout labelsLayout = (GridLayout) labels.getLayout();\n+\t\tlabelsLayout.makeColumnsEqualWidth = true;\n+\t\tlabelsLayout.horizontalSpacing = 0;\n+\t\tlabels.setLayoutData(SWTUtils.createGridData(-1, -1, SWT.FILL,\n+\t\t\t\tSWT.FILL, false, false));\n+\n+\t\tfor (int i = 0; i < values.length; ++i) {\n+\t\t\tLabel label = SWTUtils.createLabel(labels, \"\" + values[i]);\n+\t\t\tif (i == 0) {\n+\t\t\t\tlabel.setAlignment(SWT.LEFT);\n+\t\t\t\tlabel.setText(\"Off\");\n+\t\t\t} else if (i == values.length - 1) {\n+\t\t\t\tlabel.setAlignment(SWT.RIGHT);\n+\t\t\t\tlabel.setText(\"Inf.\");\n+\t\t\t} else {\n+\t\t\t\tlabel.setAlignment(SWT.CENTER);\n+\t\t\t}\n+\t\t}\n+\n+\t\tfinal Scale scale = new Scale(composite, SWT.HORIZONTAL);\n+\t\tscale.setLayoutData(SWTUtils.createHVFillGridData());\n+\t\tscale.setMaximum(values.length - 1);\n+\t\tscale.setMinimum(0);\n+\t\tscale.setIncrement(1);\n+\t\tscale.setPageIncrement(1);\n+\n+\t\tscale.addListener(SWT.Selection, new Listener() {\n+\t\t\tpublic void handleEvent(Event event) {\n+\t\t\t\t// Workaround for GTK treating the slider as stepless\n+\t\t\t\tscale.setSelection(scale.getSelection());\n+\t\t\t}\n+\t\t});\n+\n+\t\treturn scale;\n+\t}\n+\n \t/**\n \t * Creates the controls for the first tab folder\n \t * \n@@ -301,8 +360,10 @@ public void handleEvent(Event event) {\n \tprivate void initializeValues() {\n \t\tfinal IPreferenceStore store = getPreferenceStore();\n \n-\t\tcomputeDeepDirtyState.setSelection(store\n-\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\trecomputeAncestorDecorations.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS));\n+\t\tcontainerRecurseLimit.setSelection(store\n+\t\t\t\t.getInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT));\n \n \t\tfileTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n@@ -328,7 +389,6 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t}\n \t\t};\n \n-\t\tcomputeDeepDirtyState.addSelectionListener(selectionListener);\n \t\tshowTracked.addSelectionListener(selectionListener);\n \t\tshowUntracked.addSelectionListener(selectionListener);\n \t\tshowStaged.addSelectionListener(selectionListener);\n@@ -371,8 +431,10 @@ public boolean performOk() {\n \t */\n \tprivate boolean performOk(IPreferenceStore store) {\n \n-\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY,\n-\t\t\t\tcomputeDeepDirtyState.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS,\n+\t\t\t\trecomputeAncestorDecorations.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_RECURSIVE_LIMIT,\n+\t\t\t\tcontainerRecurseLimit.getSelection());\n \n \t\tstore.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tfileTextFormat.getText());\n@@ -403,8 +465,11 @@ protected void performDefaults() {\n \t\tsuper.performDefaults();\n \t\tIPreferenceStore store = getPreferenceStore();\n \n-\t\tcomputeDeepDirtyState.setSelection(store\n-\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\trecomputeAncestorDecorations\n+\t\t\t\t.setSelection(store\n+\t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_RECOMPUTE_ANCESTORS));\n+\t\tcontainerRecurseLimit.setSelection(store\n+\t\t\t\t.getDefaultInt(UIPreferences.DECORATOR_RECURSIVE_LIMIT));\n \n \t\tfileTextFormat.setText(store\n \t\t\t\t.getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\ndiff --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\nindex e9a2321..847cb2c 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -350,9 +350,12 @@ Decorator_exceptionMessage=Errors occurred while applying Git decorations to res\n \n DecoratorPreferencesPage_addVariablesTitle=Add Variables\n DecoratorPreferencesPage_addVariablesAction=Add &Variables...\n-DecoratorPreferencesPage_computeDeep=Include &ancestors when re-decorating changed resources\n+DecoratorPreferencesPage_recomputeAncestorDecorations=Re-decorate &ancestors when decorating changed resources\n+DecoratorPreferencesPage_recomputeAncestorDecorationsTooltip=Enabling this option will cause the ancestor-tree of any updated resources to also be re-decorated (minor performance impact).\n+DecoratorPreferencesPage_computeRecursiveLimit=Maximum number of levels to recurse for container decoration:\n+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.\n DecoratorPreferencesPage_description=Shows Git specific information on resources in projects under version control.\n-\n+DecoratorPreferencesPage_invalidInput=''{0}'' is not a valid input\n DecoratorPreferencesPage_decorationSettings=Decoration &settings:\n DecoratorPreferencesPage_preview=Preview:\n DecoratorPreferencesPage_fileFormatLabel=&Files:\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104305","messageId":"200902112316.28646.robin.rosenberg@gmail.com","threadId":"17728","inReplyTo":"1234377614-23798-9-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH v2 08/12] Add icon decoration for tracked and untracked resources","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@gmail.com","sentAt":"2009-02-11T22:16:28Z","receivedAt":"2009-02-11T22:16:28Z","isPatch":true,"sender":{"key":"robin.rosenberg@gmail.com","avatar":null},"body":"\nThis one fails to compile, \n\nDescription\tResource\tPath\tLocation\tType\nfPreview cannot be resolved\tGitDecoratorPreferencePage.java\torg.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences\tline 279\tJava Problem\n\nWith patch 9 things build again, but swapping the patches does not help.\n\nEvery patch should build and pass unit tests so we can perform bisect over the patches.\n\n-- robin\n"},{"id":"104308","messageId":"1234392384-31725-1-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"200902112316.28646.robin.rosenberg@gmail.com","subject":"[EGIT PATCH 08/12 v3] Add icon decoration for tracked and untracked resources","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-11T22:46:24Z","receivedAt":"2009-02-11T22:46:24Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Can be enabled/disabled in the preferences\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n\nSorry, got a little trigger happy with the rebasing and messed up a merge :/ \n\norg.spearce.egit.ui/icons/ovr/shared.gif           |  Bin 106 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/untracked.gif        |  Bin 0 -> 79 bytes\n .../egit/ui/PluginPreferenceInitializer.java       |    4 +-\n .../src/org/spearce/egit/ui/UIIcons.java           |    6 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +\n .../src/org/spearce/egit/ui/UIText.java            |    6 +\n .../decorators/GitLightweightDecorator.java        |  162 ++++++++++++++++++--\n .../internal/decorators/IDecoratableResource.java  |   15 ++\n .../preferences/GitDecoratorPreferencePage.java    |   89 +++++++++--\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 10 files changed, 259 insertions(+), 29 deletions(-)\n delete mode 100644 org.spearce.egit.ui/icons/ovr/shared.gif\n create mode 100644 org.spearce.egit.ui/icons/ovr/untracked.gif\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/shared.gif b/org.spearce.egit.ui/icons/ovr/shared.gif\ndeleted file mode 100644\nindex eb71a3c742e133c2ed61c958e237c71e0f7cb6aa..0000000000000000000000000000000000000000\nGIT binary patch\nliteral 0\nHcmV?d00001\n\nliteral 106\nzcmZ?wbhEHbWM|-D*v!E2|KHF5k5c|0Q~7_){{ITmE8U{M-dx?8EBgQce+Fzo@h1x-\np15m3DND^cQ1B<x8$xa82iYrMhmeJo^<v0pdEtw8K%o1d<1^~aoAP)cl\n\ndiff --git a/org.spearce.egit.ui/icons/ovr/untracked.gif b/org.spearce.egit.ui/icons/ovr/untracked.gif\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..45ca32060700d71abcb88bbcdb1000d503fe11cf\nGIT binary patch\nliteral 79\nzcmZ?wbhEHbWM|-DSj51<z}U+mGMhnsAy57};pVIV!2l?%_>+Z^fq{)d2gqgssbydm\nbiP#m!(lAAgdxkboj%vJ&f(EaJDuXouU6m8v\n\nliteral 0\nHcmV?d00001\n\ndiff --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\nindex 79c2665..7465444 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java\n@@ -35,13 +35,15 @@ public void initializeDefaultPreferences() {\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);\n \t\tprefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);\n \n+\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_fileFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_folderFormatDefault);\n \t\tprefs.setDefault(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n \t\t\t\tUIText.DecoratorPreferencesPage_projectFormatDefault);\n-\t\tprefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, true);\n+\t\tprefs.setDefault(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON, true);\n \n \t\tw = new int[] { 500, 500 };\n \t\tUIPreferences.setDefault(prefs,\ndiff --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\nindex ced186e..4c0d189 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIIcons.java\n@@ -24,8 +24,8 @@\n \t/** Decoration for resource removed from the index but not commit. */\n \tpublic static final ImageDescriptor OVR_PENDING_REMOVE;\n \n-\t/** Decoration for resource tracked and committed in git. */\n-\tpublic static final ImageDescriptor OVR_SHARED;\n+\t/** Decoration for resource not being tracked by Git */\n+\tpublic static final ImageDescriptor OVR_UNTRACKED;\n \n \t/** Decoration for tracked resource with a merge conflict.  */\n \tpublic static final ImageDescriptor OVR_CONFLICT;\n@@ -86,7 +86,7 @@\n \t\tbase = init();\n \t\tOVR_PENDING_ADD = map(\"ovr/pending_add.gif\");\n \t\tOVR_PENDING_REMOVE = map(\"ovr/pending_remove.gif\");\n-\t\tOVR_SHARED = map(\"ovr/shared.gif\");\n+\t\tOVR_UNTRACKED = map(\"ovr/untracked.gif\");\n \t\tOVR_CONFLICT = map(\"ovr/conflict.gif\");\n \t\tOVR_ASSUMEVALID = map(\"ovr/assumevalid.gif\");\n \t\tELCL16_FIND = map(\"elcl16/find.gif\");\ndiff --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\nindex a6168a0..7916cea 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java\n@@ -60,6 +60,10 @@\n \tpublic final static String DECORATOR_FOLDERTEXT_DECORATION = \"decorator_foldertext_decoration\";\n \t/** */\n \tpublic final static String DECORATOR_PROJECTTEXT_DECORATION = \"decorator_projecttext_decoration\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_TRACKED_ICON = \"decorator_show_tracked_icon\";\n+\t/** */\n+\tpublic final static String DECORATOR_SHOW_UNTRACKED_ICON = \"decorator_show_untracked_icon\";\n \n \t/**\n \t * Get the preference values associated with a fixed integer array.\ndiff --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\nindex 345c66b..60e4eaa 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -988,6 +988,12 @@\n \t/** */\n \tpublic static String DecoratorPreferencesPage_labelDecorationsLink;\n \n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowTracked;\n+\n+\t/** */\n+\tpublic static String DecoratorPreferencesPage_iconsShowUntracked;\n+\n \tstatic {\n \t\tinitializeMessages(UIText.class.getPackage().getName() + \".uitext\",\n \t\t\t\tUIText.class);\ndiff --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\nindex 265d5a3..b20070a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java\n@@ -15,6 +15,7 @@\n \n import java.io.IOException;\n import java.util.ArrayList;\n+import java.util.Collections;\n import java.util.HashMap;\n import java.util.HashSet;\n import java.util.List;\n@@ -34,6 +35,7 @@\n import org.eclipse.core.runtime.IAdaptable;\n import org.eclipse.core.runtime.IStatus;\n import org.eclipse.jface.preference.IPreferenceStore;\n+import org.eclipse.jface.resource.ImageDescriptor;\n import org.eclipse.jface.util.IPropertyChangeListener;\n import org.eclipse.jface.util.PropertyChangeEvent;\n import org.eclipse.jface.viewers.IDecoration;\n@@ -41,7 +43,11 @@\n import org.eclipse.jface.viewers.LabelProvider;\n import org.eclipse.jface.viewers.LabelProviderChangedEvent;\n import org.eclipse.osgi.util.TextProcessor;\n+import org.eclipse.swt.graphics.ImageData;\n import org.eclipse.swt.widgets.Display;\n+import org.eclipse.team.core.Team;\n+import org.eclipse.team.ui.ISharedImages;\n+import org.eclipse.team.ui.TeamImages;\n import org.eclipse.team.ui.TeamUI;\n import org.eclipse.ui.IContributorResourceAdapter;\n import org.eclipse.ui.PlatformUI;\n@@ -51,13 +57,22 @@\n import org.spearce.egit.core.project.RepositoryChangeListener;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.egit.ui.Activator;\n+import org.spearce.egit.ui.UIIcons;\n import org.spearce.egit.ui.UIPreferences;\n import org.spearce.egit.ui.UIText;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheIterator;\n+import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.IndexChangedEvent;\n+import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.RefsChangedEvent;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.RepositoryChangedEvent;\n import org.spearce.jgit.lib.RepositoryListener;\n+import org.spearce.jgit.revwalk.RevWalk;\n+import org.spearce.jgit.treewalk.EmptyTreeIterator;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n \n /**\n  * Supplies annotations for displayed resources\n@@ -144,7 +159,7 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (!resource.exists() && !resource.isPhantom())\n \t\t\treturn;\n \n-\t\t// Make sure we're dealing with a Git project\n+\t\t// Make sure we're dealing with a project under Git revision control\n \t\tfinal RepositoryMapping mapping = RepositoryMapping\n \t\t\t\t.getMapping(resource);\n \t\tif (mapping == null)\n@@ -171,14 +186,80 @@ public void decorate(Object element, IDecoration decoration) {\n \n \tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n \n-\t\tprivate IResource resource;\n-\t\tprivate String branch;\n+\t\tprivate final IResource resource;\n \n-\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap) throws IOException {\n+\t\tprivate final RepositoryMapping mapping;\n+\n+\t\tprivate final Repository repository;\n+\n+\t\tprivate final ObjectId headId;\n+\n+\t\tprivate String branch = \"\";\n+\n+\t\tprivate boolean tracked = false;\n+\n+\t\tprivate boolean ignored = false;\n+\n+\t\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n+\t\t\t\tthrows IOException {\n \t\t\tresource = resourceToWrap;\n-\t\t\tRepositoryMapping mapping = RepositoryMapping.getMapping(resource);\n-\t\t\tRepository repository = mapping.getRepository();\n+\t\t\tmapping = RepositoryMapping.getMapping(resource);\n+\t\t\trepository = mapping.getRepository();\n+\t\t\theadId = repository.resolve(Constants.HEAD);\n+\n+\t\t\tinitializeValues();\n+\t\t}\n+\n+\t\t/**\n+\t\t * Initialize the various values that are used for making decoration\n+\t\t * decisions later on.\n+\t\t * \n+\t\t * We might as well pre-load these now, instead of using lazy\n+\t\t * initialization, because they are all read by the decorator when\n+\t\t * building variable bindings and computing the preferred overlay.\n+\t\t * \n+\t\t * @throws IOException\n+\t\t */\n+\t\tprivate void initializeValues() throws IOException {\n+\n+\t\t\t// Resolve current branch\n \t\t\tbranch = repository.getBranch();\n+\n+\t\t\t// Resolve tracked state\n+\t\t\tif (getType() == IResource.PROJECT) {\n+\t\t\t\ttracked = true;\n+\t\t\t} else {\n+\t\t\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n+\n+\t\t\t\tSet<String> repositoryPaths = Collections.singleton(mapping\n+\t\t\t\t\t\t.getRepoRelativePath(resource));\n+\t\t\t\tif (!(repositoryPaths.isEmpty() || repositoryPaths.contains(\"\"))) {\n+\t\t\t\t\ttreeWalk.setFilter(PathFilterGroup\n+\t\t\t\t\t\t\t.createFromStrings(repositoryPaths));\n+\t\t\t\t\ttreeWalk.setRecursive(treeWalk.getFilter()\n+\t\t\t\t\t\t\t.shouldBeRecursive());\n+\t\t\t\t\ttreeWalk.reset();\n+\n+\t\t\t\t\tif (headId != null)\n+\t\t\t\t\t\ttreeWalk.addTree(new RevWalk(repository)\n+\t\t\t\t\t\t\t\t.parseTree(headId));\n+\t\t\t\t\telse\n+\t\t\t\t\t\ttreeWalk.addTree(new EmptyTreeIterator());\n+\n+\t\t\t\t\ttreeWalk.addTree(new DirCacheIterator(DirCache\n+\t\t\t\t\t\t\t.read(repository)));\n+\t\t\t\t\tif (treeWalk.next()) {\n+\t\t\t\t\t\ttracked = true;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\t// Resolve ignored state (currently only reads the global Eclipse\n+\t\t\t// ignores)\n+\t\t\t// TODO: Also read ignores from .git/info/excludes et al.\n+\t\t\tif (Team.isIgnoredHint(resource)) {\n+\t\t\t\tignored = true;\n+\t\t\t}\n \t\t}\n \n \t\tpublic String getName() {\n@@ -192,6 +273,14 @@ public int getType() {\n \t\tpublic String getBranch() {\n \t\t\treturn branch;\n \t\t}\n+\n+\t\tpublic boolean isTracked() {\n+\t\t\treturn tracked;\n+\t\t}\n+\n+\t\tpublic boolean isIgnored() {\n+\t\t\treturn ignored;\n+\t\t}\n \t}\n \n \t/**\n@@ -203,13 +292,45 @@ public String getBranch() {\n \t */\n \tpublic static class DecorationHelper {\n \n-\t\tprivate IPreferenceStore store;\n-\n \t\t/** */\n \t\tpublic static final String BINDING_RESOURCE_NAME = \"name\"; //$NON-NLS-1$\n+\n \t\t/** */\n \t\tpublic static final String BINDING_BRANCH_NAME = \"branch\"; //$NON-NLS-1$\n \n+\t\tprivate IPreferenceStore store;\n+\n+\t\t/**\n+\t\t * Define a cached image descriptor which only creates the image data\n+\t\t * once\n+\t\t */\n+\t\tprivate static class CachedImageDescriptor extends ImageDescriptor {\n+\t\t\tImageDescriptor descriptor;\n+\n+\t\t\tImageData data;\n+\n+\t\t\tpublic CachedImageDescriptor(ImageDescriptor descriptor) {\n+\t\t\t\tthis.descriptor = descriptor;\n+\t\t\t}\n+\n+\t\t\tpublic ImageData getImageData() {\n+\t\t\t\tif (data == null) {\n+\t\t\t\t\tdata = descriptor.getImageData();\n+\t\t\t\t}\n+\t\t\t\treturn data;\n+\t\t\t}\n+\t\t}\n+\n+\t\tprivate static ImageDescriptor trackedImage;\n+\n+\t\tprivate static ImageDescriptor untrackedImage;\n+\n+\t\tstatic {\n+\t\t\ttrackedImage = new CachedImageDescriptor(TeamImages\n+\t\t\t\t\t.getImageDescriptor(ISharedImages.IMG_CHECKEDIN_OVR));\n+\t\t\tuntrackedImage = new CachedImageDescriptor(UIIcons.OVR_UNTRACKED);\n+\t\t}\n+\n \t\t/**\n \t\t * Constructs a decorator using the rules from the given\n \t\t * <code>preferencesStore</code>\n@@ -233,6 +354,12 @@ public DecorationHelper(IPreferenceStore preferencesStore) {\n \t\t */\n \t\tpublic void decorate(IDecoration decoration,\n \t\t\t\tIDecoratableResource resource) {\n+\t\t\tdecorateText(decoration, resource);\n+\t\t\tdecorateIcons(decoration, resource);\n+\t\t}\n+\n+\t\tprivate void decorateText(IDecoration decoration,\n+\t\t\t\tIDecoratableResource resource) {\n \t\t\tString format = \"\";\n \t\t\tswitch (resource.getType()) {\n \t\t\tcase IResource.FILE:\n@@ -256,9 +383,24 @@ public void decorate(IDecoration decoration,\n \t\t\tdecorate(decoration, format, bindings);\n \t\t}\n \n+\t\tprivate void decorateIcons(IDecoration decoration,\n+\t\t\t\tIDecoratableResource resource) {\n+\t\t\tif (resource.isIgnored())\n+\t\t\t\treturn;\n+\n+\t\t\tif (resource.isTracked()) {\n+\t\t\t\tif (store.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON))\n+\t\t\t\t\tdecoration.addOverlay(trackedImage);\n+\t\t\t} else if (store\n+\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON)) {\n+\t\t\t\tdecoration.addOverlay(untrackedImage);\n+\t\t\t}\n+\t\t}\n+\n \t\t/**\n-\t\t * Decorates the given <code>decoration</code>, using the given\n-\t\t * <code>format</code>, and mapped using <code>bindings</code>\n+\t\t * Decorates the given <code>decoration</code>, using the specified text\n+\t\t * <code>format</code>, and mapped using the variable bindings from\n+\t\t * <code>bindings</code>\n \t\t * \n \t\t * @param decoration\n \t\t *            the decoration to decorate\ndiff --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\nindex 6b36e0e..f144214 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java\n@@ -36,4 +36,19 @@\n \t *         applicable\n \t */\n \tString getBranch();\n+\n+\t/**\n+\t * Returns whether or not the resource is tracked by Git\n+\t * \n+\t * @return whether or not the resource is tracked by Git\n+\t */\n+\tboolean isTracked();\n+\n+\t/**\n+\t * Returns whether or not the resource is ignored, either by a global team\n+\t * ignore in Eclipse, or by .git/info/exclude et al.\n+\t * \n+\t * @return whether or not the resource is ignored\n+\t */\n+\tboolean isIgnored();\n }\ndiff --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\nindex b7d737c..06ddf50 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -44,6 +44,9 @@\n import org.eclipse.swt.SWT;\n import org.eclipse.swt.events.ModifyEvent;\n import org.eclipse.swt.events.ModifyListener;\n+import org.eclipse.swt.events.SelectionAdapter;\n+import org.eclipse.swt.events.SelectionEvent;\n+import org.eclipse.swt.events.SelectionListener;\n import org.eclipse.swt.graphics.Color;\n import org.eclipse.swt.graphics.Font;\n import org.eclipse.swt.graphics.Image;\n@@ -83,7 +86,11 @@\n \n \tprivate Text projectTextFormat;\n \n-\tprivate Button showDirty;\n+\tprivate Button computeDeepDirtyState;\n+\n+\tprivate Button showTracked;\n+\n+\tprivate Button showUntracked;\n \n \tprivate Preview preview;\n \n@@ -93,10 +100,16 @@\n \n \tstatic {\n \t\tfinal PreviewResource project = new PreviewResource(\n-\t\t\t\t\"Project\", IResource.PROJECT, \"master\"); //$NON-NLS-1$1\n+\t\t\t\t\"Project\", IResource.PROJECT, \"master\", true, false); //$NON-NLS-1$1\n \t\tfinal ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();\n-\t\tchildren.add(new PreviewResource(\"folder\", IResource.FOLDER, null)); //$NON-NLS-1$\n-\t\tchildren.add(new PreviewResource(\"file.txt\", IResource.FILE, null)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"folder\", IResource.FOLDER, null, true, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"file.txt\", IResource.FILE, null, true, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"untracked.txt\", IResource.FILE, null, false, false)); //$NON-NLS-1$\n+\t\tchildren.add(new PreviewResource(\n+\t\t\t\t\"ignored.txt\", IResource.FILE, null, false, true)); //$NON-NLS-1$\n \t\tproject.children = children;\n \t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n \t}\n@@ -160,7 +173,7 @@ private Control createGeneralDecoratorPage(Composite parent) {\n \t\tComposite composite = SWTUtils.createHVFillComposite(parent,\n \t\t\t\tSWTUtils.MARGINS_DEFAULT);\n \n-\t\tshowDirty = SWTUtils.createCheckBox(composite,\n+\t\tcomputeDeepDirtyState = SWTUtils.createCheckBox(composite,\n \t\t\t\tUIText.DecoratorPreferencesPage_computeDeep);\n \n \t\treturn composite;\n@@ -202,6 +215,11 @@ private Control createIconDecoratorPage(Composite parent) {\n \t\tComposite imageGroup = SWTUtils.createHVFillComposite(parent,\n \t\t\t\tSWTUtils.MARGINS_DEFAULT, 2);\n \n+\t\tshowTracked = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowTracked);\n+\t\tshowUntracked = SWTUtils.createCheckBox(imageGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_iconsShowUntracked);\n+\n \t\treturn imageGroup;\n \t}\n \n@@ -241,6 +259,9 @@ public void handleEvent(Event event) {\n \tprivate void initializeValues() {\n \t\tfinal IPreferenceStore store = getPreferenceStore();\n \n+\t\tcomputeDeepDirtyState.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\n \t\tfileTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n \t\tfolderTextFormat.setText(store\n@@ -248,8 +269,20 @@ private void initializeValues() {\n \t\tprojectTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n \n-\t\tshowDirty.setSelection(store\n-\t\t\t\t.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\tshowTracked.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));\n+\t\tshowUntracked.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n+\n+\t\tSelectionListener selectionListener = new SelectionAdapter() {\n+\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\tpreview.refresh();\n+\t\t\t}\n+\t\t};\n+\n+\t\tcomputeDeepDirtyState.addSelectionListener(selectionListener);\n+\t\tshowTracked.addSelectionListener(selectionListener);\n+\t\tshowUntracked.addSelectionListener(selectionListener);\n \n \t\tsetValid(true);\n \t}\n@@ -287,6 +320,9 @@ public boolean performOk() {\n \t */\n \tprivate boolean performOk(IPreferenceStore store) {\n \n+\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY,\n+\t\t\t\tcomputeDeepDirtyState.getSelection());\n+\n \t\tstore.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tfileTextFormat.getText());\n \t\tstore.setValue(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,\n@@ -294,8 +330,10 @@ private boolean performOk(IPreferenceStore store) {\n \t\tstore.setValue(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,\n \t\t\t\tprojectTextFormat.getText());\n \n-\t\tstore.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY, showDirty\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, showTracked\n \t\t\t\t.getSelection());\n+\t\tstore.setValue(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON,\n+\t\t\t\tshowUntracked.getSelection());\n \n \t\treturn true;\n \t}\n@@ -308,6 +346,9 @@ protected void performDefaults() {\n \t\tsuper.performDefaults();\n \t\tIPreferenceStore store = getPreferenceStore();\n \n+\t\tcomputeDeepDirtyState.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\n \t\tfileTextFormat.setText(store\n \t\t\t\t.getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n \t\tfolderTextFormat\n@@ -317,8 +358,11 @@ protected void performDefaults() {\n \t\t\t\t.setText(store\n \t\t\t\t\t\t.getDefaultString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));\n \n-\t\tshowDirty.setSelection(store\n-\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));\n+\t\tshowTracked.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));\n+\t\tshowUntracked\n+\t\t\t\t.setSelection(store\n+\t\t\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));\n \t}\n \n \t/**\n@@ -602,19 +646,26 @@ private PreviewDecoration getDecoration(Object element) {\n \t}\n \n \tprivate static class PreviewResource implements IDecoratableResource {\n-\t\tpublic final String name;\n+\t\tprivate final String name;\n+\n+\t\tprivate final String branch;\n \n-\t\tpublic final String branch;\n+\t\tprivate final int type;\n \n-\t\tpublic final int type;\n+\t\tprivate Collection children;\n \n-\t\tpublic Collection children;\n+\t\tprivate boolean tracked;\n \n-\t\tpublic PreviewResource(String name, int type, String branch) {\n+\t\tprivate boolean ignored;\n+\n+\t\tpublic PreviewResource(String name, int type, String branch,\n+\t\t\t\tboolean tracked, boolean ignored) {\n \t\t\tthis.name = name;\n \t\t\tthis.branch = branch;\n \t\t\tthis.type = type;\n \t\t\tthis.children = Collections.EMPTY_LIST;\n+\t\t\tthis.tracked = tracked;\n+\t\t\tthis.ignored = ignored;\n \t\t}\n \n \t\tpublic String getName() {\n@@ -628,6 +679,14 @@ public int getType() {\n \t\tpublic String getBranch() {\n \t\t\treturn branch;\n \t\t}\n+\n+\t\tpublic boolean isTracked() {\n+\t\t\treturn tracked;\n+\t\t}\n+\n+\t\tpublic boolean isIgnored() {\n+\t\t\treturn ignored;\n+\t\t}\n \t}\n \n \tprivate class PreviewDecoration implements IDecoration {\ndiff --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\nindex d050be9..9940177 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -369,4 +369,6 @@ DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and\n DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:\n DecoratorPreferencesPage_textLabel=T&ext Decorations\n DecoratorPreferencesPage_iconLabel=&Icon Decorations\n+DecoratorPreferencesPage_iconsShowTracked=Tracked resources\n+DecoratorPreferencesPage_iconsShowUntracked=Untracked resources\n \n-- \n1.6.1.2.309.g2ea3\n"},{"id":"104315","messageId":"200902120102.28171.robin.rosenberg@dewire.com","threadId":"17728","inReplyTo":"1234377614-23798-13-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH v2 12/12] Implement label decorations for folders and projects","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-02-12T00:02:27Z","receivedAt":"2009-02-12T00:02:27Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nSince this is a large change, we can wait a day or so before pushing to master. This and\nShawn locking patch is in the update site on www.jgit.org (tentative builds) for those that want\nto experience it without building. The version is called 0.4.0.200902120052.\n\n-- robin\n"},{"id":"105005","messageId":"200902162157.28316.robin.rosenberg@dewire.com","threadId":"17728","inReplyTo":"1234377614-23798-1-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH v2 00/12] Support customizable label decorations","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-02-16T20:57:28Z","receivedAt":"2009-02-16T20:57:28Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdag 11 februari 2009 19:40:02 skrev Tor Arne Vestbø <torarnv@gmail.com>:\n> Known issues are:\n> \n>   - If a project has a repository more than one level above the\n>     project directory decorations will fail.\n\nThat one *is* annoying. My biggest projects don't get decorations now...\n\n-- robin\n"},{"id":"105022","messageId":"4999ED97.7050502@gmail.com","threadId":"17728","inReplyTo":"200902162157.28316.robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH v2 00/12] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-16T22:49:59Z","receivedAt":"2009-02-16T22:49:59Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Robin Rosenberg wrote:\n> onsdag 11 februari 2009 19:40:02 skrev Tor Arne Vestbø <torarnv@gmail.com>:\n>> Known issues are:\n>>\n>>   - If a project has a repository more than one level above the\n>>     project directory decorations will fail.\n> \n> That one *is* annoying. My biggest projects don't get decorations now...\n\nOuch. I thought that was a rather rare situation, given that we only let\nusers create new repos either in the project or the project parent.\n\nI'll whip something up :)\n\nHow has the series held up this past week in the update manager? Any\ncomplaints from users or other things I should be aware of?\n\nTor Arne\n"},{"id":"105078","messageId":"200902170652.36701.robin.rosenberg@dewire.com","threadId":"17728","inReplyTo":"4999ED97.7050502@gmail.com","subject":"Re: [EGIT PATCH v2 00/12] Support customizable label decorations","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-02-17T05:52:36Z","receivedAt":"2009-02-17T05:52:36Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"måndag 16 februari 2009 23:49:59 skrev Tor Arne Vestbø <torarnv@gmail.com>:\n> Robin Rosenberg wrote:\n> > onsdag 11 februari 2009 19:40:02 skrev Tor Arne Vestbø <torarnv@gmail.com>:\n> >> Known issues are:\n> >>\n> >>   - If a project has a repository more than one level above the\n> >>     project directory decorations will fail.\n> > \n> > That one *is* annoying. My biggest projects don't get decorations now...\n> \n> Ouch. I thought that was a rather rare situation, given that we only let\n> users create new repos either in the project or the project parent.\nLegacy, but some reasons make sense.\n\n> I'll whip something up :)\n> \n> How has the series held up this past week in the update manager? Any\n642 or 461 downloads (depending on how you count). It's hard to say if different\nif some downloads are from multiple or single users.\n\n> complaints from users or other things I should be aware of?\nOne :)\n\n-- robin\n"},{"id":"105189","messageId":"1234893101-23143-1-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"200902170652.36701.robin.rosenberg@dewire.com","subject":"[EGIT PATCH 13/12] Add new file tree iterator that can adapt into a ContainerTreeIterator","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-17T17:51:41Z","receivedAt":"2009-02-17T17:51:41Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"As the iterator recurses into subtrees it will look for directories\nthat can be mapped using a given workspace root, and if there's a\nmapping it will return a ContainerTreeIterator instead.\n\nThis feature required exposing the underlying file of the FileEntry,\nas well as making ContainerTreeIterator's constructor that takes a\nparent iterator public.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../core/T0003_AdaptableFileTreeIteratorTest.java  |   74 +++++++++++++++++\n .../egit/core/AdaptableFileTreeIterator.java       |   87 ++++++++++++++++++++\n .../spearce/egit/core/ContainerTreeIterator.java   |   17 ++++-\n .../spearce/jgit/treewalk/FileTreeIterator.java    |   14 +++-\n 4 files changed, 190 insertions(+), 2 deletions(-)\n create mode 100644 org.spearce.egit.core.test/src/org/spearce/egit/core/T0003_AdaptableFileTreeIteratorTest.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/AdaptableFileTreeIterator.java\n\ndiff --git a/org.spearce.egit.core.test/src/org/spearce/egit/core/T0003_AdaptableFileTreeIteratorTest.java b/org.spearce.egit.core.test/src/org/spearce/egit/core/T0003_AdaptableFileTreeIteratorTest.java\nnew file mode 100644\nindex 0000000..1e2fe03\n--- /dev/null\n+++ b/org.spearce.egit.core.test/src/org/spearce/egit/core/T0003_AdaptableFileTreeIteratorTest.java\n@@ -0,0 +1,74 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core;\n+\n+import java.io.File;\n+import java.io.FileWriter;\n+import java.io.IOException;\n+import java.util.Collections;\n+import java.util.Set;\n+\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IWorkspaceRoot;\n+import org.spearce.egit.core.op.ConnectProviderOperation;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.egit.core.test.GitTestCase;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.WorkingTreeIterator;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n+\n+public class T0003_AdaptableFileTreeIteratorTest extends GitTestCase {\n+\n+\tprivate Repository repository;\n+\n+\tprivate File repositoryRoot;\n+\n+\tprivate File file;\n+\n+\tprotected void setUp() throws Exception {\n+\t\tsuper.setUp();\n+\n+\t\trepository = new Repository(gitDir);\n+\t\trepositoryRoot = repository.getWorkDir();\n+\t\trepository.create();\n+\n+\t\tfile = new File(project.getProject().getLocation().toFile(), \"a.txt\");\n+\t\tfinal FileWriter fileWriter = new FileWriter(file);\n+\t\tfileWriter.write(\"aaaaaaaaaaa\");\n+\t\tfileWriter.close();\n+\n+\t\tfinal ConnectProviderOperation operation = new ConnectProviderOperation(\n+\t\t\t\tproject.getProject(), null);\n+\t\toperation.run(null);\n+\t}\n+\n+\tpublic void testFileTreeToContainerAdaptation() throws IOException {\n+\t\tfinal IWorkspaceRoot root = project.getProject().getWorkspace()\n+\t\t\t\t.getRoot();\n+\n+\t\tfinal TreeWalk treeWalk = new TreeWalk(repository);\n+\t\ttreeWalk.addTree(new AdaptableFileTreeIterator(repositoryRoot, root));\n+\t\ttreeWalk.setRecursive(true);\n+\n+\t\tfinal IFile eclipseFile = project.getProject().getFile(file.getName());\n+\t\tfinal RepositoryMapping mapping = RepositoryMapping\n+\t\t\t\t.getMapping(eclipseFile);\n+\t\tfinal Set<String> repositoryPaths = Collections.singleton(mapping\n+\t\t\t\t.getRepoRelativePath(eclipseFile));\n+\n+\t\tassertTrue(repositoryPaths.size() == 1);\n+\t\ttreeWalk.setFilter(PathFilterGroup.createFromStrings(repositoryPaths));\n+\n+\t\tassertTrue(treeWalk.next());\n+\n+\t\tfinal WorkingTreeIterator iterator = treeWalk.getTree(1,\n+\t\t\t\tWorkingTreeIterator.class);\n+\t\tassertTrue(iterator instanceof ContainerTreeIterator);\n+\t}\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/AdaptableFileTreeIterator.java b/org.spearce.egit.core/src/org/spearce/egit/core/AdaptableFileTreeIterator.java\nnew file mode 100644\nindex 0000000..61211f6\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/AdaptableFileTreeIterator.java\n@@ -0,0 +1,87 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+\n+package org.spearce.egit.core;\n+\n+import java.io.File;\n+import java.io.IOException;\n+\n+import org.eclipse.core.resources.IContainer;\n+import org.eclipse.core.resources.IWorkspaceRoot;\n+import org.eclipse.core.runtime.Path;\n+import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.treewalk.AbstractTreeIterator;\n+import org.spearce.jgit.treewalk.FileTreeIterator;\n+\n+/**\n+ * Java IO file tree iterator that can adapt to a {@link ContainerTreeIterator}\n+ * <p>\n+ * The iterator automatically adapts to a {@link ContainerTreeIterator} when\n+ * recursing into directories that are accessible from the given workspace root.\n+ *\n+ * @see org.spearce.jgit.treewalk.FileTreeIterator\n+ * @see org.spearce.egit.core.ContainerTreeIterator\n+ */\n+public class AdaptableFileTreeIterator extends FileTreeIterator {\n+\n+\tIWorkspaceRoot root;\n+\n+\t/**\n+\t * Create a new iterator to traverse the given directory and its children\n+\t * <p>\n+\t * The iterator will automatically adapt to a {@link ContainerTreeIterator}\n+\t * when encountering directories what can be mapped into the given workspace\n+\t * root.\n+\t *\n+\t * @param path\n+\t *            the starting directory. This directory should correspond to\n+\t *            the repository root.\n+\t * @param workspaceRoot\n+\t *            the workspace root to check resource mapping against.\n+\t *\n+\t */\n+\tpublic AdaptableFileTreeIterator(final File path,\n+\t\t\tfinal IWorkspaceRoot workspaceRoot) {\n+\t\tsuper(path);\n+\t\troot = workspaceRoot;\n+\t}\n+\n+\t/**\n+\t * Create a new iterator to traverse a subdirectory.\n+\t * <p>\n+\t * The iterator will automatically adapt to a {@link ContainerTreeIterator}\n+\t * when encountering directories what can be mapped into the given workspace\n+\t * root.\n+\t *\n+\t * @param path\n+\t *            the subdirectory. This should be a directory contained within\n+\t *            the parent directory.\n+\t * @param parent\n+\t *            the parent iterator we were created from.\n+\t * @param workspaceRoot\n+\t *            the workspace root to check resource mapping against.\n+\t */\n+\tprotected AdaptableFileTreeIterator(final AdaptableFileTreeIterator parent,\n+\t\t\tFile path, final IWorkspaceRoot workspaceRoot) {\n+\t\tsuper(parent, path);\n+\t\troot = workspaceRoot;\n+\t}\n+\n+\t@Override\n+\tpublic AbstractTreeIterator createSubtreeIterator(Repository repo)\n+\t\t\tthrows IncorrectObjectTypeException, IOException {\n+\t\tfinal File currentFile = ((FileEntry) current()).getFile();\n+\t\tfinal IContainer[] containers = root.findContainersForLocation(new Path(\n+\t\t\t\tcurrentFile.getAbsolutePath()));\n+\t\tif (containers.length > 0)\n+\t\t\treturn new ContainerTreeIterator(this, containers[0]);\n+\t\telse\n+\t\t\treturn new AdaptableFileTreeIterator(this, currentFile, root);\n+\t}\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java b/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\nindex 2403252..de592aa 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ContainerTreeIterator.java\n@@ -88,7 +88,22 @@ public ContainerTreeIterator(final IWorkspaceRoot root) {\n \t\tinit(entries());\n \t}\n \n-\tprivate ContainerTreeIterator(final WorkingTreeIterator p,\n+\t/**\n+\t * Construct a new iterator from a container in the workspace, with a given\n+\t * parent iterator.\n+\t * <p>\n+\t * The iterator will support traversal over the named container, but only if\n+\t * it is contained within a project which has the Git repository provider\n+\t * connected and this resource is mapped into a Git repository. During the\n+\t * iteration the paths will be automatically generated to match the proper\n+\t * repository paths for this container's children.\n+\t * \n+\t * @param p\n+\t *            the parent iterator we were created from.\n+\t * @param base\n+\t *            the part of the workspace the iterator will walk over.\n+\t */\n+\tpublic ContainerTreeIterator(final WorkingTreeIterator p,\n \t\t\tfinal IContainer base) {\n \t\tsuper(p);\n \t\tnode = base;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/FileTreeIterator.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/FileTreeIterator.java\nindex 2c71151..ef9866a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/FileTreeIterator.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/FileTreeIterator.java\n@@ -100,7 +100,10 @@ public AbstractTreeIterator createSubtreeIterator(final Repository repo)\n \t\treturn r;\n \t}\n \n-\tstatic class FileEntry extends Entry {\n+\t/**\n+\t * Wrapper for a standard Java IO file\n+\t */\n+\tstatic public class FileEntry extends Entry {\n \t\tfinal File file;\n \n \t\tprivate final FileMode mode;\n@@ -151,5 +154,14 @@ public long getLastModified() {\n \t\tpublic InputStream openInputStream() throws IOException {\n \t\t\treturn new FileInputStream(file);\n \t\t}\n+\n+\t\t/**\n+\t\t * Get the underlying file of this entry.\n+\t\t * \n+\t\t * @return the underlying file of this entry\n+\t\t */\n+\t\tpublic File getFile() {\n+\t\t\treturn file;\n+\t\t}\n \t}\n }\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"105190","messageId":"1234893162-23254-1-git-send-email-torarnv@gmail.com","threadId":"17728","inReplyTo":"1234893101-23143-1-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 14/12] Allow project decorations regardless of repository root location","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-17T17:52:42Z","receivedAt":"2009-02-17T17:52:42Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"If the repository root is more than one level above the project\ndirectory we use the AdaptableFileTreeIterator to recurse the\nrepository tree until we find a directory that can be mapped\nto a container in the workspace. The iterator is then adapted to\na ContainerTreeIterator and decorations are applied like usual.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../decorators/DecoratableResourceAdapter.java     |   39 +++++++++++++-------\n 1 files changed, 26 insertions(+), 13 deletions(-)\n\ndiff --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\nindex e2fe54b..5c68d5b 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n@@ -13,15 +13,17 @@\n \n package org.spearce.egit.ui.internal.decorators;\n \n+import java.io.File;\n import java.io.IOException;\n import java.util.Collections;\n import java.util.Set;\n \n import org.eclipse.core.resources.IProject;\n import org.eclipse.core.resources.IResource;\n-import org.eclipse.core.resources.IWorkspace;\n+import org.eclipse.core.resources.IWorkspaceRoot;\n import org.eclipse.jface.preference.IPreferenceStore;\n import org.eclipse.team.core.Team;\n+import org.spearce.egit.core.AdaptableFileTreeIterator;\n import org.spearce.egit.core.ContainerTreeIterator;\n import org.spearce.egit.core.ContainerTreeIterator.ResourceEntry;\n import org.spearce.egit.core.project.RepositoryMapping;\n@@ -39,6 +41,7 @@\n import org.spearce.jgit.revwalk.RevWalk;\n import org.spearce.jgit.treewalk.EmptyTreeIterator;\n import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.WorkingTreeIterator;\n import org.spearce.jgit.treewalk.filter.AndTreeFilter;\n import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n import org.spearce.jgit.treewalk.filter.TreeFilter;\n@@ -210,13 +213,21 @@ public boolean include(TreeWalk treeWalk)\n \t\t}\n \n \t\tprivate boolean shouldRecurse(TreeWalk treeWalk) {\n-\t\t\tfinal ContainerTreeIterator workspaceIterator = treeWalk.getTree(\n-\t\t\t\t\tT_WORKSPACE, ContainerTreeIterator.class);\n-\t\t\tfinal ResourceEntry resourceEntry = workspaceIterator != null ? workspaceIterator\n-\t\t\t\t\t.getResourceEntry()\n-\t\t\t\t\t: null;\n-\t\t\tIResource visitingResource = resourceEntry.getResource();\n+\t\t\tfinal WorkingTreeIterator workspaceIterator = treeWalk.getTree(\n+\t\t\t\t\tT_WORKSPACE, WorkingTreeIterator.class);\n+\n+\t\t\tif (workspaceIterator instanceof AdaptableFileTreeIterator)\n+\t\t\t\treturn true;\n+\n+\t\t\tResourceEntry resourceEntry = null;\n+\t\t\tif (workspaceIterator != null)\n+\t\t\t\tresourceEntry = ((ContainerTreeIterator) workspaceIterator)\n+\t\t\t\t\t\t.getResourceEntry();\n+\n+\t\t\tif (resourceEntry == null)\n+\t\t\t\treturn true;\n \n+\t\t\tIResource visitingResource = resourceEntry.getResource();\n \t\t\tif (targetDepth == -1) {\n \t\t\t\tif (visitingResource.equals(resource)\n \t\t\t\t\t\t|| visitingResource.getParent().equals(resource))\n@@ -319,14 +330,16 @@ private TreeWalk createThreeWayTreeWalk() throws IOException {\n \n \t\t// Working directory\n \t\tIProject project = resource.getProject();\n-\t\tIWorkspace workspace = resource.getWorkspace();\n-\t\tif (repository.getWorkDir().equals(project.getLocation().toFile()))\n+\t\tIWorkspaceRoot workspaceRoot = resource.getWorkspace().getRoot();\n+\t\tFile repoRoot = repository.getWorkDir();\n+\n+\t\tif (repoRoot.equals(project.getLocation().toFile()))\n \t\t\ttreeWalk.addTree(new ContainerTreeIterator(project));\n+\t\telse if (repoRoot.equals(workspaceRoot.getLocation().toFile()))\n+\t\t\ttreeWalk.addTree(new ContainerTreeIterator(workspaceRoot));\n \t\telse\n-\t\t\ttreeWalk.addTree(new ContainerTreeIterator(workspace.getRoot()));\n-\n-\t\t// TODO: Add fallback for projects with the repository more than\n-\t\t// one parent up, for example by using a stack of DummyIterators\n+\t\t\ttreeWalk.addTree(new AdaptableFileTreeIterator(repoRoot,\n+\t\t\t\t\tworkspaceRoot));\n \n \t\treturn treeWalk;\n \t}\n-- \n1.6.1.2.309.g2ea3\n"}]}