X-Git-Url: http://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2Ffunction%2Fautoplay%2FAutoplayFunction.java;fp=tim%2Fprune%2Ffunction%2Fautoplay%2FAutoplayFunction.java;h=acb71eed599f8894e8f04f5283f192fc7362a34f;hp=0000000000000000000000000000000000000000;hb=0a2480df5845e2d7190dfdec9b2653b1609e853d;hpb=2154b1969ac2995cca46546f217f53c066b0b749 diff --git a/tim/prune/function/autoplay/AutoplayFunction.java b/tim/prune/function/autoplay/AutoplayFunction.java new file mode 100644 index 0000000..acb71ee --- /dev/null +++ b/tim/prune/function/autoplay/AutoplayFunction.java @@ -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 set = new TreeSet(); + int numPoints = inTrack.getNumPoints(); + for (int i=0; i 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