X-Git-Url: https://gitweb.fperrin.net/?p=GpsPrune.git;a=blobdiff_plain;f=tim%2Fprune%2FApp.java;h=686b3dee485d4b3ffcd58203f2a3e5445be9bb17;hp=88cafea3cd1901e23949ccba07579f3bdeabe8d4;hb=8c8868ae29b3252f02e094c02307384cf61ba667;hpb=140e9d165f85c3d4f0435a311e091209313faa2a diff --git a/tim/prune/App.java b/tim/prune/App.java index 88cafea..686b3de 100644 --- a/tim/prune/App.java +++ b/tim/prune/App.java @@ -9,7 +9,7 @@ import java.util.Stack; import javax.swing.JFrame; import javax.swing.JOptionPane; -import tim.prune.data.Altitude; +import tim.prune.config.Config; import tim.prune.data.Checker; import tim.prune.data.DataPoint; import tim.prune.data.Field; @@ -17,20 +17,27 @@ import tim.prune.data.LatLonRectangle; import tim.prune.data.NumberUtils; import tim.prune.data.Photo; import tim.prune.data.PhotoList; +import tim.prune.data.PointCreateOptions; +import tim.prune.data.RecentFile; import tim.prune.data.SourceInfo; import tim.prune.data.Track; import tim.prune.data.TrackInfo; +import tim.prune.data.SourceInfo.FILE_TYPE; +import tim.prune.data.Unit; +import tim.prune.function.AsyncMediaLoader; +import tim.prune.function.SaveConfig; 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.gui.SidebarController; import tim.prune.gui.MenuManager; +import tim.prune.gui.SidebarController; import tim.prune.gui.UndoManager; import tim.prune.gui.Viewport; import tim.prune.load.FileLoader; import tim.prune.load.JpegLoader; +import tim.prune.load.MediaLinkInfo; import tim.prune.load.TrackNameList; import tim.prune.save.ExifSaver; import tim.prune.save.FileSaver; @@ -48,7 +55,7 @@ public class App private TrackInfo _trackInfo = null; private int _lastSavePosition = 0; private MenuManager _menuManager = null; - private SidebarController __sidebarController = null; + private SidebarController _sidebarController = null; private FileLoader _fileLoader = null; private JpegLoader _jpegLoader = null; private FileSaver _fileSaver = null; @@ -56,7 +63,12 @@ public class App private boolean _mangleTimestampsConfirmed = false; private Viewport _viewport = null; private ArrayList _dataFiles = null; - private boolean _firstDataFile = true; + private boolean _autoAppendNextFile = false; + private boolean _busyLoading = false; + private AppMode _appMode = AppMode.NORMAL; + + /** Enum for the app mode - currently only two options but may expand later */ + public enum AppMode {NORMAL, DRAWRECT}; /** @@ -96,7 +108,7 @@ public class App public boolean hasDataUnsaved() { return (_undoStack.size() > _lastSavePosition - && (_track.getNumPoints() > 0 || _trackInfo.getPhotoList().getNumPhotos() > 0)); + && (_track.getNumPoints() > 0 || _trackInfo.getPhotoList().hasModifiedMedia())); } /** @@ -116,14 +128,15 @@ public class App if (inDataFiles == null || inDataFiles.size() == 0) { _dataFiles = null; } - else { + 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; + _autoAppendNextFile = false; // prompt for append _fileLoader.openFile(f); } } @@ -137,6 +150,7 @@ public class App { _undoStack.add(inUndo); UpdateMessageBroker.informSubscribers(inConfirmText); + setCurrentMode(AppMode.NORMAL); } /** @@ -206,6 +220,10 @@ public class App JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) == JOptionPane.YES_OPTION) { + // save settings + if (Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS)) { + new SaveConfig(this).silentSave(); + } System.exit(0); } } @@ -261,13 +279,14 @@ public class App DataPoint currentPoint = _trackInfo.getCurrentPoint(); if (currentPoint != null) { + // Check for photo boolean deletePhoto = false; Photo currentPhoto = currentPoint.getPhoto(); if (currentPhoto != null) { // Confirm deletion of photo or decoupling int response = JOptionPane.showConfirmDialog(_frame, - I18nManager.getText("dialog.deletepoint.deletephoto") + " " + currentPhoto.getFile().getName(), + I18nManager.getText("dialog.deletepoint.deletephoto") + " " + currentPhoto.getName(), I18nManager.getText("dialog.deletepoint.title"), JOptionPane.YES_NO_CANCEL_OPTION); if (response == JOptionPane.CANCEL_OPTION || response == JOptionPane.CLOSED_OPTION) @@ -280,10 +299,11 @@ public class App // store necessary information to undo it later int pointIndex = _trackInfo.getSelection().getCurrentPointIndex(); int photoIndex = _trackInfo.getPhotoList().getPhotoIndex(currentPhoto); + int audioIndex = _trackInfo.getAudioList().getAudioIndex(currentPoint.getAudio()); DataPoint nextTrackPoint = _trackInfo.getTrack().getNextTrackPoint(pointIndex + 1); // Construct Undo object UndoOperation undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, - nextTrackPoint != null && nextTrackPoint.getSegmentStart()); + audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart()); // call track to delete point if (_trackInfo.deletePoint()) { @@ -301,95 +321,15 @@ public class App // decouple photo from point currentPhoto.setDataPoint(null); } + UpdateMessageBroker.informSubscribers(DataSubscriber.PHOTOS_MODIFIED); + } + // Delete audio object (without bothering to ask) + if (audioIndex > -1) { + _trackInfo.getAudioList().deleteAudio(audioIndex); } // Confirm UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.deletepoint.single")); - } - } - } - - - /** - * Delete the currently selected range - */ - public void deleteSelectedRange() - { - if (_track != null) - { - // Find out if photos should be deleted or not - int selStart = _trackInfo.getSelection().getStart(); - int selEnd = _trackInfo.getSelection().getEnd(); - if (selStart >= 0 && selEnd >= selStart) - { - int numToDelete = selEnd - selStart + 1; - boolean[] deletePhotos = new boolean[numToDelete]; - Photo[] photosToDelete = new Photo[numToDelete]; - boolean deleteAll = false; - boolean deleteNone = false; - String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"), - I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"), - I18nManager.getText("button.cancel")}; - DataPoint point = null; - for (int i=0; i inIndex) + { + // Extend end of selection by 1 + _trackInfo.getSelection().selectRange(selStart, selEnd+1); + } + // update listeners + UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.createpoint")); + } + + /** * Cut the current selection and move it to before the currently selected point */ @@ -629,32 +573,51 @@ public class App } /** - * Receive loaded data and start load + * 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 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, SourceInfo inSourceInfo) + SourceInfo inSourceInfo, TrackNameList inTrackNameList) { - informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null); + // no link array given + informDataLoaded(inFieldArray, inDataArray, null, inSourceInfo, + inTrackNameList, null); } /** * 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 inOptions creation options such as units * @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, SourceInfo inSourceInfo, TrackNameList inTrackNameList) + PointCreateOptions inOptions, SourceInfo inSourceInfo, TrackNameList inTrackNameList) + { + // no link array given + informDataLoaded(inFieldArray, inDataArray, inOptions, inSourceInfo, + inTrackNameList, null); + } + + /** + * Receive loaded data and determine whether to filter on tracks or not + * @param inFieldArray array of fields + * @param inDataArray array of data + * @param inOptions creation options such as units + * @param inSourceInfo information about the source of the data + * @param inTrackNameList information about the track names + * @param inLinkInfo links to photo/audio clips + */ + public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, PointCreateOptions inOptions, + SourceInfo inSourceInfo, TrackNameList inTrackNameList, MediaLinkInfo inLinkInfo) { // Check whether loaded array can be properly parsed into a Track Track loadedTrack = new Track(); - loadedTrack.load(inFieldArray, inDataArray, inAltFormat); + loadedTrack.load(inFieldArray, inDataArray, inOptions); if (loadedTrack.getNumPoints() <= 0) { showErrorMessage("error.load.dialogtitle", "error.load.nopoints"); @@ -667,19 +630,30 @@ public class App JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"), I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE); } + + _busyLoading = true; + // Attach photos and/or audio clips to points + if (inLinkInfo != null) + { + String[] linkArray = inLinkInfo.getLinkArray(); + if (linkArray != null) { + new AsyncMediaLoader(this, inLinkInfo.getZipFile(), linkArray, loadedTrack, inSourceInfo.getFile()).begin(); + } + } // 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(); + new SelectTracksFunction(this, loadedTrack, inSourceInfo, inTrackNameList).begin(); } else { // go directly to load informDataLoaded(loadedTrack, inSourceInfo); } + setCurrentMode(AppMode.NORMAL); } + /** * Receive loaded data and optionally merge with current Track * @param inLoadedTrack loaded track @@ -692,20 +666,25 @@ public class App { // ask whether to replace or append int answer = 0; - if (_dataFiles == null || _firstDataFile) { + if (_autoAppendNextFile) { + // Automatically append the next file + answer = JOptionPane.YES_OPTION; + } + else { + // Ask whether to append or not 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; - } + _autoAppendNextFile = false; // reset flag to cancel autoappend + if (answer == JOptionPane.YES_OPTION) { // append data to current Track - _undoStack.add(new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints())); + UndoLoad undo = new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints()); + undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios()); + _undoStack.add(undo); _track.combine(inLoadedTrack); // set source information inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints()); @@ -718,33 +697,41 @@ public class App if (_trackInfo.getPhotoList().hasCorrelatedPhotos()) { photos = _trackInfo.getPhotoList().cloneList(); } - _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos)); + UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos); + undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios()); + _undoStack.add(undo); _lastSavePosition = _undoStack.size(); _trackInfo.getSelection().clearAll(); _track.load(inLoadedTrack); inSourceInfo.populatePointObjects(_track, _track.getNumPoints()); _trackInfo.getFileInfo().replaceSource(inSourceInfo); - if (photos != null) { - _trackInfo.getPhotoList().removeCorrelatedPhotos(); - } + _trackInfo.getPhotoList().removeCorrelatedPhotos(); + _trackInfo.getAudioList().removeCorrelatedAudios(); } } else { // Currently no data held, so transfer received data - _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null)); + UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null); + undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios()); + _undoStack.add(undo); _lastSavePosition = _undoStack.size(); _trackInfo.getSelection().clearAll(); _track.load(inLoadedTrack); inSourceInfo.populatePointObjects(_track, _track.getNumPoints()); _trackInfo.getFileInfo().addSource(inSourceInfo); } + // Update config before subscribers are told + boolean isRegularLoad = (inSourceInfo.getFileType() != FILE_TYPE.GPSBABEL); + Config.getRecentFileList().addFile(new RecentFile(inSourceInfo.getFile(), isRegularLoad)); UpdateMessageBroker.informSubscribers(); // Update status bar UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile") + " '" + inSourceInfo.getName() + "'"); // update menu _menuManager.informFileLoaded(); + // Remove busy lock + _busyLoading = false; // load next file if there's a queue loadNextFile(); } @@ -759,12 +746,20 @@ public class App loadNextFile(); } + /** + * External trigger to automatically append the next loaded file + * instead of prompting to replace or append + */ + public void autoAppendNextFile() + { + _autoAppendNextFile = true; + } + /** * Load the next file in the waiting list, if any */ private void loadNextFile() { - _firstDataFile = false; if (_dataFiles == null || _dataFiles.size() == 0) { _dataFiles = null; } @@ -773,6 +768,7 @@ public class App public void run() { File f = _dataFiles.get(0); _dataFiles.remove(0); + _autoAppendNextFile = true; _fileLoader.openFile(f); } }).start(); @@ -805,92 +801,7 @@ public class App // MAYBE: Improve message when photo(s) fail to load (eg already added) UpdateMessageBroker.informSubscribers(); // update menu - _menuManager.informFileLoaded(); - } - } - - - /** - * Connect the current photo to the current point - */ - public void connectPhotoToPoint() - { - Photo photo = _trackInfo.getCurrentPhoto(); - DataPoint point = _trackInfo.getCurrentPoint(); - if (photo != null && point != null) - { - if (point.getPhoto() == null) - { - // point doesn't currently have a photo, so just connect it - _undoStack.add(new UndoConnectPhoto(point, photo.getFile().getName())); - photo.setDataPoint(point); - point.setPhoto(photo); - UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); - UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.connect")); - } - } - } - - - /** - * Disconnect the current photo from its point - */ - public void disconnectPhotoFromPoint() - { - Photo photo = _trackInfo.getCurrentPhoto(); - if (photo != null && photo.getDataPoint() != null) - { - DataPoint point = photo.getDataPoint(); - _undoStack.add(new UndoDisconnectPhoto(point, photo.getFile().getName())); - // disconnect - photo.setDataPoint(null); - point.setPhoto(null); - UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); - UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.disconnect")); - } - } - - - /** - * Remove the current photo, if any - */ - public void deleteCurrentPhoto() - { - // Delete the current photo, and optionally its point too, keeping undo information - Photo currentPhoto = _trackInfo.getCurrentPhoto(); - if (currentPhoto != null) - { - // Photo is selected, see if it has a point or not - boolean photoDeleted = false; - UndoDeletePhoto undoAction = null; - if (currentPhoto.getDataPoint() == null) - { - // no point attached, so just delete photo - undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(), - null, -1); - photoDeleted = _trackInfo.deleteCurrentPhoto(false); - } - else - { - // point is attached, so need to confirm point deletion - undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(), - currentPhoto.getDataPoint(), _trackInfo.getTrack().getPointIndex(currentPhoto.getDataPoint())); - int response = JOptionPane.showConfirmDialog(_frame, - I18nManager.getText("dialog.deletephoto.deletepoint"), - I18nManager.getText("dialog.deletephoto.title"), - JOptionPane.YES_NO_CANCEL_OPTION); - boolean deletePointToo = (response == JOptionPane.YES_OPTION); - // Cancel delete if cancel pressed or dialog closed - if (response == JOptionPane.YES_OPTION || response == JOptionPane.NO_OPTION) - { - photoDeleted = _trackInfo.deleteCurrentPhoto(deletePointToo); - } - } - // Add undo information to stack if necessary - if (photoDeleted) - { - _undoStack.add(undoAction); - } + if (numPointsAdded > 0) _menuManager.informFileLoaded(); } } @@ -978,32 +889,12 @@ public class App showErrorMessageNoLookup("error.undofailed.title", I18nManager.getText("error.undofailed.text") + " : " + ue.getMessage()); _undoStack.clear(); - UpdateMessageBroker.informSubscribers(); } catch (EmptyStackException empty) {} + UpdateMessageBroker.informSubscribers(); } - /** - * Helper method to parse an Object into an integer - * @param inObject object, eg from dialog - * @return int value given - */ - private static int parseNumber(Object inObject) - { - int num = 0; - if (inObject != null) - { - try - { - num = Integer.parseInt(inObject.toString()); - } - catch (NumberFormatException nfe) - {} - } - return num; - } - /** * Show a map url in an external browser * @param inSourceIndex index of map source to use @@ -1057,7 +948,7 @@ public class App */ public void setSidebarController(SidebarController inController) { - __sidebarController = inController; + _sidebarController = inController; } /** @@ -1065,6 +956,21 @@ public class App */ public void toggleSidebars() { - __sidebarController.toggle(); + _sidebarController.toggle(); + } + + /** @return true if App is currently busy with loading data */ + public boolean isBusyLoading() { + return _busyLoading; + } + + /** @return current app mode */ + public AppMode getCurrentMode() { + return _appMode; + } + + /** @param inMode the current app mode */ + public void setCurrentMode(AppMode inMode) { + _appMode = inMode; } }