org.mwc.debrief.track_shift.views.BaseStackedDotsView.java Source code

Java tutorial

Introduction

Here is the source code for org.mwc.debrief.track_shift.views.BaseStackedDotsView.java

Source

/*
 *    Debrief - the Open Source Maritime Analysis Application
 *    http://debrief.info
 *
 *    (C) 2000-2014, PlanetMayo Ltd
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the Eclipse Public License v1.0
 *    (http://www.eclipse.org/legal/epl-v10.html)
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 */
package org.mwc.debrief.track_shift.views;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Stroke;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.Vector;

import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.LegendItemSource;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.event.ChartProgressListener;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.DefaultXYItemRenderer;
import org.jfree.experimental.chart.swt.ChartComposite;
import org.jfree.ui.TextAnchor;
import org.mwc.cmap.core.CorePlugin;
import org.mwc.cmap.core.DataTypes.TrackData.TrackDataProvider;
import org.mwc.cmap.core.DataTypes.TrackData.TrackDataProvider.TrackDataListener;
import org.mwc.cmap.core.DataTypes.TrackData.TrackDataProvider.TrackShiftListener;
import org.mwc.cmap.core.DataTypes.TrackData.TrackManager;
import org.mwc.cmap.core.property_support.EditableWrapper;
import org.mwc.cmap.core.ui_support.PartMonitor;
import org.mwc.debrief.core.actions.DragSegment;
import org.mwc.debrief.track_shift.Activator;

import MWC.GUI.Editable;
import MWC.GUI.ErrorLogger;
import MWC.GUI.Layer;
import MWC.GUI.Layers;
import MWC.GUI.Layers.DataListener;
import MWC.GUI.JFreeChart.ColourStandardXYItemRenderer;
import MWC.GUI.JFreeChart.DateAxisEditor;
import MWC.GUI.Shapes.DraggableItem;
import MWC.GenericData.WatchableList;

/**
 */

abstract public class BaseStackedDotsView extends ViewPart implements ErrorLogger {

    private static final String SHOW_DOT_PLOT = "SHOW_DOT_PLOT";

    private static final String SHOW_LINE_PLOT = "SHOW_LINE_PLOT";

    /**
     * helper application to help track creation/activation of new plots
     */
    private PartMonitor _myPartMonitor;

    /**
     * the errors we're plotting
     */
    XYPlot _dotPlot;

    /**
     * and the actual values
     * 
     */
    XYPlot _linePlot;

    /**
     * legacy helper class
     */
    final StackedDotHelper _myHelper;

    /**
     * our track-data provider
     */
    protected TrackManager _theTrackDataListener;

    /**
     * our listener for tracks being shifted...
     */
    protected TrackShiftListener _myShiftListener;

    /**
     * buttons for which plots to show
     * 
     */
    protected Action _showLinePlot;
    Action _showDotPlot;

    /**
     * flag indicating whether we should only show stacked dots for visible fixes
     */
    Action _onlyVisible;

    /**
     * our layers listener...
     */
    protected DataListener _layersListener;

    /**
     * the set of layers we're currently listening to
     */
    protected Layers _ourLayersSubject;

    protected TrackDataProvider _myTrackDataProvider;

    ChartComposite _holder;

    JFreeChart _myChart;

    private Vector<Action> _customActions;

    protected Action _autoResize;

    private CombinedDomainXYPlot _combined;

    protected TrackDataListener _myTrackDataListener;

    /**
     * does our output need bearing in the data?
     * 
     */
    private final boolean _needBrg;

    /**
     * does our output need frequency in the data?
     * 
     */
    private final boolean _needFreq;

    // private Action _magicBtn;

    protected Vector<ISelectionProvider> _selProviders;

    protected ISelectionChangedListener _mySelListener;

    protected Vector<DraggableItem> _draggableSelection;

    /**
     * 
     * @param needBrg
     *          if the algorithm needs bearing data
     * @param needFreq
     *          if the agorithm needs frequency data
     */
    protected BaseStackedDotsView(final boolean needBrg, final boolean needFreq) {
        _myHelper = new StackedDotHelper();

        _needBrg = needBrg;
        _needFreq = needFreq;

        // create the actions - the 'centre-y axis' action may get called before
        // the
        // interface is shown
        makeActions();
    }

    abstract protected String getUnits();

    abstract protected String getType();

    abstract protected void updateData(boolean updateDoublets);

    private void contributeToActionBars() {
        final IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }

    protected void fillLocalToolBar(final IToolBarManager toolBarManager) {
        // fit to window
        toolBarManager.add(_autoResize);
        toolBarManager.add(_onlyVisible);
        toolBarManager.add(_showLinePlot);
        toolBarManager.add(_showDotPlot);
        // toolBarManager.add(_magicBtn);

        // and a separator
        toolBarManager.add(new Separator());

        final Vector<Action> actions = DragSegment.getDragModes();
        for (final Iterator<Action> iterator = actions.iterator(); iterator.hasNext();) {
            final Action action = iterator.next();
            toolBarManager.add(action);
        }
    }

    /**
     * This is a callback that will allow us to create the viewer and initialize
     * it.
     */
    @Override
    public void createPartControl(final Composite parent) {

        _holder = new ChartComposite(parent, SWT.NONE, null, 400, 600, 300, 200, 1800, 1800, true, true, true, true,
                true, true) {
            @Override
            public void mouseUp(MouseEvent event) {
                super.mouseUp(event);
                JFreeChart c = getChart();
                if (c != null) {
                    c.setNotify(true); // force redraw
                }
            }
        };

        // hey - now create the stacked plot!
        createStackedPlot();

        // /////////////////////////////////////////
        // ok - listen out for changes in the view
        // /////////////////////////////////////////
        _selProviders = new Vector<ISelectionProvider>();
        _mySelListener = new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                final ISelection sel = event.getSelection();
                final Vector<DraggableItem> dragees = new Vector<DraggableItem>();
                if (sel instanceof StructuredSelection) {
                    final StructuredSelection str = (StructuredSelection) sel;
                    final Iterator<?> iter = str.iterator();
                    while (iter.hasNext()) {
                        final Object object = (Object) iter.next();
                        if (object instanceof EditableWrapper) {
                            final EditableWrapper ew = (EditableWrapper) object;
                            final Editable item = ew.getEditable();
                            if (item instanceof DraggableItem) {

                                dragees.add((DraggableItem) item);
                            }
                        } else {
                            return;
                        }
                    }

                    // ok, we've just got draggable items - override the current
                    // item
                    _draggableSelection = dragees;
                }
            }
        };

        // sort out the part monitor
        _myPartMonitor = new PartMonitor(getSite().getWorkbenchWindow().getPartService());

        // now start listening out for people's parts
        watchMyParts();

        // put the actions in the UI
        contributeToActionBars();
    }

    /**
     * method to create a working plot (to contain our data)
     * 
     * @return the chart, in it's own panel
     */
    @SuppressWarnings("deprecation")
    protected void createStackedPlot() {

        // first create the x (time) axis
        final SimpleDateFormat _df = new SimpleDateFormat("HHmm:ss");
        _df.setTimeZone(TimeZone.getTimeZone("GMT"));

        final DateAxis xAxis = new CachedTickDateAxis("");
        xAxis.setDateFormatOverride(_df);
        Font tickLabelFont = new Font("Courier", Font.PLAIN, 13);
        xAxis.setTickLabelFont(tickLabelFont);
        xAxis.setTickLabelPaint(Color.BLACK);

        xAxis.setStandardTickUnits(DateAxisEditor.createStandardDateTickUnitsAsTickUnits());
        xAxis.setAutoTickUnitSelection(true);

        // create the special stepper plot
        _dotPlot = new XYPlot();
        NumberAxis errorAxis = new NumberAxis("Error (" + getUnits() + ")");
        Font axisLabelFont = new Font("Courier", Font.PLAIN, 16);
        errorAxis.setLabelFont(axisLabelFont);
        errorAxis.setTickLabelFont(tickLabelFont);
        _dotPlot.setRangeAxis(errorAxis);
        _dotPlot.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);
        _dotPlot.setRenderer(new ColourStandardXYItemRenderer(null, null, _dotPlot));

        _dotPlot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        _dotPlot.setRangeGridlineStroke(new BasicStroke(2));
        _dotPlot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        _dotPlot.setDomainGridlineStroke(new BasicStroke(2));

        // now try to do add a zero marker on the error bar
        final Paint thePaint = Color.DARK_GRAY;
        final Stroke theStroke = new BasicStroke(3);
        final ValueMarker zeroMarker = new ValueMarker(0.0, thePaint, theStroke);
        _dotPlot.addRangeMarker(zeroMarker);

        _linePlot = new XYPlot();
        final NumberAxis absBrgAxis = new NumberAxis("Absolute (" + getUnits() + ")");
        absBrgAxis.setLabelFont(axisLabelFont);
        absBrgAxis.setTickLabelFont(tickLabelFont);
        _linePlot.setRangeAxis(absBrgAxis);
        absBrgAxis.setAutoRangeIncludesZero(false);
        _linePlot.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT);
        final DefaultXYItemRenderer lineRend = new ColourStandardXYItemRenderer(null, null, _linePlot);
        lineRend.setPaint(Color.DARK_GRAY);
        _linePlot.setRenderer(lineRend);

        _linePlot.setDomainCrosshairVisible(true);
        _linePlot.setRangeCrosshairVisible(true);
        _linePlot.setDomainCrosshairPaint(Color.GRAY);
        _linePlot.setRangeCrosshairPaint(Color.GRAY);
        _linePlot.setDomainCrosshairStroke(new BasicStroke(3.0f));
        _linePlot.setRangeCrosshairStroke(new BasicStroke(3.0f));

        _linePlot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        _linePlot.setRangeGridlineStroke(new BasicStroke(2));
        _linePlot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        _linePlot.setDomainGridlineStroke(new BasicStroke(2));

        // and the plot object to display the cross hair value
        final XYTextAnnotation annot = new XYTextAnnotation("-----", 2, 2);
        annot.setTextAnchor(TextAnchor.TOP_LEFT);

        Font annotationFont = new Font("Courier", Font.BOLD, 16);
        annot.setFont(annotationFont);
        annot.setPaint(Color.DARK_GRAY);
        annot.setBackgroundPaint(Color.white);
        _linePlot.addAnnotation(annot);

        // give them a high contrast backdrop
        _dotPlot.setBackgroundPaint(Color.white);
        _linePlot.setBackgroundPaint(Color.white);

        // set the y axes to autocalculate
        _dotPlot.getRangeAxis().setAutoRange(true);
        _linePlot.getRangeAxis().setAutoRange(true);

        _combined = new CombinedDomainXYPlot(xAxis);

        _combined.add(_linePlot);
        _combined.add(_dotPlot);

        _combined.setOrientation(PlotOrientation.HORIZONTAL);

        // put the plot into a chart
        _myChart = new JFreeChart(null, null, _combined, true);

        final LegendItemSource[] sources = { _linePlot };
        _myChart.getLegend().setSources(sources);

        _myChart.addProgressListener(new ChartProgressListener() {
            public void chartProgress(final ChartProgressEvent cpe) {
                if (cpe.getType() != ChartProgressEvent.DRAWING_FINISHED)
                    return;

                // is hte line plot visible?
                if (!_showLinePlot.isChecked())
                    return;

                // double-check our label is still in the right place
                final double xVal = _linePlot.getRangeAxis().getLowerBound();
                final double yVal = _linePlot.getDomainAxis().getUpperBound();
                boolean annotChanged = false;
                if (annot.getX() != yVal) {
                    annot.setX(yVal);
                    annotChanged = true;
                }
                if (annot.getY() != xVal) {
                    annot.setY(xVal);
                    annotChanged = true;
                }
                // and write the text
                final String numA = MWC.Utilities.TextFormatting.GeneralFormat
                        .formatOneDecimalPlace(_linePlot.getRangeCrosshairValue());
                final Date newDate = new Date((long) _linePlot.getDomainCrosshairValue());
                final SimpleDateFormat _df = new SimpleDateFormat("HHmm:ss");
                _df.setTimeZone(TimeZone.getTimeZone("GMT"));
                final String dateVal = _df.format(newDate);
                final String theMessage = " [" + dateVal + "," + numA + "]";
                if (!theMessage.equals(annot.getText())) {
                    annot.setText(theMessage);
                    annotChanged = true;
                }
                if (annotChanged) {
                    _linePlot.removeAnnotation(annot);
                    _linePlot.addAnnotation(annot);
                }
            }
        });

        // and insert into the panel
        _holder.setChart(_myChart);

        // do a little tidying to reflect the memento settings
        if (!_showLinePlot.isChecked())
            _combined.remove(_linePlot);
        if (!_showDotPlot.isChecked() && _showLinePlot.isChecked())
            _combined.remove(_dotPlot);
    }

    /**
     * view is closing, shut down, preserve life
     */
    @Override
    public void dispose() {
        // get parent to ditch itself
        super.dispose();

        // ditch the actions
        if (_customActions != null)
            _customActions.removeAllElements();

        // are we listening to any layers?
        if (_ourLayersSubject != null)
            _ourLayersSubject.removeDataReformattedListener(_layersListener);

        if (_theTrackDataListener != null) {
            _theTrackDataListener.removeTrackShiftListener(_myShiftListener);
            _theTrackDataListener.removeTrackDataListener(_myTrackDataListener);
        }

        // stop the part monitor
        _myPartMonitor.ditch();

    }

    protected void fillLocalPullDown(final IMenuManager manager) {
        manager.add(_onlyVisible);
        // and the help link
        manager.add(new Separator());
        manager.add(CorePlugin.createOpenHelpAction("org.mwc.debrief.help.TrackShifting", null, this));

    }

    protected void makeActions() {

        _autoResize = new Action("Auto resize", IAction.AS_CHECK_BOX) {
            @Override
            public void run() {
                super.run();
                final boolean val = _autoResize.isChecked();
                if (_showLinePlot.isChecked()) {
                    // ok - redraw the plot we may have changed the axis
                    // centreing
                    _linePlot.getRangeAxis().setAutoRange(val);
                    _linePlot.getDomainAxis().setAutoRange(val);
                }
                if (_showDotPlot.isChecked()) {
                    _dotPlot.getRangeAxis().setAutoRange(val);
                    _dotPlot.getDomainAxis().setAutoRange(val);
                }
            }
        };
        _autoResize.setChecked(true);
        _autoResize.setToolTipText("Keep plot sized to show all data");
        _autoResize.setImageDescriptor(CorePlugin.getImageDescriptor("icons/24/fit_to_win.png"));

        _showLinePlot = new Action("Actuals plot", IAction.AS_CHECK_BOX) {
            @Override
            public void run() {
                super.run();
                if (_showLinePlot.isChecked()) {
                    _combined.remove(_linePlot);
                    _combined.remove(_dotPlot);

                    _combined.add(_linePlot);
                    if (_showDotPlot.isChecked())
                        _combined.add(_dotPlot);
                } else {
                    if (_combined.getSubplots().size() > 1)
                        _combined.remove(_linePlot);
                }
            }
        };
        _showLinePlot.setChecked(true);
        _showLinePlot.setToolTipText("Show the actuals plot");
        _showLinePlot.setImageDescriptor(Activator.getImageDescriptor("icons/24/stacked_lines.png"));

        _showDotPlot = new Action("Error plot", IAction.AS_CHECK_BOX) {
            @Override
            public void run() {
                super.run();
                if (_showDotPlot.isChecked()) {
                    _combined.remove(_linePlot);
                    _combined.remove(_dotPlot);

                    if (_showLinePlot.isChecked())
                        _combined.add(_linePlot);
                    _combined.add(_dotPlot);
                } else {
                    if (_combined.getSubplots().size() > 1)
                        _combined.remove(_dotPlot);
                }
            }
        };
        _showDotPlot.setChecked(true);
        _showDotPlot.setToolTipText("Show the error plot");
        _showDotPlot.setImageDescriptor(Activator.getImageDescriptor("icons/24/stacked_dots.png"));

        // get an error logger
        final ErrorLogger logger = this;

        _onlyVisible = new Action("Only draw dots for visible data points", IAction.AS_CHECK_BOX) {

            @Override
            public void run() {
                super.run();

                // set the title, so there's something useful in there
                _myChart.setTitle("");

                // we need to get a fresh set of data pairs - the number may
                // have
                // changed
                _myHelper.initialise(_theTrackDataListener, true, _onlyVisible.isChecked(), _holder, logger,
                        getType(), _needBrg, _needFreq);

                // and a new plot please
                updateStackedDots(true);
            }
        };
        _onlyVisible.setText("Only plot visible data");
        _onlyVisible.setChecked(true);
        _onlyVisible.setToolTipText("Only draw dots for visible data points");
        _onlyVisible.setImageDescriptor(Activator.getImageDescriptor("icons/24/reveal.png"));
    }

    /**
     * Passing the focus request to the viewer's control.
     */
    @Override
    public void setFocus() {
    }

    public void logError(final int statusCode, final String string, final Exception object) {
        // somehow, put the message into the UI
        _myChart.setTitle(string);

        // is it a fail status
        if (statusCode != Status.OK) {
            // and store the problem into the log
            CorePlugin.logError(statusCode, string, object);

            // also ditch the data in the plots - to blank them out
            clearPlots();
        }
    }

    /**
     * the track has been moved, update the dots
     */
    void clearPlots() {
        if (Thread.currentThread() == Display.getDefault().getThread()) {
            // it's ok we're already in a display thread
            _dotPlot.setDataset(null);
            _linePlot.setDataset(null);
        } else {
            // we're not in the display thread - make it so!
            Display.getDefault().syncExec(new Runnable() {
                public void run() {
                    _dotPlot.setDataset(null);
                    _linePlot.setDataset(null);
                }
            });
        }
    }

    /**
     * the track has been moved, update the dots
     */
    void updateStackedDots(final boolean updateDoublets) {
        if (Thread.currentThread() == Display.getDefault().getThread()) {
            // it's ok we're already in a display thread
            wrappedUpdateStackedDots(updateDoublets);
        } else {
            // we're not in the display thread - make it so!
            Display.getDefault().syncExec(new Runnable() {
                public void run() {
                    // update the current datasets
                    wrappedUpdateStackedDots(updateDoublets);
                }
            });
        }
    }

    /**
     * the track has been moved, update the dots
     */
    void wrappedUpdateStackedDots(final boolean updateDoublets) {

        // update the current datasets
        updateData(updateDoublets);

        // right, are we updating the range data?
        if (_autoResize.isChecked()) {
            if (_showDotPlot.isChecked()) {
                _dotPlot.getRangeAxis().setAutoRange(false);
                _dotPlot.getRangeAxis().setAutoRange(true);
            }
            if (_showLinePlot.isChecked()) {
                _linePlot.getRangeAxis().setAutoRange(false);
                _linePlot.getRangeAxis().setAutoRange(true);
            }
        }

        // note, we also update the domain axis if we're updating the data in
        // question
        if (updateDoublets) {
            // trigger recalculation of date axis ticks
            final CachedTickDateAxis date = (CachedTickDateAxis) _combined.getDomainAxis();
            date.clearTicks();

            if (_showDotPlot.isChecked()) {
                _dotPlot.getDomainAxis().setAutoRange(false);
                _dotPlot.getDomainAxis().setAutoRange(true);
                _dotPlot.getDomainAxis().setAutoRange(false);
            }
            if (_showLinePlot.isChecked()) {
                _linePlot.getDomainAxis().setAutoRange(false);
                _linePlot.getDomainAxis().setAutoRange(true);
                _linePlot.getDomainAxis().setAutoRange(false);
            }
        }
    }

    /**
     * sort out what we're listening to...
     */
    private final void watchMyParts() {

        final ErrorLogger logger = this;

        _myPartMonitor.addPartListener(ISelectionProvider.class, PartMonitor.ACTIVATED,
                new PartMonitor.ICallback() {
                    public void eventTriggered(final String type, final Object part,
                            final IWorkbenchPart parentPart) {
                        final ISelectionProvider prov = (ISelectionProvider) part;

                        // am I already listning to this
                        if (_selProviders.contains(prov)) {
                            // ignore, we're already listening to it
                        } else {
                            prov.addSelectionChangedListener(_mySelListener);
                            _selProviders.add(prov);
                        }
                    }
                });
        _myPartMonitor.addPartListener(ISelectionProvider.class, PartMonitor.CLOSED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                final ISelectionProvider prov = (ISelectionProvider) part;

                // am I already listning to this
                if (_selProviders.contains(prov)) {
                    // ok, ditch this listener
                    _selProviders.remove(prov);

                    // and stop listening
                    prov.removeSelectionChangedListener(_mySelListener);
                } else {
                    // hey, we're not even listening to it.
                }
            }
        });

        _myPartMonitor.addPartListener(TrackManager.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                // is it a new one?
                if (part != _theTrackDataListener) {
                    // cool, remember about it.
                    _theTrackDataListener = (TrackManager) part;

                    // set the title, so there's something useful in
                    // there
                    _myChart.setTitle("");

                    // ok - fire off the event for the new tracks
                    _myHelper.initialise(_theTrackDataListener, false, _onlyVisible.isChecked(), _holder, logger,
                            getType(), _needBrg, _needFreq);

                    // just in case we're ready to start plotting, go
                    // for it!
                    updateStackedDots(true);
                }

            }
        });
        _myPartMonitor.addPartListener(TrackManager.class, PartMonitor.CLOSED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                // ok, ditch it.
                _theTrackDataListener = null;

                _myHelper.reset();
            }
        });
        _myPartMonitor.addPartListener(TrackDataProvider.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                // cool, remember about it.
                final TrackDataProvider dataP = (TrackDataProvider) part;

                // do we need to generate the shift listener?
                if (_myShiftListener == null) {
                    _myShiftListener = new TrackShiftListener() {
                        public void trackShifted(final WatchableList subject) {
                            // the tracks have moved, we haven't changed
                            // the tracks or
                            // anything like that...
                            updateStackedDots(false);
                        }
                    };

                    _myTrackDataListener = new TrackDataListener() {

                        public void tracksUpdated(final WatchableList primary, final WatchableList[] secondaries) {
                            _myHelper.initialise(_theTrackDataListener, false, _onlyVisible.isChecked(), _holder,
                                    logger, getType(), _needBrg, _needFreq);

                            // ahh, the tracks have changed, better
                            // update the doublets

                            // ok, do the recalc
                            updateStackedDots(true);

                            // ok - if we're on auto update, do the
                            // update
                            updateLinePlotRanges();

                        }
                    };
                }

                // is this the one we're already listening to?
                if (_myTrackDataProvider != dataP) {
                    // ok - let's start off with a clean plot
                    _dotPlot.setDataset(null);

                    // nope, better stop listening then
                    if (_myTrackDataProvider != null) {
                        _myTrackDataProvider.removeTrackShiftListener(_myShiftListener);
                        _myTrackDataProvider.removeTrackDataListener(_myTrackDataListener);
                    }

                    // ok, start listening to it anyway
                    _myTrackDataProvider = dataP;
                    _myTrackDataProvider.addTrackShiftListener(_myShiftListener);
                    _myTrackDataProvider.addTrackDataListener(_myTrackDataListener);

                    // hey - fire a dot update
                    updateStackedDots(true);
                }
            }
        });

        _myPartMonitor.addPartListener(TrackDataProvider.class, PartMonitor.CLOSED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                final TrackDataProvider tdp = (TrackDataProvider) part;
                tdp.removeTrackShiftListener(_myShiftListener);
                tdp.removeTrackDataListener(_myTrackDataListener);

                if (tdp == _myTrackDataProvider) {
                    _myTrackDataProvider = null;
                }

                // hey - lets clear our plot
                updateStackedDots(true);
            }
        });

        _myPartMonitor.addPartListener(Layers.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                final Layers theLayers = (Layers) part;

                // do we need to create our listener
                if (_layersListener == null) {
                    _layersListener = new Layers.DataListener() {
                        public void dataExtended(final Layers theData) {
                        }

                        public void dataModified(final Layers theData, final Layer changedLayer) {
                        }

                        public void dataReformatted(final Layers theData, final Layer changedLayer) {
                            _myHelper.initialise(_theTrackDataListener, false, _onlyVisible.isChecked(), _holder,
                                    logger, getType(), _needBrg, _needFreq);

                            updateStackedDots(false);

                        }
                    };
                }

                // is this what we're listening to?
                if (_ourLayersSubject != theLayers) {
                    // nope, stop listening to the old one (if there is
                    // one!)
                    if (_ourLayersSubject != null)
                        _ourLayersSubject.removeDataReformattedListener(_layersListener);

                    // and remember the new one
                    _ourLayersSubject = theLayers;
                }

                // now start listening to the new one.
                theLayers.addDataReformattedListener(_layersListener);
            }
        });
        _myPartMonitor.addPartListener(Layers.class, PartMonitor.CLOSED, new PartMonitor.ICallback() {
            public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) {
                final Layers theLayers = (Layers) part;

                // is this what we're listening to?
                if (_ourLayersSubject == theLayers) {
                    // yup, stop listening
                    _ourLayersSubject.removeDataReformattedListener(_layersListener);

                    _linePlot.setDataset(null);
                    _dotPlot.setDataset(null);
                }
            }

        });

        // ok we're all ready now. just try and see if the current part is valid
        _myPartMonitor.fireActivePart(getSite().getWorkbenchWindow().getActivePage());
    }

    /**
     * some data has changed. if we're auto ranging, update the axes
     * 
     */
    protected void updateLinePlotRanges() {
        // have a look at the auto resize
        if (_autoResize.isChecked()) {
            if (_showLinePlot.isChecked()) {
                _linePlot.getRangeAxis().setAutoRange(false);
                _linePlot.getDomainAxis().setAutoRange(false);
                _linePlot.getRangeAxis().setAutoRange(true);
                _linePlot.getDomainAxis().setAutoRange(true);
            }
        }
    }

    @Override
    public void init(final IViewSite site, final IMemento memento) throws PartInitException {
        super.init(site, memento);

        if (memento != null) {

            final Boolean showLineVal = memento.getBoolean(SHOW_LINE_PLOT);
            final Boolean showDotVal = memento.getBoolean(SHOW_DOT_PLOT);
            if (showLineVal != null) {
                _showLinePlot.setChecked(showLineVal);
            }
            if (showDotVal != null) {
                _showDotPlot.setChecked(showDotVal);
            }
        }
    }

    @Override
    public void saveState(final IMemento memento) {
        super.saveState(memento);

        // remember if we're showing the error plot
        memento.putBoolean(SHOW_LINE_PLOT, _showLinePlot.isChecked());
        memento.putBoolean(SHOW_DOT_PLOT, _showDotPlot.isChecked());

    }

}