{"thread":{"id":"17574","subject":"[EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners","startedAt":"2009-02-05T01:00:07Z","lastAt":"2009-02-05T22:09:10Z","messageCount":31,"participants":["Tor Arne Vestbø","Shawn O. Pearce","Robin Rosenberg"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"103251","messageId":"1233795618-20249-1-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":null,"subject":"[EGIT PATCH 00/11] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:07Z","receivedAt":"2009-02-05T01:00:07Z","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\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'Inspect dirty state...', controls whether decoration of container\nelements such as projects and folders should traverse child elements\nto decide if the container is dirty.\n\nDisabling these options will improve performance for large trees.\n\nThe code should be solid enough for normal use, but I may have\nmissed situations that the code does not handle -- resuling in\ncrash and burn. If so, please 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 parent package in the\n    package hierarcy will appear dirty, even when the layout is\n    set to 'flat'.\n\nI've sprinkled the code with TODOs where I found possible future\nimprovments. One such improvment is performance, where for example\nrefactoring to use one shared status cache should help.\n\nTor Arne\n\nPS: This is my first major patch to EGit, so apologies in advance\nif I messed up the steps of the submit process in any way :)\n\n\nTor Arne VestbÃ¸ (11):\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 decorations of dirty, staged, and conflicting resources\n  Don't decorate every single resource on repository change\n  Implement label decorations for folders and projects\n\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    5 +-\n .../src/org/spearce/egit/core/GitException.java    |  168 +++\n .../core/internal/util/ExceptionCollector.java     |  128 +++\n .../spearce/egit/core/project/GitProjectData.java  |   33 +-\n org.spearce.egit.ui/.options                       |    8 +-\n org.spearce.egit.ui/icons/ovr/assumevalid.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/conflict.gif         |  Bin 64 -> 164 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 -> 114 bytes\n org.spearce.egit.ui/icons/ovr/staged_removed.gif   |  Bin 0 -> 114 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         |   70 ++-\n .../egit/ui/PluginPreferenceInitializer.java       |   13 +\n .../src/org/spearce/egit/ui/UIIcons.java           |   19 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |   19 +\n .../src/org/spearce/egit/ui/UIText.java            |   87 ++-\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/GitLightweightDecorator.java        | 1062 ++++++++++++++++++++\n .../internal/decorators/GitResourceDecorator.java  |  454 ---------\n .../internal/decorators/IDecoratableResource.java  |   93 ++\n .../preferences/GitDecoratorPreferencePage.java    |  911 +++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   35 +-\n 30 files changed, 3230 insertions(+), 495 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 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/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":"103250","messageId":"1233795618-20249-2-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-1-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 01/11] Add support code to handle plugin property changes","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:08Z","receivedAt":"2009-02-05T01:00:08Z","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..d4a9e8e 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 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 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 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":"103248","messageId":"1233795618-20249-3-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-2-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:09Z","receivedAt":"2009-02-05T01:00:09Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Also, add method for removing listeners, and remove unused\nprivate method for getting the current listeners.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n .../spearce/egit/core/project/GitProjectData.java  |   33 +++++++++-----------\n 1 files changed, 15 insertions(+), 18 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..4ca4f8e 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,8 @@ 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}\n-\n-\tprivate static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {\n-\t\treturn repositoryChangeListeners;\n+\t\tfor (RepositoryChangeListener listener : repositoryChangeListeners)\n+\t\t\tlistener.repositoryChanged(which);\n \t}\n \n \t/**\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"103249","messageId":"1233795618-20249-4-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-3-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 03/11] Add a specialized team exception for Git","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:10Z","receivedAt":"2009-02-05T01:00:10Z","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":"103252","messageId":"1233795618-20249-5-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-4-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 04/11] Add new class ExceptionCollector for grouping exceptions","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:11Z","receivedAt":"2009-02-05T01:00:11Z","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":"103257","messageId":"1233795618-20249-6-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-5-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 05/11] Add new class SWTUtils with helper-methods for creating controls","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:12Z","receivedAt":"2009-02-05T01:00:12Z","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":"103258","messageId":"1233795618-20249-7-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-6-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:13Z","receivedAt":"2009-02-05T01:00:13Z","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    |  735 ++++++++++++++++++++\n .../src/org/spearce/egit/ui/uitext.properties      |   25 +-\n 14 files changed, 1438 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 d4a9e8e..9d03c70 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 7a7d3ef..23498c8 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@@ -913,6 +910,66 @@\n \t/** */\n \tpublic static String BranchSelectionDialog_Refs;\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..2ef0292\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java\n@@ -0,0 +1,735 @@\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.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.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 fPreview;\n+\n+\tprivate static final Collection PREVIEW_FILESYSTEM_ROOT;\n+\n+\tprivate static ThemeListener fThemeListener;\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+\t\tComposite headerGroup = new Composite(parent, SWT.NULL);\n+\t\tGridLayout layout = new GridLayout();\n+\t\tlayout.marginWidth = 0;\n+\t\tlayout.marginHeight = 0;\n+\t\tlayout.verticalSpacing = 10;\n+\t\theaderGroup.setLayout(layout);\n+\t\tGridData data = new GridData();\n+\t\tdata.horizontalAlignment = GridData.FILL;\n+\t\theaderGroup.setLayoutData(data);\n+\n+\t\tSWTUtils.createPreferenceLink(\n+\t\t\t\t(IWorkbenchPreferenceContainer) getContainer(), headerGroup,\n+\t\t\t\t\"org.eclipse.ui.preferencePages.Decorators\",\n+\t\t\t\tUIText.DecoratorPreferencesPage_labelDecorationsLink); //$NON-NLS-1$\n+\n+\t\tSWTUtils.createLabel(headerGroup,\n+\t\t\t\tUIText.DecoratorPreferencesPage_decorationSettings);\n+\n+\t\tTabFolder tabFolder = new TabFolder(parent, SWT.NONE);\n+\t\tGridData tabData = SWTUtils.createHVFillGridData();\n+\t\ttabData.heightHint = 100;\n+\t\ttabFolder.setLayoutData(tabData);\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\tfPreview = new Preview(parent);\n+\t\tfPreview.refresh();\n+\n+\t\t// TODO: Add help text for this preference page\n+\n+\t\tfThemeListener = new ThemeListener(fPreview);\n+\t\tPlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(\n+\t\t\t\tfThemeListener);\n+\t\tDialog.applyDialogFont(parent);\n+\t\treturn tabFolder;\n+\t}\n+\n+\tprivate Control createGeneralDecoratorPage(Composite parent) {\n+\t\tComposite composite = new Composite(parent, SWT.NULL);\n+\n+\t\tGridLayout layout = new GridLayout();\n+\t\tcomposite.setLayout(layout);\n+\t\tGridData data = new GridData();\n+\t\tdata.horizontalAlignment = GridData.FILL;\n+\t\tcomposite.setLayoutData(data);\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 = new Composite(parent, SWT.NULL);\n+\t\tGridLayout layout = new GridLayout();\n+\t\tlayout.numColumns = 3;\n+\t\tfileTextGroup.setLayout(layout);\n+\t\tGridData data = new GridData();\n+\t\tdata.horizontalAlignment = GridData.FILL;\n+\t\tfileTextGroup.setLayoutData(data);\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 = new Composite(parent, SWT.NULL);\n+\t\tGridLayout layout = new GridLayout();\n+\t\timageGroup.setLayout(layout);\n+\t\tGridData data = new GridData();\n+\t\tdata.horizontalAlignment = GridData.FILL;\n+\t\timageGroup.setLayoutData(data);\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(fThemeListener);\n+\t\tsuper.dispose();\n+\t}\n+\n+\tprivate static class ThemeListener implements IPropertyChangeListener {\n+\t\tprivate final Preview preview;\n+\n+\t\tThemeListener(Preview preview) {\n+\t\t\tthis.preview = preview;\n+\t\t}\n+\n+\t\tpublic void propertyChange(PropertyChangeEvent event) {\n+\t\t\tpreview.refresh();\n+\t\t}\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 (fPreview != null)\n+\t\t\tfPreview.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+\t\t\tfViewer = new TreeViewer(composite);\n+\t\t\tGridData previewGridData = SWTUtils.createHVFillGridData();\n+\t\t\tpreviewGridData.heightHint = 100;\n+\t\t\tfViewer.getControl().setLayoutData(previewGridData);\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 a86e58b..4a0a387 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@@ -340,3 +339,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":"103253","messageId":"1233795618-20249-8-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-7-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 07/11] Add binding for name of the current branch","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:14Z","receivedAt":"2009-02-05T01:00:14Z","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 23498c8..f939558 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@@ -956,6 +956,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 2ef0292..2a1a3a8 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@@ -94,10 +94,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@@ -488,6 +488,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@@ -510,8 +512,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@@ -635,12 +637,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@@ -652,6 +657,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 4a0a387..58cb9e6 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@@ -354,10 +354,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":"103256","messageId":"1233795618-20249-9-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-8-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 08/11] Add icon decoration for tracked and untracked resources","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:15Z","receivedAt":"2009-02-05T01:00:15Z","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 1697542..0cead29 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@@ -77,7 +77,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 f939558..fe62d0a 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@@ -973,6 +973,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 2a1a3a8..2cbf07a 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@@ -84,7 +87,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 fPreview;\n \n@@ -94,10 +101,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@@ -171,7 +184,7 @@ private Control createGeneralDecoratorPage(Composite parent) {\n \t\tdata.horizontalAlignment = GridData.FILL;\n \t\tcomposite.setLayoutData(data);\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@@ -222,6 +235,11 @@ private Control createIconDecoratorPage(Composite parent) {\n \t\tdata.horizontalAlignment = GridData.FILL;\n \t\timageGroup.setLayoutData(data);\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@@ -261,6 +279,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@@ -268,8 +289,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@@ -307,6 +340,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@@ -314,8 +350,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@@ -328,6 +366,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@@ -337,8 +378,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@@ -635,19 +679,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@@ -661,6 +712,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 58cb9e6..05fdba9 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@@ -363,4 +363,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":"103255","messageId":"1233795618-20249-10-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-9-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 09/11] Implement decorations of dirty, staged, and conflicting resources","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:16Z","receivedAt":"2009-02-05T01:00:16Z","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 org.spearce.egit.ui/icons/ovr/assumevalid.gif      |  Bin 64 -> 0 bytes\n org.spearce.egit.ui/icons/ovr/conflict.gif         |  Bin 64 -> 164 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 -> 114 bytes\n org.spearce.egit.ui/icons/ovr/staged_removed.gif   |  Bin 0 -> 114 bytes\n .../egit/ui/PluginPreferenceInitializer.java       |    2 +\n .../src/org/spearce/egit/ui/UIIcons.java           |   13 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +\n .../src/org/spearce/egit/ui/UIText.java            |   14 +-\n .../decorators/GitLightweightDecorator.java        |  284 ++++++++++++++++----\n .../internal/decorators/IDecoratableResource.java  |   39 +++\n .../preferences/GitDecoratorPreferencePage.java    |  131 ++++++++--\n .../src/org/spearce/egit/ui/uitext.properties      |   14 +-\n 15 files changed, 427 insertions(+), 74 deletions(-)\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/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..186345711bb5443b440c4265fe62b099cce5def0 100644\nGIT binary patch\nliteral 164\nzcmZ?wbhEHbWM|-DIKseS;8EwAJi|S8rcc&9znlfG$<y4^W_f1J^~{*<m%Cuej;o77\nz=*sb{&yHVx`snqS|6ssCG*JA>!pOiN$e;t#0kV^URWU)OPmsZ3MW&ETlG9;j{ud8T\nWc$>JmHcJQuD+C104Qdczum%A1Q!l9i\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..f2b87e274667c441897d0853e6a437ba5487f51c\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{QC}7o_(m`FiYy3\nRK~byn9^Qv66>=^N)&TbU9;*NV\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..b6b4911d9eba72838d54fbcf6109eaf3836bf0db\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{QC~|wl_0qWLX?O\nRA=-DKlJ_A?g`5k6H304c9%KLj\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..ef886cf 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,8 @@ 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 \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 0cead29..302637e 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@@ -75,8 +79,9 @@\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\");\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..409b335 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,10 @@\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 \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 fe62d0a..b62ca4c 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@@ -953,12 +953,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@@ -979,6 +985,12 @@\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 \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..aa6a261 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,18 @@ 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 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 +222,161 @@ 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} 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 +398,18 @@ 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 \t}\n \n \t/**\n@@ -298,6 +427,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 +460,23 @@ 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\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}\n \n \t\t/**\n@@ -354,6 +502,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 +530,44 @@ 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\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 +584,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 +599,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 +615,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 +704,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..d2b6b5c 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,27 @@\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 }\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 2cbf07a..74bc209 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@@ -56,6 +56,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@@ -72,8 +73,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@@ -93,6 +95,10 @@\n \n \tprivate Button showUntracked;\n \n+\tprivate Button showStaged;\n+\n+\tprivate Button showConflicts;\n+\n \tprivate Preview fPreview;\n \n \tprivate static final Collection PREVIEW_FILESYSTEM_ROOT;\n@@ -101,16 +107,29 @@\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); //$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)); //$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)); //$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)); //$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)); //$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)); //$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)); //$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)); //$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)); //$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)); //$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)); //$NON-NLS-1$\n \t\tproject.children = children;\n \t\tPREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);\n \t}\n@@ -179,6 +198,7 @@ private Control createGeneralDecoratorPage(Composite parent) {\n \t\tComposite composite = new Composite(parent, SWT.NULL);\n \n \t\tGridLayout layout = new GridLayout();\n+\t\tlayout.marginHeight = 10;\n \t\tcomposite.setLayout(layout);\n \t\tGridData data = new GridData();\n \t\tdata.horizontalAlignment = GridData.FILL;\n@@ -206,22 +226,29 @@ private Control createTextDecoratorPage(Composite parent) {\n \t\tdata.horizontalAlignment = GridData.FILL;\n \t\tfileTextGroup.setLayoutData(data);\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@@ -230,6 +257,7 @@ private Control createTextDecoratorPage(Composite parent) {\n \tprivate Control createIconDecoratorPage(Composite parent) {\n \t\tComposite imageGroup = new Composite(parent, SWT.NULL);\n \t\tGridLayout layout = new GridLayout();\n+\t\tlayout.marginHeight = 10;\n \t\timageGroup.setLayout(layout);\n \t\tGridData data = new GridData();\n \t\tdata.horizontalAlignment = GridData.FILL;\n@@ -239,17 +267,27 @@ 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 \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@@ -293,6 +331,10 @@ 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 \n \t\tSelectionListener selectionListener = new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(SelectionEvent e) {\n@@ -303,6 +345,8 @@ public void widgetSelected(SelectionEvent e) {\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 \n \t\tsetValid(true);\n \t}\n@@ -354,6 +398,10 @@ 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 \n \t\treturn true;\n \t}\n@@ -383,6 +431,11 @@ 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}\n \n \t/**\n@@ -505,7 +558,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@@ -518,7 +575,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@@ -531,7 +592,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@@ -691,14 +756,25 @@ 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\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) {\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}\n \n \t\tpublic String getName() {\n@@ -720,6 +796,18 @@ 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 \t}\n \n \tprivate class PreviewDecoration implements IDecoration {\n@@ -736,12 +824,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 05fdba9..2762d11 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@@ -352,13 +352,15 @@ 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@@ -366,3 +368,7 @@ DecoratorPreferencesPage_iconLabel=&Icon Decorations\n DecoratorPreferencesPage_iconsShowTracked=Tracked resources\n DecoratorPreferencesPage_iconsShowUntracked=Untracked resources\n \n+Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources.\n+\n+DecoratorPreferencesPage_iconsShowStaged=Staged resources\n+DecoratorPreferencesPage_iconsShowConflicts=Conflicting resources\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"103254","messageId":"1233795618-20249-11-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-10-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 10/11] Don't decorate every single resource on repository change","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:17Z","receivedAt":"2009-02-05T01:00:17Z","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 aa6a261..45b9f83 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@@ -682,7 +679,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@@ -809,29 +806,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":"103259","messageId":"1233795618-20249-12-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-11-git-send-email-torarnv@gmail.com","subject":"[EGIT PATCH 11/11] Implement label decorations for folders and projects","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:00:18Z","receivedAt":"2009-02-05T01:00:18Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"The option \"Inspect dirty state of children...\" controls\nif the decoration process should look at child resources\nto decide if a container is dirty or not. For large/deep\nprojects this can be quite time consuming.\n\nThe other option, \"Also re-decorate ancestors...\" controls if\nparents of a re-decorated resource also should be updated, for\nexample to signal that the containing folder is now dirty.\n\nSigned-off-by: Tor Arne Vestbø <torarnv@gmail.com>\n---\n org.spearce.egit.ui/.options                       |    8 +-\n .../src/org/spearce/egit/ui/Activator.java         |    2 +-\n .../egit/ui/PluginPreferenceInitializer.java       |    3 +-\n .../src/org/spearce/egit/ui/UIPreferences.java     |    4 +-\n .../src/org/spearce/egit/ui/UIText.java            |    5 +-\n .../decorators/GitLightweightDecorator.java        |  344 +++++++++++++++-----\n .../preferences/GitDecoratorPreferencePage.java    |   33 ++-\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +-\n 8 files changed, 311 insertions(+), 91 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/.options b/org.spearce.egit.ui/.options\nindex a084b35..8fc1c19 100644\n--- a/org.spearce.egit.ui/.options\n+++ b/org.spearce.egit.ui/.options\n@@ -1 +1,7 @@\n-org.spearce.egit.ui/trace/verbose = false\n+# Debugging options for the org.spearce.egit.ui plugin.\n+\n+# Show general verbose output\n+org.spearce.egit.ui/verbose = false\n+\n+# Show debug output for label decorations\n+org.spearce.egit.ui/decorations = false\n\\ No newline at end of file\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 9d03c70..45010ce 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@@ -190,7 +190,7 @@ public Activator() {\n \n \tpublic void start(final BundleContext context) throws Exception {\n \t\tsuper.start(context);\n-\t\ttraceVerbose = isOptionSet(\"/trace/verbose\");\n+\t\ttraceVerbose = isOptionSet(\"/verbose\");\n \t\tsetupSSH(context);\n \t\tsetupProxy(context);\n \t\tsetupRepoChangeScanner();\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 ef886cf..9af35bc 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,8 @@ 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_COMPUTE_DEEP_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,\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 409b335..f0a5e28 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_COMPUTE_DEEP_DIRTY = \"decorator_compute_deep_dirty\";\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 b62ca4c..5dd28df 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@@ -920,7 +920,10 @@\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_computeDeepDirtyState;\n \n \t/** */\n \tpublic static String DecoratorPreferencesPage_description;\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 45b9f83..c72bfdb 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@@ -31,6 +31,7 @@\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.core.runtime.Platform;\n import org.eclipse.jface.preference.IPreferenceStore;\n import org.eclipse.jface.resource.ImageDescriptor;\n import org.eclipse.jface.util.IPropertyChangeListener;\n@@ -48,6 +49,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.ContainerTreeIterator;\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@@ -61,6 +63,8 @@\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.IndexChangedEvent;\n@@ -72,7 +76,11 @@\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+import org.spearce.jgit.util.FS;\n \n /**\n  * Supplies annotations for displayed resources\n@@ -110,6 +118,34 @@\n \t\t\tIStatus.ERROR, Activator.getDefault().getLog());\n \n \t/**\n+\t * Property constant indicating if tracing/debugging of decorations is\n+\t * enabled\n+\t */\n+\tprivate static boolean DEBUG_DECORATIONS = false;\n+\n+\tstatic {\n+\t\tDEBUG_DECORATIONS = \"true\".equals(Platform.getDebugOption(Activator.getPluginId() + \"/decorations\")); //$NON-NLS-1$ //$NON-NLS-2$\n+\t}\n+\n+\tprivate static void debug(String what) {\n+\t\tStackTraceElement frame = Thread.currentThread().getStackTrace()[2];\n+\t\tif (frame.getMethodName().endsWith(\"access$1\"))\n+\t\t\tframe = Thread.currentThread().getStackTrace()[3];\n+\t\tString className = frame.getClassName().substring(\n+\t\t\t\tframe.getClassName().lastIndexOf('.') + 1);\n+\t\tSystem.out.print(className + \"::\" + frame.getMethodName() + \"(): \");\n+\n+\t\tdebug(what, 0);\n+\t}\n+\n+\tprivate static void debug(String what, int numIndents) {\n+\t\tfor (int i = 0; i < numIndents; ++i)\n+\t\t\tSystem.out.print(\"\\t\");\n+\n+\t\tSystem.out.println(what);\n+\t}\n+\n+\t/**\n \t * Constructs a new Git resource decorator\n \t */\n \tpublic GitLightweightDecorator() {\n@@ -174,6 +210,9 @@ public void decorate(Object element, IDecoration decoration) {\n \t\tif (activator == null)\n \t\t\treturn;\n \n+\t\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"deocrating '\" + resource.getLocation() + \"'\");\n+\n \t\ttry {\n \t\t\tDecorationHelper helper = new DecorationHelper(activator\n \t\t\t\t\t.getPreferenceStore());\n@@ -184,7 +223,8 @@ public void decorate(Object element, IDecoration decoration) {\n \t\t}\n \t}\n \n-\tprivate class DecoratableResourceAdapter implements IDecoratableResource {\n+\tprivate static class DecoratableResourceAdapter implements\n+\t\t\tIDecoratableResource {\n \n \t\tprivate final IResource resource;\n \n@@ -212,6 +252,111 @@ public void decorate(Object element, IDecoration decoration) {\n \n \t\tstatic final int T_WORKSPACE = 2;\n \n+\t\tprivate static class ContainerDiffFilter extends TreeFilter {\n+\t\t\tstatic final ContainerDiffFilter INSTANCE = new ContainerDiffFilter();\n+\n+\t\t\t@Override\n+\t\t\tpublic boolean include(final TreeWalk tw)\n+\t\t\t\t\tthrows MissingObjectException,\n+\t\t\t\t\tIncorrectObjectTypeException, IOException {\n+\n+\t\t\t\tif (tw.getFileMode(T_HEAD) == FileMode.MISSING\n+\t\t\t\t\t\t&& tw.getFileMode(T_INDEX) == FileMode.MISSING)\n+\t\t\t\t\treturn false; // Untracked and unstaged, so not dirty\n+\n+\t\t\t\tif (DEBUG_DECORATIONS)\n+\t\t\t\t\tdebug(\"checking '\" + tw.getPathString() + \"'\");\n+\n+\t\t\t\tint mHead = tw.getRawMode(T_HEAD);\n+\t\t\t\tint mIndex = tw.getRawMode(T_INDEX);\n+\t\t\t\tint mWorkspace = tw.getRawMode(T_WORKSPACE);\n+\n+\t\t\t\tif (!FS.INSTANCE.supportsExecute()) {\n+\t\t\t\t\t// We need to clear the executable bits of the head and\n+\t\t\t\t\t// index modes, to prevent false positives when the\n+\t\t\t\t\t// resource is +x in the repository but not on disk\n+\t\t\t\t\tmHead &= (~0111);\n+\t\t\t\t\tmIndex &= (~0111);\n+\t\t\t\t}\n+\n+\t\t\t\tif (DEBUG_DECORATIONS) {\n+\t\t\t\t\tdebug(\"mHead=\" + Integer.toOctalString(mHead) + \" mIndex=\"\n+\t\t\t\t\t\t\t+ Integer.toOctalString(mIndex) + \" mWorkspace=\"\n+\t\t\t\t\t\t\t+ Integer.toOctalString(mWorkspace), 1);\n+\t\t\t\t}\n+\n+\t\t\t\tif (mHead != mIndex || mHead != mWorkspace) {\n+\t\t\t\t\t// If all three modes aren't identical there is a difference\n+\t\t\t\t\t// here. Its the fastest test we have and it neatly handles\n+\t\t\t\t\t// weird D->F/F->D style changes that may cause issues later\n+\n+\t\t\t\t\tif (DEBUG_DECORATIONS) {\n+\t\t\t\t\t\tdebug(\"Modes did not match, so concider the whole \"\n+\t\t\t\t\t\t\t\t+ \"tree dirty\", 1);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\treturn true;\n+\t\t\t\t}\n+\n+\t\t\t\tif (FileMode.TREE.equals(mHead)) {\n+\t\t\t\t\t// We must include trees otherwise the walker won't recurse\n+\t\t\t\t\t// into a subtree for us. Computing anything more about a\n+\t\t\t\t\t// tree is too expensive from the workspace so we do not do\n+\t\t\t\t\t// a prune based on no changes.\n+\n+\t\t\t\t\tfinal IPreferenceStore store = Activator.getDefault()\n+\t\t\t\t\t\t\t.getPreferenceStore();\n+\t\t\t\t\treturn store\n+\t\t\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_COMPUTE_DEEP_DIRTY);\n+\t\t\t\t}\n+\n+\t\t\t\tfinal DirCacheIterator iIndex = tw.getTree(T_INDEX,\n+\t\t\t\t\t\tDirCacheIterator.class);\n+\t\t\t\tfinal WorkingTreeIterator iWorkspace = tw.getTree(T_WORKSPACE,\n+\t\t\t\t\t\tWorkingTreeIterator.class);\n+\n+\t\t\t\tif (!timestampMatches(iIndex, iWorkspace)) {\n+\t\t\t\t\t// If the modification time of the file differs we know the\n+\t\t\t\t\t// folder is (possibly) dirty and should be marked as such.\n+\t\t\t\t\t// This is faster than the id check below so it goes first.\n+\n+\t\t\t\t\tif (DEBUG_DECORATIONS) {\n+\t\t\t\t\t\tdebug(\"Timestamps did not match, so concider the \"\n+\t\t\t\t\t\t\t\t+ \"whole tree dirty\", 1);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\treturn true;\n+\t\t\t\t}\n+\n+\t\t\t\tif (!tw.idEqual(T_HEAD, T_INDEX)) {\n+\t\t\t\t\t// Staged id difference indicates the path is modified.\n+\n+\t\t\t\t\tif (DEBUG_DECORATIONS) {\n+\t\t\t\t\t\tdebug(\"File is staged, so concider the whole \"\n+\t\t\t\t\t\t\t\t+ \"tree dirty\", 1);\n+\t\t\t\t\t}\n+\n+\t\t\t\t\treturn true;\n+\t\t\t\t}\n+\n+\t\t\t\t// TODO: Consider doing a content check here, to rule out false\n+\t\t\t\t// positives, as we might get mismatch between timestamps, even\n+\t\t\t\t// if the content is the same\n+\n+\t\t\t\treturn false;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic boolean shouldBeRecursive() {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\n+\t\t\t@Override\n+\t\t\tpublic TreeFilter clone() {\n+\t\t\t\treturn this;\n+\t\t\t}\n+\t\t}\n+\n \t\tpublic DecoratableResourceAdapter(IResource resourceToWrap)\n \t\t\t\tthrows IOException {\n \t\t\tresource = resourceToWrap;\n@@ -219,25 +364,12 @@ public DecoratableResourceAdapter(IResource resourceToWrap)\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+\t\t\t// TODO: Add option to shorten branch name to 6 chars if it's a SHA\n+\t\t\tbranch = repository.getBranch();\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\tif (resource.getType() == IResource.PROJECT\n+\t\t\t\t\t|| (treeWalk != null && treeWalk.next()))\n \t\t\t\ttracked = true;\n \t\t\telse\n \t\t\t\treturn;\n@@ -248,6 +380,27 @@ private void extractFileProperties() throws IOException {\n \t\t\t\treturn;\n \t\t\t}\n \n+\t\t\tswitch (resource.getType()) {\n+\t\t\tcase IResource.FILE:\n+\t\t\t\textractFileProperties(treeWalk);\n+\t\t\t\tbreak;\n+\n+\t\t\tcase IResource.FOLDER:\n+\t\t\t\textractContainerProperties();\n+\t\t\t\tbreak;\n+\n+\t\t\tcase IResource.PROJECT:\n+\t\t\t\tfinal IPreferenceStore store = Activator.getDefault()\n+\t\t\t\t\t\t.getPreferenceStore();\n+\t\t\t\tif (!store\n+\t\t\t\t\t\t.getBoolean(UIPreferences.DECORATOR_COMPUTE_DEEP_DIRTY))\n+\t\t\t\t\treturn;\n+\t\t\t\textractContainerProperties();\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\n+\t\tprivate void extractFileProperties(TreeWalk treeWalk) {\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@@ -265,18 +418,7 @@ private void extractFileProperties() throws IOException {\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\tif (!timestampMatches(indexIterator, resource)) {\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@@ -290,37 +432,27 @@ private void extractFileProperties() throws IOException {\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\ttreeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter(),\n+\t\t\t\t\tContainerDiffFilter.INSTANCE));\n+\t\t\ttreeWalk.setRecursive(true);\n+\n+\t\t\tif (repository.getWorkDir().equals(\n+\t\t\t\t\tresource.getProject().getLocation().toFile()))\n+\t\t\t\ttreeWalk.addTree(new ContainerTreeIterator(resource\n+\t\t\t\t\t\t.getProject()));\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+\t\t\t\ttreeWalk.addTree(new ContainerTreeIterator(resource\n+\t\t\t\t\t\t.getWorkspace().getRoot()));\n \n+\t\t\t// TODO: Add fallback for projects with the repository more than\n+\t\t\t// one parent up, for example by using a stack of DummyIterators\n+\t\t\t\n+\t\t\tif (treeWalk.next())\n+\t\t\t\tdirty = true;\n \t\t}\n \n \t\t/**\n@@ -328,9 +460,10 @@ private void extractProjectProperties() throws IOException {\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 * If the resource does not exists in the current repository, no filter\n+\t\t * is added and the method returns <code>false</code>. If the resource\n+\t\t * is a project, no filter is added, but the operation is considered a\n+\t\t * success.\n \t\t * \n \t\t * @param treeWalk\n \t\t *            the tree walk to add the filter to\n@@ -344,9 +477,12 @@ private 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\tif (repositoryPaths.isEmpty())\n \t\t\t\treturn false;\n \n+\t\t\tif (repositoryPaths.contains(\"\"))\n+\t\t\t\treturn true; // Project filter\n+\n \t\t\ttreeWalk.setFilter(PathFilterGroup\n \t\t\t\t\t.createFromStrings(repositoryPaths));\n \t\t\treturn true;\n@@ -376,6 +512,31 @@ private TreeWalk createHeadVsIndexTreeWalk() throws IOException {\n \t\t\treturn treeWalk;\n \t\t}\n \n+\t\tprivate static boolean timestampMatches(DirCacheIterator index,\n+\t\t\t\tIResource workspaceResource) {\n+\t\t\treturn timestampMatches(index.getDirCacheEntry().getLastModified(),\n+\t\t\t\t\tworkspaceResource.getLocalTimeStamp());\n+\t\t}\n+\n+\t\tprivate static boolean timestampMatches(DirCacheIterator index,\n+\t\t\t\tWorkingTreeIterator workspaceResource) {\n+\t\t\treturn timestampMatches(index.getDirCacheEntry().getLastModified(),\n+\t\t\t\t\tworkspaceResource.getEntryLastModified());\n+\t\t}\n+\n+\t\tprivate static boolean timestampMatches(long index,\n+\t\t\t\tlong workspaceResource) {\n+\t\t\t// C-Git under Windows stores timestamps with 1-seconds resolution,\n+\t\t\t// so we need to check to see if this is the case here, and possibly\n+\t\t\t// fix the timestamp of the resource to match the resolution of the\n+\t\t\t// index.\n+\t\t\tif (index % 1000 == 0) {\n+\t\t\t\treturn index == (workspaceResource - (workspaceResource % 1000));\n+\t\t\t} else {\n+\t\t\t\treturn index == workspaceResource;\n+\t\t\t}\n+\t\t}\n+\n \t\tpublic String getName() {\n \t\t\treturn resource.getName();\n \t\t}\n@@ -555,11 +716,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@@ -657,6 +820,9 @@ public static void decorate(IDecoration decoration, String format,\n \t * Perform a blanket refresh of all decorations\n \t */\n \tpublic static void refresh() {\n+\t\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"blanket refesh\");\n+\n \t\tDisplay.getDefault().asyncExec(new Runnable() {\n \t\t\tpublic void run() {\n \t\t\t\tActivator.getDefault().getWorkbench().getDecoratorManager()\n@@ -679,6 +845,9 @@ 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\tif (DEBUG_DECORATIONS)\n+\t\t\t\tdebug(\"property=\" + prop);\n+\n \t\t\tpostLabelEvent(new LabelProviderChangedEvent(this));\n \t\t}\n \t}\n@@ -696,9 +865,20 @@ public void propertyChange(PropertyChangeEvent event) {\n \tpublic void resourceChanged(IResourceChangeEvent event) {\n \t\tfinal Set<IResource> resourcesToUpdate = new HashSet<IResource>();\n \n+\t\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"IResourceChangeEvent\");\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+\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@@ -709,6 +889,11 @@ 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 (DEBUG_DECORATIONS)\n+\t\t\t\t\t\tdebug(\"Git resource: \"\n+\t\t\t\t\t\t\t\t+ resource.getLocation().toString(), 1);\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@@ -720,16 +905,10 @@ 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\tif (DEBUG_DECORATIONS)\n+\t\t\t\t\t\tdebug(\"Will update this resource\", 2);\n \t\t\t\t\treturn true;\n \t\t\t\t}\n \t\t\t}, true /* includePhantoms */);\n@@ -737,19 +916,25 @@ public boolean visit(IResourceDelta delta) throws CoreException {\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\tif (DEBUG_DECORATIONS)\n+\t\t\t\tdebug(\"Calulcating dirty ancestor..\", 1);\n+\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);\n+\t\t\t\t\tif (resourcesToUpdate.add(current) && DEBUG_DECORATIONS)\n+\t\t\t\t\t\tdebug(\"Will also update \" + current.getLocation(), 2);\n \t\t\t\t}\n \t\t\t}\n \t\t}\n@@ -768,6 +953,9 @@ postLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate\n \t *            The original change event\n \t */\n \tprivate void repositoryChanged(RepositoryChangedEvent e) {\n+\t\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"RepositoryChangedEvent=\" + e.getRepository().getWorkDir());\n+\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@@ -809,6 +997,9 @@ public void refsChanged(RefsChangedEvent e) {\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\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"RepositoryMapping=\" + mapping.getWorkDir());\n+\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@@ -845,6 +1036,9 @@ private static IResource getResource(Object element) {\n \t *            The event to post\n \t */\n \tprivate void postLabelEvent(final LabelProviderChangedEvent event) {\n+\t\tif (DEBUG_DECORATIONS)\n+\t\t\tdebug(\"event=\" + event.getElements());\n+\n \t\tDisplay.getDefault().asyncExec(new Runnable() {\n \t\t\tpublic void run() {\n \t\t\t\tfireLabelProviderChanged(event);\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 74bc209..1a13a15 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@@ -89,7 +89,9 @@\n \n \tprivate Text projectTextFormat;\n \n-\tprivate Button computeDeepDirtyState;\n+\tprivate Button recomputeAncestorDecorations;\n+\n+\tprivate Button computeDeepDirtyStateForContainers;\n \n \tprivate Button showTracked;\n \n@@ -204,8 +206,10 @@ private Control createGeneralDecoratorPage(Composite parent) {\n \t\tdata.horizontalAlignment = GridData.FILL;\n \t\tcomposite.setLayoutData(data);\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\tcomputeDeepDirtyStateForContainers = SWTUtils.createCheckBox(composite,\n+\t\t\t\tUIText.DecoratorPreferencesPage_computeDeepDirtyState);\n \n \t\treturn composite;\n \t}\n@@ -317,8 +321,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\tcomputeDeepDirtyStateForContainers.setSelection(store\n+\t\t\t\t.getBoolean(UIPreferences.DECORATOR_COMPUTE_DEEP_DIRTY));\n \n \t\tfileTextFormat.setText(store\n \t\t\t\t.getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));\n@@ -342,7 +348,9 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t}\n \t\t};\n \n-\t\tcomputeDeepDirtyState.addSelectionListener(selectionListener);\n+\t\trecomputeAncestorDecorations.addSelectionListener(selectionListener);\n+\t\tcomputeDeepDirtyStateForContainers\n+\t\t\t\t.addSelectionListener(selectionListener);\n \t\tshowTracked.addSelectionListener(selectionListener);\n \t\tshowUntracked.addSelectionListener(selectionListener);\n \t\tshowStaged.addSelectionListener(selectionListener);\n@@ -384,8 +392,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_COMPUTE_DEEP_DIRTY,\n+\t\t\t\tcomputeDeepDirtyStateForContainers.getSelection());\n \n \t\tstore.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,\n \t\t\t\tfileTextFormat.getText());\n@@ -414,8 +424,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\tcomputeDeepDirtyStateForContainers.setSelection(store\n+\t\t\t\t.getDefaultBoolean(UIPreferences.DECORATOR_COMPUTE_DEEP_DIRTY));\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 2762d11..770de4a 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@@ -344,7 +344,8 @@ 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=Also re-decorate &ancestors when decorating changed resources\n+DecoratorPreferencesPage_computeDeepDirtyState=Inspect dirty state of &children when decorating folders and projects\n DecoratorPreferencesPage_description=Shows Git specific information on resources in projects under version control.\n \n DecoratorPreferencesPage_decorationSettings=Decoration &settings:\n-- \n1.6.1.2.309.g2ea3\n"},{"id":"103260","messageId":"498A3B1B.3000600@gmail.com","threadId":"17574","inReplyTo":"1233795618-20249-1-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T01:04:27Z","receivedAt":"2009-02-05T01:04:27Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Tor Arne Vestbø wrote:\n> This series adds support for customizable label decorations, which\n> is usefull for hiding selected decorations, or tweaking the format\n> of the decoration text.\n\nOh, I forgot, the branch can be found here:\n\nhttp://repo.or.cz/w/egit/torarne.git?a=shortlog;h=refs/heads/customizable-decorations\n\nTor Arne\n"},{"id":"103343","messageId":"20090205154815.GG26880@spearce.org","threadId":"17574","inReplyTo":"1233795618-20249-3-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-02-05T15:48:15Z","receivedAt":"2009-02-05T15:48:15Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Tor Arne Vestbø <torarnv@gmail.com> wrote:\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,8 @@ 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}\n> -\n> -\tprivate static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {\n> -\t\treturn repositoryChangeListeners;\n> +\t\tfor (RepositoryChangeListener listener : repositoryChangeListeners)\n> +\t\t\tlistener.repositoryChanged(which);\n\nSee anything wrong here, like that the Set can be modified while\nGitProjectData's class lock is held, but its being read here without\nany locking?\n\nThe array trick worked before because we always made a copy anytime\nthe array was modified.  So we could safely return the array to the\ncaller and let the caller iterate it unlocked; we just had to read\nthe current array using a synchronized method to ensure we had a\nstable read.\n\nYou'll need to copy the Set somehow while inside of a synchronized\nmethod, then return the copy to the fireRepositoryChanged() method\nso it can iterate the copy to fire the events.\n\n-- \nShawn.\n"},{"id":"103344","messageId":"20090205155337.GH26880@spearce.org","threadId":"17574","inReplyTo":"1233795618-20249-2-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 01/11] Add support code to handle plugin property changes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-02-05T15:53:37Z","receivedAt":"2009-02-05T15:53:37Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Tor Arne Vestbø <torarnv@gmail.com> wrote:\n> diff --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\n> index fced643..d4a9e8e 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> @@ -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\nWhat does this field do?  I don't see it referenced anywhere...\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\nNone of these list accesses are thread-safe.  Are we certain they\ncall will come from a single thread, e.g. the SWT event thread?\nOr do we need to put synchronized protection in here?\n\n-- \nShawn.\n"},{"id":"103345","messageId":"20090205160625.GI26880@spearce.org","threadId":"17574","inReplyTo":"1233795618-20249-1-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-02-05T16:06:25Z","receivedAt":"2009-02-05T16:06:25Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Tor Arne Vestbø <torarnv@gmail.com> wrote:\n> This series adds support for customizable label decorations, which\n> is usefull for hiding selected decorations, or tweaking the format\n> of the decoration text.\n\nAside from my two remarks about the synchronized collections,\nI like it.  There's no display for \"added and dirty\", aka doing:\n\n  echo a >a; # create a\n  git add a\n  echo b >>a; # append a and dirty it\n\nThis still shows as just \"added\".  It should be \"added and dirty\",\nas its partially staged.\n \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\nI'd like to see this fixed in the near-ish future, but I don't\nthink its blocking to merging your patches.\n \n>   - When a Java resource is dirty, each parent package in the\n>     package hierarcy will appear dirty, even when the layout is\n>     set to 'flat'.\n\nBah.  I've seen the Java compiler error marks also report like this.\nI don't think its our issue.  But maybe I'm wrong.\n\n-- \nShawn.\n"},{"id":"103348","messageId":"498B110B.5040304@gmail.com","threadId":"17574","inReplyTo":"20090205160625.GI26880@spearce.org","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T16:17:15Z","receivedAt":"2009-02-05T16:17:15Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Tor Arne Vestbø <torarnv@gmail.com> wrote:\n>> This series adds support for customizable label decorations, which\n>> is usefull for hiding selected decorations, or tweaking the format\n>> of the decoration text.\n> \n> Aside from my two remarks about the synchronized collections,\n> I like it. \n\nThanks :)\n\n> There's no display for \"added and dirty\", aka doing:\n\nThat's a bug, should work the same as \"staged and dirty\", with [*] >,\nI'll look into it.\n\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> I'd like to see this fixed in the near-ish future, but I don't\n> think its blocking to merging your patches.\n\nYepp, It's on my list. This series is basically the minimum feature-set\nI could produce that has most of what the current decorators have, so I\n could get a basis landed.\n\n>>   - When a Java resource is dirty, each parent package in the\n>>     package hierarcy will appear dirty, even when the layout is\n>>     set to 'flat'.\n> \n> Bah.  I've seen the Java compiler error marks also report like this.\n> I don't think its our issue.  But maybe I'm wrong.\n\nThe CVS plugin handles this -- possibly through the TeamStateProvider\nand friends -- so it is possible. Something to look at.\n\nTor Arne\n"},{"id":"103349","messageId":"498B1563.4070905@gmail.com","threadId":"17574","inReplyTo":"20090205155337.GH26880@spearce.org","subject":"Re: [EGIT PATCH 01/11] Add support code to handle plugin property changes","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T16:35:47Z","receivedAt":"2009-02-05T16:35:47Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Tor Arne Vestbø <torarnv@gmail.com> wrote:\n>> diff --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\n>> index fced643..d4a9e8e 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>> @@ -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> What does this field do?  I don't see it referenced anywhere...\n\nIt's the singleton instance, referenced from Activator.getDefault(). I\njust documented it :)\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> None of these list accesses are thread-safe.  Are we certain they\n> call will come from a single thread, e.g. the SWT event thread?\n> Or do we need to put synchronized protection in here?\n\nThe addPropertyChangeListener method is called at startup from the\nGitLightweightDecorator constructor, in one of the worker threads, and\nsame thing with removePropertyChangeListener from dispose(). The\nbroadcastPropertyChange method is called in the main thread every time\nthe Git decorator preference page is closes.\n\nThis is the sync model used by other Eclipse plugins for keeping track\nof propertyChangeListeners, for example in the TeamUIPlugin, that's why\nI assumed it was OK. I'm perfectly fine with syncrhronizing it though,\nsimilar to repositoryChangeListeners in GitProjectData?\n\nTor Arne\n"},{"id":"103350","messageId":"498B159B.4040001@gmail.com","threadId":"17574","inReplyTo":"20090205154815.GG26880@spearce.org","subject":"Re: [EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T16:36:43Z","receivedAt":"2009-02-05T16:36:43Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Tor Arne Vestbø <torarnv@gmail.com> wrote:\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,8 @@ 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}\n>> -\n>> -\tprivate static synchronized RepositoryChangeListener[] getRepositoryChangeListeners() {\n>> -\t\treturn repositoryChangeListeners;\n>> +\t\tfor (RepositoryChangeListener listener : repositoryChangeListeners)\n>> +\t\t\tlistener.repositoryChanged(which);\n> \n> See anything wrong here, like that the Set can be modified while\n> GitProjectData's class lock is held, but its being read here without\n> any locking?\n\nYou're perfectly right. New patch coming up!\n\nTor Arne\n"},{"id":"103351","messageId":"20090205164022.GK26880@spearce.org","threadId":"17574","inReplyTo":"498B1563.4070905@gmail.com","subject":"Re: [EGIT PATCH 01/11] Add support code to handle plugin property changes","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-02-05T16:40:22Z","receivedAt":"2009-02-05T16:40:22Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Tor Arne Vestbø <torarnv@gmail.com> wrote:\n> Shawn O. Pearce wrote:\n> > Tor Arne Vestbø <torarnv@gmail.com> wrote:\n> >> diff --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\n> >> index fced643..d4a9e8e 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> >> @@ -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> > What does this field do?  I don't see it referenced anywhere...\n> \n> It's the singleton instance, referenced from Activator.getDefault(). I\n> just documented it :)\n\n*sigh*.  I haven't had enough caffiene yet this morning.  Yes,\nI see what you mean, its only the Javadoc that was added.  OK,\nforget I said anything here.  :-)\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> > None of these list accesses are thread-safe.  Are we certain they\n> > call will come from a single thread, e.g. the SWT event thread?\n> > Or do we need to put synchronized protection in here?\n> \n> The addPropertyChangeListener method is called at startup from the\n> GitLightweightDecorator constructor, in one of the worker threads, and\n> same thing with removePropertyChangeListener from dispose(). The\n> broadcastPropertyChange method is called in the main thread every time\n> the Git decorator preference page is closes.\n> \n> This is the sync model used by other Eclipse plugins for keeping track\n> of propertyChangeListeners, for example in the TeamUIPlugin, that's why\n> I assumed it was OK. I'm perfectly fine with syncrhronizing it though,\n> similar to repositoryChangeListeners in GitProjectData?\n\nOK, I see.  I'd perhaps prefer to make this thread-safe just in case.\nIf its always coming off the SWT event thread, then just tossing\na synchronized keyword on all 3 methods should be Good Enough(tm).\n\n-- \nShawn.\n"},{"id":"103366","messageId":"1233858172-21289-1-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"20090205164022.GK26880@spearce.org","subject":"[EGIT PATCH v2] Add support code to handle plugin property changes","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T18:22:52Z","receivedAt":"2009-02-05T18:22:52Z","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":"103367","messageId":"1233858518-21563-1-git-send-email-torarnv@gmail.com","threadId":"17574","inReplyTo":"498B159B.4040001@gmail.com","subject":"[EGIT PATCH 02/11 v2] Use Set instead of array to keep track of change listeners","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T18:28:38Z","receivedAt":"2009-02-05T18:28:38Z","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":"103368","messageId":"200902051932.24600.robin.rosenberg.lists@dewire.com","threadId":"17574","inReplyTo":"20090205160625.GI26880@spearce.org","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-02-05T18:32:24Z","receivedAt":"2009-02-05T18:32:24Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdag 05 februari 2009 17:06:25 skrev Shawn O. Pearce:\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> I'd like to see this fixed in the near-ish future, but I don't\n> think its blocking to merging your patches.\n\nI'm not sure this is Tore's bug. We've had problems with nested\nrepos before.\n\n> >   - When a Java resource is dirty, each parent package in the\n> >     package hierarcy will appear dirty, even when the layout is\n> >     set to 'flat'.\n> \n> Bah.  I've seen the Java compiler error marks also report like this.\n> I don't think its our issue.  But maybe I'm wrong.\n\nIt's our choice here. Generally, I think we should shift behavior here\ndepending on layout. Java packages are not hierarchical in nature.\n\nCurrently it may look like this with flat package layout:\n\n\t>org.spearce.jgit.treewalk\n\t\tCanonicalTreeParserTest.java\n\t\tEmptyTreeIteratorTest.java\n\t\tFileTreeIteratorTest.java\n\t\tNameConflictTreeWalkTest.java\n\t\tPostOrderTreeWalkTest.java\n\t\tTreeWalkBasicDiffTest.java\n\n\t>org.spearce.jgit.treewalk.filter\n\t\tAlwaysCloneTreeFilter.java\n\t\t>NotTreeFilterTest.java\n\t\tTreeFilterTest.java\n\n\nWhich is odd when looking at org.spearce.jgit.treewalk, because\nno files in that package have been changed. A tricky question is\nhow to decorate empty packages. like org, org.spearce. Probably\nas unchanged.\n\n-- robin\n"},{"id":"103370","messageId":"498B31EC.2020006@gmail.com","threadId":"17574","inReplyTo":"200902051932.24600.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T18:37:32Z","receivedAt":"2009-02-05T18:37:32Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Robin Rosenberg wrote:\n> It's our choice here. Generally, I think we should shift behavior here\n> depending on layout. Java packages are not hierarchical in nature.\n> \n> Currently it may look like this with flat package layout:\n> \n> \t>org.spearce.jgit.treewalk\n> \t\tCanonicalTreeParserTest.java\n> \t\tEmptyTreeIteratorTest.java\n> \t\tFileTreeIteratorTest.java\n> \t\tNameConflictTreeWalkTest.java\n> \t\tPostOrderTreeWalkTest.java\n> \t\tTreeWalkBasicDiffTest.java\n> \n> \t>org.spearce.jgit.treewalk.filter\n> \t\tAlwaysCloneTreeFilter.java\n> \t\t>NotTreeFilterTest.java\n> \t\tTreeFilterTest.java\n> \n> \n> Which is odd when looking at org.spearce.jgit.treewalk, because\n> no files in that package have been changed.\n\nAgreed. But, as the CVS plugin seems to handle this gracefully, without\nany JDT specific code (from my brief investigations), it seems that this\nis something we can get for free from the team plugins. I will look into\nit as soon as this series is done cooking.\n\nTor Arne\n"},{"id":"103374","messageId":"200902052102.00409.robin.rosenberg@dewire.com","threadId":"17574","inReplyTo":"1233795618-20249-7-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-02-05T20:02:00Z","receivedAt":"2009-02-05T20:02:00Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdag 05 februari 2009 02:00:13 skrev Tor Arne Vestbø:\n> Currently the only binding available is the resource name, but\n> this commit enables a framework for adding more bindings.\n\nThe size of the components of the page is wrong forcing unnecessary scrollbars.\n\nThe terms use should probably have a cleared explanation/definition \nand the same terms used for the flags/icons. The explanation could\nprobably be done using longer tooltips.\n\nI miss the red attention-gathering red conflict icon we had. I also\nhave a hard time distinguishing the staged/added/removed icons\nwithout reading glasses. The decorations are only 1.3 mm on my\nscreen (150 dpi), so I think the icons should have different colors and\ndifferent shapes instead of a few different pixels withing a square \nbox.\n\nI'd also like to see the conflict state for files inherited by parent\nfolder and projects.\n\nThe checkboxes in the general tab have no effect on the preview.\n\n-- robin\n"},{"id":"103375","messageId":"200902052104.23876.robin.rosenberg.lists@dewire.com","threadId":"17574","inReplyTo":"1233795618-20249-7-git-send-email-torarnv@gmail.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-02-05T20:04:23Z","receivedAt":"2009-02-05T20:04:23Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nThe \"assume unchanged\" decoration seems lost.\n\n-- robin\n"},{"id":"103379","messageId":"498B4A66.9020108@gmail.com","threadId":"17574","inReplyTo":"200902052102.00409.robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T20:21:58Z","receivedAt":"2009-02-05T20:21:58Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Robin Rosenberg wrote:\n> torsdag 05 februari 2009 02:00:13 skrev Tor Arne Vestbø:\n>> Currently the only binding available is the resource name, but\n>> this commit enables a framework for adding more bindings.\n> \n> The size of the components of the page is wrong forcing unnecessary scrollbars.\n\nArgh, I've been battling those layouts on Linux, but though it was\nfinally fixed. On Windows the dialog looks fine. I'll investigate further.\n\n> The terms use should probably have a cleared explanation/definition \n> and the same terms used for the flags/icons. The explanation could\n> probably be done using longer tooltips.\n\nNoted.\n\n> I miss the red attention-gathering red conflict icon we had.\n\nHappy to replace. If I remember correctly that's the symbol used by\nother plugins to indicate conflicts, so I was aiming for consistency and\nrecognizability.\n\n> I also have a hard time distinguishing the staged/added/removed icons\n> without reading glasses. The decorations are only 1.3 mm on my \n> screen (150 dpi), so I think the icons should have different colors\n> and different shapes instead of a few different pixels withing a\n> square box.>\n\nNoted, I'll try to make some mocukups of variations to the icons.\n\n> I'd also like to see the conflict state for files inherited by parent\n> folder and projects.\n\nGood idea, like the synchronize view shows it. Noted.\n\n> The checkboxes in the general tab have no effect on the preview.\n\nTrue. Should be easy to fix.\n\nThanks for the feedback!\n\nTor Arne\n"},{"id":"103391","messageId":"498B5374.1030305@gmail.com","threadId":"17574","inReplyTo":"498B4A66.9020108@gmail.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T21:00:36Z","receivedAt":"2009-02-05T21:00:36Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Tor Arne Vestbø wrote:\n>> I miss the red attention-gathering red conflict icon we had.\n> \n> Happy to replace. If I remember correctly that's the symbol used by\n> other plugins to indicate conflicts, so I was aiming for consistency and\n> recognizability.\n\nOkey, I've replaced the blue one with the default conflict icon from the\nteam org.eclipse.team.ui plugin, what can be more consistent then that?\n:) See mockup below.\n\n>> I also have a hard time distinguishing the staged/added/removed icons\n>> without reading glasses. The decorations are only 1.3 mm on my \n>> screen (150 dpi), so I think the icons should have different colors\n>> and different shapes instead of a few different pixels withing a\n>> square box.>\n> \n> Noted, I'll try to make some mocukups of variations to the icons.\n\nHere are two alternatives. The first one (A) uses the same icons as\nbefore, but adds red and green shading. The second one (B) uses the\ngreen plus sign for added (which is used all over the place in Eclipse\nto mean 'added'), and the gray x for removed (which is also used all\nover the place in Eclipse for that same concept).\n\nhttp://img443.imageshack.us/img443/5138/egitmockupmm7.png\n\nI favor solution B, as it uses recognizable concepts from the existing\nEclipse UI, plus that having a red removed-icon conflicts too much with\nthe conflict-icon (pun intended). There's also a very similar red icon\nin JDT that indicates that something needs fixing (a red medic kit),\nwhich is another reason to go with solution B.\n\nWhat do you say?\n\nTor Arne\n\n> \n>> I'd also like to see the conflict state for files inherited by parent\n>> folder and projects.\n> \n> Good idea, like the synchronize view shows it. Noted.\n> \n>> The checkboxes in the general tab have no effect on the preview.\n> \n> True. Should be easy to fix.\n> \n> Thanks for the feedback!\n> \n> Tor Arne\n"},{"id":"103395","messageId":"200902052236.01478.robin.rosenberg@dewire.com","threadId":"17574","inReplyTo":"498B5374.1030305@gmail.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-02-05T21:36:01Z","receivedAt":"2009-02-05T21:36:01Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdag 05 februari 2009 22:00:36 skrev Tor Arne Vestbø:\n> Here are two alternatives. The first one (A) uses the same icons as\n> before, but adds red and green shading. The second one (B) uses the\n> green plus sign for added (which is used all over the place in Eclipse\n> to mean 'added'), and the gray x for removed (which is also used all\n> over the place in Eclipse for that same concept).\n> \n> http://img443.imageshack.us/img443/5138/egitmockupmm7.png\n> \n> I favor solution B, as it uses recognizable concepts from the existing\n> Eclipse UI, plus that having a red removed-icon conflicts too much with\n> the conflict-icon (pun intended). There's also a very similar red icon\n> in JDT that indicates that something needs fixing (a red medic kit),\n> which is another reason to go with solution B.\n\nI go with B.\n\nBtw, shouldn't staged, added and removed also count as outgoing, i.e. \">\" ?\n\n-- robin\n"},{"id":"103396","messageId":"498B5DB7.7090905@gmail.com","threadId":"17574","inReplyTo":"200902052236.01478.robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences","fromName":"Tor Arne Vestbø","fromEmail":"torarnv@gmail.com","sentAt":"2009-02-05T21:44:23Z","receivedAt":"2009-02-05T21:44:23Z","isPatch":true,"sender":{"key":"torarnv@gmail.com","avatar":"https://gravatar.com/avatar/847af0150a5a28ad86d9db1556a48aba04e7642c6a29a5a767f8c2bdf1720321?d=mp&s=160"},"body":"Robin Rosenberg wrote:\n> torsdag 05 februari 2009 22:00:36 skrev Tor Arne Vestbø:\n>> Here are two alternatives. The first one (A) uses the same icons as\n>> before, but adds red and green shading. The second one (B) uses the\n>> green plus sign for added (which is used all over the place in Eclipse\n>> to mean 'added'), and the gray x for removed (which is also used all\n>> over the place in Eclipse for that same concept).\n>>\n>> http://img443.imageshack.us/img443/5138/egitmockupmm7.png\n>>\n>> I favor solution B, as it uses recognizable concepts from the existing\n>> Eclipse UI, plus that having a red removed-icon conflicts too much with\n>> the conflict-icon (pun intended). There's also a very similar red icon\n>> in JDT that indicates that something needs fixing (a red medic kit),\n>> which is another reason to go with solution B.\n> \n> I go with B.\n\nAll right :)\n\n> Btw, shouldn't staged, added and removed also count as outgoing, i.e. \">\" ?\n\nCurrently there's two variables, 'dirty', and 'staged'. The former\nmappps to a diff between working directory and index, and has the\ndefault symbol '>'. The latter mapps to a diff between index and\nrepository, with the default symbol '*'.\n\nThe default decoration for files does not have the 'staged' variable,\nbut you can add {staged} for a *, or override with {staged:>} for a >.\n\nThe idea was to get both states between wd and index and index and repo\nin the decorators. We could add an option to treat both as one, ie only\ncare about wd vs repo?\n\nI plan to implement the synchronize view too, in which case we could\nshare these two pieces of information between the Package Explorer (wd\nvs index), and the Synchronize View (index vs repo), similar to how git\ngui works.\n\nTor Arne\n"},{"id":"103400","messageId":"200902052309.10333.robin.rosenberg.lists@dewire.com","threadId":"17574","inReplyTo":"498B31EC.2020006@gmail.com","subject":"Re: [EGIT PATCH 00/11] Support customizable label decorations","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-02-05T22:09:10Z","receivedAt":"2009-02-05T22:09:10Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"torsdag 05 februari 2009 19:37:32 skrev Tor Arne Vestbø:\n> Robin Rosenberg wrote:\n> > It's our choice here. Generally, I think we should shift behavior here\n> > depending on layout. Java packages are not hierarchical in nature.\n> > \n> > Currently it may look like this with flat package layout:\n> > \n> > \t>org.spearce.jgit.treewalk\n> > \t\tCanonicalTreeParserTest.java\n> > \t\tEmptyTreeIteratorTest.java\n> > \t\tFileTreeIteratorTest.java\n> > \t\tNameConflictTreeWalkTest.java\n> > \t\tPostOrderTreeWalkTest.java\n> > \t\tTreeWalkBasicDiffTest.java\n> > \n> > \t>org.spearce.jgit.treewalk.filter\n> > \t\tAlwaysCloneTreeFilter.java\n> > \t\t>NotTreeFilterTest.java\n> > \t\tTreeFilterTest.java\n> > \n> > \n> > Which is odd when looking at org.spearce.jgit.treewalk, because\n> > no files in that package have been changed.\n> \n> Agreed. But, as the CVS plugin seems to handle this gracefully, without\n> any JDT specific code (from my brief investigations), it seems that this\n> is something we can get for free from the team plugins. I will look into\n> it as soon as this series is done cooking.\n\nMy guess is it has to do with traversal according to the structure layout, so\nJDT only does the layout and the CVS decorator follows that one.\n\n-- robin\n"}]}