org.eclipse.ice.client.widgets.properties.TreePropertySection.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ice.client.widgets.properties.TreePropertySection.java

Source

/*******************************************************************************
 * Copyright (c) 2014 UT-Battelle, LLC.
 * 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:
 *   Initial API and implementation and/or initial documentation - Jay Jay Billings,
 *   Jordan H. Deyton, Dasha Gorin, Alexander J. McCaskey, Taylor Patterson,
 *   Claire Saunders, Matthew Wang, Anna Wojtowicz
 *******************************************************************************/
package org.eclipse.ice.client.widgets.properties;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.ice.client.common.TreeCompositeViewer;
import org.eclipse.ice.client.widgets.ICEFormEditor;
import org.eclipse.ice.datastructures.ICEObject.Component;
import org.eclipse.ice.datastructures.ICEObject.IUpdateable;
import org.eclipse.ice.datastructures.ICEObject.IUpdateableListener;
import org.eclipse.ice.datastructures.componentVisitor.IComponentVisitor;
import org.eclipse.ice.datastructures.componentVisitor.SelectiveComponentVisitor;
import org.eclipse.ice.datastructures.form.AdaptiveTreeComposite;
import org.eclipse.ice.datastructures.form.BasicEntryContentProvider;
import org.eclipse.ice.datastructures.form.DataComponent;
import org.eclipse.ice.datastructures.form.Entry;
import org.eclipse.ice.datastructures.form.TreeComposite;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.window.ToolTip;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;

/**
 * This class provides a proprty <code>Section</code> that displays the
 * properties for a {@link TreeComposite}.
 * <p>
 * The properties are displayed in a JFace {@link TableViewer}. Each row in the
 * table corresponds to an {@link Entry} in one of the tree's
 * {@link DataComponent}s or data nodes.
 * </p>
 * 
 * @author Jordan
 * 
 */
public class TreePropertySection extends AbstractPropertySection implements IUpdateableListener {

    /**
     * The <code>TreeComposite</code> whose properties are displayed in this
     * section.
     */
    private TreeComposite tree;
    /**
     * Whether or not the <code>TreeComposite</code> is an
     * <code>AdaptiveTreeComposite</code>. This is necessary for creating a
     * widget that can set the type of adaptive tree.
     */
    private boolean isAdaptive;

    /**
     * The currently selected property or parameter in the {@link #tableViewer}.
     */
    private Entry selectedEntry;

    // ---- Ancestor Controls ---- //
    // We keep track of these so we can fill the Properties View with the table.
    // We also do not want to use the Property View's scroll bars since the
    // table has its own horizontal and vertical scroll bars and we would like
    // to show the add and delete buttons next to the table.
    /**
     * The ScrolledComposite that occupies the Property Viewer. Its minimum size
     * (the size at which it starts drawing scroll bars) should be updated
     * because of GridLayouts in this Section.
     */
    private ScrolledComposite scrollComposite;
    /**
     * The client Composite for the {@link #scrollComposite}.
     */
    private Composite scrollCompositeClient;
    /**
     * The default minimum width of {@link #scrollComposite} as defined by
     * {@link ScrolledComposite#getMinWidth()}.
     */
    private int scrollMinWidth;
    /**
     * The default minimum height of {@link #scrollComposite} as defined by
     * {@link ScrolledComposite#getMinHeight()}.
     */
    private int scrollMinHeight;

    // --------------------------- //

    // ---- Section Controls ---- //
    /**
     * The <code>Section</code> that contains the properties or parameters of
     * the {@link #tree}.
     */
    private Section section;

    /**
     * The <code>Composite</code> that contains the {@link #type} selection
     * widget and other related <code>Controls</code>.
     */
    private Composite typeComposite;
    /**
     * The widget used to select the type of the {@link #tree} if it is an
     * {@link AdaptiveTreeComposite}.
     */
    private ComboViewer type;

    /**
     * The JFace <code>TableViewer</code> that contains the properties of the
     * {@link #tree}.
     */
    private TableViewer tableViewer;
    /**
     * The {@link #tableViewer}'s column for a property or parameter's name.
     */
    private TableViewerColumn nameColumn;
    /**
     * The {@link #tableViewer}'s column for a property or parameter's value.
     */
    private TableViewerColumn valueColumn;

    /**
     * The <code>Button</code> for adding a new <code>Entry</code> (aka property
     * or parameter) to the {@link #tableViewer}'s underlying {@link #tree}.
     */
    private Button add;
    /**
     * The <code>Button</code> for deleting the {@link #selectedEntry} (aka
     * property or parameter) from the {@link #tableViewer}'s underlying
     * {@link #tree}.
     */
    private Button delete;

    // -------------------------- //

    /**
     * The currently associated {@link ICEFormEditor} to which the {@link #tree}
     * belongs, or null if one does not exist.
     */
    private ICEFormEditor editor;

    /**
     * The default constructor.
     */
    public TreePropertySection() {

        // Initialize the widgets and initial tree to null.
        tree = null;
        isAdaptive = false;
        selectedEntry = null;

        return;
    }

    // ---- Extends AbstractPropertySection ---- //
    /**
     * This operation draws the (initial) controls in the properties view based
     * on the input. In this case, there are initially no widgets to prepare.
     */
    @Override
    public void createControls(Composite parent, TabbedPropertySheetPage aTabbedPropertySheetPage) {
        super.createControls(parent, aTabbedPropertySheetPage);

        // Get the default background color.
        Color backgroundColor = parent.getBackground();

        // Create a section for the data composites.
        section = getWidgetFactory().createSection(parent, Section.SHORT_TITLE_BAR | Section.DESCRIPTION);
        section.setText("Node properties");
        section.setDescription("All properties available for " + "this node can be modified here.");
        section.setBackground(backgroundColor);

        // Create the Composite that contains all DataComponentComposites.
        final Composite client = new Composite(section, SWT.NONE);
        GridLayout clientLayout = new GridLayout(2, false);
        // Set the margins and spacing based on the tabbed property constants.
        clientLayout.marginLeft = ITabbedPropertyConstants.HMARGIN;
        clientLayout.marginRight = ITabbedPropertyConstants.HMARGIN;
        clientLayout.marginTop = ITabbedPropertyConstants.VMARGIN;
        clientLayout.marginBottom = ITabbedPropertyConstants.VMARGIN;
        clientLayout.horizontalSpacing = ITabbedPropertyConstants.HSPACE;
        clientLayout.verticalSpacing = ITabbedPropertyConstants.VSPACE;
        client.setLayout(clientLayout);

        // Make the background of the section client white unless ICE is in
        // debug mode.
        if (System.getProperty("DebugICE") == null) {
            client.setBackground(backgroundColor);
        } else {
            client.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
        }

        // Set the client area for the section.
        section.setClient(client);

        // Get the property viewer's ScrolledComposite and its first Composite
        // (its "client" Composite).
        scrollCompositeClient = section.getParent().getParent().getParent().getParent();
        scrollComposite = (ScrolledComposite) scrollCompositeClient.getParent();

        // Add a listener to resize the Section's properties and update the
        // ScrollComposite's minimum bounds correctly based on the displayed
        // properties.
        scrollComposite.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                resizePropertyView();
            }
        });

        // Create the type Combo Composite.
        typeComposite = createTypeComposite(client);
        GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
        gridData.horizontalSpan = 2;
        typeComposite.setLayoutData(gridData);
        // Refresh the contents of the type Combo and its containing Composite.
        refreshTypeWidgets();

        // Create the table of properties.
        tableViewer = createTableViewer(client);
        // Set the table's layout data so it occupies all spare space in the
        // property section client.
        tableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        // Create the add/delete buttons.
        Composite buttonComposite = createButtons(client);
        // The button Composite shouldn't grab any space. Align it along the
        // center and top of the space to the right of the table.
        buttonComposite.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));

        return;
    }

    /**
     * This operation sets the current input displayed in the table of
     * properties. Specifically, it sets {@link #adaptiveTree} if the input is
     * an {@link AdaptiveTreeComposite}.
     */
    @Override
    public void setInput(IWorkbenchPart part, ISelection selection) {
        // Since the tree's DataComponent will still have its properties shown,
        // continue with the default setInput() operation.
        super.setInput(part, selection);

        // Get and check the selection.
        Assert.isTrue(selection instanceof IStructuredSelection);
        Object input = ((IStructuredSelection) selection).getFirstElement();

        // Try to get the input TreeComposite from the selection.
        if (input != null && input instanceof Component) {
            IComponentVisitor visitor = new SelectiveComponentVisitor() {
                @Override
                public void visit(TreeComposite component) {

                    // If the visited tree is new, we need to replace the
                    // current tree with the new one.
                    if (tree != component) {
                        // If possible, unregister from the current tree.
                        if (tree != null) {
                            tree.unregister(TreePropertySection.this);
                        }

                        // Set the reference to the new tree and register for
                        // updates. We need the updates for keeping the type
                        // ComboViewer updated.
                        tree = component;
                        tree.register(TreePropertySection.this);

                        // Unset the isAdaptive flag.
                        isAdaptive = false;

                        System.out.println("TreePropertySection message: " + "Setting the input to tree "
                                + tree.getName() + ".");
                    }

                    return;
                }

                @Override
                public void visit(AdaptiveTreeComposite component) {
                    visit((TreeComposite) component);
                    isAdaptive = true;
                }
            };
            ((Component) input).accept(visitor);
        }

        // Enable or disable the add button depending on whether the input is
        // valid.
        if (add != null) {
            boolean canAdd = (canAdd(tree) != null);
            add.setEnabled(canAdd);
        }

        // Try to find the associated ICEFormEditor. It will be marked as dirty
        // if the properties change.
        if (part instanceof TreeCompositeViewer) {
            editor = ((TreeCompositeViewer) part).getFormEditor();
        } else {
            editor = null;
        }

        // Refresh the type Combo widget.
        refreshTypeWidgets();

        return;
    }

    /**
     * Refreshes any widgets required for the current input for this property
     * section.
     */
    @Override
    public void refresh() {

        if (tree != null) {
            // Set the contents of the TableViewer.
            tableViewer.setInput(tree);
            // Automatically adjust the widths of the name and value columns.
            nameColumn.getColumn().pack();
            valueColumn.getColumn().pack();
            // Refresh the type Combo widget.
            refreshTypeWidgets();
        }

        // Refresh the size of the contents of the section.
        resizePropertyView();

        // Now perform the usual refresh operation.
        super.refresh();

        return;
    }

    /**
     * Because this <code>Section</code> must alter the layout behavior of the
     * containing Eclipse RCP View in order to properly lay out the
     * {@link #tableViewer} and related <code>Controls</code>, this method must
     * store any layout properties for parent <code>Composites</code> (e.g.,
     * {@link #scrollComposite} and {@link #scrollCompositeClient}) that will be
     * changed in {@link #resizePropertyView()}. These layout properties must be
     * restored in {@link #aboutToBeHidden()}.
     */
    @Override
    public void aboutToBeShown() {
        super.aboutToBeShown();

        // Store some of the default properties of the ScrolledComposite.
        scrollMinWidth = scrollComposite.getMinWidth();
        scrollMinHeight = scrollComposite.getMinHeight();

        return;
    }

    /**
     * Restores the layout properties stored in {@link #aboutToBeShown()} and
     * changed in {@link #resizePropertyView()}.
     */
    @Override
    public void aboutToBeHidden() {
        super.aboutToBeHidden();

        // If possible, we need to restore the settings clobbered in
        // resizeProperties();
        if (!scrollComposite.isDisposed()) {
            // Restore the GridData of the Section's parent.
            GridData gridData = (GridData) section.getParent().getLayoutData();
            gridData.heightHint = SWT.DEFAULT;

            // Restore the default size of the ScrolledComposite's client.
            Point size = scrollCompositeClient.computeSize(SWT.DEFAULT, SWT.DEFAULT);
            scrollCompositeClient.setSize(size);

            // Restore the min size of the ScrolledComposite.
            scrollComposite.setMinSize(scrollMinWidth, scrollMinHeight);

            // Re-layout the ScrolledComposite.
            scrollComposite.layout();
        }

        return;
    }

    /**
     * Disposes of the {@link #section} and its contained Widgets and clears
     * references to any class variables.
     */
    @Override
    public void dispose() {
        super.dispose();

        // Clear the references to the selected Tree and selected property
        // Entry.
        tree = null;
        selectedEntry = null;

        // Clear the references to the Property View's ScrolledComposite and its
        // client.
        scrollComposite = null;
        scrollCompositeClient = null;

        // Dispose the section and clear references to all widgets. (Disposing
        // the section should dispose of its child widgets recursively.)
        section.dispose();
        section = null;
        tableViewer = null;
        nameColumn = null;
        valueColumn = null;
        add = null;
        delete = null;

        return;
    }

    // ----------------------------------------- //

    // ---- AdaptiveTreeComposite type selection widgets ---- //
    /**
     * Creates the {@link #type} selection widget for changing the type of the
     * current <code>AdaptiveTreeComposite</code>. These widgets are
     * 
     * @param client
     *            The client <code>Composite</code> that should contain the type
     *            selection widgets.
     * @return The <code>Composite</code> that contains the type selection
     *         widgets.
     */
    private Composite createTypeComposite(Composite client) {

        // Get the client's background color.
        Color backgroundColor = client.getBackground();

        // Create the type sub-section Composite that will contain the
        // type label and combo (dropdown).
        Composite typeComposite = new Composite(client, SWT.NONE);
        typeComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, true));
        typeComposite.setBackground(backgroundColor);

        // Set the layout of the Composite to a vertical fill layout.
        // This ensures the type Label is above the Combo and that both
        // consume all available space.
        FillLayout typeCompositeLayout = new FillLayout(SWT.VERTICAL);
        typeCompositeLayout.marginHeight = 5;
        typeCompositeLayout.marginWidth = 3;
        typeCompositeLayout.spacing = 5;
        typeComposite.setLayout(typeCompositeLayout);

        // Add the type Label and an empty Combo.
        Label typeLabel = new Label(typeComposite, SWT.NONE);
        typeLabel.setText("Type:");
        typeLabel.setBackground(backgroundColor);

        // Create the ComboViewer for selecting the adaptive type.
        type = new ComboViewer(typeComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
        type.getCombo().setBackground(backgroundColor);
        // Use an ArrayContentProvider so we can set the List of type Strings as
        // input to the ComboViewer.
        type.setContentProvider(ArrayContentProvider.getInstance());
        // The labels for each value should just be their String value.
        type.setLabelProvider(new LabelProvider() {
            @Override
            public String getText(Object element) {
                return (element != null ? element.toString() : "");
            }
        });

        // Add a listener to set the type of the AdaptiveTreeComposite when the
        // Combo's selection changes.
        ISelectionChangedListener listener = new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                ISelection selection = event.getSelection();
                if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
                    IStructuredSelection s = (IStructuredSelection) selection;
                    String type = (String) s.getFirstElement();
                    ((AdaptiveTreeComposite) tree).setType(type);
                }
            }
        };
        type.addSelectionChangedListener(listener);

        return typeComposite;
    }

    /**
     * Refreshes the contents of the {@link #type} selection widget. If the
     * {@link #tree} is not an {@link AdaptiveTreeComposite}, then the entire
     * {@link #typeComposite} is hidden.
     */
    private void refreshTypeWidgets() {

        // Set the default empty list of types and the default empty selection.
        List<String> types = new ArrayList<String>(1);
        ISelection selection = new StructuredSelection();

        // Get the list of adaptive types if possible.
        if (tree != null && isAdaptive) {
            // Update the available types in the type Combo widget.
            AdaptiveTreeComposite adaptiveTree = (AdaptiveTreeComposite) tree;
            types = adaptiveTree.getTypes();
            String adaptiveType = adaptiveTree.getType();
            if (adaptiveType != null) {
                selection = new StructuredSelection(adaptiveType);
            }
        }

        // Set the ComboViewer's contents to the List of type Strings.
        type.setInput(types);
        type.setSelection(selection);

        // Hide the type selection widgets if the List is empty.
        boolean hide = types.isEmpty();
        typeComposite.setVisible(!hide);
        ((GridData) typeComposite.getLayoutData()).exclude = hide;

        // Re-adjust the size of the type Composite.
        typeComposite.pack();
        typeComposite.getParent().layout();

        return;
    }

    // ------------------------------------------------------ //\

    // ---- TreeComposite property Table widgets ---- //
    /**
     * Creates the table that displays properties for viewing and editing.
     * 
     * @param client
     *            The client <code>Composite</code> that should contain the
     *            table of properties.
     * @return The <code>TableViewer</code> for the table of properties.
     */
    protected TableViewer createTableViewer(Composite client) {

        TableViewer tableViewer = null;

        if (client != null) {
            Table table;

            // Create the TableViewer and the underlying Table Control.
            tableViewer = new TableViewer(client, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL);
            // Set some properties for the table.
            table = tableViewer.getTable();
            table.setHeaderVisible(true);
            table.setLinesVisible(true);

            // Set up the content provider for the table viewer. Now the table
            // viewer's input can be set.
            tableViewer.setContentProvider(new TreePropertyContentProvider());

            // Enable tool tips for the Table's cells.
            ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.NO_RECREATE);

            // Populate the TableViewer with its columns.
            addTableViewerColumns(tableViewer);
        }

        return tableViewer;
    }

    /**
     * Adds columns to the provided <code>TableViewer</code>.
     * 
     * @param tableViewer
     */
    protected void addTableViewerColumns(TableViewer tableViewer) {

        if (tableViewer != null) {
            TableColumn column;
            ICellContentProvider contentProvider;

            // ---- Create the name column. ---- //
            nameColumn = new TableViewerColumn(tableViewer, SWT.LEFT);
            column = nameColumn.getColumn();
            column.setText("Name");
            column.setToolTipText("The name of the property");
            column.setResizable(true);
            // Create an ICellContentProvider, then use it to generate the
            // column labels and provide editing support for the name column.
            contentProvider = new NameCellContentProvider() {
                @Override
                public boolean setValue(Object element, Object value) {
                    boolean changed = super.setValue(element, value);
                    // If the value changed, mark the associated ICEFormEditor
                    // as dirty.
                    if (changed && editor != null) {
                        editor.setDirty(true);
                    }
                    return changed;
                }
            };
            nameColumn.setLabelProvider(new CellColumnLabelProvider(contentProvider));
            nameColumn.setEditingSupport(new TextCellEditingSupport(tableViewer, contentProvider));
            // --------------------------------- //

            // ---- Create the value column. ---- //
            valueColumn = new TableViewerColumn(tableViewer, SWT.LEFT);
            column = valueColumn.getColumn();
            column.setText("Value");
            column.setToolTipText("The value of the property");
            column.setResizable(true);
            // Create an ICellContentProvider, then use it to generate the
            // column labels and provide editing support for the value column.
            contentProvider = new ValueCellContentProvider() {
                @Override
                public boolean setValue(Object element, Object value) {
                    boolean changed = super.setValue(element, value);
                    // If the value changed, mark the associated ICEFormEditor
                    // as dirty.
                    if (changed && editor != null) {
                        editor.setDirty(true);
                    }
                    return changed;
                }
            };
            valueColumn.setLabelProvider(new CellColumnLabelProvider(contentProvider));
            valueColumn.setEditingSupport(
                    new ComboCellEditingSupport(tableViewer, (IComboCellContentProvider) contentProvider));
            // ---------------------------------- //
        }

        return;
    }

    // ---------------------------------------------- //

    // ---- Add/Delete property widgets ---- //
    /**
     * Creates {@link #add} and {@link #delete} buttons for changing the
     * parameters displayed in the properties.
     * 
     * @param client
     *            The client <code>Composite</code> that should contain the
     *            buttons.
     * @return The <code>Composite</code> that contains the add and delete
     *         buttons.
     */
    private Composite createButtons(Composite client) {

        // Create the two buttons to add/remove parameters next to the table.
        // Create a Composite to hold these buttons.
        Composite composite = new Composite(client, SWT.NONE);
        composite.setBackground(client.getBackground());
        // The buttons should sit in a column.
        composite.setLayout(new GridLayout());

        // Create the add button.
        add = new Button(composite, SWT.PUSH);
        add.setBackground(client.getBackground());
        add.setText("+");
        add.setToolTipText("Add a new property.");
        // Set the layout data. It should default to at least 30 pixels wide.
        GridData gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
        gridData.heightHint = 30;
        gridData.widthHint = 30;
        add.setLayoutData(gridData);

        // Add should only be enabled if something's properties are displayed.
        // Delete should only be enabled if a property is selected.
        boolean canAdd = (tree != null);
        add.setEnabled(canAdd);

        // Add a listener to add a new property/parameter when the add button is
        // clicked.
        add.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                addParameter();
            }
        });

        // Create the delete button.
        delete = new Button(composite, SWT.PUSH);
        delete.setBackground(client.getBackground());
        delete.setText("-");
        delete.setToolTipText("Delete the selected property.");
        // Set the layout data. It should default to at least 30 pixels wide.
        gridData = new GridData(SWT.RIGHT, SWT.CENTER, false, false);
        gridData.heightHint = 30;
        gridData.widthHint = 30;
        delete.setLayoutData(gridData);

        // Listen for changes to the TableViewer's selection and enable/disable
        // the delete button if a property is selected.
        delete.setEnabled(false);
        ISelectionChangedListener listener = new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {

                // Disable the option to delete.
                selectedEntry = null;
                delete.setEnabled(false);

                // Try to get the selected Entry from the selection.
                ISelection selection = event.getSelection();
                if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
                    Entry entry = ((TreeProperty) ((IStructuredSelection) selection).getFirstElement()).getEntry();

                    // If the Entry is not required, we can delete it.
                    if (canDelete(entry)) {
                        selectedEntry = entry;
                        delete.setEnabled(true);
                    }
                }

                return;
            }
        };
        tableViewer.addSelectionChangedListener(listener);

        // Add a listener to delete the currently selected property/parameter
        // when the delete button is clicked.
        delete.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent event) {
                deleteParameter();
            }
        });

        return composite;
    }

    // TODO We may need a different requirement on when we can add an Entry to
    // the selected tree or where we can add it.
    /**
     * Gets whether or not the specified <code>TreeComposite</code> can have an
     * <code>Entry</code> (aka property or parameter) added to it. The
     * <code>DataComponent</code> that can receive the new <code>Entry</code> is
     * returned.
     * 
     * @param tree
     *            The tree to which we would like to add a new property or
     *            parameter.
     * @return The tree's data node that can receive a new property or
     *         parameter, or null if one cannot be added.
     */
    private DataComponent canAdd(TreeComposite tree) {

        DataComponent dataNode = null;

        // Look for the active data node. If there is no active data node, get
        // the first available data node.
        if (tree != null) {
            dataNode = (DataComponent) tree.getActiveDataNode();
            if (dataNode == null && !tree.getDataNodes().isEmpty()) {
                dataNode = (DataComponent) tree.getDataNodes().get(0);
            }
        }

        return dataNode;
    }

    /**
     * Adds a new <code>Entry</code> (aka property or parameter) to the active
     * or first available data node in the {@link #tree}.
     */
    private void addParameter() {

        // Get either the active data node or the first available data node.
        DataComponent dataNode = canAdd(tree);

        // If there is a data node, add a new Entry to it.
        if (dataNode != null && !dataNode.contains("New parameter")) {
            // Create an Entry with a BasicEntryContentProvider.
            Entry entry = new Entry(new BasicEntryContentProvider());
            // Set the Entry's initial properties.
            entry.setName("new_parameter");
            entry.setDescription("");
            entry.setTag(entry.getName());
            entry.setRequired(false);
            entry.setValue("");
            // Add the Entry to the data node.
            dataNode.addEntry(entry);
        }

        return;
    }

    /**
     * Gets whether or not the specified <code>Entry</code> (aka property or
     * parameter) can be deleted from its containing <code>DataComponent</code>
     * or data node in the {@link #tree}. Any parameter can be deleted.
     * 
     * @param entry
     *            The property or parameter that is a candidate for deletion.
     * @return True if the entry can be deleted, false otherwise.
     */
    private boolean canDelete(Entry entry) {
        return (entry != null && !entry.isRequired());
    }

    /**
     * Deletes the <code>Entry</code> (aka property or parameter) that is
     * currently selected in the {@link #tableViewer}. A reference to this
     * <code>Entry</code> is stored as {@link #selectedEntry} when selected in
     * the <code>TableViewer</code>.
     */
    private void deleteParameter() {

        if (canDelete(selectedEntry)) {
            String entryName = selectedEntry.getName();
            // Find the data node that has the selected Entry. If the Entry is
            // found, remove it from the containing data node and stop.
            for (Component component : tree.getDataNodes()) {
                DataComponent dataNode = (DataComponent) component;
                if (dataNode.contains(entryName) && dataNode.retrieveEntry(entryName) == selectedEntry) {
                    dataNode.deleteEntry(entryName);
                    break;
                }
            }
        }

        return;
    }

    // ------------------------------------- //

    /**
     * Gets the <code>TreeComposite</code> whose properties are displayed in
     * this section.
     * 
     * @return The current tree whose properties are shown in a table.
     */
    protected TreeComposite getTree() {
        return tree;
    }

    /**
     * Resizes the Section's client and updates the Property Viewer's
     * {@link ScrolledComposite} to account for GridLayouts in the client.
     */
    private void resizePropertyView() {
        // Disable re-drawing for the ScrolledComposite.
        scrollComposite.setRedraw(false);

        int verticalPadding = scrollComposite.getHorizontalBar().getSize().y;
        int horizontalPadding = scrollComposite.getVerticalBar().getSize().x;
        Rectangle clientArea = scrollComposite.getClientArea();
        Point clientAreaSize = new Point(clientArea.width - horizontalPadding, clientArea.height - verticalPadding);

        // Recompute the size of the first Composite in the ScrolledComposite
        // based on the width of the ScrolledComposite's client area.
        Point size = scrollCompositeClient.computeSize(clientAreaSize.x, clientAreaSize.y);

        // Update the size of the ScrolledComposite's client.
        scrollCompositeClient.setSize(size);

        // Set the minimum size at which the ScrolledComposite will start
        // drawing scroll bars. This should be the size of its client area minus
        // the spaces the scroll bars would consume.
        scrollComposite.setMinSize(clientAreaSize.x + 1, clientAreaSize.y + 1);

        // We need to call layout() so the ScrolledComposite will update.
        scrollComposite.layout();

        // Set the height hint for the Section's parent Composite. This keeps
        // the TableViewer from going beyond the bottom edge of the Properties
        // Viewer. We want to keep the add and delete buttons visible, and the
        // TableViewer already has its own scroll bars!
        Composite sectionParent = section.getParent();
        GridData gridData = (GridData) sectionParent.getLayoutData();
        GridLayout gridLayout = (GridLayout) sectionParent.getParent().getLayout();
        gridData.heightHint = sectionParent.getParent().getSize().y - gridLayout.marginTop;

        // The parent of the Section has a FillLayout. Its parent has a
        // GridLayout, but sometimes it does not update. Tell it to layout so
        // the Section's parent will update its size.
        section.getParent().getParent().layout();

        // Enable re-drawing for the ScrolledComposite.
        scrollComposite.setRedraw(true);

        return;
    }

    /**
     * Gets the currently associated {@link ICEFormEditor} to which the
     * {@link #tree} belongs, or null if one does not exist.
     * 
     * @return The associated {@code ICEFormEditor}, or {@code null} if none.
     */
    protected ICEFormEditor getFormEditor() {
        return editor;
    }

    // ---- Implements IUpdateableListener ---- //
    /**
     * Listens for updates from the {@link #tree}. If the tree is an
     * {@link AdaptiveTreeComposite}, then this method updates the {@link #type}
     * selection widget's selection.
     */
    @Override
    public void update(IUpdateable component) {

        // If the adaptive tree's type was changed, we should update the type
        // Combo widget.
        if (component != null && component == tree && isAdaptive) {
            // Create a selection from the adaptive tree's current type.
            String adaptiveType = ((AdaptiveTreeComposite) component).getType();
            final StructuredSelection selection;
            selection = new StructuredSelection(adaptiveType);

            // Use the UI thread to update the type ComboViewer's selection.
            PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
                @Override
                public void run() {
                    type.setSelection(selection);
                }
            });
        }

        return;
    }
    // ---------------------------------------- //

}