org.eclipse.egit.ui.internal.preferences.ConfigurationEditorComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.ui.internal.preferences.ConfigurationEditorComponent.java

Source

/*******************************************************************************
 * Copyright (c) 2010, SAP AG.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Mathias Kinzler (SAP AG) - initial implementation
 *******************************************************************************/
package org.eclipse.egit.ui.internal.preferences;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.Path;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIText;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
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.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.ide.IDE;

/**
 * A reusable UI component to display and edit a Git configuration.
 * <p>
 * Concrete subclasses that are interested in displaying error messages should
 * override {@link #setErrorMessage(String)}
 * <p>
 * TODO: do the changes in memory and offer methods to obtain dirty state, to
 * save, and something like setDirty(boolean) to be implemented by subclasses so
 * that proper save/revert can be implemented; we could also offer this for
 * non-stored configurations
 */
public class ConfigurationEditorComponent {

    private final static String DOT = "."; //$NON-NLS-1$

    private final StoredConfig editableConfig;

    private final IShellProvider shellProvider;

    private final Composite parent;

    private final boolean useDialogFont;

    private Text valueText;

    private Button changeValue;

    private Button addValue;

    private Button remove;

    private Button deleteValue;

    private TreeViewer tv;

    private Text location;

    /**
     * @param parent
     *            the parent
     * @param config
     *            to be used instead of the user configuration
     * @param useDialogFont
     *            if <code>true</code>, the current dialog font is used
     */
    public ConfigurationEditorComponent(Composite parent, StoredConfig config, boolean useDialogFont) {
        editableConfig = config;
        this.shellProvider = new SameShellProvider(parent);
        this.parent = parent;
        this.useDialogFont = useDialogFont;
    }

    /**
     * Saves and (in case of success) reloads the current configuration
     *
     * @throws IOException
     */
    public void save() throws IOException {
        editableConfig.save();
        setDirty(false);
        initControlsFromConfig();
    }

    /**
     * Restores and (in case of success) reloads the current configuration
     *
     * @throws IOException
     */
    public void restore() throws IOException {
        try {
            editableConfig.load();
            tv.refresh();
        } catch (ConfigInvalidException e) {
            throw new IOException(e.getMessage());
        }
        initControlsFromConfig();
    }

    /**
     * @return the control being created
     */
    public Control createContents() {
        final Composite main = new Composite(parent, SWT.NONE);
        main.setLayout(new GridLayout(1, false));
        GridDataFactory.fillDefaults().grab(true, true).applyTo(main);

        if (editableConfig instanceof FileBasedConfig) {
            Composite locationPanel = new Composite(main, SWT.NONE);
            locationPanel.setLayout(new GridLayout(3, false));
            GridDataFactory.fillDefaults().grab(true, false).applyTo(locationPanel);
            Label locationLabel = new Label(locationPanel, SWT.NONE);
            locationLabel.setText(UIText.ConfigurationEditorComponent_ConfigLocationLabel);
            // GridDataFactory.fillDefaults().applyTo(locationLabel);
            location = new Text(locationPanel, SWT.BORDER);
            GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(location);
            location.setEditable(false);
            Button openEditor = new Button(locationPanel, SWT.PUSH);
            // GridDataFactory.fillDefaults().applyTo(openEditor);
            openEditor.setText(UIText.ConfigurationEditorComponent_OpenEditorButton);
            openEditor.setToolTipText(UIText.ConfigurationEditorComponent_OpenEditorTooltip);
            openEditor.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    IFileStore store = EFS.getLocalFileSystem()
                            .getStore(new Path(((FileBasedConfig) editableConfig).getFile().getAbsolutePath()));
                    try {
                        IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(),
                                new FileStoreEditorInput(store), EditorsUI.DEFAULT_TEXT_EDITOR_ID);
                    } catch (PartInitException ex) {
                        Activator.handleError(ex.getMessage(), ex, true);
                    }
                }
            });
        }
        tv = new TreeViewer(main, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
        Tree tree = tv.getTree();
        GridDataFactory.fillDefaults().hint(100, 60).grab(true, true).applyTo(tree);
        TreeColumn key = new TreeColumn(tree, SWT.NONE);
        key.setText(UIText.ConfigurationEditorComponent_KeyColumnHeader);
        key.setWidth(150);

        TreeColumn value = new TreeColumn(tree, SWT.NONE);
        value.setText(UIText.ConfigurationEditorComponent_ValueColumnHeader);
        value.setWidth(250);

        tv.setContentProvider(new ConfigEditorContentProvider());
        Font defaultFont;
        if (useDialogFont)
            defaultFont = JFaceResources.getDialogFont();
        else
            defaultFont = JFaceResources.getDefaultFont();
        tv.setLabelProvider(new ConfigEditorLabelProvider(defaultFont));

        tree.setHeaderVisible(true);
        tree.setLinesVisible(true);

        // if section or subsection is selected, we show the remove button
        Composite buttonPanel = new Composite(main, SWT.NONE);
        buttonPanel.setLayout(new GridLayout(2, false));
        GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonPanel);
        final Button newEntry = new Button(buttonPanel, SWT.PUSH);
        GridDataFactory.fillDefaults().applyTo(newEntry);
        newEntry.setText(UIText.ConfigurationEditorComponent_NewValueButton);
        newEntry.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {

                String suggestedKey;
                IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
                Object first = sel.getFirstElement();
                if (first instanceof Section)
                    suggestedKey = ((Section) first).name + DOT;
                else if (first instanceof SubSection) {
                    SubSection sub = (SubSection) first;
                    suggestedKey = sub.parent.name + DOT + sub.name + DOT;
                } else if (first instanceof Entry) {
                    Entry entry = (Entry) first;
                    if (entry.sectionparent != null)
                        suggestedKey = entry.sectionparent.name + DOT;
                    else
                        suggestedKey = entry.subsectionparent.parent.name + DOT + entry.subsectionparent.name + DOT;
                } else
                    suggestedKey = null;

                AddConfigEntryDialog dlg = new AddConfigEntryDialog(getShell(), editableConfig, suggestedKey);
                if (dlg.open() == Window.OK) {
                    StringTokenizer st = new StringTokenizer(dlg.getKey(), DOT);
                    if (st.countTokens() == 2) {
                        editableConfig.setString(st.nextToken(), null, st.nextToken(), dlg.getValue());
                        markDirty();
                    } else if (st.countTokens() == 3) {
                        editableConfig.setString(st.nextToken(), st.nextToken(), st.nextToken(), dlg.getValue());
                        markDirty();
                    } else
                        Activator.handleError(UIText.ConfigurationEditorComponent_WrongNumberOfTokensMessage, null,
                                true);
                }
            }

        });
        remove = new Button(buttonPanel, SWT.PUSH);
        GridDataFactory.fillDefaults().applyTo(remove);
        remove.setText(UIText.ConfigurationEditorComponent_RemoveAllButton);
        remove.setToolTipText(UIText.ConfigurationEditorComponent_RemoveAllTooltip);
        remove.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
                Object first = sel.getFirstElement();
                if (first instanceof Section) {
                    Section section = (Section) first;
                    if (MessageDialog.openConfirm(getShell(),
                            UIText.ConfigurationEditorComponent_RemoveSectionTitle,
                            NLS.bind(UIText.ConfigurationEditorComponent_RemoveSectionMessage, section.name))) {
                        editableConfig.unsetSection(section.name, null);
                        markDirty();
                    }
                } else if (first instanceof SubSection) {
                    SubSection section = (SubSection) first;
                    if (MessageDialog.openConfirm(getShell(),
                            UIText.ConfigurationEditorComponent_RemoveSubsectionTitle,
                            NLS.bind(UIText.ConfigurationEditorComponent_RemoveSubsectionMessage,
                                    section.parent.name + DOT + section.name))) {
                        editableConfig.unsetSection(section.parent.name, section.name);
                        markDirty();
                    }
                } else {
                    Activator.handleError(UIText.ConfigurationEditorComponent_NoSectionSubsectionMessage, null,
                            true);
                }

                super.widgetSelected(e);
            }
        });

        // if an entry is selected, then we show the value plus change button
        Composite valuePanel = new Composite(main, SWT.NONE);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(valuePanel);
        valuePanel.setLayout(new GridLayout(3, false));
        new Label(valuePanel, SWT.NONE).setText(UIText.ConfigurationEditorComponent_ValueLabel);
        valueText = new Text(valuePanel, SWT.BORDER);
        valueText.setText(UIText.ConfigurationEditorComponent_NoEntrySelectedMessage);
        GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).applyTo(valueText);
        // make the buttons equal width
        Composite buttonContainer = new Composite(valuePanel, SWT.NONE);
        buttonContainer.setLayout(new GridLayout(3, true));
        changeValue = new Button(buttonContainer, SWT.PUSH);
        GridDataFactory.fillDefaults().applyTo(changeValue);
        changeValue.setText(UIText.ConfigurationEditorComponent_ChangeButton);
        changeValue.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
                Object first = sel.getFirstElement();
                if (first instanceof Entry) {
                    Entry entry = (Entry) first;
                    entry.changeValue(valueText.getText());
                    markDirty();
                }
            }
        });
        deleteValue = new Button(buttonContainer, SWT.PUSH);
        GridDataFactory.fillDefaults().applyTo(deleteValue);
        deleteValue.setText(UIText.ConfigurationEditorComponent_DeleteButton);
        deleteValue.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
                Object first = sel.getFirstElement();
                if (first instanceof Entry) {
                    Entry entry = (Entry) first;
                    entry.removeValue();
                    markDirty();
                }

            }
        });
        addValue = new Button(buttonContainer, SWT.PUSH);
        GridDataFactory.fillDefaults().applyTo(addValue);
        addValue.setText(UIText.ConfigurationEditorComponent_AddButton);
        addValue.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IStructuredSelection sel = (IStructuredSelection) tv.getSelection();
                Object first = sel.getFirstElement();
                if (first instanceof Entry) {
                    Entry entry = (Entry) first;
                    entry.addValue(valueText.getText());
                    markDirty();
                }

            }
        });

        tv.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                updateEnablement();
            }
        });

        valueText.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent e) {
                if (valueText.getText().length() == 0) {
                    setErrorMessage(UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
                    changeValue.setEnabled(false);
                } else {
                    setErrorMessage(null);
                    changeValue.setEnabled(true);
                }
            }
        });

        initControlsFromConfig();
        return main;
    }

    private void initControlsFromConfig() {
        try {
            editableConfig.load();
            tv.setInput(editableConfig);
            if (editableConfig instanceof FileBasedConfig) {
                location.setText(((FileBasedConfig) editableConfig).getFile().getPath());
            }
        } catch (IOException e) {
            Activator.handleError(e.getMessage(), e, true);
        } catch (ConfigInvalidException e) {
            Activator.handleError(e.getMessage(), e, true);
        }
        tv.expandAll();
        updateEnablement();
    }

    /**
     * @param message
     *            the error message to display
     */
    protected void setErrorMessage(String message) {
        // the default implementation does nothing
    }

    /**
     * @param dirty
     *            the dirty flag
     */
    protected void setDirty(boolean dirty) {
        // the default implementation does nothing
    }

    private void updateEnablement() {
        Object selected = ((IStructuredSelection) tv.getSelection()).getFirstElement();

        boolean entrySelected = selected instanceof Entry;
        boolean sectionOrSubSectionSelected = (selected instanceof Section || selected instanceof SubSection);

        if (entrySelected)
            valueText.setText(((Entry) selected).value);
        else
            valueText.setText(UIText.ConfigurationEditorComponent_NoEntrySelectedMessage);
        changeValue.setEnabled(false);
        valueText.setEnabled(entrySelected);
        deleteValue.setEnabled(entrySelected);
        addValue.setEnabled(entrySelected);
        remove.setEnabled(sectionOrSubSectionSelected);
    }

    private void markDirty() {
        setDirty(true);
        tv.refresh();
    }

    private final static class Section {
        private final String name;

        private final Config config;

        Section(Config config, String name) {
            this.config = config;
            this.name = name;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + name.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Section other = (Section) obj;
            if (!name.equals(other.name))
                return false;
            return true;
        }
    }

    private final static class SubSection {

        private final Section parent;

        private final String name;

        SubSection(Section parent, String name) {
            this.parent = parent;
            this.name = name;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + name.hashCode();
            result = prime * result + parent.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            SubSection other = (SubSection) obj;
            if (!name.equals(other.name))
                return false;
            if (!parent.equals(other.parent))
                return false;
            return true;
        }
    }

    private final static class Entry {

        private final Section sectionparent;

        private final SubSection subsectionparent;

        private final String name;

        private final String value;

        private final int index;

        Entry(Section parent, String name, String value, int index) {
            this.sectionparent = parent;
            this.subsectionparent = null;
            this.name = name;
            this.value = value;
            this.index = index;
        }

        public void addValue(String newValue) {
            if (newValue.length() == 0)
                throw new IllegalArgumentException(UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
            Config config = getConfig();

            List<String> entries;
            if (sectionparent != null) {
                // Arrays.asList returns a fixed-size list, so we need to copy
                // over to a mutable list
                entries = new ArrayList<String>(
                        Arrays.asList(config.getStringList(sectionparent.name, null, name)));
                entries.add(Math.max(index, 0), newValue);
                config.setStringList(sectionparent.name, null, name, entries);
            } else {
                // Arrays.asList returns a fixed-size list, so we need to copy
                // over to a mutable list
                entries = new ArrayList<String>(Arrays
                        .asList(config.getStringList(subsectionparent.parent.name, subsectionparent.name, name)));
                entries.add(Math.max(index, 0), newValue);
                config.setStringList(subsectionparent.parent.name, subsectionparent.name, name, entries);
            }

        }

        Entry(SubSection parent, String name, String value, int index) {
            this.sectionparent = null;
            this.subsectionparent = parent;
            this.name = name;
            this.value = value;
            this.index = index;
        }

        public void changeValue(String newValue) throws IllegalArgumentException {
            if (newValue.length() == 0)
                throw new IllegalArgumentException(UIText.ConfigurationEditorComponent_EmptyStringNotAllowed);
            Config config = getConfig();

            if (index < 0) {
                if (sectionparent != null)
                    config.setString(sectionparent.name, null, name, newValue);
                else
                    config.setString(subsectionparent.parent.name, subsectionparent.name, name, newValue);
            } else {
                String[] entries;
                if (sectionparent != null) {
                    entries = config.getStringList(sectionparent.name, null, name);
                    entries[index] = newValue;
                    config.setStringList(sectionparent.name, null, name, Arrays.asList(entries));
                } else {
                    entries = config.getStringList(subsectionparent.parent.name, subsectionparent.name, name);
                    entries[index] = newValue;
                    config.setStringList(subsectionparent.parent.name, subsectionparent.name, name,
                            Arrays.asList(entries));
                }
            }
        }

        private Config getConfig() {
            Config config;
            if (sectionparent != null)
                config = sectionparent.config;
            else
                config = subsectionparent.parent.config;
            return config;
        }

        public void removeValue() {
            Config config = getConfig();

            if (index < 0) {
                if (sectionparent != null)
                    config.unset(sectionparent.name, null, name);
                else
                    config.unset(subsectionparent.parent.name, subsectionparent.name, name);
            } else {
                List<String> entries;
                if (sectionparent != null) {
                    // Arrays.asList returns a fixed-size list, so we need to
                    // copy over to a mutable list
                    entries = new ArrayList<String>(
                            Arrays.asList(config.getStringList(sectionparent.name, null, name)));

                    entries.remove(index);
                    config.setStringList(sectionparent.name, null, name, entries);
                } else {
                    // Arrays.asList returns a fixed-size list, so we need to
                    // copy over to a mutable list
                    entries = new ArrayList<String>(Arrays.asList(
                            config.getStringList(subsectionparent.parent.name, subsectionparent.name, name)));
                    // the list is fixed-size, so we have to copy over
                    entries.remove(index);
                    config.setStringList(subsectionparent.parent.name, subsectionparent.name, name, entries);
                }
            }
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + index;
            result = prime * result + name.hashCode();
            result = prime * result + ((sectionparent == null) ? 0 : sectionparent.hashCode());
            result = prime * result + ((subsectionparent == null) ? 0 : subsectionparent.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Entry other = (Entry) obj;
            // the index may change between 0 and -1 when values are added
            if (index != other.index && (index > 0 || other.index > 0))
                return false;
            if (!name.equals(other.name))
                return false;
            if (sectionparent == null) {
                if (other.sectionparent != null)
                    return false;
            } else if (!sectionparent.equals(other.sectionparent))
                return false;
            if (subsectionparent == null) {
                if (other.subsectionparent != null)
                    return false;
            } else if (!subsectionparent.equals(other.subsectionparent))
                return false;
            return true;
        }
    }

    private static final class ConfigEditorContentProvider implements ITreeContentProvider {
        Config userConfig;

        public Object[] getElements(Object inputElement) {
            if (userConfig == null)
                return null;
            List<Section> sections = new ArrayList<Section>();
            Set<String> sectionNames = userConfig.getSections();
            for (String sectionName : sectionNames)
                sections.add(new Section(userConfig, sectionName));
            Collections.sort(sections, new Comparator<Section>() {

                public int compare(Section o1, Section o2) {
                    return o1.name.compareTo(o2.name);
                }
            });
            return sections.toArray();

        }

        public void dispose() {
            userConfig = null;
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
            userConfig = (Config) newInput;
        }

        public Object[] getChildren(Object parentElement) {
            List<Object> result = new ArrayList<Object>();
            if (parentElement instanceof Section) {
                Section section = (Section) parentElement;
                Set<String> subSectionNames = userConfig.getSubsections(((Section) parentElement).name);
                for (String subSectionName : subSectionNames)
                    result.add(new SubSection(section, subSectionName));

                Set<String> entryNames = userConfig.getNames(section.name);
                for (String entryName : entryNames) {
                    String[] values = userConfig.getStringList(section.name, null, entryName);
                    if (values.length == 1)
                        result.add(new Entry(section, entryName, values[0], -1));
                    else {
                        int index = 0;
                        for (String value : values)
                            result.add(new Entry(section, entryName, value, index++));
                    }
                }
            }
            if (parentElement instanceof SubSection) {
                SubSection subSection = (SubSection) parentElement;
                Set<String> entryNames = userConfig.getNames(subSection.parent.name, subSection.name);
                for (String entryName : entryNames) {
                    String[] values = userConfig.getStringList(subSection.parent.name, subSection.name, entryName);
                    if (values.length == 1)
                        result.add(new Entry(subSection, entryName, values[0], -1));
                    else {
                        int index = 0;
                        for (String value : values)
                            result.add(new Entry(subSection, entryName, value, index++));
                    }
                }
            }
            return result.toArray();
        }

        public Object getParent(Object element) {
            if (element instanceof Section)
                return null;
            if (element instanceof SubSection)
                return ((SubSection) element).parent;
            if (element instanceof Entry) {
                Entry entry = (Entry) element;
                if (entry.sectionparent != null)
                    return entry.sectionparent;
                return entry.subsectionparent;
            }
            return null;
        }

        public boolean hasChildren(Object element) {
            return getChildren(element) != null && getChildren(element).length > 0;
        }
    }

    private static final class ConfigEditorLabelProvider extends BaseLabelProvider
            implements ITableLabelProvider, IFontProvider {
        private Font boldFont = null;

        private final Font defaultFont;

        public ConfigEditorLabelProvider(Font defaultFont) {
            this.defaultFont = defaultFont;
        }

        public Image getColumnImage(Object element, int columnIndex) {
            return null;
        }

        public String getColumnText(Object element, int columnIndex) {
            switch (columnIndex) {
            case 0:
                if (element instanceof Section)
                    return ((Section) element).name;
                if (element instanceof SubSection)
                    return ((SubSection) element).name;
                if (element instanceof Entry) {
                    Entry entry = (Entry) element;
                    if (entry.index < 0)
                        return entry.name;
                    return entry.name + "[" + entry.index + "]"; //$NON-NLS-1$ //$NON-NLS-2$
                }
                return null;
            case 1:
                if (element instanceof Entry)
                    return ((Entry) element).value;
                return null;
            default:
                return null;
            }
        }

        public Font getFont(Object element) {
            if (element instanceof Section || element instanceof SubSection)
                return getBoldFont();
            else
                return null;
        }

        private Font getBoldFont() {
            if (boldFont != null)
                return boldFont;
            FontData[] data = defaultFont.getFontData();
            for (int i = 0; i < data.length; i++)
                data[i].setStyle(data[i].getStyle() | SWT.BOLD);

            boldFont = new Font(Display.getDefault(), data);
            return boldFont;
        }

        @Override
        public void dispose() {
            if (boldFont != null)
                boldFont.dispose();
            super.dispose();
        }
    }

    private Shell getShell() {
        return shellProvider.getShell();
    }
}