1 package tim.prune.function.autoplay;
3 import java.awt.Component;
4 import java.awt.FlowLayout;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.awt.event.WindowAdapter;
8 import java.awt.event.WindowEvent;
9 import java.util.Iterator;
10 import java.util.TreeSet;
12 import javax.swing.BoxLayout;
13 import javax.swing.JButton;
14 import javax.swing.JCheckBox;
15 import javax.swing.JDialog;
16 import javax.swing.JLabel;
17 import javax.swing.JPanel;
20 import tim.prune.GenericFunction;
21 import tim.prune.I18nManager;
22 import tim.prune.data.Field;
23 import tim.prune.data.Timestamp;
24 import tim.prune.data.Track;
25 import tim.prune.gui.GuiGridLayout;
26 import tim.prune.gui.IconManager;
27 import tim.prune.gui.WholeNumberField;
30 * Function to handle the autoplay of a track
32 public class AutoplayFunction extends GenericFunction implements Runnable
35 private JDialog _dialog = null;
36 /** Entry field for number of seconds to autoplay for */
37 private WholeNumberField _durationField = null;
38 /** Checkbox for using point timestamps */
39 private JCheckBox _useTimestampsCheckbox = null;
40 /** Buttons for controlling autoplay */
41 private JButton _rewindButton = null, _pauseButton = null, _playButton = null;
42 /** Flag for recalculating all the times */
43 private boolean _needToRecalculate = true;
45 private PointList _pointList = null;
46 /** Flag to see if we're still running or not */
47 private boolean _running = false;
48 /** Remember the time we started playing */
49 private long _startTime = 0L;
54 * @param inApp App object
56 public AutoplayFunction(App inApp) {
61 public String getNameKey() {
62 return "function.autoplay";
72 _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
73 _dialog.setLocationRelativeTo(_parentFrame);
74 _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
75 _dialog.getContentPane().add(makeDialogComponents());
77 _dialog.setResizable(false);
78 _dialog.addWindowListener(new WindowAdapter() {
79 public void windowClosing(WindowEvent e) {
81 super.windowClosing(e);
85 // Don't select any point
86 _app.getTrackInfo().selectPoint(-1);
88 enableButtons(false, true); // can't pause, can play
89 // MAYBE: reset duration if it's too long
90 // Disable point checkbox if there aren't any times
91 final boolean hasTimes = _app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP);
92 _useTimestampsCheckbox.setEnabled(hasTimes);
95 _useTimestampsCheckbox.setSelected(false);
98 _needToRecalculate = true;
99 _dialog.setVisible(true);
104 * Create dialog components
105 * @return Panel containing all gui elements in dialog
107 private Component makeDialogComponents()
109 JPanel dialogPanel = new JPanel();
110 dialogPanel.setLayout(new BoxLayout(dialogPanel, BoxLayout.Y_AXIS));
112 JPanel durationPanel = new JPanel();
113 GuiGridLayout grid = new GuiGridLayout(durationPanel);
114 grid.add(new JLabel(I18nManager.getText("dialog.autoplay.duration") + " :"));
115 _durationField = new WholeNumberField(3);
116 _durationField.setValue(60); // default is one minute
117 _durationField.addActionListener(new ActionListener() {
118 public void actionPerformed(ActionEvent e) {
122 grid.add(_durationField);
123 durationPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
124 dialogPanel.add(durationPanel);
126 _useTimestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.autoplay.usetimestamps"));
127 _useTimestampsCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
128 dialogPanel.add(_useTimestampsCheckbox);
129 _useTimestampsCheckbox.addActionListener(new ActionListener() {
130 public void actionPerformed(ActionEvent e) {
135 JPanel buttonPanel = new JPanel();
136 buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
137 buttonPanel.add(_rewindButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_REWIND)));
138 _rewindButton.setToolTipText(I18nManager.getText("dialog.autoplay.rewind"));
139 _rewindButton.addActionListener(new ActionListener() {
140 public void actionPerformed(ActionEvent arg0) {
144 buttonPanel.add(_pauseButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_PAUSE)));
145 _pauseButton.setToolTipText(I18nManager.getText("dialog.autoplay.pause"));
146 _pauseButton.addActionListener(new ActionListener() {
147 public void actionPerformed(ActionEvent arg0) {
151 buttonPanel.add(_playButton = new JButton(IconManager.getImageIcon(IconManager.AUTOPLAY_PLAY)));
152 _playButton.setToolTipText(I18nManager.getText("dialog.autoplay.play"));
153 _playButton.addActionListener(new ActionListener() {
154 public void actionPerformed(ActionEvent arg0) {
158 buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
159 dialogPanel.add(buttonPanel);
164 * React to a change to either the duration or the checkbox
166 private void onParamsChanged()
169 enableButtons(false, _durationField.getValue() > 0);
170 _needToRecalculate = true;
174 * React to rewind button pressed - stop and go back to the first point
176 private void onRewindPressed()
178 //System.out.println("Rewind! Stop thread if playing");
180 if (_pointList != null)
183 _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
188 * React to pause button pressed - stop scrolling but maintain position
190 private void onPausePressed()
192 //System.out.println("Pause! Stop thread if playing");
194 enableButtons(false, true);
198 * React to play button being pressed - either start or resume
200 private void onPlayPressed()
202 //System.out.println("Play!");
203 if (_needToRecalculate) {
206 enableButtons(true, false);
207 if (_pointList.isAtStart() || _pointList.isFinished())
210 _startTime = System.currentTimeMillis();
214 // Get current millis from pointList, reset _startTime
215 _startTime = System.currentTimeMillis() - _pointList.getCurrentMilliseconds();
217 new Thread(this).start();
221 * Recalculate the times using the dialog settings
223 private void recalculateTimes()
225 //System.out.println("Recalculate using params " + _durationField.getValue()
226 // + " and " + (_useTimestampsCheckbox.isSelected() ? "times" : "indexes"));
227 if (_useTimestampsCheckbox.isSelected()) {
228 _pointList = generatePointListUsingTimes(_app.getTrackInfo().getTrack(), _durationField.getValue());
231 _pointList = generatePointListUsingIndexes(_app.getTrackInfo().getTrack().getNumPoints(), _durationField.getValue());
233 _needToRecalculate = false;
237 * Enable and disable the pause and play buttons
238 * @param inCanPause true to enable pause button
239 * @param inCanPlay true to enable play button
241 private void enableButtons(boolean inCanPause, boolean inCanPlay)
243 _pauseButton.setEnabled(inCanPause);
244 _playButton.setEnabled(inCanPlay);
248 * Generate a points list based just on the point timestamps
249 * (points without timestamps will be ignored)
250 * @param inTrack track object
251 * @param inDuration number of seconds to play
252 * @return PointList object
254 private static PointList generatePointListUsingTimes(Track inTrack, int inDuration)
256 // Make a Set of all the points with timestamps and sort them
257 TreeSet<PointInfo> set = new TreeSet<PointInfo>();
258 int numPoints = inTrack.getNumPoints();
259 for (int i=0; i<numPoints; i++)
261 PointInfo info = new PointInfo(inTrack.getPoint(i), i);
262 if (info.getTimestamp() != null) {
266 // For each point, keep track of the time since the previous time
267 Timestamp previousTime = null;
268 long trackMillis = 0L;
269 // Copy info to point list
270 numPoints = set.size();
271 PointList list = new PointList(numPoints);
272 Iterator<PointInfo> it = set.iterator();
275 PointInfo info = it.next();
276 if (previousTime != null)
278 if (info.getSegmentFlag()) {
279 trackMillis += 1000; // just add a second if it's a new segment
282 trackMillis += (info.getTimestamp().getMillisecondsSince(previousTime));
285 previousTime = info.getTimestamp();
286 list.setPoint(trackMillis, info.getIndex());
288 // Now normalize the list to the requested length
289 list.normalize(inDuration);
295 * Generate a points list based just on indexes, ignoring timestamps
296 * @param inNumPoints number of points in track
297 * @param inDuration number of seconds to play
298 * @return PointList object
300 private static PointList generatePointListUsingIndexes(int inNumPoints, int inDuration)
302 // simple case, just take all the points in the track
303 PointList list = new PointList(inNumPoints);
304 // Add each of the points in turn
305 for (int i=0; i<inNumPoints; i++)
309 list.normalize(inDuration);
314 * Run method, for scrolling in separate thread
319 _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
320 while (_running && !_pointList.isFinished())
322 _pointList.set(System.currentTimeMillis() - _startTime);
323 final int pointIndex = _pointList.getCurrentPointIndex();
324 //System.out.println("Set point index to " + pointIndex);
325 _app.getTrackInfo().selectPoint(pointIndex);
326 long waitInterval = _pointList.getMillisUntilNextPoint(System.currentTimeMillis() - _startTime);
327 if (waitInterval < 20) {waitInterval = 20;}
328 try {Thread.sleep(waitInterval);}
329 catch (InterruptedException ie) {}
332 enableButtons(false, true);