org.eclipse.rcptt.ctx.parameters.ui.ParametersContextEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.rcptt.ctx.parameters.ui.ParametersContextEditor.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2014 Xored Software Inc and others.
 * 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:
 *     Xored Software Inc - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.rcptt.ctx.parameters.ui;

import static java.util.Arrays.asList;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.value.ComputedValue;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.IViewerObservableList;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
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.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolTip;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchSite;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.eclipse.rcptt.core.model.ModelException;
import org.eclipse.rcptt.internal.core.RcpttPlugin;
import org.eclipse.rcptt.internal.ui.Images;
import org.eclipse.rcptt.internal.ui.Q7UIPlugin;
import org.eclipse.rcptt.parameters.Parameter;
import org.eclipse.rcptt.parameters.ParametersContext;
import org.eclipse.rcptt.parameters.ParametersFactory;
import org.eclipse.rcptt.ui.commons.ArrayTreeContentProvider;
import org.eclipse.rcptt.ui.context.BaseContextEditor;
import org.eclipse.rcptt.ui.editors.EditorHeader;
import org.eclipse.rcptt.ui.editors.IQ7EditorActionsProvider;
import org.eclipse.rcptt.ui.editors.NamedElementEditorActions.INamedElementActions;
import org.eclipse.rcptt.util.StringUtils;

public class ParametersContextEditor extends BaseContextEditor implements IQ7EditorActionsProvider {

    private static final String NAME_PROPERTY = "name";
    private static final String VALUE_PROPERTY = "value";
    private static final String[] NAME_PROPERTIES = new String[] { NAME_PROPERTY };
    private static final String[] VALUE_PROPERTIES = new String[] { VALUE_PROPERTY };

    private static Parameter newParameterItem;
    static {
        newParameterItem = ParametersFactory.eINSTANCE.createParameter();
        newParameterItem.setName("Add new parameter");
        newParameterItem.setValue("");
    }

    private Table table;
    private TableViewer viewer;
    private ToolTip tip;
    private String lastNewName = "";

    private TableColumn nameColumn;
    private TableColumn valueColumn;

    // to preserve default sort order while keeping newParameterItem on bottom
    private HashMap<String, Integer> nameToIndex = new HashMap<String, Integer>();

    @Override
    public void setSelectionAtLine(int line) {
        if (table != null) {
            if (line < table.getItemCount()) {
                table.deselectAll();
                table.select(line);
                table.setFocus();
            }
        }
    }

    public ParametersContext getContextElement() {
        try {
            return (ParametersContext) super.getElement().getNamedElement();
        } catch (ModelException e) {
            Q7UIPlugin.log(e);
            return null;
        }
    }

    public void dispose() {
        tip.setVisible(false);
        super.dispose();
    }

    public Control create(Composite parent, FormToolkit toolkit, IWorkbenchSite site, EditorHeader header) {
        Section section = toolkit.createSection(parent,
                Section.TITLE_BAR | Section.TWISTIE | Section.EXPANDED | Section.NO_TITLE_FOCUS_BOX);
        GridDataFactory.fillDefaults().grab(true, true).applyTo(section);
        section.setText("Parameters");

        Composite composite = toolkit.createComposite(section);
        section.setClient(composite);
        GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
        viewer = createTable(composite, toolkit);
        createPanel(composite, toolkit);

        tip = new ToolTip(parent.getShell(), SWT.BALLOON);
        tip.setText("Error");

        // Composite forToolbar = toolkit.createComposite(section);
        Clipboard clipboard = new Clipboard(parent.getDisplay());
        pasteAction = new PasteAction(clipboard);
        copyAction = new CopyAction(clipboard);
        ToolBarManager toolBarManager = new ToolBarManager(SWT.HORIZONTAL);
        toolBarManager.add(copyAction);
        toolBarManager.add(pasteAction);
        section.setTextClient(toolBarManager.createControl(section));
        MenuManager menu = new MenuManager();
        menu.add(copyAction);
        menu.add(pasteAction);
        menu.addMenuListener(new IMenuListener() {

            @Override
            public void menuAboutToShow(IMenuManager manager) {
                copyAction.update();
                pasteAction.update();
            }
        });
        menu.update(true);
        viewer.getControl().setMenu(menu.createContextMenu(viewer.getControl()));
        return section;
    }

    private void sortBy(TableColumn column) {
        if (table.getSortColumn() != column) {
            table.setSortColumn(column);
            table.setSortDirection(SWT.UP);
            viewer.setComparator(new Comparator(column, SWT.UP));
        } else if (table.getSortDirection() == SWT.UP) {
            table.setSortDirection(SWT.DOWN);
            viewer.setComparator(new Comparator(column, SWT.DOWN));
        } else {
            table.setSortColumn(null);
            table.setSortDirection(SWT.NONE);
            viewer.setComparator(new Comparator());
        }

        viewer.refresh();
    }

    private static final ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();

    class CopyAction extends Action {
        private final Clipboard clipboard;

        public CopyAction(Clipboard clipboard) {
            super("Copy selected parameters", sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
            setDisabledImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_COPY_DISABLED));
            this.clipboard = clipboard;
            viewer.addSelectionChangedListener(new ISelectionChangedListener() {
                @Override
                public void selectionChanged(SelectionChangedEvent event) {
                    update();
                }
            });
            update();
        }

        @Override
        public void run() {
            IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
            Parameter[] parameters = new Parameter[selection.size()];
            Object[] selected = selection.toArray();
            Properties properties = new Properties();
            for (int i = 0; i < selection.size(); i++) {
                Parameter parameter = (Parameter) selected[i];
                parameters[i] = parameter;
                properties.put(parameter.getName(), parameter.getValue());
            }
            StringWriter writer = new StringWriter();
            try {
                properties.store(writer, "");
            } catch (IOException e) {
                RcpttPlugin.log(e);
                writer = new StringWriter();
            }

            clipboard.setContents(new Object[] { parameters, writer.getBuffer().toString() },
                    new Transfer[] { ParametersTransfer.INSTANCE, TextTransfer.getInstance() });
        }

        Object[] getSelection() {
            IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
            return selection.toArray();
        }

        void update() {
            setEnabled(getSelection().length > 0);
        }

    }

    static class Conflict {
        public final String name;
        public final String value1;
        public final String value2;

        public Conflict(String name, String value1, String value2) {
            super();
            this.name = name;
            this.value1 = value1;
            this.value2 = value2;
        }
    }

    private static Map<String, String> toDictionary(Collection<Parameter> parameters) {
        Map<String, String> rv = new HashMap<String, String>();
        for (Parameter parameter : parameters) {
            rv.put(parameter.getName(), parameter.getValue());
        }
        return rv;
    }

    private static Collection<Conflict> findConflicts(Map<String, String> first, Map<String, String> second) {
        List<Conflict> rv = Lists.newArrayList();

        for (Object key : (Diffs.computeMapDiff(first, second).getChangedKeys())) {
            String name = (String) key;
            String value1 = first.get(key);
            String value2 = second.get(name);
            rv.add(new Conflict(name, value1, value2));
        }
        return rv;
    }

    /**
     * Asks user to select parameters to overwrite
     * 
     * @return conflicts to overwrite
     */
    private Collection<Conflict> resolveConflicts(final Collection<Conflict> conflict) {

        CheckedTreeSelectionDialog dialog = new CheckedTreeSelectionDialog(getForm().getForm().getShell(),
                new LabelProvider(), new ArrayTreeContentProvider()) {

            @Override
            protected CheckboxTreeViewer createTreeViewer(Composite parent) {
                final CheckboxTreeViewer rv = super.createTreeViewer(parent);
                TreeViewerColumn column = new TreeViewerColumn(rv, SWT.NONE);
                column.getColumn().setText("Name");
                column.setLabelProvider(new ColumnLabelProvider() {

                    @Override
                    public String getText(Object element) {
                        return ((Conflict) element).name;
                    }

                });
                column = new TreeViewerColumn(rv, SWT.NONE);
                column.getColumn().setText("Original value");
                column.setLabelProvider(new ColumnLabelProvider() {

                    @Override
                    public String getText(Object element) {
                        return ((Conflict) element).value1;
                    }

                });
                column = new TreeViewerColumn(rv, SWT.NONE);
                column.getColumn().setText("New value");
                column.setLabelProvider(new ColumnLabelProvider() {

                    @Override
                    public String getText(Object element) {
                        return ((Conflict) element).value2;
                    }

                });
                rv.getTree().setHeaderVisible(true);
                rv.refresh();
                for (TreeColumn col : rv.getTree().getColumns()) {
                    col.pack();
                }
                rv.addSelectionChangedListener(new ISelectionChangedListener() {
                    @Override
                    public void selectionChanged(final SelectionChangedEvent event) {
                        if (!event.getSelection().isEmpty()) {
                            rv.setSelection(StructuredSelection.EMPTY);
                        }
                    }
                });
                return rv;
            }
        };

        dialog.setTitle("Parameters name conflict");
        dialog.setMessage("Select parameters to overwrite with new values");
        dialog.setInput(conflict);
        if (dialog.open() != Dialog.OK)
            return null;
        Object[] result = dialog.getResult();
        if (result == null)
            return null;
        List<Conflict> rv = Lists.newArrayList();
        for (Object object : result) {
            rv.add((Conflict) object);
        }
        return rv;
    }

    class PasteAction extends Action {
        private final Clipboard clipboard;

        public PasteAction(Clipboard clipboard) {
            super("Paste",
                    PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
            setDisabledImageDescriptor(sharedImages.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE_DISABLED));
            this.clipboard = clipboard;
            viewer.getControl().addFocusListener(new FocusListener() {

                @Override
                public void focusLost(FocusEvent e) {
                    update();
                }

                @Override
                public void focusGained(FocusEvent e) {
                    update();
                }
            });
            update();
        }

        protected void update() {
            setEnabled(getPasted().length > 0);
        }

        @Override
        public void run() {
            ParametersContext element = getContextElement();
            List<Parameter> newParameters = asList(getPasted());

            Map<String, String> origMap = toDictionary(element.getParameters());
            Map<String, String> newMap = toDictionary(newParameters);

            Collection<Conflict> conflicts = findConflicts(origMap, newMap);

            if (conflicts.size() > 0) {
                // only overwrite conflicts selected by user
                for (Conflict conflict : conflicts) {
                    newMap.remove(conflict.name);
                }
                conflicts = resolveConflicts(conflicts);
                if (conflicts == null)
                    return;
                for (Conflict conflict : conflicts) {
                    newMap.put(conflict.name, conflict.value2);
                }
            }

            Map<String, Parameter> paramMap = Maps.newHashMap();
            for (Parameter parameter : element.getParameters()) {
                paramMap.put(parameter.getName(), parameter);
            }

            for (Map.Entry<String, String> entry : newMap.entrySet()) {
                Parameter parameter = paramMap.get(entry.getKey());
                if (parameter == null) {
                    parameter = ParametersFactory.eINSTANCE.createParameter();
                    parameter.setName(entry.getKey());
                    element.getParameters().add(parameter);
                }
                parameter.setValue(entry.getValue());
            }

            updateIndexes();
            viewer.refresh();
        }

        private Parameter[] getPasted() {
            Parameter[] rv = (Parameter[]) clipboard.getContents(ParametersTransfer.INSTANCE);
            if (rv == null)
                rv = new Parameter[0];
            return rv;
        }
    }

    PasteAction pasteAction;
    CopyAction copyAction;

    private TableViewer createTable(Composite parent, FormToolkit toolkit) {
        table = new Table(parent, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
        GridDataFactory.fillDefaults().grab(true, true).hint(100, 50).applyTo(table);
        table.setHeaderVisible(true);
        toolkit.adapt(table);
        table.setLinesVisible(true);

        nameColumn = new TableColumn(table, SWT.LEFT);
        nameColumn.setText("Name");
        nameColumn.setWidth(200);
        nameColumn.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                sortBy(nameColumn);
            }
        });

        valueColumn = new TableColumn(table, SWT.LEFT);
        valueColumn.setText("Value");
        valueColumn.setWidth(350);
        valueColumn.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                sortBy(valueColumn);
            }
        });

        table.addKeyListener(new KeyAdapter() {

            public void keyPressed(KeyEvent e) {
                switch (e.keyCode) {
                case SWT.DEL:
                case SWT.BS:
                    removeSelected();
                    break;
                case SWT.INSERT:
                    addNew();
                    break;
                case SWT.CR:
                case SWT.F2:
                    lastNewName = "";
                    Object el = ((StructuredSelection) viewer.getSelection()).getFirstElement();
                    if (el != null)
                        viewer.editElement(el, 0);
                    else
                        viewer.editElement(newParameterItem, 0);
                    break;
                }
            }
        });

        final TableViewer viewer = new TableViewer(table);
        viewer.setContentProvider(new IStructuredContentProvider() {

            public void dispose() {
                // do nothing
            }

            public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
                // do nothing
            }

            public Object[] getElements(Object inputElement) {
                ParametersContext ctx = (ParametersContext) inputElement;

                List<Parameter> elements = new ArrayList<Parameter>();
                elements.addAll(ctx.getParameters());
                elements.add(newParameterItem);

                return elements.toArray();
            }

        });

        // --

        TableViewerColumn nameViewerColumn = new TableViewerColumn(viewer, nameColumn);
        nameViewerColumn.setLabelProvider(new ColumnLabelProvider() {

            public String getText(Object element) {
                Parameter p = (Parameter) element;
                return p.getName();
            }

            public Image getImage(Object element) {
                return element == newParameterItem
                        ? Images.getImageDescriptor(ISharedImages.IMG_OBJ_ADD).createImage()
                        : Images.getImage(Images.PARAMETER);
            }

            public Font getFont(Object element) {
                if (element == newParameterItem) {
                    Font tableFont = table.getFont();
                    FontData fd = tableFont.getFontData()[0];
                    fd.setStyle(fd.getStyle() | SWT.ITALIC);

                    return new Font(tableFont.getDevice(), fd);
                } else
                    return null;
            }
        });

        nameViewerColumn.setEditingSupport(new EditingSupport(viewer) {

            protected CellEditor getCellEditor(final Object element) {
                final TextCellEditor editor = new TextCellEditor(table);
                editor.getControl().addTraverseListener(new TraverseListener() {

                    public void keyTraversed(TraverseEvent e) {
                        Text text = (Text) editor.getControl();

                        switch (e.detail) {
                        case SWT.TRAVERSE_TAB_NEXT:
                            if (validateName(element, text.getText()))
                                viewer.editElement(element, 1);
                            e.detail = SWT.TRAVERSE_NONE;
                            e.doit = false;
                            break;
                        case SWT.TRAVERSE_ARROW_NEXT:
                            if (e.keyCode == SWT.ARROW_DOWN) {
                                if (table.getItemCount() > table.getSelectionIndex() + 1
                                        && (isNewAndBlank(element, text.getText())
                                                || validateName(element, text.getText())))
                                    viewer.editElement(viewer.getElementAt(table.getSelectionIndex() + 1), 0);
                                e.detail = SWT.TRAVERSE_NONE;
                                e.doit = false;
                            }
                            break;
                        case SWT.TRAVERSE_ARROW_PREVIOUS:
                            if (e.keyCode == SWT.ARROW_UP) {
                                if (table.getSelectionIndex() > 0 && (isNewAndBlank(element, text.getText())
                                        || validateName(element, text.getText())))
                                    viewer.editElement(viewer.getElementAt(table.getSelectionIndex() - 1), 0);
                                e.detail = SWT.TRAVERSE_NONE;
                                e.doit = false;
                            }
                            break;
                        }
                    }

                });
                return editor;
            }

            protected boolean canEdit(Object element) {
                return true;
            }

            protected Object getValue(Object element) {
                if (lastNewName.length() > 0)
                    return lastNewName;
                else if (element == newParameterItem)
                    return "";
                else {
                    Parameter p = (Parameter) element;
                    return p.getName();
                }
            }

            protected void setValue(final Object element, Object value) {
                String name = (String) value;
                Parameter param = (Parameter) element;
                ParametersContext ctx = getContextElement();

                if (isNewAndBlank(element, name)) {
                    lastNewName = "";
                    return;
                }

                if (!validateName(element, name)) {
                    table.getDisplay().asyncExec(new Runnable() {

                        public void run() {
                            if (!table.isDisposed())
                                viewer.editElement(element, 0);
                        }
                    });

                    return;
                }

                if (param != newParameterItem) {
                    if (!param.getName().equals(name)) {
                        Integer index = nameToIndex.get(param.getName());
                        nameToIndex.remove(param.getName());
                        nameToIndex.put(name, index);

                        param.setName(name);
                        viewer.update(element, NAME_PROPERTIES);
                    }
                } else {
                    final Parameter newParam = ParametersFactory.eINSTANCE.createParameter();
                    newParam.setName(name);
                    newParam.setValue("");

                    nameToIndex.put(name, table.getItemCount() - 1);

                    ctx.getParameters().add(newParam);
                    viewer.add(newParam);

                    viewer.setSelection(new StructuredSelection(newParam), true);
                    table.getDisplay().asyncExec(new Runnable() {

                        public void run() {
                            viewer.editElement(newParam, 1);
                        }
                    });
                }
            }

        });

        // --

        TableViewerColumn valueViewerColumn = new TableViewerColumn(viewer, valueColumn);
        valueViewerColumn.setLabelProvider(new ColumnLabelProvider() {
            public String getText(Object element) {
                Parameter p = (Parameter) element;
                return p.getValue();
            }
        });

        valueViewerColumn.setEditingSupport(new EditingSupport(viewer) {

            protected CellEditor getCellEditor(final Object element) {
                final TextCellEditor editor = new TextCellEditor(table);
                editor.getControl().addTraverseListener(new TraverseListener() {

                    public void keyTraversed(TraverseEvent e) {
                        Text text = (Text) editor.getControl();

                        switch (e.detail) {
                        case SWT.TRAVERSE_TAB_PREVIOUS:
                            if (validateName(element, text.getText()))
                                viewer.editElement(element, 0);
                            e.detail = SWT.TRAVERSE_NONE;
                            e.doit = false;
                            break;
                        case SWT.TRAVERSE_ARROW_NEXT:
                            if (e.keyCode == SWT.ARROW_DOWN) {
                                if (table.getItemCount() > table.getSelectionIndex() + 1)
                                    viewer.editElement(viewer.getElementAt(table.getSelectionIndex() + 1), 1);
                                e.detail = SWT.TRAVERSE_NONE;
                                e.doit = false;
                            }
                            break;
                        case SWT.TRAVERSE_ARROW_PREVIOUS:
                            if (e.keyCode == SWT.ARROW_UP) {
                                if (table.getSelectionIndex() > 0)
                                    viewer.editElement(viewer.getElementAt(table.getSelectionIndex() - 1), 1);
                                e.detail = SWT.TRAVERSE_NONE;
                                e.doit = false;
                            }
                            break;
                        }
                    }

                });
                return editor;
            }

            protected boolean canEdit(Object element) {
                return element != newParameterItem;
            }

            protected Object getValue(Object element) {
                Parameter p = (Parameter) element;
                return p.getValue();
            }

            protected void setValue(Object element, Object value) {
                Parameter p = (Parameter) element;
                String v = (String) value;
                if (!p.getValue().equals(v)) {
                    p.setValue((String) value);
                    viewer.update(element, VALUE_PROPERTIES);
                }
            }

        });

        // --

        updateIndexes();
        viewer.setComparator(new Comparator());

        // --

        viewer.setInput(getContextElement());

        return viewer;
    }

    private void updateIndexes() {
        nameToIndex.clear();
        EList<Parameter> params = getContextElement().getParameters();
        for (int i = 0; i < params.size(); ++i)
            nameToIndex.put(params.get(i).getName(), i);
    }

    private void createPanel(Composite parent, FormToolkit toolkit) {
        Composite panel = toolkit.createComposite(parent);
        GridLayoutFactory.fillDefaults().applyTo(panel);
        GridDataFactory.fillDefaults().grab(false, true).applyTo(panel);

        Button addButton = toolkit.createButton(panel, "Add", SWT.PUSH);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(addButton);
        addButton.setImage(Images.getImageDescriptor(ISharedImages.IMG_OBJ_ADD).createImage());
        addButton.addSelectionListener(new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                addNew();
            }
        });

        Button removeButton = toolkit.createButton(panel, "Remove", SWT.PUSH);
        GridDataFactory.fillDefaults().grab(true, false).applyTo(removeButton);
        removeButton.setImage(Images.getImageDescriptor(ISharedImages.IMG_ETOOL_DELETE).createImage());
        dbc.bindValue(SWTObservables.observeEnabled(removeButton), new ComputedValue() {

            protected Object calculate() {
                IViewerObservableList selection = ViewersObservables.observeMultiSelection(viewer);
                if (selection.size() == 1 && selection.get(0) == newParameterItem)
                    return false;
                else
                    return !selection.isEmpty();
            }
        });
        removeButton.addSelectionListener(new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                removeSelected();
            }
        });
    }

    protected void addNew() {
        lastNewName = "";
        viewer.editElement(newParameterItem, 0);
    }

    protected void removeSelected() {
        ParametersContext ctx = getContextElement();
        Object[] selection = ((IStructuredSelection) viewer.getSelection()).toArray();

        ArrayList<Object> removed = new ArrayList<Object>();
        for (Object o : selection) {
            if (o == newParameterItem)
                continue;
            ctx.getParameters().remove(o);
            nameToIndex.remove(((Parameter) o).getName());
            removed.add(o);
        }

        viewer.remove(removed.toArray());
    }

    private boolean validateName(final Object element, String name) {
        ParametersContext ctx = getContextElement();
        Parameter param = (Parameter) element;
        String error = null;

        if (StringUtils.isBlank(name))
            error = "Blank name";
        else
            for (Parameter p : ctx.getParameters())
                if (p != param && p.getName().equals(name)) {
                    error = "Duplicate parameter name";
                    break;
                }

        if (error != null) {
            lastNewName = name;

            Rectangle b = table.getSelection()[0].getBounds();
            Point p = table.toDisplay(b.x, b.y);
            tip.setLocation(p.x + 16, p.y + b.height);

            tip.setMessage(error);
            tip.setVisible(false);
            tip.setVisible(true);

            return false;
        } else {
            tip.setVisible(false);
            lastNewName = "";
            return true;
        }
    }

    private boolean isNewAndBlank(Object element, String name) {
        return element == newParameterItem && StringUtils.isBlank(name);
    }

    private class Comparator extends ViewerComparator {

        private final TableColumn column;
        private final int direction;

        public Comparator() {
            this(null, SWT.NONE);
        }

        public Comparator(TableColumn column, int direction) {
            this.column = column;
            this.direction = direction;
        }

        @Override
        public int compare(Viewer viewer, Object e1, Object e2) {
            Parameter a = (Parameter) e1;
            Parameter b = (Parameter) e2;

            if (a == newParameterItem)
                return +1;
            else if (b == newParameterItem)
                return -1;

            if (column == null) {
                return nameToIndex.get(a.getName()) - nameToIndex.get(b.getName());
            } else {
                String strA = column == nameColumn ? a.getName() : a.getValue();
                String strB = column == nameColumn ? b.getName() : b.getValue();
                int r = strA.compareTo(strB);
                return direction == SWT.UP ? r : -r;
            }
        }

        @Override
        public boolean isSorterProperty(Object element, String property) {
            if (column == nameColumn)
                return NAME_PROPERTY.equals(property);
            if (column == valueColumn)
                return VALUE_PROPERTY.equals(property);
            return false;
        }
    }

    private final INamedElementActions actions = new INamedElementActions() {

        @Override
        public void undo() {
            // TODO Auto-generated method stub

        }

        @Override
        public void redo() {
            // TODO Auto-generated method stub

        }

        @Override
        public void paste() {
            pasteAction.run();
        }

        @Override
        public void cut() {
            // TODO Auto-generated method stub

        }

        @Override
        public void copy() {
            copyAction.run();
        }

        @Override
        public boolean canUndo() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean canRedo() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean canPaste() {
            pasteAction.update();
            return pasteAction.isEnabled();
        }

        @Override
        public boolean canCut() {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean canCopy() {
            pasteAction.update();
            return copyAction.isEnabled();
        }
    };

    @Override
    public INamedElementActions createActions() {
        return actions;
    }

}