Disable delete config section button for no selection
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / preferences / ConfigurationEditorComponent.java
blob3a6ffd39a346105f4d36fb22ff24f0882aab3dcf
1 /*******************************************************************************
2 * Copyright (c) 2010, SAP AG.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
8 * Contributors:
9 * Mathias Kinzler (SAP AG) - initial implementation
10 *******************************************************************************/
11 package org.eclipse.egit.ui.internal.preferences;
13 import java.io.File;
14 import java.io.IOException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.Comparator;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.StringTokenizer;
23 import org.eclipse.core.filesystem.EFS;
24 import org.eclipse.core.filesystem.IFileStore;
25 import org.eclipse.core.runtime.Path;
26 import org.eclipse.egit.ui.Activator;
27 import org.eclipse.egit.ui.internal.UIText;
28 import org.eclipse.jface.dialogs.MessageDialog;
29 import org.eclipse.jface.layout.GridDataFactory;
30 import org.eclipse.jface.layout.GridLayoutFactory;
31 import org.eclipse.jface.resource.JFaceResources;
32 import org.eclipse.jface.viewers.BaseLabelProvider;
33 import org.eclipse.jface.viewers.CellEditor;
34 import org.eclipse.jface.viewers.EditingSupport;
35 import org.eclipse.jface.viewers.ICellEditorListener;
36 import org.eclipse.jface.viewers.ICellEditorValidator;
37 import org.eclipse.jface.viewers.IFontProvider;
38 import org.eclipse.jface.viewers.ISelectionChangedListener;
39 import org.eclipse.jface.viewers.IStructuredSelection;
40 import org.eclipse.jface.viewers.ITableLabelProvider;
41 import org.eclipse.jface.viewers.SelectionChangedEvent;
42 import org.eclipse.jface.viewers.TextCellEditor;
43 import org.eclipse.jface.viewers.TreeViewer;
44 import org.eclipse.jface.viewers.TreeViewerColumn;
45 import org.eclipse.jface.window.IShellProvider;
46 import org.eclipse.jface.window.SameShellProvider;
47 import org.eclipse.jface.window.Window;
48 import org.eclipse.jgit.errors.ConfigInvalidException;
49 import org.eclipse.jgit.lib.Config;
50 import org.eclipse.jgit.lib.StoredConfig;
51 import org.eclipse.jgit.storage.file.FileBasedConfig;
52 import org.eclipse.osgi.util.NLS;
53 import org.eclipse.swt.SWT;
54 import org.eclipse.swt.events.SelectionAdapter;
55 import org.eclipse.swt.events.SelectionEvent;
56 import org.eclipse.swt.graphics.Font;
57 import org.eclipse.swt.graphics.FontData;
58 import org.eclipse.swt.graphics.Image;
59 import org.eclipse.swt.layout.GridLayout;
60 import org.eclipse.swt.widgets.Button;
61 import org.eclipse.swt.widgets.Composite;
62 import org.eclipse.swt.widgets.Control;
63 import org.eclipse.swt.widgets.Display;
64 import org.eclipse.swt.widgets.Label;
65 import org.eclipse.swt.widgets.Shell;
66 import org.eclipse.swt.widgets.Text;
67 import org.eclipse.swt.widgets.Tree;
68 import org.eclipse.swt.widgets.TreeColumn;
69 import org.eclipse.ui.PartInitException;
70 import org.eclipse.ui.PlatformUI;
71 import org.eclipse.ui.editors.text.EditorsUI;
72 import org.eclipse.ui.ide.FileStoreEditorInput;
73 import org.eclipse.ui.ide.IDE;
74 import org.eclipse.ui.model.WorkbenchAdapter;
75 import org.eclipse.ui.model.WorkbenchContentProvider;
77 /**
78 * A reusable UI component to display and edit a Git configuration.
79 * <p>
80 * Concrete subclasses that are interested in displaying error messages should
81 * override {@link #setErrorMessage(String)}
82 * <p>
83 * TODO: do the changes in memory and offer methods to obtain dirty state, to
84 * save, and something like setDirty(boolean) to be implemented by subclasses so
85 * that proper save/revert can be implemented; we could also offer this for
86 * non-stored configurations
88 public class ConfigurationEditorComponent {
90 private final static String DOT = "."; //$NON-NLS-1$
92 private StoredConfig editableConfig;
94 private final IShellProvider shellProvider;
96 private final Composite parent;
98 private final boolean useDialogFont;
100 private Composite contents;
102 private Button newValue;
104 private Button remove;
106 private TreeViewer tv;
108 private Text location;
110 private boolean editable;
113 * @param parent
114 * the parent
115 * @param config
116 * to be used instead of the user configuration
117 * @param useDialogFont
118 * if <code>true</code>, the current dialog font is used
120 public ConfigurationEditorComponent(Composite parent, StoredConfig config,
121 boolean useDialogFont) {
122 editableConfig = config;
123 this.shellProvider = new SameShellProvider(parent);
124 this.parent = parent;
125 this.useDialogFont = useDialogFont;
128 void setConfig(FileBasedConfig config) throws IOException {
129 editableConfig = config;
130 try {
131 editableConfig.clear();
132 editableConfig.load();
133 } catch (ConfigInvalidException e) {
134 throw new IOException(e.getMessage());
136 initControlsFromConfig();
140 * Saves and (in case of success) reloads the current configuration
142 * @throws IOException
144 public void save() throws IOException {
145 editableConfig.save();
146 setDirty(false);
147 initControlsFromConfig();
151 * Restores and (in case of success) reloads the current configuration
153 * @throws IOException
155 public void restore() throws IOException {
156 try {
157 editableConfig.clear();
158 editableConfig.load();
159 } catch (ConfigInvalidException e) {
160 throw new IOException(e.getMessage());
162 initControlsFromConfig();
166 * @return the control being created
168 public Control createContents() {
169 final Composite main = new Composite(parent, SWT.NONE);
170 main.setLayout(new GridLayout(2, false));
171 GridDataFactory.fillDefaults().grab(true, true).applyTo(main);
173 if (editableConfig instanceof FileBasedConfig) {
174 Composite locationPanel = new Composite(main, SWT.NONE);
175 GridLayout locationLayout = new GridLayout(3, false);
176 locationLayout.marginWidth = 0;
177 locationPanel.setLayout(locationLayout);
178 GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
179 .applyTo(locationPanel);
180 Label locationLabel = new Label(locationPanel, SWT.NONE);
181 locationLabel
182 .setText(UIText.ConfigurationEditorComponent_ConfigLocationLabel);
183 // GridDataFactory.fillDefaults().applyTo(locationLabel);
184 int locationStyle = SWT.BORDER|SWT.READ_ONLY;
185 location = new Text(locationPanel, locationStyle);
186 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER)
187 .grab(true, false).applyTo(location);
188 Button openEditor = new Button(locationPanel, SWT.PUSH);
189 openEditor
190 .setText(UIText.ConfigurationEditorComponent_OpenEditorButton);
191 openEditor
192 .setToolTipText(UIText.ConfigurationEditorComponent_OpenEditorTooltip);
193 openEditor.addSelectionListener(new SelectionAdapter() {
194 @Override
195 public void widgetSelected(SelectionEvent e) {
196 IFileStore store = EFS.getLocalFileSystem().getStore(
197 new Path(((FileBasedConfig) editableConfig)
198 .getFile().getAbsolutePath()));
199 try {
200 IDE.openEditor(PlatformUI.getWorkbench()
201 .getActiveWorkbenchWindow().getActivePage(),
202 new FileStoreEditorInput(store),
203 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
204 } catch (PartInitException ex) {
205 Activator.handleError(ex.getMessage(), ex, true);
209 openEditor
210 .setEnabled(((FileBasedConfig) editableConfig).getFile() != null);
212 tv = new TreeViewer(main, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
213 Tree tree = tv.getTree();
214 GridDataFactory.fillDefaults().hint(100, 60).grab(true, true)
215 .applyTo(tree);
216 TreeColumn key = new TreeColumn(tree, SWT.NONE);
217 key.setText(UIText.ConfigurationEditorComponent_KeyColumnHeader);
218 key.setWidth(150);
220 final TextCellEditor editor = new TextCellEditor(tree);
221 editor.setValidator(new ICellEditorValidator() {
223 @Override
224 public String isValid(Object value) {
225 String editedValue = value.toString();
226 return editedValue.length() > 0 ? null
227 : UIText.ConfigurationEditorComponent_EmptyStringNotAllowed;
230 editor.addListener(new ICellEditorListener() {
232 @Override
233 public void editorValueChanged(boolean oldValidState,
234 boolean newValidState) {
235 setErrorMessage(editor.getErrorMessage());
238 @Override
239 public void cancelEditor() {
240 setErrorMessage(null);
243 @Override
244 public void applyEditorValue() {
245 setErrorMessage(null);
249 TreeColumn value = new TreeColumn(tree, SWT.NONE);
250 value.setText(UIText.ConfigurationEditorComponent_ValueColumnHeader);
251 value.setWidth(250);
252 new TreeViewerColumn(tv, value)
253 .setEditingSupport(new EditingSupport(tv) {
255 @Override
256 protected void setValue(Object element, Object newValue) {
257 Entry entry = (Entry) element;
258 if (!entry.value.equals(newValue)) {
259 entry.changeValue(newValue.toString());
260 markDirty();
264 @Override
265 protected Object getValue(Object element) {
266 return ((Entry) element).value;
269 @Override
270 protected CellEditor getCellEditor(Object element) {
271 return editor;
274 @Override
275 protected boolean canEdit(Object element) {
276 return editable && element instanceof Entry;
280 tv.setContentProvider(new WorkbenchContentProvider());
281 Font defaultFont;
282 if (useDialogFont)
283 defaultFont = JFaceResources.getDialogFont();
284 else
285 defaultFont = JFaceResources.getDefaultFont();
286 tv.setLabelProvider(new ConfigEditorLabelProvider(defaultFont));
288 tree.setHeaderVisible(true);
289 tree.setLinesVisible(true);
291 Composite buttonPanel = new Composite(main, SWT.NONE);
292 GridLayoutFactory.fillDefaults().applyTo(buttonPanel);
293 GridDataFactory.fillDefaults().grab(false, false).applyTo(buttonPanel);
294 newValue = new Button(buttonPanel, SWT.PUSH);
295 GridDataFactory.fillDefaults().applyTo(newValue);
296 newValue.setText(UIText.ConfigurationEditorComponent_AddButton);
297 newValue.addSelectionListener(new SelectionAdapter() {
298 @Override
299 public void widgetSelected(SelectionEvent e) {
301 String suggestedKey;
302 IStructuredSelection sel = (IStructuredSelection) tv
303 .getSelection();
304 Object first = sel.getFirstElement();
305 if (first instanceof Section)
306 suggestedKey = ((Section) first).name + DOT;
307 else if (first instanceof SubSection) {
308 SubSection sub = (SubSection) first;
309 suggestedKey = sub.parent.name + DOT + sub.name + DOT;
310 } else if (first instanceof Entry) {
311 Entry entry = (Entry) first;
312 if (entry.sectionparent != null)
313 suggestedKey = entry.sectionparent.name + DOT;
314 else
315 suggestedKey = entry.subsectionparent.parent.name + DOT
316 + entry.subsectionparent.name + DOT;
317 } else
318 suggestedKey = null;
320 AddConfigEntryDialog dlg = new AddConfigEntryDialog(getShell(),
321 suggestedKey);
322 if (dlg.open() == Window.OK) {
323 String result = dlg.getKey();
324 if (result == null) {
325 // bug in swt bot, see
326 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=472110
327 return;
329 StringTokenizer st = new StringTokenizer(result, DOT);
330 if (st.countTokens() == 2) {
331 String sectionName = st.nextToken();
332 String entryName = st.nextToken();
333 Entry entry = ((GitConfig) tv.getInput()).getEntry(
334 sectionName, null, entryName);
335 if (entry == null)
336 editableConfig.setString(sectionName, null,
337 entryName, dlg.getValue());
338 else
339 entry.addValue(dlg.getValue());
340 markDirty();
341 } else if (st.countTokens() > 2) {
342 int n = st.countTokens();
343 String sectionName = st.nextToken();
344 StringBuilder b = new StringBuilder(st.nextToken());
345 for (int i = 0; i < n - 3; i++) {
346 b.append(DOT);
347 b.append(st.nextToken());
349 String subSectionName = b.toString();
350 String entryName = st.nextToken();
351 Entry entry = ((GitConfig) tv.getInput()).getEntry(
352 sectionName, subSectionName, entryName);
353 if (entry == null)
354 editableConfig.setString(sectionName,
355 subSectionName, entryName, dlg.getValue());
356 else
357 entry.addValue(dlg.getValue());
358 markDirty();
359 } else
360 Activator
361 .handleError(
362 UIText.ConfigurationEditorComponent_WrongNumberOfTokensMessage,
363 null, true);
368 remove = new Button(buttonPanel, SWT.PUSH);
369 GridDataFactory.fillDefaults().applyTo(remove);
370 remove.setText(UIText.ConfigurationEditorComponent_RemoveButton);
371 remove.setToolTipText(UIText.ConfigurationEditorComponent_RemoveTooltip);
372 remove.addSelectionListener(new SelectionAdapter() {
373 @Override
374 public void widgetSelected(SelectionEvent e) {
375 IStructuredSelection sel = (IStructuredSelection) tv
376 .getSelection();
377 Object first = sel.getFirstElement();
378 if (first instanceof Section) {
379 Section section = (Section) first;
380 if (MessageDialog
381 .openConfirm(
382 getShell(),
383 UIText.ConfigurationEditorComponent_RemoveSectionTitle,
384 NLS.bind(
385 UIText.ConfigurationEditorComponent_RemoveSectionMessage,
386 section.name))) {
387 editableConfig.unsetSection(section.name, null);
388 markDirty();
390 } else if (first instanceof SubSection) {
391 SubSection section = (SubSection) first;
392 if (MessageDialog
393 .openConfirm(
394 getShell(),
395 UIText.ConfigurationEditorComponent_RemoveSubsectionTitle,
396 NLS.bind(
397 UIText.ConfigurationEditorComponent_RemoveSubsectionMessage,
398 section.parent.name + DOT
399 + section.name))) {
400 editableConfig.unsetSection(section.parent.name,
401 section.name);
402 markDirty();
404 } else if (first instanceof Entry) {
405 ((Entry) first).removeValue();
406 markDirty();
409 super.widgetSelected(e);
413 tv.addSelectionChangedListener(new ISelectionChangedListener() {
414 @Override
415 public void selectionChanged(SelectionChangedEvent event) {
416 updateEnablement();
420 initControlsFromConfig();
421 contents = main;
422 return contents;
426 * @return the composite containing all the controls
428 public Composite getContents() {
429 return contents;
432 private boolean isWriteable(final File f) {
433 if (f.exists())
434 if (f.isFile())
435 if (f.canWrite())
436 return true;
437 else
438 return false;
439 else
440 return false;
441 // no file, can we create one
442 for (File d = f.getParentFile(); d != null; d = d.getParentFile())
443 if (d.isDirectory())
444 if (d.canWrite())
445 return true;
446 else
447 return false;
448 else if (d.exists())
449 return false;
450 // else continue
451 return false;
454 private void initControlsFromConfig() {
455 try {
456 editableConfig.load();
457 tv.setInput(new GitConfig(editableConfig));
458 editable = true;
459 if (editableConfig instanceof FileBasedConfig) {
460 FileBasedConfig fileConfig = (FileBasedConfig) editableConfig;
461 File configFile = fileConfig.getFile();
462 if (configFile != null)
463 if (isWriteable(configFile))
464 location.setText(configFile.getPath());
465 else {
466 location.setText(NLS
467 .bind(UIText.ConfigurationEditorComponent_ReadOnlyLocationFormat,
468 configFile.getPath()));
469 editable = false;
471 else {
472 location.setText(UIText.ConfigurationEditorComponent_NoConfigLocationKnown);
473 editable = false;
476 } catch (IOException e) {
477 Activator.handleError(e.getMessage(), e, true);
478 } catch (ConfigInvalidException e) {
479 Activator.handleError(e.getMessage(), e, true);
481 tv.expandAll();
482 updateEnablement();
486 * @param message
487 * the error message to display
489 protected void setErrorMessage(String message) {
490 // the default implementation does nothing
494 * @param dirty
495 * the dirty flag
497 protected void setDirty(boolean dirty) {
498 // the default implementation does nothing
501 private void updateEnablement() {
502 remove.setEnabled(editable && !tv.getSelection().isEmpty());
503 newValue.setEnabled(editable);
506 private void markDirty() {
507 setDirty(true);
508 ((GitConfig) tv.getInput()).refresh();
509 tv.refresh();
512 private final static class GitConfig extends WorkbenchAdapter {
514 private final Config config;
516 private Section[] children;
518 GitConfig(Config config) {
519 this.config = config;
522 GitConfig refresh() {
523 children = null;
524 return this;
527 @Override
528 public Object[] getChildren(Object o) {
529 if (children == null)
530 if (config != null) {
531 List<Section> sections = new ArrayList<>();
532 Set<String> sectionNames = config.getSections();
533 for (String sectionName : sectionNames)
534 sections.add(new Section(this, sectionName));
535 Collections.sort(sections, new Comparator<Section>() {
537 @Override
538 public int compare(Section o1, Section o2) {
539 return o1.name.compareTo(o2.name);
542 children = sections.toArray(new Section[sections.size()]);
543 } else
544 children = new Section[0];
545 return children;
548 public Entry getEntry(String sectionName, String subsectionName,
549 String entryName) {
550 for (Object child : getChildren(this)) {
551 Section section = (Section) child;
552 if (sectionName.equals(section.name))
553 return section.getEntry(subsectionName, entryName);
555 return null;
559 private final static class Section extends WorkbenchAdapter {
560 private final String name;
562 private final GitConfig parent;
564 private Object[] children;
566 Section(GitConfig parent, String name) {
567 this.parent = parent;
568 this.name = name;
571 @Override
572 public int hashCode() {
573 final int prime = 31;
574 int result = 1;
575 result = prime * result + name.hashCode();
576 return result;
579 @Override
580 public boolean equals(Object obj) {
581 if (this == obj)
582 return true;
583 if (obj == null)
584 return false;
585 if (getClass() != obj.getClass())
586 return false;
587 Section other = (Section) obj;
588 if (!name.equals(other.name))
589 return false;
590 return true;
593 @Override
594 public Object getParent(Object object) {
595 return parent;
598 @Override
599 public Object[] getChildren(Object o) {
600 if (children == null) {
601 List<Object> allChildren = new ArrayList<>();
602 Set<String> subSectionNames = parent.config
603 .getSubsections(name);
604 for (String subSectionName : subSectionNames)
605 allChildren.add(new SubSection(parent.config, this,
606 subSectionName));
608 Set<String> entryNames = parent.config.getNames(name);
609 for (String entryName : entryNames) {
610 String[] values = parent.config.getStringList(name, null,
611 entryName);
612 if (values.length == 1)
613 allChildren.add(new Entry(this, entryName, values[0],
614 -1));
615 else {
616 int index = 0;
617 for (String value : values)
618 allChildren.add(new Entry(this, entryName, value,
619 index++));
622 children = allChildren.toArray();
624 return children;
627 @Override
628 public String getLabel(Object o) {
629 return name;
632 public Entry getEntry(String subsectionName, String entryName) {
633 if (subsectionName != null) {
634 for (Object child : getChildren(this))
635 if (child instanceof SubSection
636 && ((SubSection) child).name.equals(subsectionName))
637 return ((SubSection) child).getEntry(entryName);
638 } else
639 for (Object child : getChildren(this))
640 if (child instanceof Entry
641 && ((Entry) child).name.equals(entryName))
642 return (Entry) child;
643 return null;
647 private final static class SubSection extends WorkbenchAdapter {
649 private final Config config;
651 private final Section parent;
653 private final String name;
655 private Entry[] children;
657 SubSection(Config config, Section parent, String name) {
658 this.config = config;
659 this.parent = parent;
660 this.name = name;
663 @Override
664 public int hashCode() {
665 final int prime = 31;
666 int result = 1;
667 result = prime * result + name.hashCode();
668 result = prime * result + parent.hashCode();
669 return result;
672 @Override
673 public boolean equals(Object obj) {
674 if (this == obj)
675 return true;
676 if (obj == null)
677 return false;
678 if (getClass() != obj.getClass())
679 return false;
680 SubSection other = (SubSection) obj;
681 if (!name.equals(other.name))
682 return false;
683 if (!parent.equals(other.parent))
684 return false;
685 return true;
688 @Override
689 public Object[] getChildren(Object o) {
690 if (children == null) {
691 List<Entry> entries = new ArrayList<>();
692 Set<String> entryNames = config.getNames(parent.name, name);
693 for (String entryName : entryNames) {
694 String[] values = config.getStringList(parent.name, name,
695 entryName);
696 if (values.length == 1)
697 entries.add(new Entry(this, entryName, values[0], -1));
698 else {
699 int index = 0;
700 for (String value : values)
701 entries.add(new Entry(this, entryName, value,
702 index++));
705 children = entries.toArray(new Entry[entries.size()]);
707 return children;
710 @Override
711 public String getLabel(Object o) {
712 return name;
715 @Override
716 public Object getParent(Object object) {
717 return parent;
720 public Entry getEntry(String entryName) {
721 for (Object child : getChildren(this))
722 if (entryName.equals(((Entry) child).name))
723 return (Entry) child;
724 return null;
728 private final static class Entry extends WorkbenchAdapter {
730 private final Section sectionparent;
732 private final SubSection subsectionparent;
734 private final String name;
736 private final String value;
738 private final int index;
740 Entry(Section parent, String name, String value, int index) {
741 this.sectionparent = parent;
742 this.subsectionparent = null;
743 this.name = name;
744 this.value = value;
745 this.index = index;
748 public void addValue(String newValue) {
749 if (newValue.length() == 0)
750 throw new IllegalArgumentException(
751 UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
752 Config config = getConfig();
754 List<String> entries;
755 if (sectionparent != null) {
756 // Arrays.asList returns a fixed-size list, so we need to copy
757 // over to a mutable list
758 entries = new ArrayList<>(Arrays.asList(config
759 .getStringList(sectionparent.name, null, name)));
760 entries.add(Math.max(index, 0), newValue);
761 config.setStringList(sectionparent.name, null, name, entries);
762 } else {
763 // Arrays.asList returns a fixed-size list, so we need to copy
764 // over to a mutable list
765 entries = new ArrayList<>(Arrays.asList(config
766 .getStringList(subsectionparent.parent.name,
767 subsectionparent.name, name)));
768 entries.add(Math.max(index, 0), newValue);
769 config.setStringList(subsectionparent.parent.name,
770 subsectionparent.name, name, entries);
775 Entry(SubSection parent, String name, String value, int index) {
776 this.sectionparent = null;
777 this.subsectionparent = parent;
778 this.name = name;
779 this.value = value;
780 this.index = index;
783 public void changeValue(String newValue)
784 throws IllegalArgumentException {
785 if (newValue.length() == 0)
786 throw new IllegalArgumentException(
787 UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
788 Config config = getConfig();
790 if (index < 0) {
791 if (sectionparent != null)
792 config.setString(sectionparent.name, null, name, newValue);
793 else
794 config.setString(subsectionparent.parent.name,
795 subsectionparent.name, name, newValue);
796 } else {
797 String[] entries;
798 if (sectionparent != null) {
799 entries = config.getStringList(sectionparent.name, null,
800 name);
801 entries[index] = newValue;
802 config.setStringList(sectionparent.name, null, name,
803 Arrays.asList(entries));
804 } else {
805 entries = config.getStringList(
806 subsectionparent.parent.name,
807 subsectionparent.name, name);
808 entries[index] = newValue;
809 config.setStringList(subsectionparent.parent.name,
810 subsectionparent.name, name, Arrays.asList(entries));
815 private Config getConfig() {
816 Config config;
817 if (sectionparent != null)
818 config = sectionparent.parent.config;
819 else
820 config = subsectionparent.parent.parent.config;
821 return config;
824 public void removeValue() {
825 Config config = getConfig();
827 if (index < 0) {
828 if (sectionparent != null)
829 config.unset(sectionparent.name, null, name);
830 else
831 config.unset(subsectionparent.parent.name,
832 subsectionparent.name, name);
833 } else {
834 List<String> entries;
835 if (sectionparent != null) {
836 // Arrays.asList returns a fixed-size list, so we need to
837 // copy over to a mutable list
838 entries = new ArrayList<>(Arrays.asList(config
839 .getStringList(sectionparent.name, null, name)));
841 entries.remove(index);
842 config.setStringList(sectionparent.name, null, name,
843 entries);
844 } else {
845 // Arrays.asList returns a fixed-size list, so we need to
846 // copy over to a mutable list
847 entries = new ArrayList<>(Arrays.asList(config
848 .getStringList(subsectionparent.parent.name,
849 subsectionparent.name, name)));
850 // the list is fixed-size, so we have to copy over
851 entries.remove(index);
852 config.setStringList(subsectionparent.parent.name,
853 subsectionparent.name, name, entries);
858 @Override
859 public int hashCode() {
860 final int prime = 31;
861 int result = 1;
862 result = prime * result + index;
863 result = prime * result + name.hashCode();
864 result = prime * result
865 + ((sectionparent == null) ? 0 : sectionparent.hashCode());
866 result = prime
867 * result
868 + ((subsectionparent == null) ? 0 : subsectionparent
869 .hashCode());
870 return result;
873 @Override
874 public boolean equals(Object obj) {
875 if (this == obj)
876 return true;
877 if (obj == null)
878 return false;
879 if (getClass() != obj.getClass())
880 return false;
881 Entry other = (Entry) obj;
882 // the index may change between 0 and -1 when values are added
883 if (index != other.index && (index > 0 || other.index > 0))
884 return false;
885 if (!name.equals(other.name))
886 return false;
887 if (sectionparent == null) {
888 if (other.sectionparent != null)
889 return false;
890 } else if (!sectionparent.equals(other.sectionparent))
891 return false;
892 if (subsectionparent == null) {
893 if (other.subsectionparent != null)
894 return false;
895 } else if (!subsectionparent.equals(other.subsectionparent))
896 return false;
897 return true;
901 private static final class ConfigEditorLabelProvider extends
902 BaseLabelProvider implements ITableLabelProvider, IFontProvider {
903 private Font boldFont = null;
905 private final Font defaultFont;
907 public ConfigEditorLabelProvider(Font defaultFont) {
908 this.defaultFont = defaultFont;
911 @Override
912 public Image getColumnImage(Object element, int columnIndex) {
913 return null;
916 @Override
917 public String getColumnText(Object element, int columnIndex) {
918 switch (columnIndex) {
919 case 0:
920 if (element instanceof Section)
921 return ((Section) element).name;
922 if (element instanceof SubSection)
923 return ((SubSection) element).name;
924 if (element instanceof Entry) {
925 Entry entry = (Entry) element;
926 if (entry.index < 0)
927 return entry.name;
928 return entry.name + "[" + entry.index + "]"; //$NON-NLS-1$ //$NON-NLS-2$
930 return null;
931 case 1:
932 if (element instanceof Entry)
933 return ((Entry) element).value;
934 return null;
935 default:
936 return null;
940 @Override
941 public Font getFont(Object element) {
942 if (element instanceof Section || element instanceof SubSection)
943 return getBoldFont();
944 else
945 return null;
948 private Font getBoldFont() {
949 if (boldFont != null)
950 return boldFont;
951 FontData[] data = defaultFont.getFontData();
952 for (int i = 0; i < data.length; i++)
953 data[i].setStyle(data[i].getStyle() | SWT.BOLD);
955 boldFont = new Font(Display.getDefault(), data);
956 return boldFont;
959 @Override
960 public void dispose() {
961 if (boldFont != null)
962 boldFont.dispose();
963 super.dispose();
967 private Shell getShell() {
968 return shellProvider.getShell();