org.dawnsci.common.widgets.gda.function.FunctionTreeViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.common.widgets.gda.function.FunctionTreeViewer.java

Source

/*
 * Copyright (c) 2012 Diamond Light Source Ltd.
 *
 * 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
 */
package org.dawnsci.common.widgets.gda.function;

import java.text.DecimalFormat;

import org.dawnsci.common.widgets.gda.function.descriptors.IFunctionDescriptorProvider;
import org.dawnsci.common.widgets.gda.function.internal.FunctionLabelProvider;
import org.dawnsci.common.widgets.gda.function.internal.FunctionSelectionEditingSupport;
import org.dawnsci.common.widgets.gda.function.internal.FunctionTreeViewerContentProvider;
import org.dawnsci.common.widgets.gda.function.internal.IGetSetValueOnParameterModel;
import org.dawnsci.common.widgets.gda.function.internal.ITextEditingSupport;
import org.dawnsci.common.widgets.gda.function.internal.ValueColumnEditingSupport;
import org.dawnsci.common.widgets.gda.function.internal.model.FunctionModel;
import org.dawnsci.common.widgets.gda.function.internal.model.FunctionModelElement;
import org.dawnsci.common.widgets.gda.function.internal.model.FunctionModelRoot;
import org.dawnsci.common.widgets.gda.function.internal.model.OperatorModel;
import org.dawnsci.common.widgets.gda.function.internal.model.ParameterModel;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.dawnsci.analysis.api.fitting.functions.IFunction;
import org.eclipse.dawnsci.analysis.api.fitting.functions.IOperator;
import org.eclipse.dawnsci.analysis.api.fitting.functions.IParameter;
import org.eclipse.jface.bindings.keys.IKeyLookup;
import org.eclipse.jface.bindings.keys.KeyLookupFactory;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.TreeViewerEditor;
import org.eclipse.jface.viewers.TreeViewerFocusCellManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;

import uk.ac.diamond.scisoft.analysis.fitting.functions.Add;

public class FunctionTreeViewer implements IFunctionViewer {
    protected static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("##0.0####");;

    public enum COLUMN {
        FUNCTION(0), VALUE(1), LOWERLIMIT(2), UPPERLIMIT(3), FITTEDVALUE(4);
        public final int COLUMN_INDEX;

        COLUMN(int column) {
            this.COLUMN_INDEX = column;
        }
    }

    final private TreeViewer treeViewer;
    private FunctionModelRoot modelRoot;
    private ValueColumnEditingSupport[] valueColumnEditingSupport = new ValueColumnEditingSupport[COLUMN
            .values().length];
    private FunctionSelectionEditingSupport functionSelectionEditingSupport;
    private IFunctionDescriptorProvider functionDescriptorProvider;
    private ListenerList selectionChangedListeners;

    public FunctionTreeViewer(final Composite parent, IFunctionDescriptorProvider functionDescriptorProvider) {
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new FillLayout());
        treeViewer = new TreeViewer(composite, SWT.BORDER | SWT.FULL_SELECTION);

        treeViewer.getTree().setLinesVisible(true);
        treeViewer.getTree().setHeaderVisible(true);
        TreeViewerFocusCellManager focusCellManager = new TreeViewerFocusCellManager(treeViewer,
                new FocusCellOwnerDrawHighlighter(treeViewer));
        ColumnViewerEditorActivationStrategy actSupport = new ColumnViewerEditorActivationStrategy(treeViewer) {
            @Override
            protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
                // TODO see AbstractComboBoxCellEditor for how list is made visible
                return super.isEditorActivationEvent(event)
                        || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED
                                && (event.keyCode == KeyLookupFactory.getDefault()
                                        .formalKeyLookup(IKeyLookup.ENTER_NAME)));
            }
        };

        TreeViewerEditor.create(treeViewer, focusCellManager, actSupport,
                ColumnViewerEditor.TABBING_HORIZONTAL | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
                        | ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);

        createFunctionNameColumn();
        createParameterValueColumn("Value", COLUMN.VALUE, false, new IGetSetValueOnParameterModel() {
            @Override
            public void setValue(ParameterModel param, String value) {
                param.setParameterValue(value);
            }

            @Override
            public double getValue(ParameterModel param) {
                return param.getParameterValue();
            }

            @Override
            public String getErrorValue(ParameterModel param) {
                return param.getParameterValueError();
            }
        });
        createParameterValueColumn("Lower Limit", COLUMN.LOWERLIMIT, true, new IGetSetValueOnParameterModel() {
            @Override
            public void setValue(ParameterModel param, String value) {
                param.setParameterLowerLimit(value);
            }

            @Override
            public double getValue(ParameterModel param) {
                return param.getParameterLowerLimit();
            }

            @Override
            public String getErrorValue(ParameterModel param) {
                return param.getParameterLowerLimitError();
            }
        });
        createParameterValueColumn("Upper Limit", COLUMN.UPPERLIMIT, true, new IGetSetValueOnParameterModel() {
            @Override
            public void setValue(ParameterModel param, String value) {
                param.setParameterUpperLimit(value);
            }

            @Override
            public double getValue(ParameterModel param) {
                return param.getParameterUpperLimit();
            }

            @Override
            public String getErrorValue(ParameterModel param) {
                return param.getParameterUpperLimitError();
            }
        });

        createFittedParamsColumn();

        treeViewer.setContentProvider(new FunctionTreeViewerContentProvider());
        treeViewer.expandToLevel(2);
        // IFunctions are not safely hashable for this use (despite their having
        // a custom hashCode)
        treeViewer.setUseHashlookup(false);
        setInput(null);
        this.functionDescriptorProvider = functionDescriptorProvider;
        functionSelectionEditingSupport.setFunctionDesciptorProvider(functionDescriptorProvider);

        treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                if (event.getSelection() instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                    ISelection realSelection = convertSelection(selection);
                    fireSelectionChanged(new SelectionChangedEvent(FunctionTreeViewer.this, realSelection));
                }

            }
        });
    }

    private void createFittedParamsColumn() {
        TreeViewerColumn fittedParametersColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        fittedParametersColumn.getColumn().setWidth(200);
        fittedParametersColumn.getColumn().setMoveable(true);
        fittedParametersColumn.getColumn().setText("Fitted Parameters");
        fittedParametersColumn.setLabelProvider(new ColumnLabelProvider() {

            @Override
            public String getText(Object element) {
                if (element instanceof ParameterModel) {
                    ParameterModel parameterModel = (ParameterModel) element;
                    IParameter fittedParameter = parameterModel.getFittedParameter();
                    if (fittedParameter != null) {
                        return DOUBLE_FORMAT.format(fittedParameter.getValue());
                    } else {
                        return "Not defined";
                    }

                } else {
                    return "";
                }
            }

        });
    }

    private static final class ParameterValueColumnLabelProvider extends BaseLabelProvider
            implements IStyledLabelProvider {
        private static StyledString FIXED = new StyledString("(Fixed)", StyledString.QUALIFIER_STYLER);
        private boolean hideValueOnFixed;
        private IGetSetValueOnParameterModel getSetParameterModel;

        public ParameterValueColumnLabelProvider(boolean hideValueOnFixed,
                IGetSetValueOnParameterModel getSetParameterModel) {
            this.hideValueOnFixed = hideValueOnFixed;
            this.getSetParameterModel = getSetParameterModel;
        }

        @Override
        public StyledString getStyledText(Object element) {
            if (element instanceof ParameterModel) {
                ParameterModel param = (ParameterModel) element;
                StyledString styledString;
                String errorValue = getSetParameterModel.getErrorValue(param);
                if (hideValueOnFixed && param.isParameterFixed()) {
                    styledString = FIXED;
                } else if (errorValue != null) {
                    styledString = new StyledString(errorValue, FunctionLabelProvider.ERROR_STYLER);
                } else {
                    double value = getSetParameterModel.getValue(param);
                    if (value == Double.MAX_VALUE)
                        styledString = new StyledString("Max Double", StyledString.QUALIFIER_STYLER);
                    else if (value == -Double.MAX_VALUE)
                        styledString = new StyledString("Min Double", StyledString.QUALIFIER_STYLER);
                    else {
                        styledString = new StyledString(DOUBLE_FORMAT.format(value));
                    }
                    if (param.getParameter().isFixed()) {
                        styledString.append("   ");
                        styledString.append(FIXED);
                    }
                }
                return styledString;
            } else {
                return new StyledString();
            }
        }

        @Override
        public Image getImage(Object element) {
            return null;
        }
    }

    private void createParameterValueColumn(String columnTitle, COLUMN column, boolean hideValueOnFixed,
            final IGetSetValueOnParameterModel getSetParameterModel) {
        TreeViewerColumn valueColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        valueColumn.getColumn().setWidth(100);
        valueColumn.getColumn().setMoveable(true);
        valueColumn.getColumn().setText(columnTitle);
        valueColumn.setLabelProvider(new DelegatingStyledCellLabelProvider(
                new ParameterValueColumnLabelProvider(hideValueOnFixed, getSetParameterModel)));
        valueColumnEditingSupport[column.COLUMN_INDEX] = new ValueColumnEditingSupport(this) {

            @Override
            public double getValue(ParameterModel param) {
                return getSetParameterModel.getValue(param);
            }

            @Override
            public void setValue(ParameterModel param, String value) {
                getSetParameterModel.setValue(param, value);
            }

            @Override
            public String getErrorValue(ParameterModel param) {
                return getSetParameterModel.getErrorValue(param);
            }

        };
        valueColumn.setEditingSupport(valueColumnEditingSupport[column.COLUMN_INDEX]);
    }

    private void createFunctionNameColumn() {
        TreeViewerColumn functionNameColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        functionNameColumn.getColumn().setWidth(250);
        functionNameColumn.getColumn().setMoveable(true);
        functionNameColumn.getColumn().setText("Function Name");
        functionNameColumn.setLabelProvider(new DelegatingStyledCellLabelProvider(new FunctionLabelProvider()));

        functionSelectionEditingSupport = new FunctionSelectionEditingSupport(this);
        functionNameColumn.setEditingSupport(functionSelectionEditingSupport);
    }

    /**
     * Returns this viewer's tree control
     *
     * @return Tree
     */
    public Tree getTreeControl() {
        return treeViewer.getTree();
    }

    /**
     * Return the Tree Viewer control
     */
    public TreeViewer getTreeViewer() {
        return treeViewer;
    }

    @Override
    public void setInput(Add compositeFunction) {
        if (compositeFunction == null) {
            modelRoot = null;
        } else {
            if (modelRoot == null) {
                modelRoot = new FunctionModelRoot(compositeFunction, functionDescriptorProvider);
            } else {
                modelRoot.setRoot(compositeFunction);
            }
        }
        treeViewer.setInput(modelRoot);
        treeViewer.refresh();
    }

    @Override
    public void setFittedInput(Add fittedCompositeFunction) {
        if (modelRoot == null) {
            setInput(fittedCompositeFunction);
        }

        modelRoot.setFittedRoot(fittedCompositeFunction);
        treeViewer.refresh();
    }

    /**
     * Refreshes this viewer completely
     */
    @Override
    public void refresh() {
        treeViewer.refresh();
    }

    public void refresh(Object element) {
        if (element == null) {
            refresh();
        }

        FunctionModelElement[] modelElements = null;
        if (element instanceof IFunction) {
            modelElements = modelRoot.getModelElement((IFunction) element);
        } else if (element instanceof IParameter) {
            modelElements = modelRoot.getParameterModel((IParameter) element, true);
        }
        if (modelElements == null || modelElements.length == 0) {
            refresh();
        } else {
            for (FunctionModelElement functionModelElement : modelElements) {
                treeViewer.refresh(functionModelElement, true);
            }
        }
    }

    public FunctionModelElement getSelectedFunctionModel() {
        ISelection selection = treeViewer.getSelection();
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structuredSelection = (IStructuredSelection) selection;
            Object selectedElement = structuredSelection.getFirstElement();
            if (selectedElement instanceof FunctionModelElement) {
                FunctionModelElement modelElement = (FunctionModelElement) selectedElement;
                return modelElement;
            }
        }
        return null;
    }

    @Override
    public IFunction getSelectedFunction() {
        FunctionModelElement model = getSelectedFunctionModel();
        if (model == null)
            return null;
        return model.getFunction();
    }

    @Override
    public IOperator getSelectedFunctionParent() {
        FunctionModelElement model = getSelectedFunctionModel();
        if (model == null)
            return null;
        return model.getParentOperator();
    }

    @Override
    public int getSelectedFunctionParentIndex() {
        FunctionModelElement model = getSelectedFunctionModel();
        if (model == null)
            return 0;
        return model.getFunctionIndexInParentOperator();
    }

    private ParameterModel getSelectedParameterModel() {
        ISelection selection = treeViewer.getSelection();
        if (selection instanceof ITreeSelection) {
            ITreeSelection treeSelection = (ITreeSelection) selection;
            TreePath[] paths = treeSelection.getPaths();
            // we only support single selection
            if (paths.length == 1) {
                TreePath path = paths[0];
                Object lastSegment = path.getLastSegment();
                if (lastSegment instanceof ParameterModel) {
                    return (ParameterModel) lastSegment;
                }
            }
        }
        return null;
    }

    @Override
    public IParameter getSelectedParameter() {
        ParameterModel parameterModel = getSelectedParameterModel();
        if (parameterModel != null) {
            return parameterModel.getParameter();
        }
        return null;
    }

    @Override
    public int getSelectedParameterIndex() {
        ParameterModel parameterModel = getSelectedParameterModel();
        if (parameterModel != null) {
            return parameterModel.getParameterIndex();
        }
        return 0;
    }

    @Override
    public void addSelectionChangedListener(ISelectionChangedListener listener) {
        if (selectionChangedListeners == null) {
            selectionChangedListeners = new ListenerList();
        }
        selectionChangedListeners.add(listener);
    }

    @Override
    public void removeSelectionChangedListener(ISelectionChangedListener listener) {
        if (selectionChangedListeners == null) {
            return;
        }
        selectionChangedListeners.remove(listener);
    }

    protected void fireSelectionChanged(SelectionChangedEvent event) {
        if (selectionChangedListeners == null) {
            return;
        }
        for (Object listener : selectionChangedListeners.getListeners()) {
            ((ISelectionChangedListener) listener).selectionChanged(event);
        }
    }

    public Composite getControl() {
        return getTreeControl();
    }

    @Override
    public void expandAll() {
        treeViewer.expandAll();
    }

    /**
     * If function is an identity match to a function in the view, reveal and
     * fully expand it, otherwise expand all instances equal to function in the
     * tree
     *
     * @param function
     */
    public void expandFunction(IFunction function) {
        FunctionModelElement[] elementModel = modelRoot.getModelElement(function);
        for (FunctionModelElement element : elementModel) {
            if (element.getFunction() == function) {
                treeViewer.expandToLevel(element, AbstractTreeViewer.ALL_LEVELS);
                return;
            }
        }
        // no identity match, expand all equals functions
        for (FunctionModelElement element : elementModel) {
            treeViewer.expandToLevel(element, AbstractTreeViewer.ALL_LEVELS);
        }
    }

    /** Exposed for testing only */
    public ITextEditingSupport getColumnEditingSupport(COLUMN column) {
        if (column == COLUMN.FUNCTION)
            return functionSelectionEditingSupport;
        else
            return valueColumnEditingSupport[column.COLUMN_INDEX];
    }

    /** Exposed for testing only */
    public FunctionModelRoot getModelRoot() {
        return modelRoot;
    }

    @Override
    public void addModelModifiedListener(IModelModifiedListener modelModifiedListener) {
        modelRoot.addModelModifiedListener(modelModifiedListener);
    }

    @Override
    public void removeModelModifiedListener(IModelModifiedListener modelModifiedListener) {
        modelRoot.removeModelModifiedListener(modelModifiedListener);
    }

    @Override
    public ISelection getSelection() {
        return convertSelection(treeViewer.getSelection());
    }

    @Override
    public void setSelection(ISelection selection) {
        throw new UnsupportedOperationException("TODO"); // TODO
    }

    /**
     * Convert a selection from internal model types to external equivalents
     *
     * @param selection
     *            of FunctionModel/ParameterModel/etc...
     * @return non-null selection of IFunction/IParameter
     */
    private ISelection convertSelection(ISelection selection) {
        Object realSelection = null;
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structuredSelection = (IStructuredSelection) selection;
            Object element = structuredSelection.getFirstElement();
            if (element instanceof FunctionModel) {
                FunctionModel functionModel = (FunctionModel) element;
                realSelection = functionModel.getFunction();
            } else if (element instanceof OperatorModel) {
                OperatorModel operatorModel = (OperatorModel) element;
                realSelection = operatorModel.getFunction();
            } else if (element instanceof ParameterModel) {
                ParameterModel parameterModel = (ParameterModel) element;
                realSelection = parameterModel.getParameter();
            }
        }
        if (realSelection != null) {
            return new StructuredSelection(realSelection);
        } else {
            return StructuredSelection.EMPTY;
        }
    }

    @Override
    public boolean isValid() {
        return modelRoot.isValid();
    }

    @Override
    public boolean isFittedValid() {
        return modelRoot.isFittedValid();
    }

}