]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/App.java
Version 11, August 2010
[GpsPrune.git] / tim / prune / App.java
index ced255ae111cc194eac8fb0d1db64b2f2b019b36..88cafea3cd1901e23949ccba07579f3bdeabe8d4 100644 (file)
@@ -1,5 +1,7 @@
 package tim.prune;
 
+import java.io.File;
+import java.util.ArrayList;
 import java.util.EmptyStackException;
 import java.util.Set;
 import java.util.Stack;
@@ -8,45 +10,31 @@ import javax.swing.JFrame;
 import javax.swing.JOptionPane;
 
 import tim.prune.data.Altitude;
-import tim.prune.data.Coordinate;
+import tim.prune.data.Checker;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.LatLonRectangle;
-import tim.prune.data.Latitude;
-import tim.prune.data.Longitude;
+import tim.prune.data.NumberUtils;
 import tim.prune.data.Photo;
 import tim.prune.data.PhotoList;
+import tim.prune.data.SourceInfo;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
+import tim.prune.function.SelectTracksFunction;
 import tim.prune.function.browser.BrowserLauncher;
 import tim.prune.function.browser.UrlGenerator;
 import tim.prune.function.edit.FieldEditList;
 import tim.prune.function.edit.PointEditor;
-import tim.prune.function.edit.PointNameEditor;
+import tim.prune.gui.SidebarController;
 import tim.prune.gui.MenuManager;
 import tim.prune.gui.UndoManager;
+import tim.prune.gui.Viewport;
 import tim.prune.load.FileLoader;
 import tim.prune.load.JpegLoader;
+import tim.prune.load.TrackNameList;
 import tim.prune.save.ExifSaver;
 import tim.prune.save.FileSaver;
-import tim.prune.undo.UndoAddTimeOffset;
-import tim.prune.undo.UndoCompress;
-import tim.prune.undo.UndoConnectPhoto;
-import tim.prune.undo.UndoConnectPhotoWithClone;
-import tim.prune.undo.UndoCreatePoint;
-import tim.prune.undo.UndoCutAndMove;
-import tim.prune.undo.UndoDeletePhoto;
-import tim.prune.undo.UndoDeletePoint;
-import tim.prune.undo.UndoDeleteRange;
-import tim.prune.undo.UndoDisconnectPhoto;
-import tim.prune.undo.UndoEditPoint;
-import tim.prune.undo.UndoException;
-import tim.prune.undo.UndoInsert;
-import tim.prune.undo.UndoLoad;
-import tim.prune.undo.UndoLoadPhotos;
-import tim.prune.undo.UndoMergeTrackSegments;
-import tim.prune.undo.UndoOperation;
-import tim.prune.undo.UndoReverseSection;
+import tim.prune.undo.*;
 
 
 /**
@@ -60,11 +48,16 @@ public class App
        private TrackInfo _trackInfo = null;
        private int _lastSavePosition = 0;
        private MenuManager _menuManager = null;
+       private SidebarController __sidebarController = null;
        private FileLoader _fileLoader = null;
        private JpegLoader _jpegLoader = null;
        private FileSaver _fileSaver = null;
        private Stack<UndoOperation> _undoStack = null;
        private boolean _mangleTimestampsConfirmed = false;
+       private Viewport _viewport = null;
+       private ArrayList<File> _dataFiles = null;
+       private boolean _firstDataFile = true;
+
 
        /**
         * Constructor
@@ -114,6 +107,27 @@ public class App
                return _undoStack;
        }
 
+       /**
+        * Load the specified data files one by one
+        * @param inDataFiles arraylist containing File objects to load
+        */
+       public void loadDataFiles(ArrayList<File> inDataFiles)
+       {
+               if (inDataFiles == null || inDataFiles.size() == 0) {
+                       _dataFiles = null;
+               }
+               else {
+                       _dataFiles = inDataFiles;
+                       File f = _dataFiles.get(0);
+                       _dataFiles.remove(0);
+                       // Start load of specified file
+                       if (_fileLoader == null)
+                               _fileLoader = new FileLoader(this, _frame);
+                       _firstDataFile = true;
+                       _fileLoader.openFile(f);
+               }
+       }
+
        /**
         * Complete a function execution
         * @param inUndo undo object to be added to stack
@@ -167,7 +181,7 @@ public class App
                else
                {
                        if (_fileSaver == null) {
-                               _fileSaver = new FileSaver(this, _frame, _track);
+                               _fileSaver = new FileSaver(this, _frame);
                        }
                        char delim = ',';
                        if (_fileLoader != null) {delim = _fileLoader.getLastUsedDelimiter();}
@@ -228,7 +242,7 @@ public class App
                        // add information to undo stack
                        UndoOperation undo = new UndoEditPoint(currentPoint, inUndoList);
                        // pass to track for completion
-                       if (_track.editPoint(currentPoint, inEditList))
+                       if (_track.editPoint(currentPoint, inEditList, false))
                        {
                                _undoStack.push(undo);
                                // Confirm point edit
@@ -238,24 +252,6 @@ public class App
        }
 
 
-       /**
-        * Edit the name of the currently selected (way)point
-        */
-       public void editCurrentPointName()
-       {
-               if (_track != null)
-               {
-                       DataPoint currentPoint = _trackInfo.getCurrentPoint();
-                       if (currentPoint != null)
-                       {
-                               // Open point dialog to display details
-                               PointNameEditor editor = new PointNameEditor(this, _frame);
-                               editor.showDialog(currentPoint);
-                       }
-               }
-       }
-
-
        /**
         * Delete the currently selected point
         */
@@ -456,7 +452,7 @@ public class App
                int selStart = _trackInfo.getSelection().getStart();
                int selEnd = _trackInfo.getSelection().getEnd();
                UndoAddTimeOffset undo = new UndoAddTimeOffset(selStart, selEnd, inTimeOffset);
-               if (_trackInfo.getTrack().addTimeOffset(selStart, selEnd, inTimeOffset))
+               if (_trackInfo.getTrack().addTimeOffset(selStart, selEnd, inTimeOffset, false))
                {
                        _undoStack.add(undo);
                        UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
@@ -465,6 +461,40 @@ public class App
        }
 
 
+       /**
+        * Complete the add altitude offset function with the specified offset
+        * @param inOffset altitude offset to add as String
+        * @param inFormat altitude format of offset (eg Feet, Metres)
+        */
+       public void finishAddAltitudeOffset(String inOffset, Altitude.Format inFormat)
+       {
+               // Sanity check
+               if (inOffset == null || inOffset.equals("") || inFormat==Altitude.Format.NO_FORMAT) {
+                       return;
+               }
+               // Construct undo information
+               UndoAddAltitudeOffset undo = new UndoAddAltitudeOffset(_trackInfo);
+               int selStart = _trackInfo.getSelection().getStart();
+               int selEnd = _trackInfo.getSelection().getEnd();
+               // How many decimal places are given in the offset?
+               int numDecimals = NumberUtils.getDecimalPlaces(inOffset);
+               boolean success = false;
+               // Decimal offset given
+               try {
+                       double offsetd = Double.parseDouble(inOffset);
+                       success = _trackInfo.getTrack().addAltitudeOffset(selStart, selEnd, offsetd, inFormat, numDecimals);
+               }
+               catch (NumberFormatException nfe) {}
+               if (success)
+               {
+                       _undoStack.add(undo);
+                       _trackInfo.getSelection().markInvalid();
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
+                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.addaltitudeoffset"));
+               }
+       }
+
+
        /**
         * Merge the track segments within the current selection
         */
@@ -480,7 +510,7 @@ public class App
                        // Make undo object
                        UndoMergeTrackSegments undo = new UndoMergeTrackSegments(_track, selStart, selEnd);
                        // Call track to merge segments
-                       if (_track.mergeTrackSegments(selStart, selEnd)) {
+                       if (_trackInfo.mergeTrackSegments(selStart, selEnd)) {
                                _undoStack.add(undo);
                                UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.mergetracksegments"));
                        }
@@ -530,21 +560,20 @@ public class App
 
 
        /**
-        * Create a new point at the given lat/long coordinates
-        * @param inLat latitude
-        * @param inLong longitude
+        * Create a new point at the given position
+        * @param inPoint point to add
         */
-       public void createPoint(double inLat, double inLong)
+       public void createPoint(DataPoint inPoint)
        {
                // create undo object
                UndoCreatePoint undo = new UndoCreatePoint();
-               // create point and add to track
-               DataPoint point = new DataPoint(new Latitude(inLat, Coordinate.FORMAT_NONE), new Longitude(inLong, Coordinate.FORMAT_NONE), null);
-               point.setSegmentStart(true);
-               _track.appendPoints(new DataPoint[] {point});
-               _trackInfo.getSelection().selectPoint(_trackInfo.getTrack().getNumPoints()-1);
-               // add undo object to stack
                _undoStack.add(undo);
+               // add point to track
+               inPoint.setSegmentStart(true);
+               _track.appendPoints(new DataPoint[] {inPoint});
+               // ensure track's field list contains point's fields
+               _track.extendFieldList(inPoint.getFieldList());
+               _trackInfo.selectPoint(_trackInfo.getTrack().getNumPoints()-1);
                // update listeners
                UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.createpoint"));
        }
@@ -582,22 +611,13 @@ public class App
 
                                // Add undo object to stack, set confirm message
                                _undoStack.add(undo);
-                               _trackInfo.getSelection().deselectRange();
+                               _trackInfo.getSelection().selectRange(-1, -1);
                                UpdateMessageBroker.informSubscribers();
                                UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.cutandmove"));
                        }
                }
        }
 
-
-       /**
-        * Select all points
-        */
-       public void selectAll()
-       {
-               _trackInfo.getSelection().select(0, 0, _track.getNumPoints()-1);
-       }
-
        /**
         * Select nothing
         */
@@ -608,16 +628,29 @@ public class App
                _track.clearDeletionMarkers();
        }
 
+       /**
+        * Receive loaded data and start load
+        * @param inFieldArray array of fields
+        * @param inDataArray array of data
+        * @param inAltFormat altitude format
+        * @param inSourceInfo information about the source of the data
+        */
+       public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
+               Altitude.Format inAltFormat, SourceInfo inSourceInfo)
+       {
+               informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null);
+       }
 
        /**
-        * Receive loaded data and optionally merge with current Track
+        * Receive loaded data and determine whether to filter on tracks or not
         * @param inFieldArray array of fields
         * @param inDataArray array of data
         * @param inAltFormat altitude format
-        * @param inFilename filename used
+        * @param inSourceInfo information about the source of the data
+        * @param inTrackNameList information about the track names
         */
-       public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, Altitude.Format inAltFormat,
-               String inFilename)
+       public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
+               Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList)
        {
                // Check whether loaded array can be properly parsed into a Track
                Track loadedTrack = new Track();
@@ -625,46 +658,73 @@ public class App
                if (loadedTrack.getNumPoints() <= 0)
                {
                        showErrorMessage("error.load.dialogtitle", "error.load.nopoints");
+                       // load next file if there's a queue
+                       loadNextFile();
                        return;
                }
+               // Check for doubled track
+               if (Checker.isDoubledTrack(loadedTrack)) {
+                       JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"),
+                               I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE);
+               }
+               // Look at TrackNameList, decide whether to filter or not
+               if (inTrackNameList != null && inTrackNameList.getNumTracks() > 1)
+               {
+                       // Launch a dialog to let the user choose which tracks to load, then continue
+                       new SelectTracksFunction(this, inFieldArray, inDataArray, inAltFormat, inSourceInfo,
+                               inTrackNameList).begin();
+               }
+               else {
+                       // go directly to load
+                       informDataLoaded(loadedTrack, inSourceInfo);
+               }
+       }
+
+       /**
+        * Receive loaded data and optionally merge with current Track
+        * @param inLoadedTrack loaded track
+        * @param inSourceInfo information about the source of the data
+        */
+       public void informDataLoaded(Track inLoadedTrack, SourceInfo inSourceInfo)
+       {
                // Decide whether to load or append
                if (_track.getNumPoints() > 0)
                {
                        // ask whether to replace or append
-                       int answer = JOptionPane.showConfirmDialog(_frame,
-                               I18nManager.getText("dialog.openappend.text"),
-                               I18nManager.getText("dialog.openappend.title"),
-                               JOptionPane.YES_NO_CANCEL_OPTION);
+                       int answer = 0;
+                       if (_dataFiles == null || _firstDataFile) {
+                               answer = JOptionPane.showConfirmDialog(_frame,
+                                       I18nManager.getText("dialog.openappend.text"),
+                                       I18nManager.getText("dialog.openappend.title"),
+                                       JOptionPane.YES_NO_CANCEL_OPTION);
+                       }
+                       else {
+                               // Automatically append if there's a file load queue
+                               answer = JOptionPane.YES_OPTION;
+                       }
                        if (answer == JOptionPane.YES_OPTION)
                        {
                                // append data to current Track
-                               _undoStack.add(new UndoLoad(_track.getNumPoints(), loadedTrack.getNumPoints()));
-                               _track.combine(loadedTrack);
-                               // set filename if currently empty
-                               if (_trackInfo.getFileInfo().getNumFiles() == 0)
-                               {
-                                       _trackInfo.getFileInfo().setFile(inFilename);
-                               }
-                               else
-                               {
-                                       _trackInfo.getFileInfo().addFile();
-                               }
+                               _undoStack.add(new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints()));
+                               _track.combine(inLoadedTrack);
+                               // set source information
+                               inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
+                               _trackInfo.getFileInfo().addSource(inSourceInfo);
                        }
                        else if (answer == JOptionPane.NO_OPTION)
                        {
                                // Don't append, replace data
                                PhotoList photos = null;
-                               if (_trackInfo.getPhotoList().hasCorrelatedPhotos())
-                               {
+                               if (_trackInfo.getPhotoList().hasCorrelatedPhotos()) {
                                        photos = _trackInfo.getPhotoList().cloneList();
                                }
-                               _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, photos));
+                               _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos));
                                _lastSavePosition = _undoStack.size();
                                _trackInfo.getSelection().clearAll();
-                               _track.load(loadedTrack);
-                               _trackInfo.getFileInfo().setFile(inFilename);
-                               if (photos != null)
-                               {
+                               _track.load(inLoadedTrack);
+                               inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                               _trackInfo.getFileInfo().replaceSource(inSourceInfo);
+                               if (photos != null) {
                                        _trackInfo.getPhotoList().removeCorrelatedPhotos();
                                }
                        }
@@ -672,17 +732,51 @@ public class App
                else
                {
                        // Currently no data held, so transfer received data
-                       _undoStack.add(new UndoLoad(_trackInfo, inDataArray.length, null));
+                       _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null));
                        _lastSavePosition = _undoStack.size();
                        _trackInfo.getSelection().clearAll();
-                       _track.load(loadedTrack);
-                       _trackInfo.getFileInfo().setFile(inFilename);
+                       _track.load(inLoadedTrack);
+                       inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                       _trackInfo.getFileInfo().addSource(inSourceInfo);
                }
                UpdateMessageBroker.informSubscribers();
                // Update status bar
-               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile") + " '" + inFilename + "'");
+               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile")
+                       + " '" + inSourceInfo.getName() + "'");
                // update menu
                _menuManager.informFileLoaded();
+               // load next file if there's a queue
+               loadNextFile();
+       }
+
+       /**
+        * Inform the app that NO data was loaded, eg cancel pressed
+        * Only needed if there's another file waiting in the queue
+        */
+       public void informNoDataLoaded()
+       {
+               // Load next file if there's a queue
+               loadNextFile();
+       }
+
+       /**
+        * Load the next file in the waiting list, if any
+        */
+       private void loadNextFile()
+       {
+               _firstDataFile = false;
+               if (_dataFiles == null || _dataFiles.size() == 0) {
+                       _dataFiles = null;
+               }
+               else {
+                       new Thread(new Runnable() {
+                               public void run() {
+                                       File f = _dataFiles.get(0);
+                                       _dataFiles.remove(0);
+                                       _fileLoader.openFile(f);
+                               }
+                       }).start();
+               }
        }
 
 
@@ -702,15 +796,13 @@ public class App
                                // Save numbers so load can be undone
                                _undoStack.add(new UndoLoadPhotos(numPhotosAdded, numPointsAdded));
                        }
-                       if (numPhotosAdded == 1)
-                       {
+                       if (numPhotosAdded == 1) {
                                UpdateMessageBroker.informSubscribers("" + numPhotosAdded + " " + I18nManager.getText("confirm.jpegload.single"));
                        }
-                       else
-                       {
+                       else {
                                UpdateMessageBroker.informSubscribers("" + numPhotosAdded + " " + I18nManager.getText("confirm.jpegload.multi"));
                        }
-                       // TODO: Improve message when photo(s) fail to load (eg already added)
+                       // MAYBE: Improve message when photo(s) fail to load (eg already added)
                        UpdateMessageBroker.informSubscribers();
                        // update menu
                        _menuManager.informFileLoaded();
@@ -727,29 +819,7 @@ public class App
                DataPoint point = _trackInfo.getCurrentPoint();
                if (photo != null && point != null)
                {
-                       if (point.getPhoto() != null)
-                       {
-                               // point already has a photo, confirm cloning of new point
-                               if (JOptionPane.showConfirmDialog(_frame,
-                                       I18nManager.getText("dialog.connectphoto.clonepoint"),
-                                       I18nManager.getText("dialog.connect.title"),
-                                       JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)
-                               {
-                                       // Create undo, clone point and attach
-                                       int pointIndex = _trackInfo.getSelection().getCurrentPointIndex() + 1;
-                                       // insert new point after current one
-                                       point = point.clonePoint();
-                                       UndoConnectPhotoWithClone undo = new UndoConnectPhotoWithClone(
-                                               point, photo.getFile().getName(), pointIndex);
-                                       _track.insertPoint(point, pointIndex);
-                                       photo.setDataPoint(point);
-                                       point.setPhoto(photo);
-                                       _undoStack.add(undo);
-                                       UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
-                                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.connect"));
-                               }
-                       }
-                       else
+                       if (point.getPhoto() == null)
                        {
                                // point doesn't currently have a photo, so just connect it
                                _undoStack.add(new UndoConnectPhoto(point, photo.getFile().getName()));
@@ -964,4 +1034,37 @@ public class App
                JOptionPane.showMessageDialog(_frame, inMessage,
                        I18nManager.getText(inTitleKey), JOptionPane.ERROR_MESSAGE);
        }
+
+       /**
+        * @param inViewport viewport object
+        */
+       public void setViewport(Viewport inViewport)
+       {
+               _viewport = inViewport;
+       }
+
+       /**
+        * @return current viewport object
+        */
+       public Viewport getViewport()
+       {
+               return _viewport;
+       }
+
+       /**
+        * Set the controller for the full screen mode
+        * @param inController controller object
+        */
+       public void setSidebarController(SidebarController inController)
+       {
+               __sidebarController = inController;
+       }
+
+       /**
+        * Toggle sidebars on and off
+        */
+       public void toggleSidebars()
+       {
+               __sidebarController.toggle();
+       }
 }