org.fhcrc.cpl.viewer.gui.SpectrumComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.fhcrc.cpl.viewer.gui.SpectrumComponent.java

Source

/*
 * Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.fhcrc.cpl.viewer.gui;

import modwt.Filter;
import modwt.Transform;
import org.apache.log4j.Logger;
import org.fhcrc.cpl.viewer.util.Haar;
import org.fhcrc.cpl.viewer.util.SharedProperties;
import org.fhcrc.cpl.toolbox.proteomics.feature.Spectrum;
import org.fhcrc.cpl.toolbox.proteomics.MSRun;
import org.fhcrc.cpl.viewer.feature.Smooth2D;
import org.fhcrc.cpl.viewer.feature.FeatureStrategyCentroided;
import org.fhcrc.cpl.viewer.feature.extraction.SmootherCreator;
import org.fhcrc.cpl.viewer.Localizer;
import org.fhcrc.cpl.viewer.Application;
import org.fhcrc.cpl.toolbox.*;
import org.fhcrc.cpl.toolbox.datastructure.FloatArray;
import org.fhcrc.cpl.toolbox.datastructure.FloatRange;
import org.fhcrc.cpl.toolbox.datastructure.Pair;
import org.fhcrc.cpl.toolbox.gui.ListenerHelper;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.Range;
import org.jfree.data.xy.XYSeriesCollection;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.LinkedList;

public class SpectrumComponent {
    static Logger _log = Logger.getLogger(SpectrumComponent.class);

    public JPanel contentPanel;
    public JTextField timeTextField;
    public JPanel chartArea;
    public JComboBox displayMode;
    public JComboBox spectrumElutionComboBox;
    public JButton copyButton;
    public JSpinner scanSpinner;
    public JSpinner mzSpinner;

    int _selectedScanNum;
    double _selectedMZ;

    JFreeChart _chart = null;
    ChartPanel _chartPanel = null;
    ListenerHelper helper = new ListenerHelper(this);

    float[][] _spectrum = null;

    String _type = "scan";

    //define the regular background color, so we can switch back after the initial white
    //in the chart panel
    protected static final Color _backgroundColor = new java.awt.Color(238, 238, 238);

    //dhmay adding to record vertical axis range of spectrum chart, for locking the Y axis
    protected Range _lockedYAxisRange = null;
    protected Range _previousYAxisRange = null;

    public SpectrumComponent() {
        try {
            Localizer.getSwingEngine(this).render("org/fhcrc/cpl/viewer/gui/SpectrumComponentForm.xml");
            Localizer.localizeComponentTree(contentPanel);
            contentPanel.doLayout();
        } catch (Exception x) {
            ApplicationContext.errorMessage(TextProvider.getText("ERROR_CREATING_DIALOG"), x);
            throw new RuntimeException(x);
        }

        if (null != displayMode) {
            displayMode.addItem("raw");
            displayMode.addItem("background centroided");
            displayMode.addItem("clean centroided");
            displayMode.addItem("resampled");
            displayMode.addItem("subtract background");
            displayMode.addItem("smoothed");
            displayMode.addItem("peaks");
            displayMode.addItem("wavelet peaks");
            displayMode.addItem("wavelet decomposition");
            displayMode.addItem("wavelet decomposition 2");
            displayMode.addItem("wavelet multiresolution");
            displayMode.addItem("haar8");
            displayMode.addItem("haar16");
            helper.addListener(displayMode, "display_actionPerformed");
        }

        if (null != copyButton) {
            helper.addListener(copyButton, "copy_actionPerformed");
        }

        if (null != scanSpinner) {
            scanSpinner.setModel(new SpinnerRunModel());
            helper.addListener(scanSpinner, "scanSpinner_stateChanged");
        }

        if (null != mzSpinner) {
            mzSpinner.setModel(new SpinnerNumberModel(0.0, 0.0, 1000000.0, 1.0 / 36));
            helper.addListener(mzSpinner, "mzSpinner_stateChanged");
        }

        //dhmay changing from radio group to combo box, 12/19/2005
        if (null != spectrumElutionComboBox) {
            spectrumElutionComboBox.addItem("spectrum");
            spectrumElutionComboBox.addItem("elution");
            helper.addListener(spectrumElutionComboBox, "spectrumElutionComboBox_actionPerformed");
        }

        helper.addListener(contentPanel, "_componentResized");
        helper.addListener(Application.getInstance(), "updateChart_propertyChange", "thresholding");
        //helper.addListener(ApplicationContext, "scan_propertyChange", "MSScan");
        helper.addListener(Application.getInstance(), "selectedPoint_propertyChange",
                SharedProperties.SELECTED_POINT);
        helper.addListener(Application.getInstance(), "run_propertyChange", SharedProperties.MS_RUN);
    }

    public void spectrumElutionComboBox_actionPerformed(ActionEvent e) {
        String previousType = _type;
        String spectrumElutionMode = (String) spectrumElutionComboBox.getSelectedItem();
        if ("elution".equals(spectrumElutionMode))
            setType("elution");
        else
            setType("scan");

        //if changing from elution to spectrum or vice versa, unlock Y axis
        if (!(_type.equals(previousType))) {
            _lockedYAxisRange = null;
            _previousYAxisRange = null;
            updateChart(true);
        }
    }

    public void scanSpinner_stateChanged(ChangeEvent e) {
        Integer Num = (Integer) scanSpinner.getValue();
        MSRun run = (MSRun) ApplicationContext.getProperty(SharedProperties.MS_RUN);
        if (null == Num || null == run) {
            clearChart();
            return;
        }
        int scanNum = Num.intValue();
        if (_selectedScanNum == scanNum)
            return;

        _selectedScanNum = scanNum;
        _log.debug("_selectedScanNum = " + _selectedScanNum);
        updateChart(true);
    }

    public void mzSpinner_stateChanged(ChangeEvent e) {
        Double Num = (Double) mzSpinner.getValue();
        if (null == Num) {
            clearChart();
            return;
        }
        if (_selectedMZ == Num.doubleValue())
            return;

        _selectedMZ = Num.doubleValue();
        _log.debug("_selectedMZ = " + _selectedMZ);
        updateChart(true);
    }

    public void setType(String type) {
        if (!"scan".equals(type) && !"elution".equals(type))
            throw new IllegalArgumentException(type);
        if (type.equals(_type))
            return;
        _type = type;
        redrawChart();
    }

    /*   public void scan_propertyChange(PropertyChangeEvent e)
          {
          if (!"scan".equals(_type))
      return;
          _selectedScan = (MSRun.MSScan)ApplicationContext.getProperty(SharedProperties.MS_SCAN);
          updateChart();
          } */

    public void run_propertyChange(PropertyChangeEvent e) {
        clearChart();
        MSRun run = (MSRun) e.getNewValue();
        ((SpinnerRunModel) scanSpinner.getModel()).setRun(run);
    }

    public void selectedPoint_propertyChange(PropertyChangeEvent e) {
        Spectrum.Peak p = (Spectrum.Peak) e.getNewValue();

        if (null == p) {
            _selectedMZ = -1;
            _selectedScanNum = -1;
            clearChart();
            return;
        }

        if (_selectedMZ == p.mz && _selectedScanNum == p.scan)
            return;

        _selectedMZ = p.mz;
        _selectedScanNum = p.scan;
        _log.debug("_selectedMZ = " + _selectedMZ);
        _log.debug("_selectedScanNum = " + _selectedScanNum);
        updateChart(false);
    }

    public void copy_actionPerformed(ActionEvent event) {
        copyChartData();
    }

    public void display_actionPerformed(ActionEvent event) {
        updateChart(true);
    }

    public void updateChart_propertyChange(PropertyChangeEvent e) {
        updateChart(true);
    }

    public void _componentResized(ComponentEvent event) {
        redrawChart();
    }

    public Component getComponent() {
        return contentPanel;
    }

    protected void copyChartData() {
        if (null == _chart || null == _spectrum)
            return;
        XYPlot xyplot;
        if (_chart.getPlot() instanceof XYPlot) {
            xyplot = _chart.getXYPlot();
        } else {
            // UNDONE:
            return;
        }
        ValueAxis domainAxis = xyplot.getDomainAxis();
        Range range = domainAxis.getRange();
        double min = range.getLowerBound();
        double max = range.getUpperBound();

        float[] x = _spectrum[0];
        float[] y = _spectrum[1];
        int start = Arrays.binarySearch(x, (float) min);
        if (start < 0)
            start = -(start + 1);
        int end = Arrays.binarySearch(x, (float) max);
        if (end < 0)
            end = -(end + 1);

        StringBuffer sb = new StringBuffer(20 * (end - start));
        for (int i = start; i < end; i++)
            sb.append(x[i]).append('\t').append(y[i]).append('\n');
        StringSelection sel = new StringSelection(sb.toString());
        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(sel, sel);
    }

    /**
     * redo chart from scratch (e.g. for a resize)
     */
    protected void redrawChart() {
        // UNDONE: just set preferredSize?
        _chart = null;
        _chartPanel = null;
        updateChart(true);
    }

    /**
     * update the chart with new data
     */
    public void updateChart(boolean preserveDomain) {
        _log.debug("updateChart: start");
        float[][] spectrumIN = null;
        MSRun run = (MSRun) ApplicationContext.getProperty(SharedProperties.MS_RUN);
        MSRun.MSScan selectedScan = getSelectedScan();

        FloatRange rangeSpectrum = null; // restricted range of displayed data (for autoZoom)
        Pair<Spectrum.Peak, Spectrum.Peak> chartRange = null; // tell other components what we are displaying

        Range domainRange = null;
        if (preserveDomain && _chart != null)
            domainRange = _chart.getXYPlot().getDomainAxis().getRange();

        if (null == run || null == selectedScan) {
            clearChart();
            //if no chart to display, show white background
            chartArea.setBackground(java.awt.Color.WHITE);
            return;
        }

        chartArea.setBackground(_backgroundColor);

        if ("scan".equals(_type)) {
            scanSpinner.setValue(new Integer(selectedScan.getNum()));
            double t = selectedScan.getDoubleRetentionTime();
            timeTextField.setText("" + Math.round(t * 10) / 10.0);
            scanSpinner.setEnabled(true);
            //mzTextField.setText("");
            //mzTextField.setEnabled(false);
            mzSpinner.setValue(new Double(_selectedMZ));
            mzSpinner.setEnabled(false);

            /*         ArrayList l = new ArrayList();
                     l.add(selectedScan.getSpectrum());
                        spectrumIN = Spectrum.CombineRawSpectra(
                run.getTemplateSpectrum(),
                l,
                new FloatRange(selectedScan.getLowMz(),selectedScan.getHighMz())
                ); */

            spectrumIN = selectedScan.getSpectrum();

            if (autoZoom()) {
                // we don't want to filter the analyzed data, in case
                // that affects any of the algorithms
                // we only filter the result Datasets
                // UNDONE: choose range based on size of MSDetailPanel

                // range of 27+ is enough to see 4 ICAT labelled features (9*(4-1))
                rangeSpectrum = new FloatRange((float) _selectedMZ - 10F, (float) _selectedMZ + 30F);
            }

            if (null == rangeSpectrum) {
                chartRange = new Pair<Spectrum.Peak, Spectrum.Peak>(
                        new Spectrum.Peak(_selectedScanNum, spectrumIN[0][0], 0.0F),
                        new Spectrum.Peak(_selectedScanNum, spectrumIN[0][spectrumIN[0].length - 1]));
            } else {
                chartRange = new Pair<Spectrum.Peak, Spectrum.Peak>(
                        new Spectrum.Peak(_selectedScanNum, rangeSpectrum.min, 0.0F),
                        new Spectrum.Peak(_selectedScanNum, rangeSpectrum.max, 0.0F));
            }
        } else // elution
        {
            if (_selectedMZ == -1) {
                clearChart();
                return;
            }

            int RESAMPLE = 36;
            float mz = (float) Math.round(_selectedMZ * RESAMPLE) / RESAMPLE;
            _log.debug("updateChart mz=" + mz);

            scanSpinner.setValue(new Integer(selectedScan.getNum()));
            scanSpinner.setEnabled(false);
            double t = selectedScan.getDoubleRetentionTime();
            timeTextField.setText("" + Math.round(t * 10) / 10.0);
            //mzTextField.setText("" + mz);
            //mzTextField.setEnabled(true);
            mzSpinner.setValue(new Double(_selectedMZ));
            mzSpinner.setEnabled(true);

            FloatArray arrayIntensity = new FloatArray();
            FloatArray arrayScan = new FloatArray();
            int scanIndex = run.getIndexForScanNum(selectedScan.getNum());
            int scanStart = Math.max(0, scanIndex - 64);
            int scanEnd = Math.min(run.getScanCount() - 1, scanStart + 128);
            FloatRange r = new FloatRange(mz, mz);
            for (int s = scanStart; s < scanEnd; s++) {
                MSRun.MSScan scan = run.getScan(s);
                float[][] spectrumRaw = scan.getSpectrum();
                float[] resample = Spectrum.Resample(spectrumRaw, r, RESAMPLE);
                float f = resample[0];
                arrayIntensity.add(f);
                //arrayScan.add((float)scan.getDoubleRetentionTime());
                //            arrayScan.add((float)s);
                arrayScan.add((float) scan.getNum());
            }
            spectrumIN = new float[][] { arrayScan.toArray(null), arrayIntensity.toArray(null) };

            chartRange = new Pair<Spectrum.Peak, Spectrum.Peak>(
                    new Spectrum.Peak(run.getScan(scanStart).getNum(), mz, 0.0F),
                    new Spectrum.Peak(run.getScan(scanEnd).getNum(), mz, 0.0F));
        }

        String mode = (String) displayMode.getSelectedItem();

        java.util.List<XYSeriesCollection> listMultipleCharts = null;
        XYSeriesCollection series = new XYSeriesCollection();
        series.setIntervalWidth(0.0);

        //
        // Process source spectrum
        //

        float[][] spectrum = new float[][] { (float[]) spectrumIN[0].clone(), (float[]) spectrumIN[1].clone() };
        ProcessSpectrum: {
            if ("raw".equals(mode)) {
                break ProcessSpectrum;
            }

            if ("background centroided".equals(mode)) {
                if (run.getHeaderInfo().getDataProcessing().getCentroided() == 1)
                    spectrum = FeatureStrategyCentroided.backgroundSpectrum(spectrum);
                break ProcessSpectrum;
            }

            if ("clean centroided".equals(mode)) {
                if (run.getHeaderInfo().getDataProcessing().getCentroided() == 1)
                    spectrum = FeatureStrategyCentroided.cleanSpectrum(spectrum);
                break ProcessSpectrum;
            }

            // all subsequent processing expects resampling
            // don't resample elution profile
            if ("scan".equals(_type)) {
                int len = spectrum[0].length;
                spectrum = Spectrum.ResampleSpectrum(spectrumIN,
                        new FloatRange((float) Math.floor(spectrum[0][0]), (float) Math.ceil(spectrum[0][len - 1])),
                        36, false);
            } else {
                spectrum[1] = Spectrum.MedianSmooth(spectrum[1]);
                Spectrum.SmoothALittle(spectrum[1]);
            }

            if ("resampled".equals(mode))
                break ProcessSpectrum;

            /*         if ("compressed".equals(mode)) (data encoding test)
                        {
                        float[] s = spectrum[1];
                        short c[] = new short[s.length];
                        double m = 1.0;
                        for (int i = 0 ; i<c.length ; i++)
                           m = Math.max(m, s[i]);
                        double sqrt = Math.sqrt(m+1);
                        m = Math.round(Math.log(sqrt))+1;
                        double f = Math.floor(0x7fff / Math.log(sqrt));
                        m = 0;
                        for (int i = 0 ; i<c.length ; i++)
                           {
                           c[i] = (short)(Math.round(Math.log((s[i]+1)/sqrt) * f));
                           m = Math.max(m, Math.abs(c[i]));
                           }
                        System.err.println("MAX " + m);
                        for (int i = 0 ; i<c.length ; i++)
                           s[i] = (float)((Math.exp((double)c[i]/f) * sqrt) - 1);
                        break ProcessSpectrum;
                        } */

            // remove background for further processing
            if (run.getHeaderInfo().getDataProcessing().getCentroided() != 1) {
                int window = "scan".equals(_type) ? 72 : 15;
                float x[] = spectrum[1];
                float bg[] = Spectrum.MinimaWindow(x, spectrum[0].length, window, null);
                for (int i = 0; i < bg.length; i++)
                    bg[i] = Math.max(0, x[i] - bg[i]);
                spectrum = new float[][] { spectrum[0], bg };
            }

            if ("subtract background".equals(mode))
                break ProcessSpectrum;

            if (mode.startsWith("threshold")) {
                spectrum[1] = Spectrum.WaveletD3(spectrum[1], null);
                break ProcessSpectrum;
            }

            if ("peaks".equals(mode) || "smoothed".equals(mode)) {
                if ("scan".equals(_type)) {
                    double s = Smooth2D.smoothYfactor;
                    spectrum[1] = Spectrum.FFTsmooth(spectrum[1], s, false);
                } else {
                    //FeatureStrategyPeakClusters.smoothTHRESHOLD sm = new FeatureStrategyPeakClusters.smoothTHRESHOLD();
                    spectrum[1] = SmootherCreator._thresholdElution(spectrum[1]);
                }
                break ProcessSpectrum;
            }
        } // ProcessSpectrum:

        //
        // This is the source spectrum
        //

        String name = mode.indexOf("peaks") != -1 ? "-spectrum" : "|spectrum";
        series.addSeries(new SpectrumXYSeries(name, spectrum, rangeSpectrum));
        _spectrum = spectrum;

        //
        // add additional series/charts
        //

        // show min/med for reference
        if (mode.equals("resampled") || mode.equals("subtract background")) {
            float[] T;
            if (mode.equals("resampled")) {
                T = Spectrum.MinimaWindow(spectrum[1], spectrum[0].length, 72, null);
                series.addSeries(new SpectrumXYSeries("-min", new float[][] { spectrum[0], T }, rangeSpectrum));
            }
            T = Spectrum.MedianWindow(spectrum[1], spectrum[0].length, 72, false);
            series.addSeries(new SpectrumXYSeries("-med", new float[][] { spectrum[0], T }, rangeSpectrum));
        }

        if (mode.startsWith("wavelet decomposition") || mode.startsWith("wavelet multiresolution")) {
            Filter f = new Filter("haar");
            float[] signal = spectrum[1]; // Spectrum.PadToDouble(spectrum[1], 32);
            int N = spectrum[0].length; // signal.length;
            int levels = 5;

            //if ("wavelet decomposition 2".equals(mode)) Spectrum.SmoothALittle(signal);

            float[][] modwt1 = Transform.decompose(signal, N, levels, f, "modwt", "periodic", null);
            float[][] t = modwt1;

            if ("wavelet multiresolution".equals(mode)) {
                t = Transform.multiresolution(t, N, levels, f, "modwt", "periodic", null);
            } else if ("wavelet decomposition 2".equals(mode)) {
                float[][] mra = Transform.multiresolution(t, N, levels, f, "modwt", "periodic", null);
                float[][] modwt2 = Transform.decompose(t[2], N, levels, f, "modwt", "periodic", null);
                // show original, d1, and d2
                float[] a = modwt1[2];
                float[] b = modwt2[2];
                float[] m = mra[2];
                Spectrum.Rotate(a, -3);
                Spectrum.Rotate(b, -7);
                t = new float[][] { a, /*b,*/ m }; // b is a mirror image of m
            }

            // copy array into data series
            listMultipleCharts = new java.util.LinkedList<XYSeriesCollection>();
            XYSeriesCollection ds = new XYSeriesCollection(
                    new SpectrumXYSeries("spectrum", spectrum, rangeSpectrum));
            ds.setIntervalWidth(0.0);
            listMultipleCharts.add(ds);
            for (int i = 0; i < t.length; i++) {
                //float[] s = Spectrum.UnpadToFloat(t[i], 32, null);
                float[][] l = new float[][] { spectrum[0], t[i] };
                ds = new XYSeriesCollection(new SpectrumXYSeries("level " + (i + 1), l, rangeSpectrum));
                ds.setIntervalWidth(0.0);
                listMultipleCharts.add(ds);
            }
        } else if (mode.startsWith("haar")) {
            int l = Integer.parseInt(mode.substring(4));
            listMultipleCharts = new LinkedList<XYSeriesCollection>();

            float[] t1 = Haar.transform(spectrum[1], l);
            float[] t2 = Haar.transform(t1, l);
            XYSeriesCollection ds = new XYSeriesCollection(
                    new SpectrumXYSeries("spectrum", spectrum, rangeSpectrum));
            ds.setIntervalWidth(0.0);
            listMultipleCharts.add(ds);
            XYSeriesCollection ds1 = new XYSeriesCollection(
                    new SpectrumXYSeries(mode, new float[][] { spectrum[0], t1 }, rangeSpectrum));
            ds1.setIntervalWidth(0.0);
            listMultipleCharts.add(ds1);
            XYSeriesCollection ds2 = new XYSeriesCollection(
                    new SpectrumXYSeries(mode, new float[][] { spectrum[0], t2 }, rangeSpectrum));
            ds2.setIntervalWidth(0.0);
            listMultipleCharts.add(ds2);
        } else if ("peaks".equals(mode) || "threshold peaks".equals(mode)) {
            double noise = 0.1; //"peaks".equals(mode) ? 2.0 : 1.0; // Spectrum.Noise(spectrum[1], 0, spectrum[1].length);
            int[] peakList = Spectrum.PickPeakIndexes(spectrum[1], noise);

            float[][] peaks = new float[2][peakList.length];
            for (int i = 0; i < peakList.length; i++) {
                int p = peakList[i];
                if (p >= spectrum[0].length)
                    continue;
                peaks[0][i] = spectrum[0][p];
                peaks[1][i] = spectrum[1][p];
            }
            series.addSeries(new SpectrumXYSeries("|peaks", peaks, rangeSpectrum));
            _spectrum = peaks;
        } else if ("wavelet peaks".equals(mode)) {
            //Spectrum.Peak[] peaks = Spectrum.WaveletPeaks(spectrum);
            Spectrum.Peak[] peaks = Spectrum.WaveletPeaksD3(spectrum);
            float[][] peakSpectrum = new float[2][peaks.length];
            for (int p = 0; p < peaks.length; p++) {
                peakSpectrum[0][p] = peaks[p].mz;
                peakSpectrum[1][p] = peaks[p].intensity;
            }

            // show d3 and thresholded spectrum
            int levels = 3, N = spectrum[0].length;
            Filter f = new Filter("haar");
            float[][] modwt = Transform.decompose(spectrum[1], N, levels, f, "modwt", "periodic", null);
            float[][] mra = Transform.multiresolution(modwt, N, levels, f, "modwt", "periodic", null);
            float[] thresholded = new float[N];
            for (int i = 0; i < N; i++)
                thresholded[i] = mra[2][i] + mra[3][i];

            series.removeAllSeries();
            //series.addSeries(new SpectrumXYSeries("-spectrum", new float[][] {spectrum[0],thresholded}, rangeSpectrum));
            series.addSeries(new SpectrumXYSeries("-mra", new float[][] { spectrum[0], mra[2] }, rangeSpectrum));
            series.addSeries(new SpectrumXYSeries("|peaks", peakSpectrum, rangeSpectrum));
            _spectrum = peakSpectrum;
        }

        //
        // now update or create chart
        //

        Color[] colors = series.getSeriesCount() == 3 ? new Color[] { Color.RED, Color.BLUE, Color.BLUE }
                : series.getSeriesCount() == 2 ? new Color[] { Color.BLUE, Color.RED } : new Color[] { Color.RED };

        // NOTE: the more often we call setDatasets instead of creating new chart the better
        // CONSIDER: if we don't save chart, at least preserve zoom settings
        if (false && _chart != null && _chartPanel != null && !(_chart.getPlot() instanceof CombinedDomainXYPlot)
                && listMultipleCharts == null) {
            SpectrumChartFactory.setColors(_chartPanel, colors);
            _chart.getXYPlot().setDataset(series);
            _chartPanel.updateUI();
        } else {
            if (listMultipleCharts == null) {
                _log.debug(
                        "updateChart: series=" + series.getSeriesCount() + " length(0)=" + series.getItemCount(0));
                _chartPanel = SpectrumChartFactory.CreateChartPanel(series, colors);
            } else {
                _log.debug("updateChart: charts=" + listMultipleCharts.size());
                _chartPanel = SpectrumChartFactory.CreateChartPanel(listMultipleCharts, colors);
            }
            _chart = _chartPanel.getChart();

            // there seem to be mystery margins so give a little extra space
            Dimension size = chartArea.getSize();
            _chartPanel.setPreferredSize(new Dimension(size.width - 10, size.height - 10));
            chartArea.removeAll();
            chartArea.add(_chartPanel);
            chartArea.doLayout();
        }
        if (null != domainRange)
            _chart.getXYPlot().getDomainAxis().setRange(domainRange);

        //dhmay: if the user has locked the Y axis, then try to use the stored Y axis range
        if (isYAxisLocked()) {
            //a locked axis value won't be available if the axis was _just_ locked
            if (null == _lockedYAxisRange) {
                //if we've already displayed a chart, use that chart's axis range.  Otherwise,
                //don't force, and record this chart's axis range
                if (_previousYAxisRange != null)
                    _lockedYAxisRange = _previousYAxisRange;
                else
                    _lockedYAxisRange = _chart.getXYPlot().getRangeAxis().getRange();
            }
            _chart.getXYPlot().getRangeAxis().setRange(_lockedYAxisRange);
        } else {
            //if the Y axis isn't locked, then dump the stored range so it isn't used later
            _lockedYAxisRange = null;
        }

        //always store the previous Y axis range
        if (_chart.getXYPlot() != null && _chart.getXYPlot().getRangeAxis() != null
                && _chart.getXYPlot().getRangeAxis().getRange() != null) {
            _previousYAxisRange = _chart.getXYPlot().getRangeAxis().getRange();
        }

        ApplicationContext.setProperty(SharedProperties.CHART_RANGE, chartRange);
    }

    /* UNDONE move to Spectrum.java
    private static int removeZeros(float[][] spectrum)
       {
       int dst = 0;
       float[] s = spectrum[1];
       float[] mz = spectrum[0];
       for (int src = 0; src < mz.length; src++)
     {
     if (s[src] != 0.0F)
        {
        mz[dst] = mz[src];
        s[dst] = s[src];
        dst++;
        }
     }
       if (dst < mz.length) Arrays.fill(mz, dst, mz.length-1, 0.0F);
       if (dst < s.length) Arrays.fill(s, dst, s.length-1, 0.0F);
       return dst;
       }*/

    protected void clearChart() {
        _log.debug("clearChart");
        scanSpinner.setValue(null);
        scanSpinner.setEnabled(false);
        timeTextField.setText("");
        chartArea.removeAll();
        _chart = null;
    }

    public static class SpinnerRunModel extends SpinnerNumberModel {
        MSRun _run = null;
        int _scanIndex = -1;

        SpinnerRunModel() {
        }

        void setRun(MSRun run) {
            if (_run == run)
                return;
            _run = run;
            fireStateChanged();
        }

        public Object getNextValue() {
            if (null == _run)
                return null;
            if (_scanIndex >= _run.getScanCount())
                return null;
            _scanIndex++;
            fireStateChanged();
            MSRun.MSScan scan = _run.getScan(_scanIndex);
            return new Integer(scan.getNum());
        }

        public Object getPreviousValue() {
            if (null == _run)
                return new Integer(0);
            if (_scanIndex <= 0)
                return null;
            _scanIndex--;
            fireStateChanged();
            MSRun.MSScan scan = _run.getScan(_scanIndex);
            return new Integer(scan.getNum());
        }

        public Object getValue() {
            if (null == _run || _scanIndex < 0 || _scanIndex >= _run.getScanCount())
                return new Integer(0);
            return new Integer(_run.getScan(_scanIndex).getNum());
        }

        public int getIndex() {
            return _scanIndex;
        }

        public void setValue(Object value) {
            if (null == _run)
                return;
            if (!(value instanceof Integer))
                return;
            int v = ((Integer) value).intValue();

            Object current = getValue();
            if (null != current && v == ((Integer) current).intValue())
                return;

            int i = _run.getIndexForScanNum(v);
            i = i < 0 ? -(i + 1) : i;
            _scanIndex = i;
            fireStateChanged();

            return;
            /*for (int i=0, count=_run.getScanCount() ; i<count ; i++)
               {
               int num = _run.getScan(i).getNum();
               if (num < v) continue;
               if (num > v) break;
            _scanIndex = i;
               fireStateChanged();
               return;
               }*/
        }
    }

    /*
    public void setPoint(Point point)
       {
       if (null == point)
     return;
        
       if (_chart != null && _chartPanel != null)
     {
     float min = (float)(point.getY() - 10);
     float max = min + 20;
     XYPlot xyplot = _chart.getXYPlot();
     ValueAxis domainAxis = xyplot.getDomainAxis();
     domainAxis.setRange(min, max);
        
     float[][] spectrum = _selectedScan.getSpectrum();
     double maxIntensity = 0;
     int i = 0;
     for (i = 0; i < spectrum[0].length && spectrum[0][i] < min; i++)
        ;
        
     for (; i < spectrum[0].length && spectrum[0][i] < max; i++)
        if (spectrum[1][i] > maxIntensity)
           maxIntensity = spectrum[1][i];
        
     ValueAxis rangeAxis = xyplot.getRangeAxis();
     if (null != rangeAxis)
        rangeAxis.setRange(0, maxIntensity * 1.1);
     return;
     }
       }*/

    protected MSRun.MSScan getSelectedScan() {
        MSRun run = (MSRun) ApplicationContext.getProperty(SharedProperties.MS_RUN);
        if (null == run)
            return null;
        int i = run.getIndexForScanNum(_selectedScanNum);

        if (i < 0) {
            // Try getting the previous scan (helps if we're looking at ms2 features)
            if (i == -1)
                return null;
            i = -i - 2;
            _selectedScanNum = run.getScan(i).getNum();
        }
        return run.getScan(i);
    }

    protected boolean autoZoom() {
        Boolean B = (Boolean) ApplicationContext.getProperty(SharedProperties.AUTO_ZOOM);
        return null == B ? false : B.booleanValue();
    }

    //dhmay adding 12/19/2005
    protected boolean isYAxisLocked() {
        Boolean B = (Boolean) ApplicationContext.getProperty(SharedProperties.LOCK_Y_AXIS);
        return null == B ? false : B.booleanValue();
    }

}