]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/function/autoplay/AutoplayFunction.java
Version 18, July 2015
[GpsPrune.git] / tim / prune / function / autoplay / AutoplayFunction.java
diff --git a/tim/prune/function/autoplay/AutoplayFunction.java b/tim/prune/function/autoplay/AutoplayFunction.java
new file mode 100644 (file)
index 0000000..acb71ee
--- /dev/null
@@ -0,0 +1,334 @@
+package tim.prune.function.autoplay;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Field;
+import tim.prune.data.Timestamp;
+import tim.prune.data.Track;
+import tim.prune.gui.GuiGridLayout;
+import tim.prune.gui.IconManager;
+import tim.prune.gui.WholeNumberField;
+
+/**
+ * Function to handle the autoplay of a track
+ */
+public class AutoplayFunction extends GenericFunction implements Runnable
+{
+       /** Dialog */
+       private JDialog _dialog = null;
+       /** Entry field for number of seconds to autoplay for */
+       private WholeNumberField _durationField = null;
+       /** Checkbox for using point timestamps */
+       private JCheckBox _useTimestampsCheckbox = null;
+       /** Buttons for controlling autoplay */
+       private JButton _rewindButton = null, _pauseButton = null, _playButton = null;
+       /** Flag for recalculating all the times */
+       private boolean _needToRecalculate = true;
+       /** Point list */
+       private PointList _pointList = null;
+       /** Flag to see if we're still running or not */
+       private boolean _running = false;
+       /** Remember the time we started playing */
+       private long _startTime = 0L;
+
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public AutoplayFunction(App inApp) {
+               super(inApp);
+       }
+
+       @Override
+       public String getNameKey() {
+               return "function.autoplay";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+                       _dialog.getContentPane().add(makeDialogComponents());
+                       _dialog.pack();
+                       _dialog.setResizable(false);
+                       _dialog.addWindowListener(new WindowAdapter() {
+                               public void windowClosing(WindowEvent e) {
+                                       _running = false;
+                                       super.windowClosing(e);
+                               }
+                       });
+               }
+               // Don't select any point
+               _app.getTrackInfo().selectPoint(-1);
+               // enable buttons
+               enableButtons(false, true); // can't pause, can play
+               // MAYBE: reset duration if it's too long
+               // Disable point checkbox if there aren't any times
+               final boolean hasTimes = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
+               _useTimestampsCheckbox.setEnabled(hasTimes);
+               if (!hasTimes)
+               {
+                       _useTimestampsCheckbox.setSelected(false);
+               }
+
+               _needToRecalculate = true;
+               _dialog.setVisible(true);
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private Component makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BoxLayout(dialogPanel, BoxLayout.Y_AXIS));
+               // Duration panel
+               JPanel durationPanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(durationPanel);
+               grid.add(new JLabel(I18nManager.getText("dialog.autoplay.duration") + " :"));
+               _durationField = new WholeNumberField(3);
+               _durationField.setValue(60); // default is one minute
+               _durationField.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               onParamsChanged();
+                       }
+               });
+               grid.add(_durationField);
+               durationPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
+               dialogPanel.add(durationPanel);
+               // Checkbox
+               _useTimestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.autoplay.usetimestamps"));
+               _useTimestampsCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
+               dialogPanel.add(_useTimestampsCheckbox);
+               _useTimestampsCheckbox.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               onParamsChanged();
+                       }
+               });
+               // Button panel
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               buttonPanel.add(_rewindButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_REWIND)));
+               _rewindButton.setToolTipText(I18nManager.getText("dialog.autoplay.rewind"));
+               _rewindButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               onRewindPressed();
+                       }
+               });
+               buttonPanel.add(_pauseButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_PAUSE)));
+               _pauseButton.setToolTipText(I18nManager.getText("dialog.autoplay.pause"));
+               _pauseButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               onPausePressed();
+                       }
+               });
+               buttonPanel.add(_playButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_PLAY)));
+               _playButton.setToolTipText(I18nManager.getText("dialog.autoplay.play"));
+               _playButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               onPlayPressed();
+                       }
+               });
+               buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
+               dialogPanel.add(buttonPanel);
+               return dialogPanel;
+       }
+
+       /**
+        * React to a change to either the duration or the checkbox
+        */
+       private void onParamsChanged()
+       {
+               onRewindPressed();
+               enableButtons(false, _durationField.getValue() > 0);
+               _needToRecalculate = true;
+       }
+
+       /**
+        * React to rewind button pressed - stop and go back to the first point
+        */
+       private void onRewindPressed()
+       {
+               //System.out.println("Rewind!  Stop thread if playing");
+               _running = false;
+               if (_pointList != null)
+               {
+                       _pointList.set(0);
+                       _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
+               }
+       }
+
+       /**
+        * React to pause button pressed - stop scrolling but maintain position
+        */
+       private void onPausePressed()
+       {
+               //System.out.println("Pause!  Stop thread if playing");
+               _running = false;
+               enableButtons(false, true);
+       }
+
+       /**
+        * React to play button being pressed - either start or resume
+        */
+       private void onPlayPressed()
+       {
+               //System.out.println("Play!");
+               if (_needToRecalculate) {
+                       recalculateTimes();
+               }
+               enableButtons(true, false);
+               if (_pointList.isAtStart() || _pointList.isFinished())
+               {
+                       _pointList.set(0);
+                       _startTime = System.currentTimeMillis();
+               }
+               else
+               {
+                       // Get current millis from pointList, reset _startTime
+                       _startTime = System.currentTimeMillis() - _pointList.getCurrentMilliseconds();
+               }
+               new Thread(this).start();
+       }
+
+       /**
+        * Recalculate the times using the dialog settings
+        */
+       private void recalculateTimes()
+       {
+               //System.out.println("Recalculate using params " + _durationField.getValue()
+               //      + " and " + (_useTimestampsCheckbox.isSelected() ? "times" : "indexes"));
+               if (_useTimestampsCheckbox.isSelected()) {
+                       _pointList = generatePointListUsingTimes(_app.getTrackInfo().getTrack(), _durationField.getValue());
+               }
+               else {
+                       _pointList = generatePointListUsingIndexes(_app.getTrackInfo().getTrack().getNumPoints(), _durationField.getValue());
+               }
+               _needToRecalculate = false;
+       }
+
+       /**
+        * Enable and disable the pause and play buttons
+        * @param inCanPause true to enable pause button
+        * @param inCanPlay  true to enable play button
+        */
+       private void enableButtons(boolean inCanPause, boolean inCanPlay)
+       {
+               _pauseButton.setEnabled(inCanPause);
+               _playButton.setEnabled(inCanPlay);
+       }
+
+       /**
+        * Generate a points list based just on the point timestamps
+        * (points without timestamps will be ignored)
+        * @param inTrack track object
+        * @param inDuration number of seconds to play
+        * @return PointList object
+        */
+       private static PointList generatePointListUsingTimes(Track inTrack, int inDuration)
+       {
+               // Make a Set of all the points with timestamps and sort them
+               TreeSet<PointInfo> set = new TreeSet<PointInfo>();
+               int numPoints = inTrack.getNumPoints();
+               for (int i=0; i<numPoints; i++)
+               {
+                       PointInfo info = new PointInfo(inTrack.getPoint(i), i);
+                       if (info.getTimestamp() != null) {
+                               set.add(info);
+                       }
+               }
+               // For each point, keep track of the time since the previous time
+               Timestamp previousTime = null;
+               long trackMillis = 0L;
+               // Copy info to point list
+               numPoints = set.size();
+               PointList list = new PointList(numPoints);
+               Iterator<PointInfo> it = set.iterator();
+               while (it.hasNext())
+               {
+                       PointInfo info = it.next();
+                       if (previousTime != null)
+                       {
+                               if (info.getSegmentFlag()) {
+                                       trackMillis += 1000; // just add a second if it's a new segment
+                               }
+                               else {
+                                       trackMillis += (info.getTimestamp().getMillisecondsSince(previousTime));
+                               }
+                       }
+                       previousTime = info.getTimestamp();
+                       list.setPoint(trackMillis, info.getIndex());
+               }
+               // Now normalize the list to the requested length
+               list.normalize(inDuration);
+               return list;
+       }
+
+
+       /**
+        * Generate a points list based just on indexes, ignoring timestamps
+        * @param inNumPoints number of points in track
+        * @param inDuration number of seconds to play
+        * @return PointList object
+        */
+       private static PointList generatePointListUsingIndexes(int inNumPoints, int inDuration)
+       {
+               // simple case, just take all the points in the track
+               PointList list = new PointList(inNumPoints);
+               // Add each of the points in turn
+               for (int i=0; i<inNumPoints; i++)
+               {
+                       list.setPoint(i, i);
+               }
+               list.normalize(inDuration);
+               return list;
+       }
+
+       /**
+        * Run method, for scrolling in separate thread
+        */
+       public void run()
+       {
+               _running = true;
+               _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
+               while (_running && !_pointList.isFinished())
+               {
+                       _pointList.set(System.currentTimeMillis() - _startTime);
+                       final int pointIndex = _pointList.getCurrentPointIndex();
+                       //System.out.println("Set point index to " + pointIndex);
+                       _app.getTrackInfo().selectPoint(pointIndex);
+                       long waitInterval = _pointList.getMillisUntilNextPoint(System.currentTimeMillis() - _startTime);
+                       if (waitInterval < 20) {waitInterval = 20;}
+                       try {Thread.sleep(waitInterval);}
+                       catch (InterruptedException ie) {}
+               }
+               _running = false;
+               enableButtons(false, true);
+       }
+}