From: "Tor Arne Vestbø" <torarnv@gmail.com>
To: "Shawn O. Pearce" <spearce@spearce.org>,
Robin Rosenberg <robin.rosenberg@dewire.com>
Cc: git@vger.kernel.org
Subject: [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences
Date: Thu, 5 Feb 2009 02:00:13 +0100 [thread overview]
Message-ID: <1233795618-20249-7-git-send-email-torarnv@gmail.com> (raw)
In-Reply-To: <1233795618-20249-6-git-send-email-torarnv@gmail.com>
Currently the only binding available is the resource name, but
this commit enables a framework for adding more bindings.
Signed-off-by: Tor Arne Vestbø <torarnv@gmail.com>
---
org.spearce.egit.ui/plugin.properties | 1 +
org.spearce.egit.ui/plugin.xml | 12 +-
.../src/org/spearce/egit/ui/Activator.java | 16 +
.../egit/ui/PluginPreferenceInitializer.java | 8 +
.../src/org/spearce/egit/ui/UIPreferences.java | 9 +
.../src/org/spearce/egit/ui/UIText.java | 63 ++-
.../egit/ui/internal/actions/BranchAction.java | 4 +-
.../egit/ui/internal/actions/Disconnect.java | 4 +-
.../egit/ui/internal/actions/ResetAction.java | 4 +-
.../decorators/GitLightweightDecorator.java | 538 ++++++++++++++
.../internal/decorators/GitResourceDecorator.java | 454 ------------
.../internal/decorators/IDecoratableResource.java | 31 +
.../preferences/GitDecoratorPreferencePage.java | 735 ++++++++++++++++++++
.../src/org/spearce/egit/ui/uitext.properties | 25 +-
14 files changed, 1438 insertions(+), 466 deletions(-)
create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java
delete mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java
create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java
create mode 100644 org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java
diff --git a/org.spearce.egit.ui/plugin.properties b/org.spearce.egit.ui/plugin.properties
index fa043f1..58b879f 100644
--- a/org.spearce.egit.ui/plugin.properties
+++ b/org.spearce.egit.ui/plugin.properties
@@ -64,3 +64,4 @@ Theme_CommitMessageFont_description=This font is used to show a commit message.
GitPreferences_name=Git
GitPreferences_HistoryPreferencePage_name=History
GitPreferences_WindowCachePreferencePage_name=Window Cache
+GitPreferences_DecoratorPreferencePage_name=Label Decorations
diff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml
index 869108c..2f23559 100644
--- a/org.spearce.egit.ui/plugin.xml
+++ b/org.spearce.egit.ui/plugin.xml
@@ -200,6 +200,14 @@
id="org.spearce.egit.ui.keyword.git">
</keywordReference>
</page>
+ <page name="%GitPreferences_DecoratorPreferencePage_name"
+ category="org.spearce.egit.ui.GitPreferences"
+ class="org.spearce.egit.ui.internal.preferences.GitDecoratorPreferencePage"
+ id="org.spearce.egit.ui.internal.preferences.GitDecoratorPreferencePage" >
+ <keywordReference
+ id="org.spearce.egit.ui.keyword.git">
+ </keywordReference>
+ </page>
</extension>
<extension point="org.eclipse.ui.propertyPages">
@@ -233,10 +241,10 @@
lightweight="true"
adaptable="true"
label="%Decorator_name"
- class="org.spearce.egit.ui.internal.decorators.GitResourceDecorator"
+ class="org.spearce.egit.ui.internal.decorators.GitLightweightDecorator"
state="true"
location="BOTTOM_RIGHT"
- id="org.spearce.egit.ui.internal.decorators.GitResourceDecorator">
+ id="org.spearce.egit.ui.internal.decorators.GitLightweightDecorator">
<enablement>
<objectClass name="org.eclipse.core.resources.IResource"/>
</enablement>
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
index d4a9e8e..9d03c70 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/Activator.java
@@ -33,6 +33,7 @@
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jsch.core.IJSchService;
import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.themes.ITheme;
import org.osgi.framework.BundleContext;
@@ -80,6 +81,21 @@ public static String getPluginId() {
}
/**
+ * Returns the standard display to be used. The method first checks, if the
+ * thread calling this method has an associated display. If so, this display
+ * is returned. Otherwise the method returns the default display.
+ *
+ * @return the display to use
+ */
+ public static Display getStandardDisplay() {
+ Display display = Display.getCurrent();
+ if (display == null) {
+ display = Display.getDefault();
+ }
+ return display;
+ }
+
+ /**
* Instantiate an error exception.
*
* @param message
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java
index bb7381f..79c2665 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/PluginPreferenceInitializer.java
@@ -35,6 +35,14 @@ public void initializeDefaultPreferences() {
prefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_REV_COMMENT, true);
prefs.setDefault(UIPreferences.RESOURCEHISTORY_SHOW_TOOLTIPS, false);
+ prefs.setDefault(UIPreferences.DECORATOR_FILETEXT_DECORATION,
+ UIText.DecoratorPreferencesPage_fileFormatDefault);
+ prefs.setDefault(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,
+ UIText.DecoratorPreferencesPage_folderFormatDefault);
+ prefs.setDefault(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,
+ UIText.DecoratorPreferencesPage_projectFormatDefault);
+ prefs.setDefault(UIPreferences.DECORATOR_CALCULATE_DIRTY, true);
+
w = new int[] { 500, 500 };
UIPreferences.setDefault(prefs,
UIPreferences.RESOURCEHISTORY_GRAPH_SPLIT, w);
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java
index 5ab6b25..a6168a0 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIPreferences.java
@@ -52,6 +52,15 @@
/** */
public final static String THEME_CommitMessageFont = "org.spearce.egit.ui.CommitMessageFont";
+ /** */
+ public final static String DECORATOR_CALCULATE_DIRTY = "decorator_calculate_dirty";
+ /** */
+ public final static String DECORATOR_FILETEXT_DECORATION = "decorator_filetext_decoration";
+ /** */
+ public final static String DECORATOR_FOLDERTEXT_DECORATION = "decorator_foldertext_decoration";
+ /** */
+ public final static String DECORATOR_PROJECTTEXT_DECORATION = "decorator_projecttext_decoration";
+
/**
* Get the preference values associated with a fixed integer array.
*
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
index 7a7d3ef..23498c8 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
@@ -446,9 +446,6 @@
public static String RefSpecPage_annotatedTagsNoTags;
/** */
- public static String Decorator_failedLazyLoading;
-
- /** */
public static String QuickDiff_failedLoading;
/** */
@@ -913,6 +910,66 @@
/** */
public static String BranchSelectionDialog_Refs;
+ /** */
+ public static String Decorator_exceptionMessage;
+
+ /** */
+ public static String DecoratorPreferencesPage_addVariablesTitle;
+
+ /** */
+ public static String DecoratorPreferencesPage_addVariablesAction;
+
+ /** */
+ public static String DecoratorPreferencesPage_computeDeep;
+
+ /** */
+ public static String DecoratorPreferencesPage_description;
+
+ /** */
+ public static String DecoratorPreferencesPage_decorationSettings;
+
+ /** */
+ public static String DecoratorPreferencesPage_preview;
+
+ /** */
+ public static String DecoratorPreferencesPage_fileFormatLabel;
+
+ /** */
+ public static String DecoratorPreferencesPage_folderFormatLabel;
+
+ /** */
+ public static String DecoratorPreferencesPage_projectFormatLabel;
+
+ /** */
+ public static String DecoratorPreferencesPage_fileFormatDefault;
+
+ /** */
+ public static String DecoratorPreferencesPage_projectFormatDefault;
+
+ /** */
+ public static String DecoratorPreferencesPage_folderFormatDefault;
+
+ /** */
+ public static String DecoratorPreferencesPage_generalTabFolder;
+
+ /** */
+ public static String DecoratorPreferencesPage_nameResourceVariable;
+
+ /** */
+ public static String DecoratorPreferencesPage_selectFormats;
+
+ /** */
+ public static String DecoratorPreferencesPage_selectVariablesToAdd;
+
+ /** */
+ public static String DecoratorPreferencesPage_textLabel;
+
+ /** */
+ public static String DecoratorPreferencesPage_iconLabel;
+
+ /** */
+ public static String DecoratorPreferencesPage_labelDecorationsLink;
+
static {
initializeMessages(UIText.class.getPackage().getName() + ".uitext",
UIText.class);
diff --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
index 7ca4d10..38ee3d8 100644
--- 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
@@ -19,7 +19,7 @@
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Display;
import org.spearce.egit.core.op.BranchOperation;
-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;
+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;
import org.spearce.egit.ui.internal.dialogs.BranchSelectionDialog;
import org.spearce.jgit.lib.Repository;
@@ -56,7 +56,7 @@ public void run(final IProgressMonitor monitor)
throws InvocationTargetException {
try {
new BranchOperation(repository, refName).run(monitor);
- GitResourceDecorator.refresh();
+ GitLightweightDecorator.refresh();
} catch (final CoreException ce) {
ce.printStackTrace();
Display.getDefault().asyncExec(new Runnable() {
diff --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
index 18d6b4b..4201822 100644
--- 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
@@ -13,7 +13,7 @@
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.jface.action.IAction;
import org.spearce.egit.core.op.DisconnectProviderOperation;
-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;
+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;
/**
* Action to disassociate a project from its Git repository.
@@ -27,6 +27,6 @@ protected IWorkspaceRunnable createOperation(final IAction act,
}
protected void postOperation() {
- GitResourceDecorator.refresh();
+ GitLightweightDecorator.refresh();
}
}
diff --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
index b05cdd3..a329925 100644
--- 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
@@ -19,7 +19,7 @@
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.spearce.egit.core.op.ResetOperation;
import org.spearce.egit.core.op.ResetOperation.ResetType;
-import org.spearce.egit.ui.internal.decorators.GitResourceDecorator;
+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator;
import org.spearce.egit.ui.internal.dialogs.BranchSelectionDialog;
import org.spearce.jgit.lib.Repository;
@@ -55,7 +55,7 @@ public void run(final IProgressMonitor monitor)
throws InvocationTargetException {
try {
new ResetOperation(repository, refName, type).run(monitor);
- GitResourceDecorator.refresh();
+ GitLightweightDecorator.refresh();
} catch (CoreException ce) {
ce.printStackTrace();
throw new InvocationTargetException(ce);
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java
new file mode 100644
index 0000000..85b9173
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitLightweightDecorator.java
@@ -0,0 +1,538 @@
+/*******************************************************************************
+ * Copyright (C) 2007, IBM Corporation and others
+ * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2008, Google Inc.
+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+
+package org.spearce.egit.ui.internal.decorators;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.IDecoration;
+import org.eclipse.jface.viewers.ILightweightLabelDecorator;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.LabelProviderChangedEvent;
+import org.eclipse.osgi.util.TextProcessor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.team.ui.TeamUI;
+import org.eclipse.ui.IContributorResourceAdapter;
+import org.eclipse.ui.PlatformUI;
+import org.spearce.egit.core.internal.util.ExceptionCollector;
+import org.spearce.egit.core.project.GitProjectData;
+import org.spearce.egit.core.project.RepositoryChangeListener;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.egit.ui.Activator;
+import org.spearce.egit.ui.UIPreferences;
+import org.spearce.egit.ui.UIText;
+import org.spearce.jgit.lib.IndexChangedEvent;
+import org.spearce.jgit.lib.RefsChangedEvent;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.RepositoryChangedEvent;
+import org.spearce.jgit.lib.RepositoryListener;
+
+/**
+ * Supplies annotations for displayed resources
+ *
+ * This decorator provides annotations to indicate the status of each resource
+ * when compared to <code>HEAD</code>, as well as the index in the relevant
+ * repository.
+ *
+ * TODO: Add support for colors and font decoration
+ */
+public class GitLightweightDecorator extends LabelProvider implements
+ ILightweightLabelDecorator, IPropertyChangeListener,
+ IResourceChangeListener, RepositoryChangeListener, RepositoryListener {
+
+ /**
+ * Property constant pointing back to the extension point id of the
+ * decorator
+ */
+ public static final String DECORATOR_ID = "org.spearce.egit.ui.internal.decorators.GitLightweightDecorator"; //$NON-NLS-1$
+
+ /**
+ * Bit-mask describing interesting changes for IResourceChangeListener
+ * events
+ */
+ private static int INTERESTING_CHANGES = IResourceDelta.CONTENT
+ | IResourceDelta.MOVED_FROM | IResourceDelta.MOVED_TO
+ | IResourceDelta.OPEN | IResourceDelta.REPLACED
+ | IResourceDelta.TYPE;
+
+ /**
+ * Collector for keeping the error view from filling up with exceptions
+ */
+ private static ExceptionCollector exceptions = new ExceptionCollector(
+ UIText.Decorator_exceptionMessage, Activator.getPluginId(),
+ IStatus.ERROR, Activator.getDefault().getLog());
+
+ /**
+ * Constructs a new Git resource decorator
+ */
+ public GitLightweightDecorator() {
+ TeamUI.addPropertyChangeListener(this);
+ Activator.addPropertyChangeListener(this);
+ PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
+ .addPropertyChangeListener(this);
+ Repository.addAnyRepositoryChangedListener(this);
+ GitProjectData.addRepositoryChangeListener(this);
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this,
+ IResourceChangeEvent.POST_CHANGE);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
+ */
+ @Override
+ public void dispose() {
+ super.dispose();
+ PlatformUI.getWorkbench().getThemeManager().getCurrentTheme()
+ .removePropertyChangeListener(this);
+ TeamUI.removePropertyChangeListener(this);
+ Activator.removePropertyChangeListener(this);
+ Repository.removeAnyRepositoryChangedListener(this);
+ GitProjectData.removeRepositoryChangeListener(this);
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ }
+
+ /**
+ * This method should only be called by the decorator thread.
+ *
+ * @see org.eclipse.jface.viewers.ILightweightLabelDecorator#decorate(java.lang.Object,
+ * org.eclipse.jface.viewers.IDecoration)
+ */
+ public void decorate(Object element, IDecoration decoration) {
+ final IResource resource = getResource(element);
+ if (resource == null)
+ return;
+
+ // Don't decorate the workspace root
+ if (resource.getType() == IResource.ROOT)
+ return;
+
+ // Don't decorate non-existing resources
+ if (!resource.exists() && !resource.isPhantom())
+ return;
+
+ // Make sure we're dealing with a Git project
+ final RepositoryMapping mapping = RepositoryMapping
+ .getMapping(resource);
+ if (mapping == null)
+ return;
+
+ // Cannot decorate linked resources
+ if (mapping.getRepoRelativePath(resource) == null)
+ return;
+
+ // Don't decorate if UI plugin is not running
+ Activator activator = Activator.getDefault();
+ if (activator == null)
+ return;
+
+ DecorationHelper helper = new DecorationHelper(activator
+ .getPreferenceStore());
+ helper.decorate(decoration, new DecoratableResourceAdapter(resource));
+ }
+
+ private class DecoratableResourceAdapter implements IDecoratableResource {
+
+ private IResource resource;
+
+ public DecoratableResourceAdapter(IResource resourceToWrap) {
+ resource = resourceToWrap;
+ }
+
+ public String getName() {
+ return resource.getName();
+ }
+
+ public int getType() {
+ return resource.getType();
+ }
+ }
+
+ /**
+ * Helper class for doing resource decoration, based on the given
+ * preferences
+ *
+ * Used for real-time decoration, as well as in the decorator preview
+ * preferences page
+ */
+ public static class DecorationHelper {
+
+ private IPreferenceStore store;
+
+ /** */
+ public static final String BINDING_RESOURCE_NAME = "name"; //$NON-NLS-1$
+
+ /**
+ * Constructs a decorator using the rules from the given
+ * <code>preferencesStore</code>
+ *
+ * @param preferencesStore
+ * the preferences store with the preferred decorator rules
+ */
+ public DecorationHelper(IPreferenceStore preferencesStore) {
+ store = preferencesStore;
+ }
+
+ /**
+ * Decorates the given <code>decoration</code> based on the state of the
+ * given <code>resource</code>, using the preferences passed when
+ * constructing this decoration helper.
+ *
+ * @param decoration
+ * the decoration to decorate
+ * @param resource
+ * the resource to retrieve state from
+ */
+ public void decorate(IDecoration decoration,
+ IDecoratableResource resource) {
+ String format = "";
+ switch (resource.getType()) {
+ case IResource.FILE:
+ format = store
+ .getString(UIPreferences.DECORATOR_FILETEXT_DECORATION);
+ break;
+ case IResource.FOLDER:
+ format = store
+ .getString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION);
+ break;
+ case IResource.PROJECT:
+ format = store
+ .getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION);
+ break;
+ }
+
+ Map<String, String> bindings = new HashMap<String, String>();
+ bindings.put(BINDING_RESOURCE_NAME, resource.getName());
+
+ decorate(decoration, format, bindings);
+ }
+
+ /**
+ * Decorates the given <code>decoration</code>, using the given
+ * <code>format</code>, and mapped using <code>bindings</code>
+ *
+ * @param decoration
+ * the decoration to decorate
+ * @param format
+ * the format to base the decoration on
+ * @param bindings
+ * the bindings between variables in the format and actual
+ * values
+ */
+ public static void decorate(IDecoration decoration, String format,
+ Map bindings) {
+ StringBuffer prefix = new StringBuffer();
+ StringBuffer suffix = new StringBuffer();
+ StringBuffer output = prefix;
+
+ int length = format.length();
+ int start = -1;
+ int end = length;
+ while (true) {
+ if ((end = format.indexOf('{', start)) > -1) {
+ output.append(format.substring(start + 1, end));
+ if ((start = format.indexOf('}', end)) > -1) {
+ String key = format.substring(end + 1, start);
+ String s;
+
+ // We use the BINDING_RESOURCE_NAME key to determine if
+ // we are doing the prefix or suffix. The name isn't
+ // actually part of either.
+ if (key.equals(BINDING_RESOURCE_NAME)) {
+ output = suffix;
+ s = null;
+ } else {
+ s = (String) bindings.get(key);
+ }
+
+ if (s != null) {
+ output.append(s);
+ } else {
+ // Support removing prefix character if binding is
+ // null
+ int curLength = output.length();
+ if (curLength > 0) {
+ char c = output.charAt(curLength - 1);
+ if (c == ':' || c == '@') {
+ output.deleteCharAt(curLength - 1);
+ }
+ }
+ }
+ } else {
+ output.append(format.substring(end, length));
+ break;
+ }
+ } else {
+ output.append(format.substring(start + 1, length));
+ break;
+ }
+ }
+
+ String prefixString = prefix.toString().replaceAll("^\\s+", "");
+ if (prefixString != null) {
+ decoration.addPrefix(TextProcessor.process(prefixString,
+ "()[].")); //$NON-NLS-1$
+ }
+ String suffixString = suffix.toString().replaceAll("\\s+$", "");
+ if (suffixString != null) {
+ decoration.addSuffix(TextProcessor.process(suffixString,
+ "()[].")); //$NON-NLS-1$
+ }
+ }
+ }
+
+ // -------- Refresh handling --------
+
+ /**
+ * Perform a blanket refresh of all decorations
+ */
+ public static void refresh() {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ Activator.getDefault().getWorkbench().getDecoratorManager()
+ .update(DECORATOR_ID);
+ }
+ });
+ }
+
+ /**
+ * Callback for IPropertyChangeListener events
+ *
+ * If any of the relevant preferences has been changed we refresh all
+ * decorations (all projects and their resources).
+ *
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ final String prop = event.getProperty();
+ // If the property is of any interest to us
+ if (prop.equals(TeamUI.GLOBAL_IGNORES_CHANGED)
+ || prop.equals(TeamUI.GLOBAL_FILE_TYPES_CHANGED)
+ || prop.equals(Activator.DECORATORS_CHANGED)) {
+ postLabelEvent(new LabelProviderChangedEvent(this, null /* all */));
+ }
+ }
+
+ /**
+ * Callback for IResourceChangeListener events
+ *
+ * Schedules a refresh of the changed resource
+ *
+ * If the preference for computing deep dirty states has been set we walk
+ * the ancestor tree of the changed resource and update all parents as well.
+ *
+ * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
+ */
+ public void resourceChanged(IResourceChangeEvent event) {
+ final Set<IResource> resourcesToUpdate = new HashSet<IResource>();
+
+ try { // Compute the changed resources by looking at the delta
+ event.getDelta().accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ final IResource resource = delta.getResource();
+
+ if (resource.getType() == IResource.ROOT) {
+ // Continue with the delta
+ return true;
+ }
+
+ if (resource.getType() == IResource.PROJECT) {
+ // If the project is not accessible, don't process it
+ if (!resource.isAccessible())
+ return false;
+ }
+
+ // If the file has changed but not in a way that we care
+ // about
+ // (e.g. marker changes to files) then ignore the change
+ if (delta.getKind() == IResourceDelta.CHANGED
+ && (delta.getFlags() & INTERESTING_CHANGES) == 0) {
+ return true;
+ }
+
+ // All seems good, schedule the resource for update
+ resourcesToUpdate.add(resource);
+ return true;
+ }
+ }, true /* includePhantoms */);
+ } catch (final CoreException e) {
+ handleException(null, e);
+ }
+
+ // If deep decorator calculation is enabled in the preferences we
+ // walk the ancestor tree of each of the changed resources and add
+ // their parents to the update set
+ final IPreferenceStore store = Activator.getDefault()
+ .getPreferenceStore();
+ if (store.getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY)) {
+ final IResource[] changedResources = resourcesToUpdate
+ .toArray(new IResource[resourcesToUpdate.size()]);
+ for (int i = 0; i < changedResources.length; i++) {
+ IResource current = changedResources[i];
+ while (current.getType() != IResource.ROOT) {
+ current = current.getParent();
+ resourcesToUpdate.add(current);
+ }
+ }
+ }
+
+ postLabelEvent(new LabelProviderChangedEvent(this, resourcesToUpdate
+ .toArray()));
+ }
+
+ /**
+ * Callback for RepositoryListener events
+ *
+ * We resolve the repository mapping for the changed repository and forward
+ * that to repositoryChanged(RepositoryMapping).
+ *
+ * @param e
+ * The original change event
+ */
+ private void repositoryChanged(RepositoryChangedEvent e) {
+ final Set<RepositoryMapping> ms = new HashSet<RepositoryMapping>();
+ for (final IProject p : ResourcesPlugin.getWorkspace().getRoot()
+ .getProjects()) {
+ final RepositoryMapping mapping = RepositoryMapping.getMapping(p);
+ if (mapping != null && mapping.getRepository() == e.getRepository())
+ ms.add(mapping);
+ }
+ for (final RepositoryMapping m : ms) {
+ repositoryChanged(m);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.spearce.jgit.lib.RepositoryListener#indexChanged(org.spearce.jgit
+ * .lib.IndexChangedEvent)
+ */
+ public void indexChanged(IndexChangedEvent e) {
+ repositoryChanged(e);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.spearce.jgit.lib.RepositoryListener#refsChanged(org.spearce.jgit.
+ * lib.RefsChangedEvent)
+ */
+ public void refsChanged(RefsChangedEvent e) {
+ repositoryChanged(e);
+ }
+
+ /**
+ * Callback for RepositoryChangeListener events, as well as
+ * RepositoryListener events via repositoryChanged()
+ *
+ * We resolve the project and schedule a refresh of each resource in the
+ * project.
+ *
+ * @see org.spearce.egit.core.project.RepositoryChangeListener#repositoryChanged(org.spearce.egit.core.project.RepositoryMapping)
+ */
+ public void repositoryChanged(RepositoryMapping mapping) {
+ final IProject project = mapping.getContainer().getProject();
+ if (project == null)
+ return;
+
+ final List<IResource> resources = new ArrayList<IResource>();
+ try {
+ project.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) {
+ resources.add(resource);
+ return true;
+ }
+ });
+ postLabelEvent(new LabelProviderChangedEvent(this, resources
+ .toArray()));
+ } catch (final CoreException e) {
+ handleException(project, e);
+ }
+ }
+
+ // -------- Helper methods --------
+
+ private static IResource getResource(Object element) {
+ if (element instanceof ResourceMapping) {
+ element = ((ResourceMapping) element).getModelObject();
+ }
+
+ IResource resource = null;
+ if (element instanceof IResource) {
+ resource = (IResource) element;
+ } else if (element instanceof IAdaptable) {
+ final IAdaptable adaptable = (IAdaptable) element;
+ resource = (IResource) adaptable.getAdapter(IResource.class);
+ if (resource == null) {
+ final IContributorResourceAdapter adapter = (IContributorResourceAdapter) adaptable
+ .getAdapter(IContributorResourceAdapter.class);
+ if (adapter != null)
+ resource = adapter.getAdaptedResource(adaptable);
+ }
+ }
+
+ return resource;
+ }
+
+ /**
+ * Post the label event to the UI thread
+ *
+ * @param event
+ * The event to post
+ */
+ private void postLabelEvent(final LabelProviderChangedEvent event) {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ fireLabelProviderChanged(event);
+ }
+ });
+ }
+
+ /**
+ * Handle exceptions that occur in the decorator. Exceptions are only logged
+ * for resources that are accessible (i.e. exist in an open project).
+ *
+ * @param resource
+ * The resource that triggered the exception
+ * @param e
+ * The exception that occurred
+ */
+ private static void handleException(IResource resource, CoreException e) {
+ if (resource == null || resource.isAccessible())
+ exceptions.handleException(e);
+ }
+}
diff --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
deleted file mode 100644
index f24b1eb..0000000
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/GitResourceDecorator.java
+++ /dev/null
@@ -1,454 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * Copyright (C) 2008, Google Inc.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * See LICENSE for the full license text, also available.
- *******************************************************************************/
-package org.spearce.egit.ui.internal.decorators;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Set;
-
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IResourceChangeEvent;
-import org.eclipse.core.resources.IResourceChangeListener;
-import org.eclipse.core.resources.IResourceDelta;
-import org.eclipse.core.resources.IResourceDeltaVisitor;
-import org.eclipse.core.resources.IResourceVisitor;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.QualifiedName;
-import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.ISchedulingRule;
-import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.jface.viewers.IDecoration;
-import org.eclipse.jface.viewers.ILightweightLabelDecorator;
-import org.eclipse.jface.viewers.LabelProvider;
-import org.eclipse.jface.viewers.LabelProviderChangedEvent;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.team.core.Team;
-import org.eclipse.ui.IDecoratorManager;
-import org.spearce.egit.core.project.GitProjectData;
-import org.spearce.egit.core.project.RepositoryChangeListener;
-import org.spearce.egit.core.project.RepositoryMapping;
-import org.spearce.egit.ui.Activator;
-import org.spearce.egit.ui.UIIcons;
-import org.spearce.egit.ui.UIText;
-import org.spearce.jgit.lib.Constants;
-import org.spearce.jgit.lib.GitIndex;
-import org.spearce.jgit.lib.IndexChangedEvent;
-import org.spearce.jgit.lib.RefsChangedEvent;
-import org.spearce.jgit.lib.Repository;
-import org.spearce.jgit.lib.RepositoryChangedEvent;
-import org.spearce.jgit.lib.RepositoryListener;
-import org.spearce.jgit.lib.RepositoryState;
-import org.spearce.jgit.lib.Tree;
-import org.spearce.jgit.lib.TreeEntry;
-import org.spearce.jgit.lib.GitIndex.Entry;
-
-/**
- * Supplies annotations for displayed resources.
- * <p>
- * This decorator provides annotations to indicate the status of each resource
- * when compared to <code>HEAD</code> as well as the index in the relevant
- * repository.
- *
- * When either the index or the working directory is different from HEAD an
- * indicator is set.
- *
- * </p>
- */
-public class GitResourceDecorator extends LabelProvider implements
- ILightweightLabelDecorator {
-
- static final String decoratorId = "org.spearce.egit.ui.internal.decorators.GitResourceDecorator";
- static class ResCL extends Job implements IResourceChangeListener, RepositoryChangeListener, RepositoryListener {
-
- ResCL() {
- super("Git resource decorator trigger");
- }
-
- GitResourceDecorator getActiveDecorator() {
- IDecoratorManager decoratorManager = Activator.getDefault()
- .getWorkbench().getDecoratorManager();
- if (decoratorManager.getEnabled(decoratorId))
- return (GitResourceDecorator) decoratorManager
- .getLightweightLabelDecorator(decoratorId);
- return null;
- }
-
- private Set<IResource> resources = new LinkedHashSet<IResource>();
-
- public void refsChanged(RefsChangedEvent e) {
- repositoryChanged(e);
- }
-
- public void indexChanged(IndexChangedEvent e) {
- repositoryChanged(e);
- }
-
- private void repositoryChanged(RepositoryChangedEvent e) {
- Set<RepositoryMapping> ms = new HashSet<RepositoryMapping>();
- for (IProject p : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
- RepositoryMapping mapping = RepositoryMapping.getMapping(p);
- if (mapping != null && mapping.getRepository() == e.getRepository())
- ms.add(mapping);
- }
- for (RepositoryMapping m : ms) {
- repositoryChanged(m);
- }
- }
-
- public void repositoryChanged(final RepositoryMapping which) {
- synchronized (resources) {
- resources.add(which.getContainer());
- }
- schedule();
- }
-
- @Override
- protected IStatus run(IProgressMonitor arg0) {
- try {
- if (resources.size() > 0) {
- IResource m;
- synchronized(resources) {
- Iterator<IResource> i = resources.iterator();
- m = i.next();
- i.remove();
-
- while (!m.isAccessible()) {
- if (!i.hasNext())
- return Status.OK_STATUS;
- m = i.next();
- i.remove();
- }
-
- if (resources.size() > 0)
- schedule();
- }
- ISchedulingRule markerRule = m.getWorkspace().getRuleFactory().markerRule(m);
- getJobManager().beginRule(markerRule, arg0);
- try {
- m.accept(new IResourceVisitor() {
- public boolean visit(IResource resource) throws CoreException {
- GitResourceDecorator decorator = getActiveDecorator();
- if (decorator != null)
- decorator.clearDecorationState(resource);
- return true;
- }
- },
- IResource.DEPTH_INFINITE,
- true);
- } finally {
- getJobManager().endRule(markerRule);
- }
- }
- return Status.OK_STATUS;
- } catch (Exception e) {
- // We must be silent here or the UI will panic with lots of error messages
- Activator.logError("Failed to trigger resource re-decoration", e);
- return Status.OK_STATUS;
- }
- }
-
- public void resourceChanged(IResourceChangeEvent event) {
- if (event.getType() != IResourceChangeEvent.POST_CHANGE) {
- return;
- }
- try {
- event.getDelta().accept(new IResourceDeltaVisitor() {
- public boolean visit(IResourceDelta delta)
- throws CoreException {
- for (IResource r = delta.getResource(); r.getType() != IResource.ROOT; r = r
- .getParent()) {
- synchronized (resources) {
- resources.add(r);
- }
- }
- return true;
- }
- },
- true
- );
- } catch (Exception e) {
- Activator.logError("Problem during decorations. Stopped", e);
- }
- schedule();
- }
-
- void force() {
- for (IProject p : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
- synchronized (resources) {
- resources.add(p);
- }
- }
- schedule();
- }
- } // End ResCL
-
- void clearDecorationState(IResource r) throws CoreException {
- if (r.isAccessible()) {
- r.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, null);
- fireLabelProviderChanged(new LabelProviderChangedEvent(this, r));
- }
- }
-
- static ResCL myrescl = new ResCL();
-
- static {
- Repository.addAnyRepositoryChangedListener(myrescl);
- GitProjectData.addRepositoryChangeListener(myrescl);
- ResourcesPlugin.getWorkspace().addResourceChangeListener(myrescl,
- IResourceChangeEvent.POST_CHANGE);
- }
-
- /**
- * Request that the decorator be updated, to reflect any recent changes.
- * <p>
- * Can be invoked any any thread. If the current thread is not the UI
- * thread, an async update will be scheduled.
- * </p>
- */
- public static void refresh() {
- myrescl.force();
- }
-
- private static IResource toIResource(final Object e) {
- if (e instanceof IResource)
- return (IResource) e;
- if (e instanceof IAdaptable) {
- final Object c = ((IAdaptable) e).getAdapter(IResource.class);
- if (c instanceof IResource)
- return (IResource) c;
- }
- return null;
- }
-
- static QualifiedName GITFOLDERDIRTYSTATEPROPERTY = new QualifiedName(
- "org.spearce.egit.ui.internal.decorators.GitResourceDecorator",
- "dirty");
-
- static final int UNCHANGED = 0;
-
- static final int CHANGED = 1;
-
- private Boolean isDirty(IResource rsrc) {
- try {
- if (rsrc.getType() == IResource.FILE && Team.isIgnored((IFile)rsrc))
- return Boolean.FALSE;
-
- RepositoryMapping mapped = RepositoryMapping.getMapping(rsrc);
- if (mapped != null) {
- if (rsrc instanceof IContainer) {
- for (IResource r : ((IContainer) rsrc)
- .members(IContainer.EXCLUDE_DERIVED)) {
- Boolean f = isDirty(r);
- if (f == null || f.booleanValue())
- return Boolean.TRUE;
- }
- return Boolean.FALSE;
- }
-
- return Boolean.valueOf(mapped.isResourceChanged(rsrc));
- }
- return null; // not mapped
- } catch (CoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return null;
- }
-
- public void decorate(final Object element, final IDecoration decoration) {
- final IResource rsrc = toIResource(element);
- if (rsrc == null)
- return;
-
- // If the workspace has not been refreshed properly a resource might
- // not actually exist, so we ignore these and do not decorate them
- if (!rsrc.exists() && !rsrc.isPhantom()) {
- Activator.trace("Tried to decorate non-existent resource "+rsrc);
- return;
- }
-
- RepositoryMapping mapped = RepositoryMapping.getMapping(rsrc);
-
- // TODO: How do I see a renamed resource?
- // TODO: Even trickier: when a path change from being blob to tree?
- try {
- if (mapped != null) {
- Repository repository = mapped.getRepository();
- GitIndex index = repository.getIndex();
- String repoRelativePath = mapped.getRepoRelativePath(rsrc);
-
- if (repoRelativePath == null) {
- Activator.trace("Cannot decorate linked resource " + rsrc);
- return;
- }
-
- Tree headTree = repository.mapTree(Constants.HEAD);
- TreeEntry blob = headTree!=null ? headTree.findBlobMember(repoRelativePath) : null;
- Entry entry = index.getEntry(repoRelativePath);
- if (entry == null) {
- if (blob == null) {
- if (rsrc instanceof IContainer) {
- Integer df = (Integer) rsrc
- .getSessionProperty(GITFOLDERDIRTYSTATEPROPERTY);
- Boolean f = df == null ? isDirty(rsrc)
- : Boolean.valueOf(df.intValue() == CHANGED);
- if (f != null) {
- if (f.booleanValue()) {
- decoration.addPrefix(">"); // Have not
- // seen
- orState(rsrc, CHANGED);
- } else {
- orState(rsrc, UNCHANGED);
- // decoration.addSuffix("=?");
- }
- } else {
- decoration.addSuffix(" ?* ");
- }
-
- if (rsrc instanceof IProject) {
- Repository repo = mapped.getRepository();
- try {
- String branch = repo.getBranch();
- RepositoryState repositoryState = repo.getRepositoryState();
- String statename;
- if (repositoryState.equals(RepositoryState.SAFE))
- statename = "";
- else
- statename = repositoryState.getDescription() + " ";
- decoration.addSuffix(" [Git " + statename + "@ " + branch + "]");
- } catch (IOException e) {
- e.printStackTrace();
- decoration.addSuffix(" [Git ?]");
- }
- decoration.addOverlay(UIIcons.OVR_SHARED);
- }
-
- } else {
- if (Team.isIgnoredHint(rsrc)) {
- decoration.addSuffix("(ignored)");
- } else {
- decoration.addPrefix(">");
- decoration.addSuffix("(untracked)");
- orState(rsrc.getParent(), CHANGED);
- }
- }
- } else {
- if (!(rsrc instanceof IContainer)) {
- decoration.addSuffix("(deprecated)"); // Will drop on
- // commit
- decoration.addOverlay(UIIcons.OVR_PENDING_REMOVE);
- orState(rsrc.getParent(), CHANGED);
- }
- }
- } else {
- if (entry.getStage() != GitIndex.STAGE_0) {
- decoration.addSuffix("(conflict)");
- decoration.addOverlay(UIIcons.OVR_CONFLICT);
- orState(rsrc.getParent(), CHANGED);
- return;
- }
-
- if (blob == null) {
- decoration.addOverlay(UIIcons.OVR_PENDING_ADD);
- orState(rsrc.getParent(), CHANGED);
- } else {
-
- if (entry.isAssumedValid()) {
- decoration.addOverlay(UIIcons.OVR_ASSUMEVALID);
- return;
- }
-
- decoration.addOverlay(UIIcons.OVR_SHARED);
-
- if (entry.isModified(mapped.getWorkDir(), true)) {
- decoration.addPrefix(">");
- decoration.addSuffix("(not updated)");
- orState(rsrc.getParent(), CHANGED);
- } else {
- if (!entry.getObjectId().equals(blob.getId()))
- decoration.addPrefix(">");
- else
- decoration.addPrefix(""); // set it to avoid further calls
- }
- }
- }
- }
- } catch (IOException e) {
- decoration.addSuffix("?");
- // If we throw an exception Eclipse will log the error and
- // unregister us thereby preventing us from dragging down the
- // entire workbench because we are crashing.
- //
- throw new RuntimeException(UIText.Decorator_failedLazyLoading, e);
- } catch (CoreException e) {
- throw new RuntimeException(UIText.Decorator_failedLazyLoading, e);
- }
- }
-
- private void orState(final IResource rsrc, int flag) {
- if (rsrc == null || rsrc.getType() == IResource.ROOT) {
- return;
- }
-
- try {
- Integer dirty = (Integer) rsrc.getSessionProperty(GITFOLDERDIRTYSTATEPROPERTY);
- Runnable runnable = new Runnable() {
- public void run() {
- // Async could be called after a
- // project is closed or a
- // resource is deleted
- if (!rsrc.isAccessible())
- return;
- fireLabelProviderChanged(new LabelProviderChangedEvent(
- GitResourceDecorator.this, rsrc));
- }
- };
- if (dirty == null) {
- rsrc.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, new Integer(flag));
- orState(rsrc.getParent(), flag);
-// if (Thread.currentThread() == Display.getDefault().getThread())
-// runnable.run();
-// else
- Display.getDefault().asyncExec(runnable);
- } else {
- if ((dirty.intValue() | flag) != dirty.intValue()) {
- dirty = new Integer(dirty.intValue() | flag);
- rsrc.setSessionProperty(GITFOLDERDIRTYSTATEPROPERTY, dirty);
- orState(rsrc.getParent(), dirty.intValue());
-// if (Thread.currentThread() == Display.getDefault().getThread())
-// runnable.run();
-// else
- Display.getDefault().asyncExec(runnable);
- }
- }
- } catch (CoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- @Override
- public boolean isLabelProperty(Object element, String property) {
- return super.isLabelProperty(element, property);
- }
-}
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java
new file mode 100644
index 0000000..8d6c741
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/IDecoratableResource.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+
+package org.spearce.egit.ui.internal.decorators;
+
+import org.eclipse.core.resources.IResource;
+
+/**
+ * Represents the state of a resource that can be used as a basis for decoration
+ */
+public interface IDecoratableResource {
+
+ /**
+ * Gets the type of the resource as defined by {@link IResource}
+ *
+ * @return the type of the resource
+ */
+ int getType();
+
+ /**
+ * Gets the name of the resource
+ *
+ * @return the name of the resource
+ */
+ String getName();
+}
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java
new file mode 100644
index 0000000..2ef0292
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/preferences/GitDecoratorPreferencePage.java
@@ -0,0 +1,735 @@
+/*******************************************************************************
+ * Copyright (C) 2003, 2006 Subclipse project and others.
+ * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.ui.internal.preferences;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.DecorationContext;
+import org.eclipse.jface.viewers.DecorationOverlayIcon;
+import org.eclipse.jface.viewers.IDecoration;
+import org.eclipse.jface.viewers.IDecorationContext;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.dialogs.ListSelectionDialog;
+import org.eclipse.ui.ide.IDE.SharedImages;
+import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
+import org.spearce.egit.ui.Activator;
+import org.spearce.egit.ui.UIPreferences;
+import org.spearce.egit.ui.UIText;
+import org.spearce.egit.ui.internal.SWTUtils;
+import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;
+import org.spearce.egit.ui.internal.decorators.IDecoratableResource;
+
+/**
+ * Preference page for customizing Git label decorations
+ */
+public class GitDecoratorPreferencePage extends PreferencePage implements
+ IWorkbenchPreferencePage {
+
+ private Text fileTextFormat;
+
+ private Text folderTextFormat;
+
+ private Text projectTextFormat;
+
+ private Button showDirty;
+
+ private Preview fPreview;
+
+ private static final Collection PREVIEW_FILESYSTEM_ROOT;
+
+ private static ThemeListener fThemeListener;
+
+ static {
+ final PreviewResource project = new PreviewResource(
+ "Project", IResource.PROJECT); //$NON-NLS-1$1
+ final ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();
+ children.add(new PreviewResource("folder", IResource.FOLDER)); //$NON-NLS-1$
+ children.add(new PreviewResource("file.txt", IResource.FILE)); //$NON-NLS-1$
+ project.children = children;
+ PREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);
+ }
+
+ /**
+ * Constructs a decorator preference page
+ */
+ public GitDecoratorPreferencePage() {
+ setDescription(UIText.DecoratorPreferencesPage_description);
+ }
+
+ /**
+ * @see PreferencePage#createContents(Composite)
+ */
+ protected Control createContents(Composite parent) {
+ Composite headerGroup = new Composite(parent, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.marginWidth = 0;
+ layout.marginHeight = 0;
+ layout.verticalSpacing = 10;
+ headerGroup.setLayout(layout);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ headerGroup.setLayoutData(data);
+
+ SWTUtils.createPreferenceLink(
+ (IWorkbenchPreferenceContainer) getContainer(), headerGroup,
+ "org.eclipse.ui.preferencePages.Decorators",
+ UIText.DecoratorPreferencesPage_labelDecorationsLink); //$NON-NLS-1$
+
+ SWTUtils.createLabel(headerGroup,
+ UIText.DecoratorPreferencesPage_decorationSettings);
+
+ TabFolder tabFolder = new TabFolder(parent, SWT.NONE);
+ GridData tabData = SWTUtils.createHVFillGridData();
+ tabData.heightHint = 100;
+ tabFolder.setLayoutData(tabData);
+
+ TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
+ tabItem.setText(UIText.DecoratorPreferencesPage_generalTabFolder);
+ tabItem.setControl(createGeneralDecoratorPage(tabFolder));
+
+ tabItem = new TabItem(tabFolder, SWT.NONE);
+ tabItem.setText(UIText.DecoratorPreferencesPage_textLabel);
+ tabItem.setControl(createTextDecoratorPage(tabFolder));
+
+ tabItem = new TabItem(tabFolder, SWT.NONE);
+ tabItem.setText(UIText.DecoratorPreferencesPage_iconLabel);
+ tabItem.setControl(createIconDecoratorPage(tabFolder));
+
+ initializeValues();
+
+ fPreview = new Preview(parent);
+ fPreview.refresh();
+
+ // TODO: Add help text for this preference page
+
+ fThemeListener = new ThemeListener(fPreview);
+ PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(
+ fThemeListener);
+ Dialog.applyDialogFont(parent);
+ return tabFolder;
+ }
+
+ private Control createGeneralDecoratorPage(Composite parent) {
+ Composite composite = new Composite(parent, SWT.NULL);
+
+ GridLayout layout = new GridLayout();
+ composite.setLayout(layout);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ composite.setLayoutData(data);
+
+ showDirty = SWTUtils.createCheckBox(composite,
+ UIText.DecoratorPreferencesPage_computeDeep);
+
+ return composite;
+ }
+
+ /**
+ * Creates the controls for the first tab folder
+ *
+ * @param parent
+ *
+ * @return the control
+ */
+ private Control createTextDecoratorPage(Composite parent) {
+ Composite fileTextGroup = new Composite(parent, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 3;
+ fileTextGroup.setLayout(layout);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ fileTextGroup.setLayoutData(data);
+
+ TextPair format = createFormatEditorControl(fileTextGroup,
+ UIText.DecoratorPreferencesPage_fileFormatLabel,
+ UIText.DecoratorPreferencesPage_addVariablesAction,
+ getFileBindingDescriptions());
+ fileTextFormat = format.t1;
+
+ format = createFormatEditorControl(fileTextGroup,
+ UIText.DecoratorPreferencesPage_folderFormatLabel,
+ UIText.DecoratorPreferencesPage_addVariablesAction,
+ getFolderBindingDescriptions());
+ folderTextFormat = format.t1;
+
+ format = createFormatEditorControl(fileTextGroup,
+ UIText.DecoratorPreferencesPage_projectFormatLabel,
+ UIText.DecoratorPreferencesPage_addVariablesAction,
+ getProjectBindingDescriptions());
+ projectTextFormat = format.t1;
+
+ return fileTextGroup;
+ }
+
+ private Control createIconDecoratorPage(Composite parent) {
+ Composite imageGroup = new Composite(parent, SWT.NULL);
+ GridLayout layout = new GridLayout();
+ imageGroup.setLayout(layout);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ imageGroup.setLayoutData(data);
+
+ return imageGroup;
+ }
+
+ private TextPair createFormatEditorControl(Composite composite,
+ String title, String buttonText, final Map supportedBindings) {
+
+ SWTUtils.createLabel(composite, title);
+
+ Text format = new Text(composite, SWT.BORDER);
+ format.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ format.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updatePreview();
+ }
+ });
+ Button b = new Button(composite, SWT.NONE);
+ b.setText(buttonText);
+ GridData data = new GridData();
+ data.horizontalAlignment = GridData.FILL;
+ int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+ data.widthHint = Math.max(widthHint, b.computeSize(SWT.DEFAULT,
+ SWT.DEFAULT, true).x);
+ b.setLayoutData(data);
+ final Text formatToInsert = format;
+ b.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ addVariables(formatToInsert, supportedBindings);
+ }
+ });
+
+ return new TextPair(format, null);
+ }
+
+ /**
+ * Initializes states of the controls from the preference store.
+ */
+ private void initializeValues() {
+ final IPreferenceStore store = getPreferenceStore();
+
+ fileTextFormat.setText(store
+ .getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
+ folderTextFormat.setText(store
+ .getString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));
+ projectTextFormat.setText(store
+ .getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));
+
+ showDirty.setSelection(store
+ .getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
+
+ setValid(true);
+ }
+
+ /**
+ * @see IWorkbenchPreferencePage#init(IWorkbench)
+ */
+ public void init(IWorkbench workbench) {
+ // No-op
+ }
+
+ /**
+ * OK was clicked. Store the preferences to the plugin store
+ *
+ * @return whether it is okay to close the preference page
+ */
+ public boolean performOk() {
+ IPreferenceStore store = getPreferenceStore();
+ final boolean okToClose = performOk(store);
+ if (store.needsSaving()) {
+ Activator.getDefault().savePluginPreferences();
+ Activator.broadcastPropertyChange(new PropertyChangeEvent(this,
+ Activator.DECORATORS_CHANGED, null, null));
+ }
+ return okToClose;
+ }
+
+ /**
+ * Store the preferences to the given preference store
+ *
+ * @param store
+ * the preference store to store the preferences to
+ *
+ * @return whether it operation succeeded
+ */
+ private boolean performOk(IPreferenceStore store) {
+
+ store.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,
+ fileTextFormat.getText());
+ store.setValue(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,
+ folderTextFormat.getText());
+ store.setValue(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,
+ projectTextFormat.getText());
+
+ store.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY, showDirty
+ .getSelection());
+
+ return true;
+ }
+
+ /**
+ * Defaults was clicked. Restore the Git decoration preferences to their
+ * default values
+ */
+ protected void performDefaults() {
+ super.performDefaults();
+ IPreferenceStore store = getPreferenceStore();
+
+ fileTextFormat.setText(store
+ .getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
+ folderTextFormat
+ .setText(store
+ .getDefaultString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));
+ projectTextFormat
+ .setText(store
+ .getDefaultString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));
+
+ showDirty.setSelection(store
+ .getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
+ }
+
+ /**
+ * Returns the preference store that belongs to the our plugin.
+ *
+ * This is important because we want to store our preferences separately
+ * from the desktop.
+ *
+ * @return the preference store for this plugin
+ */
+ protected IPreferenceStore doGetPreferenceStore() {
+ return Activator.getDefault().getPreferenceStore();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.dialogs.DialogPage#dispose()
+ */
+ public void dispose() {
+ PlatformUI.getWorkbench().getThemeManager()
+ .removePropertyChangeListener(fThemeListener);
+ super.dispose();
+ }
+
+ private static class ThemeListener implements IPropertyChangeListener {
+ private final Preview preview;
+
+ ThemeListener(Preview preview) {
+ this.preview = preview;
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ preview.refresh();
+ }
+ }
+
+ /**
+ * Adds another variable to the given target text
+ *
+ * A ListSelectionDialog pops up and allow the user to choose the variable,
+ * which is then inserted at current position in <code>text</code>
+ *
+ * @param target
+ * the target to add the variable to
+ * @param bindings
+ * the map of bindings
+ */
+ private void addVariables(Text target, Map bindings) {
+
+ final List<StringPair> variables = new ArrayList<StringPair>(bindings
+ .size());
+
+ ILabelProvider labelProvider = new LabelProvider() {
+ public String getText(Object element) {
+ return ((StringPair) element).s1
+ + " - " + ((StringPair) element).s2; //$NON-NLS-1$
+ }
+ };
+
+ IStructuredContentProvider contentsProvider = new IStructuredContentProvider() {
+ public Object[] getElements(Object inputElement) {
+ return variables.toArray(new StringPair[variables.size()]);
+ }
+
+ public void dispose() {
+ // No-op
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput,
+ Object newInput) {
+ // No-op
+ }
+ };
+
+ for (Iterator it = bindings.keySet().iterator(); it.hasNext();) {
+ StringPair variable = new StringPair();
+ variable.s1 = (String) it.next(); // variable
+ variable.s2 = (String) bindings.get(variable.s1); // description
+ variables.add(variable);
+ }
+
+ ListSelectionDialog dialog = new ListSelectionDialog(this.getShell(),
+ this, contentsProvider, labelProvider,
+ UIText.DecoratorPreferencesPage_selectVariablesToAdd);
+ dialog.setTitle(UIText.DecoratorPreferencesPage_addVariablesTitle);
+ if (dialog.open() != Window.OK)
+ return;
+
+ Object[] result = dialog.getResult();
+
+ for (int i = 0; i < result.length; i++) {
+ target.insert("{" + ((StringPair) result[i]).s1 + "}"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ class StringPair {
+ String s1;
+
+ String s2;
+ }
+
+ class TextPair {
+ TextPair(Text t1, Text t2) {
+ this.t1 = t1;
+ this.t2 = t2;
+ }
+
+ Text t1;
+
+ Text t2;
+ }
+
+ /**
+ * Gets the map of bindings between variables and description, to use for
+ * the format editors for files
+ *
+ * @return the bindings
+ */
+ private Map getFileBindingDescriptions() {
+ Map<String, String> bindings = new HashMap<String, String>();
+ bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
+ UIText.DecoratorPreferencesPage_nameResourceVariable);
+ return bindings;
+ }
+
+ /**
+ * Gets the map of bindings between variables and description, to use for
+ * the format editors for folders
+ *
+ * @return the bindings
+ */
+ private Map getFolderBindingDescriptions() {
+ Map<String, String> bindings = new HashMap<String, String>();
+ bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
+ UIText.DecoratorPreferencesPage_nameResourceVariable);
+ return bindings;
+ }
+
+ /**
+ * Gets the map of bindings between variables and description, to use for
+ * the format editors for projects
+ *
+ * @return the bindings
+ */
+ private Map getProjectBindingDescriptions() {
+ Map<String, String> bindings = new HashMap<String, String>();
+ bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
+ UIText.DecoratorPreferencesPage_nameResourceVariable);
+ return bindings;
+ }
+
+ private void updatePreview() {
+ if (fPreview != null)
+ fPreview.refresh();
+ }
+
+ /**
+ * Preview control for showing how changes in the dialog will affect
+ * decoration
+ */
+ private class Preview extends LabelProvider implements Observer,
+ ITreeContentProvider {
+
+ private final ResourceManager fImageCache;
+
+ private final TreeViewer fViewer;
+
+ private DecorationHelper fHelper;
+
+ public Preview(Composite composite) {
+ reloadDecorationHelper(); // Has to happen before the tree control
+ // is constructed
+ SWTUtils.createLabel(composite,
+ UIText.DecoratorPreferencesPage_preview);
+ fImageCache = new LocalResourceManager(JFaceResources
+ .getResources());
+ fViewer = new TreeViewer(composite);
+ GridData previewGridData = SWTUtils.createHVFillGridData();
+ previewGridData.heightHint = 100;
+ fViewer.getControl().setLayoutData(previewGridData);
+ fViewer.setContentProvider(this);
+ fViewer.setLabelProvider(this);
+ fViewer.setInput(PREVIEW_FILESYSTEM_ROOT);
+ fViewer.expandAll();
+ fHelper = new DecorationHelper(new PreferenceStore());
+ }
+
+ private void reloadDecorationHelper() {
+ PreferenceStore store = new PreferenceStore();
+ performOk(store);
+ fHelper = new DecorationHelper(store);
+ }
+
+ public void refresh() {
+ reloadDecorationHelper();
+ fViewer.refresh(true);
+ setColorsAndFonts(fViewer.getTree().getItems());
+ }
+
+ @SuppressWarnings("unused")
+ private void setColorsAndFonts(TreeItem[] items) {
+ // TODO: Implement colors and fonts
+ }
+
+ public void update(Observable o, Object arg) {
+ refresh();
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ return ((PreviewResource) parentElement).children.toArray();
+ }
+
+ public Object getParent(Object element) {
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ return !((PreviewResource) element).children.isEmpty();
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return ((Collection) inputElement).toArray();
+ }
+
+ public void dispose() {
+ fImageCache.dispose();
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // No-op
+ }
+
+ public Color getBackground(Object element) {
+ return getDecoration(element).getBackgroundColor();
+ }
+
+ public Color getForeground(Object element) {
+ return getDecoration(element).getForegroundColor();
+ }
+
+ public Font getFont(Object element) {
+ return getDecoration(element).getFont();
+ }
+
+ public String getText(Object element) {
+ final PreviewDecoration decoration = getDecoration(element);
+ final StringBuffer buffer = new StringBuffer();
+ final String prefix = decoration.getPrefix();
+ if (prefix != null)
+ buffer.append(prefix);
+ buffer.append(((PreviewResource) element).getName());
+ final String suffix = decoration.getSuffix();
+ if (suffix != null)
+ buffer.append(suffix);
+ return buffer.toString();
+ }
+
+ public Image getImage(Object element) {
+ final String s;
+ switch (((PreviewResource) element).type) {
+ case IResource.PROJECT:
+ s = SharedImages.IMG_OBJ_PROJECT;
+ break;
+ case IResource.FOLDER:
+ s = ISharedImages.IMG_OBJ_FOLDER;
+ break;
+ default:
+ s = ISharedImages.IMG_OBJ_FILE;
+ break;
+ }
+ final Image baseImage = PlatformUI.getWorkbench().getSharedImages()
+ .getImage(s);
+ final ImageDescriptor overlay = getDecoration(element).getOverlay();
+ if (overlay == null)
+ return baseImage;
+ try {
+ return fImageCache.createImage(new DecorationOverlayIcon(
+ baseImage, overlay, IDecoration.BOTTOM_RIGHT));
+ } catch (Exception e) {
+ Activator.logError(e.getMessage(), e);
+ }
+
+ return null;
+ }
+
+ private PreviewDecoration getDecoration(Object element) {
+ PreviewDecoration decoration = new PreviewDecoration();
+ fHelper.decorate(decoration, (PreviewResource) element);
+ return decoration;
+ }
+ }
+
+ private static class PreviewResource implements IDecoratableResource {
+ public final String name;
+
+ public final int type;
+
+ public Collection children;
+
+ public PreviewResource(String name, int type) {
+ this.name = name;
+ this.type = type;
+ this.children = Collections.EMPTY_LIST;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getType() {
+ return type;
+ }
+ }
+
+ private class PreviewDecoration implements IDecoration {
+
+ private List<String> prefixes = new ArrayList<String>();
+
+ private List<String> suffixes = new ArrayList<String>();
+
+ private ImageDescriptor overlay = null;
+
+ private Font font;
+
+ private Color backgroundColor;
+
+ private Color foregroundColor;
+
+ public void addOverlay(ImageDescriptor overlayImage) {
+ overlay = overlayImage;
+ }
+
+ public void addOverlay(ImageDescriptor overlayImage, int quadrant) {
+ overlay = overlayImage;
+ }
+
+ public void addPrefix(String prefix) {
+ prefixes.add(prefix);
+ }
+
+ public void addSuffix(String suffix) {
+ suffixes.add(suffix);
+ }
+
+ public IDecorationContext getDecorationContext() {
+ return new DecorationContext();
+ }
+
+ public void setBackgroundColor(Color color) {
+ backgroundColor = color;
+ }
+
+ public void setForegroundColor(Color color) {
+ foregroundColor = color;
+ }
+
+ public void setFont(Font font) {
+ this.font = font;
+ }
+
+ public ImageDescriptor getOverlay() {
+ return overlay;
+ }
+
+ public String getPrefix() {
+ StringBuffer sb = new StringBuffer();
+ for (Iterator<String> iter = prefixes.iterator(); iter.hasNext();) {
+ sb.append(iter.next());
+ }
+ return sb.toString();
+ }
+
+ public String getSuffix() {
+ StringBuffer sb = new StringBuffer();
+ for (Iterator<String> iter = suffixes.iterator(); iter.hasNext();) {
+ sb.append(iter.next());
+ }
+ return sb.toString();
+ }
+
+ public Font getFont() {
+ return font;
+ }
+
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ public Color getForegroundColor() {
+ return foregroundColor;
+ }
+ }
+}
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
index a86e58b..4a0a387 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
@@ -169,7 +169,6 @@ RefSpecPage_annotatedTagsAutoFollow=Automatically follow tags if we fetch the th
RefSpecPage_annotatedTagsFetchTags=Always fetch tags, even if we do not have the thing it points at
RefSpecPage_annotatedTagsNoTags=Never fetch tags, even if we have the thing it points at
-Decorator_failedLazyLoading=Resource decorator failed to load tree contents on demand.
QuickDiff_failedLoading=Quick diff failed to obtain file data.
ResourceHistory_toggleCommentWrap=Wrap Comments
@@ -340,3 +339,27 @@ BranchSelectionDialog_ResetTypeMixed=&Mixed (working directory unmodified)
BranchSelectionDialog_ResetTypeSoft=&Soft (Index and working directory unmodified)
BranchSelectionDialog_Tags=Tags
BranchSelectionDialog_Refs=&Refs
+
+Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources.
+
+DecoratorPreferencesPage_addVariablesTitle=Add Variables
+DecoratorPreferencesPage_addVariablesAction=Add &Variables...
+DecoratorPreferencesPage_computeDeep=Include &ancestors when re-decorating changed resources
+DecoratorPreferencesPage_description=Shows Git specific information on resources in projects under version control.
+
+DecoratorPreferencesPage_decorationSettings=Decoration &settings:
+DecoratorPreferencesPage_preview=Preview:
+DecoratorPreferencesPage_fileFormatLabel=&Files:
+DecoratorPreferencesPage_folderFormatLabel=F&olders:
+DecoratorPreferencesPage_projectFormatLabel=&Projects:
+DecoratorPreferencesPage_fileFormatDefault={name}
+DecoratorPreferencesPage_folderFormatDefault={name}
+DecoratorPreferencesPage_projectFormatDefault={name}
+DecoratorPreferencesPage_labelDecorationsLink=See <a>''{0}''</a> to enable or disable Git decorations.
+DecoratorPreferencesPage_generalTabFolder=&General
+DecoratorPreferencesPage_nameResourceVariable=name of the resource being decorated
+DecoratorPreferencesPage_selectFormats=Select the format for file, folders, and project text labels:
+DecoratorPreferencesPage_selectVariablesToAdd=Select the &variables to add to the decoration format:
+DecoratorPreferencesPage_textLabel=T&ext Decorations
+DecoratorPreferencesPage_iconLabel=&Icon Decorations
+
--
1.6.1.2.309.g2ea3
next prev parent reply other threads:[~2009-02-05 1:02 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-02-05 1:00 [EGIT PATCH 00/11] Support customizable label decorations Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 01/11] Add support code to handle plugin property changes Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 03/11] Add a specialized team exception for Git Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 04/11] Add new class ExceptionCollector for grouping exceptions Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 05/11] Add new class SWTUtils with helper-methods for creating controls Tor Arne Vestbø
2009-02-05 1:00 ` Tor Arne Vestbø [this message]
2009-02-05 1:00 ` [EGIT PATCH 07/11] Add binding for name of the current branch Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 08/11] Add icon decoration for tracked and untracked resources Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 09/11] Implement decorations of dirty, staged, and conflicting resources Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 10/11] Don't decorate every single resource on repository change Tor Arne Vestbø
2009-02-05 1:00 ` [EGIT PATCH 11/11] Implement label decorations for folders and projects Tor Arne Vestbø
2009-02-05 20:02 ` [EGIT PATCH 06/11] Implement basic customizable label decorations with preferences Robin Rosenberg
2009-02-05 20:21 ` Tor Arne Vestbø
2009-02-05 21:00 ` Tor Arne Vestbø
2009-02-05 21:36 ` Robin Rosenberg
2009-02-05 21:44 ` Tor Arne Vestbø
2009-02-05 20:04 ` Robin Rosenberg
2009-02-05 15:48 ` [EGIT PATCH 02/11] Use Set instead of array to keep track of change listeners Shawn O. Pearce
2009-02-05 16:36 ` Tor Arne Vestbø
2009-02-05 18:28 ` [EGIT PATCH 02/11 v2] " Tor Arne Vestbø
2009-02-05 15:53 ` [EGIT PATCH 01/11] Add support code to handle plugin property changes Shawn O. Pearce
2009-02-05 16:35 ` Tor Arne Vestbø
2009-02-05 16:40 ` Shawn O. Pearce
2009-02-05 18:22 ` [EGIT PATCH v2] " Tor Arne Vestbø
2009-02-05 1:04 ` [EGIT PATCH 00/11] Support customizable label decorations Tor Arne Vestbø
2009-02-05 16:06 ` Shawn O. Pearce
2009-02-05 16:17 ` Tor Arne Vestbø
2009-02-05 18:32 ` Robin Rosenberg
2009-02-05 18:37 ` Tor Arne Vestbø
2009-02-05 22:09 ` Robin Rosenberg
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1233795618-20249-7-git-send-email-torarnv@gmail.com \
--to=torarnv@gmail.com \
--cc=git@vger.kernel.org \
--cc=robin.rosenberg@dewire.com \
--cc=spearce@spearce.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.