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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).