]> gitweb.fperrin.net Git - GpsPrune.git/blob - src/tim/prune/function/autoplay/AutoplayFunction.java
acb71eed599f8894e8f04f5283f192fc7362a34f
[GpsPrune.git] / src / tim / prune / function / autoplay / AutoplayFunction.java
1 package tim.prune.function.autoplay;
2
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;
11
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;
18
19 import tim.prune.App;
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;
28
29 /**
30  * Function to handle the autoplay of a track
31  */
32 public class AutoplayFunction extends GenericFunction implements Runnable
33 {
34         /** Dialog */
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;
44         /** Point list */
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;
50
51
52         /**
53          * Constructor
54          * @param inApp App object
55          */
56         public AutoplayFunction(App inApp) {
57                 super(inApp);
58         }
59
60         @Override
61         public String getNameKey() {
62                 return "function.autoplay";
63         }
64
65         /**
66          * Begin the function
67          */
68         public void begin()
69         {
70                 if (_dialog == null)
71                 {
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());
76                         _dialog.pack();
77                         _dialog.setResizable(false);
78                         _dialog.addWindowListener(new WindowAdapter() {
79                                 public void windowClosing(WindowEvent e) {
80                                         _running = false;
81                                         super.windowClosing(e);
82                                 }
83                         });
84                 }
85                 // Don't select any point
86                 _app.getTrackInfo().selectPoint(-1);
87                 // enable buttons
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);
93                 if (!hasTimes)
94                 {
95                         _useTimestampsCheckbox.setSelected(false);
96                 }
97
98                 _needToRecalculate = true;
99                 _dialog.setVisible(true);
100         }
101
102
103         /**
104          * Create dialog components
105          * @return Panel containing all gui elements in dialog
106          */
107         private Component makeDialogComponents()
108         {
109                 JPanel dialogPanel = new JPanel();
110                 dialogPanel.setLayout(new BoxLayout(dialogPanel, BoxLayout.Y_AXIS));
111                 // Duration panel
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) {
119                                 onParamsChanged();
120                         }
121                 });
122                 grid.add(_durationField);
123                 durationPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
124                 dialogPanel.add(durationPanel);
125                 // Checkbox
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) {
131                                 onParamsChanged();
132                         }
133                 });
134                 // Button panel
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) {
141                                 onRewindPressed();
142                         }
143                 });
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) {
148                                 onPausePressed();
149                         }
150                 });
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) {
155                                 onPlayPressed();
156                         }
157                 });
158                 buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
159                 dialogPanel.add(buttonPanel);
160                 return dialogPanel;
161         }
162
163         /**
164          * React to a change to either the duration or the checkbox
165          */
166         private void onParamsChanged()
167         {
168                 onRewindPressed();
169                 enableButtons(false, _durationField.getValue() > 0);
170                 _needToRecalculate = true;
171         }
172
173         /**
174          * React to rewind button pressed - stop and go back to the first point
175          */
176         private void onRewindPressed()
177         {
178                 //System.out.println("Rewind!  Stop thread if playing");
179                 _running = false;
180                 if (_pointList != null)
181                 {
182                         _pointList.set(0);
183                         _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
184                 }
185         }
186
187         /**
188          * React to pause button pressed - stop scrolling but maintain position
189          */
190         private void onPausePressed()
191         {
192                 //System.out.println("Pause!  Stop thread if playing");
193                 _running = false;
194                 enableButtons(false, true);
195         }
196
197         /**
198          * React to play button being pressed - either start or resume
199          */
200         private void onPlayPressed()
201         {
202                 //System.out.println("Play!");
203                 if (_needToRecalculate) {
204                         recalculateTimes();
205                 }
206                 enableButtons(true, false);
207                 if (_pointList.isAtStart() || _pointList.isFinished())
208                 {
209                         _pointList.set(0);
210                         _startTime = System.currentTimeMillis();
211                 }
212                 else
213                 {
214                         // Get current millis from pointList, reset _startTime
215                         _startTime = System.currentTimeMillis() - _pointList.getCurrentMilliseconds();
216                 }
217                 new Thread(this).start();
218         }
219
220         /**
221          * Recalculate the times using the dialog settings
222          */
223         private void recalculateTimes()
224         {
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());
229                 }
230                 else {
231                         _pointList = generatePointListUsingIndexes(_app.getTrackInfo().getTrack().getNumPoints(), _durationField.getValue());
232                 }
233                 _needToRecalculate = false;
234         }
235
236         /**
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
240          */
241         private void enableButtons(boolean inCanPause, boolean inCanPlay)
242         {
243                 _pauseButton.setEnabled(inCanPause);
244                 _playButton.setEnabled(inCanPlay);
245         }
246
247         /**
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
253          */
254         private static PointList generatePointListUsingTimes(Track inTrack, int inDuration)
255         {
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++)
260                 {
261                         PointInfo info = new PointInfo(inTrack.getPoint(i), i);
262                         if (info.getTimestamp() != null) {
263                                 set.add(info);
264                         }
265                 }
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();
273                 while (it.hasNext())
274                 {
275                         PointInfo info = it.next();
276                         if (previousTime != null)
277                         {
278                                 if (info.getSegmentFlag()) {
279                                         trackMillis += 1000; // just add a second if it's a new segment
280                                 }
281                                 else {
282                                         trackMillis += (info.getTimestamp().getMillisecondsSince(previousTime));
283                                 }
284                         }
285                         previousTime = info.getTimestamp();
286                         list.setPoint(trackMillis, info.getIndex());
287                 }
288                 // Now normalize the list to the requested length
289                 list.normalize(inDuration);
290                 return list;
291         }
292
293
294         /**
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
299          */
300         private static PointList generatePointListUsingIndexes(int inNumPoints, int inDuration)
301         {
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++)
306                 {
307                         list.setPoint(i, i);
308                 }
309                 list.normalize(inDuration);
310                 return list;
311         }
312
313         /**
314          * Run method, for scrolling in separate thread
315          */
316         public void run()
317         {
318                 _running = true;
319                 _app.getTrackInfo().selectPoint(_pointList.getCurrentPointIndex());
320                 while (_running && !_pointList.isFinished())
321                 {
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) {}
330                 }
331                 _running = false;
332                 enableButtons(false, true);
333         }
334 }