X-Git-Url: http://gitweb.fperrin.net/?a=blobdiff_plain;f=tim%2Fprune%2FApp.java;h=c69d73e484463a722f68fcab2acde659e6337e73;hb=a6197ddcaac11c0b943183da7d46169742d024af;hp=6fab5833af1c56ecddb5b506a9c4b03a2de31bbd;hpb=f35b6d628f68e3b5ef19965ad8988d0dd1eb8efa;p=GpsPrune.git diff --git a/tim/prune/App.java b/tim/prune/App.java index 6fab583..c69d73e 100644 --- a/tim/prune/App.java +++ b/tim/prune/App.java @@ -9,34 +9,41 @@ import java.util.Stack; import javax.swing.JFrame; import javax.swing.JOptionPane; -import tim.prune.data.Altitude; -import tim.prune.data.AudioFile; +import tim.prune.config.Config; import tim.prune.data.Checker; import tim.prune.data.DataPoint; import tim.prune.data.Field; import tim.prune.data.LatLonRectangle; -import tim.prune.data.MediaFile; 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.gui.colour.ColourerCaretaker; +import tim.prune.gui.colour.PointColourer; import tim.prune.load.FileLoader; import tim.prune.load.JpegLoader; -import tim.prune.load.MediaHelper; +import tim.prune.load.MediaLinkInfo; import tim.prune.load.TrackNameList; import tim.prune.save.ExifSaver; import tim.prune.save.FileSaver; +import tim.prune.tips.TipManager; import tim.prune.undo.*; @@ -55,11 +62,17 @@ public class App private FileLoader _fileLoader = null; private JpegLoader _jpegLoader = null; private FileSaver _fileSaver = null; - private Stack _undoStack = null; + private UndoStack _undoStack = null; + private ColourerCaretaker _colCaretaker = null; 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}; /** @@ -69,10 +82,13 @@ public class App public App(JFrame inFrame) { _frame = inFrame; - _undoStack = new Stack(); + _undoStack = new UndoStack(); _track = new Track(); _trackInfo = new TrackInfo(_track); FunctionLibrary.initialise(this); + _colCaretaker = new ColourerCaretaker(this); + UpdateMessageBroker.addSubscriber(_colCaretaker); + _colCaretaker.setColourer(Config.getPointColourer()); } @@ -99,7 +115,7 @@ public class App public boolean hasDataUnsaved() { return (_undoStack.size() > _lastSavePosition - && (_track.getNumPoints() > 0 || _trackInfo.getPhotoList().getNumPhotos() > 0)); + && (_track.getNumPoints() > 0 || _trackInfo.getPhotoList().hasModifiedMedia())); } /** @@ -110,6 +126,40 @@ public class App return _undoStack; } + /** + * Update the system's point colourer using the one in the Config + */ + public void updatePointColourer() + { + if (_colCaretaker != null) { + _colCaretaker.setColourer(Config.getPointColourer()); + } + } + + /** + * @return colourer object, or null + */ + public PointColourer getPointColourer() + { + if (_colCaretaker == null) {return null;} + return _colCaretaker.getColourer(); + } + + /** + * Show the specified tip if appropriate + * @param inTipNumber tip number from TipManager + */ + public void showTip(int inTipNumber) + { + String key = TipManager.fireTipTrigger(inTipNumber); + if (key != null && !key.equals("")) + { + JOptionPane.showMessageDialog(_frame, I18nManager.getText(key), + I18nManager.getText("tip.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + + /** * Load the specified data files one by one * @param inDataFiles arraylist containing File objects to load @@ -119,14 +169,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); } } @@ -140,6 +191,7 @@ public class App { _undoStack.add(inUndo); UpdateMessageBroker.informSubscribers(inConfirmText); + setCurrentMode(AppMode.NORMAL); } /** @@ -209,6 +261,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); } } @@ -264,13 +320,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) @@ -283,10 +340,13 @@ 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()); + UndoDeletePoint undo = new UndoDeletePoint(pointIndex, currentPoint, photoIndex, + audioIndex, nextTrackPoint != null && nextTrackPoint.getSegmentStart()); + undo.setAtBoundaryOfSelectedRange(pointIndex == _trackInfo.getSelection().getStart() || + pointIndex == _trackInfo.getSelection().getEnd()); // call track to delete point if (_trackInfo.deletePoint()) { @@ -304,95 +364,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 */ @@ -632,31 +626,33 @@ 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, 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, inAltFormat, inSourceInfo, + informDataLoaded(inFieldArray, inDataArray, inOptions, inSourceInfo, inTrackNameList, null); } @@ -664,18 +660,17 @@ public class App * 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 - * @param inLinkArray array of links to photo/audio files + * @param inLinkInfo links to photo/audio clips */ - public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, - Altitude.Format inAltFormat, SourceInfo inSourceInfo, - TrackNameList inTrackNameList, String[] inLinkArray) + 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"); @@ -688,33 +683,27 @@ public class App JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"), I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE); } - // Attach photos and/or audio files to points - if (inLinkArray != null) + + _busyLoading = true; + // Attach photos and/or audio clips to points + if (inLinkInfo != null) { - for (int i=0; i 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); } @@ -730,16 +719,19 @@ 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 @@ -747,9 +739,6 @@ public class App undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios()); _undoStack.add(undo); _track.combine(inLoadedTrack); - // Add photos and audios (if any in loaded track) to list(s) - MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class); - MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class); // set source information inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints()); _trackInfo.getFileInfo().addSource(inSourceInfo); @@ -771,9 +760,6 @@ public class App _trackInfo.getFileInfo().replaceSource(inSourceInfo); _trackInfo.getPhotoList().removeCorrelatedPhotos(); _trackInfo.getAudioList().removeCorrelatedAudios(); - // Add photos and audios (if any in loaded track) to list(s) - MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class); - MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class); } } else @@ -787,16 +773,18 @@ public class App _track.load(inLoadedTrack); inSourceInfo.populatePointObjects(_track, _track.getNumPoints()); _trackInfo.getFileInfo().addSource(inSourceInfo); - // Add photos and audios (if any in loaded track) to list(s) - MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class); - MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class); } + // 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(); } @@ -811,12 +799,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; } @@ -825,6 +821,7 @@ public class App public void run() { File f = _dataFiles.get(0); _dataFiles.remove(0); + _autoAppendNextFile = true; _fileLoader.openFile(f); } }).start(); @@ -945,30 +942,16 @@ 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 + * @return the current data status, used for later comparison */ - private static int parseNumber(Object inObject) - { - int num = 0; - if (inObject != null) - { - try - { - num = Integer.parseInt(inObject.toString()); - } - catch (NumberFormatException nfe) - {} - } - return num; + public DataStatus getCurrentDataStatus() { + return new DataStatus(_undoStack.size(), _undoStack.getNumTimesDeleted()); } /** @@ -1034,4 +1017,19 @@ public class App { _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; + } }