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
;
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
;
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
;
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
));
154 preview
= new Preview(composite
);
157 // TODO: Add help text for this preference page
159 themeListener
= new IPropertyChangeListener() {
160 public void propertyChange(PropertyChangeEvent event
) {
164 PlatformUI
.getWorkbench().getThemeManager().addPropertyChangeListener(
167 Dialog
.applyDialogFont(parent
);
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
);
183 * Creates the controls for the first tab folder
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
);
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
) {
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
) {
283 computeDeepDirtyState
.addSelectionListener(selectionListener
);
284 showTracked
.addSelectionListener(selectionListener
);
285 showUntracked
.addSelectionListener(selectionListener
);
291 * @see IWorkbenchPreferencePage#init(IWorkbench)
293 public void init(IWorkbench workbench
) {
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));
314 * Store the preferences to the given preference 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
335 store
.setValue(UIPreferences
.DECORATOR_SHOW_UNTRACKED_ICON
,
336 showUntracked
.getSelection());
342 * Defaults was clicked. Restore the Git decoration preferences to their
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
));
356 .getDefaultString(UIPreferences
.DECORATOR_FOLDERTEXT_DECORATION
));
359 .getDefaultString(UIPreferences
.DECORATOR_PROJECTTEXT_DECORATION
));
361 showTracked
.setSelection(store
362 .getDefaultBoolean(UIPreferences
.DECORATOR_SHOW_TRACKED_ICON
));
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
374 * @return the preference store for this plugin
376 protected IPreferenceStore
doGetPreferenceStore() {
377 return Activator
.getDefault().getPreferenceStore();
383 * @see org.eclipse.jface.dialogs.DialogPage#dispose()
385 public void dispose() {
386 PlatformUI
.getWorkbench().getThemeManager()
387 .removePropertyChangeListener(themeListener
);
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>
398 * the target to add the variable to
400 * the map of bindings
402 private void addVariables(Text target
, Map bindings
) {
404 final List
<StringPair
> variables
= new ArrayList
<StringPair
>(bindings
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() {
423 public void inputChanged(Viewer viewer
, Object oldInput
,
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
)
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$
457 TextPair(Text t1
, 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
);
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
);
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
);
508 private void updatePreview() {
514 * Preview control for showing how changes in the dialog will affect
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
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
);
540 fHelper
= new DecorationHelper(new PreferenceStore());
543 private void reloadDecorationHelper() {
544 PreferenceStore store
= new PreferenceStore();
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
) {
564 public Object
[] getChildren(Object parentElement
) {
565 return ((PreviewResource
) parentElement
).children
.toArray();
568 public Object
getParent(Object element
) {
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
) {
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();
605 buffer
.append(prefix
);
606 buffer
.append(((PreviewResource
) element
).getName());
607 final String suffix
= decoration
.getSuffix();
609 buffer
.append(suffix
);
610 return buffer
.toString();
613 public Image
getImage(Object element
) {
615 switch (((PreviewResource
) element
).type
) {
616 case IResource
.PROJECT
:
617 s
= SharedImages
.IMG_OBJ_PROJECT
;
619 case IResource
.FOLDER
:
620 s
= ISharedImages
.IMG_OBJ_FOLDER
;
623 s
= ISharedImages
.IMG_OBJ_FILE
;
626 final Image baseImage
= PlatformUI
.getWorkbench().getSharedImages()
628 final ImageDescriptor overlay
= getDecoration(element
).getOverlay();
632 return fImageCache
.createImage(new DecorationOverlayIcon(
633 baseImage
, overlay
, IDecoration
.BOTTOM_RIGHT
));
634 } catch (Exception e
) {
635 Activator
.logError(e
.getMessage(), e
);
641 private PreviewDecoration
getDecoration(Object element
) {
642 PreviewDecoration decoration
= new PreviewDecoration();
643 fHelper
.decorate(decoration
, (PreviewResource
) element
);
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
) {
664 this.branch
= branch
;
666 this.children
= Collections
.EMPTY_LIST
;
667 this.tracked
= tracked
;
668 this.ignored
= ignored
;
671 public String
getName() {
675 public int getType() {
679 public String
getBranch() {
683 public boolean isTracked() {
687 public boolean isIgnored() {
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;
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
) {
738 public ImageDescriptor
getOverlay() {
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() {
762 public Color
getBackgroundColor() {
763 return backgroundColor
;
766 public Color
getForegroundColor() {
767 return foregroundColor
;