3 import java.util.EmptyStackException;
5 import java.util.Stack;
7 import javax.swing.JFrame;
8 import javax.swing.JOptionPane;
10 import tim.prune.data.DataPoint;
11 import tim.prune.data.Field;
12 import tim.prune.data.Photo;
13 import tim.prune.data.PhotoList;
14 import tim.prune.data.Track;
15 import tim.prune.data.TrackInfo;
16 import tim.prune.edit.FieldEditList;
17 import tim.prune.edit.PointEditor;
18 import tim.prune.edit.PointNameEditor;
19 import tim.prune.gui.MenuManager;
20 import tim.prune.gui.UndoManager;
21 import tim.prune.load.FileLoader;
22 import tim.prune.load.JpegLoader;
23 import tim.prune.load.PhotoMeasurer;
24 import tim.prune.save.ExifSaver;
25 import tim.prune.save.FileSaver;
26 import tim.prune.save.KmlExporter;
27 import tim.prune.save.PovExporter;
28 import tim.prune.threedee.ThreeDException;
29 import tim.prune.threedee.ThreeDWindow;
30 import tim.prune.threedee.WindowFactory;
31 import tim.prune.undo.UndoCompress;
32 import tim.prune.undo.UndoConnectPhoto;
33 import tim.prune.undo.UndoDeleteDuplicates;
34 import tim.prune.undo.UndoDeletePhoto;
35 import tim.prune.undo.UndoDeletePoint;
36 import tim.prune.undo.UndoDeleteRange;
37 import tim.prune.undo.UndoEditPoint;
38 import tim.prune.undo.UndoException;
39 import tim.prune.undo.UndoInsert;
40 import tim.prune.undo.UndoLoad;
41 import tim.prune.undo.UndoLoadPhotos;
42 import tim.prune.undo.UndoOperation;
43 import tim.prune.undo.UndoRearrangeWaypoints;
44 import tim.prune.undo.UndoReverseSection;
48 * Main controller for the application
53 private JFrame _frame = null;
54 private Track _track = null;
55 private TrackInfo _trackInfo = null;
56 private int _lastSavePosition = 0;
57 private MenuManager _menuManager = null;
58 private FileLoader _fileLoader = null;
59 private JpegLoader _jpegLoader = null;
60 private KmlExporter _exporter = null;
61 private PovExporter _povExporter = null;
62 private Stack _undoStack = null;
63 private UpdateMessageBroker _broker = null;
64 private boolean _reversePointsConfirmed = false;
67 public static final int REARRANGE_TO_START = 0;
68 public static final int REARRANGE_TO_END = 1;
69 public static final int REARRANGE_TO_NEAREST = 2;
74 * @param inFrame frame object for application
75 * @param inBroker message broker
77 public App(JFrame inFrame, UpdateMessageBroker inBroker)
80 _undoStack = new Stack();
82 _track = new Track(_broker);
83 _trackInfo = new TrackInfo(_track, _broker);
88 * @return the current TrackInfo
90 public TrackInfo getTrackInfo()
96 * Check if the application has unsaved data
97 * @return true if data is unsaved, false otherwise
99 public boolean hasDataUnsaved()
101 return (_undoStack.size() > _lastSavePosition
102 && (_track.getNumPoints() > 0 || _trackInfo.getPhotoList().getNumPhotos() > 0));
106 * @return the undo stack
108 public Stack getUndoStack()
114 * Set the MenuManager object to be informed about changes
115 * @param inManager MenuManager object
117 public void setMenuManager(MenuManager inManager)
119 _menuManager = inManager;
124 * Open a file containing track or waypoint data
126 public void openFile()
128 if (_fileLoader == null)
129 _fileLoader = new FileLoader(this, _frame);
130 _fileLoader.openFile();
135 * Add a photo or a directory of photos which are already correlated
137 public void addPhotos()
139 if (_jpegLoader == null)
140 _jpegLoader = new JpegLoader(this, _frame);
141 _jpegLoader.openFile();
146 * Save the file in the selected format
148 public void saveFile()
152 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
153 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
157 FileSaver saver = new FileSaver(this, _frame, _track);
158 saver.showDialog(_fileLoader.getLastUsedDelimiter());
164 * Export track data as Kml
166 public void exportKml()
170 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
171 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
176 if (_exporter == null)
178 _exporter = new KmlExporter(_frame, _trackInfo);
180 _exporter.showDialog();
186 * Export track data as Pov without specifying settings
188 public void exportPov()
190 exportPov(false, 0.0, 0.0, 0.0, 0);
194 * Export track data as Pov and also specify settings
195 * @param inX X component of unit vector
196 * @param inY Y component of unit vector
197 * @param inZ Z component of unit vector
198 * @param inAltitudeCap altitude cap
200 public void exportPov(double inX, double inY, double inZ, int inAltitudeCap)
202 exportPov(true, inX, inY, inZ, inAltitudeCap);
206 * Export track data as Pov with optional angle specification
207 * @param inDefineAngles true to define angles, false to ignore
208 * @param inX X component of unit vector
209 * @param inY Y component of unit vector
210 * @param inZ Z component of unit vector
212 private void exportPov(boolean inDefineSettings, double inX, double inY, double inZ, int inAltitudeCap)
214 // Check track has data to export
215 if (_track == null || _track.getNumPoints() <= 0)
217 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.save.nodata"),
218 I18nManager.getText("error.save.dialogtitle"), JOptionPane.ERROR_MESSAGE);
222 // Make new exporter if necessary
223 if (_povExporter == null)
225 _povExporter = new PovExporter(_frame, _track);
227 // Specify angles if necessary
228 if (inDefineSettings)
230 _povExporter.setCameraCoordinates(inX, inY, inZ);
231 _povExporter.setAltitudeCap(inAltitudeCap);
234 _povExporter.showDialog();
240 * Exit the application if confirmed
246 _frame.requestFocus();
247 // check if ok to exit
248 Object[] buttonTexts = {I18nManager.getText("button.exit"), I18nManager.getText("button.cancel")};
249 if (!hasDataUnsaved()
250 || JOptionPane.showOptionDialog(_frame, I18nManager.getText("dialog.exit.confirm.text"),
251 I18nManager.getText("dialog.exit.confirm.title"), JOptionPane.YES_NO_OPTION,
252 JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
253 == JOptionPane.YES_OPTION)
261 * Edit the currently selected point
263 public void editCurrentPoint()
267 DataPoint currentPoint = _trackInfo.getCurrentPoint();
268 if (currentPoint != null)
270 // Open point dialog to display details
271 PointEditor editor = new PointEditor(this, _frame);
272 editor.showDialog(_track, currentPoint);
279 * Complete the point edit
280 * @param inEditList list of edits
282 public void completePointEdit(FieldEditList inEditList, FieldEditList inUndoList)
284 DataPoint currentPoint = _trackInfo.getCurrentPoint();
285 if (inEditList != null && inEditList.getNumEdits() > 0 && currentPoint != null)
287 // add information to undo stack
288 UndoOperation undo = new UndoEditPoint(currentPoint, inUndoList);
289 // pass to track for completion
290 if (_track.editPoint(currentPoint, inEditList))
292 _undoStack.push(undo);
299 * Edit the name of the currently selected (way)point
301 public void editCurrentPointName()
305 DataPoint currentPoint = _trackInfo.getCurrentPoint();
306 if (currentPoint != null)
308 // Open point dialog to display details
309 PointNameEditor editor = new PointNameEditor(this, _frame);
310 editor.showDialog(currentPoint);
317 * Delete the currently selected point
319 public void deleteCurrentPoint()
323 DataPoint currentPoint = _trackInfo.getCurrentPoint();
324 if (currentPoint != null)
326 boolean deletePhoto = false;
327 Photo currentPhoto = currentPoint.getPhoto();
328 if (currentPhoto != null)
330 // Confirm deletion of photo or decoupling
331 int response = JOptionPane.showConfirmDialog(_frame,
332 I18nManager.getText("dialog.deletepoint.deletephoto") + " " + currentPhoto.getFile().getName(),
333 I18nManager.getText("dialog.deletepoint.title"),
334 JOptionPane.YES_NO_CANCEL_OPTION);
335 if (response == JOptionPane.CANCEL_OPTION || response == JOptionPane.CLOSED_OPTION)
337 // cancel pressed- abort delete
340 if (response == JOptionPane.YES_OPTION) {deletePhoto = true;}
342 // add information to undo stack
343 int pointIndex = _trackInfo.getSelection().getCurrentPointIndex();
344 int photoIndex = _trackInfo.getPhotoList().getPhotoIndex(currentPhoto);
345 // Undo object needs to know index of photo in list (if any) to restore
346 UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex);
347 // call track to delete point
348 if (_trackInfo.deletePoint())
350 _undoStack.push(undo);
351 if (currentPhoto != null)
353 // delete photo if necessary
356 _trackInfo.getPhotoList().deletePhoto(photoIndex);
360 // decouple photo from point
361 currentPhoto.setDataPoint(null);
371 * Delete the currently selected range
373 public void deleteSelectedRange()
377 // Find out if photos should be deleted or not
378 int selStart = _trackInfo.getSelection().getStart();
379 int selEnd = _trackInfo.getSelection().getEnd();
380 if (selStart >= 0 && selEnd >= selStart)
382 int numToDelete = selEnd - selStart + 1;
383 boolean[] deletePhotos = new boolean[numToDelete];
384 Photo[] photosToDelete = new Photo[numToDelete];
385 boolean deleteAll = false;
386 boolean deleteNone = false;
387 String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
388 I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"),
389 I18nManager.getText("button.cancel")};
390 DataPoint point = null;
391 for (int i=0; i<numToDelete; i++)
393 point = _trackInfo.getTrack().getPoint(i + selStart);
394 if (point != null && point.getPhoto() != null)
398 deletePhotos[i] = true;
399 photosToDelete[i] = point.getPhoto();
401 else if (deleteNone) {deletePhotos[i] = false;}
404 int response = JOptionPane.showOptionDialog(_frame,
405 I18nManager.getText("dialog.deletepoint.deletephoto") + " " + point.getPhoto().getFile().getName(),
406 I18nManager.getText("dialog.deletepoint.title"),
407 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
408 questionOptions, questionOptions[1]);
409 // check for cancel or close
410 if (response == 4 || response == -1) {return;}
411 // check for yes or yes to all
412 if (response == 0 || response == 2)
414 deletePhotos[i] = true;
415 photosToDelete[i] = point.getPhoto();
416 if (response == 2) {deleteAll = true;}
418 // check for no to all
419 if (response == 3) {deleteNone = true;}
423 // add information to undo stack
424 UndoOperation undo = new UndoDeleteRange(_trackInfo);
425 // delete requested photos
426 for (int i=0; i<numToDelete; i++)
428 point = _trackInfo.getTrack().getPoint(i + selStart);
429 if (point != null && point.getPhoto() != null)
433 // delete photo from list
434 _trackInfo.getPhotoList().deletePhoto(_trackInfo.getPhotoList().getPhotoIndex(point.getPhoto()));
438 // decouple from point
439 point.getPhoto().setDataPoint(null);
443 // call track to delete range
444 if (_trackInfo.deleteRange())
446 _undoStack.push(undo);
454 * Delete all the duplicate points in the track
456 public void deleteDuplicates()
460 // Save undo information
461 UndoOperation undo = new UndoDeleteDuplicates(_track);
462 // tell track to do it
463 int numDeleted = _trackInfo.deleteDuplicates();
466 _undoStack.add(undo);
467 String message = null;
470 message = "1 " + I18nManager.getText("dialog.deleteduplicates.single.text");
474 message = "" + numDeleted + " " + I18nManager.getText("dialog.deleteduplicates.multi.text");
476 JOptionPane.showMessageDialog(_frame, message,
477 I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
481 JOptionPane.showMessageDialog(_frame,
482 I18nManager.getText("dialog.deleteduplicates.nonefound"),
483 I18nManager.getText("dialog.deleteduplicates.title"), JOptionPane.INFORMATION_MESSAGE);
492 public void compressTrack()
494 UndoCompress undo = new UndoCompress(_track);
495 // Get compression parameter
496 Object compParam = JOptionPane.showInputDialog(_frame,
497 I18nManager.getText("dialog.compresstrack.parameter.text"),
498 I18nManager.getText("dialog.compresstrack.title"),
499 JOptionPane.QUESTION_MESSAGE, null, null, "100");
500 int compNumber = parseNumber(compParam);
501 if (compNumber <= 0) return;
502 // call track to do compress
503 int numPointsDeleted = _trackInfo.compress(compNumber);
504 // add to undo stack if successful
505 if (numPointsDeleted > 0)
507 undo.setNumPointsDeleted(numPointsDeleted);
508 _undoStack.add(undo);
509 JOptionPane.showMessageDialog(_frame,
510 I18nManager.getText("dialog.compresstrack.text") + " - "
511 + numPointsDeleted + " "
512 + (numPointsDeleted==1?I18nManager.getText("dialog.compresstrack.single.text"):I18nManager.getText("dialog.compresstrack.multi.text")),
513 I18nManager.getText("dialog.compresstrack.title"), JOptionPane.INFORMATION_MESSAGE);
517 JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.compresstrack.nonefound"),
518 I18nManager.getText("dialog.compresstrack.title"), JOptionPane.WARNING_MESSAGE);
524 * Reverse a section of the track
526 public void reverseRange()
528 // check whether Timestamp field exists, and if so confirm reversal
529 int selStart = _trackInfo.getSelection().getStart();
530 int selEnd = _trackInfo.getSelection().getEnd();
531 if (!_track.hasData(Field.TIMESTAMP, selStart, selEnd)
532 || _reversePointsConfirmed
533 || (JOptionPane.showConfirmDialog(_frame,
534 I18nManager.getText("dialog.confirmreversetrack.text"),
535 I18nManager.getText("dialog.confirmreversetrack.title"),
536 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION && (_reversePointsConfirmed = true)))
538 UndoReverseSection undo = new UndoReverseSection(selStart, selEnd);
539 // call track to reverse range
540 if (_track.reverseRange(selStart, selEnd))
542 _undoStack.add(undo);
549 * Interpolate the two selected points
551 public void interpolateSelection()
553 // Get number of points to add
554 Object numPointsStr = JOptionPane.showInputDialog(_frame,
555 I18nManager.getText("dialog.interpolate.parameter.text"),
556 I18nManager.getText("dialog.interpolate.title"),
557 JOptionPane.QUESTION_MESSAGE, null, null, "");
558 int numPoints = parseNumber(numPointsStr);
559 if (numPoints <= 0) return;
561 UndoInsert undo = new UndoInsert(_trackInfo.getSelection().getStart() + 1,
563 // call track to interpolate
564 if (_trackInfo.interpolate(numPoints))
566 _undoStack.add(undo);
572 * Rearrange the waypoints into track order
574 public void rearrangeWaypoints(int inFunction)
576 UndoRearrangeWaypoints undo = new UndoRearrangeWaypoints(_track);
577 boolean success = false;
578 if (inFunction == REARRANGE_TO_START || inFunction == REARRANGE_TO_END)
580 // Collect the waypoints to the start or end of the track
581 success = _track.collectWaypoints(inFunction == REARRANGE_TO_START);
585 // Interleave the waypoints into track order
586 success = _track.interleaveWaypoints();
590 _undoStack.add(undo);
594 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.rearrange.noop"),
595 I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
601 * Open a new window with the 3d view
603 public void show3dWindow()
605 ThreeDWindow window = WindowFactory.getWindow(this, _frame);
608 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.function.nojava3d"),
609 I18nManager.getText("error.function.notavailable.title"), JOptionPane.WARNING_MESSAGE);
615 // Pass the track object and show the window
616 window.setTrack(_track);
619 catch (ThreeDException e)
621 JOptionPane.showMessageDialog(_frame, I18nManager.getText("error.3d") + ": " + e.getMessage(),
622 I18nManager.getText("error.3d.title"), JOptionPane.ERROR_MESSAGE);
631 public void selectAll()
633 _trackInfo.getSelection().select(0, 0, _track.getNumPoints()-1);
639 public void selectNone()
641 // deselect point, range and photo
642 _trackInfo.getSelection().clearAll();
647 * Receive loaded data and optionally merge with current Track
648 * @param inFieldArray array of fields
649 * @param inDataArray array of data
651 public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, int inAltFormat, String inFilename)
653 // Check whether loaded array can be properly parsed into a Track
654 Track loadedTrack = new Track(_broker);
655 loadedTrack.load(inFieldArray, inDataArray, inAltFormat);
656 if (loadedTrack.getNumPoints() <= 0)
658 JOptionPane.showMessageDialog(_frame,
659 I18nManager.getText("error.load.nopoints"),
660 I18nManager.getText("error.load.dialogtitle"),
661 JOptionPane.ERROR_MESSAGE);
664 // Decide whether to load or append
665 if (_track != null && _track.getNumPoints() > 0)
667 // ask whether to replace or append
668 int answer = JOptionPane.showConfirmDialog(_frame,
669 I18nManager.getText("dialog.openappend.text"),
670 I18nManager.getText("dialog.openappend.title"),
671 JOptionPane.YES_NO_CANCEL_OPTION);
672 if (answer == JOptionPane.YES_OPTION)
674 // append data to current Track
675 _undoStack.add(new UndoLoad(_track.getNumPoints(), loadedTrack.getNumPoints()));
676 _track.combine(loadedTrack);
677 // set filename if currently empty
678 if (_trackInfo.getFileInfo().getNumFiles() == 0)
680 _trackInfo.getFileInfo().setFile(inFilename);
684 _trackInfo.getFileInfo().addFile();
687 else if (answer == JOptionPane.NO_OPTION)
689 // Don't append, replace data
690 PhotoList photos = null;
691 if (_trackInfo.getPhotoList().hasCorrelatedPhotos())
693 photos = _trackInfo.getPhotoList().cloneList();
695 _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, photos));
696 _lastSavePosition = _undoStack.size();
697 // TODO: Should be possible to reuse the Track object already loaded?
698 _trackInfo.loadTrack(inFieldArray, inDataArray, inAltFormat);
699 _trackInfo.getFileInfo().setFile(inFilename);
702 _trackInfo.getPhotoList().removeCorrelatedPhotos();
708 // currently no data held, so use received data
709 _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, null));
710 _lastSavePosition = _undoStack.size();
711 _trackInfo.loadTrack(inFieldArray, inDataArray, inAltFormat);
712 _trackInfo.getFileInfo().setFile(inFilename);
714 _broker.informSubscribers();
716 _menuManager.informFileLoaded();
721 * Accept a list of loaded photos
722 * @param inPhotoList List of Photo objects
724 public void informPhotosLoaded(List inPhotoList)
726 if (inPhotoList != null && !inPhotoList.isEmpty())
728 int[] numsAdded = _trackInfo.addPhotos(inPhotoList);
729 int numPhotosAdded = numsAdded[0];
730 int numPointsAdded = numsAdded[1];
731 if (numPhotosAdded > 0)
733 // Save numbers so load can be undone
734 _undoStack.add(new UndoLoadPhotos(numPhotosAdded, numPointsAdded));
735 // Trigger preloading of photo sizes in separate thread
736 new PhotoMeasurer(_trackInfo.getPhotoList()).measurePhotos();
738 if (numPhotosAdded == 1)
740 JOptionPane.showMessageDialog(_frame,
741 "" + numPhotosAdded + " " + I18nManager.getText("dialog.jpegload.photoadded"),
742 I18nManager.getText("dialog.jpegload.title"), JOptionPane.INFORMATION_MESSAGE);
746 JOptionPane.showMessageDialog(_frame,
747 "" + numPhotosAdded + " " + I18nManager.getText("dialog.jpegload.photosadded"),
748 I18nManager.getText("dialog.jpegload.title"), JOptionPane.INFORMATION_MESSAGE);
750 // TODO: Improve message when photo(s) fail to load (eg already added)
751 _broker.informSubscribers();
753 _menuManager.informFileLoaded();
759 * Connect the current photo to the current point
761 public void connectPhotoToPoint()
763 Photo photo = _trackInfo.getCurrentPhoto();
764 DataPoint point = _trackInfo.getCurrentPoint();
765 if (photo != null && point != null && point.getPhoto() == null)
768 _undoStack.add(new UndoConnectPhoto(point, photo.getFile().getName()));
769 photo.setDataPoint(point);
770 point.setPhoto(photo);
771 //TODO: Confirm connect (maybe with status in photo panel?)
777 * Remove the current photo, if any
779 public void deleteCurrentPhoto()
781 // Delete the current photo, and optionally its point too, keeping undo information
782 Photo currentPhoto = _trackInfo.getCurrentPhoto();
783 if (currentPhoto != null)
785 // Photo is selected, see if it has a point or not
786 boolean photoDeleted = false;
787 UndoDeletePhoto undoAction = null;
788 if (currentPhoto.getDataPoint() == null)
790 // no point attached, so just delete photo
791 undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(),
793 photoDeleted = _trackInfo.deleteCurrentPhoto(false);
797 // point is attached, so need to confirm point deletion
798 undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(),
799 currentPhoto.getDataPoint(), _trackInfo.getTrack().getPointIndex(currentPhoto.getDataPoint()));
800 int response = JOptionPane.showConfirmDialog(_frame,
801 I18nManager.getText("dialog.deletephoto.deletepoint"),
802 I18nManager.getText("dialog.deletephoto.title"),
803 JOptionPane.YES_NO_CANCEL_OPTION);
804 boolean deletePointToo = (response == JOptionPane.YES_OPTION);
805 // Cancel delete if cancel pressed or dialog closed
806 if (response == JOptionPane.YES_OPTION || response == JOptionPane.NO_OPTION)
808 photoDeleted = _trackInfo.deleteCurrentPhoto(deletePointToo);
811 // Add undo information to stack if necessary
814 _undoStack.add(undoAction);
821 * Save the coordinates of photos in their exif data
823 public void saveExif()
825 ExifSaver saver = new ExifSaver(_frame);
826 saver.saveExifInformation(_trackInfo.getPhotoList());
831 * Inform the app that the data has been saved
833 public void informDataSaved()
835 _lastSavePosition = _undoStack.size();
842 public void beginUndo()
844 if (_undoStack.isEmpty())
846 JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.undo.none.text"),
847 I18nManager.getText("dialog.undo.none.title"), JOptionPane.INFORMATION_MESSAGE);
851 new UndoManager(this, _frame);
857 * Clear the undo stack (losing all undo information
859 public void clearUndo()
861 // Exit if nothing to undo
862 if (_undoStack == null || _undoStack.isEmpty())
864 // Has track got unsaved data?
865 boolean unsaved = hasDataUnsaved();
866 // Confirm operation with dialog
867 int answer = JOptionPane.showConfirmDialog(_frame,
868 I18nManager.getText("dialog.clearundo.text"),
869 I18nManager.getText("dialog.clearundo.title"),
870 JOptionPane.YES_NO_OPTION);
871 if (answer == JOptionPane.YES_OPTION)
874 _lastSavePosition = 0;
875 if (unsaved) _lastSavePosition = -1;
876 _broker.informSubscribers();
882 * Undo the specified number of actions
883 * @param inNumUndos number of actions to undo
885 public void undoActions(int inNumUndos)
889 for (int i=0; i<inNumUndos; i++)
891 ((UndoOperation) _undoStack.pop()).performUndo(_trackInfo);
893 JOptionPane.showMessageDialog(_frame, "" + inNumUndos + " "
894 + (inNumUndos==1?I18nManager.getText("dialog.confirmundo.single.text"):I18nManager.getText("dialog.confirmundo.multiple.text")),
895 I18nManager.getText("dialog.confirmundo.title"),
896 JOptionPane.INFORMATION_MESSAGE);
898 catch (UndoException ue)
900 JOptionPane.showMessageDialog(_frame,
901 I18nManager.getText("error.undofailed.text") + " : " + ue.getMessage(),
902 I18nManager.getText("error.undofailed.title"),
903 JOptionPane.ERROR_MESSAGE);
905 _broker.informSubscribers();
907 catch (EmptyStackException empty) {}
912 * Helper method to parse an Object into an integer
913 * @param inObject object, eg from dialog
914 * @return int value given
916 private static int parseNumber(Object inObject)
919 if (inObject != null)
923 num = Integer.parseInt(inObject.toString());
925 catch (NumberFormatException nfe)