package tim.prune;
+import java.io.File;
+import java.util.ArrayList;
import java.util.EmptyStackException;
import java.util.Set;
import java.util.Stack;
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.*;
/**
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
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
else
{
if (_fileSaver == null) {
- _fileSaver = new FileSaver(this, _frame, _track);
+ _fileSaver = new FileSaver(this, _frame);
}
char delim = ',';
if (_fileLoader != null) {delim = _fileLoader.getLastUsedDelimiter();}
// 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
}
- /**
- * 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
*/
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);
}
+ /**
+ * 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
*/
// 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"));
}
/**
- * 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"));
}
// 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
*/
_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();
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();
}
}
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();
+ }
}
// 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();
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()));
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();
+ }
}