3 import java.util.EmptyStackException;
4 import java.util.Stack;
6 import javax.swing.JFrame;
7 import javax.swing.JOptionPane;
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;
31 * Main controller for the application
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;
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;
52 // TODO: Make waypoint window to allow list of waypoints, edit names etc
56 * @param inFrame frame object for application
57 * @param inBroker message broker
59 public App(JFrame inFrame, UpdateMessageBroker inBroker)
62 _undoStack = new Stack();
64 _track = new Track(_broker);
65 _trackInfo = new TrackInfo(_track, _broker);
70 * @return the current TrackInfo
72 public TrackInfo getTrackInfo()
78 * Check if the application has unsaved data
79 * @return true if data is unsaved, false otherwise
81 public boolean hasDataUnsaved()
83 return _undoStack.size() > _lastSavePosition;
87 * @return the undo stack
89 public Stack getUndoStack()
95 * Set the MenuManager object to be informed about changes
96 * @param inManager MenuManager object
98 public void setMenuManager(MenuManager inManager)
100 _menuManager = inManager;
105 * Open a file containing track or waypoint data
107 public void openFile()
109 if (_fileLoader == null)
110 _fileLoader = new FileLoader(this, _frame);
111 _fileLoader.openFile();
116 * Save the file in the selected format
118 public void saveFile()
122 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
123 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
127 FileSaver saver = new FileSaver(this, _frame, _track);
128 saver.showDialog(_fileLoader.getLastUsedDelimiter());
134 * Export track data as Kml
136 public void exportKml()
140 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
141 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
145 KmlExporter exporter = new KmlExporter(this, _frame, _track);
146 exporter.showDialog();
152 * Exit the application if confirmed
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)
170 * Delete the currently selected point
172 public void deleteCurrentPoint()
176 DataPoint currentPoint = _trackInfo.getCurrentPoint();
177 if (currentPoint != null)
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())
185 _undoStack.push(undo);
193 * Delete the currently selected range
195 public void deleteSelectedRange()
199 // add information to undo stack
200 UndoOperation undo = new UndoDeleteRange(_trackInfo);
201 // call track to delete point
202 if (_trackInfo.deleteRange())
204 _undoStack.push(undo);
211 * Delete all the duplicate points in the track
213 public void deleteDuplicates()
217 // Save undo information
218 UndoOperation undo = new UndoDeleteDuplicates(_track);
219 // tell track to do it
220 int numDeleted = _trackInfo.deleteDuplicates();
223 _undoStack.add(undo);
224 String message = null;
227 message = "1 " + I18nManager.getText("dialog.deleteduplicates.single.text");
231 message = "" + numDeleted + " " + I18nManager.getText("dialog.deleteduplicates.multi.text");
233 JOptionPane.showMessageDialog(_frame, message,
234 I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
238 JOptionPane.showMessageDialog(_frame,
239 I18nManager.getText("dialog.deleteduplicates.nonefound"),
240 I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
249 public void compressTrack()
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)
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);
274 JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.compresstrack.nonefound"),
275 I18nManager.getText("dialog.compresstrack.title"), JOptionPane.WARNING_MESSAGE);
281 * Reverse a section of the track
283 public void reverseRange()
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)))
295 UndoReverseSection undo = new UndoReverseSection(selStart, selEnd);
296 // call track to reverse range
297 if (_track.reverseRange(selStart, selEnd))
299 _undoStack.add(undo);
306 * Interpolate the two selected points
308 public void interpolateSelection()
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;
318 UndoInsert undo = new UndoInsert(_trackInfo.getSelection().getStart() + 1,
320 // call track to interpolate
321 if (_trackInfo.interpolate(numPoints))
323 _undoStack.add(undo);
329 * Rearrange the waypoints into track order
331 public void rearrangeWaypoints(int inFunction)
333 UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(_track);
334 boolean success = false;
335 if (inFunction == REARRANGE_TO_START || inFunction == REARRANGE_TO_END)
337 // Collect the waypoints to the start or end of the track
338 success = _track.collectWaypoints(inFunction == REARRANGE_TO_START);
342 // Interleave the waypoints into track order
343 success = _track.interleaveWaypoints();
347 _undoStack.add(undo);
351 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.rearrange.noop"),
352 I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
358 * Open a new window with the 3d view
360 public void show3dWindow()
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);
371 public void selectAll()
373 _trackInfo.getSelection().select(0, 0, _track.getNumPoints()-1);
379 public void selectNone()
381 _trackInfo.getSelection().clearAll();
385 * Receive loaded data and optionally merge with current Track
386 * @param inFieldArray array of fields
387 * @param inDataArray array of data
389 public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, int inAltFormat, String inFilename)
391 // Decide whether to load or append
392 if (_track != null && _track.getNumPoints() > 0)
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)
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();
408 else if (answer == JOptionPane.NO_OPTION)
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);
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);
425 _broker.informSubscribers();
427 _menuManager.informFileLoaded();
432 * Inform the app that the data has been saved
434 public void informDataSaved()
436 _lastSavePosition = _undoStack.size();
443 public void beginUndo()
445 if (_undoStack.isEmpty())
447 JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.undo.none.text"),
448 I18nManager.getText("dialog.undo.none.title"), JOptionPane.INFORMATION_MESSAGE);
452 new UndoManager(this, _frame);
458 * Clear the undo stack (losing all undo information
460 public void clearUndo()
462 // Exit if nothing to undo
463 if (_undoStack == null || _undoStack.isEmpty())
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)
475 _lastSavePosition = 0;
476 if (unsaved) _lastSavePosition = -1;
477 _broker.informSubscribers();
483 * Undo the specified number of actions
484 * @param inNumUndos number of actions to undo
486 public void undoActions(int inNumUndos)
490 for (int i=0; i<inNumUndos; i++)
492 ((UndoOperation) _undoStack.pop()).performUndo(_trackInfo);
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);
499 catch (UndoException ue)
501 JOptionPane.showMessageDialog(_frame,
502 I18nManager.getText("error.undofailed.text") + " : " + ue.getMessage(),
503 I18nManager.getText("error.undofailed.title"),
504 JOptionPane.ERROR_MESSAGE);
506 _broker.informSubscribers();
508 catch (EmptyStackException empty) {}
513 * Helper method to parse an Object into an integer
514 * @param inObject object, eg from dialog
515 * @return int value given
517 private static int parseNumber(Object inObject)
520 if (inObject != null)
524 num = Integer.parseInt(inObject.toString());
526 catch (NumberFormatException nfe)