org.dawnsci.plotting.tools.fitting.PeakFittingTool.java Source code

Java tutorial

Introduction

Here is the source code for org.dawnsci.plotting.tools.fitting.PeakFittingTool.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.plotting.tools.fitting;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.dawb.common.ui.image.IconUtils;
import org.dawb.common.ui.menu.CheckableActionGroup;
import org.dawb.common.ui.menu.MenuAction;
import org.dawb.common.ui.plot.tools.IDataReductionToolPage;
import org.dawb.common.ui.util.EclipseUtils;
import org.dawnsci.plotting.tools.Activator;
import org.dawnsci.plotting.tools.preference.FittingPreferencePage;
import org.eclipse.core.runtime.Status;
import org.eclipse.dawnsci.analysis.api.dataset.IDataset;
import org.eclipse.dawnsci.analysis.dataset.impl.Dataset;
import org.eclipse.dawnsci.analysis.dataset.impl.DatasetFactory;
import org.eclipse.dawnsci.analysis.dataset.impl.DoubleDataset;
import org.eclipse.dawnsci.analysis.dataset.impl.IntegerDataset;
import org.eclipse.dawnsci.analysis.dataset.roi.LinearROI;
import org.eclipse.dawnsci.analysis.dataset.roi.RectangularROI;
import org.eclipse.dawnsci.plotting.api.annotation.IAnnotation;
import org.eclipse.dawnsci.plotting.api.region.IRegion;
import org.eclipse.dawnsci.plotting.api.region.IRegion.RegionType;
import org.eclipse.dawnsci.plotting.api.region.IRegionListener;
import org.eclipse.dawnsci.plotting.api.region.RegionUtils;
import org.eclipse.dawnsci.plotting.api.trace.ILineTrace;
import org.eclipse.dawnsci.plotting.api.views.ISettablePlotView;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
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.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.ac.diamond.scisoft.analysis.fitting.FittingConstants;
import uk.ac.diamond.scisoft.analysis.fitting.Generic1DFitter;
import uk.ac.diamond.scisoft.analysis.fitting.functions.FunctionFactory;
import uk.ac.diamond.scisoft.analysis.fitting.functions.FunctionSquirts;
import uk.ac.diamond.scisoft.analysis.fitting.functions.IPeak;
import uk.ac.diamond.scisoft.analysis.fitting.functions.IdentifiedPeak;

public class PeakFittingTool extends AbstractFittingTool implements IRegionListener, IDataReductionToolPage {

    private static final Logger logger = LoggerFactory.getLogger(PeakFittingTool.class);
    private MenuAction numberPeaks;
    private FittedPeaksInfo fittedPeaksInfo;

    public PeakFittingTool() {
        super();

        Activator.getPlottingPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent event) {
                if (isActive()) {
                    if (isInterestedProperty(event)) {
                        if (isActive())
                            fittingJob.fit(false);

                        if (FittingConstants.PEAK_NUMBER.equals(event.getProperty())) {
                            final int ipeak = Activator.getPlottingPreferenceStore()
                                    .getInt(FittingConstants.PEAK_NUMBER);
                            if (numberPeaks != null) {
                                if (ipeak < 11) {
                                    numberPeaks.setSelectedAction(ipeak - 1);
                                    numberPeaks.setCheckedAction(ipeak - 1, true);
                                } else {
                                    numberPeaks.setSelectedAction(10);
                                    numberPeaks.setCheckedAction(10, true);
                                }
                            }
                        }
                    } else if (FittingConstants.INT_FORMAT.equals(event.getProperty())
                            || FittingConstants.REAL_FORMAT.equals(event.getProperty())) {

                        viewer.refresh();
                    }
                }
            }

            private boolean isInterestedProperty(PropertyChangeEvent event) {
                final String propName = event.getProperty();
                return FittingConstants.PEAK_NUMBER.equals(propName) || FittingConstants.SMOOTHING.equals(propName)
                        || FittingConstants.QUALITY.equals(propName);
            }
        });

    }

    @Override
    protected List<TableViewerColumn> createColumns(final TableViewer viewer) {

        PeakColumnComparitor cc = new PeakColumnComparitor();
        viewer.setComparator(cc);

        ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);

        List<TableViewerColumn> ret = new ArrayList<TableViewerColumn>(9);

        TableViewerColumn var = new TableViewerColumn(viewer, SWT.LEFT, 0);
        var.getColumn().setText("Trace");
        var.getColumn().setWidth(80);
        var.setLabelProvider(new PeakLabelProvider(0));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 0, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.LEFT, 1);
        var.getColumn().setText("Name");
        var.getColumn().setWidth(150);
        var.setLabelProvider(new PeakLabelProvider(1));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 1, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 2);
        var.getColumn().setText("Position");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(2));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 2, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 3);
        var.getColumn().setText("Data");
        var.getColumn().setToolTipText("The nearest data value of the fitted peak.");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(3));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 3, cc));
        ret.add(var);

        // Data Column not that useful, do not show unless property set.
        if (!Boolean.getBoolean("org.dawb.workbench.plotting.tools.fitting.tool.data.column.required")) {
            var.getColumn().setWidth(0);
            var.getColumn().setResizable(false);
        }

        var = new TableViewerColumn(viewer, SWT.CENTER, 4);
        var.getColumn().setText("Fit");
        var.getColumn().setToolTipText("The value of the fitted peak.");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(4));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 4, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 5);
        var.getColumn().setText("FWHM");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(5));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 5, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 6);
        var.getColumn().setText("Area");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(6));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 6, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 7);
        var.getColumn().setText("Type");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(7));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 7, cc));
        ret.add(var);

        var = new TableViewerColumn(viewer, SWT.CENTER, 8);
        var.getColumn().setText("Algorithm");
        var.getColumn().setWidth(100);
        var.setLabelProvider(new PeakLabelProvider(8));
        var.getColumn().addSelectionListener(getSelectionAdapter(var.getColumn(), 8, cc));
        ret.add(var);

        return ret;

    }

    private SelectionAdapter getSelectionAdapter(final TableColumn column, final int index,
            final PeakColumnComparitor comparator) {
        SelectionAdapter selectionAdapter = new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                comparator.setColumn(index);
                int dir = comparator.getDirection();
                viewer.getTable().setSortDirection(dir);
                viewer.getTable().setSortColumn(column);
                viewer.refresh();
            }
        };
        return selectionAdapter;
    }

    /**
     * 
     * @param fittedPeaksInfo
     * @return
     * @throws Exception 
     */
    protected FittedFunctions getFittedFunctions(FittedPeaksInfo fittedPeaksInfo) throws Exception {
        FittedFunctions functions = FittingUtils.getFittedPeaks(fittedPeaksInfo);
        this.fittedPeaksInfo = fittedPeaksInfo;
        return functions;
    }

    @Override
    public Serializable getToolData() {

        final FunctionSquirts fs = (FunctionSquirts) super.getToolData();
        if (fittedPeaksInfo != null) {
            fs.setIdentifiedPeaks(fittedPeaksInfo.getIdentifiedPeaks());
        }
        fs.setFitBounds(getFitBounds());
        return fs;
    }

    /**
     * Thread safe
     * @param peaks
     */
    @Override
    protected synchronized void createFittedFunctionUI(final FittedFunctions newBean) {

        if (newBean == null) {
            fittedFunctions = null;
            logger.error("Cannot find peaks in the given selection.");
            return;
        }
        composite.getDisplay().syncExec(new Runnable() {

            public void run() {
                try {

                    boolean requireFWHMSelections = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_FWHM_SELECTIONS);
                    boolean requirePeakSelections = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_PEAK_SELECTIONS);
                    boolean requireTrace = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_FITTING_TRACE);
                    boolean requireAnnot = Activator.getPlottingPreferenceStore()
                            .getBoolean(FittingConstants.SHOW_ANNOTATION_AT_PEAK);

                    // Draw the regions
                    for (int i = 0; i < newBean.size(); i++) {

                        FittedFunction fp = newBean.getFunctionList().get(i); // TODO proper encapsulation               

                        if (fp.isSaved())
                            continue;
                        if (fp.getFwhm() != null)
                            continue; // Already got some UI

                        RectangularROI rb = fp.getRoi();
                        String areaName = RegionUtils.getUniqueName("Peak Area", getPlottingSystem());
                        String suffix = areaName.replaceAll("Peak Area", "");

                        final IRegion area = getPlottingSystem().createRegion(areaName, RegionType.XAXIS);
                        area.setRegionColor(ColorConstants.orange);
                        area.setROI(rb);
                        area.setMobile(false);
                        area.setUserObject(FittedFunction.class);
                        getPlottingSystem().addRegion(area);
                        fp.setFwhm(area);
                        if (!requireFWHMSelections)
                            area.setVisible(false);

                        final Dataset[] pair = fp.getPeakFunctions();
                        final ILineTrace trace = getPlottingSystem().createLineTrace("Peak" + suffix);
                        //set user trace false before setting data otherwise the trace sent to events will be a true by default
                        trace.setUserTrace(false);
                        trace.setData(pair[0], pair[1]);
                        trace.setLineWidth(1);
                        trace.setTraceColor(ColorConstants.black);
                        getPlottingSystem().addTrace(trace);
                        fp.setTrace(trace);
                        if (!requireTrace)
                            trace.setVisible(false);

                        final IAnnotation ann = getPlottingSystem().createAnnotation("Peak" + suffix);
                        ann.setLocation(fp.getPosition(), fp.getPeakValue());
                        getPlottingSystem().addAnnotation(ann);
                        fp.setAnnotation(ann);
                        if (!requireAnnot)
                            ann.setVisible(false);

                        final IRegion line = getPlottingSystem().createRegion("Peak Line" + suffix,
                                RegionType.XAXIS_LINE);
                        line.setRegionColor(ColorConstants.black);
                        line.setAlpha(150);
                        line.setLineWidth(1);
                        getPlottingSystem().addRegion(line);
                        line.setROI(new LinearROI(rb.getMidPoint(), rb.getMidPoint()));
                        line.setMobile(false);
                        fp.setCenter(line);
                        if (!requirePeakSelections)
                            line.setVisible(false);

                    }

                    PeakFittingTool.this.fittedFunctions = newBean;
                    viewer.setInput(newBean);
                    viewer.refresh();

                    algorithmMessage.setText(getAlgorithmSummary());
                    algorithmMessage.getParent().layout();
                    //updatePlotServerConnection(newBean);

                } catch (Exception ne) {
                    logger.error("Cannot create fitted peaks!", ne);
                }
            }
        });
    }

    protected String getAlgorithmSummary() {
        StringBuilder buf = new StringBuilder("Fit attempted: '");
        buf.append(FittingUtils.getPeaksRequired());
        buf.append("' ");
        buf.append(FittingUtils.getPeakClass().getSimpleName());
        buf.append("'s using ");
        buf.append(FittingUtils.getOptimizer().getClass().getSimpleName());
        buf.append(" with smoothing of '");
        buf.append(FittingUtils.getSmoothing());
        buf.append("' (<a>configure smoothing</a>)");
        return buf.toString();
    }

    private Map<Integer, List<Double>> fit, xpos, fwhm, area;
    private Map<Integer, List<IDataset>> functions;
    private DataReductionSlice lastSlice;

    public DataReductionInfo export(DataReductionSlice slice) throws Exception {

        final RectangularROI roi = getFitBounds();
        if (roi == null)
            return new DataReductionInfo(Status.CANCEL_STATUS, null);

        final double[] p1 = roi.getPointRef();
        final double[] p2 = roi.getEndPoint();

        Dataset x = slice.getAxes() != null && !slice.getAxes().isEmpty() ? (Dataset) slice.getAxes().get(0)
                : IntegerDataset.createRange(slice.getData().getSize());

        Dataset[] a = Generic1DFitter.xintersection(x, (Dataset) slice.getData(), p1[0], p2[0]);
        x = a[0];
        Dataset y = a[1];

        // If the IdentifiedPeaks are null, we make them.
        @SuppressWarnings("unchecked")
        List<IdentifiedPeak> identifiedPeaks = (List<IdentifiedPeak>) slice.getUserData();
        if (slice.getUserData() == null) {
            identifiedPeaks = Generic1DFitter.parseDataDerivative(x, y, FittingUtils.getSmoothing());
        }

        if (fit == null)
            fit = new LinkedHashMap<Integer, List<Double>>(7);
        if (xpos == null)
            xpos = new LinkedHashMap<Integer, List<Double>>(7);
        if (fwhm == null)
            fwhm = new LinkedHashMap<Integer, List<Double>>(7);
        if (area == null)
            area = new LinkedHashMap<Integer, List<Double>>(7);
        if (functions == null)
            functions = new LinkedHashMap<Integer, List<IDataset>>(7);
        lastSlice = slice;

        try {
            final FittedPeaksInfo info = new FittedPeaksInfo(x, y, slice.getMonitor());
            info.setIdentifiedPeaks(identifiedPeaks);

            FittedFunctions bean = FittingUtils.getFittedPeaks(info);

            int index = 1;
            for (FittedFunction fp : bean.getFunctionList()) {

                final String peakName = "Peak" + index;
                DoubleDataset sdd = new DoubleDataset(new double[] { fp.getPeakValue() }, new int[] { 1 });
                addValue(fit, index, fp.getPeakValue());
                sdd.setName(peakName + "_fit");
                slice.appendData(sdd);

                sdd = new DoubleDataset(new double[] { fp.getPosition() }, new int[] { 1 });
                addValue(xpos, index, fp.getPosition());
                sdd.setName(peakName + "_xposition");
                slice.appendData(sdd);

                sdd = new DoubleDataset(new double[] { fp.getFWHM() }, new int[] { 1 });
                addValue(fwhm, index, fp.getFWHM());
                sdd.setName(peakName + "_fwhm");
                slice.appendData(sdd);

                sdd = new DoubleDataset(new double[] { fp.getArea() }, new int[] { 1 });
                addValue(area, index, fp.getArea());
                sdd.setName(peakName + "_area");
                slice.appendData(sdd);

                final Dataset[] pair = fp.getPeakFunctions();
                Dataset function = pair[1];
                Dataset fc = function.clone();
                fc.setName(peakName + "_function");
                slice.appendData(fc);
                addList(functions, index, fc);

                ++index;
            }

        } catch (Exception ne) {
            logger.error("Cannot fit peaks!", ne);
            return new DataReductionInfo(Status.CANCEL_STATUS, null);
        }

        DataReductionInfo status = new DataReductionInfo(Status.OK_STATUS, identifiedPeaks);
        return status;
    }

    private void addValue(Map<Integer, List<Double>> col, int index, double value) {

        List<Double> values = col.get(index);
        if (values == null) {
            values = new ArrayList<Double>(31);
            col.put(index, values);
        }
        values.add(value);
    }

    private void addList(Map<Integer, List<IDataset>> lists, int index, IDataset item) {
        List<IDataset> list = lists.get(index);
        if (list == null) {
            list = new ArrayList<IDataset>(31);
            lists.put(index, list);
        }
        list.add(item);
    }

    /**
     * The user may enter a regular expression for their dataset name. In this case 
     * all datasets matching it will be fitted for peaks. If they do this, we attempt to 
     * detect that they selected more than one dataset and write a single list of the peaks
     * and other scalars. This is used for EDXD to process individual separate elements in the
     * results file which should be a single block, but isnt. Example /dls/i12/data/2014/cm4963-2/rawdata/36153.nxs
     */
    public void exportFinished() throws Exception {

        if (fit == null || xpos == null || fwhm == null || area == null || functions == null || lastSlice == null)
            return;

        if (lastSlice.getExpandedDatasetNames() == null || lastSlice.getExpandedDatasetNames().size() < 2)
            return;

        final String nodeName = getTitle().replace(' ', '_');

        String entry = (String) lastSlice.getFile().getRoot();
        String container = (String) lastSlice.getFile().group(nodeName, entry);
        for (int i = 1; i <= fit.size(); i++) {

            final String peakName = "Peak" + i;

            final IDataset fits = DatasetFactory.createFromList(fit.get(i));
            fits.setName(peakName + "_fit_byElement");
            lastSlice.appendData(fits, container);

            final IDataset xposi = DatasetFactory.createFromList(xpos.get(i));
            xposi.setName(peakName + "_xposition_byElement");
            lastSlice.appendData(xposi, container);

            final IDataset fwhms = DatasetFactory.createFromList(fwhm.get(i));
            fwhms.setName(peakName + "_fwhm_byElement");
            lastSlice.appendData(fwhms, container);

            final IDataset areas = DatasetFactory.createFromList(area.get(i));
            areas.setName(peakName + "_area_byElement");
            lastSlice.appendData(areas, container);

            final List<IDataset> fs = functions.get(i);
            for (IDataset iDataset : fs) {
                Dataset ds = (Dataset) iDataset.squeeze();
                ds.setName(peakName + "_functions");

                // Append directly into file.
                lastSlice.getFile().appendDataset(ds.getName(), ds, container);
            }

        }

        fit.clear();
        xpos.clear();
        fwhm.clear();
        area.clear();
        functions.clear();
        lastSlice = null;

    }

    /**
     * We use the old actions here for simplicity of configuration.
     * 
     * TODO consider moving to commands.
     */
    protected void createActions() {

        final Action createNewSelection = new Action("New fit selection.", IAction.AS_PUSH_BUTTON) {
            public void run() {
                createNewFit();
            }
        };
        createNewSelection.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit.png"));
        getSite().getActionBars().getToolBarManager().add(createNewSelection);

        final Action addMode = new Action("Add peaks to those already found", IAction.AS_CHECK_BOX) {
            public void run() {
                setAddingPeaks(isChecked());
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.ADD_PEAK_MODE, isChecked());
                if (!isChecked()) {
                    if (fitRegion != null) {
                        getPlottingSystem().removeRegion(fitRegion);
                        fitRegion = null;
                    }
                }
            }
        };
        addMode.setImageDescriptor(Activator.getImageDescriptor("icons/add.png"));
        getSite().getActionBars().getToolBarManager().add(addMode);
        addMode.setChecked(Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.ADD_PEAK_MODE));
        setAddingPeaks(addMode.isChecked());

        getSite().getActionBars().getToolBarManager().add(new Separator());

        final Action showAnns = new Action("Show annotations at the peak position.", IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_ANNOTATION_AT_PEAK,
                        isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setAnnotationsVisible(isChecked);
            }
        };
        showAnns.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showAnnotation.png"));
        getSite().getActionBars().getToolBarManager().add(showAnns);

        showAnns.setChecked(
                Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_ANNOTATION_AT_PEAK));

        final Action showTrace = new Action("Show fitting traces.", IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_FITTING_TRACE, isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setTracesVisible(isChecked);
            }
        };
        showTrace.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showFittingTrace.png"));
        getSite().getActionBars().getToolBarManager().add(showTrace);

        showTrace
                .setChecked(Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_FITTING_TRACE));

        final Action showPeak = new Action("Show peak lines.", IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_PEAK_SELECTIONS, isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setPeaksVisible(isChecked);
            }
        };
        showPeak.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showPeakLine.png"));
        getSite().getActionBars().getToolBarManager().add(showPeak);

        showPeak.setChecked(
                Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_PEAK_SELECTIONS));

        final Action showFWHM = new Action("Show selection regions for full width, half max.",
                IAction.AS_CHECK_BOX) {
            public void run() {
                final boolean isChecked = isChecked();
                Activator.getPlottingPreferenceStore().setValue(FittingConstants.SHOW_FWHM_SELECTIONS, isChecked);
                if (fittedFunctions != null)
                    fittedFunctions.setAreasVisible(isChecked);
            }
        };
        showFWHM.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-showFWHM.png"));
        getSite().getActionBars().getToolBarManager().add(showFWHM);

        showFWHM.setChecked(
                Activator.getPlottingPreferenceStore().getBoolean(FittingConstants.SHOW_FWHM_SELECTIONS));

        final Separator sep = new Separator(getClass().getName() + ".separator1");
        getSite().getActionBars().getToolBarManager().add(sep);

        final Action savePeak = new Action("Store peak.", IAction.AS_PUSH_BUTTON) {
            public void run() {
                try {
                    fittedFunctions.saveSelectedPeak(getPlottingSystem());
                } catch (Exception e) {
                    logger.error("Cannot rename stored peak ", e);
                }
                viewer.refresh();
            }
        };
        savePeak.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-savePeak.png"));
        getSite().getActionBars().getToolBarManager().add(savePeak);

        final Separator sep3 = new Separator(getClass().getName() + ".separator3");
        getSite().getActionBars().getToolBarManager().add(sep3);

        final MenuAction peakType = new MenuAction("Peak type to fit");
        peakType.setToolTipText("Peak type to fit");
        peakType.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-peak-fit-peak-type.png"));

        CheckableActionGroup group = new CheckableActionGroup();

        Action selectedPeakAction = null;
        final Map<String, Class<? extends IPeak>> peakFunctions = FunctionFactory.getPeakFns();
        for (final String peakName : peakFunctions.keySet()) {
            final Action action = new Action(peakName, IAction.AS_CHECK_BOX) {
                public void run() {
                    Activator.getPlottingPreferenceStore().setValue(FittingConstants.PEAK_TYPE, peakName);
                    setChecked(true);
                    if (fittingJob != null && isActive())
                        fittingJob.fit(false);
                    peakType.setSelectedAction(this);
                }
            };
            peakType.add(action);
            group.add(action);
            if (peakName.equals(Activator.getPlottingPreferenceStore().getString(FittingConstants.PEAK_TYPE))) {
                selectedPeakAction = action;
            }
        }

        if (selectedPeakAction != null) {
            peakType.setSelectedAction(selectedPeakAction);
            selectedPeakAction.setChecked(true);
        }
        getSite().getActionBars().getToolBarManager().add(peakType);
        getSite().getActionBars().getMenuManager().add(peakType);

        final Separator sep2 = new Separator(getClass().getName() + ".separator2");
        getSite().getActionBars().getToolBarManager().add(sep2);

        this.tracesMenu = new MenuAction("Traces");
        tracesMenu.setToolTipText("Choose trace for fit.");
        tracesMenu.setImageDescriptor(Activator.getImageDescriptor("icons/plot-tool-trace-choice.png"));

        getSite().getActionBars().getToolBarManager().add(tracesMenu);
        getSite().getActionBars().getMenuManager().add(tracesMenu);

        this.numberPeaks = new MenuAction("Number peaks to fit");
        numberPeaks.setToolTipText("Number peaks to fit");

        group = new CheckableActionGroup();

        final int npeak = Activator.getPlottingPreferenceStore()
                .getDefaultInt(FittingConstants.PEAK_NUMBER_CHOICES);
        for (int ipeak = 1; ipeak <= npeak; ipeak++) {

            final int peak = ipeak;
            final Action action = new Action("Fit " + String.valueOf(ipeak) + " Peaks", IAction.AS_CHECK_BOX) {
                public void run() {
                    Activator.getPlottingPreferenceStore().setValue(FittingConstants.PEAK_NUMBER, peak);
                }
            };

            action.setImageDescriptor(IconUtils.createIconDescriptor(String.valueOf(ipeak)));
            numberPeaks.add(action);
            group.add(action);
            action.setChecked(false);
            action.setToolTipText("Fit " + ipeak + " peak(s)");

        }
        final Action preferences = new Action("Preferences...") {
            public void run() {
                if (!isActive())
                    return;
                PreferenceDialog pref = PreferencesUtil.createPreferenceDialogOn(
                        PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), FittingPreferencePage.ID,
                        null, null);
                if (pref != null)
                    pref.open();
            }
        };

        final Action npeaks = new Action("Fit n Peaks", IAction.AS_CHECK_BOX) {
            public void run() {
                preferences.run();
            }
        };

        npeaks.setImageDescriptor(IconUtils.createIconDescriptor("n"));
        numberPeaks.add(npeaks);
        group.add(npeaks);
        npeaks.setChecked(false);
        npeaks.setToolTipText("Fit n peaks");

        final int ipeak = Activator.getPlottingPreferenceStore().getInt(FittingConstants.PEAK_NUMBER);
        if (ipeak < 11) {
            numberPeaks.setSelectedAction(ipeak - 1);
            numberPeaks.setCheckedAction(ipeak - 1, true);
        } else {
            numberPeaks.setSelectedAction(npeaks);
            npeaks.setChecked(true);
        }

        getSite().getActionBars().getToolBarManager().add(numberPeaks);
        //getSite().getActionBars().getMenuManager().add(numberPeaks);

        final Action clear = new Action("Clear all",
                Activator.getImageDescriptor("icons/plot-tool-peak-fit-clear.png")) {
            public void run() {
                clearAll();
            }
        };
        clear.setToolTipText("Clear all regions found in the fitting");

        getSite().getActionBars().getToolBarManager().add(clear);
        getSite().getActionBars().getMenuManager().add(clear);

        final Action delete = new Action("Delete peak selected", Activator.getImageDescriptor("icons/delete.gif")) {
            public void run() {
                if (!isActive())
                    return;
                if (fittedFunctions != null)
                    fittedFunctions.deleteSelectedFunction(getPlottingSystem());
                pushFunctionsToPlotter();
                viewer.refresh();
                createNewFit();
            }
        };
        delete.setToolTipText("Delete peak selected, if any");

        getSite().getActionBars().getToolBarManager().add(delete);

        getSite().getActionBars().getMenuManager().add(preferences);

        final Action export = new Action("Export...", IAction.AS_PUSH_BUTTON) {
            public void run() {
                try {
                    EclipseUtils.openWizard(FittedPeaksExportWizard.ID, true);
                } catch (Exception e) {
                    logger.error("Cannot open wizard " + FittedPeaksExportWizard.ID, e);
                }
            }
        };

        final MenuManager menuManager = new MenuManager();
        menuManager.add(clear);
        menuManager.add(delete);
        menuManager.add(savePeak);
        menuManager.add(new Separator());
        menuManager.add(showAnns);
        menuManager.add(showTrace);
        menuManager.add(showPeak);
        menuManager.add(showFWHM);
        menuManager.add(new Separator());
        menuManager.add(export);

        viewer.getControl().setMenu(menuManager.createContextMenu(viewer.getControl()));

    }

    @Override
    public Object getAdapter(@SuppressWarnings("rawtypes") Class key) {
        if (key == IPeak.class) {
            return fittedFunctions != null && !fittedFunctions.isEmpty() ? fittedFunctions.getPeakFunctions()
                    : null;
        }
        return super.getAdapter(key);
    }

    public String exportFittedData(final String path) throws Exception {
        return exportFittedPeaks(path);
    }

    /**
     * Will export to file and overwrite.
     * Will append ".csv" if it is not already there.
     * 
     * @param path
     */
    String exportFittedPeaks(final String path) throws Exception {

        File file = new File(path);
        if (!file.getName().toLowerCase().endsWith(".dat"))
            file = new File(path + ".dat");
        if (file.exists())
            file.delete();

        final BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
        try {
            writer.write(FittedFunction.getCVSTitle());
            writer.newLine();
            for (FittedFunction peak : getSortedFunctionList()) {
                writer.write(peak.getTabString());
                writer.newLine();
            }

        } finally {
            writer.close();
        }

        return file.getAbsolutePath();
    }

    @Override
    void pushFunctionsToPlotter() {
        if (getPart() instanceof ISettablePlotView) {
            ISettablePlotView plotView = (ISettablePlotView) getPart();
            ArrayList<IPeak> peaks = new ArrayList<IPeak>();
            if (fittedFunctions != null) {
                for (FittedFunction func : fittedFunctions.getFunctionList()) {
                    IPeak peak = func.getPeak();
                    peaks.add(peak);
                }
            }
            plotView.updateData(peaks, IPeak.class);
        }
    }

}