Java tutorial
/* * 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.sensorfusion.views; import java.awt.event.InputEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Vector; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.operations.IUndoableOperation; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; 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.layout.FillLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IActionBars; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.part.ViewPart; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.JFreeChart; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.experimental.chart.swt.ChartComposite; import org.mwc.cmap.core.CorePlugin; import org.mwc.cmap.core.DataTypes.TrackData.TrackManager; import org.mwc.cmap.core.operations.CMAPOperation; import org.mwc.cmap.core.preferences.SelectionHelper; import org.mwc.cmap.core.property_support.EditableWrapper; import org.mwc.cmap.core.property_support.RightClickSupport; import org.mwc.cmap.core.ui_support.PartMonitor; import org.mwc.debrief.sensorfusion.Activator; import org.mwc.debrief.sensorfusion.views.DataSupport.SensorSeries; import org.mwc.debrief.sensorfusion.views.DataSupport.TacticalSeries; import org.mwc.debrief.sensorfusion.views.FusionPlotRenderer.FusionHelper; import Debrief.Wrappers.SensorWrapper; import Debrief.Wrappers.TrackWrapper; import Debrief.Wrappers.Track.SplittableLayer; import MWC.GUI.BaseLayer; import MWC.GUI.Editable; import MWC.GUI.Layer; import MWC.GUI.Layers; import MWC.GUI.Layers.DataListener; import MWC.GenericData.WatchableList; public class SensorFusionView extends ViewPart implements ISelectionProvider, FusionHelper { private static class TrimToSubjectOperation extends CMAPOperation { final private TrackWrapper _primary; final private WatchableList[] _secondaries; final private Layers _layers; private ArrayList<SensorWrapper> _deletedSensors; public TrimToSubjectOperation(TrackWrapper primary, WatchableList[] secondaries, Layers _currentLayers) { super("Trim to sensors that match secondary tracks"); _primary = primary; _secondaries = secondaries; _layers = _currentLayers; } public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { _deletedSensors = DataSupport.trimToSensorNearSubjectTracks(_primary, _secondaries); Iterator<SensorWrapper> iter = _deletedSensors.iterator(); while (iter.hasNext()) { SensorWrapper thisSensor = (SensorWrapper) iter.next(); _primary.removeElement(thisSensor); } // ok, fire off a layers extended event to share the good news if (_layers != null && !_deletedSensors.isEmpty()) { _layers.fireExtended(); } IStatus res = new Status(IStatus.OK, Activator.PLUGIN_ID, "trim sensors successful", null); return res; } @Override public boolean canRedo() { return true; } @Override public boolean canUndo() { return true; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { // ok, restore the items Iterator<SensorWrapper> iter = _deletedSensors.iterator(); while (iter.hasNext()) { SensorWrapper thisSensor = (SensorWrapper) iter.next(); _primary.add(thisSensor); } // ok, fire off a layers extended event to share the good news if (_layers != null && !_deletedSensors.isEmpty()) { _layers.fireExtended(); _deletedSensors.clear(); } // register success return new Status(IStatus.OK, Activator.PLUGIN_ID, "Undo trim sensors", null); } } private static class TrimToTrackPeriodOperation extends CMAPOperation { final private TrackWrapper _primary; final private Layers _layers; private ArrayList<SensorWrapper> _deletedSensors; public TrimToTrackPeriodOperation(TrackWrapper primary, Layers _currentLayers) { super("Trim sensors outside primary track period"); _primary = primary; _layers = _currentLayers; } public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { _deletedSensors = DataSupport.trimToTrackPeriod(_primary); Iterator<SensorWrapper> iter = _deletedSensors.iterator(); while (iter.hasNext()) { SensorWrapper thisSensor = (SensorWrapper) iter.next(); _primary.removeElement(thisSensor); } // ok, fire off a layers extended event to share the good news if (_layers != null && !_deletedSensors.isEmpty()) { _layers.fireExtended(); } IStatus res = new Status(IStatus.OK, Activator.PLUGIN_ID, "trim sensors successful", null); return res; } @Override public boolean canRedo() { return true; } @Override public boolean canUndo() { return true; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { // ok, restore the items Iterator<SensorWrapper> iter = _deletedSensors.iterator(); while (iter.hasNext()) { SensorWrapper thisSensor = (SensorWrapper) iter.next(); _primary.add(thisSensor); } // ok, fire off a layers extended event to share the good news if (_layers != null && !_deletedSensors.isEmpty()) { _layers.fireExtended(); _deletedSensors.clear(); } // register success return new Status(IStatus.OK, Activator.PLUGIN_ID, "Undo trim sensors", null); } } private static class SplitSegmentsOperation extends CMAPOperation { final private Layers _layers; private SplittableLayer _youSplitter; public SplitSegmentsOperation(SplittableLayer youSplitter, Layers currentLayers) { super("Auto-split sensor datasets"); _youSplitter = youSplitter; _layers = currentLayers; } public IStatus execute(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { _youSplitter.AutoSplitTracks(); // ok, fire off a layers extended event to share the good news if (_layers != null) _layers.fireExtended(); IStatus res = new Status(IStatus.OK, Activator.PLUGIN_ID, "split sensor tracks successful", null); return res; } @Override public boolean canRedo() { return false; } @Override public boolean canUndo() { return false; } @Override public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { return execute(monitor, info); } @Override public IStatus undo(final IProgressMonitor monitor, final IAdaptable info) throws ExecutionException { // register success return new Status(IStatus.OK, Activator.PLUGIN_ID, "Undo trim sensors", null); } } private static final String CHART_NAME = "Bearing data"; /** * The ID of the view as specified by the extension. */ public static final String ID = "org.mwc.debrief.SensorFusion"; /** * helper application to help track creation/activation of new plots */ private PartMonitor _myPartMonitor = null; protected TrackManager _trackData; private ChartComposite _myChartFrame; private Action _useOriginalColors; final private XYLineAndShapeRenderer _plotRenderer; final private Vector<SensorSeries> _selectedTracks; final private HashMap<SensorWrapper, SensorSeries> _trackIndex; private Layers _currentLayers; /** * helper - handle the selection a little better */ private SelectionHelper _selectionHelper; /** * listen out for new data being added or removed * */ protected DataListener _layerListener; protected ISelectionChangedListener _selectionChangeListener; protected ISelectionProvider _currentProvider; private Action _doSplit; private Action _trimToTrack; private Action _trimToSubjectTracks; /** * The constructor. */ public SensorFusionView() { _selectedTracks = new Vector<SensorSeries>(0, 1); _plotRenderer = new FusionPlotRenderer(this); _trackIndex = new HashMap<SensorWrapper, SensorSeries>(); } private SensorFusionView getMe() { return this; } protected void setupListeners() { _myPartMonitor = new PartMonitor(getSite().getWorkbenchWindow().getPartService()); _myPartMonitor.addPartListener(ISelectionProvider.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { // aah, just check it's not us if (part != getMe()) { if (_currentProvider != null) stopListeningTo(_currentProvider); startListeningTo((ISelectionProvider) part); } } }); _myPartMonitor.addPartListener(ISelectionProvider.class, PartMonitor.DEACTIVATED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { // aah, just check it's not is if (part != getMe()) { stopListeningTo((ISelectionProvider) part); } } }); _myPartMonitor.addPartListener(TrackManager.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { final TrackManager provider = (TrackManager) part; // is this different to our current one? if (provider != _trackData) storeDetails(provider, parentPart); } }); _myPartMonitor.addPartListener(TrackManager.class, PartMonitor.CLOSED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { if (part == _trackData) { _trackData = null; resetPlot(); } } }); _myPartMonitor.addPartListener(Layers.class, PartMonitor.ACTIVATED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { // is this different to our current one? if (part != _currentLayers) { if (_layerListener == null) _layerListener = new DataListener() { public void dataExtended(final Layers theData) { // redo the data recalculateData(); } public void dataModified(final Layers theData, final Layer changedLayer) { // redo the data recalculateData(); } public void dataReformatted(final Layers theData, final Layer changedLayer) { // redo the presentation redrawPlot(); } }; // ok, stop listening to the current one if (_currentLayers != null) stopListeningTo(_currentLayers); // and start listening to the new one startListeningTo((Layers) part); } } }); _myPartMonitor.addPartListener(Layers.class, PartMonitor.CLOSED, new PartMonitor.ICallback() { public void eventTriggered(final String type, final Object part, final IWorkbenchPart parentPart) { if (part == _currentLayers) { stopListeningTo((Layers) part); } } }); // ok we're all ready now. just try and see if the current part is valid _myPartMonitor.fireActivePart(getSite().getWorkbenchWindow().getActivePage()); } protected void startListeningTo(final Layers part) { _currentLayers = part; _currentLayers.addDataModifiedListener(_layerListener); _currentLayers.addDataReformattedListener(_layerListener); _currentLayers.addDataExtendedListener(_layerListener); } protected void stopListeningTo(final Layers part) { if (_currentLayers != null) { _currentLayers.removeDataModifiedListener(_layerListener); _currentLayers.removeDataReformattedListener(_layerListener); _currentLayers.removeDataExtendedListener(_layerListener); } _currentLayers = null; } protected void startListeningTo(final ISelectionProvider part) { _currentProvider = part; _currentProvider.addSelectionChangedListener(_selectionChangeListener); } protected void stopListeningTo(final ISelectionProvider part) { if (_currentProvider != null) _currentProvider.removeSelectionChangedListener(_selectionChangeListener); _currentProvider = null; } protected void storeDetails(final TrackManager provider, final IWorkbenchPart parentPart) { // ok, we've got a new plot to watch. better watch it... _trackData = provider; // clear our list _selectedTracks.removeAllElements(); recalculateData(); } private void recalculateData() { // which is the primary? final WatchableList primary = _trackData.getPrimaryTrack(); if (primary == null) { _myChartFrame.getChart().setTitle("Primary track missing"); _myChartFrame.getChart().getXYPlot().setDataset(null); return; } else _myChartFrame.getChart().setTitle(CHART_NAME); // check it's a track if (!(primary instanceof TrackWrapper)) { CorePlugin.logError(Status.WARNING, "Primary track not suitable for watching", null); } else { final TrackWrapper _primary = (TrackWrapper) primary; // and which are the secondaries? final WatchableList[] secondaries = _trackData.getSecondaryTracks(); // sort out the bearing tracks final TimeSeriesCollection newData = new TimeSeriesCollection(); DataSupport.tracksFor(_primary, secondaries, newData); DataSupport.sensorDataFor(_primary, newData, _trackIndex); // and now the sensor data _myChartFrame.getChart().getXYPlot().setDataset(newData); } } /** * This is a callback that will allow us to create the viewer and initialize * it. */ public void createPartControl(final Composite parent) { makeActions(); contributeToActionBars(); // and the selection provider bits _selectionHelper = new SelectionHelper(); getSite().setSelectionProvider(_selectionHelper); // declare our context sensitive help CorePlugin.declareContextHelp(parent, "org.mwc.debrief.help.BulkSensorData"); parent.setLayout(new FillLayout()); // ok, let's mockup the UI final JFreeChart myChart = DataSupport.createChart(null); myChart.setTitle(CHART_NAME); myChart.getXYPlot().setRenderer(_plotRenderer); // and the chart frame _myChartFrame = new ChartComposite(parent, SWT.NONE, myChart, 400, 600, 300, 200, 1800, 1800, true, true, true, true, true, true) { @Override protected Menu createPopupMenu(boolean arg0, boolean arg1, boolean arg2, boolean arg3) { // prevent the JFreeChart menu from opening return null; } @Override public void mouseUp(MouseEvent event) { super.mouseUp(event); JFreeChart c = getChart(); if (c != null) { c.setNotify(true); // force redraw } } }; _myChartFrame.setDisplayToolTips(false); _myChartFrame.setHorizontalAxisTrace(false); _myChartFrame.setVerticalAxisTrace(false); _myChartFrame.addChartMouseListener(new ChartMouseListener() { public void chartMouseClicked(final ChartMouseEvent event) { final ChartEntity entity = event.getEntity(); if (entity instanceof XYItemEntity) { final XYItemEntity xyi = (XYItemEntity) entity; final TimeSeriesCollection coll = (TimeSeriesCollection) xyi.getDataset(); final TacticalSeries ts = (TacticalSeries) coll .getSeries(((XYItemEntity) entity).getSeriesIndex()); if (ts instanceof SensorSeries) { final SensorSeries ss = (SensorSeries) ts; // check which mouse it si if (event.getTrigger().getButton() == 3) { // get the sensor itself final SensorWrapper sensor = ss.getSensor(); // get the parent layer final TrackWrapper parentLayer = (TrackWrapper) _trackData.getPrimaryTrack(); // ok, create ourselves a menu final MenuManager mmgr = new MenuManager(); // insert the relevant menu items RightClickSupport.getDropdownListFor(mmgr, new Editable[] { sensor }, null, new Layer[] { parentLayer }, _currentLayers, false); // get the SWT menu object for it final Menu thisM = mmgr.createContextMenu(_myChartFrame); // and display it thisM.setVisible(true); } else { // right, is ctrl-key pressed final int mods = event.getTrigger().getModifiers(); if ((mods & InputEvent.CTRL_MASK) == 0) { _selectedTracks.removeAllElements(); _selectedTracks.add(ss); } else { if (_selectedTracks.contains(ts)) _selectedTracks.remove(ts); else _selectedTracks.add(ss); } // and update the UI updatedSelection(); // ok, we need to redraw redrawPlot(); } } } else { _selectedTracks.removeAllElements(); // and update the UI updatedSelection(); // ok, we need to redraw redrawPlot(); } } public void chartMouseMoved(final ChartMouseEvent event) { } }); _selectionChangeListener = new ISelectionChangedListener() { @SuppressWarnings({ "rawtypes" }) public void selectionChanged(final SelectionChangedEvent event) { // right, see what it is final ISelection sel = event.getSelection(); if (sel instanceof StructuredSelection) { final StructuredSelection ss = (StructuredSelection) sel; final Iterator eles = ss.iterator(); boolean processingThese = false; while (eles.hasNext()) { final Object datum = eles.next(); if (datum instanceof EditableWrapper) { final EditableWrapper pw = (EditableWrapper) datum; final Editable ed = pw.getEditable(); if (ed instanceof SensorWrapper) { if (!processingThese) { processingThese = true; _selectedTracks.removeAllElements(); } final SensorSeries thisSeries = _trackIndex.get(ed); _selectedTracks.add(thisSeries); } } } if (processingThese) redrawPlot(); } } }; // and sort out the listeners setupListeners(); } @Override public void dispose() { stopListeningTo(_currentProvider); stopListeningTo(_currentLayers); _myPartMonitor.ditch(); super.dispose(); } protected void updatedSelection() { final Vector<EditableWrapper> wrappers = new Vector<EditableWrapper>(0, 1); final Iterator<SensorSeries> it = _selectedTracks.iterator(); while (it.hasNext()) { final SensorSeries ss = (SensorSeries) it.next(); final SensorWrapper sw = ss.getSensor(); final EditableWrapper ed = new EditableWrapper(sw); wrappers.add(ed); } if (wrappers.size() > 0) { // and provide the selection object final StructuredSelection trackSelection = new StructuredSelection(wrappers); setSelection(trackSelection); } } private void contributeToActionBars() { final IActionBars bars = getViewSite().getActionBars(); bars.getToolBarManager().add(_useOriginalColors); bars.getToolBarManager().add(new Separator()); bars.getToolBarManager().add(_doSplit); bars.getToolBarManager().add(_trimToTrack); bars.getToolBarManager().add(_trimToSubjectTracks); // and the help link bars.getToolBarManager().add(new Separator()); bars.getToolBarManager() .add(CorePlugin.createOpenHelpAction("org.mwc.debrief.help.BulkSensorData", null, this)); IMenuManager menu = getViewSite().getActionBars().getMenuManager(); menu.add(_useOriginalColors); bars.getToolBarManager().add(new Separator()); menu.add(_doSplit); menu.add(_trimToTrack); menu.add(_trimToSubjectTracks); menu.add(new Separator()); menu.add(CorePlugin.createOpenHelpAction("org.mwc.debrief.help.BulkSensorData", null, this)); } private void makeActions() { _useOriginalColors = new Action("Plot tracks using original colors", SWT.TOGGLE) { @Override public void run() { // we don't need to do any fancy processing. If we trigger redraw, // it will pick up the new value redrawPlot(); } }; _useOriginalColors.setImageDescriptor(Activator.getImageDescriptor("icons/ColorPalette.png")); _doSplit = new Action("Auto-split sensor segments", SWT.NONE) { @Override public void run() { // we don't need to do any fancy processing. If we trigger redraw, // it will pick up the new value splitTracks(); } }; _doSplit.setImageDescriptor(Activator.getImageDescriptor("icons/handaxe.png")); _trimToTrack = new Action("Trim to track period", SWT.NONE) { @Override public void run() { trimToTrack(); } }; _trimToTrack.setImageDescriptor(Activator.getImageDescriptor("icons/hourglass.png")); _trimToSubjectTracks = new Action("Remove sensor data unrelated to Secondary Tracks", SWT.NONE) { @Override public void run() { trimToSubjectTracks(); } }; _trimToSubjectTracks.setImageDescriptor(Activator.getImageDescriptor("icons/scissors.png")); } protected void splitTracks() { // ok, go get the primary track if (_trackData != null) { final TrackWrapper primary = (TrackWrapper) _trackData.getPrimaryTrack(); final BaseLayer sensors = primary.getSensors(); if (sensors instanceof SplittableLayer) { final SplittableLayer youSplitter = (SplittableLayer) sensors; IUndoableOperation op = new SplitSegmentsOperation(youSplitter, _currentLayers); CorePlugin.run(op); } } } protected void trimToTrack() { // ok, go get the primary track if (_trackData != null) { final TrackWrapper primary = (TrackWrapper) _trackData.getPrimaryTrack(); IUndoableOperation op = new TrimToTrackPeriodOperation(primary, _currentLayers); CorePlugin.run(op); } } protected void trimToSubjectTracks() { // ok, go get the primary track if (_trackData != null) { final TrackWrapper primary = (TrackWrapper) _trackData.getPrimaryTrack(); final WatchableList[] secondaries = _trackData.getSecondaryTracks(); IUndoableOperation op = new TrimToSubjectOperation(primary, secondaries, _currentLayers); CorePlugin.run(op); } } protected void redrawPlot() { if (_plotRenderer != null) { _plotRenderer.setSeriesShapesVisible(0, true); } } protected void resetPlot() { if (!_myChartFrame.isDisposed()) { _myChartFrame.getChart().getXYPlot().setDataset(null); _myChartFrame.getChart().setTitle("Pending"); } } // protected void resetData() // { // // final long _start = _primary.getStartDTG().getDate().getTime(); // final long _end = _primary.getEndDTG().getDate().getTime(); // // int ctr = 0; // // int MAX_SENSORS = 110; // for (int i = 0; i < MAX_SENSORS; i++) // { // final long _step = DataSupport.stepInterval(); // final long _thisStart = _start + DataSupport.delay(); // long _thisEnd = _thisStart + DataSupport.duration(); // _thisEnd = Math.min(_thisEnd, _end); // long _this = _thisStart; // // SensorWrapper sw = new SensorWrapper("sensor 3:" + i); // sw.setColor(null); // double theVal = Math.random() * 360; // while (_this < _thisEnd) // { // theVal = theVal - 1 + (Math.random() * 2); // SensorContactWrapper scw = new SensorContactWrapper(_primary.getName(), // new HiResDate(_this), null, theVal, null, null, null, null, // "some label:", 0, sw.getName()); // sw.add(scw); // ctr++; // _this += _step; // } // // _primary.add(sw); // // } // System.out.println("created " + ctr + " cuts"); // } /** * Passing the focus request to the viewer's control. */ public void setFocus() { } public void addSelectionChangedListener(final ISelectionChangedListener listener) { _selectionHelper.addSelectionChangedListener(listener); } public ISelection getSelection() { return _selectionHelper.getSelection(); } public void removeSelectionChangedListener(final ISelectionChangedListener listener) { _selectionHelper.removeSelectionChangedListener(listener); } public void setSelection(final ISelection selection) { _selectionHelper.fireNewSelection(selection); } public Vector<SensorSeries> getSelectedItems() { return _selectedTracks; } public boolean useOriginalColors() { return _useOriginalColors.isChecked(); } public HashMap<SensorWrapper, SensorSeries> getIndex() { return _trackIndex; } }