]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/App.java
Version 1, September 2006
[GpsPrune.git] / tim / prune / App.java
1 package tim.prune;
2
3 import java.util.EmptyStackException;
4 import java.util.Stack;
5
6 import javax.swing.JFrame;
7 import javax.swing.JOptionPane;
8
9 import tim.prune.data.DataPoint;
10 import tim.prune.data.Field;
11 import tim.prune.data.Track;
12 import tim.prune.data.TrackInfo;
13 import tim.prune.gui.MenuManager;
14 import tim.prune.gui.UndoManager;
15 import tim.prune.load.FileLoader;
16 import tim.prune.save.FileSaver;
17 import tim.prune.save.KmlExporter;
18 import tim.prune.undo.UndoCompress;
19 import tim.prune.undo.UndoDeleteDuplicates;
20 import tim.prune.undo.UndoDeletePoint;
21 import tim.prune.undo.UndoDeleteRange;
22 import tim.prune.undo.UndoException;
23 import tim.prune.undo.UndoInsert;
24 import tim.prune.undo.UndoLoad;
25 import tim.prune.undo.UndoOperation;
26 import tim.prune.undo.UndoRearrangeWaypoints;
27 import tim.prune.undo.UndoReverseSection;
28
29
30 /**
31  * Main controller for the application
32  */
33 public class App
34 {
35         // Instance variables
36         private JFrame _frame = null;
37         private Track _track = null;
38         private TrackInfo _trackInfo = null;
39         private int _lastSavePosition = 0;
40         private MenuManager _menuManager = null;
41         private FileLoader _fileLoader = null;
42         private Stack _undoStack = null;
43         private UpdateMessageBroker _broker = null;
44         private boolean _reversePointsConfirmed = false;
45
46         // Constants
47         public static final int REARRANGE_TO_START   = 0;
48         public static final int REARRANGE_TO_END     = 1;
49         public static final int REARRANGE_TO_NEAREST = 2;
50
51
52         // TODO: Make waypoint window to allow list of waypoints, edit names etc
53
54         /**
55          * Constructor
56          * @param inFrame frame object for application
57          * @param inBroker message broker
58          */
59         public App(JFrame inFrame, UpdateMessageBroker inBroker)
60         {
61                 _frame = inFrame;
62                 _undoStack = new Stack();
63                 _broker = inBroker;
64                 _track = new Track(_broker);
65                 _trackInfo = new TrackInfo(_track, _broker);
66         }
67
68
69         /**
70          * @return the current TrackInfo
71          */
72         public TrackInfo getTrackInfo()
73         {
74                 return _trackInfo;
75         }
76
77         /**
78          * Check if the application has unsaved data
79          * @return true if data is unsaved, false otherwise
80          */
81         public boolean hasDataUnsaved()
82         {
83                 return _undoStack.size() > _lastSavePosition;
84         }
85
86         /**
87          * @return the undo stack
88          */
89         public Stack getUndoStack()
90         {
91                 return _undoStack;
92         }
93
94         /**
95          * Set the MenuManager object to be informed about changes
96          * @param inManager MenuManager object
97          */
98         public void setMenuManager(MenuManager inManager)
99         {
100                 _menuManager = inManager;
101         }
102
103
104         /**
105          * Open a file containing track or waypoint data
106          */
107         public void openFile()
108         {
109                 if (_fileLoader == null)
110                         _fileLoader = new FileLoader(this, _frame);
111                 _fileLoader.openFile();
112         }
113
114
115         /**
116          * Save the file in the selected format
117          */
118         public void saveFile()
119         {
120                 if (_track == null)
121                 {
122                         JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
123                                 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
124                 }
125                 else
126                 {
127                         FileSaver saver = new FileSaver(this, _frame, _track);
128                         saver.showDialog(_fileLoader.getLastUsedDelimiter());
129                 }
130         }
131
132
133         /**
134          * Export track data as Kml
135          */
136         public void exportKml()
137         {
138                 if (_track == null)
139                 {
140                         JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
141                                 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
142                 }
143                 else
144                 {
145                         KmlExporter exporter = new KmlExporter(this, _frame, _track);
146                         exporter.showDialog();
147                 }
148         }
149
150
151         /**
152          * Exit the application if confirmed
153          */
154         public void exit()
155         {
156                 // check if ok to exit
157                 Object[] buttonTexts = {I18nManager.getText("button.exit"), I18nManager.getText("button.cancel")};
158                 if (!hasDataUnsaved()
159                         || JOptionPane.showOptionDialog(_frame, I18nManager.getText("dialog.exit.confirm.text"),
160                                 I18nManager.getText("dialog.exit.confirm.title"), JOptionPane.YES_NO_OPTION,
161                                 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
162                         == JOptionPane.YES_OPTION)
163                 {
164                         System.exit(0);
165                 }
166         }
167
168
169         /**
170          * Delete the currently selected point
171          */
172         public void deleteCurrentPoint()
173         {
174                 if (_track != null)
175                 {
176                         DataPoint currentPoint = _trackInfo.getCurrentPoint();
177                         if (currentPoint != null)
178                         {
179                                 // add information to undo stack
180                                 int pointIndex = _trackInfo.getSelection().getCurrentPointIndex();
181                                 UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint);
182                                 // call track to delete point
183                                 if (_trackInfo.deletePoint())
184                                 {
185                                         _undoStack.push(undo);
186                                 }
187                         }
188                 }
189         }
190
191
192         /**
193          * Delete the currently selected range
194          */
195         public void deleteSelectedRange()
196         {
197                 if (_track != null)
198                 {
199                         // add information to undo stack
200                         UndoOperation undo = new UndoDeleteRange(_trackInfo);
201                         // call track to delete point
202                         if (_trackInfo.deleteRange())
203                         {
204                                 _undoStack.push(undo);
205                         }
206                 }
207         }
208
209
210         /**
211          * Delete all the duplicate points in the track
212          */
213         public void deleteDuplicates()
214         {
215                 if (_track != null)
216                 {
217                         // Save undo information
218                         UndoOperation undo = new UndoDeleteDuplicates(_track);
219                         // tell track to do it
220                         int numDeleted = _trackInfo.deleteDuplicates();
221                         if (numDeleted > 0)
222                         {
223                                 _undoStack.add(undo);
224                                 String message = null;
225                                 if (numDeleted == 1)
226                                 {
227                                         message = "1 " + I18nManager.getText("dialog.deleteduplicates.single.text");
228                                 }
229                                 else
230                                 {
231                                         message = "" + numDeleted + " " + I18nManager.getText("dialog.deleteduplicates.multi.text");
232                                 }
233                                 JOptionPane.showMessageDialog(_frame, message,
234                                         I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
235                         }
236                         else
237                         {
238                                 JOptionPane.showMessageDialog(_frame,
239                                         I18nManager.getText("dialog.deleteduplicates.nonefound"),
240                                         I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
241                         }
242                 }
243         }
244
245
246         /**
247          * Compress the track
248          */
249         public void compressTrack()
250         {
251                 UndoCompress undo = new UndoCompress(_track);
252                 // Get compression parameter
253                 Object compParam = JOptionPane.showInputDialog(_frame,
254                         I18nManager.getText("dialog.compresstrack.parameter.text"),
255                         I18nManager.getText("dialog.compresstrack.title"),
256                         JOptionPane.QUESTION_MESSAGE, null, null, "100");
257                 int compNumber = parseNumber(compParam);
258                 if (compNumber <= 0) return;
259                 // call track to do compress
260                 int numPointsDeleted = _trackInfo.compress(compNumber);
261                 // add to undo stack if successful
262                 if (numPointsDeleted > 0)
263                 {
264                         undo.setNumPointsDeleted(numPointsDeleted);
265                         _undoStack.add(undo);
266                         JOptionPane.showMessageDialog(_frame,
267                                 I18nManager.getText("dialog.compresstrack.text") + " - "
268                                  + numPointsDeleted + " "
269                                  + (numPointsDeleted==1?I18nManager.getText("dialog.compresstrack.single.text"):I18nManager.getText("dialog.compresstrack.multi.text")),
270                                 I18nManager.getText("dialog.compresstrack.title"), JOptionPane.INFORMATION_MESSAGE);
271                 }
272                 else
273                 {
274                         JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.compresstrack.nonefound"),
275                                 I18nManager.getText("dialog.compresstrack.title"), JOptionPane.WARNING_MESSAGE);
276                 }
277         }
278
279
280         /**
281          * Reverse a section of the track
282          */
283         public void reverseRange()
284         {
285                 // check whether Timestamp field exists, and if so confirm reversal
286                 int selStart = _trackInfo.getSelection().getStart();
287                 int selEnd = _trackInfo.getSelection().getEnd();
288                 if (!_track.hasData(Field.TIMESTAMP, selStart, selEnd)
289                         || _reversePointsConfirmed
290                         || (JOptionPane.showConfirmDialog(_frame,
291                                  I18nManager.getText("dialog.confirmreversetrack.text"),
292                                  I18nManager.getText("dialog.confirmreversetrack.title"),
293                                  JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION && (_reversePointsConfirmed = true)))
294                 {
295                         UndoReverseSection undo = new UndoReverseSection(selStart, selEnd);
296                         // call track to reverse range
297                         if (_track.reverseRange(selStart, selEnd))
298                         {
299                                 _undoStack.add(undo);
300                         }
301                 }
302         }
303
304
305         /**
306          * Interpolate the two selected points
307          */
308         public void interpolateSelection()
309         {
310                 // Get number of points to add
311                 Object numPointsStr = JOptionPane.showInputDialog(_frame,
312                         I18nManager.getText("dialog.interpolate.parameter.text"),
313                         I18nManager.getText("dialog.interpolate.title"),
314                         JOptionPane.QUESTION_MESSAGE, null, null, "");
315                 int numPoints = parseNumber(numPointsStr);
316                 if (numPoints <= 0) return;
317
318                 UndoInsert undo = new UndoInsert(_trackInfo.getSelection().getStart() + 1,
319                         numPoints);
320                 // call track to interpolate
321                 if (_trackInfo.interpolate(numPoints))
322                 {
323                         _undoStack.add(undo);
324                 }
325         }
326
327
328         /**
329          * Rearrange the waypoints into track order
330          */
331         public void rearrangeWaypoints(int inFunction)
332         {
333                 UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(_track);
334                 boolean success = false;
335                 if (inFunction == REARRANGE_TO_START || inFunction == REARRANGE_TO_END)
336                 {
337                         // Collect the waypoints to the start or end of the track
338                         success = _track.collectWaypoints(inFunction == REARRANGE_TO_START);
339                 }
340                 else
341                 {
342                         // Interleave the waypoints into track order
343                         success = _track.interleaveWaypoints();
344                 }
345                 if (success)
346                 {
347                         _undoStack.add(undo);
348                 }
349                 else
350                 {
351                         JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.rearrange.noop"),
352                                 I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
353                 }
354         }
355
356
357         /**
358          * Open a new window with the 3d view
359          */
360         public void show3dWindow()
361         {
362                 // TODO: open 3d view window
363                 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.function.notimplemented"),
364                         I18nManager.getText("error.function.notimplemented.title"), JOptionPane.WARNING_MESSAGE);
365         }
366
367
368         /**
369          * Select all points
370          */
371         public void selectAll()
372         {
373                 _trackInfo.getSelection().select(0, 0, _track.getNumPoints()-1);
374         }
375
376         /**
377          * Select nothing
378          */
379         public void selectNone()
380         {
381                 _trackInfo.getSelection().clearAll();
382         }
383
384         /**
385          * Receive loaded data and optionally merge with current Track
386          * @param inFieldArray array of fields
387          * @param inDataArray array of data
388          */
389         public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, int inAltFormat, String inFilename)
390         {
391                 // Decide whether to load or append
392                 if (_track != null && _track.getNumPoints() > 0)
393                 {
394                         // ask whether to replace or append
395                         int answer = JOptionPane.showConfirmDialog(_frame,
396                                 I18nManager.getText("dialog.openappend.text"),
397                                 I18nManager.getText("dialog.openappend.title"),
398                                 JOptionPane.YES_NO_CANCEL_OPTION);
399                         if (answer == JOptionPane.YES_OPTION)
400                         {
401                                 // append data to current Track
402                                 Track loadedTrack = new Track(_broker);
403                                 loadedTrack.load(inFieldArray, inDataArray, inAltFormat);
404                                 _undoStack.add(new UndoLoad(_track.getNumPoints(), loadedTrack.getNumPoints()));
405                                 _track.combine(loadedTrack);
406                                 _trackInfo.getFileInfo().addFile();
407                         }
408                         else if (answer == JOptionPane.NO_OPTION)
409                         {
410                                 // Don't append, replace data
411                                 _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length));
412                                 _lastSavePosition = _undoStack.size();
413                                 _trackInfo.loadTrack(inFieldArray, inDataArray, inAltFormat);
414                                 _trackInfo.getFileInfo().setFile(inFilename);
415                         }
416                 }
417                 else
418                 {
419                         // currently no data held, so use received data
420                         _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length));
421                         _lastSavePosition = _undoStack.size();
422                         _trackInfo.loadTrack(inFieldArray, inDataArray, inAltFormat);
423                         _trackInfo.getFileInfo().setFile(inFilename);
424                 }
425                 _broker.informSubscribers();
426                 // update menu
427                 _menuManager.informFileLoaded();
428         }
429
430
431         /**
432          * Inform the app that the data has been saved
433          */
434         public void informDataSaved()
435         {
436                 _lastSavePosition = _undoStack.size();
437         }
438
439
440         /**
441          * Begin undo process
442          */
443         public void beginUndo()
444         {
445                 if (_undoStack.isEmpty())
446                 {
447                         JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.undo.none.text"),
448                                 I18nManager.getText("dialog.undo.none.title"), JOptionPane.INFORMATION_MESSAGE);
449                 }
450                 else
451                 {
452                         new UndoManager(this, _frame);
453                 }
454         }
455
456
457         /**
458          * Clear the undo stack (losing all undo information
459          */
460         public void clearUndo()
461         {
462                 // Exit if nothing to undo
463                 if (_undoStack == null || _undoStack.isEmpty())
464                         return;
465                 // Has track got unsaved data?
466                 boolean unsaved = hasDataUnsaved();
467                 // Confirm operation with dialog
468                 int answer = JOptionPane.showConfirmDialog(_frame,
469                         I18nManager.getText("dialog.clearundo.text"),
470                         I18nManager.getText("dialog.clearundo.title"),
471                         JOptionPane.YES_NO_OPTION);
472                 if (answer == JOptionPane.YES_OPTION)
473                 {
474                         _undoStack.clear();
475                         _lastSavePosition = 0;
476                         if (unsaved) _lastSavePosition = -1;
477                         _broker.informSubscribers();
478                 }
479         }
480
481
482         /**
483          * Undo the specified number of actions
484          * @param inNumUndos number of actions to undo
485          */
486         public void undoActions(int inNumUndos)
487         {
488                 try
489                 {
490                         for (int i=0; i<inNumUndos; i++)
491                         {
492                                 ((UndoOperation) _undoStack.pop()).performUndo(_trackInfo);
493                         }
494                         JOptionPane.showMessageDialog(_frame, "" + inNumUndos + " "
495                                  + (inNumUndos==1?I18nManager.getText("dialog.confirmundo.single.text"):I18nManager.getText("dialog.confirmundo.multiple.text")),
496                                 I18nManager.getText("dialog.confirmundo.title"),
497                                 JOptionPane.INFORMATION_MESSAGE);
498                 }
499                 catch (UndoException ue)
500                 {
501                         JOptionPane.showMessageDialog(_frame,
502                                 I18nManager.getText("error.undofailed.text") + " : " + ue.getMessage(),
503                                 I18nManager.getText("error.undofailed.title"),
504                                 JOptionPane.ERROR_MESSAGE);
505                         _undoStack.clear();
506                         _broker.informSubscribers();
507                 }
508                 catch (EmptyStackException empty) {}
509         }
510
511
512         /**
513          * Helper method to parse an Object into an integer
514          * @param inObject object, eg from dialog
515          * @return int value given
516          */
517         private static int parseNumber(Object inObject)
518         {
519                 int num = 0;
520                 if (inObject != null)
521                 {
522                         try
523                         {
524                                 num = Integer.parseInt(inObject.toString());
525                         }
526                         catch (NumberFormatException nfe)
527                         {}
528                 }
529                 return num;
530         }
531 }