Add icon decoration for tracked and untracked resources
[egit/torarne.git] / org.spearce.egit.ui / src / org / spearce / egit / ui / internal / preferences / GitDecoratorPreferencePage.java
blob06ddf50f6ca9b796f11cb7e3cc9dc3a4d73f3d59
1 /*******************************************************************************
2 * Copyright (C) 2003, 2006 Subclipse project and others.
3 * Copyright (C) 2008, Tor Arne Vestbø <torarnv@gmail.com>
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * See LICENSE for the full license text, also available.
8 *******************************************************************************/
9 package org.spearce.egit.ui.internal.preferences;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Observable;
19 import java.util.Observer;
21 import org.eclipse.core.resources.IResource;
22 import org.eclipse.jface.dialogs.Dialog;
23 import org.eclipse.jface.dialogs.IDialogConstants;
24 import org.eclipse.jface.preference.IPreferenceStore;
25 import org.eclipse.jface.preference.PreferencePage;
26 import org.eclipse.jface.preference.PreferenceStore;
27 import org.eclipse.jface.resource.ImageDescriptor;
28 import org.eclipse.jface.resource.JFaceResources;
29 import org.eclipse.jface.resource.LocalResourceManager;
30 import org.eclipse.jface.resource.ResourceManager;
31 import org.eclipse.jface.util.IPropertyChangeListener;
32 import org.eclipse.jface.util.PropertyChangeEvent;
33 import org.eclipse.jface.viewers.DecorationContext;
34 import org.eclipse.jface.viewers.DecorationOverlayIcon;
35 import org.eclipse.jface.viewers.IDecoration;
36 import org.eclipse.jface.viewers.IDecorationContext;
37 import org.eclipse.jface.viewers.ILabelProvider;
38 import org.eclipse.jface.viewers.IStructuredContentProvider;
39 import org.eclipse.jface.viewers.ITreeContentProvider;
40 import org.eclipse.jface.viewers.LabelProvider;
41 import org.eclipse.jface.viewers.TreeViewer;
42 import org.eclipse.jface.viewers.Viewer;
43 import org.eclipse.jface.window.Window;
44 import org.eclipse.swt.SWT;
45 import org.eclipse.swt.events.ModifyEvent;
46 import org.eclipse.swt.events.ModifyListener;
47 import org.eclipse.swt.events.SelectionAdapter;
48 import org.eclipse.swt.events.SelectionEvent;
49 import org.eclipse.swt.events.SelectionListener;
50 import org.eclipse.swt.graphics.Color;
51 import org.eclipse.swt.graphics.Font;
52 import org.eclipse.swt.graphics.Image;
53 import org.eclipse.swt.layout.GridData;
54 import org.eclipse.swt.widgets.Button;
55 import org.eclipse.swt.widgets.Composite;
56 import org.eclipse.swt.widgets.Control;
57 import org.eclipse.swt.widgets.Event;
58 import org.eclipse.swt.widgets.Listener;
59 import org.eclipse.swt.widgets.TabFolder;
60 import org.eclipse.swt.widgets.TabItem;
61 import org.eclipse.swt.widgets.Text;
62 import org.eclipse.swt.widgets.TreeItem;
63 import org.eclipse.ui.ISharedImages;
64 import org.eclipse.ui.IWorkbench;
65 import org.eclipse.ui.IWorkbenchPreferencePage;
66 import org.eclipse.ui.PlatformUI;
67 import org.eclipse.ui.dialogs.ListSelectionDialog;
68 import org.eclipse.ui.ide.IDE.SharedImages;
69 import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
70 import org.spearce.egit.ui.Activator;
71 import org.spearce.egit.ui.UIPreferences;
72 import org.spearce.egit.ui.UIText;
73 import org.spearce.egit.ui.internal.SWTUtils;
74 import org.spearce.egit.ui.internal.decorators.GitLightweightDecorator.DecorationHelper;
75 import org.spearce.egit.ui.internal.decorators.IDecoratableResource;
77 /**
78 * Preference page for customizing Git label decorations
80 public class GitDecoratorPreferencePage extends PreferencePage implements
81 IWorkbenchPreferencePage {
83 private Text fileTextFormat;
85 private Text folderTextFormat;
87 private Text projectTextFormat;
89 private Button computeDeepDirtyState;
91 private Button showTracked;
93 private Button showUntracked;
95 private Preview preview;
97 private static final Collection PREVIEW_FILESYSTEM_ROOT;
99 private static IPropertyChangeListener themeListener;
101 static {
102 final PreviewResource project = new PreviewResource(
103 "Project", IResource.PROJECT, "master", true, false); //$NON-NLS-1$1
104 final ArrayList<PreviewResource> children = new ArrayList<PreviewResource>();
105 children.add(new PreviewResource(
106 "folder", IResource.FOLDER, null, true, false)); //$NON-NLS-1$
107 children.add(new PreviewResource(
108 "file.txt", IResource.FILE, null, true, false)); //$NON-NLS-1$
109 children.add(new PreviewResource(
110 "untracked.txt", IResource.FILE, null, false, false)); //$NON-NLS-1$
111 children.add(new PreviewResource(
112 "ignored.txt", IResource.FILE, null, false, true)); //$NON-NLS-1$
113 project.children = children;
114 PREVIEW_FILESYSTEM_ROOT = Collections.singleton(project);
118 * Constructs a decorator preference page
120 public GitDecoratorPreferencePage() {
121 setDescription(UIText.DecoratorPreferencesPage_description);
125 * @see PreferencePage#createContents(Composite)
127 protected Control createContents(Composite parent) {
129 Composite composite = SWTUtils.createHVFillComposite(parent,
130 SWTUtils.MARGINS_NONE);
132 SWTUtils.createPreferenceLink(
133 (IWorkbenchPreferenceContainer) getContainer(), composite,
134 "org.eclipse.ui.preferencePages.Decorators",
135 UIText.DecoratorPreferencesPage_labelDecorationsLink); //$NON-NLS-1$
137 TabFolder tabFolder = new TabFolder(composite, SWT.NONE);
138 tabFolder.setLayoutData(SWTUtils.createHVFillGridData());
140 TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
141 tabItem.setText(UIText.DecoratorPreferencesPage_generalTabFolder);
142 tabItem.setControl(createGeneralDecoratorPage(tabFolder));
144 tabItem = new TabItem(tabFolder, SWT.NONE);
145 tabItem.setText(UIText.DecoratorPreferencesPage_textLabel);
146 tabItem.setControl(createTextDecoratorPage(tabFolder));
148 tabItem = new TabItem(tabFolder, SWT.NONE);
149 tabItem.setText(UIText.DecoratorPreferencesPage_iconLabel);
150 tabItem.setControl(createIconDecoratorPage(tabFolder));
152 initializeValues();
154 preview = new Preview(composite);
155 preview.refresh();
157 // TODO: Add help text for this preference page
159 themeListener = new IPropertyChangeListener() {
160 public void propertyChange(PropertyChangeEvent event) {
161 preview.refresh();
164 PlatformUI.getWorkbench().getThemeManager().addPropertyChangeListener(
165 themeListener);
167 Dialog.applyDialogFont(parent);
169 return composite;
172 private Control createGeneralDecoratorPage(Composite parent) {
173 Composite composite = SWTUtils.createHVFillComposite(parent,
174 SWTUtils.MARGINS_DEFAULT);
176 computeDeepDirtyState = SWTUtils.createCheckBox(composite,
177 UIText.DecoratorPreferencesPage_computeDeep);
179 return composite;
183 * Creates the controls for the first tab folder
185 * @param parent
187 * @return the control
189 private Control createTextDecoratorPage(Composite parent) {
190 Composite fileTextGroup = SWTUtils.createHVFillComposite(parent,
191 SWTUtils.MARGINS_DEFAULT, 3);
193 TextPair format = createFormatEditorControl(fileTextGroup,
194 UIText.DecoratorPreferencesPage_fileFormatLabel,
195 UIText.DecoratorPreferencesPage_addVariablesAction,
196 getFileBindingDescriptions());
197 fileTextFormat = format.t1;
199 format = createFormatEditorControl(fileTextGroup,
200 UIText.DecoratorPreferencesPage_folderFormatLabel,
201 UIText.DecoratorPreferencesPage_addVariablesAction,
202 getFolderBindingDescriptions());
203 folderTextFormat = format.t1;
205 format = createFormatEditorControl(fileTextGroup,
206 UIText.DecoratorPreferencesPage_projectFormatLabel,
207 UIText.DecoratorPreferencesPage_addVariablesAction,
208 getProjectBindingDescriptions());
209 projectTextFormat = format.t1;
211 return fileTextGroup;
214 private Control createIconDecoratorPage(Composite parent) {
215 Composite imageGroup = SWTUtils.createHVFillComposite(parent,
216 SWTUtils.MARGINS_DEFAULT, 2);
218 showTracked = SWTUtils.createCheckBox(imageGroup,
219 UIText.DecoratorPreferencesPage_iconsShowTracked);
220 showUntracked = SWTUtils.createCheckBox(imageGroup,
221 UIText.DecoratorPreferencesPage_iconsShowUntracked);
223 return imageGroup;
226 private TextPair createFormatEditorControl(Composite composite,
227 String title, String buttonText, final Map supportedBindings) {
229 SWTUtils.createLabel(composite, title);
231 Text format = new Text(composite, SWT.BORDER);
232 format.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
233 format.addModifyListener(new ModifyListener() {
234 public void modifyText(ModifyEvent e) {
235 updatePreview();
238 Button b = new Button(composite, SWT.NONE);
239 b.setText(buttonText);
240 GridData data = new GridData();
241 data.horizontalAlignment = GridData.FILL;
242 int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
243 data.widthHint = Math.max(widthHint, b.computeSize(SWT.DEFAULT,
244 SWT.DEFAULT, true).x);
245 b.setLayoutData(data);
246 final Text formatToInsert = format;
247 b.addListener(SWT.Selection, new Listener() {
248 public void handleEvent(Event event) {
249 addVariables(formatToInsert, supportedBindings);
253 return new TextPair(format, null);
257 * Initializes states of the controls from the preference store.
259 private void initializeValues() {
260 final IPreferenceStore store = getPreferenceStore();
262 computeDeepDirtyState.setSelection(store
263 .getBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
265 fileTextFormat.setText(store
266 .getString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
267 folderTextFormat.setText(store
268 .getString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));
269 projectTextFormat.setText(store
270 .getString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));
272 showTracked.setSelection(store
273 .getBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));
274 showUntracked.setSelection(store
275 .getBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));
277 SelectionListener selectionListener = new SelectionAdapter() {
278 public void widgetSelected(SelectionEvent e) {
279 preview.refresh();
283 computeDeepDirtyState.addSelectionListener(selectionListener);
284 showTracked.addSelectionListener(selectionListener);
285 showUntracked.addSelectionListener(selectionListener);
287 setValid(true);
291 * @see IWorkbenchPreferencePage#init(IWorkbench)
293 public void init(IWorkbench workbench) {
294 // No-op
298 * OK was clicked. Store the preferences to the plugin store
300 * @return whether it is okay to close the preference page
302 public boolean performOk() {
303 IPreferenceStore store = getPreferenceStore();
304 final boolean okToClose = performOk(store);
305 if (store.needsSaving()) {
306 Activator.getDefault().savePluginPreferences();
307 Activator.broadcastPropertyChange(new PropertyChangeEvent(this,
308 Activator.DECORATORS_CHANGED, null, null));
310 return okToClose;
314 * Store the preferences to the given preference store
316 * @param store
317 * the preference store to store the preferences to
319 * @return whether it operation succeeded
321 private boolean performOk(IPreferenceStore store) {
323 store.setValue(UIPreferences.DECORATOR_CALCULATE_DIRTY,
324 computeDeepDirtyState.getSelection());
326 store.setValue(UIPreferences.DECORATOR_FILETEXT_DECORATION,
327 fileTextFormat.getText());
328 store.setValue(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION,
329 folderTextFormat.getText());
330 store.setValue(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION,
331 projectTextFormat.getText());
333 store.setValue(UIPreferences.DECORATOR_SHOW_TRACKED_ICON, showTracked
334 .getSelection());
335 store.setValue(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON,
336 showUntracked.getSelection());
338 return true;
342 * Defaults was clicked. Restore the Git decoration preferences to their
343 * default values
345 protected void performDefaults() {
346 super.performDefaults();
347 IPreferenceStore store = getPreferenceStore();
349 computeDeepDirtyState.setSelection(store
350 .getDefaultBoolean(UIPreferences.DECORATOR_CALCULATE_DIRTY));
352 fileTextFormat.setText(store
353 .getDefaultString(UIPreferences.DECORATOR_FILETEXT_DECORATION));
354 folderTextFormat
355 .setText(store
356 .getDefaultString(UIPreferences.DECORATOR_FOLDERTEXT_DECORATION));
357 projectTextFormat
358 .setText(store
359 .getDefaultString(UIPreferences.DECORATOR_PROJECTTEXT_DECORATION));
361 showTracked.setSelection(store
362 .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_TRACKED_ICON));
363 showUntracked
364 .setSelection(store
365 .getDefaultBoolean(UIPreferences.DECORATOR_SHOW_UNTRACKED_ICON));
369 * Returns the preference store that belongs to the our plugin.
371 * This is important because we want to store our preferences separately
372 * from the desktop.
374 * @return the preference store for this plugin
376 protected IPreferenceStore doGetPreferenceStore() {
377 return Activator.getDefault().getPreferenceStore();
381 * (non-Javadoc)
383 * @see org.eclipse.jface.dialogs.DialogPage#dispose()
385 public void dispose() {
386 PlatformUI.getWorkbench().getThemeManager()
387 .removePropertyChangeListener(themeListener);
388 super.dispose();
392 * Adds another variable to the given target text
394 * A ListSelectionDialog pops up and allow the user to choose the variable,
395 * which is then inserted at current position in <code>text</code>
397 * @param target
398 * the target to add the variable to
399 * @param bindings
400 * the map of bindings
402 private void addVariables(Text target, Map bindings) {
404 final List<StringPair> variables = new ArrayList<StringPair>(bindings
405 .size());
407 ILabelProvider labelProvider = new LabelProvider() {
408 public String getText(Object element) {
409 return ((StringPair) element).s1
410 + " - " + ((StringPair) element).s2; //$NON-NLS-1$
414 IStructuredContentProvider contentsProvider = new IStructuredContentProvider() {
415 public Object[] getElements(Object inputElement) {
416 return variables.toArray(new StringPair[variables.size()]);
419 public void dispose() {
420 // No-op
423 public void inputChanged(Viewer viewer, Object oldInput,
424 Object newInput) {
425 // No-op
429 for (Iterator it = bindings.keySet().iterator(); it.hasNext();) {
430 StringPair variable = new StringPair();
431 variable.s1 = (String) it.next(); // variable
432 variable.s2 = (String) bindings.get(variable.s1); // description
433 variables.add(variable);
436 ListSelectionDialog dialog = new ListSelectionDialog(this.getShell(),
437 this, contentsProvider, labelProvider,
438 UIText.DecoratorPreferencesPage_selectVariablesToAdd);
439 dialog.setTitle(UIText.DecoratorPreferencesPage_addVariablesTitle);
440 if (dialog.open() != Window.OK)
441 return;
443 Object[] result = dialog.getResult();
445 for (int i = 0; i < result.length; i++) {
446 target.insert("{" + ((StringPair) result[i]).s1 + "}"); //$NON-NLS-1$ //$NON-NLS-2$
450 class StringPair {
451 String s1;
453 String s2;
456 class TextPair {
457 TextPair(Text t1, Text t2) {
458 this.t1 = t1;
459 this.t2 = t2;
462 Text t1;
464 Text t2;
468 * Gets the map of bindings between variables and description, to use for
469 * the format editors for files
471 * @return the bindings
473 private Map getFileBindingDescriptions() {
474 Map<String, String> bindings = new HashMap<String, String>();
475 bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
476 UIText.DecoratorPreferencesPage_nameResourceVariable);
477 return bindings;
481 * Gets the map of bindings between variables and description, to use for
482 * the format editors for folders
484 * @return the bindings
486 private Map getFolderBindingDescriptions() {
487 Map<String, String> bindings = new HashMap<String, String>();
488 bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
489 UIText.DecoratorPreferencesPage_nameResourceVariable);
490 return bindings;
494 * Gets the map of bindings between variables and description, to use for
495 * the format editors for projects
497 * @return the bindings
499 private Map getProjectBindingDescriptions() {
500 Map<String, String> bindings = new HashMap<String, String>();
501 bindings.put(DecorationHelper.BINDING_RESOURCE_NAME,
502 UIText.DecoratorPreferencesPage_nameResourceVariable);
503 bindings.put(DecorationHelper.BINDING_BRANCH_NAME,
504 UIText.DecoratorPreferencesPage_bindingBranchName);
505 return bindings;
508 private void updatePreview() {
509 if (preview != null)
510 preview.refresh();
514 * Preview control for showing how changes in the dialog will affect
515 * decoration
517 private class Preview extends LabelProvider implements Observer,
518 ITreeContentProvider {
520 private final ResourceManager fImageCache;
522 private final TreeViewer fViewer;
524 private DecorationHelper fHelper;
526 public Preview(Composite composite) {
527 // Has to happen before the tree control is constructed
528 reloadDecorationHelper();
529 SWTUtils.createLabel(composite,
530 UIText.DecoratorPreferencesPage_preview);
531 fImageCache = new LocalResourceManager(JFaceResources
532 .getResources());
534 fViewer = new TreeViewer(composite);
535 fViewer.getControl().setLayoutData(SWTUtils.createHVFillGridData());
536 fViewer.setContentProvider(this);
537 fViewer.setLabelProvider(this);
538 fViewer.setInput(PREVIEW_FILESYSTEM_ROOT);
539 fViewer.expandAll();
540 fHelper = new DecorationHelper(new PreferenceStore());
543 private void reloadDecorationHelper() {
544 PreferenceStore store = new PreferenceStore();
545 performOk(store);
546 fHelper = new DecorationHelper(store);
549 public void refresh() {
550 reloadDecorationHelper();
551 fViewer.refresh(true);
552 setColorsAndFonts(fViewer.getTree().getItems());
555 @SuppressWarnings("unused")
556 private void setColorsAndFonts(TreeItem[] items) {
557 // TODO: Implement colors and fonts
560 public void update(Observable o, Object arg) {
561 refresh();
564 public Object[] getChildren(Object parentElement) {
565 return ((PreviewResource) parentElement).children.toArray();
568 public Object getParent(Object element) {
569 return null;
572 public boolean hasChildren(Object element) {
573 return !((PreviewResource) element).children.isEmpty();
576 public Object[] getElements(Object inputElement) {
577 return ((Collection) inputElement).toArray();
580 public void dispose() {
581 fImageCache.dispose();
584 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
585 // No-op
588 public Color getBackground(Object element) {
589 return getDecoration(element).getBackgroundColor();
592 public Color getForeground(Object element) {
593 return getDecoration(element).getForegroundColor();
596 public Font getFont(Object element) {
597 return getDecoration(element).getFont();
600 public String getText(Object element) {
601 final PreviewDecoration decoration = getDecoration(element);
602 final StringBuffer buffer = new StringBuffer();
603 final String prefix = decoration.getPrefix();
604 if (prefix != null)
605 buffer.append(prefix);
606 buffer.append(((PreviewResource) element).getName());
607 final String suffix = decoration.getSuffix();
608 if (suffix != null)
609 buffer.append(suffix);
610 return buffer.toString();
613 public Image getImage(Object element) {
614 final String s;
615 switch (((PreviewResource) element).type) {
616 case IResource.PROJECT:
617 s = SharedImages.IMG_OBJ_PROJECT;
618 break;
619 case IResource.FOLDER:
620 s = ISharedImages.IMG_OBJ_FOLDER;
621 break;
622 default:
623 s = ISharedImages.IMG_OBJ_FILE;
624 break;
626 final Image baseImage = PlatformUI.getWorkbench().getSharedImages()
627 .getImage(s);
628 final ImageDescriptor overlay = getDecoration(element).getOverlay();
629 if (overlay == null)
630 return baseImage;
631 try {
632 return fImageCache.createImage(new DecorationOverlayIcon(
633 baseImage, overlay, IDecoration.BOTTOM_RIGHT));
634 } catch (Exception e) {
635 Activator.logError(e.getMessage(), e);
638 return null;
641 private PreviewDecoration getDecoration(Object element) {
642 PreviewDecoration decoration = new PreviewDecoration();
643 fHelper.decorate(decoration, (PreviewResource) element);
644 return decoration;
648 private static class PreviewResource implements IDecoratableResource {
649 private final String name;
651 private final String branch;
653 private final int type;
655 private Collection children;
657 private boolean tracked;
659 private boolean ignored;
661 public PreviewResource(String name, int type, String branch,
662 boolean tracked, boolean ignored) {
663 this.name = name;
664 this.branch = branch;
665 this.type = type;
666 this.children = Collections.EMPTY_LIST;
667 this.tracked = tracked;
668 this.ignored = ignored;
671 public String getName() {
672 return name;
675 public int getType() {
676 return type;
679 public String getBranch() {
680 return branch;
683 public boolean isTracked() {
684 return tracked;
687 public boolean isIgnored() {
688 return ignored;
692 private class PreviewDecoration implements IDecoration {
694 private List<String> prefixes = new ArrayList<String>();
696 private List<String> suffixes = new ArrayList<String>();
698 private ImageDescriptor overlay = null;
700 private Font font;
702 private Color backgroundColor;
704 private Color foregroundColor;
706 public void addOverlay(ImageDescriptor overlayImage) {
707 overlay = overlayImage;
710 public void addOverlay(ImageDescriptor overlayImage, int quadrant) {
711 overlay = overlayImage;
714 public void addPrefix(String prefix) {
715 prefixes.add(prefix);
718 public void addSuffix(String suffix) {
719 suffixes.add(suffix);
722 public IDecorationContext getDecorationContext() {
723 return new DecorationContext();
726 public void setBackgroundColor(Color color) {
727 backgroundColor = color;
730 public void setForegroundColor(Color color) {
731 foregroundColor = color;
734 public void setFont(Font font) {
735 this.font = font;
738 public ImageDescriptor getOverlay() {
739 return overlay;
742 public String getPrefix() {
743 StringBuffer sb = new StringBuffer();
744 for (Iterator<String> iter = prefixes.iterator(); iter.hasNext();) {
745 sb.append(iter.next());
747 return sb.toString();
750 public String getSuffix() {
751 StringBuffer sb = new StringBuffer();
752 for (Iterator<String> iter = suffixes.iterator(); iter.hasNext();) {
753 sb.append(iter.next());
755 return sb.toString();
758 public Font getFont() {
759 return font;
762 public Color getBackgroundColor() {
763 return backgroundColor;
766 public Color getForegroundColor() {
767 return foregroundColor;