From f35b6d628f68e3b5ef19965ad8988d0dd1eb8efa Mon Sep 17 00:00:00 2001 From: activityworkshop Date: Sun, 15 Feb 2015 10:49:41 +0100 Subject: [PATCH] Version 12, December 2010 --- tim/prune/App.java | 159 ++-- tim/prune/FunctionLibrary.java | 30 + tim/prune/GpsPruner.java | 15 +- tim/prune/config/Config.java | 2 + tim/prune/copyright.txt | 2 +- tim/prune/correlate/AudioCorrelator.java | 279 +++++++ .../correlate/AudioTimestampSelector.java | 75 ++ tim/prune/correlate/CardStack.java | 64 ++ tim/prune/correlate/Correlator.java | 682 +++++++++++++++++ ...Model.java => MediaPreviewTableModel.java} | 47 +- ...ableRow.java => MediaPreviewTableRow.java} | 15 +- .../correlate/MediaSelectionTableModel.java | 109 +++ .../correlate/MediaSelectionTableRow.java | 39 + .../correlate/OptionsChangedListener.java | 6 +- tim/prune/correlate/PhotoCorrelator.java | 626 +--------------- .../correlate/PhotoSelectionTableModel.java | 95 --- .../correlate/PhotoSelectionTableRow.java | 41 -- .../{PointPair.java => PointMediaPair.java} | 60 +- tim/prune/data/AudioFile.java | 45 ++ tim/prune/data/AudioList.java | 99 +++ tim/prune/data/DataPoint.java | 51 +- tim/prune/data/Field.java | 50 +- tim/prune/data/FieldList.java | 4 +- tim/prune/data/FieldType.java | 34 - tim/prune/data/MediaFile.java | 157 ++++ tim/prune/data/MediaList.java | 227 ++++++ tim/prune/data/Photo.java | 128 +--- tim/prune/data/PhotoList.java | 219 +----- tim/prune/data/Selection.java | 62 +- tim/prune/data/Timestamp.java | 33 +- tim/prune/data/Track.java | 2 +- tim/prune/data/TrackInfo.java | 253 +++++-- tim/prune/function/AboutScreen.java | 27 +- tim/prune/function/AddMapSourceDialog.java | 28 +- .../function/ConnectToPointFunction.java | 63 ++ .../function/DisconnectAudioFunction.java | 48 ++ .../function/DisconnectPhotoFunction.java | 47 ++ tim/prune/function/DownloadOsmFunction.java | 277 +++++++ tim/prune/function/GetWikipediaFunction.java | 133 ++++ .../function/GetWikipediaXmlHandler.java | 93 +++ tim/prune/function/PhotoPopupFunction.java | 113 +++ tim/prune/function/PlayAudioFunction.java | 153 ++++ tim/prune/function/RemoveAudioFunction.java | 68 ++ tim/prune/function/RemovePhotoFunction.java | 69 ++ tim/prune/function/SaveConfig.java | 7 +- tim/prune/function/SearchWikipediaNames.java | 144 ++++ tim/prune/function/SetLanguage.java | 13 +- tim/prune/function/SetLineWidth.java | 56 ++ tim/prune/function/StopAudioFunction.java | 35 + .../compress/ClosePointsAlgorithm.java | 2 +- .../compress/CompressTrackFunction.java | 2 +- .../compress/DuplicatePointAlgorithm.java | 4 +- .../function/compress/SingletonAlgorithm.java | 2 +- .../compress/WackyPointAlgorithm.java | 2 +- .../function/distance/DistanceFunction.java | 7 +- .../gpsies/GenericDownloaderFunction.java | 226 ++++++ .../function/gpsies/GetGpsiesFunction.java | 203 +---- tim/prune/function/gpsies/GpsiesTrack.java | 16 +- .../function/gpsies/GpsiesXmlHandler.java | 43 +- tim/prune/function/gpsies/TrackListModel.java | 17 +- .../function/gpsies/UploadGpsiesFunction.java | 14 +- tim/prune/gui/AudioListener.java | 52 ++ tim/prune/gui/DetailsDisplay.java | 137 +++- tim/prune/gui/DialogCloser.java | 33 + tim/prune/gui/IconManager.java | 6 + tim/prune/gui/MediaListModel.java | 47 ++ tim/prune/gui/MenuManager.java | 325 ++++---- tim/prune/gui/PhotoListModel.java | 57 -- tim/prune/gui/PhotoThumbnail.java | 76 +- tim/prune/gui/SelectorDisplay.java | 141 +++- tim/prune/gui/images/play_audio.gif | Bin 0 -> 207 bytes tim/prune/gui/images/show_details_icon.gif | Bin 0 -> 297 bytes tim/prune/gui/images/stop_audio.gif | Bin 0 -> 201 bytes tim/prune/gui/map/DiskTileCacher.java | 19 +- tim/prune/gui/map/MapCanvas.java | 133 +++- tim/prune/gui/map/MapSource.java | 34 +- tim/prune/jpeg/ExifGateway.java | 29 +- tim/prune/jpeg/ExternalExifLibrary.java | 7 +- tim/prune/jpeg/drew/ExifReader.java | 10 +- tim/prune/jpeg/drew/JpegSegmentReader.java | 105 +-- tim/prune/lang/prune-texts_af.properties | 6 +- tim/prune/lang/prune-texts_cz.properties | 99 ++- tim/prune/lang/prune-texts_de.properties | 90 ++- tim/prune/lang/prune-texts_de_CH.properties | 106 ++- tim/prune/lang/prune-texts_en.properties | 88 ++- tim/prune/lang/prune-texts_es.properties | 119 ++- tim/prune/lang/prune-texts_fa.properties | 6 +- tim/prune/lang/prune-texts_fr.properties | 40 +- tim/prune/lang/prune-texts_hu.properties | 693 ++++++++++++++++++ tim/prune/lang/prune-texts_in.properties | 8 +- tim/prune/lang/prune-texts_it.properties | 88 ++- tim/prune/lang/prune-texts_ja.properties | 36 +- tim/prune/lang/prune-texts_ko.properties | 693 ++++++++++++++++++ tim/prune/lang/prune-texts_nl.properties | 115 ++- tim/prune/lang/prune-texts_pl.properties | 86 ++- tim/prune/lang/prune-texts_pt.properties | 83 ++- tim/prune/lang/prune-texts_ro.properties | 6 +- tim/prune/lang/prune-texts_tr.properties | 34 +- tim/prune/lang/prune-texts_zh.properties | 63 +- tim/prune/load/AudioFileFilter.java | 13 + tim/prune/load/AudioLoader.java | 99 +++ tim/prune/load/GenericFileFilter.java | 13 +- tim/prune/load/JpegFileFilter.java | 13 + tim/prune/load/JpegLoader.java | 49 +- tim/prune/load/MediaHelper.java | 70 ++ .../{PhotoSorter.java => MediaSorter.java} | 8 +- tim/prune/load/TrackNameList.java | 15 +- tim/prune/load/xml/GpxHandler.java | 123 ++-- tim/prune/load/xml/GpxTag.java | 24 + tim/prune/load/xml/GzipFileLoader.java | 2 +- tim/prune/load/xml/XmlFileLoader.java | 11 +- tim/prune/load/xml/XmlHandler.java | 11 +- tim/prune/load/xml/ZipFileLoader.java | 2 +- tim/prune/readme.txt | 41 +- tim/prune/save/FileSaver.java | 10 +- tim/prune/save/GpsSaver.java | 17 +- tim/prune/save/GpxExporter.java | 168 ++++- tim/prune/save/KmlExporter.java | 137 ++-- tim/prune/save/PointTypeSelector.java | 56 +- tim/prune/save/PovExporter.java | 2 + tim/prune/save/SvgExporter.java | 5 +- tim/prune/save/SvgFragment.java | 8 + tim/prune/undo/UndoConnectMedia.java | 75 ++ tim/prune/undo/UndoConnectPhoto.java | 61 -- tim/prune/undo/UndoCorrelateAudios.java | 81 ++ tim/prune/undo/UndoCorrelatePhotos.java | 18 +- tim/prune/undo/UndoDeleteAudio.java | 70 ++ tim/prune/undo/UndoDeletePhoto.java | 5 +- tim/prune/undo/UndoDisconnectMedia.java | 73 ++ tim/prune/undo/UndoDisconnectPhoto.java | 62 -- tim/prune/undo/UndoLoad.java | 20 +- tim/prune/undo/UndoLoadAudios.java | 49 ++ 132 files changed, 7866 insertions(+), 2837 deletions(-) create mode 100644 tim/prune/correlate/AudioCorrelator.java create mode 100644 tim/prune/correlate/AudioTimestampSelector.java create mode 100644 tim/prune/correlate/CardStack.java create mode 100644 tim/prune/correlate/Correlator.java rename tim/prune/correlate/{PhotoPreviewTableModel.java => MediaPreviewTableModel.java} (74%) rename tim/prune/correlate/{PhotoPreviewTableRow.java => MediaPreviewTableRow.java} (70%) create mode 100644 tim/prune/correlate/MediaSelectionTableModel.java create mode 100644 tim/prune/correlate/MediaSelectionTableRow.java delete mode 100644 tim/prune/correlate/PhotoSelectionTableModel.java delete mode 100644 tim/prune/correlate/PhotoSelectionTableRow.java rename tim/prune/correlate/{PointPair.java => PointMediaPair.java} (63%) create mode 100644 tim/prune/data/AudioFile.java create mode 100644 tim/prune/data/AudioList.java delete mode 100644 tim/prune/data/FieldType.java create mode 100644 tim/prune/data/MediaFile.java create mode 100644 tim/prune/data/MediaList.java create mode 100644 tim/prune/function/ConnectToPointFunction.java create mode 100644 tim/prune/function/DisconnectAudioFunction.java create mode 100644 tim/prune/function/DisconnectPhotoFunction.java create mode 100644 tim/prune/function/DownloadOsmFunction.java create mode 100644 tim/prune/function/GetWikipediaFunction.java create mode 100644 tim/prune/function/GetWikipediaXmlHandler.java create mode 100644 tim/prune/function/PhotoPopupFunction.java create mode 100644 tim/prune/function/PlayAudioFunction.java create mode 100644 tim/prune/function/RemoveAudioFunction.java create mode 100644 tim/prune/function/RemovePhotoFunction.java create mode 100644 tim/prune/function/SearchWikipediaNames.java create mode 100644 tim/prune/function/SetLineWidth.java create mode 100644 tim/prune/function/StopAudioFunction.java create mode 100644 tim/prune/function/gpsies/GenericDownloaderFunction.java create mode 100644 tim/prune/gui/AudioListener.java create mode 100644 tim/prune/gui/DialogCloser.java create mode 100644 tim/prune/gui/MediaListModel.java delete mode 100644 tim/prune/gui/PhotoListModel.java create mode 100644 tim/prune/gui/images/play_audio.gif create mode 100644 tim/prune/gui/images/show_details_icon.gif create mode 100644 tim/prune/gui/images/stop_audio.gif create mode 100644 tim/prune/lang/prune-texts_hu.properties create mode 100644 tim/prune/lang/prune-texts_ko.properties create mode 100644 tim/prune/load/AudioFileFilter.java create mode 100644 tim/prune/load/AudioLoader.java create mode 100644 tim/prune/load/JpegFileFilter.java create mode 100644 tim/prune/load/MediaHelper.java rename tim/prune/load/{PhotoSorter.java => MediaSorter.java} (75%) create mode 100644 tim/prune/load/xml/GpxTag.java create mode 100644 tim/prune/undo/UndoConnectMedia.java delete mode 100644 tim/prune/undo/UndoConnectPhoto.java create mode 100644 tim/prune/undo/UndoCorrelateAudios.java create mode 100644 tim/prune/undo/UndoDeleteAudio.java create mode 100644 tim/prune/undo/UndoDisconnectMedia.java delete mode 100644 tim/prune/undo/UndoDisconnectPhoto.java create mode 100644 tim/prune/undo/UndoLoadAudios.java diff --git a/tim/prune/App.java b/tim/prune/App.java index 88cafea..6fab583 100644 --- a/tim/prune/App.java +++ b/tim/prune/App.java @@ -10,10 +10,12 @@ import javax.swing.JFrame; import javax.swing.JOptionPane; import tim.prune.data.Altitude; +import tim.prune.data.AudioFile; 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; @@ -31,6 +33,7 @@ import tim.prune.gui.UndoManager; import tim.prune.gui.Viewport; import tim.prune.load.FileLoader; import tim.prune.load.JpegLoader; +import tim.prune.load.MediaHelper; import tim.prune.load.TrackNameList; import tim.prune.save.ExifSaver; import tim.prune.save.FileSaver; @@ -48,7 +51,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; @@ -638,7 +641,7 @@ public class App public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, Altitude.Format inAltFormat, SourceInfo inSourceInfo) { - informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null); + informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null, null); } /** @@ -651,6 +654,24 @@ public class App */ public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList) + { + // no link array given + informDataLoaded(inFieldArray, inDataArray, inAltFormat, 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 inSourceInfo information about the source of the data + * @param inTrackNameList information about the track names + * @param inLinkArray array of links to photo/audio files + */ + public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, + Altitude.Format inAltFormat, SourceInfo inSourceInfo, + TrackNameList inTrackNameList, String[] inLinkArray) { // Check whether loaded array can be properly parsed into a Track Track loadedTrack = new Track(); @@ -667,6 +688,22 @@ 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) + { + for (int i=0; i 1) { @@ -680,6 +717,7 @@ public class App } } + /** * Receive loaded data and optionally merge with current Track * @param inLoadedTrack loaded track @@ -705,8 +743,13 @@ public class App 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); + // 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); @@ -718,26 +761,35 @@ 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(); + // 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 { // 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); + // 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); } UpdateMessageBroker.informSubscribers(); // Update status bar @@ -805,92 +857,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(); } } @@ -1057,7 +1024,7 @@ public class App */ public void setSidebarController(SidebarController inController) { - __sidebarController = inController; + _sidebarController = inController; } /** @@ -1065,6 +1032,6 @@ public class App */ public void toggleSidebars() { - __sidebarController.toggle(); + _sidebarController.toggle(); } } diff --git a/tim/prune/FunctionLibrary.java b/tim/prune/FunctionLibrary.java index 92947f0..93532b5 100644 --- a/tim/prune/FunctionLibrary.java +++ b/tim/prune/FunctionLibrary.java @@ -1,5 +1,6 @@ package tim.prune; +import tim.prune.correlate.AudioCorrelator; import tim.prune.correlate.PhotoCorrelator; import tim.prune.function.*; import tim.prune.function.charts.Charter; @@ -9,6 +10,7 @@ import tim.prune.function.edit.PointNameEditor; import tim.prune.function.gpsies.GetGpsiesFunction; import tim.prune.function.gpsies.UploadGpsiesFunction; import tim.prune.function.srtm.LookupSrtmFunction; +import tim.prune.load.AudioLoader; import tim.prune.load.GpsLoader; import tim.prune.save.GpsSaver; import tim.prune.save.GpxExporter; @@ -33,6 +35,9 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null; public static GenericFunction FUNCTION_COMPRESS = null; public static GenericFunction FUNCTION_LOOKUP_SRTM = null; + public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null; + public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null; + public static GenericFunction FUNCTION_DOWNLOAD_OSM = null; public static GenericFunction FUNCTION_ADD_TIME_OFFSET = null; public static GenericFunction FUNCTION_ADD_ALTITUDE_OFFSET = null; public static GenericFunction FUNCTION_CONVERT_NAMES_TO_TIMES = null; @@ -40,9 +45,14 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_PASTE_COORDINATES = null; public static GenericFunction FUNCTION_FIND_WAYPOINT = null; public static GenericFunction FUNCTION_DUPLICATE_POINT = null; + public static GenericFunction FUNCTION_CONNECT_TO_POINT = null; + public static GenericFunction FUNCTION_DISCONNECT_PHOTO = null; + public static GenericFunction FUNCTION_REMOVE_PHOTO = null; + public static GenericFunction FUNCTION_DISCONNECT_AUDIO = null; public static GenericFunction FUNCTION_CORRELATE_PHOTOS = null; public static GenericFunction FUNCTION_ROTATE_PHOTO_LEFT = null; public static GenericFunction FUNCTION_ROTATE_PHOTO_RIGHT = null; + public static GenericFunction FUNCTION_PHOTO_POPUP = null; public static GenericFunction FUNCTION_IGNORE_EXIF_THUMB = null; public static GenericFunction FUNCTION_CHARTS = null; public static GenericFunction FUNCTION_3D = null; @@ -50,11 +60,17 @@ public abstract class FunctionLibrary public static GenericFunction FUNCTION_FULL_RANGE_DETAILS = null; public static GenericFunction FUNCTION_GET_GPSIES = null; public static GenericFunction FUNCTION_UPLOAD_GPSIES = null; + public static GenericFunction FUNCTION_LOAD_AUDIO = null; + public static GenericFunction FUNCTION_REMOVE_AUDIO = null; + public static GenericFunction FUNCTION_CORRELATE_AUDIOS = null; + public static GenericFunction FUNCTION_PLAY_AUDIO = null; + public static GenericFunction FUNCTION_STOP_AUDIO = null; public static GenericFunction FUNCTION_SET_MAP_BG = null; public static GenericFunction FUNCTION_SET_DISK_CACHE = null; public static GenericFunction FUNCTION_SET_PATHS = null; public static GenericFunction FUNCTION_SET_KMZ_IMAGE_SIZE = null; public static GenericFunction FUNCTION_SET_COLOURS = null; + public static GenericFunction FUNCTION_SET_LINE_WIDTH = null; public static GenericFunction FUNCTION_SET_LANGUAGE = null; public static GenericFunction FUNCTION_HELP = null; public static GenericFunction FUNCTION_SHOW_KEYS = null; @@ -80,6 +96,9 @@ public abstract class FunctionLibrary FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp); FUNCTION_COMPRESS = new CompressTrackFunction(inApp); FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp); + FUNCTION_LOOKUP_WIKIPEDIA = new GetWikipediaFunction(inApp); + FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp); + FUNCTION_DOWNLOAD_OSM = new DownloadOsmFunction(inApp); FUNCTION_ADD_TIME_OFFSET = new AddTimeOffset(inApp); FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp); FUNCTION_CONVERT_NAMES_TO_TIMES = new ConvertNamesToTimes(inApp); @@ -87,9 +106,13 @@ public abstract class FunctionLibrary FUNCTION_PASTE_COORDINATES = new PasteCoordinates(inApp); FUNCTION_FIND_WAYPOINT = new FindWaypoint(inApp); FUNCTION_DUPLICATE_POINT = new DuplicatePoint(inApp); + FUNCTION_CONNECT_TO_POINT = new ConnectToPointFunction(inApp); + FUNCTION_DISCONNECT_PHOTO = new DisconnectPhotoFunction(inApp); + FUNCTION_REMOVE_PHOTO = new RemovePhotoFunction(inApp); FUNCTION_CORRELATE_PHOTOS = new PhotoCorrelator(inApp); FUNCTION_ROTATE_PHOTO_LEFT = new RotatePhoto(inApp, false); FUNCTION_ROTATE_PHOTO_RIGHT = new RotatePhoto(inApp, true); + FUNCTION_PHOTO_POPUP = new PhotoPopupFunction(inApp); FUNCTION_IGNORE_EXIF_THUMB = new IgnoreExifThumb(inApp); FUNCTION_CHARTS = new Charter(inApp); FUNCTION_3D = new ShowThreeDFunction(inApp); @@ -97,11 +120,18 @@ public abstract class FunctionLibrary FUNCTION_FULL_RANGE_DETAILS = new FullRangeDetails(inApp); FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp); FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp); + FUNCTION_LOAD_AUDIO = new AudioLoader(inApp); + FUNCTION_REMOVE_AUDIO = new RemoveAudioFunction(inApp); + FUNCTION_CORRELATE_AUDIOS = new AudioCorrelator(inApp); + FUNCTION_PLAY_AUDIO = new PlayAudioFunction(inApp); + FUNCTION_STOP_AUDIO = new StopAudioFunction(inApp); + FUNCTION_DISCONNECT_AUDIO = new DisconnectAudioFunction(inApp); FUNCTION_SET_MAP_BG = new SetMapBgFunction(inApp); FUNCTION_SET_DISK_CACHE = new DiskCacheConfig(inApp); FUNCTION_SET_PATHS = new SetPathsFunction(inApp); FUNCTION_SET_KMZ_IMAGE_SIZE = new SetKmzImageSize(inApp); FUNCTION_SET_COLOURS = new SetColours(inApp); + FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp); FUNCTION_SET_LANGUAGE = new SetLanguage(inApp); FUNCTION_HELP = new HelpScreen(inApp); FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp); diff --git a/tim/prune/GpsPruner.java b/tim/prune/GpsPruner.java index 1242404..217d7d4 100644 --- a/tim/prune/GpsPruner.java +++ b/tim/prune/GpsPruner.java @@ -35,9 +35,9 @@ import tim.prune.gui.profile.ProfileChart; public class GpsPruner { /** Version number of application, used in about screen and for version check */ - public static final String VERSION_NUMBER = "11.2"; + public static final String VERSION_NUMBER = "12"; /** Build number, just used for about screen */ - public static final String BUILD_NUMBER = "205a"; + public static final String BUILD_NUMBER = "223"; /** Static reference to App object */ private static App APP = null; @@ -67,12 +67,7 @@ public class GpsPruner for (int i=0; i used to specify a configuration file" - + "\n --lang= or --locale= used to specify language" + + "\n --lang= used to specify language code such as DE" + "\n --langfile= used to specify an alternative language file\n"); } // Initialise configuration if selected @@ -164,7 +159,7 @@ public class GpsPruner { return new Locale(inString); } - else if (inString.length() == 5) + else if (inString.length() == 5 && inString.charAt(2) == '_') { return new Locale(inString.substring(0, 2), inString.substring(3)); } diff --git a/tim/prune/config/Config.java b/tim/prune/config/Config.java index 1423138..0873b0f 100644 --- a/tim/prune/config/Config.java +++ b/tim/prune/config/Config.java @@ -59,6 +59,8 @@ public abstract class Config public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath"; /** Key for colour scheme */ public static final String KEY_COLOUR_SCHEME = "prune.colourscheme"; + /** Key for line width used for drawing */ + public static final String KEY_LINE_WIDTH = "prune.linewidth"; /** Key for kml track colour */ public static final String KEY_KML_TRACK_COLOUR = "prune.kmltrackcolour"; diff --git a/tim/prune/copyright.txt b/tim/prune/copyright.txt index a24ac60..c25b77e 100644 --- a/tim/prune/copyright.txt +++ b/tim/prune/copyright.txt @@ -1,5 +1,5 @@ The source code of Prune is copyright 2006-2010 activityworkshop.net -and distributed under the terms of the Gnu GPL version 2. +and is distributed under the terms of the Gnu GPL version 2. Portions of the package jpeg.drew (if included in this package) were taken from Drew Noakes' "Metadata extractor" v2.3.1 which is diff --git a/tim/prune/correlate/AudioCorrelator.java b/tim/prune/correlate/AudioCorrelator.java new file mode 100644 index 0000000..57ecb7f --- /dev/null +++ b/tim/prune/correlate/AudioCorrelator.java @@ -0,0 +1,279 @@ +package tim.prune.correlate; + +import java.awt.FlowLayout; +import java.awt.GridLayout; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTable; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.AudioFile; +import tim.prune.data.AudioList; +import tim.prune.data.DataPoint; +import tim.prune.data.MediaFile; +import tim.prune.data.MediaList; +import tim.prune.data.TimeDifference; +import tim.prune.data.Timestamp; +import tim.prune.undo.UndoCorrelateAudios; + +/** + * Class to manage the automatic correlation of audio files to points + * which is very similar to the PhotoCorrelator apart from the clip lengths + */ +public class AudioCorrelator extends Correlator +{ + private AudioTimestampSelector _fileTimesSelector = null, _correlTimesSelector = null; + + + /** + * Constructor + * @param inApp App object + */ + public AudioCorrelator(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.correlateaudios"; + } + + /** @return type key */ + protected String getMediaTypeKey() { + return "audio"; + } + + /** @return photo list*/ + protected MediaList getMediaList() { + return _app.getTrackInfo().getAudioList(); + } + + + /** + * @return first gui panel including timestamp specification (beginning, middle, end) + */ + protected JPanel makeFirstPanel() + { + // First panel for timestamp stuff + JPanel card1 = new JPanel(); + card1.setLayout(new FlowLayout(FlowLayout.CENTER)); + JPanel grid1 = new JPanel(); + grid1.setLayout(new GridLayout(0, 1)); + _fileTimesSelector = new AudioTimestampSelector("dialog.correlate.filetimes", "dialog.correlate.filetimes2"); + grid1.add(_fileTimesSelector); + _correlTimesSelector = new AudioTimestampSelector("dialog.correlate.correltimes", null); + grid1.add(_correlTimesSelector); + card1.add(grid1); + return card1; + } + + + /** + * @return array of boolean flags denoting availability of cards + */ + protected boolean[] getCardEnabledFlags() + { + boolean[] cards = super.getCardEnabledFlags(); + cards[0] = getAudioLengthAvailability(_app.getTrackInfo().getAudioList()); + return cards; + } + + /** + * @param inAudios AudioList object + * @return true if there are any audio lengths available + */ + private static boolean getAudioLengthAvailability(AudioList inAudios) + { + for (int i=0; i 0) {return true;} + } + return false; + } + + /** + * Create a preview of the correlate action using the selected time difference + * @param inTimeDiff TimeDifference to use for preview + * @param inShowWarning true to show warning if all points out of range + */ + protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning) + { + TimeDifference timeLimit = parseTimeLimit(); + double angDistLimit = parseDistanceLimit(); + MediaPreviewTableModel model = new MediaPreviewTableModel("dialog.correlate.select.audioname"); + AudioList audios = _app.getTrackInfo().getAudioList(); + // Loop through audios deciding whether to set correlate flag or not + int numAudios = audios.getNumAudios(); + for (int i=0; i 0.0 && correlateAudio) + { + final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter()); + double frac = pair.getFraction(); + if (frac > 0.5) {frac = 1 - frac;} + final double angDistPhoto = angDistPair * frac; + correlateAudio = (angDistPhoto < angDistLimit); + } + // Don't select audios which are already correlated to the same point + if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(audio.getDataPoint())) { + correlateAudio = false; + } + row.setCorrelateFlag(correlateAudio); + model.addRow(row); + } + _previewTable.setModel(model); + // Set distance units + model.setDistanceUnits(getSelectedDistanceUnits()); + // Set column widths + _previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); + final int[] colWidths = {150, 160, 100, 100, 50}; + for (int i=0; i 0 && secsToAdd != 0) { + tstamp = tstamp.createPlusOffset(secsToAdd); + } + } + catch (ClassCastException cce) {} + return tstamp; + } + + /** + * Finish the correlation by modifying the track + * and passing the Undo information back to the App + */ + protected void finishCorrelation() + { + // TODO: Probably should be able to combine this into the Correlator? + PointMediaPair[] pointPairs = getPointPairs(); + if (pointPairs == null || pointPairs.length <= 0) {return;} + + // begin to construct undo information + UndoCorrelateAudios undo = new UndoCorrelateAudios(_app.getTrackInfo()); + // loop over Audios + int arraySize = pointPairs.length; + int i = 0, numAudios = 0; + int numPointsToCreate = 0; + PointMediaPair pair = null; + for (i=0; i 0) + { + // make new array for added points + DataPoint[] addedPoints = new DataPoint[numPointsToCreate]; + int pointNum = 0; + DataPoint pointToAdd = null; + for (i=0; i 0L) + { + // interpolate point + pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction()); + } + if (pointToAdd != null) + { + // link audio to point + pointToAdd.setAudio((AudioFile) pair.getMedia()); + pair.getMedia().setDataPoint(pointToAdd); + // set to start of segment so not joined in track + pointToAdd.setSegmentStart(true); + // add to point array + addedPoints[pointNum] = pointToAdd; + pointNum++; + } + } + } + // expand track + _app.getTrackInfo().getTrack().appendPoints(addedPoints); + } + + // send undo information back to controlling app + undo.setNumAudiosCorrelated(numAudios); + _app.completeFunction(undo, ("" + numAudios + " " + + (numAudios==1?I18nManager.getText("confirm.correlateaudios.single"):I18nManager.getText("confirm.correlateaudios.multi")))); + // observers already informed by track update if new points created + if (numPointsToCreate == 0) { + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + } + } +} diff --git a/tim/prune/correlate/AudioTimestampSelector.java b/tim/prune/correlate/AudioTimestampSelector.java new file mode 100644 index 0000000..ee0ce0d --- /dev/null +++ b/tim/prune/correlate/AudioTimestampSelector.java @@ -0,0 +1,75 @@ +package tim.prune.correlate; + +import java.awt.BorderLayout; +import java.awt.GridLayout; + +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.border.EtchedBorder; + +import tim.prune.I18nManager; + +/** + * GUI element to allow the selection of timestamp options + * for audio file correlation + */ +public class AudioTimestampSelector extends JPanel +{ + /** Array of radio buttons */ + private JRadioButton[] _radios = new JRadioButton[3]; + + + /** + * Constructor + * @param inTopLabelKey key for description label at top + * @param inLowerLabelKey key for description label at bottom, if any + */ + public AudioTimestampSelector(String inTopLabelKey, String inLowerLabelKey) + { + createComponents(inTopLabelKey, inLowerLabelKey); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4)) + ); + } + + /** + * Create the GUI components + * @param inTopLabelKey key for description label at top + * @param inLowerLabelKey key for description label at bottom, if any + */ + private void createComponents(String inTopLabelKey, String inLowerLabelKey) + { + setLayout(new BorderLayout()); + add(new JLabel(I18nManager.getText(inTopLabelKey)), BorderLayout.NORTH); + // panel for the radio buttons + JPanel gridPanel = new JPanel(); + gridPanel.setLayout(new GridLayout(0, 3, 15, 3)); + final String[] keys = {"beginning", "middle", "end"}; + ButtonGroup group = new ButtonGroup(); + for (int i=0; i<3; i++) + { + _radios[i] = new JRadioButton(I18nManager.getText("dialog.correlate.timestamp." + keys[i])); + group.add(_radios[i]); + gridPanel.add(_radios[i]); + } + _radios[0].setSelected(true); + add(gridPanel, BorderLayout.CENTER); + if (inLowerLabelKey != null) { + add(new JLabel(I18nManager.getText(inLowerLabelKey)), BorderLayout.SOUTH); + } + } + + /** + * Get the option selected by the user + * @return 0 for beginning, 1 for middle or 2 for end + */ + public int getSelectedOption() + { + for (int i=0; i<_radios.length; i++) + if (_radios[i].isSelected()) {return i;} + return 0; + } +} diff --git a/tim/prune/correlate/CardStack.java b/tim/prune/correlate/CardStack.java new file mode 100644 index 0000000..555c544 --- /dev/null +++ b/tim/prune/correlate/CardStack.java @@ -0,0 +1,64 @@ +package tim.prune.correlate; + +import java.awt.CardLayout; +import java.awt.Component; + +import javax.swing.JPanel; + +/** + * Panel to act as a card stack + */ +public class CardStack extends JPanel +{ + private int _numCards = 0; + private int _currCard = 0; + private CardLayout _layout = null; + private static final String cardName = "card"; + + /** + * Constructor + */ + public CardStack() + { + _layout = new CardLayout(); + setLayout(_layout); + } + + /** + * Add a card to the stack + * @param inComponent component to add + */ + public void addCard(Component inComponent) + { + super.add(inComponent, cardName + _numCards); + _numCards++; + } + + /** + * @return current card index, starting from 0 + */ + public int getCurrentCardIndex() + { + return _currCard; + } + + /** + * @return number of cards in the stack + */ + public int getNumCards() + { + return _numCards; + } + + /** + * Show the specified card + * @param inIndex index of card, starting from 0 + */ + public void showCard(int inIndex) + { + if (inIndex >= 0 && inIndex < _numCards) { + _currCard = inIndex; + _layout.show(this, cardName + inIndex); + } + } +} diff --git a/tim/prune/correlate/Correlator.java b/tim/prune/correlate/Correlator.java new file mode 100644 index 0000000..248be18 --- /dev/null +++ b/tim/prune/correlate/Correlator.java @@ -0,0 +1,682 @@ +package tim.prune.correlate; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Calendar; +import java.util.Iterator; +import java.util.TreeSet; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.DataPoint; +import tim.prune.data.Distance; +import tim.prune.data.Field; +import tim.prune.data.MediaFile; +import tim.prune.data.MediaList; +import tim.prune.data.TimeDifference; +import tim.prune.data.Timestamp; +import tim.prune.data.Track; + +/** + * Abstract superclass of the two correlator functions + */ +public abstract class Correlator extends GenericFunction +{ + protected JDialog _dialog; + private CardStack _cards = null; + private JLabel _tipLabel = null; + private JTable _selectionTable = null; + protected JTable _previewTable = null; + private boolean _previewEnabled = false; // flag required to enable preview function on final panel + private boolean[] _cardEnabled = null; // flag for each card + private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null; + private JRadioButton _mediaLaterOption = null, _pointLaterOption = null; + private JRadioButton _timeLimitRadio = null, _distLimitRadio = null; + private JTextField _limitMinBox = null, _limitSecBox = null; + private JTextField _limitDistBox = null; + private JComboBox _distUnitsDropdown = null; + private JButton _nextButton = null, _backButton = null; + protected JButton _okButton = null; + + /** + * Constructor + * @param inApp App object to report actions to + */ + public Correlator(App inApp) { + super(inApp); + } + + /** + * @return type key eg photo, audio + */ + protected abstract String getMediaTypeKey(); + + /** + * @return media list + */ + protected abstract MediaList getMediaList(); + + /** + * Begin the function by initialising and showing the dialog + */ + public void begin() + { + // Check whether track has timestamps, exit if not + if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP)) + { + JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"), + I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE); + return; + } + // Show warning if no uncorrelated audios + if (!getMediaList().hasUncorrelatedMedia()) + { + Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")}; + if (JOptionPane.showOptionDialog(_parentFrame, + I18nManager.getText("dialog.correlate.nouncorrelated" + getMediaTypeKey() + "s"), + I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) + == JOptionPane.NO_OPTION) + { + return; + } + } + // Create dialog if necessary + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.getContentPane().add(makeDialogContents()); + _dialog.pack(); + } + // Go to first available card + int card = 0; + _cardEnabled = null; + while (!isCardEnabled(card)) {card++;} + _cards.showCard(card); + showCard(0); // does set up and next/prev enabling + _okButton.setEnabled(false); + _dialog.setVisible(true); + } + + /** + * Make contents of correlate dialog + * @return JPanel containing gui elements + */ + private JPanel makeDialogContents() + { + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + + // Card panel in the middle + _cards = new CardStack(); + + // First panel (not required by photo correlator) + JPanel card1 = makeFirstPanel(); + if (card1 == null) {card1 = new JPanel();} + _cards.addCard(card1); + + // Second panel for selection of linked media + _cards.addCard(makeSecondPanel()); + + // Third panel for options and preview + _cards.addCard(makeThirdPanel()); + mainPanel.add(_cards, BorderLayout.CENTER); + + // Button panel at the bottom + JPanel buttonPanel = new JPanel(); + _backButton = new JButton(I18nManager.getText("button.back")); + _backButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCard(-1); + } + }); + _backButton.setEnabled(false); + buttonPanel.add(_backButton); + _nextButton = new JButton(I18nManager.getText("button.next")); + _nextButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + showCard(1); + } + }); + buttonPanel.add(_nextButton); + _okButton = new JButton(I18nManager.getText("button.ok")); + _okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + finishCorrelation(); + _dialog.dispose(); + } + }); + _okButton.setEnabled(false); + buttonPanel.add(_okButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + return mainPanel; + } + + /** + * Construct a table model for the photo / audio selection table + * @return table model + */ + protected MediaSelectionTableModel makeSelectionTableModel() + { + MediaList mediaList = getMediaList(); + MediaSelectionTableModel model = new MediaSelectionTableModel( + "dialog.correlate.select." + getMediaTypeKey() + "name", + "dialog.correlate.select." + getMediaTypeKey() + "later"); + int numMedia = mediaList.getNumMedia(); + for (int i=0; i set = new TreeSet(); + // loop through rows of table adding to list + int numRows = inModel.getRowCount(); + int i; + for (i=0; i iterator = set.iterator(); + for (i=0; i<(numRows+1)/2; i++) + { + pair = iterator.next(); + } + return pair.getIndex(); + } + + + /** + * Disable the ok button + */ + public void disableOkButton() + { + if (_okButton != null) { + _okButton.setEnabled(false); + } + } + + /** + * @return gui components for first panel, or null if empty + */ + protected JPanel makeFirstPanel() { + return null; + } + + /** + * Make the second panel for the selection screen + * @return JPanel object containing gui elements + */ + private JPanel makeSecondPanel() + { + JPanel card = new JPanel(); + card.setLayout(new BorderLayout(10, 10)); + card.add(new JLabel(I18nManager.getText( + "dialog.correlate." + getMediaTypeKey() + "select.intro")), BorderLayout.NORTH); + // table doesn't have model yet - that will be attached later + _selectionTable = new JTable(); + JScrollPane photoScrollPane = new JScrollPane(_selectionTable); + photoScrollPane.setPreferredSize(new Dimension(400, 100)); + card.add(photoScrollPane, BorderLayout.CENTER); + return card; + } + + + /** + * Make contents of third panel including options and preview + * @return JPanel containing gui elements + */ + private JPanel makeThirdPanel() + { + OptionsChangedListener optionsChangedListener = new OptionsChangedListener(this); + // Second panel for options + JPanel card2 = new JPanel(); + card2.setLayout(new BorderLayout()); + JPanel card2Top = new JPanel(); + card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS)); + _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip")); + _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6)); + card2Top.add(_tipLabel); + JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro")); + introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6)); + card2Top.add(introLabel); + // time offset section + JPanel offsetPanel = new JPanel(); + offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel"))); + offsetPanel.setLayout(new BoxLayout(offsetPanel, BoxLayout.Y_AXIS)); + JPanel offsetPanelTop = new JPanel(); + offsetPanelTop.setLayout(new FlowLayout()); + offsetPanelTop.setBorder(null); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset") + ": ")); + _offsetHourBox = new JTextField(3); + _offsetHourBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetHourBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours"))); + _offsetMinBox = new JTextField(3); + _offsetMinBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetMinBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes"))); + _offsetSecBox = new JTextField(3); + _offsetSecBox.addKeyListener(optionsChangedListener); + offsetPanelTop.add(_offsetSecBox); + offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds"))); + offsetPanel.add(offsetPanelTop); + + // radio buttons for photo / point later + JPanel offsetPanelBot = new JPanel(); + offsetPanelBot.setLayout(new FlowLayout()); + offsetPanelBot.setBorder(null); + _mediaLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options." + getMediaTypeKey() + "later")); + _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlaterphoto")); + _mediaLaterOption.addItemListener(optionsChangedListener); + _pointLaterOption.addItemListener(optionsChangedListener); + ButtonGroup laterGroup = new ButtonGroup(); + laterGroup.add(_mediaLaterOption); + laterGroup.add(_pointLaterOption); + offsetPanelBot.add(_mediaLaterOption); + offsetPanelBot.add(_pointLaterOption); + offsetPanel.add(offsetPanelBot); + offsetPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + card2Top.add(offsetPanel); + + // time limits section + JPanel limitsPanel = new JPanel(); + limitsPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.limitspanel"))); + limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS)); + JPanel timeLimitPanel = new JPanel(); + timeLimitPanel.setLayout(new FlowLayout()); + JRadioButton noTimeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.notimelimit")); + noTimeLimitRadio.addItemListener(optionsChangedListener); + timeLimitPanel.add(noTimeLimitRadio); + _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : "); + _timeLimitRadio.addItemListener(optionsChangedListener); + timeLimitPanel.add(_timeLimitRadio); + groupRadioButtons(noTimeLimitRadio, _timeLimitRadio); + _limitMinBox = new JTextField(3); + _limitMinBox.addKeyListener(optionsChangedListener); + timeLimitPanel.add(_limitMinBox); + timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes"))); + _limitSecBox = new JTextField(3); + _limitSecBox.addKeyListener(optionsChangedListener); + timeLimitPanel.add(_limitSecBox); + timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds"))); + limitsPanel.add(timeLimitPanel); + // distance limits + JPanel distLimitPanel = new JPanel(); + distLimitPanel.setLayout(new FlowLayout()); + JRadioButton noDistLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.nodistancelimit")); + noDistLimitRadio.addItemListener(optionsChangedListener); + distLimitPanel.add(noDistLimitRadio); + _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit")); + _distLimitRadio.addItemListener(optionsChangedListener); + distLimitPanel.add(_distLimitRadio); + groupRadioButtons(noDistLimitRadio, _distLimitRadio); + _limitDistBox = new JTextField(4); + _limitDistBox.addKeyListener(optionsChangedListener); + distLimitPanel.add(_limitDistBox); + String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"), + I18nManager.getText("units.miles")}; + _distUnitsDropdown = new JComboBox(distUnitsOptions); + _distUnitsDropdown.addItemListener(optionsChangedListener); + distLimitPanel.add(_distUnitsDropdown); + limitsPanel.add(distLimitPanel); + limitsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + card2Top.add(limitsPanel); + + // preview button + JButton previewButton = new JButton(I18nManager.getText("button.preview")); + previewButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + createPreview(true); + } + }); + card2Top.add(previewButton); + card2.add(card2Top, BorderLayout.NORTH); + // preview + _previewTable = new JTable(new MediaPreviewTableModel("dialog.correlate.select." + getMediaTypeKey() + "name")); + JScrollPane previewScrollPane = new JScrollPane(_previewTable); + previewScrollPane.setPreferredSize(new Dimension(300, 100)); + card2.add(previewScrollPane, BorderLayout.CENTER); + return card2; + } + + + /** + * Go to the next or previous card in the stack + * @param increment 1 for next, -1 for previous card + */ + private void showCard(int increment) + { + int currCard = _cards.getCurrentCardIndex(); + int next = currCard + increment; + if (!isCardEnabled(next)) { + next += increment; + } + setupCard(next); + _backButton.setEnabled(next > 0 && (isCardEnabled(next-1) || isCardEnabled(next-2))); + _nextButton.setEnabled(next < (_cards.getNumCards()-1)); + _cards.showCard(next); + } + + /** + * @param inCardNum index of card + * @return true if specified card is enabled + */ + private boolean isCardEnabled(int inCardNum) + { + if (_cardEnabled == null) {_cardEnabled = getCardEnabledFlags();} + return (inCardNum >= 0 && inCardNum < _cardEnabled.length && _cardEnabled[inCardNum]); + } + + /** + * @return array of boolean flags denoting availability of cards + */ + protected boolean[] getCardEnabledFlags() + { + // by default first is off and third is always on; second depends on selection table + return new boolean[] {false, makeSelectionTableModel().getRowCount() > 0, true}; + } + + /** + * Set up the specified card + * @param inCardNum index of card + */ + protected void setupCard(int inCardNum) + { + _previewEnabled = false; + if (inCardNum == 1) + { + // set up photo selection card + MediaSelectionTableModel model = makeSelectionTableModel(); + _selectionTable.setModel(model); + for (int i=0; i _list = new ArrayList(); + private ArrayList _list = new ArrayList(); /** Distance units */ private Distance.Units _distanceUnits = Distance.Units.KILOMETRES; /** Number formatter */ @@ -26,16 +28,21 @@ public class PhotoPreviewTableModel extends AbstractTableModel FORMAT_ONE_DP.setMinimumFractionDigits(1); } + /** + * Constructor + * @param inFirstColumnKey key for first column heading + */ + public MediaPreviewTableModel(String inFirstColumnKey) { + _firstColumnHeading = I18nManager.getText(inFirstColumnKey); + } /** * @return the column count, always 5 */ - public int getColumnCount() - { + public int getColumnCount() { return 5; } - /** * Get the name of the column * @param inColNum column number @@ -43,9 +50,9 @@ public class PhotoPreviewTableModel extends AbstractTableModel */ public String getColumnName(int inColNum) { - if (inColNum == 0) return I18nManager.getText("dialog.correlate.photoselect.photoname"); + if (inColNum == 0) return _firstColumnHeading; else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp"); - else if (inColNum == 2) return I18nManager.getText("dialog.correlate.photoselect.timediff"); + else if (inColNum == 2) return I18nManager.getText("dialog.correlate.select.timediff"); else if (inColNum == 3) return I18nManager.getText("fieldname.distance"); return I18nManager.getText("dialog.correlate.options.correlate"); } @@ -65,7 +72,7 @@ public class PhotoPreviewTableModel extends AbstractTableModel * @param inRowIndex row index * @return table row object */ - public PhotoPreviewTableRow getRow(int inRowIndex) + public MediaPreviewTableRow getRow(int inRowIndex) { return _list.get(inRowIndex); } @@ -79,10 +86,10 @@ public class PhotoPreviewTableModel extends AbstractTableModel */ public Object getValueAt(int inRowIndex, int inColumnIndex) { - PhotoPreviewTableRow row = _list.get(inRowIndex); - if (inColumnIndex == 0) return row.getPhoto().getFile().getName(); + MediaPreviewTableRow row = _list.get(inRowIndex); + if (inColumnIndex == 0) return row.getMedia().getFile().getName(); else if (inColumnIndex == 1) { - return row.getPhoto().getTimestamp().getText(); + return row.getMedia().getTimestamp().getText(); } else if (inColumnIndex == 2) { if (row.getPointPair().isValid()) { @@ -119,10 +126,10 @@ public class PhotoPreviewTableModel extends AbstractTableModel /** - * Add a photo to the list + * Add a row to the list * @param inRow row to add */ - public void addPhotoRow(PhotoPreviewTableRow inRow) + public void addRow(MediaPreviewTableRow inRow) { _list.add(inRow); } @@ -153,12 +160,11 @@ public class PhotoPreviewTableModel extends AbstractTableModel /** * @return true if any of the correlate flags are on */ - public boolean hasPhotosSelected() + public boolean hasAnySelected() { for (int i=0; i _list = new ArrayList(); + + + /** + * Constructor + * @param inFirstColumnKey key for first column heading + * @param inLastColumnKey key for last column heading + */ + public MediaSelectionTableModel(String inFirstColumnKey, String inLastColumnKey) + { + _firstColumnHeading = I18nManager.getText(inFirstColumnKey); + _lastColumnHeading = I18nManager.getText(inLastColumnKey); + } + + /** + * @return the column count, always 4 + */ + public int getColumnCount() { + return 4; + } + + /** + * Get the name of the column + * @param inColNum column number + * @return column name + */ + public String getColumnName(int inColNum) + { + if (inColNum == 0) return _firstColumnHeading; + else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp"); + else if (inColNum == 2) return I18nManager.getText("dialog.correlate.select.timediff"); + return _lastColumnHeading; + } + + + /** + * @return the row count + */ + public int getRowCount() + { + return _list.size(); + } + + + /** + * Get the selected row from the table + * @param inRowIndex row index + * @return table row object + */ + public MediaSelectionTableRow getRow(int inRowIndex) + { + return _list.get(inRowIndex); + } + + + /** + * Get the value of the specified cell + * @param inRowIndex row index + * @param inColumnIndex column index + * @return value of specified cell + */ + public Object getValueAt(int inRowIndex, int inColumnIndex) + { + // MAYBE: only show time of photos (not date) if dates all identical + MediaSelectionTableRow row = _list.get(inRowIndex); + if (inColumnIndex == 0) return row.getMedia().getFile().getName(); + else if (inColumnIndex == 1) return row.getMedia().getTimestamp().getText(); + else if (inColumnIndex == 2) return row.getTimeDiff().getDescription(); + return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") : + I18nManager.getText("dialog.about.no")); + } + + + /** + * Clear the list + */ + public void reset() { + _list.clear(); + } + + /** + * Add a media object to the list + * @param inMedia item to add + * @param inTimeDiff time difference + */ + public void addMedia(MediaFile inMedia, long inTimeDiff) + { + _list.add(new MediaSelectionTableRow(inMedia, inTimeDiff)); + } +} diff --git a/tim/prune/correlate/MediaSelectionTableRow.java b/tim/prune/correlate/MediaSelectionTableRow.java new file mode 100644 index 0000000..6b9660b --- /dev/null +++ b/tim/prune/correlate/MediaSelectionTableRow.java @@ -0,0 +1,39 @@ +package tim.prune.correlate; + +import tim.prune.data.MediaFile; +import tim.prune.data.TimeDifference; + +/** + * Class to hold the contents of a single row + * in the selection table for correlation + */ +public class MediaSelectionTableRow +{ + private MediaFile _media = null; + private TimeDifference _timeDiff = null; + + /** + * Constructor + * @param inMedia media item + * @param inNumSecs number of seconds time difference as long + */ + public MediaSelectionTableRow(MediaFile inMedia, long inNumSecs) + { + _media = inMedia; + _timeDiff = new TimeDifference(inNumSecs); + } + + /** + * @return Media object + */ + public MediaFile getMedia() { + return _media; + } + + /** + * @return time difference + */ + public TimeDifference getTimeDiff() { + return _timeDiff; + } +} diff --git a/tim/prune/correlate/OptionsChangedListener.java b/tim/prune/correlate/OptionsChangedListener.java index 70420f7..2da93e6 100644 --- a/tim/prune/correlate/OptionsChangedListener.java +++ b/tim/prune/correlate/OptionsChangedListener.java @@ -8,13 +8,13 @@ import java.awt.event.KeyEvent; import java.awt.event.KeyListener; /** - * Helper class to listen for changed options on the PhotoCorrelator + * Helper class to listen for changed options on the Correlators * Tightly coupled but only to ok button and preview function */ public class OptionsChangedListener implements KeyListener, ActionListener, ItemListener, Runnable { /** Correlator object for callbacks */ - private PhotoCorrelator _correlator; + private Correlator _correlator; /** Thread counter */ private int _threadCount = 0; @@ -26,7 +26,7 @@ public class OptionsChangedListener implements KeyListener, ActionListener, Item * Constructor * @param inCorrelator correlator object for callbacks */ - public OptionsChangedListener(PhotoCorrelator inCorrelator) + public OptionsChangedListener(Correlator inCorrelator) { _correlator = inCorrelator; } diff --git a/tim/prune/correlate/PhotoCorrelator.java b/tim/prune/correlate/PhotoCorrelator.java index 84c179e..f5f86b1 100644 --- a/tim/prune/correlate/PhotoCorrelator.java +++ b/tim/prune/correlate/PhotoCorrelator.java @@ -1,73 +1,30 @@ package tim.prune.correlate; -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Calendar; -import java.util.Iterator; -import java.util.TreeSet; - -import javax.swing.BorderFactory; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JDialog; -import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.JTextField; import tim.prune.App; -import tim.prune.GenericFunction; +import tim.prune.DataSubscriber; import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; import tim.prune.data.DataPoint; -import tim.prune.data.Distance; -import tim.prune.data.Field; +import tim.prune.data.MediaList; import tim.prune.data.Photo; import tim.prune.data.PhotoList; import tim.prune.data.TimeDifference; -import tim.prune.data.Timestamp; -import tim.prune.data.Track; -import tim.prune.data.TrackInfo; import tim.prune.undo.UndoCorrelatePhotos; /** * Class to manage the automatic correlation of photos to points * including the GUI stuff to control the correlation options */ -public class PhotoCorrelator extends GenericFunction +public class PhotoCorrelator extends Correlator { - private JDialog _dialog; - private JButton _nextButton = null, _backButton = null; - private JButton _okButton = null; - private JPanel _cards = null; - private JTable _photoSelectionTable = null; - private JLabel _tipLabel = null; - private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null; - private JRadioButton _photoLaterOption = null, _pointLaterOption = null; - private JRadioButton _timeLimitRadio = null, _distLimitRadio = null; - private JTextField _limitMinBox = null, _limitSecBox = null; - private JTextField _limitDistBox = null; - private JComboBox _distUnitsDropdown = null; - private JTable _previewTable = null; - private boolean _firstTabAvailable = false; - private boolean _previewEnabled = false; // flag required to enable preview function on second panel - - /** * Constructor * @param inApp App object to report actions to */ - public PhotoCorrelator(App inApp) - { + public PhotoCorrelator(App inApp) { super(inApp); } @@ -77,360 +34,34 @@ public class PhotoCorrelator extends GenericFunction return "function.correlatephotos"; } - /** - * Reset dialog and show it - */ - public void begin() - { - // First create dialog if necessary - if (_dialog == null) - { - _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); - _dialog.setLocationRelativeTo(_parentFrame); - _dialog.getContentPane().add(makeDialogContents()); - _dialog.pack(); - } - // Check whether track has timestamps, exit if not - if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP)) - { - JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"), - I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE); - return; - } - // Check for any non-correlated photos, show warning continue/cancel - if (!trackHasUncorrelatedPhotos()) - { - Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")}; - if (JOptionPane.showOptionDialog(_parentFrame, I18nManager.getText("dialog.correlate.nouncorrelatedphotos"), - I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) - == JOptionPane.NO_OPTION) - { - return; - } - } - PhotoSelectionTableModel model = makePhotoSelectionTableModel(_app.getTrackInfo()); - _firstTabAvailable = model != null && model.getRowCount() > 0; - CardLayout cl = (CardLayout) _cards.getLayout(); - if (_firstTabAvailable) - { - cl.first(_cards); - _nextButton.setEnabled(true); - _backButton.setEnabled(false); - _tipLabel.setVisible(false); - _photoSelectionTable.setModel(model); - _previewEnabled = false; - for (int i=0; i set = new TreeSet(); - // loop through rows of table adding to list - int numRows = inModel.getRowCount(); - int i; - for (i=0; i iterator = set.iterator(); - for (i=0; i<(numRows+1)/2; i++) - { - pair = iterator.next(); - } - return pair.getIndex(); - } - - - /** - * Disable the ok button - */ - public void disableOkButton() - { - if (_okButton != null) { - _okButton.setEnabled(false); - } - } - - - /** - * Check if the track has any uncorrelated photos - * @return true if there are any photos which are not connected to points - */ - private boolean trackHasUncorrelatedPhotos() - { - PhotoList photoList = _app.getTrackInfo().getPhotoList(); - int numPhotos = photoList.getNumPhotos(); - // loop over photos - for (int i=0; i _list = new ArrayList(); - - - /** - * @return the column count, always 4 - */ - public int getColumnCount() - { - return 4; - } - - - /** - * Get the name of the column - * @param inColNum column number - * @return column name - */ - public String getColumnName(int inColNum) - { - if (inColNum == 0) return I18nManager.getText("dialog.correlate.photoselect.photoname"); - else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp"); - else if (inColNum == 2) return I18nManager.getText("dialog.correlate.photoselect.timediff"); - return I18nManager.getText("dialog.correlate.photoselect.photolater"); - } - - - /** - * @return the row count - */ - public int getRowCount() - { - return _list.size(); - } - - - /** - * Get the selected row from the table - * @param inRowIndex row index - * @return table row object - */ - public PhotoSelectionTableRow getRow(int inRowIndex) - { - return _list.get(inRowIndex); - } - - - /** - * Get the value of the specified cell - * @param inRowIndex row index - * @param inColumnIndex column index - * @return value of specified cell - */ - public Object getValueAt(int inRowIndex, int inColumnIndex) - { - // MAYBE: only show time of photos (not date) if dates all identical - PhotoSelectionTableRow row = _list.get(inRowIndex); - if (inColumnIndex == 0) return row.getPhoto().getFile().getName(); - else if (inColumnIndex == 1) return row.getPhoto().getTimestamp().getText(); - else if (inColumnIndex == 2) return row.getTimeDiff().getDescription(); - return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") : - I18nManager.getText("dialog.about.no")); - } - - - /** - * Clear the list - */ - public void reset() - { - _list.clear(); - } - - /** - * Add a photo to the list - * @param inPhoto photo to add - * @param inTimeDiff time difference - */ - public void addPhoto(Photo inPhoto, long inTimeDiff) - { - _list.add(new PhotoSelectionTableRow(inPhoto, inTimeDiff)); - } -} diff --git a/tim/prune/correlate/PhotoSelectionTableRow.java b/tim/prune/correlate/PhotoSelectionTableRow.java deleted file mode 100644 index e818bbe..0000000 --- a/tim/prune/correlate/PhotoSelectionTableRow.java +++ /dev/null @@ -1,41 +0,0 @@ -package tim.prune.correlate; - -import tim.prune.data.Photo; -import tim.prune.data.TimeDifference; - -/** - * Class to hold contents of a single row - * in the photo selection table - */ -public class PhotoSelectionTableRow -{ - private Photo _photo = null; - private TimeDifference _timeDiff = null; - - /** - * Constructor - * @param inPhoto Photo object - * @param inNumSecs number of seconds time difference as long - */ - public PhotoSelectionTableRow(Photo inPhoto, long inNumSecs) - { - _photo = inPhoto; - _timeDiff = new TimeDifference(inNumSecs); - } - - /** - * @return Photo object - */ - public Photo getPhoto() - { - return _photo; - } - - /** - * @return time difference - */ - public TimeDifference getTimeDiff() - { - return _timeDiff; - } -} diff --git a/tim/prune/correlate/PointPair.java b/tim/prune/correlate/PointMediaPair.java similarity index 63% rename from tim/prune/correlate/PointPair.java rename to tim/prune/correlate/PointMediaPair.java index 9b27ff9..1ea8307 100644 --- a/tim/prune/correlate/PointPair.java +++ b/tim/prune/correlate/PointMediaPair.java @@ -1,15 +1,14 @@ package tim.prune.correlate; import tim.prune.data.DataPoint; -import tim.prune.data.Photo; +import tim.prune.data.MediaFile; /** - * Class to hold a pair of points - * used to hold the result of correlation of a photo + * Class to hold a pair of points used to hold the result of correlation */ -public class PointPair +public class PointMediaPair { - private Photo _photo = null; + private MediaFile _media = null; private DataPoint _pointBefore = null; private DataPoint _pointAfter = null; private long _secondsBefore = 1L; @@ -18,14 +17,12 @@ public class PointPair /** * Constructor - * @param inPhoto Photo object + * @param inMedia media object */ - public PointPair(Photo inPhoto) - { - _photo = inPhoto; + public PointMediaPair(MediaFile inMedia) { + _media = inMedia; } - /** * Add a point to the pair * @param inPoint data point @@ -36,10 +33,10 @@ public class PointPair // Check if point is closest point before if (inSeconds <= 0) { - // point stamp is before photo stamp + // point stamp is before media stamp if (inSeconds > _secondsBefore || _secondsBefore > 0L) { - // point stamp is nearer to photo + // point stamp is nearer to media _pointBefore = inPoint; _secondsBefore = inSeconds; } @@ -47,10 +44,10 @@ public class PointPair // Check if point is closest point after if (inSeconds >= 0) { - // point stamp is after photo stamp + // point stamp is after media stamp if (inSeconds < _secondsAfter || _secondsAfter < 0L) { - // point stamp is nearer to photo + // point stamp is nearer to media _pointAfter = inPoint; _secondsAfter = inSeconds; } @@ -59,50 +56,44 @@ public class PointPair /** - * @return Photo object + * @return Media object */ - public Photo getPhoto() - { - return _photo; + public MediaFile getMedia() { + return _media; } /** - * @return the closest point before the photo + * @return the closest point before the media */ - public DataPoint getPointBefore() - { + public DataPoint getPointBefore() { return _pointBefore; } /** - * @return number of seconds between photo and subsequent point + * @return number of seconds between media and subsequent point */ - public long getSecondsBefore() - { + public long getSecondsBefore() { return _secondsBefore; } /** - * @return the closest point after the photo + * @return the closest point after the media */ - public DataPoint getPointAfter() - { + public DataPoint getPointAfter() { return _pointAfter; } /** - * @return number of seconds between previous point and photo + * @return number of seconds between previous point and media */ - public long getSecondsAfter() - { + public long getSecondsAfter() { return _secondsAfter; } /** * @return true if both points found */ - public boolean isValid() - { + public boolean isValid() { return getPointBefore() != null && getPointAfter() != null; } @@ -118,13 +109,12 @@ public class PointPair /** * @return the number of seconds to the nearest point */ - public long getMinSeconds() - { + public long getMinSeconds() { return Math.min(_secondsAfter, -_secondsBefore); } /** - * @return angle from photo to nearest point in radians + * @return angle from media to nearest point in radians */ public double getMinRadians() { diff --git a/tim/prune/data/AudioFile.java b/tim/prune/data/AudioFile.java new file mode 100644 index 0000000..43089ab --- /dev/null +++ b/tim/prune/data/AudioFile.java @@ -0,0 +1,45 @@ +package tim.prune.data; + +import java.io.File; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioSystem; + +/** + * Class to represent an audio file for correlation + */ +public class AudioFile extends MediaFile +{ + /** length of current audio file in seconds */ + private int _lengthInSeconds = LENGTH_UNKNOWN; + + private static final int LENGTH_UNKNOWN = -1; + private static final int LENGTH_NOT_AVAILABLE = -2; + + /** + * Constructor + * @param inFile file object + */ + public AudioFile(File inFile) + { + // Timestamp is always just taken from the file modification stamp + super(inFile, new Timestamp(inFile.lastModified())); + } + + /** + * @return length of this audio file in seconds + */ + public int getLengthInSeconds() + { + if (_lengthInSeconds == LENGTH_UNKNOWN) + { + try { + AudioFileFormat format = AudioSystem.getAudioFileFormat(getFile()); + _lengthInSeconds = (int) (format.getFrameLength() / format.getFormat().getFrameRate()); + } + catch (Exception e) { + _lengthInSeconds = LENGTH_NOT_AVAILABLE; + } + } + return _lengthInSeconds; + } +} diff --git a/tim/prune/data/AudioList.java b/tim/prune/data/AudioList.java new file mode 100644 index 0000000..928f2b9 --- /dev/null +++ b/tim/prune/data/AudioList.java @@ -0,0 +1,99 @@ +package tim.prune.data; + +import java.util.ArrayList; + +/** + * Class to hold a list of audio files, using the MediaList superclass + */ +public class AudioList extends MediaList +{ + /** + * Empty constructor + */ + public AudioList() { + this(null); + } + + /** + * Constructor + * @param inList ArrayList containing audio file objects + */ + private AudioList(ArrayList inList) { + super(inList); + } + + /** + * @return clone of list contents + */ + public AudioList cloneList() + { + if (getNumMedia() == 0) return this; + ArrayList listCopy = new ArrayList(); + listCopy.addAll(_media); + return new AudioList(listCopy); + } + + /** + * @return the number of audio files in the list + */ + public int getNumAudios() { + return getNumMedia(); + } + + /** + * Add an audio file to the list + * @param inAudio object to add + */ + public void addAudio(AudioFile inAudio) { + addMedia(inAudio); + } + + /** + * Add an audio file to the list + * @param inAudio object to add + * @param inIndex index at which to add + */ + public void addAudio(AudioFile inAudio, int inIndex) { + addMedia(inAudio, inIndex); + } + + /** + * Remove the selected audio file from the list + * @param inIndex index number to remove + */ + public void deleteAudio(int inIndex) { + deleteMedia(inIndex); + } + + /** + * Get the index of the given audio file + * @param inAudio object to check + * @return index of this object in the list, or -1 if not found + */ + public int getAudioIndex(AudioFile inAudio) { + return getMediaIndex(inAudio); + } + + /** + * Get the Audio object at the given index + * @param inIndex index number, starting at 0 + * @return specified object + */ + public AudioFile getAudio(int inIndex) { + return (AudioFile) getMedia(inIndex); + } + + /** + * @return true if list contains correlated objects + */ + public boolean hasCorrelatedAudios() { + return hasCorrelatedMedia(); + } + + /** + * Remove all correlated media from the list + */ + public void removeCorrelatedAudios() { + removeCorrelatedMedia(); + } +} diff --git a/tim/prune/data/DataPoint.java b/tim/prune/data/DataPoint.java index bba7adc..e053e1a 100644 --- a/tim/prune/data/DataPoint.java +++ b/tim/prune/data/DataPoint.java @@ -17,7 +17,10 @@ public class DataPoint private Coordinate _latitude = null, _longitude = null; private Altitude _altitude; private Timestamp _timestamp = null; + /** Attached photo */ private Photo _photo = null; + /** Attached audio file */ + private AudioFile _audio = null; private String _waypointName = null; private boolean _startOfSegment = false; private boolean _markedForDeletion = false; @@ -299,20 +302,51 @@ public class DataPoint * Set the photo for this data point * @param inPhoto Photo object */ - public void setPhoto(Photo inPhoto) - { + public void setPhoto(Photo inPhoto) { _photo = inPhoto; + _modifyCount++; } - /** * @return associated Photo object */ - public Photo getPhoto() - { + public Photo getPhoto() { return _photo; } + /** + * Set the audio file for this point + * @param inAudio audio object + */ + public void setAudio(AudioFile inAudio) { + _audio = inAudio; + _modifyCount++; + } + + /** + * @return associated audio object + */ + public AudioFile getAudio() { + return _audio; + } + + /** + * Attach the given media object according to type + * @param inMedia either a photo or an audio file + */ + public void attachMedia(MediaFile inMedia) + { + if (inMedia != null) { + if (inMedia instanceof Photo) { + setPhoto((Photo) inMedia); + inMedia.setDataPoint(this); + } + else if (inMedia instanceof AudioFile) { + setAudio((AudioFile) inMedia); + inMedia.setDataPoint(this); + } + } + } /** * @return true if the point is valid @@ -322,6 +356,13 @@ public class DataPoint return _latitude.isValid() && _longitude.isValid(); } + /** + * @return true if the point has either a photo or audio attached + */ + public boolean hasMedia() { + return _photo != null || _audio != null; + } + /** * Interpolate a set of points between this one and the given one * @param inEndPoint end point of interpolation diff --git a/tim/prune/data/Field.java b/tim/prune/data/Field.java index 5d0adc4..fa4600d 100644 --- a/tim/prune/data/Field.java +++ b/tim/prune/data/Field.java @@ -4,41 +4,44 @@ import tim.prune.I18nManager; /** * Class to represent a field of a data point - * including its type */ public class Field { private String _labelKey = null; private String _customLabel = null; - private FieldType _type = null; private boolean _builtin = false; - public static final Field LATITUDE = new Field("fieldname.latitude", FieldType.COORD); - public static final Field LONGITUDE = new Field("fieldname.longitude", FieldType.COORD); - public static final Field ALTITUDE = new Field("fieldname.altitude", FieldType.INT); - public static final Field TIMESTAMP = new Field("fieldname.timestamp", FieldType.TIME); - public static final Field WAYPT_NAME = new Field("fieldname.waypointname", FieldType.NONE); - public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", FieldType.NONE); - public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", FieldType.BOOL); + public static final Field LATITUDE = new Field("fieldname.latitude", true); + public static final Field LONGITUDE = new Field("fieldname.longitude", true); + public static final Field ALTITUDE = new Field("fieldname.altitude", true); + public static final Field TIMESTAMP = new Field("fieldname.timestamp", true); + public static final Field WAYPT_NAME = new Field("fieldname.waypointname", true); + public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", true); + public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", true); // TODO: Field for photo filename, ability to load (from text) and save (to text) private static final Field[] ALL_AVAILABLE_FIELDS = { LATITUDE, LONGITUDE, ALTITUDE, TIMESTAMP, WAYPT_NAME, WAYPT_TYPE, NEW_SEGMENT, - new Field("fieldname.custom", FieldType.NONE) + new Field(I18nManager.getText("fieldname.custom")) }; /** * Private constructor * @param inLabelKey Key for label texts - * @param inType type of field + * @param inBuiltin true for built-in types, false for custom */ - private Field(String inLabelKey, FieldType inType) + private Field(String inLabelKey, boolean inBuiltin) { - _labelKey = inLabelKey; - _customLabel = null; - _type = inType; - _builtin = true; + if (inBuiltin) { + _labelKey = inLabelKey; + _customLabel = null; + } + else { + _labelKey = null; + _customLabel = inLabelKey; + } + _builtin = inBuiltin; } @@ -48,9 +51,7 @@ public class Field */ public Field(String inLabel) { - _labelKey = null; - _customLabel = inLabel; - _type = FieldType.NONE; + this(inLabel, false); } /** @@ -80,14 +81,6 @@ public class Field return _builtin; } - /** - * @return field type - */ - public FieldType getType() - { - return _type; - } - /** * Checks if the two fields are equal * @param inOther other Field object @@ -105,7 +98,8 @@ public class Field */ public static Field getField(String inFieldName) { - for (int i=0; i _media = null; + + + /** + * Empty constructor + */ + public MediaList() { + this(null); + } + + /** + * Constructor + * @param inList ArrayList containing media objects + */ + protected MediaList(ArrayList inList) + { + _media = inList; + if (_media == null) { + _media = new ArrayList(); + } + } + + /** + * @return the number of media in the list + */ + public int getNumMedia() { + return _media.size(); + } + + /** + * Add an object to the list + * @param inObject object to add + */ + public void addMedia(MediaFile inObject) + { + if (inObject != null) { + _media.add(inObject); + } + } + + /** + * Add an object to the list at a specified index (used for undo) + * @param inObject object to add + * @param inIndex index at which to add + */ + public void addMedia(MediaFile inObject, int inIndex) + { + if (inObject != null) { + _media.add(inIndex, inObject); + } + } + + + /** + * Remove the selected media from the list + * @param inIndex index number to remove + */ + public void deleteMedia(int inIndex) + { + // Maybe throw exception if this fails? + _media.remove(inIndex); + } + + + /** + * Checks if the specified object is already in the list + * @param inMedia media object to check + * @return true if it's already in the list + */ + public boolean contains(MediaFile inMedia) { + return (getMediaIndex(inMedia) > -1); + } + + + /** + * Get the index of the given media + * @param inMedia object to check + * @return index of this object in the list, or -1 if not found + */ + public int getMediaIndex(MediaFile inMedia) + { + // Check if we need to check + final int num = getNumMedia(); + if (num <= 0 || inMedia == null || inMedia.getFile() == null) + return -1; + // Loop over list + for (int i=0; i= getNumMedia()) return null; + return _media.get(inIndex); + } + + + /** + * Crop the list to the specified size + * @param inIndex previous size + */ + public void cropTo(int inIndex) + { + if (inIndex <= 0) + { + // delete whole list + _media.clear(); + } + else + { + // delete to previous size + while (_media.size() > inIndex) { + _media.remove(_media.size()-1); + } + } + } + + + /** + * @return array of file names + */ + public String[] getNameList() + { + final int num = getNumMedia(); + String[] names = new String[num]; + for (int i=0; i 0) + { + // Construct new list to copy into + ArrayList listCopy = new ArrayList(); + // Loop over list + for (MediaFile m : _media) + { + // Copy media if it has no point + if (m != null) + { + if (m.getDataPoint() == null) + listCopy.add(m); + else + m.resetCachedData(); + } + } + // Switch reference to new list + _media = listCopy; + } + } + + /** + * @return clone of list contents + */ + public abstract MediaList cloneList(); + + /** + * Restore contents from other MediaList + * @param inOther MediaList with cloned contents + */ + public void restore(MediaList inOther) + { + _media.clear(); + if (inOther != null && inOther.getNumMedia() > 0) + { + // Copy contents from other list + _media.addAll(inOther._media); + } + } +} diff --git a/tim/prune/data/Photo.java b/tim/prune/data/Photo.java index 7ffe35d..c904674 100644 --- a/tim/prune/data/Photo.java +++ b/tim/prune/data/Photo.java @@ -8,93 +8,23 @@ import javax.swing.ImageIcon; /** * Class to represent a photo and link to DataPoint */ -public class Photo +public class Photo extends MediaFile { - /** File where photo is stored */ - private File _file = null; - /** Timestamp, if any */ - private Timestamp _timestamp = null; - /** Associated DataPoint if correlated */ - private DataPoint _dataPoint = null; /** Size of original image */ private Dimension _size = null; - /** Status of photo when loaded */ - private Status _originalStatus = Status.NOT_CONNECTED; - /** Current photo status */ - private Status _currentStatus = Status.NOT_CONNECTED; /** rotation flag (clockwise from 0 to 3) */ private int _rotation = 0; // TODO: Need to store caption for image? // thumbnail for image (from exif) private byte[] _exifThumbnail = null; - /** Photo status */ - public enum Status { - /** Photo is not connected to any point */ - NOT_CONNECTED, - /** Photo has been connected to a point since it was loaded */ - TAGGED, - /** Photo is connected to a point */ - CONNECTED - }; - /** * Constructor * @param inFile File object for photo */ public Photo(File inFile) { - _file = inFile; - } - - - /** - * @return File object where photo resides - */ - public File getFile() - { - return _file; - } - - - /** - * Set the data point associated with the photo - * @param inPoint DataPoint with coordinates etc - */ - public void setDataPoint(DataPoint inPoint) - { - _dataPoint = inPoint; - // set status according to point - if (inPoint == null) { - setCurrentStatus(Status.NOT_CONNECTED); - } - else { - setCurrentStatus(Status.CONNECTED); - } - } - - /** - * @return the DataPoint object - */ - public DataPoint getDataPoint() - { - return _dataPoint; - } - - /** - * @param inTimestamp Timestamp of photo - */ - public void setTimestamp(Timestamp inTimestamp) - { - _timestamp = inTimestamp; - } - - /** - * @return timestamp of photo - */ - public Timestamp getTimestamp() - { - return _timestamp; + super(inFile, null); } /** @@ -116,8 +46,7 @@ public class Photo */ public Dimension getSize() { - if (_size == null) - { + if (_size == null) { calculateSize(); } return _size; @@ -149,46 +78,6 @@ public class Photo return _size.height; } - /** - * @param inStatus status of photo when loaded - */ - public void setOriginalStatus(Status inStatus) - { - _originalStatus = inStatus; - _currentStatus = inStatus; - } - - /** - * @return status of photo when it was loaded - */ - public Status getOriginalStatus() - { - return _originalStatus; - } - - /** - * @return current status of photo - */ - public Status getCurrentStatus() - { - return _currentStatus; - } - /** - * @param inStatus current status of photo - */ - public void setCurrentStatus(Status inStatus) - { - _currentStatus = inStatus; - } - - /** - * @return true if photo is connected to a point - */ - public boolean isConnected() - { - return _currentStatus != Status.NOT_CONNECTED; - } - /** * @return byte array of thumbnail data */ @@ -214,17 +103,6 @@ public class Photo // remove thumbnail too } - /** - * Check if a Photo object refers to the same File as another - * @param inOther other Photo object - * @return true if the Files are the same - */ - public boolean equals(Photo inOther) - { - return (inOther != null && inOther.getFile() != null && getFile() != null - && inOther.getFile().equals(getFile())); - } - /** * @param inRotation initial rotation value (from exif) */ diff --git a/tim/prune/data/PhotoList.java b/tim/prune/data/PhotoList.java index 18ebe15..4540553 100644 --- a/tim/prune/data/PhotoList.java +++ b/tim/prune/data/PhotoList.java @@ -3,17 +3,14 @@ package tim.prune.data; import java.util.ArrayList; /** - * Class to hold a list of Photos + * Class to hold a list of Photos, using the MediaList superclass */ -public class PhotoList +public class PhotoList extends MediaList { - private ArrayList _photos = null; - /** * Empty constructor */ - public PhotoList() - { + public PhotoList() { this(null); } @@ -21,236 +18,82 @@ public class PhotoList * Constructor * @param inList ArrayList containing Photo objects */ - private PhotoList(ArrayList inList) - { - _photos = inList; + private PhotoList(ArrayList inList) { + super(inList); } - /** - * @return the number of photos in the list + * @return clone of list contents */ - public int getNumPhotos() + public PhotoList cloneList() { - if (_photos == null) return 0; - return _photos.size(); + if (getNumMedia() == 0) return this; + ArrayList listCopy = new ArrayList(); + listCopy.addAll(_media); + return new PhotoList(listCopy); } + /** + * @return the number of photos in the list + */ + public int getNumPhotos() { + return getNumMedia(); + } /** * Add a Photo to the list * @param inPhoto Photo object to add */ - public void addPhoto(Photo inPhoto) - { - if (inPhoto != null) - { - // Make sure array is initialised - if (_photos == null) - { - _photos = new ArrayList(); - } - // Add the photo - _photos.add(inPhoto); - } + public void addPhoto(Photo inPhoto) { + addMedia(inPhoto); } - /** * Add a Photo to the list * @param inPhoto Photo object to add * @param inIndex index at which to add photo */ - public void addPhoto(Photo inPhoto, int inIndex) - { - if (inPhoto != null) - { - // Make sure array is initialised - if (_photos == null) - { - _photos = new ArrayList(); - } - // Add the photo - _photos.add(inIndex, inPhoto); - } + public void addPhoto(Photo inPhoto, int inIndex) { + addMedia(inPhoto, inIndex); } - /** * Remove the selected photo from the list * @param inIndex index number to remove */ - public void deletePhoto(int inIndex) - { - // Maybe throw exception if this fails? - if (_photos != null) - { - _photos.remove(inIndex); - } - } - - - /** - * Checks if the specified Photo is already in the list - * @param inPhoto Photo object to check - * @return true if it's already in the list - */ - public boolean contains(Photo inPhoto) - { - return (getPhotoIndex(inPhoto) > -1); + public void deletePhoto(int inIndex) { + deleteMedia(inIndex); } - /** * Get the index of the given Photo * @param inPhoto Photo object to check * @return index of this Photo in the list, or -1 if not found */ - public int getPhotoIndex(Photo inPhoto) - { - // Check if we need to check - int numPhotos = getNumPhotos(); - if (numPhotos <= 0 || inPhoto == null || inPhoto.getFile() == null) - return -1; - // Loop around photos in list - Photo foundPhoto = null; - for (int i=0; i= getNumPhotos()) return null; - return _photos.get(inIndex); + public Photo getPhoto(int inIndex) { + return (Photo) getMedia(inIndex); } - - /** - * Crop the photo list to the specified size - * @param inIndex previous size - */ - public void cropTo(int inIndex) - { - if (inIndex <= 0) - { - // delete whole list - if (_photos != null) {_photos.clear();} - } - else - { - // delete photos to previous size - while (_photos.size() > inIndex) - { - _photos.remove(_photos.size()-1); - } - } - } - - - /** - * @return array of file names - */ - public String[] getNameList() - { - String[] names = new String[getNumPhotos()]; - for (int i=0; i 0) - { - // Construct new list to copy into - ArrayList listCopy = new ArrayList(); - // Loop over photos in list - for (int i=0; i listCopy = new ArrayList(); - for (int i=0; i<_photos.size(); i++) { - listCopy.add(_photos.get(i)); - } - return new PhotoList(listCopy); - } - - - /** - * Restore contents from other PhotoList - * @param inOther PhotoList with cloned contents - */ - public void restore(PhotoList inOther) - { - if (inOther.getNumPhotos() == 0) - { - // List is empty - _photos = null; - } - else - { - // Clear array and copy over from other one - _photos.clear(); - _photos.addAll(inOther._photos); - } + public void removeCorrelatedPhotos() { + removeCorrelatedMedia(); } } diff --git a/tim/prune/data/Selection.java b/tim/prune/data/Selection.java index cbfb350..81ab64c 100644 --- a/tim/prune/data/Selection.java +++ b/tim/prune/data/Selection.java @@ -12,8 +12,10 @@ public class Selection private Track _track = null; private int _currentPoint = -1; private boolean _valid = false; + private int _prevNumPoints = 0; private int _startIndex = -1, _endIndex = -1; private int _currentPhotoIndex = -1; + private int _currentAudioIndex = -1; private IntegerRange _altitudeRange = null; private int _climb = -1, _descent = -1; private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT; @@ -66,7 +68,13 @@ public class Selection { _altitudeFormat = Altitude.Format.NO_FORMAT; _numSegments = 0; - if (_track.getNumPoints() > 0 && hasRangeSelected()) + final int numPoints = _track.getNumPoints(); + // Recheck if the number of points has changed + if (numPoints != _prevNumPoints) { + _prevNumPoints = numPoints; + check(); + } + if (numPoints > 0 && hasRangeSelected()) { _altitudeRange = new IntegerRange(); _climb = 0; @@ -242,13 +250,14 @@ public class Selection } /** - * Clear selected point, range and photo + * Clear selected point, range, photo and audio */ public void clearAll() { _currentPoint = -1; selectRange(-1, -1); _currentPhotoIndex = -1; + _currentAudioIndex = -1; check(); } @@ -373,13 +382,15 @@ public class Selection /** * Select the specified photo and point - * @param inPhotoIndex index of selected photo in PhotoList * @param inPointIndex index of selected point + * @param inPhotoIndex index of selected photo in PhotoList + * @param inAudioIndex index of selected audio item */ - public void selectPhotoAndPoint(int inPhotoIndex, int inPointIndex) + public void selectPointPhotoAudio(int inPointIndex, int inPhotoIndex, int inAudioIndex) { - _currentPhotoIndex = inPhotoIndex; _currentPoint = inPointIndex; + _currentPhotoIndex = inPhotoIndex; + _currentAudioIndex = inAudioIndex; check(); } @@ -392,35 +403,40 @@ public class Selection return _currentPhotoIndex; } + /** + * @return currently selected audio index + */ + public int getCurrentAudioIndex() + { + return _currentAudioIndex; + } + /** * Check that the selection still makes sense * and fire update message to listeners */ private void check() { - if (_track != null) + if (_track != null && _track.getNumPoints() > 0) { - if (_track.getNumPoints() > 0) + int maxIndex = _track.getNumPoints() - 1; + if (_currentPoint > maxIndex) { - int maxIndex = _track.getNumPoints() - 1; - if (_currentPoint > maxIndex) - { - _currentPoint = maxIndex; - } - if (_endIndex > maxIndex) - { - _endIndex = maxIndex; - } - if (_startIndex > maxIndex) - { - _startIndex = maxIndex; - } + _currentPoint = maxIndex; } - else + if (_endIndex > maxIndex) { - // track is empty, clear selections - _currentPoint = _startIndex = _endIndex = -1; + _endIndex = maxIndex; } + if (_startIndex > maxIndex) + { + _startIndex = maxIndex; + } + } + else + { + // track is empty, clear selections + _currentPoint = _startIndex = _endIndex = -1; } UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); } diff --git a/tim/prune/data/Timestamp.java b/tim/prune/data/Timestamp.java index 8de7c4d..e7a0ab9 100644 --- a/tim/prune/data/Timestamp.java +++ b/tim/prune/data/Timestamp.java @@ -27,7 +27,6 @@ public class Timestamp private static Calendar CALENDAR = null; private static final Pattern GENERAL_TIMESTAMP_PATTERN = Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})"); - private static Matcher GENERAL_TIMESTAMP_MATCHER = null; private static long SECS_SINCE_1970 = 0L; private static long SECS_SINCE_GARTRIP = 0L; private static long MSECS_SINCE_1970 = 0L; @@ -96,16 +95,18 @@ public class Timestamp } catch (ParseException e) {} } - if (!_valid && inString.length() == 19) { - GENERAL_TIMESTAMP_MATCHER = GENERAL_TIMESTAMP_PATTERN.matcher(inString); - if (GENERAL_TIMESTAMP_MATCHER.matches()) { + if (!_valid && inString.length() == 19) + { + final Matcher matcher = GENERAL_TIMESTAMP_PATTERN.matcher(inString); + if (matcher.matches()) + { try { - _seconds = getSeconds(Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(1)), - Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(2)), - Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(3)), - Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(4)), - Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(5)), - Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(6))); + _seconds = getSeconds(Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), + Integer.parseInt(matcher.group(4)), + Integer.parseInt(matcher.group(5)), + Integer.parseInt(matcher.group(6))); _valid = true; } catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched @@ -249,7 +250,17 @@ public class Timestamp */ public Timestamp createPlusOffset(TimeDifference inOffset) { - return new Timestamp((_seconds + inOffset.getTotalSeconds()) * 1000L); + return createPlusOffset(inOffset.getTotalSeconds()); + } + + /** + * Add the given number of seconds to this Timestamp + * @param inSeconds number of seconds to add + * @return new Timestamp object + */ + public Timestamp createPlusOffset(long inSeconds) + { + return new Timestamp((_seconds + inSeconds) * 1000L); } diff --git a/tim/prune/data/Track.java b/tim/prune/data/Track.java index 9b88ade..fa1be1e 100644 --- a/tim/prune/data/Track.java +++ b/tim/prune/data/Track.java @@ -173,7 +173,7 @@ public class Track { DataPoint point = _dataPoints[i]; // Don't delete photo points - if (point.getPhoto() != null || !point.getDeleteFlag()) + if (point.hasMedia() || !point.getDeleteFlag()) { newPointArray[numCopied] = point; numCopied++; diff --git a/tim/prune/data/TrackInfo.java b/tim/prune/data/TrackInfo.java index 6fa8e90..3df964b 100644 --- a/tim/prune/data/TrackInfo.java +++ b/tim/prune/data/TrackInfo.java @@ -1,6 +1,5 @@ package tim.prune.data; -import java.util.Iterator; import java.util.Set; import tim.prune.UpdateMessageBroker; @@ -14,6 +13,7 @@ public class TrackInfo private Selection _selection = null; private FileInfo _fileInfo = null; private PhotoList _photoList = null; + private AudioList _audioList = null; /** @@ -26,14 +26,14 @@ public class TrackInfo _selection = new Selection(_track); _fileInfo = new FileInfo(); _photoList = new PhotoList(); + _audioList = new AudioList(); } /** * @return the Track object */ - public Track getTrack() - { + public Track getTrack() { return _track; } @@ -41,8 +41,7 @@ public class TrackInfo /** * @return the Selection object */ - public Selection getSelection() - { + public Selection getSelection() { return _selection; } @@ -50,8 +49,7 @@ public class TrackInfo /** * @return the FileInfo object */ - public FileInfo getFileInfo() - { + public FileInfo getFileInfo() { return _fileInfo; } @@ -66,17 +64,22 @@ public class TrackInfo /** * @return the PhotoList object */ - public PhotoList getPhotoList() - { + public PhotoList getPhotoList() { return _photoList; } + /** + * @return the AudioList object + */ + public AudioList getAudioList() { + return _audioList; + } + /** * Get the currently selected point, if any * @return DataPoint if single point selected, otherwise null */ - public DataPoint getCurrentPoint() - { + public DataPoint getCurrentPoint() { return _track.getPoint(_selection.getCurrentPointIndex()); } @@ -84,11 +87,18 @@ public class TrackInfo * Get the currently selected photo, if any * @return Photo if selected, otherwise null */ - public Photo getCurrentPhoto() - { + public Photo getCurrentPhoto() { return _photoList.getPhoto(_selection.getCurrentPhotoIndex()); } + /** + * Get the currently selected audio file, if any + * @return AudioFile if selected, otherwise null + */ + public AudioFile getCurrentAudio() { + return _audioList.getAudio(_selection.getCurrentAudioIndex()); + } + /** * Add a Set of Photos @@ -100,25 +110,17 @@ public class TrackInfo // Firstly count number of points and photos to add int numPhotosToAdd = 0; int numPointsToAdd = 0; - Iterator iterator = null; if (inSet != null && !inSet.isEmpty()) { - iterator = inSet.iterator(); - while (iterator.hasNext()) + for (Photo photo : inSet) { - try + if (photo != null && !_photoList.contains(photo)) { - Photo photo = iterator.next(); - if (photo != null && !_photoList.contains(photo)) - { - numPhotosToAdd++; - if (photo.getDataPoint() != null) - { - numPointsToAdd++; - } + numPhotosToAdd++; + if (photo.getDataPoint() != null) { + numPointsToAdd++; } } - catch (ClassCastException ce) {} } } // If there are any photos to add, add them @@ -128,27 +130,21 @@ public class TrackInfo int pointNum = 0; boolean hasAltitude = false; // Add each Photo in turn - iterator = inSet.iterator(); - while (iterator.hasNext()) + for (Photo photo : inSet) { - try + if (photo != null && !_photoList.contains(photo)) { - Photo photo = iterator.next(); - if (photo != null && !_photoList.contains(photo)) + // Add photo + _photoList.addPhoto(photo); + // Add point if there is one + if (photo.getDataPoint() != null) { - // Add photo - _photoList.addPhoto(photo); - // Add point if there is one - if (photo.getDataPoint() != null) - { - dataPoints[pointNum] = photo.getDataPoint(); - // Check if any points have altitudes - hasAltitude |= (photo.getDataPoint().getAltitude() != null); - pointNum++; - } + dataPoints[pointNum] = photo.getDataPoint(); + // Check if any points have altitudes + hasAltitude |= (photo.getDataPoint().getAltitude() != null); + pointNum++; } } - catch (ClassCastException ce) {} } if (numPointsToAdd > 0) { @@ -164,6 +160,29 @@ public class TrackInfo return result; } + /** + * Add a Set of Audio objects + * @param inSet Set containing Audio objects + * @return number of audio objects added + */ + public int addAudios(Set inSet) + { + int numAudiosAdded = 0; + if (inSet != null && !inSet.isEmpty()) + { + for (AudioFile audio : inSet) + { + if (audio != null && !_audioList.contains(audio)) + { + // Add audio object + _audioList.addAudio(audio); + numAudiosAdded++; + // audio objects never have points when they're loaded + } + } + } + return numAudiosAdded; + } /** * Delete the currently selected range of points @@ -203,7 +222,6 @@ public class TrackInfo */ public boolean deleteCurrentPhoto(boolean inPointToo) { - // delete currently selected photo int photoIndex = _selection.getCurrentPhotoIndex(); if (photoIndex >= 0) { @@ -232,6 +250,41 @@ public class TrackInfo return true; } + /** + * Delete the currently selected audio item and optionally its point too + * @param inPointToo true to also delete associated point + * @return true if delete successful + */ + public boolean deleteCurrentAudio(boolean inPointToo) + { + int audioIndex = _selection.getCurrentAudioIndex(); + if (audioIndex >= 0) + { + AudioFile audio = _audioList.getAudio(audioIndex); + _audioList.deleteAudio(audioIndex); + // has it got a point? + if (audio.getDataPoint() != null) + { + if (inPointToo) + { + // delete point + int pointIndex = _track.getPointIndex(audio.getDataPoint()); + _track.deletePoint(pointIndex); + } + else + { + // disconnect point from audio + audio.getDataPoint().setAudio(null); + audio.setDataPoint(null); + } + } + // update subscribers + _selection.modifyPointDeleted(); + UpdateMessageBroker.informSubscribers(); + } + return true; + } + /** * Delete all the points which have been marked for deletion @@ -328,28 +381,30 @@ public class TrackInfo public void selectPoint(int inPointIndex) { if (_selection.getCurrentPointIndex() == inPointIndex || inPointIndex >= _track.getNumPoints()) {return;} + DataPoint selectedPoint = _track.getPoint(inPointIndex); // get the index of the current photo int photoIndex = _selection.getCurrentPhotoIndex(); // Check if point has photo or not - boolean pointHasPhoto = false; - if (inPointIndex >= 0) - { - Photo pointPhoto = _track.getPoint(inPointIndex).getPhoto(); - pointHasPhoto = (pointPhoto != null); - if (pointHasPhoto) { - photoIndex = _photoList.getPhotoIndex(pointPhoto); - } + boolean pointHasPhoto = inPointIndex >= 0 && selectedPoint.getPhoto() != null; + if (pointHasPhoto) { + photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto()); } - // Might need to deselect photo - if (!pointHasPhoto) - { + else if (photoIndex < 0 || _photoList.getPhoto(photoIndex).isConnected()) { // selected point hasn't got a photo - deselect photo if necessary - if (photoIndex < 0 || _photoList.getPhoto(photoIndex).isConnected()) { - photoIndex = -1; - } + photoIndex = -1; + } + // Check if point has an audio item or not + int audioIndex = _selection.getCurrentAudioIndex(); + boolean pointHasAudio = inPointIndex >= 0 && selectedPoint.getAudio() != null; + if (pointHasAudio) { + audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio()); + } + else if (audioIndex < 0 || _audioList.getAudio(audioIndex).isConnected()) { + // deselect current audio file + audioIndex = -1; } // give to selection - _selection.selectPhotoAndPoint(photoIndex, inPointIndex); + _selection.selectPointPhotoAudio(inPointIndex, photoIndex, audioIndex); } /** @@ -363,34 +418,84 @@ public class TrackInfo // Therefore the photo selection takes priority, deselecting point if necessary // Find Photo object Photo photo = _photoList.getPhoto(inPhotoIndex); + int pointIndex = _selection.getCurrentPointIndex(); + DataPoint currPoint = getCurrentPoint(); if (photo != null) { - // Find point object and its index - int pointIndex = _track.getPointIndex(photo.getDataPoint()); - // Check whether to deselect current point or not if photo not correlated - if (pointIndex < 0) - { - int currPointIndex = _selection.getCurrentPointIndex(); - if (currPointIndex >= 0 && _track.getPoint(currPointIndex).getPhoto() == null) - { - pointIndex = currPointIndex; // Keep currently selected point + // Has the photo got a point? + if (photo.isConnected()) { + pointIndex = _track.getPointIndex(photo.getDataPoint()); + } + else { + // Check whether to deselect current point or not if photo not correlated + if (pointIndex >= 0 && _track.getPoint(pointIndex).getPhoto() != null) { + pointIndex = -1; } } - // give to selection object - _selection.selectPhotoAndPoint(inPhotoIndex, pointIndex); } else { - // no photo, just reset selection - DataPoint currPoint = getCurrentPoint(); - if (currPoint != null && currPoint.getPhoto() == null) { - _selection.selectPhotoAndPoint(-1, _selection.getCurrentPointIndex()); // keep point + // no photo, but maybe need to deselect point + if (currPoint != null && currPoint.getPhoto() != null) { + pointIndex = -1; + } + } + // Has the new point got an audio file? + DataPoint selectedPoint = _track.getPoint(pointIndex); + int audioIndex = _selection.getCurrentAudioIndex(); + if (selectedPoint != null) { + if (selectedPoint.getAudio() != null) audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio()); + } + else { + if (selectedPoint != currPoint && currPoint.getAudio() != null) {audioIndex = -1;} + } + // give to selection object + _selection.selectPointPhotoAudio(pointIndex, inPhotoIndex, audioIndex); + } + + /** + * Select the given audio object and its point if any + * @param inAudioIndex index of audio item to select + */ + public void selectAudio(int inAudioIndex) + { + if (_selection.getCurrentAudioIndex() == inAudioIndex) {return;} + // Audio selection takes priority, deselecting point if necessary + AudioFile audio = _audioList.getAudio(inAudioIndex); + int pointIndex = _selection.getCurrentPointIndex(); + DataPoint currPoint = getCurrentPoint(); + if (audio != null) + { + // Find point object and its index + if (audio.isConnected()) { + pointIndex = _track.getPointIndex(audio.getDataPoint()); } else { - _selection.selectPhotoAndPoint(-1, -1); // deselect point too + // Check whether to deselect current point or not if audio not correlated + if (pointIndex >= 0 && _track.getPoint(pointIndex).getAudio() != null) { + pointIndex = -1; + } + } + } + else { + // check if current point has audio or not + if (currPoint != null && currPoint.getAudio() != null) { + pointIndex = -1; } } + // Has the new point got a photo? + DataPoint selectedPoint = _track.getPoint(pointIndex); + int photoIndex = _selection.getCurrentPhotoIndex(); + if (selectedPoint != null) { + if (selectedPoint.getPhoto() != null) photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto()); + } + else { + if (selectedPoint != currPoint && currPoint.getPhoto() != null) {photoIndex = -1;} + } + // give to selection object + _selection.selectPointPhotoAudio(pointIndex, photoIndex, inAudioIndex); } + /** * Extend the current selection to end at the given point, eg by shift-clicking * @param inPointNum index of end point diff --git a/tim/prune/function/AboutScreen.java b/tim/prune/function/AboutScreen.java index c898507..d0e4d3c 100644 --- a/tim/prune/function/AboutScreen.java +++ b/tim/prune/function/AboutScreen.java @@ -97,9 +97,9 @@ public class AboutScreen extends GenericFunction descBuffer.append("

").append(I18nManager.getText("dialog.about.summarytext2")).append("

"); descBuffer.append("

").append(I18nManager.getText("dialog.about.summarytext3")).append("

"); descBuffer.append("

").append(I18nManager.getText("dialog.about.languages")).append(" : ") - .append("deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, nederlands,
" + - " polski, portugu\u00EAs, \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, " + - "
\u010de\u0161tina, rom\u00E2n\u0103, afrikaans, bahasa indonesia, farsi").append("

"); + .append("\u010de\u0161tina, deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, magyar,
" + + " nederlands, polski, portugu\u00EAs, \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean),
" + + " schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, rom\u00E2n\u0103, afrikaans, bahasa indonesia, farsi

"); descBuffer.append("

").append(I18nManager.getText("dialog.about.translatedby")).append("

"); JEditorPane descPane = new JEditorPane("text/html", descBuffer.toString()); descPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); @@ -195,7 +195,7 @@ public class AboutScreen extends GenericFunction new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph, nazotoko,"), 1, 4); addToGridBagPanel(creditsPanel, gridBag, constraints, - new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d"), + new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d, Gy\u00F6rgy, HooAU"), 1, 5); addToGridBagPanel(creditsPanel, gridBag, constraints, new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "), @@ -297,20 +297,27 @@ public class AboutScreen extends GenericFunction // First, try locally-held readme.txt if available (as it normally should be) // Readme file can either be in file system or packed in the same jar as code String errorMessage = null; + String readme = null; + InputStream in = null; try { // For some reason using ../readme.txt doesn't work, so need absolute path - InputStream in = AboutScreen.class.getResourceAsStream("/tim/prune/readme.txt"); + in = AboutScreen.class.getResourceAsStream("/tim/prune/readme.txt"); if (in != null) { byte[] buffer = new byte[in.available()]; in.read(buffer); in.close(); - return new String(buffer); + readme = new String(buffer); } } catch (IOException e) { errorMessage = e.getMessage(); } + finally { + try {in.close();} catch (Exception e) {} + } + if (readme != null) {return readme;} + // Locally-held file failed, so try to find gz file installed on system (eg Debian) try { @@ -318,7 +325,7 @@ public class AboutScreen extends GenericFunction if (gzFile.exists()) { // Copy decompressed bytes from gz file into out - InputStream in = new GZIPInputStream(new FileInputStream(gzFile)); + in = new GZIPInputStream(new FileInputStream(gzFile)); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[8 * 1024]; int count = 0; @@ -328,12 +335,16 @@ public class AboutScreen extends GenericFunction } while (count != -1); out.close(); in.close(); - return out.toString(); + readme = out.toString(); } } catch (IOException e) { System.err.println("Exception trying to get readme.gz : " + e.getMessage()); } + finally { + try {in.close();} catch (Exception e) {} + } + if (readme != null) {return readme;} // Only show first error message if couldn't get readme from gz either if (errorMessage != null) { System.err.println("Exception trying to get readme: " + errorMessage); diff --git a/tim/prune/function/AddMapSourceDialog.java b/tim/prune/function/AddMapSourceDialog.java index d7126ea..ec13bb1 100644 --- a/tim/prune/function/AddMapSourceDialog.java +++ b/tim/prune/function/AddMapSourceDialog.java @@ -9,8 +9,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; -import java.net.MalformedURLException; -import java.net.URL; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; @@ -216,23 +214,15 @@ public class AddMapSourceDialog private boolean isOsmPanelOk() { boolean ok = _oNameField.getText().trim().length() > 1; - URL baseUrl = null, topUrl = null; - try { - // Try to parse base url if given - String baseText = _baseUrlField.getText().trim(); - if (baseText.length() > 10) { - baseUrl = new URL(baseText); - } - else if (baseText.length() > 0) {ok = false;} - // Same again for top url if given - String topText = _topUrlField.getText().trim(); - if (topText.length() > 10) { - topUrl = new URL(topText); - } - else if (topText.length() > 0) {ok = false;} - } catch (MalformedURLException e) { - ok = false; - } + String baseUrl = null, topUrl = null; + // Try to parse base url if given + String baseText = _baseUrlField.getText().trim(); + baseUrl = MapSource.fixBaseUrl(baseText); + if (baseText.length() > 0 && baseUrl == null) {ok = false;} + // Same again for top url if given + String topText = _topUrlField.getText().trim(); + topUrl = MapSource.fixBaseUrl(topText); + if (topText.length() > 0 && topUrl == null) {ok = false;} // looks ok if at least one url given return (ok && (baseUrl != null || topUrl != null)); } diff --git a/tim/prune/function/ConnectToPointFunction.java b/tim/prune/function/ConnectToPointFunction.java new file mode 100644 index 0000000..fa6f30b --- /dev/null +++ b/tim/prune/function/ConnectToPointFunction.java @@ -0,0 +1,63 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.AudioFile; +import tim.prune.data.DataPoint; +import tim.prune.data.Photo; +import tim.prune.undo.UndoConnectMedia; +import tim.prune.undo.UndoOperation; + +/** + * Function to connect either a photo or an audio file to the current point + */ +public class ConnectToPointFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp app object + */ + public ConnectToPointFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.connecttopoint"; + } + + /** + * Perform function + */ + public void begin() + { + Photo photo = _app.getTrackInfo().getCurrentPhoto(); + DataPoint point = _app.getTrackInfo().getCurrentPoint(); + AudioFile audio = _app.getTrackInfo().getCurrentAudio(); + boolean connectPhoto = (point != null && photo != null && point.getPhoto() == null); + boolean connectAudio = (point != null && audio != null && point.getAudio() == null); + + if (connectPhoto && connectAudio) { + // TODO: Let user choose whether to connect photo/audio or both + } + // Make undo object + UndoOperation undo = new UndoConnectMedia(point, connectPhoto?photo.getFile().getName():null, + connectAudio?audio.getFile().getName():null); + // Connect the media + if (connectPhoto) { + photo.setDataPoint(point); + point.setPhoto(photo); + } + if (connectAudio) { + audio.setDataPoint(point); + point.setAudio(audio); + } + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + _app.completeFunction(undo, I18nManager.getText("confirm.media.connect")); + } +} diff --git a/tim/prune/function/DisconnectAudioFunction.java b/tim/prune/function/DisconnectAudioFunction.java new file mode 100644 index 0000000..abcd9c4 --- /dev/null +++ b/tim/prune/function/DisconnectAudioFunction.java @@ -0,0 +1,48 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.AudioFile; +import tim.prune.data.DataPoint; +import tim.prune.undo.UndoDisconnectMedia; +import tim.prune.undo.UndoOperation; + +/** + * Function to disconnect the current audio object from the current point (like DisconnectPhotoFunction) + */ +public class DisconnectAudioFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp app object + */ + public DisconnectAudioFunction(App inApp) { + super(inApp); + } + + /** @return name key */ + public String getNameKey() { + return "function.disconnectfrompoint"; + } + + /** + * Perform the operation + */ + public void begin() + { + AudioFile audio = _app.getTrackInfo().getCurrentAudio(); + if (audio != null && audio.getDataPoint() != null) + { + DataPoint point = audio.getDataPoint(); + UndoOperation undo = new UndoDisconnectMedia(point, false, true, audio.getFile().getName()); + // disconnect + audio.setDataPoint(null); + point.setAudio(null); + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + _app.completeFunction(undo, I18nManager.getText("confirm.audio.disconnect")); + } + } +} diff --git a/tim/prune/function/DisconnectPhotoFunction.java b/tim/prune/function/DisconnectPhotoFunction.java new file mode 100644 index 0000000..6223433 --- /dev/null +++ b/tim/prune/function/DisconnectPhotoFunction.java @@ -0,0 +1,47 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.DataPoint; +import tim.prune.data.Photo; +import tim.prune.undo.UndoDisconnectMedia; + +/** + * Function to disconnect the current photo from the current point + */ +public class DisconnectPhotoFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp app object + */ + public DisconnectPhotoFunction(App inApp) { + super(inApp); + } + + /** @return name key */ + public String getNameKey() { + return "function.disconnectfrompoint"; + } + + /** + * Perform the operation + */ + public void begin() + { + Photo photo = _app.getTrackInfo().getCurrentPhoto(); + if (photo != null && photo.getDataPoint() != null) + { + DataPoint point = photo.getDataPoint(); + UndoDisconnectMedia undo = new UndoDisconnectMedia(point, true, false, photo.getFile().getName()); + // disconnect + photo.setDataPoint(null); + point.setPhoto(null); + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + _app.completeFunction(undo, I18nManager.getText("confirm.photo.disconnect")); + } + } +} diff --git a/tim/prune/function/DownloadOsmFunction.java b/tim/prune/function/DownloadOsmFunction.java new file mode 100644 index 0000000..bef09ad --- /dev/null +++ b/tim/prune/function/DownloadOsmFunction.java @@ -0,0 +1,277 @@ +package tim.prune.function; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.NumberFormat; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.DoubleRange; + +/** + * Class to allow the download of OSM data (using the XAPI) + * for the area covered by the data + */ +public class DownloadOsmFunction extends GenericFunction implements Runnable +{ + private JDialog _dialog = null; + private JLabel[] _latLonLabels = null; + private JProgressBar _progressBar = null; + private JButton _okButton = null; + private JFileChooser _fileChooser = null; + private File _selectedFile = null; + private boolean _cancelled = false; + /** Number formatter */ + private final NumberFormat FORMAT_TWO_DP = NumberFormat.getNumberInstance(); + + + /** + * Constructor + * @param inApp application object for callback + */ + public DownloadOsmFunction(App inApp) + { + super(inApp); + FORMAT_TWO_DP.setMaximumFractionDigits(2); + FORMAT_TWO_DP.setMinimumFractionDigits(2); + } + + /** Get the name key */ + public String getNameKey() { + return "function.downloadosm"; + } + + /** + * Begin the function + */ + public void begin() + { + // Make dialog window + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + _dialog.getContentPane().add(makeDialogComponents()); + _dialog.pack(); + _fileChooser = new JFileChooser(); + _fileChooser.setSelectedFile(new File("data.osm")); + } + initDialog(); + _dialog.setVisible(true); + } + + + /** + * Create dialog components + * @return Panel containing all gui elements in dialog + */ + private Component makeDialogComponents() + { + JPanel dialogPanel = new JPanel(); + dialogPanel.setLayout(new BorderLayout(0, 10)); + dialogPanel.add(new JLabel(I18nManager.getText("dialog.downloadosm.desc")), BorderLayout.NORTH); + // grid of labels to show lat/long extent + JPanel gridPanel = new JPanel(); + gridPanel.setLayout(new GridLayout(3, 3)); + _latLonLabels = new JLabel[4]; + for (int i=0; i<4; i++) { + _latLonLabels[i] = new JLabel("0"); + } + int lNum = 0; + for (int i=0; i<4; i++) { + gridPanel.add(new JLabel(" ")); + gridPanel.add(_latLonLabels[lNum++]); + } + // layout main panel + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.add(gridPanel); + _progressBar = new JProgressBar(); + _progressBar.setIndeterminate(true); + mainPanel.add(_progressBar); + dialogPanel.add(mainPanel, BorderLayout.CENTER); + // button panel at bottom + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + _okButton = new JButton(I18nManager.getText("button.ok")); + ActionListener okListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + finish(); + } + }; + _okButton.addActionListener(okListener); + buttonPanel.add(_okButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _cancelled = true; + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + dialogPanel.add(buttonPanel, BorderLayout.SOUTH); + dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15)); + return dialogPanel; + } + + /** + * Initialise the values of the labels in the dialog + */ + private void initDialog() + { + // Get range of data + String[] lats = expandRange(_app.getTrackInfo().getTrack().getLatRange()); + String[] lons = expandRange(_app.getTrackInfo().getTrack().getLonRange()); + _latLonLabels[0].setText(lats[1]); // max lat + _latLonLabels[1].setText(lons[0]); // min lon + _latLonLabels[2].setText(lons[1]); // max lon + _latLonLabels[3].setText(lats[0]); // min lat + _okButton.setEnabled(true); + _progressBar.setVisible(false); + _cancelled = false; + } + + /** + * Expand the given range to reasonable limits + * @param inRange range of lat/long values + * @return expanded range as pair of Strings + */ + private String[] expandRange(DoubleRange inRange) + { + double mid = (inRange.getMaximum() + inRange.getMinimum()) / 2.0; + double range = inRange.getRange(); + double max = 0.0, min = 0.0; + // Expand range to at least 0.02 degree + if (range < 0.02) + { + min = mid - 0.01; + max = mid + 0.01; + } + else { + // expand by 10% in both directions + min = mid - range * 0.55; + max = mid + range * 0.55; + } + // Round min down to 0.01 degree + int minCents = (int) (100 * min); + // Round max upwards likewise + int maxCents = (int) (100 * max + 1); + final String[] answer = new String[] {FORMAT_TWO_DP.format(minCents/100.0), + FORMAT_TWO_DP.format(maxCents/100.0)}; + return answer; + } + + /** + * Finish the dialog when OK pressed + */ + private void finish() + { + if (!_okButton.isEnabled()) return; + _selectedFile = selectOsmFile(); + if (_selectedFile != null) + { + // Show progress bar + _okButton.setEnabled(false); + _progressBar.setVisible(true); + new Thread(this).start(); + } + else + _dialog.dispose(); + } + + /** + * Select a file to save the OSM data to + * @return selected file or null if cancelled + */ + private File selectOsmFile() + { + File saveFile = null; + boolean chooseAgain = false; + do + { + chooseAgain = false; + if (_fileChooser.showSaveDialog(_dialog) == JFileChooser.APPROVE_OPTION) + { + // OK pressed and file chosen + File file = _fileChooser.getSelectedFile(); + // Check if file exists and if necessary prompt for overwrite + Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")}; + if (!file.exists() || JOptionPane.showOptionDialog(_dialog, + I18nManager.getText("dialog.save.overwrite.text"), + I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1]) + == JOptionPane.YES_OPTION) + { + // new file or overwrite confirmed + saveFile = file; + } + else + { + // file exists and overwrite cancelled - select again + chooseAgain = true; + } + } + } while (chooseAgain); + return saveFile; + } + + + /** + * Do the actual download - launched to run in another thread + */ + public void run() + { + final String url = "http://www.informationfreeway.org/api/0.6/map?bbox=" + + _latLonLabels[1].getText() + "," + _latLonLabels[3].getText() + "," + + _latLonLabels[2].getText() + "," + _latLonLabels[0].getText(); + + byte[] buffer = new byte[1024]; + InputStream inStream = null; + FileOutputStream outStream = null; + int numBytesRead = 0; + try + { + inStream = new URL(url).openStream(); + outStream = new FileOutputStream(_selectedFile); + // Loop and copy bytes to file + while ((numBytesRead = inStream.read(buffer)) > -1 && !_cancelled) + { + outStream.write(buffer, 0, numBytesRead); + } + } + catch (MalformedURLException mue) {} + catch (IOException ioe) { + // TODO: throw exception or show dialog + System.out.println("Exception: " + ioe.getClass().getName()); + } + // clean up streams + finally { + try {inStream.close();} catch (Exception e) {} + try {outStream.close();} catch (Exception e) {} + } + // close dialog + _dialog.dispose(); + } +} diff --git a/tim/prune/function/GetWikipediaFunction.java b/tim/prune/function/GetWikipediaFunction.java new file mode 100644 index 0000000..acef09c --- /dev/null +++ b/tim/prune/function/GetWikipediaFunction.java @@ -0,0 +1,133 @@ +package tim.prune.function; + +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import tim.prune.App; +import tim.prune.I18nManager; +import tim.prune.data.DataPoint; +import tim.prune.data.Field; +import tim.prune.data.Latitude; +import tim.prune.data.Longitude; +import tim.prune.function.gpsies.GenericDownloaderFunction; +import tim.prune.function.gpsies.GpsiesTrack; + +/** + * Function to load nearby point information from Wikipedia + * according to the currently viewed area + */ +public class GetWikipediaFunction extends GenericDownloaderFunction +{ + /** Maximum number of results to get */ + private static final int MAX_RESULTS = 20; + /** Maximum distance from point in km */ + private static final int MAX_DISTANCE = 15; + + + /** + * Constructor + * @param inApp App object + */ + public GetWikipediaFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.getwikipedia"; + } + + /** + * @param inColNum index of column, 0 or 1 + * @return key for this column + */ + protected String getColumnKey(int inColNum) + { + if (inColNum == 0) return "dialog.wikipedia.column.name"; + return "dialog.wikipedia.column.distance"; + } + + + /** + * Run method to call geonames in separate thread + */ + public void run() + { + _statusLabel.setText(I18nManager.getText("confirm.running")); + // Get coordinates from current point (if any) or from centre of screen + double lat = 0.0, lon = 0.0; + DataPoint point = _app.getTrackInfo().getCurrentPoint(); + if (point == null) + { + double[] coords = _app.getViewport().getBounds(); + lat = (coords[0] + coords[2]) / 2.0; + lon = (coords[1] + coords[3]) / 2.0; + } + else { + lat = point.getLatitude().getDouble(); + lon = point.getLongitude().getDouble(); + } + + String descMessage = ""; + InputStream inStream = null; + + // Example http://ws.geonames.org/findNearbyWikipedia?lat=47&lng=9 + String urlString = "http://ws.geonames.org/findNearbyWikipedia?lat=" + + lat + "&lng=" + lon + "&maxRows=" + MAX_RESULTS + + "&radius=" + MAX_DISTANCE + "&lang=" + I18nManager.getText("wikipedia.lang"); + // Parse the returned XML with a special handler + GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler(); + try + { + URL url = new URL(urlString); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + inStream = url.openStream(); + saxParser.parse(inStream, xmlHandler); + } + catch (Exception e) { + descMessage = e.getClass().getName() + " - " + e.getMessage(); + } + // Close stream and ignore errors + try { + inStream.close(); + } catch (Exception e) {} + // Add track list to model + ArrayList trackList = xmlHandler.getTrackList(); + _trackListModel.addTracks(trackList); + + // Set status label according to error or "none found", leave blank if ok + if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) { + descMessage = I18nManager.getText("dialog.gpsies.nonefound"); + } + _statusLabel.setText(descMessage); + } + + /** + * Load the selected track or point + */ + protected void loadSelected() + { + // Find the row selected in the table and get the corresponding track + int rowNum = _trackTable.getSelectedRow(); + if (rowNum >= 0 && rowNum < _trackListModel.getRowCount()) + { + String coords = _trackListModel.getTrack(rowNum).getDownloadLink(); + String[] latlon = coords.split(","); + if (latlon.length == 2) + { + DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null); + point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false); + _app.createPoint(point); + } + } + // Close the dialog + _cancelled = true; + _dialog.dispose(); + } +} diff --git a/tim/prune/function/GetWikipediaXmlHandler.java b/tim/prune/function/GetWikipediaXmlHandler.java new file mode 100644 index 0000000..3cca682 --- /dev/null +++ b/tim/prune/function/GetWikipediaXmlHandler.java @@ -0,0 +1,93 @@ +package tim.prune.function; + +import java.util.ArrayList; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import tim.prune.function.gpsies.GpsiesTrack; + +/** + * XML handler for dealing with XML returned from gpsies.com + */ +public class GetWikipediaXmlHandler extends DefaultHandler +{ + private String _value = null; + private ArrayList _trackList = null; + private GpsiesTrack _track = null; + private String _lat = null, _lon = null; + + + /** + * React to the start of an XML tag + */ + public void startElement(String inUri, String inLocalName, String inTagName, + Attributes inAttributes) throws SAXException + { + if (inTagName.equals("geonames")) { + _trackList = new ArrayList(); + } + else if (inTagName.equals("entry")) { + _track = new GpsiesTrack(); + _lat = null; + _lon = null; + } + else _value = null; + super.startElement(inUri, inLocalName, inTagName, inAttributes); + } + + /** + * React to the end of an XML tag + */ + public void endElement(String inUri, String inLocalName, String inTagName) + throws SAXException + { + if (inTagName.equals("entry")) { + // end of the entry + _track.setDownloadLink(_lat + "," + _lon); + _trackList.add(_track); + } + else if (inTagName.equals("title")) { + _track.setTrackName(_value); + } + else if (inTagName.equals("summary")) { + _track.setDescription(_value); + } + else if (inTagName.equals("lat")) { + _lat = _value; + } + else if (inTagName.equals("lng")) { + _lon = _value; + } + else if (inTagName.equals("distance")) { + try { + _track.setLength(Double.parseDouble(_value) * 1000.0); // convert from km to m + } + catch (NumberFormatException nfe) {} + } + else if (inTagName.equals("wikipediaUrl")) { + _track.setWebUrl(_value); + } + super.endElement(inUri, inLocalName, inTagName); + } + + /** + * React to characters received inside tags + */ + public void characters(char[] inCh, int inStart, int inLength) + throws SAXException + { + String value = new String(inCh, inStart, inLength); + _value = (_value==null?value:_value+value); + super.characters(inCh, inStart, inLength); + } + + /** + * @return the list of tracks + */ + public ArrayList getTrackList() + { + return _trackList; + } +} diff --git a/tim/prune/function/PhotoPopupFunction.java b/tim/prune/function/PhotoPopupFunction.java new file mode 100644 index 0000000..6b8b3e0 --- /dev/null +++ b/tim/prune/function/PhotoPopupFunction.java @@ -0,0 +1,113 @@ +package tim.prune.function; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.Photo; +import tim.prune.gui.PhotoThumbnail; + +/** + * Class to show a popup window for a photo + */ +public class PhotoPopupFunction extends GenericFunction +{ + /** popup window */ + private JFrame _frame = null; // would be a JDialog but that doesn't allow max button + /** label for filename */ + private JLabel _label = null; + /** Photo thumbnail */ + private PhotoThumbnail _photoThumb = null; + + /** + * Constructor + * @param inApp app object + */ + public PhotoPopupFunction(App inApp) + { + super(inApp); + } + + /** + * Get the name key + */ + public String getNameKey() { + return "function.photopopup"; + } + + /** + * Show the screen + */ + public void begin() + { + if (_frame == null) + { + _frame = new JFrame(I18nManager.getText(getNameKey())); + _frame.setIconImage(_parentFrame.getIconImage()); + _frame.getContentPane().add(makeContents()); + _frame.pack(); + _frame.setLocationRelativeTo(_parentFrame); + } + initFrame(); + _frame.setVisible(true); + } + + /** + * Initialise the frame to show the current photo + */ + private void initFrame() + { + _frame.setVisible(false); + Photo photo = _app.getTrackInfo().getCurrentPhoto(); + _frame.setTitle(photo.getFile().getName()); + _label.setText("'" + photo.getFile().getName() + "' (" + + photo.getWidth() + " x " + photo.getHeight() + ")"); + _photoThumb.setPhoto(photo); + } + + /** + * @return the contents of the window as a Component + */ + private Component makeContents() + { + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + _label = new JLabel("Photo popup"); + mainPanel.add(_label, BorderLayout.NORTH); + _photoThumb = new PhotoThumbnail(false); // specify not in details panel + _photoThumb.setPreferredSize(new Dimension(300, 300)); + mainPanel.add(_photoThumb, BorderLayout.CENTER); + // Close button at bottom + JPanel okPanel = new JPanel(); + okPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + JButton okButton = new JButton(I18nManager.getText("button.ok")); + okButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) { + _frame.dispose(); + } + }); + okButton.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {_frame.dispose();} + } + public void keyTyped(KeyEvent e) {} + public void keyReleased(KeyEvent e) {} + }); + okPanel.add(okButton); + mainPanel.add(okPanel, BorderLayout.SOUTH); + return mainPanel; + } +} diff --git a/tim/prune/function/PlayAudioFunction.java b/tim/prune/function/PlayAudioFunction.java new file mode 100644 index 0000000..7e7a34a --- /dev/null +++ b/tim/prune/function/PlayAudioFunction.java @@ -0,0 +1,153 @@ +package tim.prune.function; + +import java.io.File; +import java.io.IOException; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; + +import tim.prune.App; +import tim.prune.GenericFunction; + +/** + * Class to play the current audio file + */ +public class PlayAudioFunction extends GenericFunction implements Runnable +{ + /** Audio clip used for playing within java */ + private Clip _clip = null; + + + /** + * Constructor + * @param inApp app object + */ + public PlayAudioFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.playaudio"; + } + + /** + * Perform function + */ + public void begin() + { + // Launch new thread if clip isn't currently playing + if (_clip == null) { + new Thread(this).start(); + } + } + + /** + * Play the audio in a new thread + */ + public void run() + { + File audioFile = _app.getTrackInfo().getCurrentAudio().getFile(); + boolean played = false; + if (audioFile.exists() && audioFile.isFile() && audioFile.canRead()) + { + // First choice is to play using java + played = playClip(audioFile); + // Second choice is to try the Desktop library from java 6, if available + if (!played) { + try { + Class d = Class.forName("java.awt.Desktop"); + d.getDeclaredMethod("open", new Class[] {File.class}).invoke( + d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {audioFile}); + //above code mimics: Desktop.getDesktop().open(audioFile); + played = true; + } + catch (Exception ignore) { + played = false; + } + } + // If the Desktop call failed, need to try backup methods + if (!played) + { + // If system looks like a Mac, try open command + String osName = System.getProperty("os.name").toLowerCase(); + boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0; + if (isMacOsx) { + String[] command = new String[] {"open", audioFile.getAbsolutePath()}; + try { + Runtime.getRuntime().exec(command); + played = true; + } + catch (IOException ioe) {} + } + } + } + if (!played) + { + // If still not worked, show error message + _app.showErrorMessage(getNameKey(), "error.playaudiofailed"); + } + } + + /** + * Try to play the sound file using built-in java libraries + * @return true if play was successful + */ + private boolean playClip(File inFile) + { + boolean success = false; + AudioInputStream audioInputStream = null; + _clip = null; + try + { + audioInputStream = AudioSystem.getAudioInputStream(inFile); + _clip = AudioSystem.getClip(); + _clip.open(audioInputStream); + // play the clip + _clip.start(); + _clip.drain(); + success = true; + } catch (Exception e) { + } finally { + // close the stream to clean up + try { + _clip.close(); + audioInputStream.close(); + } catch (Exception e) {} + _clip = null; + } + return success; + } + + /** + * Try to stop a currently playing clip + */ + public void stopClip() + { + if (_clip != null && _clip.isActive()) { + try { + _clip.stop(); + _clip.flush(); + } + catch (Exception e) {} + } + } + + /** + * @return percentage of clip currently played, or -1 if not playing + */ + public int getPercentage() + { + int percent = -1; + if (_clip != null && _clip.isActive()) + { + long clipLen = _clip.getMicrosecondLength(); + if (clipLen > 0) { + percent = (int) (_clip.getMicrosecondPosition() * 100.0 / clipLen); + } + } + return percent; + } +} diff --git a/tim/prune/function/RemoveAudioFunction.java b/tim/prune/function/RemoveAudioFunction.java new file mode 100644 index 0000000..fa859be --- /dev/null +++ b/tim/prune/function/RemoveAudioFunction.java @@ -0,0 +1,68 @@ +package tim.prune.function; + +import javax.swing.JOptionPane; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.AudioFile; +import tim.prune.undo.UndoDeleteAudio; + +/** + * Function to remove the currently selected audio file + */ +public class RemoveAudioFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp App object + */ + public RemoveAudioFunction(App inApp) { + super(inApp); + } + + /** @return name key */ + public String getNameKey() { + return "function.removeaudio"; + } + + /** + * Perform the function + */ + public void begin() + { + // Delete the current audio, and optionally its point too, keeping undo information + AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio(); + if (currentAudio != null) + { + // Audio is selected, see if it has a point or not + boolean deleted = false; + UndoDeleteAudio undoAction = null; + if (currentAudio.getDataPoint() == null) + { + // no point attached, so just delete + undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(), + null, -1); + deleted = _app.getTrackInfo().deleteCurrentAudio(false); + } + else + { + // point is attached, so need to confirm point deletion + undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(), + currentAudio.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint())); + int response = JOptionPane.showConfirmDialog(_app.getFrame(), + I18nManager.getText("dialog.deleteaudio.deletepoint"), + I18nManager.getText(getNameKey()), 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) { + deleted = _app.getTrackInfo().deleteCurrentAudio(deletePointToo); + } + } + // Add undo information to stack if necessary + if (deleted) { + _app.completeFunction(undoAction, currentAudio.getFile().getName() + " " + I18nManager.getText("confirm.media.removed")); + } + } + } +} diff --git a/tim/prune/function/RemovePhotoFunction.java b/tim/prune/function/RemovePhotoFunction.java new file mode 100644 index 0000000..b09ae10 --- /dev/null +++ b/tim/prune/function/RemovePhotoFunction.java @@ -0,0 +1,69 @@ +package tim.prune.function; + +import javax.swing.JOptionPane; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.data.Photo; +import tim.prune.undo.UndoDeletePhoto; + +/** + * Function to remove the currently selected photo + */ +public class RemovePhotoFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp App object + */ + public RemovePhotoFunction(App inApp) { + super(inApp); + } + + /** @return name key */ + public String getNameKey() { + return "function.removephoto"; + } + + /** + * Perform the function + */ + public void begin() + { + // Delete the current photo, and optionally its point too, keeping undo information + Photo currentPhoto = _app.getTrackInfo().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, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(), + null, -1); + photoDeleted = _app.getTrackInfo().deleteCurrentPhoto(false); + } + else + { + // point is attached, so need to confirm point deletion + undoAction = new UndoDeletePhoto(currentPhoto, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(), + currentPhoto.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint())); + int response = JOptionPane.showConfirmDialog(_app.getFrame(), + 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 = _app.getTrackInfo().deleteCurrentPhoto(deletePointToo); + } + } + // Add undo information to stack if necessary + if (photoDeleted) { + _app.completeFunction(undoAction, currentPhoto.getFile().getName() + " " + I18nManager.getText("confirm.media.removed")); + } + } + } +} diff --git a/tim/prune/function/SaveConfig.java b/tim/prune/function/SaveConfig.java index 125b4b7..6c0e4d0 100644 --- a/tim/prune/function/SaveConfig.java +++ b/tim/prune/function/SaveConfig.java @@ -135,14 +135,19 @@ public class SaveConfig extends GenericFunction if (response == JFileChooser.APPROVE_OPTION) { File saveFile = chooser.getSelectedFile(); + FileOutputStream outStream = null; try { - Config.getAllConfig().store(new FileOutputStream(saveFile), "Prune config file"); + outStream = new FileOutputStream(saveFile); + Config.getAllConfig().store(outStream, "Prune config file"); } catch (IOException ioe) { _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.save.failed") + " : " + ioe.getMessage()); } + finally { + try {outStream.close();} catch (Exception e) {} + } } _dialog.dispose(); _dialog = null; diff --git a/tim/prune/function/SearchWikipediaNames.java b/tim/prune/function/SearchWikipediaNames.java new file mode 100644 index 0000000..4377df8 --- /dev/null +++ b/tim/prune/function/SearchWikipediaNames.java @@ -0,0 +1,144 @@ +package tim.prune.function; + +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; + +import javax.swing.JOptionPane; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import tim.prune.App; +import tim.prune.I18nManager; +import tim.prune.data.DataPoint; +import tim.prune.data.Field; +import tim.prune.data.Latitude; +import tim.prune.data.Longitude; +import tim.prune.function.gpsies.GenericDownloaderFunction; +import tim.prune.function.gpsies.GpsiesTrack; + +/** + * Function to search Wikipedia for place names + */ +public class SearchWikipediaNames extends GenericDownloaderFunction +{ + /** search term */ + private String _searchTerm = null; + /** Maximum number of results to get */ + private static final int MAX_RESULTS = 20; + + /** + * Constructor + * @param inApp App object + */ + public SearchWikipediaNames(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.searchwikipedianames"; + } + + /** + * @param inColNum index of column, 0 or 1 + * @return key for this column + */ + protected String getColumnKey(int inColNum) + { + if (inColNum == 0) return "dialog.wikipedia.column.name"; + return null; + } + + /** + * Before dialog is shown, need to get search term + */ + public void begin() + { + Object search = JOptionPane.showInputDialog(_app.getFrame(), + I18nManager.getText("dialog.searchwikipedianames.search"), + I18nManager.getText(getNameKey()), + JOptionPane.QUESTION_MESSAGE, null, null, ""); + if (search != null) + { + _searchTerm = search.toString(); + if (!_searchTerm.equals("")) { + super.begin(); + } + } + } + + /** + * Run method to call geonames in separate thread + */ + public void run() + { + _statusLabel.setText(I18nManager.getText("confirm.running")); + + String descMessage = ""; + InputStream inStream = null; + + // language (only de and en available) + String lang = I18nManager.getText("wikipedia.lang"); + if (lang.equals("de") || lang.equals("als")) { + lang = "de"; + } + else { + lang = "en"; + } + // Example http://ws.geonames.org/wikipediaSearch?q=london&maxRows=10 + String urlString = "http://ws.geonames.org/wikipediaSearch?title=" + _searchTerm + "&maxRows=" + MAX_RESULTS + + "&lang=" + lang; + // Parse the returned XML with a special handler + GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler(); + try + { + URL url = new URL(urlString); + SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); + inStream = url.openStream(); + saxParser.parse(inStream, xmlHandler); + } + catch (Exception e) { + descMessage = e.getClass().getName() + " - " + e.getMessage(); + } + // Close stream and ignore errors + try { + inStream.close(); + } catch (Exception e) {} + // Add track list to model + ArrayList trackList = xmlHandler.getTrackList(); + // TODO: Do a better job of sorting replies by relevance - use three different lists + _trackListModel.addTracks(trackList); + + // Set status label according to error or "none found", leave blank if ok + if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) { + descMessage = I18nManager.getText("dialog.gpsies.nonefound"); + } + _statusLabel.setText(descMessage); + } + + /** + * Load the selected track or point + */ + protected void loadSelected() + { + // Find the row selected in the table and get the corresponding track + int rowNum = _trackTable.getSelectedRow(); + if (rowNum >= 0 && rowNum < _trackListModel.getRowCount()) + { + String coords = _trackListModel.getTrack(rowNum).getDownloadLink(); + String[] latlon = coords.split(","); + if (latlon.length == 2) + { + DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null); + point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false); + _app.createPoint(point); + } + } + // Close the dialog + _cancelled = true; + _dialog.dispose(); + } +} diff --git a/tim/prune/function/SetLanguage.java b/tim/prune/function/SetLanguage.java index 90bbc8f..f2ef5d3 100644 --- a/tim/prune/function/SetLanguage.java +++ b/tim/prune/function/SetLanguage.java @@ -41,14 +41,15 @@ public class SetLanguage extends GenericFunction private int _startIndex = 0; /** Names of languages for display in dropdown (not translated) */ - private static final String[] LANGUAGE_NAMES = {"\u010de\u0161tina", "deutsch", "english", "espa\u00F1ol", - "fran\u00E7ais", "italiano", "nederlands", "polski", "portugu\u00EAs", "\u4e2d\u6587 (chinese)", - "\u65E5\u672C\u8A9E (japanese)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", "rom\u00E2n\u0103", - "afrikaans", "bahasa indonesia", "farsi" + private static final String[] LANGUAGE_NAMES = {"\u010de\u0161tina", "deutsch", "english", + "espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski", + "portugu\u00EAs", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)", + "\uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", + "rom\u00E2n\u0103", "afrikaans", "bahasa indonesia", "farsi" }; /** Associated language codes (must be in same order as names!) */ - private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "nl", "pl", "pt", "zh", - "ja", "de_ch", "tr", "ro", "af", "in", "fa" + private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "hu", + "nl", "pl", "pt", "zh", "ja", "ko", "de_ch", "tr", "ro", "af", "in", "fa" }; diff --git a/tim/prune/function/SetLineWidth.java b/tim/prune/function/SetLineWidth.java new file mode 100644 index 0000000..5ac64ae --- /dev/null +++ b/tim/prune/function/SetLineWidth.java @@ -0,0 +1,56 @@ +package tim.prune.function; + +import javax.swing.JOptionPane; + +import tim.prune.App; +import tim.prune.DataSubscriber; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; + +public class SetLineWidth extends GenericFunction +{ + + /** + * Constructor + * @param inApp App object + */ + public SetLineWidth(App inApp) { + super(inApp); + } + + /** @return name key */ + public String getNameKey() { + return "function.setlinewidth"; + } + + + /** + * Run function + */ + public void begin() + { + int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); + if (currLineWidth < 1 || currLineWidth > 4) { + currLineWidth = 2; + } + Object lineWidthStr = JOptionPane.showInputDialog(_app.getFrame(), + I18nManager.getText("dialog.setlinewidth.text"), + I18nManager.getText(getNameKey()), + JOptionPane.QUESTION_MESSAGE, null, null, "" + currLineWidth); + if (lineWidthStr != null) + { + int lineWidth = 2; + try { + lineWidth = Integer.parseInt(lineWidthStr.toString()); + if (lineWidth >= 1 && lineWidth <= 4 && lineWidth != currLineWidth) + { + Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth); + UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED); + } + } + catch (NumberFormatException nfe) {}; + } + } +} diff --git a/tim/prune/function/StopAudioFunction.java b/tim/prune/function/StopAudioFunction.java new file mode 100644 index 0000000..1d58dda --- /dev/null +++ b/tim/prune/function/StopAudioFunction.java @@ -0,0 +1,35 @@ +package tim.prune.function; + +import tim.prune.App; +import tim.prune.FunctionLibrary; +import tim.prune.GenericFunction; + +/** + * Class to stop playing the current audio file + */ +public class StopAudioFunction extends GenericFunction +{ + /** + * Constructor + * @param inApp app object + */ + public StopAudioFunction(App inApp) { + super(inApp); + } + + /** + * @return name key + */ + public String getNameKey() { + return "function.stopaudio"; + } + + /** + * Perform function + */ + public void begin() + { + PlayAudioFunction playFn = (PlayAudioFunction) FunctionLibrary.FUNCTION_PLAY_AUDIO; + playFn.stopClip(); + } +} diff --git a/tim/prune/function/compress/ClosePointsAlgorithm.java b/tim/prune/function/compress/ClosePointsAlgorithm.java index 69c3635..b488238 100644 --- a/tim/prune/function/compress/ClosePointsAlgorithm.java +++ b/tim/prune/function/compress/ClosePointsAlgorithm.java @@ -58,7 +58,7 @@ public class ClosePointsAlgorithm extends SingleParameterAlgorithm if (!currPoint.isWaypoint()) { // Don't delete any photo points or start/end of segments - if (currPoint.getPhoto() == null + if (!currPoint.hasMedia() && !_trackDetails.isSegmentStart(i) && !_trackDetails.isSegmentEnd(i)) { // Check current point against prevPoint diff --git a/tim/prune/function/compress/CompressTrackFunction.java b/tim/prune/function/compress/CompressTrackFunction.java index 35ff61d..b347305 100644 --- a/tim/prune/function/compress/CompressTrackFunction.java +++ b/tim/prune/function/compress/CompressTrackFunction.java @@ -171,7 +171,7 @@ public class CompressTrackFunction extends GenericFunction for (int i=0; i d = Class.forName("javax.swing.JTable"); + d.getDeclaredMethod("setAutoCreateRowSorter", new Class[]{Boolean.TYPE}).invoke(distTable, Boolean.TRUE); + } + catch (Exception e) {} scrollPane = new JScrollPane(distTable); scrollPane.setPreferredSize(new Dimension(200, 250)); mainPanel.add(scrollPane); diff --git a/tim/prune/function/gpsies/GenericDownloaderFunction.java b/tim/prune/function/gpsies/GenericDownloaderFunction.java new file mode 100644 index 0000000..743becf --- /dev/null +++ b/tim/prune/function/gpsies/GenericDownloaderFunction.java @@ -0,0 +1,226 @@ +package tim.prune.function.gpsies; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.function.browser.BrowserLauncher; + +/** + * Function to load track information from any source, + * subclassed for special cases gpsies or wikipedia + */ +public abstract class GenericDownloaderFunction extends GenericFunction implements Runnable +{ + /** Dialog object */ + protected JDialog _dialog = null; + /** list model */ + protected TrackListModel _trackListModel = null; + /** track table */ + protected JTable _trackTable = null; + /** Cancelled flag */ + protected boolean _cancelled = false; + /** Status label */ + protected JLabel _statusLabel = null; + /** Description box */ + private JTextArea _descriptionBox = null; + /** Load button */ + private JButton _loadButton = null; + /** Show button */ + private JButton _showButton = null; + + + /** + * Constructor + * @param inApp App object + */ + public GenericDownloaderFunction(App inApp) { + super(inApp); + } + + /** + * Begin the function + */ + public void begin() + { + // Initialise dialog, show empty list + if (_dialog == null) + { + _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); + _dialog.setLocationRelativeTo(_parentFrame); + _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + // add closing listener + _dialog.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + _cancelled = true; + } + }); + _dialog.getContentPane().add(makeDialogComponents()); + _dialog.pack(); + } + // Clear list + _trackListModel.clear(); + _loadButton.setEnabled(false); + _showButton.setEnabled(false); + _cancelled = false; + _descriptionBox.setText(""); + // Start new thread to load list asynchronously + new Thread(this).start(); + + // Show dialog + _dialog.setVisible(true); + } + + + /** + * Create dialog components + * @return Panel containing all gui elements in dialog + */ + private Component makeDialogComponents() + { + JPanel dialogPanel = new JPanel(); + dialogPanel.setLayout(new BorderLayout()); + + // Status label + _statusLabel = new JLabel(I18nManager.getText("confirm.running")); + dialogPanel.add(_statusLabel, BorderLayout.NORTH); + // Main panel with track list + _trackListModel = new TrackListModel(getColumnKey(0), getColumnKey(1)); + _trackTable = new JTable(_trackListModel); + _trackTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if (!e.getValueIsAdjusting()) + { + if (_trackTable.getSelectedRow() >= 0 + && _trackTable.getSelectedRow() < _trackListModel.getRowCount()) + { + _loadButton.setEnabled(true); + _showButton.setEnabled(true); + setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription()); + _descriptionBox.setCaretPosition(0); + } + else { + _descriptionBox.setText(""); + } + } + } + }); + _trackTable.getColumnModel().getColumn(0).setPreferredWidth(300); + if (_trackListModel.getColumnCount() > 1) { + _trackTable.getColumnModel().getColumn(1).setPreferredWidth(70); + } + JScrollPane tablePane = new JScrollPane(_trackTable); + tablePane.setPreferredSize(new Dimension(450, 200)); + // Panel to hold description label and box + JPanel descPanel = new JPanel(); + descPanel.setLayout(new BorderLayout()); + JLabel descLabel = new JLabel(I18nManager.getText("dialog.gpsies.description") + " :"); + descPanel.add(descLabel, BorderLayout.NORTH); + _descriptionBox = new JTextArea(5, 20); + _descriptionBox.setEditable(false); + _descriptionBox.setLineWrap(true); + _descriptionBox.setWrapStyleWord(true); + JScrollPane descPane = new JScrollPane(_descriptionBox); + descPane.setPreferredSize(new Dimension(400, 80)); + descPanel.add(descPane, BorderLayout.CENTER); + // Use split pane to split table from description + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tablePane, descPanel); + splitPane.setResizeWeight(1.0); + dialogPanel.add(splitPane, BorderLayout.CENTER); + + // button panel at bottom + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + _loadButton = new JButton(I18nManager.getText("button.load")); + _loadButton.setEnabled(false); + _loadButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + loadSelected(); + } + }); + buttonPanel.add(_loadButton); + _showButton = new JButton(I18nManager.getText("button.showwebpage")); + _showButton.setEnabled(false); + _showButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + showSelectedWebpage(); + } + }); + buttonPanel.add(_showButton); + JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + _cancelled = true; + _dialog.dispose(); + } + }); + buttonPanel.add(cancelButton); + dialogPanel.add(buttonPanel, BorderLayout.SOUTH); + dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15)); + return dialogPanel; + } + + /** + * @param inColNum index of column, 0 or 1 + * @return key for this column + */ + protected abstract String getColumnKey(int inColNum); + + /** + * Set the description in the box + * @param inDesc description to set, or null for no description + */ + private void setDescription(String inDesc) + { + String text = inDesc; + if (inDesc == null || inDesc.length() < 2) { + text = I18nManager.getText("dialog.gpsies.nodescription"); + } + _descriptionBox.setText(text); + } + + + /** + * Load the selected track or point + */ + protected abstract void loadSelected(); + + + /** + * Show the webpage for the selected item + */ + private void showSelectedWebpage() + { + // Find the row selected in the table and show the corresponding url + int rowNum = _trackTable.getSelectedRow(); + if (rowNum >= 0 && rowNum < _trackListModel.getRowCount()) + { + String url = _trackListModel.getTrack(rowNum).getWebUrl(); + BrowserLauncher.launchBrowser(url); + } + // Don't close the dialog + } +} diff --git a/tim/prune/function/gpsies/GetGpsiesFunction.java b/tim/prune/function/gpsies/GetGpsiesFunction.java index d1d83f3..804c4ce 100644 --- a/tim/prune/function/gpsies/GetGpsiesFunction.java +++ b/tim/prune/function/gpsies/GetGpsiesFunction.java @@ -1,36 +1,15 @@ package tim.prune.function.gpsies; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTable; -import javax.swing.JTextArea; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import tim.prune.App; -import tim.prune.GenericFunction; import tim.prune.I18nManager; -import tim.prune.function.browser.BrowserLauncher; import tim.prune.load.xml.XmlFileLoader; import tim.prune.load.xml.ZipFileLoader; @@ -38,24 +17,8 @@ import tim.prune.load.xml.ZipFileLoader; * Function to load track information from Gpsies.com * according to the currently viewed area */ -public class GetGpsiesFunction extends GenericFunction implements Runnable +public class GetGpsiesFunction extends GenericDownloaderFunction { - /** Dialog object */ - private JDialog _dialog = null; - /** list model */ - private TrackListModel _trackListModel = null; - /** track table */ - private JTable _trackTable = null; - /** Cancelled flag */ - private boolean _cancelled = false; - /** Status label */ - private JLabel _statusLabel = null; - /** Description box */ - private JTextArea _descriptionBox = null; - /** Load button */ - private JButton _loadButton = null; - /** Show button */ - private JButton _showButton = null; /** Number of results per page */ private static final int RESULTS_PER_PAGE = 20; /** Maximum number of results to get */ @@ -66,8 +29,7 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable * Constructor * @param inApp App object */ - public GetGpsiesFunction(App inApp) - { + public GetGpsiesFunction(App inApp) { super(inApp); } @@ -79,140 +41,15 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable } /** - * Begin the function - */ - public void begin() - { - // Initialise dialog, show empty list - if (_dialog == null) - { - _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); - _dialog.setLocationRelativeTo(_parentFrame); - _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - // add closing listener - _dialog.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - _cancelled = true; - } - }); - _dialog.getContentPane().add(makeDialogComponents()); - _dialog.pack(); - } - // Clear list - _trackListModel.clear(); - _loadButton.setEnabled(false); - _showButton.setEnabled(false); - _cancelled = false; - _descriptionBox.setText(""); - // Start new thread to load list asynchronously - new Thread(this).start(); - - // Show dialog - _dialog.setVisible(true); - } - - - /** - * Create dialog components - * @return Panel containing all gui elements in dialog + * @param inColNum index of column, 0 or 1 + * @return key for this column */ - private Component makeDialogComponents() + protected String getColumnKey(int inColNum) { - JPanel dialogPanel = new JPanel(); - dialogPanel.setLayout(new BorderLayout()); - - // Status label - _statusLabel = new JLabel(I18nManager.getText("confirm.running")); - dialogPanel.add(_statusLabel, BorderLayout.NORTH); - // Main panel with track list - _trackListModel = new TrackListModel(); - _trackTable = new JTable(_trackListModel); - _trackTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) - { - if (_trackTable.getSelectedRow() >= 0 - && _trackTable.getSelectedRow() < _trackListModel.getRowCount()) - { - _loadButton.setEnabled(true); - _showButton.setEnabled(true); - setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription()); - _descriptionBox.setCaretPosition(0); - } - else { - _descriptionBox.setText(""); - } - } - } - }); - _trackTable.getColumnModel().getColumn(0).setPreferredWidth(300); - _trackTable.getColumnModel().getColumn(1).setPreferredWidth(70); - JScrollPane tablePane = new JScrollPane(_trackTable); - tablePane.setPreferredSize(new Dimension(450, 200)); - // Panel to hold description label and box - JPanel descPanel = new JPanel(); - descPanel.setLayout(new BorderLayout()); - JLabel descLabel = new JLabel(I18nManager.getText("dialog.gpsies.description") + " :"); - descPanel.add(descLabel, BorderLayout.NORTH); - _descriptionBox = new JTextArea(5, 20); - _descriptionBox.setEditable(false); - _descriptionBox.setLineWrap(true); - _descriptionBox.setWrapStyleWord(true); - JScrollPane descPane = new JScrollPane(_descriptionBox); - descPane.setPreferredSize(new Dimension(400, 80)); - descPanel.add(descPane, BorderLayout.CENTER); - // Use split pane to split table from description - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tablePane, descPanel); - splitPane.setResizeWeight(1.0); - dialogPanel.add(splitPane, BorderLayout.CENTER); - - // button panel at bottom - JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); - _loadButton = new JButton(I18nManager.getText("button.load")); - _loadButton.setEnabled(false); - _loadButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - loadSelectedTrack(); - } - }); - buttonPanel.add(_loadButton); - _showButton = new JButton(I18nManager.getText("button.showwebpage")); - _showButton.setEnabled(false); - _showButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - showSelectedTrack(); - } - }); - buttonPanel.add(_showButton); - JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); - cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _cancelled = true; - _dialog.dispose(); - } - }); - buttonPanel.add(cancelButton); - dialogPanel.add(buttonPanel, BorderLayout.SOUTH); - dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15)); - return dialogPanel; + if (inColNum == 0) return "dialog.gpsies.column.name"; + return "dialog.gpsies.column.length"; } - /** - * Set the description in the box - * @param inDesc description to set, or null for no description - */ - private void setDescription(String inDesc) - { - String text = inDesc; - if (inDesc == null || inDesc.length() < 2) { - text = I18nManager.getText("dialog.gpsies.nodescription"); - } - _descriptionBox.setText(text); - } /** * Run method to call gpsies.com in separate thread @@ -235,7 +72,6 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable String urlString = "http://www.gpsies.com/api.do?BBOX=" + coords[1] + "," + coords[0] + "," + coords[3] + "," + coords[2] + "&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage; - // System.out.println(urlString); // Parse the returned XML with a special handler GpsiesXmlHandler xmlHandler = new GpsiesXmlHandler(); try @@ -260,7 +96,7 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable currPage++; } while (trackList != null && trackList.size() == RESULTS_PER_PAGE - && _trackListModel.getRowCount() < MAX_RESULTS && !_cancelled); + && _trackListModel.getRowCount() < MAX_RESULTS && !_cancelled); // Set status label according to error or "none found", leave blank if ok if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) { descMessage = I18nManager.getText("dialog.gpsies.nonefound"); @@ -268,11 +104,10 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable _statusLabel.setText(descMessage); } - /** - * Load the selected track + * Load the selected track or point */ - private void loadSelectedTrack() + protected void loadSelected() { // Find the row selected in the table and get the corresponding track int rowNum = _trackTable.getSelectedRow(); @@ -293,22 +128,4 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable _cancelled = true; _dialog.dispose(); } - - - /** - * Show the webpage for the selected track - */ - private void showSelectedTrack() - { - // Find the row selected in the table and show the corresponding url - int rowNum = _trackTable.getSelectedRow(); - if (rowNum >= 0 && rowNum < _trackListModel.getRowCount()) - { - String id = _trackListModel.getTrack(rowNum).getFileId(); - BrowserLauncher.launchBrowser("http://gpsies.com/map.do?fileId=" + id); - } - // Close the dialog - _cancelled = true; - _dialog.dispose(); - } } diff --git a/tim/prune/function/gpsies/GpsiesTrack.java b/tim/prune/function/gpsies/GpsiesTrack.java index 7441195..da1339e 100644 --- a/tim/prune/function/gpsies/GpsiesTrack.java +++ b/tim/prune/function/gpsies/GpsiesTrack.java @@ -9,8 +9,8 @@ public class GpsiesTrack private String _trackName = null; /** Description */ private String _description = null; - /** File id for more details */ - private String _fileId = null; + /** Web page for more details */ + private String _webUrl = null; /** Track length in metres */ private double _trackLength = 0.0; /** Download link */ @@ -50,19 +50,19 @@ public class GpsiesTrack } /** - * @param inId id of track + * @param inUrl web page url */ - public void setFileId(String inId) + public void setWebUrl(String inUrl) { - _fileId = inId; + _webUrl = inUrl; } /** - * @return file id + * @return web url */ - public String getFileId() + public String getWebUrl() { - return _fileId; + return _webUrl; } /** diff --git a/tim/prune/function/gpsies/GpsiesXmlHandler.java b/tim/prune/function/gpsies/GpsiesXmlHandler.java index e639160..3d801be 100644 --- a/tim/prune/function/gpsies/GpsiesXmlHandler.java +++ b/tim/prune/function/gpsies/GpsiesXmlHandler.java @@ -11,13 +11,6 @@ import org.xml.sax.helpers.DefaultHandler; */ public class GpsiesXmlHandler extends DefaultHandler { - private boolean _inTracks = false; - private boolean _inTrack = false; - private boolean _inTrackName = false; - private boolean _inDescription = false; - private boolean _inFileId = false; - private boolean _inTrackLength = false; - private boolean _inLink = false; private String _value = null; private ArrayList _trackList = null; private GpsiesTrack _track = null; @@ -30,18 +23,12 @@ public class GpsiesXmlHandler extends DefaultHandler Attributes inAttributes) throws SAXException { if (inTagName.equals("tracks")) { - _inTracks = true; _trackList = new ArrayList(); } - else if (_inTracks && inTagName.equals("track")) { - _inTrack = true; + else if (inTagName.equals("track")) { _track = new GpsiesTrack(); } - else if (_inTrack && inTagName.equals("title")) {_inTrackName = true;} - else if (_inTrack && inTagName.equals("description")) {_inDescription = true;} - else if (_inTrack && inTagName.equals("fileId")) {_inFileId = true;} - else if (_inTrack && inTagName.equals("trackLengthM")) {_inTrackLength = true;} - else if (_inTrack && inTagName.equals("downloadLink")) {_inLink = true;} + _value = null; super.startElement(inUri, inLocalName, inTagName, inAttributes); } @@ -51,33 +38,26 @@ public class GpsiesXmlHandler extends DefaultHandler public void endElement(String inUri, String inLocalName, String inTagName) throws SAXException { - if (inTagName.equals("tracks")) {_inTracks = false;} - else if (_inTrack && inTagName.equals("track")) { + if (inTagName.equals("track")) { _trackList.add(_track); - _inTrack = false; } - else if (_inTrackName && inTagName.equals("title")) { + else if (inTagName.equals("title")) { _track.setTrackName(_value); - _inTrackName = false; } - else if (_inDescription && inTagName.equals("description")) { + else if (inTagName.equals("description")) { _track.setDescription(_value); - _inDescription = false; } - else if (_inFileId && inTagName.equals("fileId")) { - _track.setFileId(_value); - _inFileId = false; + else if (inTagName.equals("fileId")) { + _track.setWebUrl("http://gpsies.com/map.do?fileId=" + _value); } - else if (_inTrackLength && inTagName.equals("trackLengthM")) { + else if (inTagName.equals("trackLengthM")) { try { _track.setLength(Double.parseDouble(_value)); } catch (NumberFormatException nfe) {} - _inTrackLength = false; } - else if (_inLink && inTagName.equals("downloadLink")) { + else if (inTagName.equals("downloadLink")) { _track.setDownloadLink(_value); - _inLink = false; } super.endElement(inUri, inLocalName, inTagName); } @@ -88,9 +68,8 @@ public class GpsiesXmlHandler extends DefaultHandler public void characters(char[] inCh, int inStart, int inLength) throws SAXException { - _value = new String(inCh, inStart, inLength); - // System.out.println("Value: '" + value + "'"); - // TODO: Note, this doesn't cope well with split characters for really long descriptions etc + String value = new String(inCh, inStart, inLength); + _value = (_value==null?value:_value+value); super.characters(inCh, inStart, inLength); } diff --git a/tim/prune/function/gpsies/TrackListModel.java b/tim/prune/function/gpsies/TrackListModel.java index 3bdeb16..3a7326f 100644 --- a/tim/prune/function/gpsies/TrackListModel.java +++ b/tim/prune/function/gpsies/TrackListModel.java @@ -17,17 +17,26 @@ public class TrackListModel extends AbstractTableModel /** List of tracks */ private ArrayList _trackList = null; /** Column heading for track name */ - private static final String _nameColLabel = I18nManager.getText("dialog.gpsies.column.name"); + private String _nameColLabel = null; /** Column heading for length */ - private static final String _lengthColLabel = I18nManager.getText("dialog.gpsies.column.length"); + private String _lengthColLabel = null; + /** Number of columns */ + private int _numColumns = 2; /** Formatter for distances */ private NumberFormat _distanceFormatter = NumberFormat.getInstance(); /** * Constructor + * @param inColumn1Key key for first column + * @param inColumn2Key key for second column */ - public TrackListModel() + public TrackListModel(String inColumn1Key, String inColumn2Key) { + _nameColLabel = I18nManager.getText(inColumn1Key); + if (inColumn2Key != null) { + _lengthColLabel = I18nManager.getText(inColumn2Key); + } + _numColumns = (_lengthColLabel != null?2:1); _distanceFormatter.setMaximumFractionDigits(1); } @@ -36,7 +45,7 @@ public class TrackListModel extends AbstractTableModel */ public int getColumnCount() { - return 2; + return _numColumns; } /** diff --git a/tim/prune/function/gpsies/UploadGpsiesFunction.java b/tim/prune/function/gpsies/UploadGpsiesFunction.java index 6bd762f..49bd000 100644 --- a/tim/prune/function/gpsies/UploadGpsiesFunction.java +++ b/tim/prune/function/gpsies/UploadGpsiesFunction.java @@ -250,6 +250,7 @@ public class UploadGpsiesFunction extends GenericFunction */ private void startUpload() { + BufferedReader reader = null; try { FormPoster poster = new FormPoster(new URL(GPSIES_URL)); @@ -277,12 +278,12 @@ public class UploadGpsiesFunction extends GenericFunction _writer = new OutputStreamWriter(oStream); new Thread(new Runnable() { public void run() { - boolean[] saveFlags = {true, true, true, false, true}; // export everything + boolean[] saveFlags = {true, true, true, true, false, true}; // export everything try { GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, saveFlags, false); - _writer.close(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) {} + finally { + try {_writer.close();} catch (IOException e) {} } } }).start(); @@ -290,7 +291,7 @@ public class UploadGpsiesFunction extends GenericFunction BufferedInputStream answer = new BufferedInputStream(poster.post()); int response = poster.getResponseCode(); - BufferedReader reader = new BufferedReader(new InputStreamReader(answer)); + reader = new BufferedReader(new InputStreamReader(answer)); String line = reader.readLine(); // Try to extract gpsies page url from the returned message String pageUrl = null; @@ -321,6 +322,9 @@ public class UploadGpsiesFunction extends GenericFunction _app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadfailed") + ": " + ioe.getClass().getName() + " : " + ioe.getMessage()); } + finally { + try {if (reader != null) reader.close();} catch (IOException e) {} + } _dialog.dispose(); } } diff --git a/tim/prune/gui/AudioListener.java b/tim/prune/gui/AudioListener.java new file mode 100644 index 0000000..fe1e933 --- /dev/null +++ b/tim/prune/gui/AudioListener.java @@ -0,0 +1,52 @@ +package tim.prune.gui; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JProgressBar; + +import tim.prune.FunctionLibrary; +import tim.prune.function.PlayAudioFunction; + +/** + * Class to update the supplied progress bar on the basis of + * the currently playing audio file (if any) + */ +public class AudioListener implements Runnable, ActionListener +{ + /** progress bar */ + private JProgressBar _progressBar = null; + + /** + * Constructor + * @param inBar progress bar object to update + */ + public AudioListener(JProgressBar inBar) { + _progressBar = inBar; + } + + /** + * React to button press + */ + public void actionPerformed(ActionEvent inEvent) { + new Thread(this).start(); + } + + /** + * Loop and update progress bar + */ + public void run() + { + int progress = 0; + while (progress >= 0) + { + try { + Thread.sleep(400); + } + catch (InterruptedException e) {} + progress = ((PlayAudioFunction) FunctionLibrary.FUNCTION_PLAY_AUDIO).getPercentage(); + _progressBar.setVisible(progress >= 0); + _progressBar.setValue(progress); + } + } +} diff --git a/tim/prune/gui/DetailsDisplay.java b/tim/prune/gui/DetailsDisplay.java index 9cfd7ce..dee14a9 100644 --- a/tim/prune/gui/DetailsDisplay.java +++ b/tim/prune/gui/DetailsDisplay.java @@ -3,6 +3,7 @@ package tim.prune.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionEvent; @@ -16,6 +17,7 @@ import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JProgressBar; import javax.swing.border.EtchedBorder; import tim.prune.DataSubscriber; @@ -25,6 +27,7 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; import tim.prune.data.Altitude; +import tim.prune.data.AudioFile; import tim.prune.data.Coordinate; import tim.prune.data.DataPoint; import tim.prune.data.Distance; @@ -55,12 +58,22 @@ public class DetailsDisplay extends GenericDisplay private JLabel _aveSpeedLabel = null; // Photo details + private JPanel _photoDetailsPanel = null; private JLabel _photoLabel = null; private PhotoThumbnail _photoThumbnail = null; private JLabel _photoTimestampLabel = null; private JLabel _photoConnectedLabel = null; private JPanel _rotationButtons = null; + // Audio details + private JPanel _audioDetailsPanel = null; + private JLabel _audioLabel = null; + private JLabel _audioConnectedLabel = null; + private JLabel _audioTimestampLabel = null; + private JLabel _audioLengthLabel = null; + private JProgressBar _audioProgress = null; + private JPanel _playAudioPanel = null; + // Units private JComboBox _coordFormatDropdown = null; private JComboBox _distUnitsDropdown = null; @@ -81,6 +94,7 @@ public class DetailsDisplay extends GenericDisplay private static final String LABEL_RANGE_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": "; private static final String LABEL_RANGE_CLIMB = I18nManager.getText("details.range.climb") + ": "; private static final String LABEL_RANGE_DESCENT = ", " + I18nManager.getText("details.range.descent") + ": "; + private static final String LABEL_AUDIO_FILE = I18nManager.getText("details.audio.file") + ": "; private static String LABEL_POINT_ALTITUDE_UNITS = null; private static Altitude.Format LABEL_POINT_ALTITUDE_FORMAT = Altitude.Format.NO_FORMAT; @@ -97,18 +111,11 @@ public class DetailsDisplay extends GenericDisplay JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); mainPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); + Font biggerFont = new JLabel().getFont(); + biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f); // Point details panel - JPanel pointDetailsPanel = new JPanel(); - pointDetailsPanel.setLayout(new BoxLayout(pointDetailsPanel, BoxLayout.Y_AXIS)); - pointDetailsPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) - ); - JLabel pointDetailsLabel = new JLabel(I18nManager.getText("details.pointdetails")); - Font biggerFont = pointDetailsLabel.getFont(); - biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f); - pointDetailsLabel.setFont(biggerFont); - pointDetailsPanel.add(pointDetailsLabel); + JPanel pointDetailsPanel = makeDetailsPanel("details.pointdetails", biggerFont); _indexLabel = new JLabel(I18nManager.getText("details.nopointselection")); pointDetailsPanel.add(_indexLabel); _latLabel = new JLabel(""); @@ -118,6 +125,7 @@ public class DetailsDisplay extends GenericDisplay _altLabel = new JLabel(""); pointDetailsPanel.add(_altLabel); _timeLabel = new JLabel(""); + _timeLabel.setMinimumSize(new Dimension(120, 10)); pointDetailsPanel.add(_timeLabel); _speedLabel = new JLabel(""); pointDetailsPanel.add(_speedLabel); @@ -128,14 +136,7 @@ public class DetailsDisplay extends GenericDisplay pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // range details panel - JPanel rangeDetailsPanel = new JPanel(); - rangeDetailsPanel.setLayout(new BoxLayout(rangeDetailsPanel, BoxLayout.Y_AXIS)); - rangeDetailsPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) - ); - JLabel rangeDetailsLabel = new JLabel(I18nManager.getText("details.rangedetails")); - rangeDetailsLabel.setFont(biggerFont); - rangeDetailsPanel.add(rangeDetailsLabel); + JPanel rangeDetailsPanel = makeDetailsPanel("details.rangedetails", biggerFont); _rangeLabel = new JLabel(I18nManager.getText("details.norangeselection")); rangeDetailsPanel.add(_rangeLabel); _distanceLabel = new JLabel(""); @@ -151,40 +152,68 @@ public class DetailsDisplay extends GenericDisplay rangeDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // photo details panel - JPanel photoDetailsPanel = new JPanel(); - photoDetailsPanel.setLayout(new BoxLayout(photoDetailsPanel, BoxLayout.Y_AXIS)); - photoDetailsPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) - ); - JLabel photoDetailsLabel = new JLabel(I18nManager.getText("details.photodetails")); - photoDetailsLabel.setFont(biggerFont); - photoDetailsPanel.add(photoDetailsLabel); + _photoDetailsPanel = makeDetailsPanel("details.photodetails", biggerFont); _photoLabel = new JLabel(I18nManager.getText("details.nophoto")); - photoDetailsPanel.add(_photoLabel); + _photoDetailsPanel.add(_photoLabel); _photoTimestampLabel = new JLabel(""); - photoDetailsPanel.add(_photoTimestampLabel); + _photoTimestampLabel.setMinimumSize(new Dimension(120, 10)); + _photoDetailsPanel.add(_photoTimestampLabel); _photoConnectedLabel = new JLabel(""); - photoDetailsPanel.add(_photoConnectedLabel); + _photoDetailsPanel.add(_photoConnectedLabel); _photoThumbnail = new PhotoThumbnail(); _photoThumbnail.setVisible(false); _photoThumbnail.setPreferredSize(new Dimension(100, 100)); - photoDetailsPanel.add(_photoThumbnail); + _photoDetailsPanel.add(_photoThumbnail); // Rotate buttons JButton rotLeft = makeRotateButton(IconManager.ROTATE_LEFT, FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT); JButton rotRight = makeRotateButton(IconManager.ROTATE_RIGHT, FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT); + JButton popup = makeRotateButton(IconManager.SHOW_DETAILS, FunctionLibrary.FUNCTION_PHOTO_POPUP); _rotationButtons = new JPanel(); _rotationButtons.add(rotLeft); _rotationButtons.add(rotRight); + _rotationButtons.add(Box.createHorizontalStrut(10)); + _rotationButtons.add(popup); _rotationButtons.setAlignmentX(Component.LEFT_ALIGNMENT); _rotationButtons.setVisible(false); - photoDetailsPanel.add(_rotationButtons); + _photoDetailsPanel.add(_rotationButtons); + _photoDetailsPanel.setVisible(false); + + // audio details panel + _audioDetailsPanel = makeDetailsPanel("details.audiodetails", biggerFont); + _audioLabel = new JLabel(I18nManager.getText("details.noaudio")); + _audioDetailsPanel.add(_audioLabel); + _audioTimestampLabel = new JLabel(""); + _audioTimestampLabel.setMinimumSize(new Dimension(120, 10)); + _audioDetailsPanel.add(_audioTimestampLabel); + _audioLengthLabel = new JLabel(""); + _audioDetailsPanel.add(_audioLengthLabel); + _audioConnectedLabel = new JLabel(""); + _audioDetailsPanel.add(_audioConnectedLabel); + _audioProgress = new JProgressBar(0, 100); + _audioProgress.setString(I18nManager.getText("details.audio.playing")); + _audioProgress.setStringPainted(true); + _audioProgress.setVisible(false); + _audioDetailsPanel.add(_audioProgress); + _playAudioPanel = new JPanel(); + _playAudioPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); + JButton playAudio = makeRotateButton(IconManager.PLAY_AUDIO, FunctionLibrary.FUNCTION_PLAY_AUDIO); + playAudio.addActionListener(new AudioListener(_audioProgress)); + _playAudioPanel.add(playAudio); + JButton stopAudio = makeRotateButton(IconManager.STOP_AUDIO, FunctionLibrary.FUNCTION_STOP_AUDIO); + _playAudioPanel.add(stopAudio); + _playAudioPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + _playAudioPanel.setVisible(false); + _audioDetailsPanel.add(_playAudioPanel); + _audioDetailsPanel.setVisible(false); // add the details panels to the main panel mainPanel.add(pointDetailsPanel); mainPanel.add(Box.createVerticalStrut(5)); mainPanel.add(rangeDetailsPanel); mainPanel.add(Box.createVerticalStrut(5)); - mainPanel.add(photoDetailsPanel); + mainPanel.add(_photoDetailsPanel); + mainPanel.add(Box.createVerticalStrut(5)); + mainPanel.add(_audioDetailsPanel); mainPanel.add(Box.createVerticalStrut(5)); // add the main panel at the top add(mainPanel, BorderLayout.NORTH); @@ -234,6 +263,7 @@ public class DetailsDisplay extends GenericDisplay // Update current point data, if any DataPoint currentPoint = _trackInfo.getCurrentPoint(); Selection selection = _trackInfo.getSelection(); + if ((inUpdateType | DATA_ADDED_OR_REMOVED) > 0) selection.markInvalid(); int currentPointIndex = selection.getCurrentPointIndex(); _speedLabel.setText(""); Distance.Units distUnits = _distUnitsDropdown.getSelectedIndex()==0?Distance.Units.KILOMETRES:Distance.Units.MILES; @@ -343,6 +373,7 @@ public class DetailsDisplay extends GenericDisplay } } // show photo details and thumbnail + _photoDetailsPanel.setVisible(_trackInfo.getPhotoList().getNumPhotos() > 0); Photo currentPhoto = _trackInfo.getPhotoList().getPhoto(_trackInfo.getSelection().getCurrentPhotoIndex()); if ((currentPoint == null || currentPoint.getPhoto() == null) && currentPhoto == null) { @@ -358,7 +389,7 @@ public class DetailsDisplay extends GenericDisplay if (currentPhoto == null) {currentPhoto = currentPoint.getPhoto();} _photoLabel.setText(I18nManager.getText("details.photofile") + ": " + currentPhoto.getFile().getName()); _photoTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText()); - _photoConnectedLabel.setText(I18nManager.getText("details.photo.connected") + ": " + _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " + (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ? I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes"))); _photoThumbnail.setVisible(true); @@ -367,6 +398,27 @@ public class DetailsDisplay extends GenericDisplay if ((inUpdateType & DataSubscriber.PHOTOS_MODIFIED) > 0) {_photoThumbnail.refresh();} } _photoThumbnail.repaint(); + + // audio details + _audioDetailsPanel.setVisible(_trackInfo.getAudioList().getNumAudios() > 0); + AudioFile currentAudio = _trackInfo.getAudioList().getAudio(_trackInfo.getSelection().getCurrentAudioIndex()); + if (currentAudio == null) { + _audioLabel.setText(I18nManager.getText("details.noaudio")); + _audioTimestampLabel.setText(""); + _audioLengthLabel.setText(""); + _audioConnectedLabel.setText(""); + } + else + { + _audioLabel.setText(LABEL_AUDIO_FILE + currentAudio.getFile().getName()); + _audioTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText()); + int audioLength = currentAudio.getLengthInSeconds(); + _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength)); + _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": " + + (currentAudio.getCurrentStatus() == Photo.Status.NOT_CONNECTED ? + I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes"))); + } + _playAudioPanel.setVisible(currentAudio != null); } @@ -455,6 +507,25 @@ public class DetailsDisplay extends GenericDisplay return inCoord; } + /** + * Make a details subpanel + * @param inNameKey key to use for top label + * @param inFont font for top label + * @return panel with correct layout, label + */ + private static JPanel makeDetailsPanel(String inNameKey, Font inFont) + { + JPanel detailsPanel = new JPanel(); + detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS)); + detailsPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) + ); + JLabel detailsLabel = new JLabel(I18nManager.getText(inNameKey)); + detailsLabel.setFont(inFont); + detailsPanel.add(detailsLabel); + return detailsPanel; + } + /** * Create a little button for rotating the current photo * @param inIcon icon to use (from IconManager) diff --git a/tim/prune/gui/DialogCloser.java b/tim/prune/gui/DialogCloser.java new file mode 100644 index 0000000..1085545 --- /dev/null +++ b/tim/prune/gui/DialogCloser.java @@ -0,0 +1,33 @@ +package tim.prune.gui; + +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import javax.swing.JDialog; + +/** + * Convenience class to close a dialog when the escape key is pressed + */ +public class DialogCloser extends KeyAdapter +{ + /** dialog to close */ + private JDialog _dialog = null; + + /** + * Constructor + * @param inDialog dialog to close + */ + public DialogCloser(JDialog inDialog) { + _dialog = inDialog; + } + + /** + * React to the release of the escape key + */ + public void keyReleased(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + _dialog.dispose(); + } + } +} diff --git a/tim/prune/gui/IconManager.java b/tim/prune/gui/IconManager.java index a42d759..858e97a 100644 --- a/tim/prune/gui/IconManager.java +++ b/tim/prune/gui/IconManager.java @@ -60,6 +60,12 @@ public abstract class IconManager public static final String ROTATE_LEFT = "rotate_left_icon.png"; /** Icon for rotating photos rightwards */ public static final String ROTATE_RIGHT = "rotate_right_icon.png"; + /** Icon for showing photo popup */ + public static final String SHOW_DETAILS = "show_details_icon.gif"; + /** Icon for playing audio file */ + public static final String PLAY_AUDIO = "play_audio.gif"; + /** Icon for stopping the current audio file */ + public static final String STOP_AUDIO = "stop_audio.gif"; /** * Get the specified image diff --git a/tim/prune/gui/MediaListModel.java b/tim/prune/gui/MediaListModel.java new file mode 100644 index 0000000..2d8eb6e --- /dev/null +++ b/tim/prune/gui/MediaListModel.java @@ -0,0 +1,47 @@ +package tim.prune.gui; + +import javax.swing.AbstractListModel; + +import tim.prune.data.MediaFile; +import tim.prune.data.MediaList; + +/** + * Class to act as list model for the photo list and audio list + */ +public class MediaListModel extends AbstractListModel +{ + /** media list */ + MediaList _media = null; + + /** + * Constructor giving MediaList object + * @param inList MediaList + */ + public MediaListModel(MediaList inList) { + _media = inList; + } + + /** + * @see javax.swing.ListModel#getSize() + */ + public int getSize() { + return _media.getNumMedia(); + } + + /** + * @see javax.swing.ListModel#getElementAt(int) + */ + public Object getElementAt(int inIndex) + { + MediaFile m = _media.getMedia(inIndex); + // * means modified since loading + return (m.getCurrentStatus() == m.getOriginalStatus()?"":"* ") + m.getFile().getName(); + } + + /** + * Fire event to notify that contents have changed + */ + public void fireChanged() { + this.fireContentsChanged(this, 0, getSize()-1); + } +} diff --git a/tim/prune/gui/MenuManager.java b/tim/prune/gui/MenuManager.java index 588b49e..47044f2 100644 --- a/tim/prune/gui/MenuManager.java +++ b/tim/prune/gui/MenuManager.java @@ -20,8 +20,8 @@ import tim.prune.GenericFunction; import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; +import tim.prune.data.AudioFile; import tim.prune.data.Photo; -import tim.prune.data.PhotoList; import tim.prune.data.Selection; import tim.prune.data.Track; import tim.prune.data.TrackInfo; @@ -37,7 +37,6 @@ public class MenuManager implements DataSubscriber private App _app = null; private Track _track = null; private Selection _selection = null; - private PhotoList _photos = null; // Menu items which need enabling/disabling private JMenuItem _sendGpsItem = null; @@ -77,18 +76,26 @@ public class MenuManager implements DataSubscriber private JMenuItem _getGpsiesItem = null; private JMenuItem _uploadGpsiesItem = null; private JMenuItem _lookupSrtmItem = null; + private JMenuItem _lookupWikipediaItem = null; + private JMenuItem _downloadOsmItem = null; private JMenuItem _distanceItem = null; private JMenuItem _fullRangeDetailsItem = null; private JMenuItem _saveExifItem = null; + private JMenuItem _photoPopupItem = null; private JMenuItem _selectNoPhotoItem = null; private JMenuItem _connectPhotoItem = null; - private JMenuItem _deletePhotoItem = null; + private JMenuItem _removePhotoItem = null; private JMenuItem _disconnectPhotoItem = null; private JMenuItem _correlatePhotosItem = null; private JMenuItem _rearrangePhotosItem = null; private JMenuItem _rotatePhotoLeft = null; private JMenuItem _rotatePhotoRight = null; private JMenuItem _ignoreExifThumb = null; + private JMenuItem _connectAudioItem = null; + private JMenuItem _disconnectAudioItem = null; + private JMenuItem _removeAudioItem = null; + private JMenuItem _correlateAudiosItem = null; + private JMenuItem _selectNoAudioItem = null; private JCheckBoxMenuItem _onlineCheckbox = null; // ActionListeners for reuse by menu and toolbar @@ -101,7 +108,6 @@ public class MenuManager implements DataSubscriber private ActionListener _deleteRangeAction = null; private ActionListener _selectStartAction = null; private ActionListener _selectEndAction = null; - private ActionListener _connectPhotoAction = null; // Toolbar buttons which need enabling/disabling private JButton _saveButton = null; @@ -111,7 +117,7 @@ public class MenuManager implements DataSubscriber private JButton _deleteRangeButton = null; private JButton _selectStartButton = null; private JButton _selectEndButton = null; - private JButton _connectPhotoButton = null; + private JButton _connectButton = null; /** Array of key events */ private static final int[] KEY_EVENTS = { @@ -132,7 +138,6 @@ public class MenuManager implements DataSubscriber _app = inApp; _track = inTrackInfo.getTrack(); _selection = inTrackInfo.getSelection(); - _photos = inTrackInfo.getPhotoList(); } @@ -149,8 +154,7 @@ public class MenuManager implements DataSubscriber JMenuItem openMenuItem = new JMenuItem(I18nManager.getText("function.open")); setShortcut(openMenuItem, "shortcut.menu.file.open"); _openFileAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.openFile(); } }; @@ -159,29 +163,29 @@ public class MenuManager implements DataSubscriber // Add photos JMenuItem addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos")); _addPhotoAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.addPhotos(); } }; addPhotosMenuItem.addActionListener(_addPhotoAction); fileMenu.add(addPhotosMenuItem); + // Add audio files + JMenuItem addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO); + fileMenu.add(addAudioMenuItem); fileMenu.addSeparator(); // Load from GPS JMenuItem loadFromGpsMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSLOAD); setShortcut(loadFromGpsMenuItem, "shortcut.menu.file.load"); fileMenu.add(loadFromGpsMenuItem); // Send to GPS - _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE); - _sendGpsItem.setEnabled(false); + _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE, false); fileMenu.add(_sendGpsItem); fileMenu.addSeparator(); // Save _saveItem = new JMenuItem(I18nManager.getText("menu.file.save"), KeyEvent.VK_S); setShortcut(_saveItem, "shortcut.menu.file.save"); _saveAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.saveFile(); } }; @@ -189,26 +193,21 @@ public class MenuManager implements DataSubscriber _saveItem.setEnabled(false); fileMenu.add(_saveItem); // Export - Kml - _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT); - _exportKmlItem.setEnabled(false); + _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT, false); fileMenu.add(_exportKmlItem); // Gpx - _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT); - _exportGpxItem.setEnabled(false); + _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT, false); fileMenu.add(_exportGpxItem); // Pov - _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT); - _exportPovItem.setEnabled(false); + _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT, false); fileMenu.add(_exportPovItem); // Svg - _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT); - _exportSvgItem.setEnabled(false); + _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT, false); fileMenu.add(_exportSvgItem); fileMenu.addSeparator(); JMenuItem exitMenuItem = new JMenuItem(I18nManager.getText("menu.file.exit")); exitMenuItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.exit(); } }); @@ -220,8 +219,7 @@ public class MenuManager implements DataSubscriber _undoItem = new JMenuItem(I18nManager.getText("menu.track.undo")); setShortcut(_undoItem, "shortcut.menu.track.undo"); _undoAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.beginUndo(); } }; @@ -230,22 +228,19 @@ public class MenuManager implements DataSubscriber trackMenu.add(_undoItem); _clearUndoItem = new JMenuItem(I18nManager.getText("menu.track.clearundo")); _clearUndoItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.clearUndo(); } }); _clearUndoItem.setEnabled(false); trackMenu.add(_clearUndoItem); trackMenu.addSeparator(); - _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS); + _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS, false); setShortcut(_compressItem, "shortcut.menu.edit.compress"); - _compressItem.setEnabled(false); trackMenu.add(_compressItem); _deleteMarkedPointsItem = new JMenuItem(I18nManager.getText("menu.track.deletemarked")); _deleteMarkedPointsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.finishCompressTrack(); } }); @@ -257,8 +252,7 @@ public class MenuManager implements DataSubscriber _rearrangeMenu.setEnabled(false); JMenuItem rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start")); rearrangeStartItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START); } }); @@ -266,8 +260,7 @@ public class MenuManager implements DataSubscriber _rearrangeMenu.add(rearrangeStartItem); JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end")); rearrangeEndItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END); } }); @@ -275,8 +268,7 @@ public class MenuManager implements DataSubscriber _rearrangeMenu.add(rearrangeEndItem); JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest")); rearrangeNearestItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST); } }); @@ -284,16 +276,19 @@ public class MenuManager implements DataSubscriber _rearrangeMenu.add(rearrangeNearestItem); trackMenu.add(_rearrangeMenu); // Get gpsies tracks - _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES); - _getGpsiesItem.setEnabled(false); + _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false); trackMenu.add(_getGpsiesItem); // Upload to gpsies - _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES); - _uploadGpsiesItem.setEnabled(false); + _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false); trackMenu.add(_uploadGpsiesItem); - _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM); - _lookupSrtmItem.setEnabled(false); + _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false); trackMenu.add(_lookupSrtmItem); + _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false); + trackMenu.add(_lookupWikipediaItem); + JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA); + trackMenu.add(searchWikipediaNamesItem); + _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false); + trackMenu.add(_downloadOsmItem); menubar.add(trackMenu); // Range menu @@ -303,8 +298,7 @@ public class MenuManager implements DataSubscriber setShortcut(_selectAllItem, "shortcut.menu.range.all"); _selectAllItem.setEnabled(false); _selectAllItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _selection.selectRange(0, _track.getNumPoints()-1); } }); @@ -312,8 +306,7 @@ public class MenuManager implements DataSubscriber _selectNoneItem = new JMenuItem(I18nManager.getText("menu.range.none")); _selectNoneItem.setEnabled(false); _selectNoneItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.selectNone(); } }); @@ -322,8 +315,7 @@ public class MenuManager implements DataSubscriber _selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start")); _selectStartItem.setEnabled(false); _selectStartAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _selection.selectRangeStart(); } }; @@ -332,8 +324,7 @@ public class MenuManager implements DataSubscriber _selectEndItem = new JMenuItem(I18nManager.getText("menu.range.end")); _selectEndItem.setEnabled(false); _selectEndAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _selection.selectRangeEnd(); } }; @@ -342,8 +333,7 @@ public class MenuManager implements DataSubscriber rangeMenu.addSeparator(); _deleteRangeItem = new JMenuItem(I18nManager.getText("menu.range.deleterange")); _deleteRangeAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.deleteSelectedRange(); } }; @@ -352,36 +342,30 @@ public class MenuManager implements DataSubscriber rangeMenu.add(_deleteRangeItem); _reverseItem = new JMenuItem(I18nManager.getText("menu.range.reverse")); _reverseItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.reverseRange(); } }); _reverseItem.setEnabled(false); rangeMenu.add(_reverseItem); - _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET); - _addTimeOffsetItem.setEnabled(false); + _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET, false); rangeMenu.add(_addTimeOffsetItem); - _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET); - _addAltitudeOffsetItem.setEnabled(false); + _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET, false); rangeMenu.add(_addAltitudeOffsetItem); _mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.range.mergetracksegments")); _mergeSegmentsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.mergeTrackSegments(); } }); _mergeSegmentsItem.setEnabled(false); rangeMenu.add(_mergeSegmentsItem); - _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES); - _deleteFieldValuesItem.setEnabled(false); + _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false); rangeMenu.add(_deleteFieldValuesItem); rangeMenu.addSeparator(); _interpolateItem = new JMenuItem(I18nManager.getText("menu.range.interpolate")); _interpolateItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.interpolateSelection(); } }); @@ -389,8 +373,7 @@ public class MenuManager implements DataSubscriber rangeMenu.add(_interpolateItem); _averageItem = new JMenuItem(I18nManager.getText("menu.range.average")); _averageItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.averageSelection(); } }); @@ -405,8 +388,7 @@ public class MenuManager implements DataSubscriber }); _cutAndMoveItem.setEnabled(false); rangeMenu.add(_cutAndMoveItem); - _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES); - _convertNamesToTimesItem.setEnabled(false); + _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES, false); rangeMenu.add(_convertNamesToTimesItem); menubar.add(rangeMenu); @@ -415,21 +397,18 @@ public class MenuManager implements DataSubscriber setAltKey(pointMenu, "altkey.menu.point"); _editPointItem = new JMenuItem(I18nManager.getText("menu.point.editpoint")); _editPointAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.editCurrentPoint(); } }; _editPointItem.addActionListener(_editPointAction); _editPointItem.setEnabled(false); pointMenu.add(_editPointItem); - _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME); - _editWaypointNameItem.setEnabled(false); + _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME, false); pointMenu.add(_editWaypointNameItem); _deletePointItem = new JMenuItem(I18nManager.getText("menu.point.deletepoint")); _deletePointAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.deleteCurrentPoint(); } }; @@ -439,12 +418,10 @@ public class MenuManager implements DataSubscriber pointMenu.add(_deletePointItem); pointMenu.addSeparator(); // find a waypoint - _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT); - _findWaypointItem.setEnabled(false); + _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT, false); pointMenu.add(_findWaypointItem); // duplicate current point - _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT); - _duplicatePointItem.setEnabled(false); + _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT, false); pointMenu.add(_duplicatePointItem); // paste coordinates function JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES); @@ -474,64 +451,55 @@ public class MenuManager implements DataSubscriber }); viewMenu.add(sidebarsCheckbox); // 3d - _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D); - _show3dItem.setEnabled(false); + _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D, false); viewMenu.add(_show3dItem); // browser submenu _browserMapMenu = new JMenu(I18nManager.getText("menu.view.browser")); _browserMapMenu.setEnabled(false); JMenuItem googleMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.google")); googleMapsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.showExternalMap(UrlGenerator.MAP_SOURCE_GOOGLE); } }); _browserMapMenu.add(googleMapsItem); JMenuItem openMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.openstreetmap")); openMapsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.showExternalMap(UrlGenerator.MAP_SOURCE_OSM); } }); _browserMapMenu.add(openMapsItem); JMenuItem mapquestMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.mapquest")); mapquestMapsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.showExternalMap(UrlGenerator.MAP_SOURCE_MAPQUEST); } }); _browserMapMenu.add(mapquestMapsItem); JMenuItem yahooMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.yahoo")); yahooMapsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.showExternalMap(UrlGenerator.MAP_SOURCE_YAHOO); } }); _browserMapMenu.add(yahooMapsItem); JMenuItem bingMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.bing")); bingMapsItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.showExternalMap(UrlGenerator.MAP_SOURCE_BING); } }); _browserMapMenu.add(bingMapsItem); viewMenu.add(_browserMapMenu); // Charts - _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS); - _chartItem.setEnabled(false); + _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS, false); viewMenu.add(_chartItem); // Distances - _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES); - _distanceItem.setEnabled(false); + _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES, false); viewMenu.add(_distanceItem); // full range details - _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS); - _fullRangeDetailsItem.setEnabled(false); + _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS, false); viewMenu.add(_fullRangeDetailsItem); menubar.add(viewMenu); @@ -543,73 +511,77 @@ public class MenuManager implements DataSubscriber photoMenu.add(addPhotosMenuItem); _saveExifItem = new JMenuItem(I18nManager.getText("menu.photo.saveexif")); _saveExifItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _app.saveExif(); } }); _saveExifItem.setEnabled(false); photoMenu.add(_saveExifItem); - _connectPhotoItem = new JMenuItem(I18nManager.getText("menu.photo.connect")); - _connectPhotoAction = new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _app.connectPhotoToPoint(); + // Deselect current photo + _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none")); + _selectNoPhotoItem.setEnabled(false); + _selectNoPhotoItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _app.getTrackInfo().selectPhoto(-1); } - }; - _connectPhotoItem.addActionListener(_connectPhotoAction); - _connectPhotoItem.setEnabled(false); + }); + photoMenu.add(_selectNoPhotoItem); photoMenu.addSeparator(); + _connectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false); photoMenu.add(_connectPhotoItem); // disconnect photo - _disconnectPhotoItem = new JMenuItem(I18nManager.getText("menu.photo.disconnect")); - _disconnectPhotoItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _app.disconnectPhotoFromPoint(); - } - }); - _disconnectPhotoItem.setEnabled(false); + _disconnectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_PHOTO, false); photoMenu.add(_disconnectPhotoItem); - _deletePhotoItem = new JMenuItem(I18nManager.getText("menu.photo.delete")); - _deletePhotoItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _app.deleteCurrentPhoto(); - } - }); - _deletePhotoItem.setEnabled(false); - photoMenu.add(_deletePhotoItem); + _removePhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_PHOTO, false); + photoMenu.add(_removePhotoItem); // Rotate current photo - _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT); - _rotatePhotoLeft.setEnabled(false); + _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT, false); photoMenu.add(_rotatePhotoLeft); - _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT); - _rotatePhotoRight.setEnabled(false); + _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT, false); photoMenu.add(_rotatePhotoRight); - _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB); - _ignoreExifThumb.setEnabled(false); + // Show photo popup + _photoPopupItem = makeMenuItem(FunctionLibrary.FUNCTION_PHOTO_POPUP, false); + photoMenu.add(_photoPopupItem); + _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB, false); photoMenu.add(_ignoreExifThumb); - _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none")); - _selectNoPhotoItem.setEnabled(false); - _selectNoPhotoItem.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { - _app.getTrackInfo().selectPhoto(-1); - } - }); - photoMenu.add(_selectNoPhotoItem); photoMenu.addSeparator(); // correlate all photos - _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS); - _correlatePhotosItem.setEnabled(false); + _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS, false); photoMenu.add(_correlatePhotosItem); // rearrange photo points - _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS); - _rearrangePhotosItem.setEnabled(false); + _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS, false); photoMenu.add(_rearrangePhotosItem); menubar.add(photoMenu); + // Audio menu + JMenu audioMenu = new JMenu(I18nManager.getText("menu.audio")); + setAltKey(audioMenu, "altkey.menu.audio"); + addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO); + audioMenu.add(addAudioMenuItem); + _selectNoAudioItem = new JMenuItem(I18nManager.getText("menu.range.none")); + _selectNoAudioItem.setEnabled(false); + _selectNoAudioItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + _app.getTrackInfo().selectAudio(-1); + } + }); + audioMenu.add(_selectNoAudioItem); + audioMenu.addSeparator(); + // connect audio + _connectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false); + audioMenu.add(_connectAudioItem); + // Disconnect current audio file + _disconnectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_AUDIO, false); + audioMenu.add(_disconnectAudioItem); + // Remove current audio file + _removeAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_AUDIO, false); + audioMenu.add(_removeAudioItem); + audioMenu.addSeparator(); + // Correlate audio files + _correlateAudiosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_AUDIOS, false); + audioMenu.add(_correlateAudiosItem); + menubar.add(audioMenu); + // Settings menu JMenu settingsMenu = new JMenu(I18nManager.getText("menu.settings")); setAltKey(settingsMenu, "altkey.menu.settings"); @@ -634,6 +606,8 @@ public class MenuManager implements DataSubscriber settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS)); // Set colours settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS)); + // Set line width used for drawing + settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH)); // Set language settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE)); settingsMenu.addSeparator(); @@ -655,6 +629,19 @@ public class MenuManager implements DataSubscriber return menubar; } + /** + * Convenience method for making a menu item using a function + * @param inFunction function + * @param inEnabled flag to specify initial enabled state + * @return menu item using localized name of function + */ + private static JMenuItem makeMenuItem(GenericFunction inFunction, boolean inEnabled) + { + JMenuItem item = makeMenuItem(inFunction); + item.setEnabled(inEnabled); + return item; + } + /** * Convenience method for making a menu item using a function * @param inFunction function @@ -767,11 +754,16 @@ public class MenuManager implements DataSubscriber _selectEndButton.addActionListener(_selectEndAction); _selectEndButton.setEnabled(false); toolbar.add(_selectEndButton); - _connectPhotoButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO)); - _connectPhotoButton.setToolTipText(I18nManager.getText("menu.photo.connect")); - _connectPhotoButton.addActionListener(_connectPhotoAction); - _connectPhotoButton.setEnabled(false); - toolbar.add(_connectPhotoButton); + // Connect to point + _connectButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO)); + _connectButton.setToolTipText(I18nManager.getText(FunctionLibrary.FUNCTION_CONNECT_TO_POINT.getNameKey())); + _connectButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + FunctionLibrary.FUNCTION_CONNECT_TO_POINT.begin(); + } + }); + _connectButton.setEnabled(false); + toolbar.add(_connectButton); // finish off toolbar.setFloatable(false); return toolbar; @@ -818,6 +810,8 @@ public class MenuManager implements DataSubscriber _getGpsiesItem.setEnabled(hasData); _uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints()); _lookupSrtmItem.setEnabled(hasData); + _lookupWikipediaItem.setEnabled(hasData); + _downloadOsmItem.setEnabled(hasData); _findWaypointItem.setEnabled(hasData && _track.hasWaypoints()); // is undo available? boolean hasUndo = !_app.getUndoStack().isEmpty(); @@ -837,24 +831,33 @@ public class MenuManager implements DataSubscriber _selectEndButton.setEnabled(hasPoint); _duplicatePointItem.setEnabled(hasPoint); // are there any photos? - boolean anyPhotos = _photos != null && _photos.getNumPhotos() > 0; + boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0; _saveExifItem.setEnabled(anyPhotos); - // is there a current photo? - boolean hasPhoto = anyPhotos && _selection.getCurrentPhotoIndex() >= 0; - // connect is available if photo and point selected, and photo has no point - Photo currentPhoto = _photos.getPhoto(_selection.getCurrentPhotoIndex()); - boolean connectAvailable = hasPhoto && hasPoint && currentPhoto != null - && currentPhoto.getDataPoint() == null; - _connectPhotoItem.setEnabled(connectAvailable); - _connectPhotoButton.setEnabled(connectAvailable); - _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getDataPoint() != null); + // is there a current photo, audio? + Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto(); + boolean hasPhoto = currentPhoto != null; + AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio(); + boolean hasAudio = currentAudio != null; + // connect is available if (photo/audio) and point selected, and media has no point + boolean connectAvailable = (hasPhoto && hasPoint && currentPhoto.getDataPoint() == null) + || (hasAudio && hasPoint && currentAudio.getDataPoint() == null); + _connectPhotoItem.setEnabled(hasPhoto && hasPoint && currentPhoto.getDataPoint() == null); + _connectButton.setEnabled(connectAvailable); + _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto.getDataPoint() != null); _correlatePhotosItem.setEnabled(anyPhotos && hasData); _rearrangePhotosItem.setEnabled(anyPhotos && hasData && _track.getNumPoints() > 1); - _deletePhotoItem.setEnabled(hasPhoto); + _removePhotoItem.setEnabled(hasPhoto); _rotatePhotoLeft.setEnabled(hasPhoto); _rotatePhotoRight.setEnabled(hasPhoto); - _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getExifThumbnail() != null); + _photoPopupItem.setEnabled(hasPhoto); + _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto.getExifThumbnail() != null); _selectNoPhotoItem.setEnabled(hasPhoto); + boolean anyAudios = _app.getTrackInfo().getAudioList().getNumAudios() > 0; + _selectNoAudioItem.setEnabled(hasAudio); + _removeAudioItem.setEnabled(hasAudio); + _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null); + _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null); + _correlateAudiosItem.setEnabled(anyAudios && hasData); // is there a current range? boolean hasRange = (hasData && _selection.hasRangeSelected()); _deleteRangeItem.setEnabled(hasRange); diff --git a/tim/prune/gui/PhotoListModel.java b/tim/prune/gui/PhotoListModel.java deleted file mode 100644 index 27ed0e6..0000000 --- a/tim/prune/gui/PhotoListModel.java +++ /dev/null @@ -1,57 +0,0 @@ -package tim.prune.gui; - -import javax.swing.AbstractListModel; - -import tim.prune.data.Photo; -import tim.prune.data.PhotoList; - -/** - * Class to act as list model for the photo list - */ -public class PhotoListModel extends AbstractListModel -{ - PhotoList _photos = null; - - /** - * Constructor giving PhotoList object - * @param inList PhotoList - */ - public PhotoListModel(PhotoList inList) - { - _photos = inList; - } - - /** - * @see javax.swing.ListModel#getSize() - */ - public int getSize() - { - return _photos.getNumPhotos(); - } - - /** - * @see javax.swing.ListModel#getElementAt(int) - */ - public Object getElementAt(int inIndex) - { - return _photos.getPhoto(inIndex).getFile().getName(); - } - - /** - * Get the Photo at the given index - * @param inIndex index number, starting at 0 - * @return Photo object - */ - public Photo getPhoto(int inIndex) - { - return _photos.getPhoto(inIndex); - } - - /** - * Fire event to notify that contents have changed - */ - public void fireChanged() - { - this.fireContentsChanged(this, 0, getSize()-1); - } -} diff --git a/tim/prune/gui/PhotoThumbnail.java b/tim/prune/gui/PhotoThumbnail.java index cc7e776..686d90a 100644 --- a/tim/prune/gui/PhotoThumbnail.java +++ b/tim/prune/gui/PhotoThumbnail.java @@ -4,7 +4,6 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; -import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.SwingUtilities; @@ -18,9 +17,10 @@ import tim.prune.data.Photo; public class PhotoThumbnail extends JPanel implements Runnable { private Photo _photo = null; - private BufferedImage _thumbnail = null; + private Image _thumbnail = null; private boolean _loadingImage = false; private boolean _loadFailed = false; + private boolean _inPanel = false; /** String to show before photo is loaded */ private static final String LOADING_STRING = I18nManager.getText("details.photo.loading") + " ..."; @@ -30,9 +30,18 @@ public class PhotoThumbnail extends JPanel implements Runnable */ public PhotoThumbnail() { - setOpaque(true); + this(true); } + /** + * Constructor + * @param inPanel true if thumbnail is inside panel + */ + public PhotoThumbnail(boolean inPanel) + { + setOpaque(true); + _inPanel = inPanel; + } /** * Set the Photo @@ -41,7 +50,8 @@ public class PhotoThumbnail extends JPanel implements Runnable public void setPhoto(Photo inPhoto) { // Check whether the photo has changed - if (_photo != inPhoto) { + if (_photo != inPhoto) + { _photo = inPhoto; _thumbnail = null; _loadFailed = false; @@ -52,7 +62,8 @@ public class PhotoThumbnail extends JPanel implements Runnable /** * Force a refresh / reload */ - public void refresh() { + public void refresh() + { _thumbnail = null; _loadFailed = false; } @@ -82,14 +93,16 @@ public class PhotoThumbnail extends JPanel implements Runnable { // Copy scaled, smoothed (and rotated) image into scaled int usableWidth = getParent().getWidth()-10; - Image scaled = ImageUtils.rotateImage(_thumbnail, usableWidth, usableWidth, _photo.getRotationDegrees()); + int usableHeight = (_inPanel?usableWidth:getHeight()-10); + Image scaled = ImageUtils.rotateImage(_thumbnail, usableWidth, usableHeight, _photo.getRotationDegrees()); int scaleWidth = scaled.getWidth(null); int scaleHeight = scaled.getHeight(null); // Draw scaled / rotated image to component int horizOffset = (getWidth() - scaleWidth) / 2; int vertOffset = (getHeight() - scaleHeight) / 2; inG.drawImage(scaled, horizOffset, vertOffset, scaleWidth, scaleHeight, null); - if (getHeight() < getWidth() || getHeight() > usableWidth) + // Special resize behaviour when locked inside details panel + if (_inPanel && (getHeight() < getWidth() || getHeight() > usableWidth)) { Dimension newsize = new Dimension(usableWidth, usableWidth); setPreferredSize(newsize); @@ -114,30 +127,37 @@ public class PhotoThumbnail extends JPanel implements Runnable */ public void run() { - // Use exif thumbnail? - if (_photo.getExifThumbnail() != null) { - Image image = new ImageIcon(_photo.getExifThumbnail()).getImage(); - _thumbnail = ImageUtils.createScaledImage(image, image.getWidth(null), image.getHeight(null)); - image = null; - } - else + if (_inPanel) { - // no exif thumbnail available, going to have to read whole thing - int picWidth = _photo.getWidth(); - int picHeight = _photo.getHeight(); - if (picWidth > -1 && picHeight > -1) - { - // Just set a "reasonable" thumbnail size for now - final int DEFAULT_THUMB_SIZE = 400; - // calculate maximum thumbnail size - Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, DEFAULT_THUMB_SIZE, DEFAULT_THUMB_SIZE); - // Make icon to load image into - Image image = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage(); - // save scaled, smoothed thumbnail for reuse - _thumbnail = ImageUtils.createScaledImage(image, thumbSize.width, thumbSize.height); + // use either exif thumbnail or photo scaled down to sensible size + if (_photo.getExifThumbnail() != null) { + // Use exif thumbnail + Image image = new ImageIcon(_photo.getExifThumbnail()).getImage(); + _thumbnail = ImageUtils.createScaledImage(image, image.getWidth(null), image.getHeight(null)); image = null; } - else _loadFailed = true; + else + { + // no exif thumbnail available, going to have to read whole thing + int picWidth = _photo.getWidth(); + int picHeight = _photo.getHeight(); + if (picWidth > -1 && picHeight > -1) + { + // Just set a "reasonable" thumbnail size for now + final int DEFAULT_THUMB_SIZE = 400; + // calculate maximum thumbnail size + Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, DEFAULT_THUMB_SIZE, DEFAULT_THUMB_SIZE); + // Make icon to load image into + Image image = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage(); + // save scaled, smoothed thumbnail for reuse + _thumbnail = ImageUtils.createScaledImage(image, thumbSize.width, thumbSize.height); + image = null; + } + else _loadFailed = true; + } + } + else { + _thumbnail = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage(); } _loadingImage = false; repaint(); diff --git a/tim/prune/gui/SelectorDisplay.java b/tim/prune/gui/SelectorDisplay.java index ed244d0..f0772bf 100644 --- a/tim/prune/gui/SelectorDisplay.java +++ b/tim/prune/gui/SelectorDisplay.java @@ -38,12 +38,21 @@ public class SelectorDisplay extends GenericDisplay private JScrollBar _scroller = null; private boolean _ignoreScrollEvents = false; - // Photos - private JList _photoList = null; - private PhotoListModel _photoListModel = null; + // Panel containing lists + private JPanel _listsPanel = null; + private int _visiblePanels = 1; // Waypoints + private JPanel _waypointListPanel = null; private JList _waypointList = null; private WaypointListModel _waypointListModel = null; + // Photos + private JPanel _photoListPanel = null; + private JList _photoList = null; + private MediaListModel _photoListModel = null; + // Audio files + private JPanel _audioListPanel = null; + private JList _audioList = null; + private MediaListModel _audioListModel = null; // scrollbar interval private static final int SCROLLBAR_INTERVAL = 50; @@ -78,43 +87,39 @@ public class SelectorDisplay extends GenericDisplay _trackpointsLabel = new JLabel(I18nManager.getText("details.notrack")); trackDetailsPanel.add(_trackpointsLabel); _filenameLabel = new JLabel(""); + _filenameLabel.setMinimumSize(new Dimension(120, 10)); trackDetailsPanel.add(_filenameLabel); // Scroll bar _scroller = new JScrollBar(JScrollBar.HORIZONTAL, 0, SCROLLBAR_INTERVAL, 0, 100); _scroller.addAdjustmentListener(new AdjustmentListener() { - public void adjustmentValueChanged(AdjustmentEvent e) - { + public void adjustmentValueChanged(AdjustmentEvent e) { selectPoint(e.getValue()); } }); _scroller.setEnabled(false); // Add panel for waypoints / photos - JPanel listsPanel = new JPanel(); - listsPanel.setLayout(new GridLayout(0, 1)); - listsPanel.setBorder(BorderFactory.createCompoundBorder( + _listsPanel = new JPanel(); + _listsPanel.setLayout(new GridLayout(0, 1)); + _listsPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3)) ); _waypointListModel = new WaypointListModel(_trackInfo.getTrack()); _waypointList = new JList(_waypointListModel); _waypointList.setVisibleRowCount(NUM_LIST_ENTRIES); - _waypointList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); _waypointList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) selectWaypoint(_waypointList.getSelectedIndex()); - }}); - JPanel waypointListPanel = new JPanel(); - waypointListPanel.setLayout(new BorderLayout()); - waypointListPanel.add(new JLabel(I18nManager.getText("details.waypointsphotos.waypoints")), BorderLayout.NORTH); - waypointListPanel.add(new JScrollPane(_waypointList), BorderLayout.CENTER); - listsPanel.add(waypointListPanel); + } + }); + _waypointListPanel = makeListPanel("details.lists.waypoints", _waypointList); + _listsPanel.add(_waypointListPanel); // photo list - _photoListModel = new PhotoListModel(_trackInfo.getPhotoList()); + _photoListModel = new MediaListModel(_trackInfo.getPhotoList()); _photoList = new JList(_photoListModel); _photoList.setVisibleRowCount(NUM_LIST_ENTRIES); - _photoList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); _photoList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { @@ -122,12 +127,22 @@ public class SelectorDisplay extends GenericDisplay selectPhoto(_photoList.getSelectedIndex()); } }}); - JPanel photoListPanel = new JPanel(); - photoListPanel.setLayout(new BorderLayout()); - photoListPanel.add(new JLabel(I18nManager.getText("details.waypointsphotos.photos")), BorderLayout.NORTH); - photoListPanel.add(new JScrollPane(_photoList), BorderLayout.CENTER); - listsPanel.add(photoListPanel); - listsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + _photoListPanel = makeListPanel("details.lists.photos", _photoList); + // don't add photo list (because there aren't any photos yet) + + // List for audio files + _audioListModel = new MediaListModel(_trackInfo.getAudioList()); + _audioList = new JList(_audioListModel); + _audioList.addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) + { + if (!e.getValueIsAdjusting()) { + selectAudio(_audioList.getSelectedIndex()); + } + }}); + _audioListPanel = makeListPanel("details.lists.audio", _audioList); + // don't add audio list either + _listsPanel.setAlignmentX(Component.LEFT_ALIGNMENT); // add the controls to the main panel mainPanel.add(trackDetailsPanel); @@ -138,7 +153,7 @@ public class SelectorDisplay extends GenericDisplay // add the main panel at the top add(mainPanel, BorderLayout.NORTH); // and lists in the centre - add(listsPanel, BorderLayout.CENTER); + add(_listsPanel, BorderLayout.CENTER); // set preferred width to be small setPreferredSize(new Dimension(100, 100)); } @@ -150,8 +165,7 @@ public class SelectorDisplay extends GenericDisplay */ private void selectPoint(int inValue) { - if (_track != null && !_ignoreScrollEvents) - { + if (_track != null && !_ignoreScrollEvents) { _trackInfo.selectPoint(inValue); } } @@ -166,6 +180,14 @@ public class SelectorDisplay extends GenericDisplay _trackInfo.selectPhoto(inPhotoIndex); } + /** + * Select the specified audio file + * @param inIndex index of selected audio file + */ + private void selectAudio(int inIndex) + { + _trackInfo.selectAudio(inIndex); + } /** * Select the specified waypoint @@ -173,8 +195,7 @@ public class SelectorDisplay extends GenericDisplay */ private void selectWaypoint(int inWaypointIndex) { - if (inWaypointIndex >= 0) - { + if (inWaypointIndex >= 0) { _trackInfo.selectPoint(_waypointListModel.getWaypoint(inWaypointIndex)); } } @@ -203,8 +224,7 @@ public class SelectorDisplay extends GenericDisplay } else if (numFiles > 1) { - _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": " - + numFiles); + _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": " + numFiles); } else _filenameLabel.setText(""); } @@ -237,6 +257,7 @@ public class SelectorDisplay extends GenericDisplay (DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.PHOTOS_MODIFIED)) > 0) { _photoListModel.fireChanged(); + _audioListModel.fireChanged(); } // Deselect selected waypoint if selected point has since changed if (_waypointList.getSelectedIndex() >= 0) @@ -249,6 +270,9 @@ public class SelectorDisplay extends GenericDisplay _waypointList.clearSelection(); } } + // Hide photo list if no photos loaded, same for audio + redrawLists(_photoListModel.getSize() > 0, _audioListModel.getSize() > 0); + // Make sure correct photo is selected if (_photoListModel.getSize() > 0) { @@ -265,5 +289,62 @@ public class SelectorDisplay extends GenericDisplay } } } + // Same for audio files + if (_audioListModel.getSize() > 0) + { + int audioIndex = _trackInfo.getSelection().getCurrentAudioIndex(); + int listSelection = _audioList.getSelectedIndex(); + // Change listbox selection if indexes not equal + if (listSelection != audioIndex) + { + if (audioIndex < 0) { + _audioList.clearSelection(); + } + else { + _audioList.setSelectedIndex(audioIndex); + } + } + } + } + + /** + * Make one of the three list panels + * @param inNameKey key for heading text + * @param inList list object + * @return panel object + */ + private static JPanel makeListPanel(String inNameKey, JList inList) + { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add(new JLabel(I18nManager.getText(inNameKey)), BorderLayout.NORTH); + inList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + panel.add(new JScrollPane(inList), BorderLayout.CENTER); + return panel; + } + + /** + * Redraw the list panels in the display according to which ones should be shown + * @param inShowPhotos true to show photo list + * @param inShowAudio true to show audio list + */ + private void redrawLists(boolean inShowPhotos, boolean inShowAudio) + { + // exit if same as last time + int panels = 1 + (inShowPhotos?2:0) + (inShowAudio?4:0); + if (panels == _visiblePanels) return; + _visiblePanels = panels; + // remove all panels and re-add them + _listsPanel.removeAll(); + _listsPanel.setLayout(new GridLayout(0, 1)); + _listsPanel.add(_waypointListPanel); + if (inShowPhotos) { + _listsPanel.add(_photoListPanel); + } + if (inShowAudio) { + _listsPanel.add(_audioListPanel); + } + _listsPanel.invalidate(); + _listsPanel.getParent().validate(); } } diff --git a/tim/prune/gui/images/play_audio.gif b/tim/prune/gui/images/play_audio.gif new file mode 100644 index 0000000000000000000000000000000000000000..8d4d6e57ad4eadc1acd0bfae2f0e0f33ed8437c7 GIT binary patch literal 207 zcmZ?wbhEHb6kyGN_zxnqY5Z(IocITfbGk-sx`TNPtzi)T`zT5og z@yy*#qI+A!cGin*su12>hRNV{u~U} E00ACiQUCw| literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/show_details_icon.gif b/tim/prune/gui/images/show_details_icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..97674f294ad0e86d2c23f6db51a1261dd402dc34 GIT binary patch literal 297 zcmZ?wbhEHb6kyBZ)0Lbl`(^H(?Q(W9LW$BbD zJyYf^ozv1Xr>A<(lsR+e6wjGcJqO5ITD^2m_R^(Omag5pc4_h2wR6_4UAh(sw(i}! zw0P^<>aA;gwr-sRh z@80u!&mrLb^Y!0L7myj9d)%3_2hgke?XX+z!ky@X(Qpl=3|p!Lu;K z;zp~c&(e&YGJ2*9tzvGj$#!`CU? M#Ln8`>d0UX04|!6_W%F@ literal 0 HcmV?d00001 diff --git a/tim/prune/gui/images/stop_audio.gif b/tim/prune/gui/images/stop_audio.gif new file mode 100644 index 0000000000000000000000000000000000000000..e0af8f5b074df89cd740c858a355cc41c2ca156c GIT binary patch literal 201 zcmZ?wbhEHb6kyRqxKj=ew)sJN>Z%mu{Lf`6zxzh`KuNUq?FBFVEq!%3& zP(G<)buqr^q`%Y2aIYWhR-UxC`cE`a{K>+|z#z|{!vF*zI~iEr9;o-FcnbNiTGsN? zp@7F)Pvp)Ihv^p!{y2CXVG$7N^EkMHB|v3f0PBGx21@-*ZJCUQ75gU~6*JP9&HgAT P`C#5=>18`KSs1JV)9+@; literal 0 HcmV?d00001 diff --git a/tim/prune/gui/map/DiskTileCacher.java b/tim/prune/gui/map/DiskTileCacher.java index dfc4f92..a250e23 100644 --- a/tim/prune/gui/map/DiskTileCacher.java +++ b/tim/prune/gui/map/DiskTileCacher.java @@ -120,7 +120,8 @@ public class DiskTileCacher implements Runnable } catch (Exception e) {return;} } - try { + try + { // Open streams from URL and to file out = new FileOutputStream(tempFile); in = _url.openStream(); @@ -131,16 +132,18 @@ public class DiskTileCacher implements Runnable } finished = true; } catch (IOException e) {} - finally { - try { - in.close(); - out.close(); - if (!finished) {tempFile.delete();} + finally + { + // clean up files + try {in.close();} catch (Exception e) {} // ignore + try {out.close();} catch (Exception e) {} // ignore + if (!finished) { + tempFile.delete(); } - catch (Exception e) {} // ignore } // Move temp file to desired file location - if (!tempFile.renameTo(_file)) { + if (!tempFile.renameTo(_file)) + { // File couldn't be moved - delete both to be sure tempFile.delete(); _file.delete(); diff --git a/tim/prune/gui/map/MapCanvas.java b/tim/prune/gui/map/MapCanvas.java index 97be7b5..2308fac 100644 --- a/tim/prune/gui/map/MapCanvas.java +++ b/tim/prune/gui/map/MapCanvas.java @@ -103,8 +103,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private int _dragFromX = -1; /** y coordinate of drag from point */ private int _dragFromY = -1; - /** Flag set to true for right-click dragging */ - private boolean _zoomDragging = false; /** x coordinate of drag to point */ private int _dragToX = -1; /** y coordinate of drag to point */ @@ -115,6 +113,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe private int _popupMenuY = -1; /** Flag to prevent showing too often the error message about loading maps */ private boolean _shownOsmErrorAlready = false; + /** Current drawing mode */ + private int _drawMode = MODE_DEFAULT; /** Constant for click sensitivity when selecting nearest point */ private static final int CLICK_SENSITIVITY = 10; @@ -126,6 +126,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe // Colours private static final Color COLOR_MESSAGES = Color.GRAY; + // Drawing modes + private static final int MODE_DEFAULT = 0; + private static final int MODE_ZOOM_RECT = 1; + private static final int MODE_DRAW_POINTS_START = 2; + private static final int MODE_DRAW_POINTS_CONT = 3; /** * Constructor @@ -273,7 +278,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe { zoomIn(); }}); - zoomInItem.setEnabled(true); _popup.add(zoomInItem); JMenuItem zoomOutItem = new JMenuItem(I18nManager.getText("menu.map.zoomout")); zoomOutItem.addActionListener(new ActionListener() { @@ -281,7 +285,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe { zoomOut(); }}); - zoomOutItem.setEnabled(true); _popup.add(zoomOutItem); JMenuItem zoomFullItem = new JMenuItem(I18nManager.getText("menu.map.zoomfull")); zoomFullItem.addActionListener(new ActionListener() { @@ -291,7 +294,6 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe _recalculate = true; repaint(); }}); - zoomFullItem.setEnabled(true); _popup.add(zoomFullItem); _popup.addSeparator(); // Set background @@ -308,13 +310,18 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe newPointItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_popupMenuY, getHeight())); - double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_popupMenuX, getWidth())); - _app.createPoint(new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE), - new Longitude(lon, Coordinate.FORMAT_NONE), null)); + _app.createPoint(createPointFromClick(_popupMenuX, _popupMenuY)); }}); - newPointItem.setEnabled(true); _popup.add(newPointItem); + // draw point series + JMenuItem drawPointsItem = new JMenuItem(I18nManager.getText("menu.map.drawpoints")); + drawPointsItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + _drawMode = MODE_DRAW_POINTS_START; + } + }); + _popup.add(drawPointsItem); } @@ -386,7 +393,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe inG.drawImage(_mapImage, 0, 0, getWidth(), getHeight(), null); } // Draw the zoom rectangle if necessary - if (_zoomDragging) + if (_drawMode == MODE_ZOOM_RECT) { inG.setColor(Color.RED); inG.drawLine(_dragFromX, _dragFromY, _dragFromX, _dragToY); @@ -394,6 +401,15 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe inG.drawLine(_dragToX, _dragFromY, _dragToX, _dragToY); inG.drawLine(_dragFromX, _dragToY, _dragToX, _dragToY); } + else if (_drawMode == MODE_DRAW_POINTS_CONT) + { + // draw line to mouse position to show drawing mode + inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT)); + int prevIndex = _track.getNumPoints()-1; + int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(prevIndex)); + int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(prevIndex)); + inG.drawLine(px, py, _dragToX, _dragToY); + } } else { @@ -520,9 +536,12 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY); final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT); - // try to set double line width for painting - if (inG instanceof Graphics2D) { - ((Graphics2D) inG).setStroke(new BasicStroke(2.0f)); + // try to set line width for painting + if (inG instanceof Graphics2D) + { + int lineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH); + if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;} + ((Graphics2D) inG).setStroke(new BasicStroke(lineWidth)); } int pointsPainted = 0; // draw track points @@ -627,11 +646,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe } } } - // Loop over points, drawing blobs for photo points + // Loop over points, drawing blobs for photo / audio points inG.setColor(secondColour); for (int i=0; i<_track.getNumPoints(); i++) { - if (_track.getPoint(i).getPhoto() != null) + if (_track.getPoint(i).hasMedia()) { int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i)); int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i)); @@ -774,6 +793,20 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe repaint(); } + /** + * Create a DataPoint object from the given click coordinates + * @param inX x coordinate of click + * @param inY y coordinate of click + * @return DataPoint with given coordinates and no altitude + */ + private DataPoint createPointFromClick(int inX, int inY) + { + double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(inY, getHeight())); + double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(inX, getWidth())); + return new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE), + new Longitude(lon, Coordinate.FORMAT_NONE), null); + } + /** * @see javax.swing.JComponent#getMinimumSize() */ @@ -806,22 +839,43 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if (inE.getClickCount() == 1) { // single click - int pointIndex = _track.getNearestPointIndex( - _mapPosition.getXFromPixels(inE.getX(), getWidth()), - _mapPosition.getYFromPixels(inE.getY(), getHeight()), - _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false); - // Extend selection for shift-click - if (inE.isShiftDown()) { - _trackInfo.extendSelection(pointIndex); + if (_drawMode == MODE_DEFAULT) + { + int pointIndex = _track.getNearestPointIndex( + _mapPosition.getXFromPixels(inE.getX(), getWidth()), + _mapPosition.getYFromPixels(inE.getY(), getHeight()), + _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false); + // Extend selection for shift-click + if (inE.isShiftDown()) { + _trackInfo.extendSelection(pointIndex); + } + else { + _trackInfo.selectPoint(pointIndex); + } } - else { - _trackInfo.selectPoint(pointIndex); + else if (_drawMode == MODE_DRAW_POINTS_START) + { + _app.createPoint(createPointFromClick(inE.getX(), inE.getY())); + _dragToX = inE.getX(); + _dragToY = inE.getY(); + _drawMode = MODE_DRAW_POINTS_CONT; + } + else if (_drawMode == MODE_DRAW_POINTS_CONT) + { + DataPoint point = createPointFromClick(inE.getX(), inE.getY()); + _app.createPoint(point); + point.setSegmentStart(false); } } else if (inE.getClickCount() == 2) { // double click - panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2); - zoomIn(); + if (_drawMode == MODE_DEFAULT) { + panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2); + zoomIn(); + } + else if (_drawMode == MODE_DRAW_POINTS_START || _drawMode == MODE_DRAW_POINTS_CONT) { + _drawMode = MODE_DEFAULT; + } } } else @@ -868,13 +922,14 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe public void mouseReleased(MouseEvent inE) { _recalculate = true; - if (_zoomDragging && Math.abs(_dragToX - _dragFromX) > 20 && Math.abs(_dragToY - _dragFromY) > 20) + if (_drawMode == MODE_ZOOM_RECT && Math.abs(_dragToX - _dragFromX) > 20 + && Math.abs(_dragToY - _dragFromY) > 20) { //System.out.println("Finished zoom: " + _dragFromX + ", " + _dragFromY + " to " + _dragToX + ", " + _dragToY); _mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight()); + _drawMode = MODE_DEFAULT; } _dragFromX = _dragFromY = -1; - _zoomDragging = false; repaint(); } @@ -887,20 +942,19 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe if (!inE.isMetaDown()) { // Left mouse drag - pan map by appropriate amount - _zoomDragging = false; if (_dragFromX != -1) { panMap(_dragFromX - inE.getX(), _dragFromY - inE.getY()); _recalculate = true; repaint(); } - _dragFromX = inE.getX(); - _dragFromY = inE.getY(); + _dragFromX = _dragToX = inE.getX(); + _dragFromY = _dragToY = inE.getY(); } else { // Right-click and drag - draw rectangle and control zoom - _zoomDragging = true; + _drawMode = MODE_ZOOM_RECT; if (_dragFromX == -1) { _dragFromX = inE.getX(); _dragFromY = inE.getY(); @@ -917,7 +971,13 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe */ public void mouseMoved(MouseEvent inEvent) { - // ignore + // Ignore unless we're drawing points + if (_drawMode == MODE_DRAW_POINTS_CONT) + { + _dragToX = inEvent.getX(); + _dragToY = inEvent.getY(); + repaint(); + } } /** @@ -998,8 +1058,11 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe else if (code == KeyEvent.VK_LEFT) rightwardsPan = -PAN_DISTANCE; panMap(rightwardsPan, upwardsPan); + // Check for escape + if (code == KeyEvent.VK_ESCAPE) + _drawMode = MODE_DEFAULT; // Check for backspace key to delete current point (delete key already handled by menu) - if (code == KeyEvent.VK_BACK_SPACE && currPointIndex >= 0) { + else if (code == KeyEvent.VK_BACK_SPACE && currPointIndex >= 0) { _app.deleteCurrentPoint(); } } diff --git a/tim/prune/gui/map/MapSource.java b/tim/prune/gui/map/MapSource.java index c2311ae..35590a6 100644 --- a/tim/prune/gui/map/MapSource.java +++ b/tim/prune/gui/map/MapSource.java @@ -69,23 +69,34 @@ public abstract class MapSource * @param inUrl url to check * @return validated url with correct prefix and trailing slash, or null */ - protected static String fixBaseUrl(String inUrl) + public static String fixBaseUrl(String inUrl) { if (inUrl == null || inUrl.equals("")) {return null;} - String url = inUrl; + String urlstr = inUrl; // check prefix try { - new URL(url); + new URL(urlstr); } catch (MalformedURLException e) { + // fail if protocol specified + if (urlstr.indexOf("://") >= 0) {return null;} // add the http protocol - url = "http://" + url; + urlstr = "http://" + urlstr; } // check trailing / - if (!url.endsWith("/")) { - url = url + "/"; + if (!urlstr.endsWith("/")) { + urlstr = urlstr + "/"; } - return url; + // Validate current url, return null if not ok + try { + URL url = new URL(urlstr); + // url host must contain a dot + if (url.getHost().indexOf('.') < 0) {return null;} + } + catch (MalformedURLException e) { + urlstr = null; + } + return urlstr; } /** @@ -114,11 +125,14 @@ public abstract class MapSource */ public String getSiteStrings() { - String s = ""; + StringBuilder sb = new StringBuilder(); for (int i=0; idouble
. + * Prevents interpretation of 32 bit numbers as negative, and forces a positive answer + */ + public static final double convertToPositiveValue(int inNumerator, int inDenominator) + { + if (inDenominator == 0) return 0.0; + double numeratorDbl = inNumerator; + double denomDbl = inDenominator; + if (inNumerator >= 0) + return numeratorDbl / denomDbl; + final double correction = Math.pow(2.0, 32); + numeratorDbl += correction; + if (inDenominator < 0) denomDbl += correction; + return numeratorDbl / denomDbl; + } } diff --git a/tim/prune/jpeg/ExternalExifLibrary.java b/tim/prune/jpeg/ExternalExifLibrary.java index 1c64b4e..cff2346 100644 --- a/tim/prune/jpeg/ExternalExifLibrary.java +++ b/tim/prune/jpeg/ExternalExifLibrary.java @@ -42,12 +42,14 @@ public class ExternalExifLibrary implements ExifLibrary { data.setLatitudeRef(gpsdir.getString(GpsDirectory.TAG_GPS_LATITUDE_REF)); Rational[] latRats = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE); + double seconds = ExifGateway.convertToPositiveValue(latRats[2].getNumerator(), latRats[2].getDenominator()); data.setLatitude(new double[] {latRats[0].doubleValue(), - latRats[1].doubleValue(), latRats[2].doubleValue()}); + latRats[1].doubleValue(), seconds}); data.setLongitudeRef(gpsdir.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF)); Rational[] lonRats = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE); + seconds = ExifGateway.convertToPositiveValue(lonRats[2].getNumerator(), lonRats[2].getDenominator()); data.setLongitude(new double[] {lonRats[0].doubleValue(), - lonRats[1].doubleValue(), lonRats[2].doubleValue()}); + lonRats[1].doubleValue(), seconds}); } // Altitude (if present) @@ -110,6 +112,7 @@ public class ExternalExifLibrary implements ExifLibrary return data; } + /** * Check whether the exifreader class can be correctly resolved * @return true if it looks ok diff --git a/tim/prune/jpeg/drew/ExifReader.java b/tim/prune/jpeg/drew/ExifReader.java index d4df892..384778d 100644 --- a/tim/prune/jpeg/drew/ExifReader.java +++ b/tim/prune/jpeg/drew/ExifReader.java @@ -3,6 +3,7 @@ package tim.prune.jpeg.drew; import java.io.File; import java.util.HashMap; +import tim.prune.jpeg.ExifGateway; import tim.prune.jpeg.JpegData; /** @@ -93,8 +94,7 @@ public class ExifReader */ public ExifReader(File inFile) throws JpegException { - JpegSegmentData segments = JpegSegmentReader.readSegments(inFile); - _data = segments.getSegment(JpegSegmentReader.SEGMENT_APP1); + _data = JpegSegmentReader.readExifSegment(inFile); } /** @@ -343,14 +343,16 @@ public class ExifReader break; case TAG_GPS_LATITUDE: Rational[] latitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), latitudes[2].doubleValue()}); + inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), + ExifGateway.convertToPositiveValue(latitudes[2].getNumerator(), latitudes[2].getDenominator())}); break; case TAG_GPS_LONGITUDE_REF: inMetadata.setLongitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount)); break; case TAG_GPS_LONGITUDE: Rational[] longitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount); - inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), longitudes[2].doubleValue()}); + inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), + ExifGateway.convertToPositiveValue(longitudes[2].getNumerator(), longitudes[2].getDenominator())}); break; case TAG_GPS_ALTITUDE_REF: inMetadata.setAltitudeRef(_data[inTagValueOffset]); diff --git a/tim/prune/jpeg/drew/JpegSegmentReader.java b/tim/prune/jpeg/drew/JpegSegmentReader.java index 382e9f5..65515a1 100644 --- a/tim/prune/jpeg/drew/JpegSegmentReader.java +++ b/tim/prune/jpeg/drew/JpegSegmentReader.java @@ -14,94 +14,64 @@ public class JpegSegmentReader /** End of image marker */ private static final byte MARKER_EOI = (byte)0xD9; - /** APP0 Jpeg segment identifier -- Jfif data. */ - public static final byte SEGMENT_APP0 = (byte)0xE0; /** APP1 Jpeg segment identifier -- where Exif data is kept. */ - public static final byte SEGMENT_APP1 = (byte)0xE1; - /** APP2 Jpeg segment identifier. */ - public static final byte SEGMENT_APP2 = (byte)0xE2; - /** APP3 Jpeg segment identifier. */ - public static final byte SEGMENT_APP3 = (byte)0xE3; - /** APP4 Jpeg segment identifier. */ - public static final byte SEGMENT_APP4 = (byte)0xE4; - /** APP5 Jpeg segment identifier. */ - public static final byte SEGMENT_APP5 = (byte)0xE5; - /** APP6 Jpeg segment identifier. */ - public static final byte SEGMENT_APP6 = (byte)0xE6; - /** APP7 Jpeg segment identifier. */ - public static final byte SEGMENT_APP7 = (byte)0xE7; - /** APP8 Jpeg segment identifier. */ - public static final byte SEGMENT_APP8 = (byte)0xE8; - /** APP9 Jpeg segment identifier. */ - public static final byte SEGMENT_APP9 = (byte)0xE9; - /** APPA Jpeg segment identifier -- can hold Unicode comments. */ - public static final byte SEGMENT_APPA = (byte)0xEA; - /** APPB Jpeg segment identifier. */ - public static final byte SEGMENT_APPB = (byte)0xEB; - /** APPC Jpeg segment identifier. */ - public static final byte SEGMENT_APPC = (byte)0xEC; - /** APPD Jpeg segment identifier -- IPTC data in here. */ - public static final byte SEGMENT_APPD = (byte)0xED; - /** APPE Jpeg segment identifier. */ - public static final byte SEGMENT_APPE = (byte)0xEE; - /** APPF Jpeg segment identifier. */ - public static final byte SEGMENT_APPF = (byte)0xEF; - /** Start Of Image segment identifier. */ - public static final byte SEGMENT_SOI = (byte)0xD8; - /** Define Quantization Table segment identifier. */ - public static final byte SEGMENT_DQT = (byte)0xDB; - /** Define Huffman Table segment identifier. */ - public static final byte SEGMENT_DHT = (byte)0xC4; - /** Start-of-Frame Zero segment identifier. */ - public static final byte SEGMENT_SOF0 = (byte)0xC0; - /** Jpeg comment segment identifier. */ - public static final byte SEGMENT_COM = (byte)0xFE; + private static final byte SEGMENT_APP1 = (byte)0xE1; /** Magic numbers to mark the beginning of all Jpegs */ private static final int MAGIC_JPEG_BYTE_1 = 0xFF; private static final int MAGIC_JPEG_BYTE_2 = 0xD8; + /** + * Get the Exif data segment for the specified file + * @param inFile File to read + * @return Exif data segment as byte array + * @throws JpegException on file read errors or exif data errors + */ + public static byte[] readExifSegment(File inFile) throws JpegException + { + JpegSegmentData data = readSegments(inFile); + return data.getSegment(SEGMENT_APP1); + } + + /** * Obtain the Jpeg segment data from the specified file * @param inFile File to read * @return Jpeg segment data from file * @throws JpegException on file read errors or exif data errors */ - public static JpegSegmentData readSegments(File inFile) throws JpegException + private static JpegSegmentData readSegments(File inFile) throws JpegException { JpegSegmentData segmentData = new JpegSegmentData(); - BufferedInputStream bStream = null; try { bStream = new BufferedInputStream(new FileInputStream(inFile)); - int offset = 0; // first two bytes should be jpeg magic number - int magic1 = bStream.read() & 0xFF; - int magic2 = bStream.read() & 0xFF; - checkMagicNumbers(magic1, magic2); + final int magic1 = bStream.read() & 0xFF; + final int magic2 = bStream.read() & 0xFF; + if (magic1 != MAGIC_JPEG_BYTE_1 || magic2 != MAGIC_JPEG_BYTE_2) { + throw new JpegException("not a jpeg file"); + } - offset += 2; // Loop around segments found + boolean foundExif = false; do { // next byte is 0xFF byte segmentIdentifier = (byte) (bStream.read() & 0xFF); if ((segmentIdentifier & 0xFF) != 0xFF) { - throw new JpegException("expected jpeg segment start identifier 0xFF at offset " - + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF)); + throw new JpegException("expected jpeg segment start 0xFF, not 0x" + + Integer.toHexString(segmentIdentifier & 0xFF)); } - offset++; // next byte is byte thisSegmentMarker = (byte) (bStream.read() & 0xFF); - offset++; // next 2-bytes are : [high-byte] [low-byte] byte[] segmentLengthBytes = new byte[2]; bStream.read(segmentLengthBytes, 0, 2); - offset += 2; int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF); // segment length includes size bytes, so subtract two segmentLength -= 2; @@ -110,8 +80,11 @@ public class JpegSegmentReader else if (segmentLength < 0) throw new JpegException("segment size would be less than zero"); byte[] segmentBytes = new byte[segmentLength]; - bStream.read(segmentBytes, 0, segmentLength); - offset += segmentLength; + int bytesRead = bStream.read(segmentBytes, 0, segmentLength); + // Bail if not all bytes read in one go - otherwise following sections will be out of step + if (bytesRead != segmentLength) { + throw new JpegException("Tried to read " + segmentLength + " bytes but only got " + bytesRead); + } if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF)) { // The 'Start-Of-Scan' segment comes last so break out of loop @@ -126,9 +99,10 @@ public class JpegSegmentReader { segmentData.addSegment(thisSegmentMarker, segmentBytes); } - // loop through to the next segment + // loop through to the next segment if exif hasn't already been found + foundExif = (thisSegmentMarker == SEGMENT_APP1); } - while (true); + while (!foundExif); } catch (FileNotFoundException fnfe) { @@ -153,19 +127,4 @@ public class JpegSegmentReader // Return the result return segmentData; } - - - /** - * Helper method that validates the Jpeg file's magic number. - * @param inMagic1 first half of magic number - * @param inMagic2 second half of magic number - * @throws JpegException if numbers do not match magic numbers expected - */ - private static void checkMagicNumbers(int inMagic1, int inMagic2) throws JpegException - { - if (inMagic1 != MAGIC_JPEG_BYTE_1 || inMagic2 != MAGIC_JPEG_BYTE_2) - { - throw new JpegException("not a jpeg file"); - } - } -} \ No newline at end of file +} diff --git a/tim/prune/lang/prune-texts_af.properties b/tim/prune/lang/prune-texts_af.properties index 109aeb8..2c0b6ce 100644 --- a/tim/prune/lang/prune-texts_af.properties +++ b/tim/prune/lang/prune-texts_af.properties @@ -29,9 +29,9 @@ menu.range.start=Stel Reeks Begin menu.range.end=Stel Reeks Einde menu.photo=Foto menu.photo.saveexif=Stoor na EXIF -menu.photo.connect=Las foto by huidige punt -menu.photo.disconnect=Ontkoppel foto vanaf huidige punt -menu.photo.delete=Verwyder foto +function.connecttopoint=Las foto by huidige punt +function.disconnectfrompoint=Ontkoppel vanaf huidige punt +function.removephoto=Verwyder foto menu.view=Kyk menu.view.browser=Kaart in werf blaaier menu.view.browser.google=Google Kaarte diff --git a/tim/prune/lang/prune-texts_cz.properties b/tim/prune/lang/prune-texts_cz.properties index 93f4494..37ab637 100644 --- a/tim/prune/lang/prune-texts_cz.properties +++ b/tim/prune/lang/prune-texts_cz.properties @@ -1,5 +1,5 @@ # Text entries for the Prune application -# Czech entries as extra +# Czech entries thanks to prot_d # Menu entries menu.file=Soubor @@ -10,7 +10,7 @@ menu.track=Trasa menu.track.undo=Undo menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo menu.track.deletemarked=Smazat ozna\u010den\u00e9 body -menu.track.rearrange=P\u0159euspo\u0159\u00e1dat body +menu.track.rearrange=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek menu.track.rearrange.end=V\u0161e na konec menu.track.rearrange.nearest=Zarovnat body na trasu @@ -30,9 +30,7 @@ menu.point.editpoint=Upravit bod menu.point.deletepoint=Smazat bod menu.photo=Fotografie menu.photo.saveexif=Ulo\u017eit do Exif -menu.photo.connect=P\u0159ipojit do bodu -menu.photo.disconnect=Odpojit od bodu -menu.photo.delete=Odebrat fotografii +menu.audio=Audionahr\u00e1vka menu.view=Zobrazen\u00ed menu.view.showsidebars=Zobrazit panely menu.view.browser=Mapa v internetov\u00e9m prohl\u00ed\u017ee\u010di @@ -49,7 +47,8 @@ menu.map.zoomin=P\u0159ibl\u00ed\u017eit menu.map.zoomout=Odd\u00e1lit menu.map.zoomfull=\u00dapln\u011b odd\u00e1lit menu.map.newpoint=Vytvo\u0159it nov\u00fd bod -menu.map.connect=Propojit body +menu.map.drawpoints=Vytvo\u0159it n\u011bkolik bod\u016f +menu.map.connect=Propojit body trasy menu.map.autopan=Automatika zorn\u00e9ho pole menu.map.showmap=Zobrazit mapu menu.map.showscalebar=Zobrazit m\u011b\u0159\u00edtko @@ -61,6 +60,7 @@ altkey.menu.range=R altkey.menu.point=B altkey.menu.view=Z altkey.menu.photo=F +altkey.menu.audio=A altkey.menu.settings=N altkey.menu.help=P @@ -81,11 +81,11 @@ function.exportkml=Export KML function.exportgpx=Export GPX function.exportpov=Export POV function.exportsvg=Export SVG -function.editwaypointname=Nastavit n\u00e1zev bodu +function.editwaypointname=Nastavit n\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu function.compress=Komprimovat trasu function.addtimeoffset=P\u0159idat \u010dasov\u00fd posun function.addaltitudeoffset=P\u0159idat v\u00fd\u0161kov\u00fd posun -function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy bod\u016f na \u010dasov\u00e9 zna\u010dky +function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy v\u00fdzna\u010dn\u00fdch bod\u016f na \u010dasy function.deletefieldvalues=Smazat hodnoty pole function.findwaypoint=Hledat bod function.pastecoordinates=Zadat sou\u0159adnice @@ -99,14 +99,27 @@ function.setpaths=Nastavit cestu k program\u016fm function.getgpsies=St\u00e1hnout trasy z Gpsies function.uploadgpsies=Nahr\u00e1t trasu na Gpsies function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM +function.getwikipedia=Hledat na Wikipedii podle vzd\u00e1lenosti +function.searchwikipedianames=Hledat na Wikipedii podle jm\u00e9na +function.downloadosm=St\u00e1hnout data OSM pro oblast function.duplicatepoint=Zdvojit bod function.setcolours=Nastavit barvy +function.setlinewidth=Nastavit tlou\u0161\u0165ku \u010d\u00e1ry function.setlanguage=Nastavit jazyk +function.connecttopoint=P\u0159ipojit do bodu +function.disconnectfrompoint=Odpojit od bodu +function.removephoto=Odebrat fotografii function.correlatephotos=Sladit fotografie podle \u010dasu function.rearrangephotos=Uspo\u0159\u00e1dat fotografie function.rotatephotoleft=Oto\u010dit fotografii doleva function.rotatephotoright=Oto\u010dit fotografii doprava +function.photopopup=Zobrazit celou fotografii function.ignoreexifthumb=Ignorovat n\u00e1hled v Exif +function.loadaudio=P\u0159idat audionahr\u00e1vky +function.removeaudio=Odebrat audionahr\u00e1vku +function.correlateaudios=Sladit audionahr\u00e1vky podle \u010dasu +function.playaudio=P\u0159ehr\u00e1t audionahr\u00e1vku +function.stopaudio=Zastavit p\u0159ehr\u00e1v\u00e1n\u00ed function.help=Pomoc function.showkeys=Zobrazit kl\u00e1vesov\u00e9 zkratky function.about=O programu @@ -149,7 +162,7 @@ dialog.jpegload.progress=Pros\u00edm chvilku strpen\u00ed p\u0159i vyhled\u00e1v dialog.gpsload.nogpsbabel=Nenalezen program gpsbabel. Pokra\u010dovat? dialog.gpsload.device=Ozna\u010den\u00ed za\u0159\u00edzen\u00ed dialog.gpsload.format=Form\u00e1t -dialog.gpsload.getwaypoints=Na\u010d\u00edst body +dialog.gpsload.getwaypoints=Na\u010d\u00edst v\u00fdzna\u010dn\u00e9 body dialog.gpsload.gettracks=Na\u010d\u00edst trasy dialog.gpsload.save=Ulo\u017eit do souboru dialog.gpssend.sendwaypoints=Poslat bod @@ -168,7 +181,7 @@ dialog.save.overwrite.title=Soubor u\u017e existuje dialog.save.overwrite.text=Tento soubor u\u017e existuje. Opravdu chcete soubor p\u0159epsat? dialog.save.notypesselected=Nebyl vybr\u00e1n ani jeden typ bod\u016f dialog.exportkml.text=Nadpis dat -dialog.exportkml.altitude=V\u00fd\u0161ka nad hladinou mo\u0159e (pro l\u00e9t\u00e1n\u00ed) +dialog.exportkml.altitude=V\u00fd\u0161ka nad hladinou mo\u0159e (pro letectv\u00ed) dialog.exportkml.kmz=Komprimovat do souboru kmz dialog.exportkml.exportimages=Vlo\u017eit n\u00e1hledy fotografi\u00ed dialog.exportkml.trackcolour=Barva trasy @@ -193,6 +206,7 @@ dialog.pointtype.desc=Ulo\u017eit body n\u00e1sleduj\u00edc\u00edch typ\u016f: dialog.pointtype.track=Body trasy dialog.pointtype.waypoint=V\u00fdzna\u010dn\u00e9 body dialog.pointtype.photo=M\u00edsta s fotografiemi +dialog.pointtype.audio=M\u00edsta s audionahr\u00e1vkami dialog.pointtype.selection=Jen v\u00fdb\u011br dialog.confirmreversetrack.title=Potvr\u010fte obr\u00e1cen\u00ed dialog.confirmreversetrack.text=Tato trasa obsahuje \u010dasov\u00e9 zna\u010dky, jejich\u017e po\u0159ad\u00ed se obr\u00e1cen\u00edm zm\u011bn\u00ed.\nOpravdu chcete obr\u00e1tit v\u00fdb\u011br? @@ -214,7 +228,7 @@ dialog.pointedit.table.changed=Zm\u011bn\u011bno dialog.pointedit.changevalue.text=Zadejte novou hodnotu pole dialog.pointedit.changevalue.title=Upravit pole dialog.pointnameedit.name=N\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu -dialog.pointnameedit.uppercase=VELKÁ p\u00edsmena +dialog.pointnameedit.uppercase=VELK\u00c1 p\u00edsmena dialog.pointnameedit.lowercase=mal\u00e1 p\u00edsmena dialog.pointnameedit.sentencecase=Po\u010d\u00e1te\u010dn\u00ed P\u00edsmena Velk\u00e1 dialog.addtimeoffset.add=P\u0159idat \u010das @@ -278,12 +292,14 @@ dialog.gpsies.activity.motorbiking=Motorka dialog.gpsies.activity.snowshoe=Sn\u011b\u017enice dialog.gpsies.activity.sailing=Lo\u010f dialog.gpsies.activity.skating=Bruslen\u00ed +dialog.wikipedia.column.name=N\u00e1zev \u010dl\u00e1nku +dialog.wikipedia.column.distance=Vzd\u00e1lenost dialog.correlate.notimestamps=U bod\u016f nejsou \u010dasov\u00e9 zna\u010dky, tak\u017ee nen\u00ed s \u010d\u00edm fotografie sladit. dialog.correlate.nouncorrelatedphotos=V\u0161echny fotografie jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat? dialog.correlate.photoselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch fotografi\u00ed pro ur\u010den\u00ed \u010dasov\u00e9ho posunu -dialog.correlate.photoselect.photoname=N\u00e1zev fotografie -dialog.correlate.photoselect.timediff=\u010casov\u00fd rozd\u00edl -dialog.correlate.photoselect.photolater=Vyfoceno pozd\u011bji +dialog.correlate.select.photoname=N\u00e1zev fotografie +dialog.correlate.select.timediff=\u010casov\u00fd rozd\u00edl +dialog.correlate.select.photolater=Vyfoceno pozd\u011bji dialog.correlate.options.tip=Tip: kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s. dialog.correlate.options.intro=Upravte mo\u017enosti automatick\u00e9ho slad\u011bn\u00ed dialog.correlate.options.offsetpanel=\u010casov\u00fd posun @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=hodin, dialog.correlate.options.offset.minutes=minut a dialog.correlate.options.offset.seconds=sekund dialog.correlate.options.photolater=Fotografie pozd\u011bj\u0161\u00ed ne\u017e bod -dialog.correlate.options.pointlater=Bod pozd\u011bj\u0161\u00ed ne\u017e fotografie +dialog.correlate.options.pointlaterphoto=Bod pozd\u011bj\u0161\u00ed ne\u017e fotografie +dialog.correlate.options.audiolater=Audio pozd\u011bj\u0161\u00ed ne\u017e bod +dialog.correlate.options.pointlateraudio=Bod pozd\u011bj\u0161\u00ed ne\u017e audio dialog.correlate.options.limitspanel=Limity slad\u011bn\u00ed dialog.correlate.options.notimelimit=Bez \u010dasov\u00e9ho limitu dialog.correlate.options.timelimit=\u010casov\u00fd limit @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Bez d\u00e9lkov\u00e9ho limitu dialog.correlate.options.distancelimit=D\u00e9lkov\u00fd limit dialog.correlate.options.correlate=Sladit dialog.correlate.alloutsiderange=V\u0161echny fotografie le\u017e\u00ed mimo \u010dasov\u00e9 rozmez\u00ed trasy, tak\u017ee nemohou b\u00fdt slad\u011bny.\nPokuste se zm\u011bnit \u010dasov\u00fd posun nebo ru\u010dn\u011b sla\u010fte aspo\u0148 jednu fotografii. +dialog.correlate.filetimes=\u010cas z\u00e1znamu souboru znamen\u00e1: +dialog.correlate.filetimes2=audionahr\u00e1vky +dialog.correlate.correltimes=Sladit tento okam\u017eik nahr\u00e1vky: +dialog.correlate.timestamp.beginning=Za\u010d\u00e1tek +dialog.correlate.timestamp.middle=St\u0159ed +dialog.correlate.timestamp.end=Konec +dialog.correlate.audioselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch nahr\u00e1vek pro ur\u010den\u00ed \u010dasov\u00e9ho posunu +dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky +dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec @@ -380,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Cache s mapami dialog.saveconfig.prune.kmzimagewidth=\u0160\u00ed\u0159ka bitmapy KMZ dialog.saveconfig.prune.kmzimageheight=V\u00fd\u0161ka bitmapy KMZ dialog.saveconfig.prune.colourscheme=Barevn\u00e9 sch\u00e9ma +dialog.saveconfig.prune.linewidth=Tlou\u0161\u0165ka \u010d\u00e1ry dialog.saveconfig.prune.kmltrackcolour=Barva trasy v KML dialog.setpaths.intro=Je-li to t\u0159eba, m\u016f\u017eete nastavit cesty k extern\u00edm aplikac\u00edm: dialog.setpaths.found=Cesta nalezena? @@ -409,6 +437,9 @@ dialog.diskcache.dir=Adres\u00e1\u0159 s cache dialog.diskcache.createdir=Vytvo\u0159it adres\u00e1\u0159 dialog.diskcache.nocreate=Adres\u00e1\u0159 nebyl vytvo\u0159en dialog.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit +dialog.setlinewidth.text=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed trasa (1-4) +dialog.downloadosm.desc=Potvr\u010fte, \u017ee se maj\u00ed k dan\u00e9 oblasti st\u00e1hnout data OSM: +dialog.searchwikipedianames.search=Vyhledat: # 3d window dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed Prune @@ -438,16 +469,21 @@ confirm.undo.single=operace vr\u00e1cena confirm.undo.multi=operac\u00ed vr\u00e1ceno confirm.jpegload.single=fotografie p\u0159id\u00e1na confirm.jpegload.multi=fotografie p\u0159id\u00e1ny -confirm.photo.connect=fotografie propojena +confirm.media.connect=soubor p\u0159ipojen confirm.photo.disconnect=fotografie odpojena -confirm.correlate.single=fotografie slad\u011bna -confirm.correlate.multi=fotografie slad\u011bny +confirm.audio.disconnect=audionahr\u00e1vka odpojena +confirm.media.removed=odstran\u011bno +confirm.correlatephotos.single=fotografie slad\u011bna +confirm.correlatephotos.multi=fotografie slad\u011bny confirm.createpoint=bod vytvo\u0159en confirm.rotatephoto=fotografie oto\u010dena confirm.running=Prob\u00edh\u00e1 ... confirm.lookupsrtm1=Nalezeno confirm.lookupsrtm2=v\u00fd\u0161kov\u00fdch hodnot confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny +confirm.audioload=Audionahr\u00e1vky p\u0159id\u00e1ny +confirm.correlateaudios.single=Audionahr\u00e1vka slad\u011bna +confirm.correlateaudios.multi=Audionahr\u00e1vky slad\u011bny # Buttons button.ok=OK @@ -490,6 +526,7 @@ filetype.kmz=soubory KMZ filetype.gpx=soubory GPX filetype.pov=soubory POV filetype.svg=soubory SVG +filetype.audio=soubory MP3, OGG, WAV # Display components display.nodata=\u017d\u00e1dn\u00e1 data @@ -524,12 +561,17 @@ details.range.maxspeed=Max. rychlost details.range.numsegments=Po\u010det segment\u016f details.range.pace=Tempo details.range.gradient=Sp\u00e1d -details.waypointsphotos.waypoints=V\u00fdzna\u010dn\u00e9 body -details.waypointsphotos.photos=Fotografie +details.lists.waypoints=V\u00fdzna\u010dn\u00e9 body +details.lists.photos=Fotografie +details.lists.audio=Audionahr\u00e1vky details.photodetails=Detaily fotografie details.nophoto=Fotografie nevybr\u00e1na details.photo.loading=Na\u010d\u00edt\u00e1m -details.photo.connected=P\u0159ipojeno +details.media.connected=P\u0159ipojeno +details.audiodetails=Detaily audionahr\u00e1vky +details.noaudio=Audionahr\u00e1vka nevybr\u00e1na +details.audio.file=Zvukov\u00fd soubor +details.audio.playing=p\u0159ehr\u00e1v\u00e1n... map.overzoom=P\u0159i tomto p\u0159ibl\u00ed\u017een\u00ed mapa nen\u00ed k dispozici # Field names @@ -572,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.cz +wikipedia.lang=cs # Cardinals for 3d plots cardinal.n=N @@ -582,9 +625,11 @@ cardinal.w=W # Undo operations undo.load=na\u010d\u00edst data undo.loadphotos=na\u010d\u00edst fotografie +undo.loadaudios=na\u010d\u00edst audionahr\u00e1vky undo.editpoint=upravit bod undo.deletepoint=smazat bod -undo.deletephoto=odebrat fotografii +undo.removephoto=odebrat fotografii +undo.removeaudio=odebrat audionahr\u00e1vku undo.deleterange=smazat rozmez\u00ed undo.compress=zkomprimovat trasu undo.insert=vlo\u017eit body @@ -594,15 +639,16 @@ undo.addtimeoffset=p\u0159idat \u010dasov\u00fd posun undo.addaltitudeoffset=p\u0159idat v\u00fd\u0161kov\u00fd posun undo.rearrangewaypoints=p\u0159euspo\u0159\u00e1dat body undo.cutandmove=p\u0159esunout v\u00fdb\u011br -undo.connectphoto=p\u0159ipojit fotografii -undo.disconnectphoto=odpojit fotografii -undo.correlate=sladit fotografie +undo.connect=p\u0159ipojit +undo.disconnect=odpojit +undo.correlatephotos=sladit fotografie undo.rearrangephotos=uspo\u0159\u00e1dat fotografie undo.createpoint=vytvo\u0159it bod undo.rotatephoto=oto\u010dit fotografii undo.convertnamestotimes=p\u0159ev\u00e9st n\u00e1zvy na \u010dasy undo.lookupsrtm=na\u010d\u00edst nadm. v\u00fd\u0161ky ze SRTM undo.deletefieldvalues=smazat hodnoty pol\u00ed +undo.correlateaudios=sladit audionahr\u00e1vky # Error messages error.save.dialogtitle=Chyba p\u0159i ukl\u00e1d\u00e1n\u00ed @@ -624,9 +670,9 @@ error.load.othererror=Chyba p\u0159i \u010dten\u00ed souboru: error.jpegload.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed fotografi\u00ed error.jpegload.nofilesfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory error.jpegload.nojpegsfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory jpeg -error.jpegload.noexiffound=Nenalezena informace EXIF error.jpegload.nogpsfound=Nenalezena informace GPS error.jpegload.exifreadfailed=Nepoda\u0159ilo se na\u010d\u00edst informaci EXIF. Tu nelze na\u010d\u00edst\nbez intern\u00ed nebo extern\u00ed knihovny. +error.audioload.nofilesfound=Nebyly nalezeny \u017e\u00e1dn\u00e9 zvukov\u00e9 soubory. error.gpsload.unknown=Nezn\u00e1m\u00e1 chyba error.undofailed.title=Selhalo undo error.undofailed.text=Nepoda\u0159ilo se vr\u00e1tit operaci @@ -644,3 +690,4 @@ error.lookupsrtm.nonefound=Pro tyto body nen\u00ed k dispozici informace o nadmo error.lookupsrtm.nonerequired=U v\u0161ech bod\u016f u\u017e je informaci o v\u00fd\u0161ce, tak\u017ee nen\u00ed co dohled\u00e1vat error.gpsies.uploadnotok=Server gpsies vr\u00e1til hl\u00e1\u0161en\u00ed error.gpsies.uploadfailed=Chyba - nepoda\u0159ilo se nahr\u00e1t data. +error.playaudiofailed=Nepoda\u0159ilo se p\u0159ehr\u00e1t zvukov\u00fd soubor. diff --git a/tim/prune/lang/prune-texts_de.properties b/tim/prune/lang/prune-texts_de.properties index cc95059..a2a45ba 100644 --- a/tim/prune/lang/prune-texts_de.properties +++ b/tim/prune/lang/prune-texts_de.properties @@ -30,9 +30,10 @@ menu.point.editpoint=Punkt bearbeiten menu.point.deletepoint=Punkt l\u00f6schen menu.photo=Foto menu.photo.saveexif=Exif Daten speichern -menu.photo.connect=Mit Punkt verkn\u00fcpfen -menu.photo.disconnect=Vom Punkt trennen -menu.photo.delete=Foto entfernen +function.connecttopoint=Mit Punkt verkn\u00fcpfen +function.disconnectfrompoint=Vom Punkt trennen +function.removephoto=Foto entfernen +menu.audio=Audio menu.view=Ansicht menu.view.showsidebars=Seitenleisten anzeigen menu.view.browser=Karte in Browser @@ -44,6 +45,7 @@ menu.map.zoomin=Hineinzoomen menu.map.zoomout=Herauszoomen menu.map.zoomfull=Auf Bildschirmgr\u00f6\u00dfe zoomen menu.map.newpoint=Neuen Punkt erzeugen +menu.map.drawpoints=Punktereihe aufzeichnen menu.map.connect=Trackpunkte mit Linie anzeigen menu.map.autopan=Autozentrierung menu.map.showmap=Karte zeigen @@ -56,6 +58,7 @@ altkey.menu.range=B altkey.menu.point=P altkey.menu.view=A altkey.menu.photo=F +altkey.menu.audio=U altkey.menu.settings=E altkey.menu.help=H @@ -94,14 +97,24 @@ function.setpaths=Programmpfade setzen function.getgpsies=Gpsies Tracks holen function.uploadgpsies=Daten zum Gpsies hochladen function.lookupsrtm=H\u00f6hendaten von SRTM holen +function.getwikipedia=Wikipediaartikeln in der N\u00e4he nachschlagen +function.searchwikipedianames=Wikipedia mit Name durchsuchen +function.downloadosm=OSM Daten f\u00fcr dieses Gebiet herunterladen function.duplicatepoint=Punkt verdoppeln function.setcolours=Farben einstellen +function.setlinewidth=Liniedicke einstellen function.setlanguage=Sprache einstellen function.correlatephotos=Fotos korrelieren function.rearrangephotos=Fotos reorganisieren function.rotatephotoleft=Foto nach Links drehen function.rotatephotoright=Foto nach Rechts drehen +function.photopopup=Fotofenster anzeigen function.ignoreexifthumb=Exif Vorschaubild ignorieren +function.loadaudio=Audiodateien laden +function.removeaudio=Audiodatei entfernen +function.correlateaudios=Audios korrelieren +function.playaudio=Audiodatei abspielen +function.stopaudio=Abspielen abbrechen function.help=Hilfe function.showkeys=Tastenkombinationen anzeigen function.about=\u00dcber Prune @@ -188,6 +201,7 @@ dialog.pointtype.desc=Folgende Punkttypen speichern: dialog.pointtype.track=Trackpunkte dialog.pointtype.waypoint=Wegpunkte dialog.pointtype.photo=Fotopunkte +dialog.pointtype.audio=Audiopunkte dialog.pointtype.selection=Nur aktuellen Bereich dialog.confirmreversetrack.title=Umkehrung best\u00e4tigen dialog.confirmreversetrack.text=Diese Daten enthalten Zeitangaben, die bei einer Umkehrung in falscher Reihenfolge erscheinen w\u00fcrden.\nSind Sie sicher, dass Sie diesen Bereich umkehren wollen? @@ -273,13 +287,15 @@ dialog.gpsies.activity.motorbiking=Motorrad dialog.gpsies.activity.snowshoe=Schneeschuh dialog.gpsies.activity.sailing=Segeln dialog.gpsies.activity.skating=Inline-Skating +dialog.wikipedia.column.name=Artikelname +dialog.wikipedia.column.distance=Entfernung dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden. dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren? dialog.correlate.photoselect.intro=W\u00e4hlen Sie eines dieser Fotos aus, um die Zeitdifferenz zu berechnen -dialog.correlate.photoselect.photoname=Bezeichnung des Fotos -dialog.correlate.photoselect.timediff=Zeitdifferenz -dialog.correlate.photoselect.photolater=Foto sp\u00e4ter -dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell korrelierten Foto kann die Zeitdifferenz automatisch berechnet werden. +dialog.correlate.select.photoname=Bezeichnung des Fotos +dialog.correlate.select.timediff=Zeitdifferenz +dialog.correlate.select.photolater=Foto sp\u00e4ter +dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden. dialog.correlate.options.intro=W\u00e4hlen Sie die Optionen f\u00fcr die Korrelation aus dialog.correlate.options.offsetpanel=Zeitunterschied dialog.correlate.options.offset=Unterschied @@ -287,7 +303,9 @@ dialog.correlate.options.offset.hours=Stunden, dialog.correlate.options.offset.minutes=Minuten und dialog.correlate.options.offset.seconds=Sekunden dialog.correlate.options.photolater=Foto sp\u00e4ter als Punkt -dialog.correlate.options.pointlater=Punkt sp\u00e4ter als Foto +dialog.correlate.options.pointlaterphoto=Punkt sp\u00e4ter als Foto +dialog.correlate.options.audiolater=Audio sp\u00e4ter als Punkt +dialog.correlate.options.pointlateraudio=Punkt sp\u00e4ter als Audio dialog.correlate.options.limitspanel=Korrelation Grenzen dialog.correlate.options.notimelimit=Keine Zeitgrenzen dialog.correlate.options.timelimit=Zeitgrenzen @@ -295,6 +313,15 @@ dialog.correlate.options.nodistancelimit=Keine Distanzgrenzen dialog.correlate.options.distancelimit=Distanzgrenzen dialog.correlate.options.correlate=Korrelieren dialog.correlate.alloutsiderange=Alle Fotos sind au\u00dferhalb des Track Zeitraums. Sie k\u00f6nnen nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Foto ein. +dialog.correlate.filetimes=Die Datei Zeitstempel zeigen: +dialog.correlate.filetimes2=der Tonspuren +dialog.correlate.correltimes=F\u00fcr das Korrelieren, folgendes verwenden: +dialog.correlate.timestamp.beginning=Anfang +dialog.correlate.timestamp.middle=Mitte +dialog.correlate.timestamp.end=Ende +dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audios aus, um die Zeitdifferenz zu berechnen +dialog.correlate.select.audioname=Audio Name +dialog.correlate.select.audiolater=Audio sp\u00e4ter dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte dialog.rearrangephotos.tostart=Am Anfang dialog.rearrangephotos.toend=Am Ende @@ -375,6 +402,7 @@ dialog.saveconfig.prune.diskcache=Kartenordner dialog.saveconfig.prune.kmzimagewidth=Bildbreite in KMZ dialog.saveconfig.prune.kmzimageheight=Bildh\u00f6he in KMZ dialog.saveconfig.prune.colourscheme=Farbschema +dialog.saveconfig.prune.linewidth=Liniedicke dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Applikationen setzen: dialog.setpaths.found=Pfad gefunden? @@ -404,16 +432,18 @@ dialog.diskcache.dir=Kartenordner dialog.diskcache.createdir=Ordner anlegen dialog.diskcache.nocreate=Ordner wurde nicht angelegt dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, die Sie l\u00f6schen m\u00f6chten +dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4) +dialog.downloadosm.desc=Best\u00e4tigen um rohe OSM Daten f\u00fcr den Gebiet herunterzuladen: +dialog.searchwikipedianames.search=Suche nach: # 3d window dialog.3d.title=Prune 3D Ansicht -dialog.3d.altitudecap=Minimum H\u00f6henskala dialog.3d.altitudefactor=Vervielfachungsfaktor für Höhen dialog.3dlines.title=Prune Gitterlinien dialog.3dlines.empty=Keine Linien zum Anzeigen! dialog.3dlines.intro=Hier sind die Linien f\u00fcr die 3D Ansicht -# Confirm messages || These are displayed as confirmation in the status bar +# Confirm messages confirm.loadfile=Daten aus Datei geladen confirm.save.ok1=Es wurden confirm.save.ok2=Punkte gespeichert nach @@ -434,18 +464,23 @@ confirm.undo.single=Operation r\u00fcckg\u00e4ngig gemacht confirm.undo.multi=Operationen r\u00fcckg\u00e4ngig gemacht confirm.jpegload.single=Foto wurde geladen confirm.jpegload.multi=Fotos wurden geladen -confirm.photo.connect=Foto verbunden +confirm.media.connect=Media verbunden confirm.photo.disconnect=Foto getrennt -confirm.correlate.single=Foto wurde korreliert -confirm.correlate.multi=Fotos wurden korreliert +confirm.audio.disconnect=Audio getrennt +confirm.correlatephotos.single=Foto wurde korreliert +confirm.correlatephotos.multi=Fotos wurden korreliert confirm.createpoint=Punkt erzeugt confirm.rotatephoto=Foto gedreht confirm.running=In Bearbeitung ... confirm.lookupsrtm1=Es wurden confirm.lookupsrtm2=H\u00f6henwerte gefunden confirm.deletefieldvalues=Feldwerte gelöscht +confirm.audioload=Audiodateien geladen +confirm.media.removed=entfernt +confirm.correlateaudios.single=Audio wurde korreliert +confirm.correlateaudios.multi=Audios wurden korreliert -# Buttons || These are all the texts for buttons +# Buttons button.ok=OK button.back=Zur\u00fcck button.next=Vorw\u00e4rts @@ -486,6 +521,7 @@ filetype.kmz=KMZ Dateien filetype.gpx=GPX Dateien filetype.pov=POV Dateien filetype.svg=SVG Dateien +filetype.audio=MP3, OGG, WAV Dateien # Display components display.nodata=Keine Daten geladen @@ -520,12 +556,17 @@ details.range.maxspeed=H\u00f6chstgeschwindigkeit details.range.numsegments=Anzahl Abschnitte details.range.pace=Tempo details.range.gradient=Gef\u00e4lle -details.waypointsphotos.waypoints=Wegpunkte -details.waypointsphotos.photos=Fotos +details.lists.waypoints=Wegpunkte +details.lists.photos=Fotos details.photodetails=Fotodetails details.nophoto=Kein Foto ausgew\u00e4hlt details.photo.loading=Laden -details.photo.connected=Verbunden +details.media.connected=Verbunden +details.lists.audio=Audio +details.audiodetails=Audiodetails +details.noaudio=Keine Audiodatei ausgew\u00e4hlt +details.audio.file=Audiodatei +details.audio.playing=wird abgespielt... map.overzoom=Keine Karten f\u00fcr diesen Zoomfaktor verf\u00fcgbar # Field names @@ -562,6 +603,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.de +wikipedia.lang=de # Cardinals for 3d plots cardinal.n=N @@ -572,9 +614,11 @@ cardinal.w=W # Undo operations undo.load=Daten laden undo.loadphotos=Fotos laden +undo.loadaudios=Audiodateien laden undo.editpoint=Punkt bearbeiten undo.deletepoint=Punkt l\u00f6schen -undo.deletephoto=Foto entfernen +undo.removephoto=Foto entfernen +undo.removeaudio=Audiodatei entfernen undo.deleterange=Bereich l\u00f6schen undo.compress=Track komprimieren undo.insert=Punkte hinzuf\u00fcgen @@ -584,15 +628,16 @@ undo.addtimeoffset=Zeitverschiebung aufrechnen undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen undo.rearrangewaypoints=Wegpunkte reorganisieren undo.cutandmove=Bereich verschieben -undo.connectphoto=Foto verbinden -undo.disconnectphoto=Foto trennen -undo.correlate=Fotos korrelieren +undo.connect=verbinden +undo.disconnect=trennen +undo.correlatephotos=Fotos korrelieren undo.rearrangephotos=Fotos reorganisieren undo.createpoint=Punkt erzeugen undo.rotatephoto=Foto umdrehen undo.convertnamestotimes=Namen in Zeitstempel umwandeln undo.lookupsrtm=H\u00f6hendaten von SRTM holen undo.deletefieldvalues=Feldwerte löschen +undo.correlateaudios=Audios korrelieren # Error messages error.save.dialogtitle=Fehler beim Speichern @@ -614,9 +659,9 @@ error.load.othererror=Fehler beim Lesen der Datei: error.jpegload.dialogtitle=Fehler beim Laden von Fotos error.jpegload.nofilesfound=Keine Dateien gefunden error.jpegload.nojpegsfound=Keine Jpeg Dateien gefunden -error.jpegload.noexiffound=Keine EXIF Information gefunden error.jpegload.nogpsfound=Keine GPS Information gefunden error.jpegload.exifreadfailed=EXIF Aufruf fehlgeschlagen. Keine EXIF Information k\u00f6nnen gelesen werden\nohne einen internen oder externen Bibliothek. +error.audioload.nofilesfound=Keine Audiodateien gefunden error.gpsload.unknown=Unbekannter Fehler error.undofailed.title=Undo fehlgeschlagen error.undofailed.text=Operation konnte nicht r\u00fcckg\u00e4ngig gemacht werden @@ -634,3 +679,4 @@ error.lookupsrtm.nonefound=Keine H\u00f6hendaten verf error.lookupsrtm.nonerequired=Alle Punkte haben schon Höhendaten error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen +error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen diff --git a/tim/prune/lang/prune-texts_de_CH.properties b/tim/prune/lang/prune-texts_de_CH.properties index 21a47c4..b4a3cb0 100644 --- a/tim/prune/lang/prune-texts_de_CH.properties +++ b/tim/prune/lang/prune-texts_de_CH.properties @@ -30,9 +30,10 @@ menu.point.editpoint=Punkt editiere menu.point.deletepoint=Punkt lösche menu.photo=Föteli menu.photo.saveexif=Exif Date speicherä -menu.photo.connect=Mitem Punkt verbindä -menu.photo.disconnect=Vonem Punkt trännä -menu.photo.delete=Föteli entfernä +function.connecttopoint=Mitem Punkt verbindä +function.disconnectfrompoint=Vonem Punkt trännä +function.removephoto=Föteli entfernä +menu.audio=Audio menu.view=Aasicht menu.view.showsidebars=Seiteleischten aazeige menu.view.browser=Karte inem Browser @@ -44,6 +45,7 @@ menu.map.zoomin=Innezoome menu.map.zoomout=Uusezoome menu.map.zoomfull=Zoome zum ganzes Bild menu.map.newpoint=Noii Punkt +menu.map.drawpoints=Noii Punkte uufzeichnä menu.map.connect=Trackpünktli verbindä menu.map.autopan=Autopan menu.map.showmap=Karte zeigä @@ -56,6 +58,7 @@ altkey.menu.range=B altkey.menu.point=P altkey.menu.view=A altkey.menu.photo=F +altkey.menu.audio=U altkey.menu.settings=I altkey.menu.help=H @@ -86,22 +89,32 @@ function.deletefieldvalues=Werte von nem F function.pastecoordinates=Noii Koordinaten iigebe function.charts=Diagramme function.show3d=Drüü-D Aasicht -function.distances=Distanze +function.distances=Entfärnige function.fullrangedetails=Zuesätzlichi Beriichinfos function.setmapbg=Karte Hintegrund setzä function.getgpsies=Gpsies Tracks holä function.uploadgpsies=Date zum Gpsies uufaladä function.lookupsrtm=Höhendate vonem SRTM hole +function.getwikipedia=Im Wikipedia in dr Nöchi naaluege +function.searchwikipedianames=Wikipedia mit Name durasueche +function.downloadosm=OSM-Date für dere Gebiet abaladä function.duplicatepoint=Punkt verdopplä function.correlatephotos=Fötelis korrelierä function.rearrangephotos=Fötelis reorganisierä function.rotatephotoleft=Föteli nach Links dräyä function.rotatephotoright=Föteli nach Rächts dräyä +function.photopopup=Fötelifänschter aazeigä function.ignoreexifthumb=Exif Vorschaubildli ignorierä -function.setkmzimagesize=Bildligrösse inem KMZ setze -function.setpaths=Programmepfade setze -function.setcolours=Farben setze -function.setlanguage=Sproch setze +function.loadaudio=Audiofiles lade +function.removeaudio=Audiodatei entfernä +function.correlateaudios=Audios korrelierä +function.playaudio=Audiofile abspielä +function.stopaudio=Abspielen abbrächä +function.setkmzimagesize=Bildligrösse inem KMZ setzä +function.setpaths=Programmepfade setzä +function.setcolours=Farben setzä +function.setlinewidth=Liniedicke setzä +function.setlanguage=Sproch setzä function.help=Hilfe function.showkeys=Tastekombinatione aazeige function.about=Über Prune @@ -188,6 +201,7 @@ dialog.pointtype.desc=Folgende Punkttype speichere: dialog.pointtype.track=Trackpunkte dialog.pointtype.waypoint=Waypoints dialog.pointtype.photo=Fötelipunkte +dialog.pointtype.audio=Audiopunkte dialog.pointtype.selection=Nur aktuelli Beriich dialog.confirmreversetrack.title=Umdrehig bestätige dialog.confirmreversetrack.text=Diese Daten enthalte Ziitstämpel Informatione, die bei dr Umkehrig usser Reihefolge erschiene würdi.\nSind Sie sicher, Sie wend dn Beriich umkehre? @@ -241,7 +255,7 @@ dialog.charts.svgwidth=SVG Breiti dialog.charts.svgheight=SVG Höhi dialog.charts.needaltitudeortimes=Ohni Höhi Date und au ohne Ziit, isch es nöd möglech, Diagramme z zeigä. dialog.charts.gnuplotnotfound=Gnuplot isch mit dem Pfad nöd gfunde worde -dialog.distances.intro=Dischtanze per Luftlinie zwüschet Punkte +dialog.distances.intro=Entfärnige per Luftlinie zwüschet Punkte dialog.distances.column.from=Vom Punkt dialog.distances.column.to=Zum Punkt dialog.distances.currentpoint=Aktuelli Punkt @@ -273,13 +287,15 @@ dialog.gpsies.activity.motorbiking=Motorrad dialog.gpsies.activity.snowshoe=Schneeschuh dialog.gpsies.activity.sailing=Segle dialog.gpsies.activity.skating=Inline-Skate +dialog.wikipedia.column.name=Artikelname +dialog.wikipedia.column.distance=Entfärnig dialog.correlate.notimestamps=Es hät kei Ziitstämpel inem Track innä, so s'isch nöd möglech die Fötelis zu korrelierä. dialog.correlate.nouncorrelatedphotos=Alle Fötelis sin scho korreliert.\nWend Sie trotzdem fortsetzä? dialog.correlate.photoselect.intro=Wählet Sie eini vo deren Föteli uus, um die Ziitdifferänz zu berächnä -dialog.correlate.photoselect.photoname=Föteli Name -dialog.correlate.photoselect.timediff=Ziitdifferänz -dialog.correlate.photoselect.photolater=Föteli spöter -dialog.correlate.options.tip=Tipp: Mit mindeschtens einem korrelierten Föteli, die Ziitdifferänz kann automatisch berächnet werdä. +dialog.correlate.select.photoname=Föteli Name +dialog.correlate.select.timediff=Ziitdifferänz +dialog.correlate.select.photolater=Föteli spöter +dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elemänt kann die Ziitdifferänz automatisch berächnet werdä. dialog.correlate.options.intro=Wählet Sie die Optione uus für die Korrelierig dialog.correlate.options.offsetpanel=Ziitunterschied dialog.correlate.options.offset=Unterschied @@ -287,7 +303,9 @@ dialog.correlate.options.offset.hours=Schtund dialog.correlate.options.offset.minutes=Minutä und dialog.correlate.options.offset.seconds=Sekundä dialog.correlate.options.photolater=Föteli spöter alsem Punkt -dialog.correlate.options.pointlater=Punkt spöter alsem Föteli +dialog.correlate.options.pointlaterphoto=Punkt spöter alsem Föteli +dialog.correlate.options.audiolater=Audio spöter alsem Punkt +dialog.correlate.options.pointlateraudio=Punkt spöter alsem Audio dialog.correlate.options.limitspanel=Korrelation Gränzä dialog.correlate.options.notimelimit=Kei Ziitgränzä dialog.correlate.options.timelimit=Ziitgränzä @@ -295,9 +313,18 @@ dialog.correlate.options.nodistancelimit=Kei Distanzgr dialog.correlate.options.distancelimit=Distanzgränzä dialog.correlate.options.correlate=Korrelierä dialog.correlate.alloutsiderange=Alli Fötelis sin uusserhalb vonem Track Ziitruum, so chönne nöd korreliert werdä.\nVersuechet Sie mitenem anderen Offset oder verbindet Sie manuell mindeschtens eis Föteli. +dialog.correlate.filetimes=Die Datei Zeitstempel zeigen: +dialog.correlate.filetimes2=der Tonspuren +dialog.correlate.correltimes=Fürs Korreliere, folgendes verwände: +dialog.correlate.timestamp.beginning=Aafang +dialog.correlate.timestamp.middle=Mitti +dialog.correlate.timestamp.end=Ände +dialog.correlate.audioselect.intro=Wählet Sie eini vo deren Audios uus, um die Ziitdifferänz zu berächnä +dialog.correlate.select.audioname=Audio Name +dialog.correlate.select.audiolater=Audio spöter dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von den Punkten setze dialog.rearrangephotos.tostart=zum Aafang -dialog.rearrangephotos.toend=zum Ende +dialog.rearrangephotos.toend=zum Ände dialog.rearrangephotos.nosort=Nöd sortiere dialog.rearrangephotos.sortbyfilename=per Filename sortiere dialog.rearrangephotos.sortbytime=per Ziit sortiere @@ -354,7 +381,7 @@ dialog.checkversion.releasedate1=Die noii Version isch am dialog.checkversion.releasedate2=ussecho. dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/prune/download.html. dialog.keys.intro=Aastatt d'Muus könnet Sie diese Tastekombinationen nutze -dialog.keys.keylist=
Pfiil TasteKarte verschiebe
Strg + links, rächts PfiilVorherigi oder nöchsti Punkt markiere
Strg + uuf, aba PfiilIi- oder Uusezoome
Strg + Bild uuf, abVorherigi oder nöchsti Segmänt markiere
Strg + Pos1, EndeErschti oder letschti Punkt markiere
EntfAktuelli Punkt lösche
+dialog.keys.keylist=
Pfiil TasteKarte verschiebe
Strg + links, rächts PfiilVorherigi oder nöchsti Punkt markiere
Strg + uuf, aba PfiilIi- oder Uusezoome
Strg + Bild uuf, abVorherigi oder nöchsti Segmänt markiere
Strg + Pos1, ÄndeErschti oder letschti Punkt markiere
EntfAktuelli Punkt lösche
dialog.keys.normalmodifier=Strg dialog.keys.macmodifier=Kommando dialog.saveconfig.desc=Die folgendi Iinstellige könne gspeicheret werde : @@ -375,6 +402,7 @@ dialog.saveconfig.prune.diskcache=Kartenordner dialog.saveconfig.prune.kmzimagewidth=Bildbreiti im KMZ dialog.saveconfig.prune.kmzimageheight=Bildhöchi im KMZ dialog.saveconfig.prune.colourscheme=Farbeschema +dialog.saveconfig.prune.linewidth=Liniedicke dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb dialog.setpaths.intro=Sie könnet dann die Pfade für dia Applikatione setzä: dialog.setpaths.found=Pfad gfunde? @@ -404,10 +432,12 @@ dialog.diskcache.dir=Kartenordner dialog.diskcache.createdir=Ordner kreiere dialog.diskcache.nocreate=Ordner isch nöd kreiert worde dialog.deletefieldvalues.intro=Wählet Sie s Fäld uus zum lösche +dialog.setlinewidth.text=Gäbet Sie die Dicke vonen Linien ii (1-4) +dialog.downloadosm.desc=Best\ätige um rohi OSM Date fürn Gebiet aba zlade: +dialog.searchwikipedianames.search=Sueche na: # 3d window dialog.3d.title=Prune Drüü-d Aasicht -dialog.3d.altitudecap=Minimum Höhenskala dialog.3d.altitudefactor=Höchivervilfachigsfaktor dialog.3dlines.title=Prune Gitterlinie dialog.3dlines.empty=Kei Linie zum aazeigä! @@ -434,16 +464,21 @@ confirm.undo.single=Operation r confirm.undo.multi=Operatione rückgängig gmacht worde. confirm.jpegload.single=Föteli isch glade worde confirm.jpegload.multi=Fötelis sin glade worde -confirm.photo.connect=Föteli verbundä +confirm.media.connect=Media verbundä confirm.photo.disconnect=Föteli gtrännt -confirm.correlate.single=Föteli isch korreliert worde -confirm.correlate.multi=Fötelis sin korreliert worde +confirm.audio.disconnect=Audio gtrännt +confirm.correlatephotos.single=Föteli isch korreliert worde +confirm.correlatephotos.multi=Fötelis sin korreliert worde confirm.createpoint=Punkt kreiert worde confirm.rotatephoto=Föteli umgedräit worde confirm.running=Am Laufe ... confirm.lookupsrtm1=Es sin confirm.lookupsrtm2=Höhenwerte gfunde -confirm.deletefieldvalues=Feldwärte glöscht +confirm.deletefieldvalues=Feldwärte glöscht worde +confirm.audioload=Audiofiles glade worde +confirm.media.removed=entfärnt +confirm.correlateaudios.single=Audiofile isch korreliert worde +confirm.correlateaudios.multi=Audiofiles sin korreliert worde # Buttons button.ok=OK @@ -486,6 +521,7 @@ filetype.kmz=KMZ Dateie filetype.gpx=GPX Dateie filetype.pov=POV Dateie filetype.svg=SVG Dateie +filetype.audio=MP3, OGG, WAV Dateie # Display components display.nodata=Kei Date glade worde @@ -520,12 +556,17 @@ details.range.maxspeed=H details.range.numsegments=Aazahl Segmänte details.range.pace=Tempo details.range.gradient=Gefälle -details.waypointsphotos.waypoints=Waypoints -details.waypointsphotos.photos=Fötelis +details.lists.waypoints=Waypoints +details.lists.photos=Fötelis details.photodetails=Details vonem Föteli details.nophoto=Kei föteli selektiert details.photo.loading=Ladä -details.photo.connected=Verbundä +details.media.connected=Verbundä +details.lists.audio=Audio +details.audiodetails=Audiodetails +details.noaudio=Kei Audiofile selektiert +details.audio.file=Audiofile +details.audio.playing=am abschpielä... map.overzoom=Kei Karte mit diesem Zoom # Field names @@ -562,6 +603,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.ch +wikipedia.lang=als # Cardinals for 3d plots cardinal.n=N @@ -572,9 +614,11 @@ cardinal.w=W # Undo operations undo.load=Date ladä undo.loadphotos=Fötelis ladä +undo.loadaudios=Audiofiles ladä undo.editpoint=Punkt editierä undo.deletepoint=Punkt löschä -undo.deletephoto=Föteli entfärnä +undo.removephoto=Föteli entfärnä +undo.removeaudio=Audiofile entfärnä undo.deleterange=Beriich löschä undo.compress=Track komprimierä undo.insert=Punkte innätuä @@ -584,15 +628,16 @@ undo.addtimeoffset=ziitverschiebig zutue undo.addaltitudeoffset=höchiverschiebig zutue undo.rearrangewaypoints=Waypoints reorganisierä undo.cutandmove=Selektion movä -undo.connectphoto=Föteli verbindä -undo.disconnectphoto=Föteli trännä -undo.correlate=Fötelis korrelierä +undo.connect=verbindä +undo.disconnect=trännä +undo.correlatephotos=Fötelis korrelierä undo.rearrangephotos=Fötelis reorganisierä undo.createpoint=Punkt kreierä undo.rotatephoto=Föteli umadräya undo.convertnamestotimes=Name ins Ziitstämple verwondlä undo.lookupsrtm=Höhendate vonem SRTM holä undo.deletefieldvalues=Feldwärte löschä +undo.correlateaudios=Audios korrelierä # Error messages error.save.dialogtitle=Fähle bim Speichere @@ -612,11 +657,11 @@ error.load.unknownxml=Unbekanntes xml Format: error.load.noxmlinzip=Kei xml im Zip File gfunde error.load.othererror=Fähle bim Läse: error.jpegload.dialogtitle=Fähle bim Lade von Fötelis -error.jpegload.nofilesfound=Kei Dateie gfunde +error.jpegload.nofilesfound=Kei Files gfunde error.jpegload.nojpegsfound=Kei Jpegs gfunde -error.jpegload.noexiffound=Kei EXIF Information gfunde error.jpegload.nogpsfound=Kei GPS Information gfunde error.jpegload.exifreadfailed=EXIF Uufruef isch fehlgschlage. Kei EXIF Infos könnet gläse werde\nohni nen interni oder extärni Bibliothek. +error.audioload.nofilesfound=Kei Audiofiles gfunde error.gpsload.unknown=Unbekannts Fähler error.undofailed.title=Undo isch fehlgschlage worde error.undofailed.text=Operation kann nöd rückgängig gmacht werde @@ -634,3 +679,4 @@ error.lookupsrtm.nonefound=Kei H error.lookupsrtm.nonerequired=Alle Punkte han die Höhendate scho. Nüüt z'tue. error.gpsies.uploadnotok=Der Gpsies Server hät gseit gha error.gpsies.uploadfailed=S Uufalade isch fehlgschlage +error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage diff --git a/tim/prune/lang/prune-texts_en.properties b/tim/prune/lang/prune-texts_en.properties index 42b1d5e..869af6d 100644 --- a/tim/prune/lang/prune-texts_en.properties +++ b/tim/prune/lang/prune-texts_en.properties @@ -30,9 +30,7 @@ menu.point.editpoint=Edit point menu.point.deletepoint=Delete point menu.photo=Photo menu.photo.saveexif=Save to Exif -menu.photo.connect=Connect to point -menu.photo.disconnect=Disconnect from point -menu.photo.delete=Remove photo +menu.audio=Audio menu.view=View menu.view.showsidebars=Show sidebars menu.view.browser=Map in a browser window @@ -49,6 +47,7 @@ menu.map.zoomin=Zoom in menu.map.zoomout=Zoom out menu.map.zoomfull=Zoom to full scale menu.map.newpoint=Create new point +menu.map.drawpoints=Create series of points menu.map.connect=Connect track points menu.map.autopan=Autopan menu.map.showmap=Show map @@ -61,6 +60,7 @@ altkey.menu.track=T altkey.menu.point=P altkey.menu.view=V altkey.menu.photo=O +altkey.menu.audio=A altkey.menu.settings=S altkey.menu.help=H @@ -96,16 +96,29 @@ function.fullrangedetails=Full range details function.getgpsies=Get Gpsies tracks function.uploadgpsies=Upload track to Gpsies function.lookupsrtm=Get altitudes from SRTM +function.getwikipedia=Get nearby Wikipedia articles +function.searchwikipedianames=Search Wikipedia by name +function.downloadosm=Download OSM data for area function.duplicatepoint=Duplicate point +function.connecttopoint=Connect to point +function.disconnectfrompoint=Disconnect from point +function.removephoto=Remove photo function.correlatephotos=Correlate photos function.rearrangephotos=Rearrange photos function.rotatephotoleft=Rotate photo left function.rotatephotoright=Rotate photo right +function.photopopup=Show photo popup function.ignoreexifthumb=Ignore exif thumbnail +function.loadaudio=Add audio files +function.removeaudio=Remove audio file +function.correlateaudios=Correlate audios +function.playaudio=Play audio file +function.stopaudio=Stop audio file function.setmapbg=Set map background function.setkmzimagesize=Set KMZ image size function.setpaths=Set program paths function.setcolours=Set colours +function.setlinewidth=Set line width function.setlanguage=Set language function.help=Help function.showkeys=Show shortcut keys @@ -193,6 +206,7 @@ dialog.pointtype.desc=Save the following point types: dialog.pointtype.track=Track points dialog.pointtype.waypoint=Waypoints dialog.pointtype.photo=Photo points +dialog.pointtype.audio=Audio points dialog.pointtype.selection=Just selection dialog.confirmreversetrack.title=Confirm reversal dialog.confirmreversetrack.text=This track contains timestamp information, which will be out of sequence after a reversal.\nAre you sure you want to reverse this section? @@ -278,13 +292,15 @@ dialog.gpsies.activity.motorbiking=Motorbiking dialog.gpsies.activity.snowshoe=Snowshoeing dialog.gpsies.activity.sailing=Sailing dialog.gpsies.activity.skating=Skating +dialog.wikipedia.column.name=Article name +dialog.wikipedia.column.distance=Distance dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos. dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue? dialog.correlate.photoselect.intro=Select one of these correlated photos to use as the time offset -dialog.correlate.photoselect.photoname=Photo name -dialog.correlate.photoselect.timediff=Time difference -dialog.correlate.photoselect.photolater=Photo later -dialog.correlate.options.tip=Tip: By manually correlating at least one photo, the time offset can be calculated for you. +dialog.correlate.select.photoname=Photo name +dialog.correlate.select.timediff=Time difference +dialog.correlate.select.photolater=Photo later +dialog.correlate.options.tip=Tip: By manually connecting at least one item, the time offset can be calculated for you. dialog.correlate.options.intro=Select the options for automatic correlation dialog.correlate.options.offsetpanel=Time offset dialog.correlate.options.offset=Offset @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=hours, dialog.correlate.options.offset.minutes=minutes and dialog.correlate.options.offset.seconds=seconds dialog.correlate.options.photolater=Photo later than point -dialog.correlate.options.pointlater=Point later than photo +dialog.correlate.options.pointlaterphoto=Point later than photo +dialog.correlate.options.audiolater=Audio later than point +dialog.correlate.options.pointlateraudio=Point later than audio dialog.correlate.options.limitspanel=Correlation limits dialog.correlate.options.notimelimit=No time limit dialog.correlate.options.timelimit=Time limit @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=No distance limit dialog.correlate.options.distancelimit=Distance limit dialog.correlate.options.correlate=Correlate dialog.correlate.alloutsiderange=All photos are outside the time range of the track, so none can be correlated.\nTry changing the offset or manually correlating at least one photo. +dialog.correlate.filetimes=File timestamps denote: +dialog.correlate.filetimes2=of audio clip +dialog.correlate.correltimes=For correlation, use: +dialog.correlate.timestamp.beginning=Beginning +dialog.correlate.timestamp.middle=Middle +dialog.correlate.timestamp.end=End +dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset +dialog.correlate.select.audioname=Audio name +dialog.correlate.select.audiolater=Audio later dialog.rearrangephotos.desc=Select the destination and sort order of the photo points dialog.rearrangephotos.tostart=Move to start dialog.rearrangephotos.toend=Move to end @@ -380,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Map cache dialog.saveconfig.prune.kmzimagewidth=KMZ image width dialog.saveconfig.prune.kmzimageheight=KMZ image height dialog.saveconfig.prune.colourscheme=Colour scheme +dialog.saveconfig.prune.linewidth=Line width dialog.saveconfig.prune.kmltrackcolour=KML track colour dialog.setpaths.intro=If you need to, you can choose the paths to the external applications: dialog.setpaths.found=Path found? @@ -409,16 +437,18 @@ dialog.diskcache.dir=Cache directory dialog.diskcache.createdir=Create directory dialog.diskcache.nocreate=Cache directory not created dialog.deletefieldvalues.intro=Select the field to delete for the current range +dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4) +dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area: +dialog.searchwikipedianames.search=Search for: # 3d window dialog.3d.title=Prune Three-d view -dialog.3d.altitudecap= dialog.3d.altitudefactor=Altitude exaggeration factor dialog.3dlines.title=Prune gridlines dialog.3dlines.empty=No gridlines to display! dialog.3dlines.intro=These are the gridlines for the three-d view -# Confirm messages || These are displayed as confirmation in the status bar +# Confirm messages confirm.loadfile=Data loaded from file confirm.save.ok1=Successfully saved confirm.save.ok2=points to file @@ -439,16 +469,21 @@ confirm.undo.single=operation undone confirm.undo.multi=operations undone confirm.jpegload.single=photo was added confirm.jpegload.multi=photos were added -confirm.photo.connect=photo connected +confirm.media.connect=media connected confirm.photo.disconnect=photo disconnected -confirm.correlate.single=photo was correlated -confirm.correlate.multi=photos were correlated +confirm.audio.disconnect=audio disconnected +confirm.media.removed=removed +confirm.correlatephotos.single=photo was correlated +confirm.correlatephotos.multi=photos were correlated confirm.rotatephoto=photo rotated confirm.createpoint=point created confirm.running=Running ... confirm.lookupsrtm1=Found confirm.lookupsrtm2=altitude values confirm.deletefieldvalues=Field values deleted +confirm.audioload=Audio files added +confirm.correlateaudios.single=audio was correlated +confirm.correlateaudios.multi=audios were correlated # Buttons button.ok=OK @@ -491,6 +526,7 @@ filetype.kmz=KMZ files filetype.gpx=GPX files filetype.pov=POV files filetype.svg=SVG files +filetype.audio=MP3, OGG, WAV files # Display components display.nodata=No data loaded @@ -525,12 +561,17 @@ details.range.maxspeed=Maximum speed details.range.numsegments=Number of segments details.range.pace=Pace details.range.gradient=Gradient -details.waypointsphotos.waypoints=Waypoints -details.waypointsphotos.photos=Photos +details.lists.waypoints=Waypoints +details.lists.photos=Photos +details.lists.audio=Audio details.photodetails=Photo details details.nophoto=No photo selected details.photo.loading=Loading -details.photo.connected=Connected +details.media.connected=Connected +details.audiodetails=Audio details +details.noaudio=No audio file selected +details.audio.file=Audio file +details.audio.playing=playing... map.overzoom=No maps available at this zoom level # Field names @@ -573,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.co.uk +wikipedia.lang=en # Cardinals for 3d plots cardinal.n=N @@ -583,9 +625,11 @@ cardinal.w=W # Undo operations undo.load=load data undo.loadphotos=load photos +undo.loadaudios=load audio files undo.editpoint=edit point undo.deletepoint=delete point -undo.deletephoto=remove photo +undo.removephoto=remove photo +undo.removeaudio=remove audio file undo.deleterange=delete range undo.compress=compress track undo.insert=insert points @@ -595,15 +639,16 @@ undo.addtimeoffset=add time offset undo.addaltitudeoffset=add altitude offset undo.rearrangewaypoints=rearrange waypoints undo.cutandmove=move section -undo.connectphoto=connect photo -undo.disconnectphoto=disconnect photo -undo.correlate=correlate photos +undo.connect=connect +undo.disconnect=disconnect +undo.correlatephotos=correlate photos undo.rearrangephotos=rearrange photos undo.rotatephoto=rotate photo undo.createpoint=create point undo.convertnamestotimes=convert names to times undo.lookupsrtm=lookup altitudes from SRTM undo.deletefieldvalues=delete field values +undo.correlateaudios=correlate audios # Error messages error.save.dialogtitle=Error saving data @@ -625,9 +670,9 @@ error.load.othererror=Error reading file: error.jpegload.dialogtitle=Error loading photos error.jpegload.nofilesfound=No files found error.jpegload.nojpegsfound=No jpeg files found -error.jpegload.noexiffound=No EXIF information found error.jpegload.nogpsfound=No GPS information found error.jpegload.exifreadfailed=Failed to read EXIF information. No EXIF information can be read\nwithout either an internal or external library. +error.audioload.nofilesfound=No audio files found error.gpsload.unknown=Unknown error error.undofailed.title=Undo failed error.undofailed.text=Failed to undo operation @@ -645,3 +690,4 @@ error.lookupsrtm.nonefound=No altitude values available for these points error.lookupsrtm.nonerequired=All points already have altitudes, so there's nothing to lookup error.gpsies.uploadnotok=The gpsies server returned the message error.gpsies.uploadfailed=The upload failed with the error +error.playaudiofailed=Failed to play audio file diff --git a/tim/prune/lang/prune-texts_es.properties b/tim/prune/lang/prune-texts_es.properties index aa76d8f..c554ab3 100644 --- a/tim/prune/lang/prune-texts_es.properties +++ b/tim/prune/lang/prune-texts_es.properties @@ -30,9 +30,7 @@ menu.point.editpoint=Editar punto menu.point.deletepoint=Eliminar punto menu.photo=Foto menu.photo.saveexif=Guardar Exif -menu.photo.connect=Conectar con punto -menu.photo.disconnect=Desconectar de punto -menu.photo.delete=Eliminar foto +menu.audio=Audio menu.view=Ver menu.view.showsidebars=Mostrar barras laterales menu.view.browser=Mapa en una ventana del navegador @@ -49,6 +47,7 @@ menu.map.zoomin=Ampliar zoom menu.map.zoomout=Reducir zoom menu.map.zoomfull=Mostrar todo menu.map.newpoint=Crear un punto nuevo +menu.map.drawpoints=Crear series de puntos menu.map.connect=Conectar puntos de track menu.map.autopan=Posicionar autom\u00e1ticamente menu.map.showmap=Mostrar el mapa @@ -61,6 +60,7 @@ altkey.menu.range=R altkey.menu.point=U altkey.menu.view=V altkey.menu.photo=F +altkey.menu.audio=D altkey.menu.settings=P altkey.menu.help=Y @@ -99,14 +99,27 @@ function.setpaths=Configurar rutas del programas function.getgpsies=Bajar ruta de Gpsies function.uploadgpsies=Subir recorrido a Gpsies function.lookupsrtm=Obtener altitudes de SRTM +function.getwikipedia=Obtener art\u00edculos de Wikipedia cercanos +function.searchwikipedianames=Buscar en Wikipedia por nombre +function.downloadosm=Descargar datos OSM del \u00e1rea function.duplicatepoint=Duplicar punto function.setcolours=Establecer color +function.setlinewidth=Establecer ancho de l\u00ednea function.setlanguage=Establecer lenguaje +function.connecttopoint=Conectar con punto +function.disconnectfrompoint=Desconectar de punto +function.removephoto=Eliminar foto function.correlatephotos=Correlacionar fotos function.rearrangephotos=Reacomodar fotos function.rotatephotoleft=Girar a la izquierda function.rotatephotoright=Girar a la derecha +function.photopopup=Mostrar foto en ventana emergente function.ignoreexifthumb=Ignorar miniatura exif +function.loadaudio=A\u00f1adir archivos de audio +function.removeaudio=Eliminar archivo de audio +function.correlateaudios=Correlacionar audios +function.playaudio=Reproducir archivo de audio +function.stopaudio=Detener reproducci\u00f3n de audio function.help=Ayuda function.showkeys=Mostrar teclas o combinaciones de atajo function.about=Acerca de Prune @@ -116,13 +129,13 @@ function.diskcache=Guardar mapas en disco # Dialogs dialog.exit.confirm.title=Salir de Prune -dialog.exit.confirm.text=¿Los datos han sido modificados. Desea salir de Prune? -dialog.openappend.title=¿Agregar a datos existentes -dialog.openappend.text=¿Agregar estos datos a los datos ya guardados? +dialog.exit.confirm.text=\u00bfLos datos han sido modificados. Desea salir de Prune? +dialog.openappend.title=\u00bfAgregar a datos existentes +dialog.openappend.text=\u00bfAgregar estos datos a los datos ya guardados? dialog.deletepoint.title=Borrar punto -dialog.deletepoint.deletephoto=¿Borrar la foto tambien? +dialog.deletepoint.deletephoto=\u00bfBorrar la foto tambien? dialog.deletephoto.title=Borrar foto -dialog.deletephoto.deletepoint=¿Borrar el punto tambien? +dialog.deletephoto.deletepoint=\u00bfBorrar el punto tambien? dialog.openoptions.title=Opciones de abrir dialog.openoptions.filesnippet=Extraer archivo dialog.load.table.field=Campo @@ -146,7 +159,7 @@ dialog.jpegload.loadjpegswithoutcoords=Fotos sin coordenadas tambien dialog.jpegload.loadjpegsoutsidearea=Incluir fotos fuera del \u00e1rea dialog.jpegload.progress.title=Cargando fotos dialog.jpegload.progress=Por favor espere mientras se buscan las fotos -dialog.gpsload.nogpsbabel=No se ha encontrado el programa gpsbabel. ¿Desea continuar? +dialog.gpsload.nogpsbabel=No se ha encontrado el programa gpsbabel. \u00bfDesea continuar? dialog.gpsload.device=Dispositivo dialog.gpsload.format=Formato dialog.gpsload.getwaypoints=Cargar waypoints @@ -165,7 +178,7 @@ dialog.save.coordinateunits=Unidades de las coordenadas dialog.save.altitudeunits=Unidades de las altitudes dialog.save.timestampformat=Formato del tiempo dialog.save.overwrite.title=El archivo ya existe -dialog.save.overwrite.text=El archivo ya existe, ¿desea sobreescribirlo? +dialog.save.overwrite.text=El archivo ya existe, \u00bfdesea sobreescribirlo? dialog.save.notypesselected=No se han seleccionado tipos de puntos dialog.exportkml.text=Descripci\u00f3n para los datos dialog.exportkml.altitude=Absoluta altitudes (para aviaci\u00f3n) @@ -186,17 +199,19 @@ dialog.exportpov.ballsandsticks=Balas en palos dialog.exportpov.tubesandwalls=Tubos y paredes dialog.exportpov.warningtracksize=Este track contiene un gran numero de puntos. Puede ser que Java3D no los pueda visualizar. Est\u00e1 seguro de que desea continuar? dialog.exportsvg.text=Seleccione los par\u00e1metros para exportar a SVG -dialog.exportsvg.phi=Ángulo de azimuth \u03d5 -dialog.exportsvg.theta=Ángulo de elevaci\u00f3n +dialog.exportsvg.phi=\u00c1ngulo de azimuth \u03d5 +dialog.exportsvg.theta=\u00c1ngulo de elevaci\u00f3n dialog.exportsvg.gradients=Usar degradado para sombras dialog.pointtype.desc=Salvar los siguientes tipos de puntos: dialog.pointtype.track=Puntos de track +dialog.pointtype.waypoint=Waypoints dialog.pointtype.photo=Puntos de foto +dialog.pointtype.audio=Puntos de audio dialog.pointtype.selection=Solo selecci\u00f3n dialog.confirmreversetrack.title=Confirmar inversi\u00f3n -dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. ¿Est\u00e1 seguro que desea invertir esta secci\u00f3n? +dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. \u00bfEst\u00e1 seguro que desea invertir esta secci\u00f3n? dialog.confirmcutandmove.title=Confirmar accion cortar/pegar -dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n¿Esta seguro que desea mover esta secci\u00f3n? +dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n\u00bfEsta seguro que desea mover esta secci\u00f3n? dialog.interpolate.title=Interpolar puntos dialog.interpolate.parameter.text=N\u00famero de los puntos a insertar entre los puntos elegidos dialog.undo.title=Deshacer @@ -204,7 +219,7 @@ dialog.undo.pretext=Por favor, seleccione la operaci\u00f3n(es) a deshacer dialog.undo.none.title=No se puede deshacer dialog.undo.none.text=Ninguna operaci\u00f3n a deshacer dialog.clearundo.title=Despejar la lista de deshacer -dialog.clearundo.text=¿Esta seguro que desea despejar la lista de deshacer?, ¡se perder\u00e1 toda la informaci\u00f3n! +dialog.clearundo.text=\u00bfEsta seguro que desea despejar la lista de deshacer?, ¡se perder\u00e1 toda la informaci\u00f3n! dialog.pointedit.title=Editar punto dialog.pointedit.text=Seleccione cada campo a editar y use el bot\u00f3n 'Editar' para modificar el valor dialog.pointedit.table.field=Campo @@ -227,7 +242,7 @@ dialog.findwaypoint.search=Buscar dialog.saveexif.title=Guardar Exif dialog.saveexif.intro=Seleccione fotos a guardar dialog.saveexif.nothingtosave=Coordenadas no modificadas, nada que guardar -dialog.saveexif.noexiftool=No se encuentra el programa exiftool. ¿Desea continuar? +dialog.saveexif.noexiftool=No se encuentra el programa exiftool. \u00bfDesea continuar? dialog.saveexif.table.photoname=Nombre de la foto dialog.saveexif.table.status=Estado dialog.saveexif.table.save=Guardar @@ -277,12 +292,14 @@ dialog.gpsies.activity.motorbiking=En moto dialog.gpsies.activity.snowshoe=Raquetas de nieve dialog.gpsies.activity.sailing=Vela dialog.gpsies.activity.skating=Patinaje +dialog.wikipedia.column.name=Nombre del art\u00edculo +dialog.wikipedia.column.distance=Distancia dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos. -dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n¿Est\u00e1 seguro de que desea continuar? +dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar? dialog.correlate.photoselect.intro=Seleccione una de estas fotos correlacionadas para usar como margen de tiempo -dialog.correlate.photoselect.photoname=Nombre de la foto -dialog.correlate.photoselect.timediff=Diferencia de tiempo -dialog.correlate.photoselect.photolater=Foto m\u00e1s adelante +dialog.correlate.select.photoname=Nombre de la foto +dialog.correlate.select.timediff=Diferencia de tiempo +dialog.correlate.select.photolater=Foto m\u00e1s adelante dialog.correlate.options.tip=Sugerencia: Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente. dialog.correlate.options.intro=Seleccionar las opciones para correlaci\u00f3n autom\u00e1tica dialog.correlate.options.offsetpanel=Margen de tiempo @@ -291,7 +308,9 @@ dialog.correlate.options.offset.hours=horas, dialog.correlate.options.offset.minutes=minutos y dialog.correlate.options.offset.seconds=segundos dialog.correlate.options.photolater=Foto despu\u00e9s de punto -dialog.correlate.options.pointlater=Punto despu\u00e9s de foto +dialog.correlate.options.pointlaterphoto=Punto despu\u00e9s de foto +dialog.correlate.options.audiolater=Audio despu\u00e9s de punto +dialog.correlate.options.pointlateraudio=Punto despu\u00e9s de audio dialog.correlate.options.limitspanel=L\u00edmites de correlaci\u00f3n dialog.correlate.options.notimelimit=Sin l\u00edmite de tiempo dialog.correlate.options.timelimit=L\u00edmite de tiempo @@ -299,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Sin l\u00edmite de distancia dialog.correlate.options.distancelimit=L\u00edmite de distancia dialog.correlate.options.correlate=Correlacionar dialog.correlate.alloutsiderange=Todas las fotos est\u00e1n fuera del margen horario del track, por lo que ninguna puede ser correlada.\nIntente cambiar el margen o correle manualmente al menos una foto. +dialog.correlate.filetimes=Las marcas del archivo denotan: +dialog.correlate.filetimes2=de sonido +dialog.correlate.correltimes=Para correlacionar use: +dialog.correlate.timestamp.beginning=Comienzo +dialog.correlate.timestamp.middle=Mitad +dialog.correlate.timestamp.end=Final +dialog.correlate.audioselect.intro=Seleccione uno de estos audios correlacionados para usarlo como margen temporal. +dialog.correlate.select.audioname=Nombre del audio +dialog.correlate.select.audiolater=Audio m\u00e1s adelante dialog.rearrangephotos.desc=Seleccionar el destino y sortear el orden de los puntos de las fotos dialog.rearrangephotos.tostart=Mover al comienzo dialog.rearrangephotos.toend=Mover al final @@ -367,7 +395,7 @@ dialog.saveconfig.prune.languagefile=Archivo de lenguaje dialog.saveconfig.prune.gpsdevice=Dispositivo GPS dialog.saveconfig.prune.gpsformat=Formato GPS dialog.saveconfig.prune.povrayfont=Fuente povray -dialog.saveconfig.prune.metricunits=¿Usar unidades m\u00e9tricas? +dialog.saveconfig.prune.metricunits=\u00bfUsar unidades m\u00e9tricas? dialog.saveconfig.prune.gnuplotpath=Ruta a gnuplot dialog.saveconfig.prune.gpsbabelpath=Ruta a gpsbabel dialog.saveconfig.prune.exiftoolpath=Ruta a exiftool @@ -377,12 +405,13 @@ dialog.saveconfig.prune.diskcache=Memoria intermedia de mapas dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en KMZ dialog.saveconfig.prune.colourscheme=Color de esquema +dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas -dialog.setpaths.found=¿Ruta encontrada? +dialog.setpaths.found=\u00bfRuta encontrada? dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes dialog.addaltitude.desc=Desplazamiento de altitud a a\u00f1adir -dialog.lookupsrtm.overwritezeros=¿Sobrescribir valores de altitud nulos? +dialog.lookupsrtm.overwritezeros=\u00bfSobrescribir valores de altitud nulos? dialog.setcolours.intro=Haga clic sobre una placa de color para cambiar el color dialog.setcolours.background=Fondo dialog.setcolours.borders=Bordes @@ -406,10 +435,12 @@ dialog.diskcache.dir=Directorio de mapas dialog.diskcache.createdir=Crear directorio dialog.diskcache.nocreate=No se ha creado el directorio de mapas dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual +dialog.setlinewidth.text=Introduzca la anchura de las l\u00edneas a dibujar para los recorridos (1-4) +dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada. +dialog.searchwikipedianames.search=Buscar: # 3d window dialog.3d.title=Prune vista 3-D -dialog.3d.altitudecap=Escala de las altitudes dialog.3d.altitudefactor=Factor de exageraci\u00f3n de altura dialog.3dlines.title=Cuadr\u00edcula Prune dialog.3dlines.empty=¡No hay ninguna cuadr\u00edcula! @@ -436,16 +467,21 @@ confirm.undo.single=operaci\u00f3n deshecha confirm.undo.multi=operaci\u00f3n(es) deshechas(s) confirm.jpegload.single=Foto incluida confirm.jpegload.multi=Fotos incluidas -confirm.photo.connect=Foto conectada +confirm.media.connect=Medio conectada confirm.photo.disconnect=Foto desconectada -confirm.correlate.single=foto fue correlacionada -confirm.correlate.multi=fotos fueron correlacionadas +confirm.audio.disconnect=Audio desconectado +confirm.media.removed=Eliminado +confirm.correlatephotos.single=foto fue correlacionada +confirm.correlatephotos.multi=fotos fueron correlacionadas confirm.createpoint=punto creado confirm.rotatephoto=foto rotada confirm.running=Trabajando ... confirm.lookupsrtm1=Encontrados confirm.lookupsrtm2=valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM confirm.deletefieldvalues=Valores del campo eliminados +confirm.audioload=A\u00f1adidos archivos de audio +confirm.correlateaudios.single=El audio fue correlacionado +confirm.correlateaudios.multi=Los audios fueron correlacionados # Buttons button.ok=Aceptar @@ -488,6 +524,7 @@ filetype.kmz=Archivos KMZ filetype.gpx=Archivos GPX filetype.pov=Archivos POV filetype.svg=Archivos SVG +filetype.audio=Archivos MP3, OGG, WAV # Display components display.nodata=Ning\u00fan dato cargado @@ -522,12 +559,17 @@ details.range.maxspeed=Velocidad m\u00e1xima details.range.numsegments=N\u00famero de segmentos details.range.pace=Ritmo details.range.gradient=Gradiente -details.waypointsphotos.waypoints=Waypoints -details.waypointsphotos.photos=Fotos +details.lists.waypoints=Waypoints +details.lists.photos=Fotos +details.lists.audio=Audio details.photodetails=Detalles de la foto details.nophoto=Ninguna foto seleccionada details.photo.loading=Cargando -details.photo.connected=Conectada +details.media.connected=Conectada +details.audiodetails=Detalles de audio +details.noaudio=No se ha seleccionado ning\u00fan archivo de audio +details.audio.file=Archivo de audio +details.audio.playing=Reproduciendo... map.overzoom=No existen mapas disponibles con este nivel de enfoque # Field names @@ -570,6 +612,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.es +wikipedia.lang=es # Cardinals for 3d plots cardinal.n=N @@ -580,9 +623,11 @@ cardinal.w=O # Undo operations undo.load=cargar datos undo.loadphotos=cargar fotos +undo.loadaudios=Cargar archivos de audio undo.editpoint=editar punto undo.deletepoint=eliminar punto -undo.deletephoto=eliminar foto +undo.removephoto=eliminar foto +undo.removeaudio=Eliminar archivos de audio undo.deleterange=eliminar rango undo.compress=comprimir track undo.insert=insertar puntos @@ -592,15 +637,16 @@ undo.addtimeoffset=a\u00f1adir margen de tiempo undo.addaltitudeoffset=a\u00f1adir margen de altitud undo.rearrangewaypoints=reordenar waypoints undo.cutandmove=mover secci\u00f3n -undo.connectphoto=conectar foto -undo.disconnectphoto=desconectar foto -undo.correlate=correlacionar fotos +undo.connect=Conectar +undo.disconnect=Desconectar +undo.correlatephotos=correlacionar fotos undo.rearrangephotos=reordenar fotos undo.createpoint=crear punto undo.rotatephoto=girar foto undo.convertnamestotimes=convertir nombres a tiempo undo.lookupsrtm=obtener altitudes de SRTM undo.deletefieldvalues=Eliminar valores de campo +undo.correlateaudios=Correlacionar audios # Error messages error.save.dialogtitle=Fallo al guardar datos @@ -622,9 +668,9 @@ error.load.othererror=Fallo al cargar datos: error.jpegload.dialogtitle=Error cargando fotos error.jpegload.nofilesfound=No se encuentra ning\u00fan archivo error.jpegload.nojpegsfound=No se encuentra ning\u00fan archivo jpeg -error.jpegload.noexiffound=No se encuentra informaci\u00f3n EXIF error.jpegload.nogpsfound=No se encuentra informaci\u00f3n GPS error.jpegload.exifreadfailed=Fallo al leer la informaci\u00f3n EXIF. No se puede leer ninguna informaci\u00f3n EXIF\ncon las librer\u00edas internas ni externas. +error.audioload.nofilesfound=No se encontraron archivos de audio error.gpsload.unknown=Error desconocido error.undofailed.title=Fallo al deshacer error.undofailed.text=No ha sido posible deshacer la operaci\u00f3n @@ -642,3 +688,4 @@ error.lookupsrtm.nonefound=No se encontraron valores de altitud error.lookupsrtm.nonerequired=Todos los puntos tienen altitudes, as\u00ed que no hay nada que buscar. error.gpsies.uploadnotok=El servidor de gpsies ha devuelto el mensaje error.gpsies.uploadfailed=La carga ha fallado con el error +error.playaudiofailed=Fallo reproduciendo archivo de audio diff --git a/tim/prune/lang/prune-texts_fa.properties b/tim/prune/lang/prune-texts_fa.properties index b2aaefe..2e9d558 100644 --- a/tim/prune/lang/prune-texts_fa.properties +++ b/tim/prune/lang/prune-texts_fa.properties @@ -30,9 +30,9 @@ menu.point.editpoint=\u062a\u0646\u0638\u064a\u0645\u0627\u062a \u0646\u0642\u06 menu.point.deletepoint=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0637\u0647 menu.photo=\u0639\u06a9\u0633 menu.photo.saveexif=\u0630\u062e\u064a\u0631\u0647 \u062f\u0631 \u0641\u0627\u064a\u0644 \u0636\u0645\u064a\u0645\u0647 \u0639\u06a9\u0633 -menu.photo.connect=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647 -menu.photo.disconnect=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647 -menu.photo.delete=\u0628\u0631\u062f\u0627\u0634\u062a\u0646 \u0639\u06a9\u0633 +function.connecttopoint=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647 +function.disconnectfrompoint=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647 +function.removephoto=\u0628\u0631\u062f\u0627\u0634\u062a\u0646 \u0639\u06a9\u0633 menu.view=\u062f\u064a\u062f menu.view.browser=\u0646\u0642\u0634\u0647 \u062f\u0631\u062c\u0633\u062a\u062c\u0648\u06af\u0631 menu.view.browser.google=Google Maps diff --git a/tim/prune/lang/prune-texts_fr.properties b/tim/prune/lang/prune-texts_fr.properties index e608c9d..8c7f1b3 100644 --- a/tim/prune/lang/prune-texts_fr.properties +++ b/tim/prune/lang/prune-texts_fr.properties @@ -30,9 +30,9 @@ menu.point.editpoint=Editer le point menu.point.deletepoint=Supprimer le point menu.photo=Photo menu.photo.saveexif=Enregistrer dans les Exif -menu.photo.connect=Relier au point -menu.photo.disconnect=D\u00e9tacher du point -menu.photo.delete=Retirer la photo +function.connecttopoint=Relier au point +function.disconnectfrompoint=D\u00e9tacher du point +function.removephoto=Retirer la photo menu.view=Affichage menu.view.browser=Ouvrir la carte dans le navigateur menu.view.browser.google=Google maps @@ -102,6 +102,7 @@ function.correlatephotos=Corr\u00e9ler les photos function.rearrangephotos=R\u00e9arranger les photos function.rotatephotoleft=Tourner la photo vers la gauche function.rotatephotoright=Tourner la photo vers la droite +function.photopopup=Montrer la photo function.ignoreexifthumb=Ignorer l\u2019aper\u00e7u Exif function.help=Aide function.showkeys=Montrer les raccourcis clavier @@ -266,9 +267,9 @@ dialog.gpsies.activity.skating=Skating dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler. dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ? dialog.correlate.photoselect.intro=S\u00e9lectionner une de ces photos corr\u00e9l\u00e9es pour d\u00e9finir le d\u00e9calage de temps -dialog.correlate.photoselect.photoname=Nom de la photo -dialog.correlate.photoselect.timediff=Diff\u00e9rence de temps -dialog.correlate.photoselect.photolater=Photo prise plus tard +dialog.correlate.select.photoname=Nom de la photo +dialog.correlate.select.timediff=Diff\u00e9rence de temps +dialog.correlate.select.photolater=Photo prise plus tard dialog.correlate.options.tip=Astuce : En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous. dialog.correlate.options.intro=S\u00e9lectionner les options pour la corr\u00e9lation automatique dialog.correlate.options.offsetpanel=D\u00e9calage de temps @@ -277,7 +278,7 @@ dialog.correlate.options.offset.hours=heures, dialog.correlate.options.offset.minutes=minutes et dialog.correlate.options.offset.seconds=secondes dialog.correlate.options.photolater=Photo post\u00e9rieure au point -dialog.correlate.options.pointlater=Point post\u00e9rieur \u00e0 la photo +dialog.correlate.options.pointlaterphoto=Point post\u00e9rieur \u00e0 la photo dialog.correlate.options.limitspanel=Limites de corr\u00e9lation dialog.correlate.options.notimelimit=Pas de limite de temps dialog.correlate.options.timelimit=Limite de temps @@ -395,7 +396,6 @@ dialog.diskcache.nocreate=Le r\u00e9pertoire cache n'est pas cr\u00e9\u00e9 # 3d window dialog.3d.title=Vue 3D de Prune -dialog.3d.altitudecap=Etendue d'altitude minimale dialog.3dlines.title=Grille de Prune dialog.3dlines.empty=Pas de grille \u00e0 afficher ! dialog.3dlines.intro=Ceci est la grille pour la vue 3D @@ -421,17 +421,17 @@ confirm.undo.single=op\u00e9ration annul\u00e9e confirm.undo.multi=op\u00e9rations annul\u00e9es confirm.jpegload.single=la photo a \u00e9t\u00e9 ajout\u00e9e confirm.jpegload.multi=les photos ont \u00e9t\u00e9 ajout\u00e9es -confirm.photo.connect=photo reli\u00e9e +confirm.media.connect=m\u00e9dia reli\u00e9e confirm.photo.disconnect=photo d\u00e9tach\u00e9e -confirm.correlate.single=photo a \u00e9t\u00e9 corr\u00e9l\u00e9e -confirm.correlate.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es +confirm.correlatephotos.single=photo a \u00e9t\u00e9 corr\u00e9l\u00e9e +confirm.correlatephotos.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es confirm.createpoint=Point cr\u00e9\u00e9 confirm.rotatephoto=Photo tourn\u00e9e confirm.running=En cours... confirm.lookupsrtm1=Trouv\u00e9 confirm.lookupsrtm2=valeurs d'altitude -# Buttons || These are all the texts for buttons +# Buttons button.ok=OK button.back=Retour button.next=Prochain @@ -505,12 +505,12 @@ details.range.maxspeed=Vitesse maximum details.range.numsegments=Nombre de segments details.range.pace=Allure details.range.gradient=Pente -details.waypointsphotos.waypoints=Waypoints -details.waypointsphotos.photos=Photos +details.lists.waypoints=Waypoints +details.lists.photos=Photos details.photodetails=D\u00e9tails de la photo details.nophoto=Pas de photo details.photo.loading=Chargement -details.photo.connected=Reli\u00e9e +details.media.connected=Reli\u00e9e map.overzoom=Aucune carte disponible \u00e0 ce niveau de zoom # Field names @@ -553,6 +553,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.fr +wikipedia.lang=fr # Cardinals for 3d plots cardinal.n=N @@ -565,7 +566,7 @@ undo.load=charger les donn\u00e9es undo.loadphotos=charger les photos undo.editpoint=\u00e9diter le point undo.deletepoint=effacer le point -undo.deletephoto=retirer la photo +undo.removephoto=retirer la photo undo.deleterange=effacer l'\u00e9tendue undo.compress=compresser la trace undo.insert=ins\u00e9rer les points @@ -575,9 +576,9 @@ undo.addtimeoffset=ajouter d\u00e9calage d'heure undo.addaltitudeoffset=ajouter d\u00e9calage d'altitude undo.rearrangewaypoints=r\u00e9arranger les waypoints undo.cutandmove=d\u00e9placer la s\u00e9lection -undo.connectphoto=relier la photo -undo.disconnectphoto=d\u00e9tacher la photo -undo.correlate=corr\u00e9ler les photos +undo.connect=relier +undo.disconnect=d\u00e9tacher +undo.correlatephotos=corr\u00e9ler les photos undo.rearrangephotos=R\u00e9arranger les photos undo.createpoint=ajouter un point undo.rotatephoto=Tourner la photo @@ -604,7 +605,6 @@ error.load.othererror=Erreur \u00e0 la lecture du fichier : error.jpegload.dialogtitle=Erreur au chargement des photos error.jpegload.nofilesfound=Aucun fichier trouv\u00e9 error.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9 -error.jpegload.noexiffound=Aucune information EXIF trouv\u00e9e error.jpegload.nogpsfound=Aucune information GPS trouv\u00e9e error.jpegload.exifreadfailed=Information EXIF illisible. Aucune information EXIF ne peut \u00eatre lue\nsans une librairie interne ou externe. error.gpsload.unknown=Erreur inconnue diff --git a/tim/prune/lang/prune-texts_hu.properties b/tim/prune/lang/prune-texts_hu.properties new file mode 100644 index 0000000..29840dd --- /dev/null +++ b/tim/prune/lang/prune-texts_hu.properties @@ -0,0 +1,693 @@ +# Text entries for the Prune application +# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3 + +# Menu entries +menu.file=F\u00e1jl +menu.file.addphotos=F\u00e9nyk\u00e9pek hozz\u00e1ad\u00e1sa +menu.file.save=Ment\u00e9s sz\u00f6vegk\u00e9nt +menu.file.exit=Kil\u00e9p\u00e9s +menu.track=Nyomvonal +menu.track.undo=Visszavon\u00e1s +menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se +menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se +menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se +menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re +menu.track.rearrange.end=\u00d6sszes a f\u00e1jl v\u00e9g\u00e9re +menu.track.rearrange.nearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz +menu.range=Tartom\u00e1ny +menu.range.all=Mindet kijel\u00f6l +menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se +menu.range.start=Tartom\u00e1ny kezdet\u00e9nek be\u00e1ll\u00edt\u00e1sa +menu.range.end=Tartom\u00e1ny v\u00e9g\u00e9nek be\u00e1ll\u00edt\u00e1sa +menu.range.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se +menu.range.interpolate=Interpol\u00e1ci\u00f3 +menu.range.average=Kijel\u00f6l\u00e9s \u00e1tlaga +menu.range.reverse=Tartom\u00e1ny megford\u00edt\u00e1sa +menu.range.mergetracksegments=Nyomvonalszakaszok egyes\u00edt\u00e9se +menu.range.cutandmove=Kijel\u00f6l\u00e9s kiv\u00e1g\u00e1sa \u00e9s mozgat\u00e1sa +menu.point=Pont +menu.point.editpoint=Pont szerkeszt\u00e9se +menu.point.deletepoint=Pont t\u00f6rl\u00e9se +menu.photo=F\u00e9nyk\u00e9p +menu.photo.saveexif=Ment\u00e9s Exifbe +menu.audio=Hang +menu.view=N\u00e9zet +menu.view.showsidebars=Oldals\u00e1vok megjelen\u00edt\u00e9se +menu.view.browser=T\u00e9rk\u00e9p b\u00f6ng\u00e9sz\u0151ablakban +menu.view.browser.google=Google T\u00e9rk\u00e9p +menu.view.browser.openstreetmap=OpenStreetMap +menu.view.browser.mapquest=Mapquest +menu.view.browser.yahoo=Yahoo! Maps +menu.view.browser.bing=Bing Maps +menu.settings=Be\u00e1ll\u00edt\u00e1sok +menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se az internetr\u0151l +menu.help=S\u00fag\u00f3 +# Popup menu for map +menu.map.zoomin=Nagy\u00edt\u00e1s +menu.map.zoomout=Kicsiny\u00edt\u00e9s +menu.map.zoomfull=Nagy\u00edt\u00e1s a teljes m\u00e9retre +menu.map.newpoint=\u00daj pont l\u00e9trehoz\u00e1sa +menu.map.drawpoints=Pontsorozat l\u00e9trehoz\u00e1sa +menu.map.connect=Nyompontok \u00f6sszek\u00f6t\u00e9se +menu.map.autopan=Automatikus mozgat\u00e1s +menu.map.showmap=T\u00e9rk\u00e9p megjelen\u00edt\u00e9se +menu.map.showscalebar=M\u00e9retar\u00e1ny megjelen\u00edt\u00e9se + +# Alt keys for menus +altkey.menu.file=F +altkey.menu.track=V +altkey.menu.range=T +altkey.menu.point=P +altkey.menu.view=N +altkey.menu.photo=K +altkey.menu.audio=H +altkey.menu.settings=B +altkey.menu.help=S + +# Ctrl shortcuts for menu items +shortcut.menu.file.open=O +shortcut.menu.file.load=L +shortcut.menu.file.save=S +shortcut.menu.track.undo=Z +shortcut.menu.edit.compress=C +shortcut.menu.range.all=A +shortcut.menu.help.help=H + +# Functions +function.open=F\u00e1jl megnyit\u00e1sa +function.loadfromgps=Adatok let\u00f6lt\u00e9se GPS-r\u0151l +function.sendtogps=Adatok felt\u00f6lt\u00e9se GPS-re +function.exportkml=Export\u00e1l\u00e1s KML-be +function.exportgpx=Export\u00e1l\u00e1s GPX-be +function.exportpov=Export\u00e1l\u00e1s POV-ba +function.exportsvg=Export\u00e1l\u00e1s SVG-be +function.editwaypointname=\u00datpont nev\u00e9nek szerkeszt\u00e9se +function.compress=Nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se +function.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa +function.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa +function.convertnamestotimes=\u00datpontok neveinek konvert\u00e1l\u00e1sa id\u0151pontokk\u00e1 +function.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9keinek t\u00f6rl\u00e9se +function.findwaypoint=\u00datpont keres\u00e9se +function.pastecoordinates=\u00daj koordin\u00e1t\u00e1k megad\u00e1sa +function.charts=Diagramok +function.show3d=3D n\u00e9zet +function.distances=T\u00e1vols\u00e1gok +function.fullrangedetails=Teljes tartom\u00e1ny r\u00e9szletei +function.setmapbg=H\u00e1tt\u00e9rk\u00e9p be\u00e1ll\u00edt\u00e1sa +function.setkmzimagesize=KMZ k\u00e9pm\u00e9ret be\u00e1ll\u00edt\u00e1sa +function.setpaths=Program\u00fatvonalak be\u00e1ll\u00edt\u00e1sa +function.getgpsies=Gpsies nyomvonalak let\u00f6lt\u00e9se +function.uploadgpsies=Nyomvonal felt\u00f6lt\u00e9se Gpsiesra +function.lookupsrtm=Magass\u00e1gok let\u00f6lt\u00e9se SRTM-r\u0151l +function.getwikipedia=K\u00f6zeli Wikip\u00e9dia sz\u00f3cikkek let\u00f6lt\u00e9se +function.searchwikipedianames=Keres\u00e9s a Wikip\u00e9di\u00e1ban n\u00e9v szerint +function.downloadosm=OSM adatok let\u00f6lt\u00e9se a ter\u00fcletr\u0151l +function.duplicatepoint=Pont kett\u0151z\u00e9se +function.setcolours=Sz\u00ednek be\u00e1ll\u00edt\u00e1sa +function.setlinewidth=Vonalsz\u00e9less\u00e9g be\u00e1ll\u00edt\u00e1sa +function.setlanguage=Nyelv be\u00e1ll\u00edt\u00e1sa +function.connecttopoint=Kapcsol\u00e1s ponthoz +function.disconnectfrompoint=Lev\u00e1laszt\u00e1s pontr\u00f3l +function.removephoto=F\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa +function.correlatephotos=F\u00e9nyk\u00e9pek megfeleltet\u00e9se +function.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendez\u00e9se +function.rotatephotoleft=F\u00e9nyk\u00e9p forgat\u00e1sa balra +function.rotatephotoright=F\u00e9nyk\u00e9p forgat\u00e1sa jobbra +function.photopopup=F\u00e9nyk\u00e9p felugr\u00f3 ablak megjelen\u00edt\u00e9se +function.ignoreexifthumb=Exif miniat\u0171r figyelmen k\u00edv\u00fcl hagy\u00e1sa +function.loadaudio=Hangf\u00e1jlok hozz\u00e1ad\u00e1sa +function.removeaudio=Hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa +function.correlateaudios=Hangok megfeleltet\u00e9se +function.playaudio=Hangf\u00e1jl lej\u00e1tsz\u00e1sa +function.stopaudio=Hangf\u00e1jl meg\u00e1ll\u00edt\u00e1sa +function.help=S\u00fag\u00f3 +function.showkeys=Gyorsbillenty\u0171k megjelen\u00edt\u00e9se +function.about=A Prune n\u00e9vjegye +function.checkversion=\u00daj verzi\u00f3 keres\u00e9se +function.saveconfig=Be\u00e1ll\u00edt\u00e1sok ment\u00e9se +function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre + +# Dialogs +dialog.exit.confirm.title=Kil\u00e9p\u00e9s a Prune-b\u00f3l +dialog.exit.confirm.text=Az adatok nincsenek elmentve. Biztos benne, hogy kil\u00e9p? +dialog.openappend.title=Hozz\u00e1f\u0171z\u00e9s a megl\u00e9v\u0151 adatokhoz +dialog.openappend.text=Hozz\u00e1f\u0171zi ezeket az adatokat a m\u00e1r bet\u00f6lt\u00f6tt adatokhoz? +dialog.deletepoint.title=Pont t\u00f6rl\u00e9se +dialog.deletepoint.deletephoto=T\u00f6rli a f\u00e9nyk\u00e9pet, amely ehhez a ponthoz tartozik? +dialog.deletephoto.title=F\u00e9nyk\u00e9p t\u00f6rl\u00e9se +dialog.deletephoto.deletepoint=T\u00f6rli a pontot, amely ehhez a f\u00e9nyk\u00e9phez tartozik? +dialog.openoptions.title=Be\u00e1ll\u00edt\u00e1sok megnyit\u00e1sa +dialog.openoptions.filesnippet=F\u00e1jl kivonata +dialog.load.table.field=Mez\u0151 +dialog.load.table.datatype=Adatt\u00edpus +dialog.load.table.description=Le\u00edr\u00e1s +dialog.delimiter.label=Mez\u0151elv\u00e1laszt\u00f3 +dialog.delimiter.comma=Vessz\u0151 , +dialog.delimiter.tab=Tabul\u00e1tor +dialog.delimiter.space=Sz\u00f3k\u00f6z +dialog.delimiter.semicolon=Pontosvessz\u0151 ; +dialog.delimiter.other=Egy\u00e9b +dialog.openoptions.deliminfo.records=rekord +dialog.openoptions.deliminfo.fields=mez\u0151vel +dialog.openoptions.deliminfo.norecords=Nincsenek rekordok +dialog.openoptions.altitudeunits=Magass\u00e1g egys\u00e9ge +dialog.open.contentsdoubled=Ez a f\u00e1jl minden egyes pont k\u00e9t p\u00e9ld\u00e1ny\u00e1t tartalmazza,\negyszer mint \u00fatpont, m\u00e1sodszor mint nyompont. +dialog.selecttracks.intro=Nyomvonal vagy nyomvonalak kiv\u00e1laszt\u00e1sa bet\u00f6lt\u00e9shez +dialog.selecttracks.noname=N\u00e9vtelen +dialog.jpegload.subdirectories=Alk\u00f6nyvt\u00e1rakban is +dialog.jpegload.loadjpegswithoutcoords=Koordin\u00e1t\u00e1k n\u00e9lk\u00fcli k\u00e9peket is +dialog.jpegload.loadjpegsoutsidearea=A jelenlegi ter\u00fcleten k\u00edv\u00fcli k\u00e9peket is +dialog.jpegload.progress.title=F\u00e9nyk\u00e9pek bet\u00f6lt\u00e9se +dialog.jpegload.progress=K\u00e9rem, v\u00e1rjon, am\u00edg a f\u00e9nyk\u00e9pek keres\u00e9se tart +dialog.gpsload.nogpsbabel=A gpsbabel program nem tal\u00e1lhat\u00f3. Folytatja? +dialog.gpsload.device=Eszk\u00f6z neve +dialog.gpsload.format=Form\u00e1tum +dialog.gpsload.getwaypoints=\u00datpontok bet\u00f6lt\u00e9se +dialog.gpsload.gettracks=Nyomvonalak bet\u00f6lt\u00e9se +dialog.gpsload.save=Ment\u00e9s f\u00e1jlba +dialog.gpssend.sendwaypoints=\u00datpontok k\u00fcld\u00e9se +dialog.gpssend.sendtracks=Nyomvonalak k\u00fcld\u00e9se +dialog.gpssend.trackname=Nyomvonal neve +dialog.saveoptions.title=F\u00e1jl ment\u00e9se +dialog.save.fieldstosave=Mentend\u0151 mez\u0151k +dialog.save.table.field=Mez\u0151 +dialog.save.table.hasdata=Tartalmaz adatot +dialog.save.table.save=Ment\u00e9s +dialog.save.headerrow=Fejl\u00e9csor a kimenetbe +dialog.save.coordinateunits=Koordin\u00e1ta egys\u00e9ge +dialog.save.altitudeunits=Magass\u00e1g egys\u00e9ge +dialog.save.timestampformat=Id\u0151b\u00e9lyeg form\u00e1tuma +dialog.save.overwrite.title=A f\u00e1jl m\u00e1r l\u00e9tezik +dialog.save.overwrite.text=Ez a f\u00e1jl m\u00e1r l\u00e9tezik. Biztos benne, hogy fel\u00fcl\u00edrja a f\u00e1jlt? +dialog.save.notypesselected=Nincs pontt\u00edpus kiv\u00e1lasztva +dialog.exportkml.text=C\u00edm az adatokhoz +dialog.exportkml.altitude=Abszol\u00fat magass\u00e1gok (rep\u00fcl\u00e9shez) +dialog.exportkml.kmz=T\u00f6m\u00f6r\u00edt\u00e9s kmz f\u00e1jl k\u00e9sz\u00edt\u00e9s\u00e9hez +dialog.exportkml.exportimages=K\u00e9pminiat\u0171r\u00f6k export\u00e1l\u00e1sa kmz-be +dialog.exportkml.trackcolour=Nyomvonal sz\u00edne +dialog.exportgpx.name=N\u00e9v +dialog.exportgpx.desc=Le\u00edr\u00e1s +dialog.exportgpx.includetimestamps=Id\u0151b\u00e9lyegek is +dialog.exportgpx.copysource=Forr\u00e1s xml m\u00e1sol\u00e1sa +dialog.exportpov.text=Adja meg a param\u00e9tereket a POV exporthoz +dialog.exportpov.font=Bet\u0171t\u00edpus +dialog.exportpov.camerax=X kamera +dialog.exportpov.cameray=Y kamera +dialog.exportpov.cameraz=Z kamera +dialog.exportpov.modelstyle=Modell st\u00edlusa +dialog.exportpov.ballsandsticks=Goly\u00f3k \u00e9s botok +dialog.exportpov.tubesandwalls=Cs\u00f6vek \u00e9s falak +dialog.exportpov.warningtracksize=Ez a nyomvonal nagy sz\u00e1m\u00fa pontot tartalmaz, amelyet a Java3D nem biztos, hogy meg tud jelen\u00edteni.\nBiztos benne, hogy folytatni szeretn\u00e9? +dialog.exportsvg.text=Param\u00e9terek kiv\u00e1laszt\u00e1sa az SVG exporthoz +dialog.exportsvg.phi=Ir\u00e1nysz\u00f6g \u03d5 +dialog.exportsvg.theta=Emel\u00e9s sz\u00f6ge \u03b8 +dialog.exportsvg.gradients=\u00c1tmenetek haszn\u00e1lata az \u00e1rny\u00e9kol\u00e1shoz +dialog.pointtype.desc=A k\u00f6vetkez\u0151 pontt\u00edpusok ment\u00e9se: +dialog.pointtype.track=Nyompontok +dialog.pointtype.waypoint=\u00datpontok +dialog.pointtype.photo=F\u00e9nyk\u00e9ppontok +dialog.pointtype.audio=Hangpontok +dialog.pointtype.selection=Csak a kijel\u00f6lt +dialog.confirmreversetrack.title=Megford\u00edt\u00e1s meger\u0151s\u00edt\u00e9se +dialog.confirmreversetrack.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje megford\u00edt\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy megford\u00edtja a kijel\u00f6l\u00e9st? +dialog.confirmcutandmove.title=Kiv\u00e1g\u00e1s \u00e9s mozgat\u00e1s meger\u0151s\u00edt\u00e9se +dialog.confirmcutandmove.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje mozgat\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy mozgatja a kijel\u00f6l\u00e9st? +dialog.interpolate.title=Pontok interpol\u00e1l\u00e1sa +dialog.interpolate.parameter.text=Pontok sz\u00e1ma, amely a k\u00e9t kiv\u00e1lasztott pont k\u00f6z\u00e9 besz\u00farand\u00f3 +dialog.undo.title=M\u0171velet(ek) visszavon\u00e1sa +dialog.undo.pretext=V\u00e1lassza ki a visszavonand\u00f3 m\u0171velet(ek)et +dialog.undo.none.title=Nem vonhat\u00f3 vissza +dialog.undo.none.text=Nincs visszavonhat\u00f3 m\u0171velet! +dialog.clearundo.title=Visszavon\u00e1si lista t\u00f6rl\u00e9se +dialog.clearundo.text=Biztos benne, hogy t\u00f6r\u00f6lni szeretn\u00e9 a visszavon\u00e1si list\u00e1t?\nMinden visszavon\u00e1si inform\u00e1ci\u00f3 el fog veszni! +dialog.pointedit.title=Pont szerkeszt\u00e9se +dialog.pointedit.text=V\u00e1lassza ki egyenk\u00e9nt a mez\u0151ket, amelyeket szerkeszteni szeretne, majd az \u00e9rt\u00e9k m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a "Szerkeszt\u00e9s" gombot +dialog.pointedit.table.field=Mez\u0151 +dialog.pointedit.table.value=\u00c9rt\u00e9k +dialog.pointedit.table.changed=M\u00f3dosult +dialog.pointedit.changevalue.text=Adjon meg egy \u00faj \u00e9rt\u00e9ket a mez\u0151h\u00f6z +dialog.pointedit.changevalue.title=Mez\u0151 szerkeszt\u00e9se +dialog.pointnameedit.name=\u00datpont neve +dialog.pointnameedit.uppercase=NAGYBET\u0170S +dialog.pointnameedit.lowercase=kisbet\u0171s +dialog.pointnameedit.sentencecase=Nagy Kezd\u0151bet\u0171s +dialog.addtimeoffset.add=Id\u0151 hozz\u00e1ad\u00e1sa +dialog.addtimeoffset.subtract=Id\u0151 kivon\u00e1sa +dialog.addtimeoffset.days=Nap +dialog.addtimeoffset.hours=\u00d3ra +dialog.addtimeoffset.minutes=Perc +dialog.addtimeoffset.notimestamps=Nem adhat\u00f3 hozz\u00e1 id\u0151eltol\u00e1s, mivel a kijel\u00f6l\u00e9s nem tartalmaz id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t +dialog.findwaypoint.intro=Adja meg az \u00fatpont nev\u00e9nek egy r\u00e9sz\u00e9t +dialog.findwaypoint.search=Keres\u00e9s +dialog.saveexif.title=Exif ment\u00e9se +dialog.saveexif.intro=V\u00e1lassza ki a mentend\u0151 f\u00e9nyk\u00e9peket a jel\u00f6l\u0151n\u00e9gyzetek haszn\u00e1lat\u00e1val +dialog.saveexif.nothingtosave=A koordin\u00e1taadatok nem m\u00f3dosultak, nincs mit menteni +dialog.saveexif.noexiftool=Az exiftool program nem tal\u00e1lhat\u00f3. Folytatja? +dialog.saveexif.table.photoname=F\u00e9nyk\u00e9p neve +dialog.saveexif.table.status=\u00c1llapot +dialog.saveexif.table.save=Ment\u00e9s +dialog.saveexif.photostatus.connected=\u00d6sszekapcsolva +dialog.saveexif.photostatus.disconnected=Lev\u00e1lasztva +dialog.saveexif.photostatus.modified=M\u00f3dos\u00edtva +dialog.saveexif.overwrite=F\u00e1jlok fel\u00fcl\u00edr\u00e1sa +dialog.saveexif.force=K\u00e9nyszer\u00edt\u00e9s kisebb hib\u00e1k ellen\u00e9re +dialog.charts.xaxis=X tengely +dialog.charts.yaxis=Y tengely +dialog.charts.output=Kimenet +dialog.charts.screen=Kimenet k\u00e9perny\u0151re +dialog.charts.svg=Kimenet SVG f\u00e1jlba +dialog.charts.svgwidth=SVG sz\u00e9less\u00e9ge +dialog.charts.svgheight=SVG magass\u00e1ga +dialog.charts.needaltitudeortimes=Diagramok k\u00e9sz\u00edt\u00e9s\u00e9hez a nyomvonalnak tartalmaznia kell magass\u00e1gi vagy id\u0151inform\u00e1ci\u00f3kat +dialog.charts.gnuplotnotfound=A gnuplot a megadott \u00fatvonalon nem tal\u00e1lhat\u00f3 +dialog.distances.intro=T\u00e1vols\u00e1gok l\u00e9gvonalban a pontok k\u00f6z\u00f6tt +dialog.distances.column.from=Indul\u00f3 pont +dialog.distances.column.to=V\u00e9gpont +dialog.distances.currentpoint=Jelenlegi pont +dialog.distances.toofewpoints=Ehhez a funkci\u00f3hoz \u00fatpontok kellenek, amelyek k\u00f6z\u00f6tt a t\u00e1vols\u00e1g sz\u00e1m\u00edt\u00e1sra ker\u00fcl +dialog.fullrangedetails.intro=Itt vannak a r\u00e9szletei a kiv\u00e1lasztott tartom\u00e1nynak +dialog.setmapbg.intro=V\u00e1lassza ki az egyik t\u00e9rk\u00e9pforr\u00e1st, vagy adjon hozz\u00e1 egy \u00fajat +dialog.addmapsource.title=\u00daj t\u00e9rk\u00e9pforr\u00e1s hozz\u00e1ad\u00e1sa +dialog.addmapsource.sourcename=Forr\u00e1s neve +dialog.addmapsource.layer1url=Els\u0151 r\u00e9teg URL-je +dialog.addmapsource.layer2url=Opcion\u00e1lis m\u00e1sodik r\u00e9teg URL-je +dialog.addmapsource.maxzoom=Maxim\u00e1lis nagy\u00edt\u00e1si szint +dialog.addmapsource.cloudstyle=St\u00edlus sz\u00e1ma +dialog.addmapsource.noname=N\u00e9vtelen +dialog.gpsies.column.name=Nyomvonal neve +dialog.gpsies.column.length=Hossz +dialog.gpsies.description=Le\u00edr\u00e1s +dialog.gpsies.nodescription=Nincs le\u00edr\u00e1s +dialog.gpsies.nonefound=Nem tal\u00e1lhat\u00f3 nyomvonal +dialog.gpsies.username=Gpsies felhaszn\u00e1l\u00f3n\u00e9v +dialog.gpsies.password=Gpsies jelsz\u00f3 +dialog.gpsies.keepprivate=A nyomvonal maradjon priv\u00e1t +dialog.gpsies.confirmopenpage=Megnyitja a weboldalt a felt\u00f6lt\u00f6tt nyomvonal sz\u00e1m\u00e1ra? +dialog.gpsies.activities=Tev\u00e9kenys\u00e9gt\u00edpusok +dialog.gpsies.activity.trekking=T\u00far\u00e1z\u00e1s +dialog.gpsies.activity.walking=S\u00e9ta +dialog.gpsies.activity.jogging=Fut\u00e1s +dialog.gpsies.activity.biking=Ker\u00e9kp\u00e1roz\u00e1s +dialog.gpsies.activity.motorbiking=Motorker\u00e9kp\u00e1roz\u00e1s +dialog.gpsies.activity.snowshoe=H\u00f3talpas s\u00e9ta +dialog.gpsies.activity.sailing=Vitorl\u00e1z\u00e1s +dialog.gpsies.activity.skating=Korcsoly\u00e1z\u00e1s +dialog.wikipedia.column.name=Sz\u00f3cikk neve +dialog.wikipedia.column.distance=T\u00e1vols\u00e1g +dialog.correlate.notimestamps=Nincsenek id\u0151b\u00e9lyegek az adatpontokon, \u00edgy nem feleltethet\u0151 meg semmi a f\u00e9nyk\u00e9pekkel. +dialog.correlate.nouncorrelatedphotos=Nincsenek megfeleltetlen f\u00e9nyk\u00e9pek.\nBiztos benne, hogy folytatja? +dialog.correlate.photoselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a megfeleltetett f\u00e9nyk\u00e9pek k\u00f6z\u00fcl az id\u0151eltol\u00e1s haszn\u00e1lat\u00e1hoz +dialog.correlate.select.photoname=F\u00e9nyk\u00e9p neve +dialog.correlate.select.timediff=Id\u0151k\u00fcl\u00f6nbs\u00e9g +dialog.correlate.select.photolater=K\u00e9s\u0151bbi f\u00e9nyk\u00e9p +dialog.correlate.options.tip=Tipp: legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3. +dialog.correlate.options.intro=V\u00e1lassza ki az opci\u00f3kat az automatikus megfeleltet\u00e9shez +dialog.correlate.options.offsetpanel=Id\u0151eltol\u00e1s +dialog.correlate.options.offset=Eltol\u00e1s +dialog.correlate.options.offset.hours=\u00f3ra, +dialog.correlate.options.offset.minutes=perc \u00e9s +dialog.correlate.options.offset.seconds=m\u00e1sodperc +dialog.correlate.options.photolater=A f\u00e9nyk\u00e9p k\u00e9s\u0151bbi, mint a pont +dialog.correlate.options.pointlaterphoto=A pont k\u00e9s\u0151bbi, mint a f\u00e9nyk\u00e9p +dialog.correlate.options.audiolater=A hang k\u00e9s\u0151bbi, mint a pont +dialog.correlate.options.pointlateraudio=A pont k\u00e9s\u0151bbi, mint a hang +dialog.correlate.options.limitspanel=Megfeleltet\u00e9s korl\u00e1tai +dialog.correlate.options.notimelimit=Nincs id\u0151korl\u00e1t +dialog.correlate.options.timelimit=Id\u0151korl\u00e1t +dialog.correlate.options.nodistancelimit=Nincs t\u00e1vols\u00e1gkorl\u00e1t +dialog.correlate.options.distancelimit=T\u00e1vols\u00e1gkorl\u00e1t +dialog.correlate.options.correlate=Megfeleltet\u00e9s +dialog.correlate.alloutsiderange=Az \u00f6sszes f\u00e9nyk\u00e9p a nyomvonal id\u0151tartom\u00e1ny\u00e1n k\u00edv\u00fcl esik, \u00edgy egyik sem feleltethet\u0151 meg.\nPr\u00f3b\u00e1lja m\u00f3dos\u00edtani az eltol\u00e1st, vagy k\u00e9zzel megfeleltetni legal\u00e1bb egy f\u00e9nyk\u00e9pet. +dialog.correlate.filetimes=F\u00e1jl-id\u0151b\u00e9lyegek jelzik: +dialog.correlate.filetimes2=a hangf\u00e1jlnak +dialog.correlate.correltimes=A megfeleltet\u00e9shez haszn\u00e1lja: +dialog.correlate.timestamp.beginning=Elej\u00e9t +dialog.correlate.timestamp.middle=K\u00f6zep\u00e9t +dialog.correlate.timestamp.end=V\u00e9g\u00e9t +dialog.correlate.audioselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a megfeleltetett f\u00e9nyk\u00e9pek k\u00f6z\u00fcl az id\u0151eltol\u00e1s haszn\u00e1lat\u00e1hoz +dialog.correlate.select.audioname=Hang neve +dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb +dialog.rearrangephotos.desc=V\u00e1lassza ki a f\u00e9nyk\u00e9ppontok c\u00e9lj\u00e1t \u00e9s rendez\u00e9si sorrendj\u00e9t +dialog.rearrangephotos.tostart=Mozgat\u00e1s a kezdet\u00e9hez +dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez +dialog.rearrangephotos.nosort=Ne rendezze +dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint +dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint +dialog.compress.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont +dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa +dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g +dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa +dialog.compress.wackypoints.paramdesc=T\u00e1vols\u00e1gt\u00e9nyez\u0151 +dialog.compress.singletons.title=Egyke pontok elt\u00e1vol\u00edt\u00e1sa +dialog.compress.singletons.paramdesc=T\u00e1vols\u00e1gt\u00e9nyez\u0151 +dialog.compress.duplicates.title=Kett\u0151z\u00f6tt pontok elt\u00e1vol\u00edt\u00e1sa +dialog.compress.summarylabel=T\u00f6rlend\u0151 pontok +dialog.pastecoordinates.desc=Adja meg vagy illessze be a koordin\u00e1t\u00e1kat ide +dialog.pastecoordinates.coords=Koordin\u00e1t\u00e1k +dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00f3jra +dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/prune/\nwebhelyet. +dialog.about.version=Verzi\u00f3 +dialog.about.build=Build +dialog.about.summarytext1=A Prune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re. +dialog.about.summarytext2=Gnu GPL licenc alatt ker\u00fclt kiad\u00e1sra a szabad, ny\u00edlt, vil\u00e1gm\u00e9ret\u0171 haszn\u00e1lathoz \u00e9s fejleszt\u00e9shez.
M\u00e1sol\u00e1sa, terjeszt\u00e9se \u00e9s m\u00f3dos\u00edt\u00e1sa megengedett \u00e9s \u00f6szt\u00f6nz\u00f6tt
a mell\u00e9kelt license.txt f\u00e1jlban r\u00f6gz\u00edtett felt\u00e9telek szerint +dialog.about.summarytext3=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a http://activityworkshop.net/ webhelyet. +dialog.about.languages=El\u00e9rhet\u0151 nyelvek +dialog.about.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy +dialog.about.systeminfo=Rendszerinform\u00e1ci\u00f3 +dialog.about.systeminfo.os=Oper\u00e1ci\u00f3s rendszer +dialog.about.systeminfo.java=Java futtat\u00f3k\u00f6rnyezet +dialog.about.systeminfo.java3d=Java3d telep\u00edtve +dialog.about.systeminfo.povray=Povray telep\u00edtve +dialog.about.systeminfo.exiftool=Exiftool telep\u00edtve +dialog.about.systeminfo.gpsbabel=Gpsbabel telep\u00edtve +dialog.about.systeminfo.gnuplot=Gnuplot telep\u00edtve +dialog.about.systeminfo.exiflib=Exif f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r +dialog.about.systeminfo.exiflib.internal=Be\u00e9p\u00edtett +dialog.about.systeminfo.exiflib.internal.failed=Be\u00e9p\u00edtett (nem tal\u00e1lhat\u00f3) +dialog.about.systeminfo.exiflib.external=K\u00fcls\u0151 +dialog.about.systeminfo.exiflib.external.failed=K\u00fcls\u0151 (nem tal\u00e1lhat\u00f3) +dialog.about.yes=Igen +dialog.about.no=Nem +dialog.about.credits=K\u00e9sz\u00edt\u0151k +dialog.about.credits.code=Prune k\u00f3dj\u00e1t \u00edrta: +dialog.about.credits.exifcode=Exif k\u00f3d: +dialog.about.credits.icons=N\u00e9h\u00e1ny ikon sz\u00e1rmazik: +dialog.about.credits.translators=Ford\u00edt\u00f3k +dialog.about.credits.translations=Ford\u00edt\u00e1st seg\u00edtette +dialog.about.credits.devtools=Fejleszt\u0151eszk\u00f6z\u00f6k +dialog.about.credits.othertools=Egy\u00e9b eszk\u00f6z\u00f6k +dialog.about.credits.thanks=K\u00f6sz\u00f6net: +dialog.about.readme=Olvassel +dialog.checkversion.error=A verzi\u00f3sz\u00e1m nem ellen\u0151rizhet\u0151.\nEllen\u0151rizze az internetkapcsolatot. +dialog.checkversion.uptodate=A Prune leg\u00fajabb verzi\u00f3j\u00e1t haszn\u00e1lja. +dialog.checkversion.newversion1=El\u00e9rhet\u0151 a Prune \u00faj verzi\u00f3ja! A leg\u00fajabb veri\u00f3 most: +dialog.checkversion.newversion2=. +dialog.checkversion.releasedate1=Ez az \u00faj verzi\u00f3 kiad\u00e1sra ker\u00fclt: +dialog.checkversion.releasedate2=. +dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/prune/download.html webhelyet. +dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00f3k az eg\u00e9r haszn\u00e1lata helyett +dialog.keys.keylist=
Ny\u00edlbillenty\u0171kT\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le
Ctrl + bal, jobb ny\u00edlEl\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa
Ctrl + fel, le ny\u00edlNagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s
Ctrl + PgUp, PgDownEl\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa
Ctrl + Home, EndEls\u0151, utols\u00f3 pont kiv\u00e1laszt\u00e1sa
DelJelenlegi pont t\u00f6rl\u00e9se
+dialog.keys.normalmodifier=Ctrl +dialog.keys.macmodifier=Command +dialog.saveconfig.desc=A k\u00f6vetkez\u0151 be\u00e1ll\u00edt\u00e1sok menthet\u0151k egy konfigur\u00e1ci\u00f3s f\u00e1jlba +dialog.saveconfig.prune.trackdirectory=Nyomvonalak k\u00f6nyvt\u00e1ra +dialog.saveconfig.prune.photodirectory=F\u00e9nyk\u00e9pek k\u00f6nyvt\u00e1ra +dialog.saveconfig.prune.languagecode=Nyelv k\u00f3dja (HU) +dialog.saveconfig.prune.languagefile=Nyelvi f\u00e1jl +dialog.saveconfig.prune.gpsdevice=GPS eszk\u00f6z +dialog.saveconfig.prune.gpsformat=GPS form\u00e1tum +dialog.saveconfig.prune.povrayfont=Povray bet\u0171t\u00edpus +dialog.saveconfig.prune.metricunits=Metrikus m\u00e9rt\u00e9krendszer haszn\u00e1lata? +dialog.saveconfig.prune.gnuplotpath=\u00datvonal a gnuplothoz +dialog.saveconfig.prune.gpsbabelpath=\u00datvonal a gpsbabelhez +dialog.saveconfig.prune.exiftoolpath=\u00datvonal az exiftoolhoz +dialog.saveconfig.prune.mapsource=Kiv\u00e1lasztott t\u00e9rk\u00e9pforr\u00e1s +dialog.saveconfig.prune.mapsourcelist=T\u00e9rk\u00e9pforr\u00e1sok +dialog.saveconfig.prune.diskcache=T\u00e9rk\u00e9p-gyors\u00edt\u00f3t\u00e1r +dialog.saveconfig.prune.kmzimagewidth=KMZ k\u00e9psz\u00e9less\u00e9g +dialog.saveconfig.prune.kmzimageheight=KMZ k\u00e9pmagass\u00e1g +dialog.saveconfig.prune.colourscheme=Sz\u00edns\u00e9ma +dialog.saveconfig.prune.linewidth=Vonalsz\u00e9less\u00e9g +dialog.saveconfig.prune.kmltrackcolour=KML nyomvonal sz\u00edne +dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait: +dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3? +dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket +dialog.addaltitude.desc=Magass\u00e1ki eltol\u00e1s, amely hozz\u00e1adand\u00f3 +dialog.lookupsrtm.overwritezeros=Fel\u00fcl\u00edrja a nulla magass\u00e1g \u00e9rt\u00e9ket? +dialog.setcolours.intro=A sz\u00edn m\u00f3dos\u00edt\u00e1s\u00e1hoz kattintson egy sz\u00ednfoltra +dialog.setcolours.background=H\u00e1tt\u00e9r +dialog.setcolours.borders=Szeg\u00e9lyek +dialog.setcolours.lines=Vonalak +dialog.setcolours.primary=Els\u0151dleges +dialog.setcolours.secondary=M\u00e1sodlagos +dialog.setcolours.point=Pontok +dialog.setcolours.selection=Kijel\u00f6l\u00e9s +dialog.setcolours.text=Sz\u00f6veg +dialog.colourchooser.title=Sz\u00edn kiv\u00e1laszt\u00e1sa +dialog.colourchooser.red=Piros +dialog.colourchooser.green=Z\u00f6ld +dialog.colourchooser.blue=K\u00e9k +dialog.setlanguage.firstintro=Kiv\u00e1laszthat egy be\u00e9p\u00edtett nyelvet,

vagy v\u00e1lasszon egy sz\u00f6vegf\u00e1jlt helyette. +dialog.setlanguage.secondintro=Mentenie kell a be\u00e1ll\u00edt\u00e1sokat, majd

a nyelv v\u00e1lt\u00e1s\u00e1hoz ind\u00edtsa \u00fajra a Prune-t. +dialog.setlanguage.language=Nyelv +dialog.setlanguage.languagefile=Nyelvi f\u00e1jl +dialog.setlanguage.endmessage=Most mentse a be\u00e1ll\u00edt\u00e1sokat, \u00e9s ind\u00edtsa \u00fajra a Prune-t,\nhogy a nyelv v\u00e1lt\u00e1sa \u00e9rv\u00e9nybe l\u00e9pjen +dialog.diskcache.save=T\u00e9rk\u00e9pek ment\u00e9se a lemezre +dialog.diskcache.dir=Gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra +dialog.diskcache.createdir=K\u00f6nyvt\u00e1r l\u00e9trehoz\u00e1sa +dialog.diskcache.nocreate=A gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem ker\u00fclt l\u00e9trehoz\u00e1sra +dialog.deletefieldvalues.intro=V\u00e1lassza ki a t\u00f6rlend\u0151 mez\u0151t a jelenlegi tartom\u00e1nyban +dialog.setlinewidth.text=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4) +dialog.downloadosm.desc=Nyers OSM adatok let\u00f6lt\u00e9s\u00e9nek meger\u0151s\u00edt\u00e9se a megadott ter\u00fcletre: +dialog.searchwikipedianames.search=Keres\u00e9s erre: + +# 3d window +dialog.3d.title=Prune 3D n\u00e9zet +dialog.3d.altitudefactor=Magass\u00e1gi ny\u00fajt\u00e1si t\u00e9nyez\u0151 +dialog.3dlines.title=Prune r\u00e1csvonalak +dialog.3dlines.empty=Nincsenek megjelen\u00edthet\u0151 r\u00e1csvonalak! +dialog.3dlines.intro=Ezek a r\u00e1csvonalak a 3D n\u00e9zethez + +# Confirm messages +confirm.loadfile=Adatok f\u00e1jlb\u00f3l bet\u00f6ltve +confirm.save.ok1= +confirm.save.ok2=pont f\u00e1jlba ment\u00e9se sikeres +confirm.deletepoint.single=adatpont t\u00f6r\u00f6lve +confirm.deletepoint.multi=adatpont t\u00f6r\u00f6lve +confirm.point.edit=pont szerkesztve +confirm.mergetracksegments=Nyomvonalszakaszok egyes\u00edtve +confirm.reverserange=Tartom\u00e1ny megford\u00edtva +confirm.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1adva +confirm.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1adva +confirm.rearrangewaypoints=\u00datpontok \u00fajrarendezve +confirm.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendezve +confirm.cutandmove=Kijel\u00f6l\u00e9s \u00e1thelyezve +confirm.convertnamestotimes=\u00datpont nevei konvert\u00e1lva +confirm.saveexif.ok1=Mentve +confirm.saveexif.ok2=k\u00e9pf\u00e1jl +confirm.undo.single=m\u0171velet visszavonva +confirm.undo.multi=m\u0171velet visszavonva +confirm.jpegload.single=f\u00e9nyk\u00e9p hozz\u00e1adva +confirm.jpegload.multi=f\u00e9nyk\u00e9p hozz\u00e1adva +confirm.media.connect=m\u00e9dium \u00f6sszekapcsolva +confirm.photo.disconnect=f\u00e9nyk\u00e9p lev\u00e1lasztva +confirm.audio.disconnect=hang lev\u00e1lasztva +confirm.media.removed=elt\u00e1vol\u00edtva +confirm.correlatephotos.single=f\u00e9nyk\u00e9p megfeleltetve +confirm.correlatephotos.multi=f\u00e9nyk\u00e9p megfeleltetve +confirm.createpoint=pont l\u00e9trehozva +confirm.rotatephoto=f\u00e9nyk\u00e9p elforgatva +confirm.running=Futtat\u00e1s... +confirm.lookupsrtm1= +confirm.lookupsrtm2=magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3 +confirm.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9kei t\u00f6r\u00f6lve +confirm.audioload=Hangf\u00e1jl hozz\u00e1adva +confirm.correlateaudios.single=hangf\u00e1jl megfeleltetve +confirm.correlateaudios.multi=hangf\u00e1jl megfeleltetve + +# Buttons +button.ok=OK +button.back=El\u0151z\u0151 +button.next=K\u00f6vetkez\u0151 +button.finish=K\u00e9sz +button.cancel=M\u00e9gse +button.overwrite=Fel\u00fcl\u00edr\u00e1s +button.moveup=Mozgat\u00e1s feljebb +button.movedown=Mozgat\u00e1s lejjebb +button.showlines=Sorok megjelen\u00edt\u00e9se +button.edit=Szerkeszt\u00e9s +button.exit=Kil\u00e9p\u00e9s +button.close=Bez\u00e1r\u00e1s +button.continue=Folytat\u00e1s +button.yes=Igen +button.no=Nem +button.yestoall=Igen, mindet +button.notoall=Nem, egyiket se +button.select=Kijel\u00f6l\u00e9s +button.selectall=Mindent kijel\u00f6l +button.selectnone=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se +button.preview=El\u0151n\u00e9zet +button.load=Bet\u00f6lt\u00e9s +button.upload=Felt\u00f6lt\u00e9s +button.guessfields=Mez\u0151k kital\u00e1l\u00e1sa +button.showwebpage=Weboldal megjelen\u00edt\u00e9se +button.check=Ellen\u0151rz\u00e9s +button.resettodefaults=Vissza\u00e1ll\u00edt\u00e1s az alap\u00e9rtelmezettre +button.browse=B\u00f6ng\u00e9sz\u00e9s... +button.addnew=\u00daj hozz\u00e1ad\u00e1sa +button.delete=T\u00f6rl\u00e9s + +# File types +filetype.txt=TXT f\u00e1jlok +filetype.jpeg=JPG f\u00e1jlok +filetype.kmlkmz=KML, KMZ f\u00e1jlok +filetype.kml=KML f\u00e1jlok +filetype.kmz=KMZ f\u00e1jlok +filetype.gpx=GPX f\u00e1jlok +filetype.pov=POV f\u00e1jlok +filetype.svg=SVG f\u00e1jlok +filetype.audio=MP3, OGG, WAV f\u00e1jlok + +# Display components +display.nodata=Nincs adat bet\u00f6ltve +display.noaltitudes=A nyomvonal nem tartalmaz magass\u00e1gi adatot +display.notimestamps=A nyomvonal nem tartalmaz id\u0151b\u00e9lyegeket +details.trackdetails=Nyomvonal r\u00e9szletei +details.notrack=Nincs adat bet\u00f6ltve +details.track.points=Pontok +details.track.file=F\u00e1jl +details.track.numfiles=F\u00e1jlok sz\u00e1ma +details.pointdetails=Pont r\u00e9szletei +details.index.selected=Jelenlegi: +details.index.of=\u00f6sszesen: +details.nopointselection=Nincs pont kijel\u00f6lve +details.photofile=F\u00e9nyk\u00e9pf\u00e1jl +details.norangeselection=Nincs tartom\u00e1ny kijel\u00f6lve +details.rangedetails=Tartom\u00e1ny r\u00e9szletei +details.range.selected=Kiv\u00e1lasztva: +details.range.to=\u00f6sszesen: +details.altitude.to=\u00f6sszesen: +details.range.climb=Emelked\u00e9s +details.range.descent=Lejt\u00e9s +details.coordformat=Koordin\u00e1ta form\u00e1tuma +details.distanceunits=T\u00e1vols\u00e1g egys\u00e9ge +display.range.time.secs=mp +display.range.time.mins=p +display.range.time.hours=\u00f3 +display.range.time.days=n +details.range.avespeed=\u00c1tlagsebess\u00e9g +details.range.avemovingspeed=Mozg\u00e1si \u00e1tlag +details.range.maxspeed=Maxim\u00e1lis sebess\u00e9g +details.range.numsegments=Szakaszok sz\u00e1ma +details.range.pace=Iram +details.range.gradient=Szintk\u00fcl\u00f6nbs\u00e9g +details.lists.waypoints=\u00datpontok +details.lists.photos=F\u00e9nyk\u00e9pek +details.lists.audio=Hang +details.photodetails=F\u00e9nyk\u00e9p r\u00e9szletei +details.nophoto=Nincs f\u00e9nyk\u00e9p kiv\u00e1lasztva +details.photo.loading=Bet\u00f6lt\u00e9s +details.media.connected=\u00d6sszekapcsolva +details.audiodetails=Hang r\u00e9szletei +details.noaudio=Nincs hang kiv\u00e1lasztva +details.audio.file=Hangf\u00e1jl +details.audio.playing=lej\u00e1tsz\u00e1s... +map.overzoom=Nem \u00e9rhet\u0151 el t\u00e9rk\u00e9p ezen a nagy\u00edt\u00e1si szinten + +# Field names +fieldname.latitude=Sz\u00e9less\u00e9g +fieldname.longitude=Hossz\u00fas\u00e1g +fieldname.altitude=Magass\u00e1g +fieldname.timestamp=Id\u00f5 +fieldname.time=Id\u0151 +fieldname.waypointname=N\u00e9v +fieldname.waypointtype=T\u00edpus +fieldname.newsegment=Szakasz +fieldname.custom=Egy\u00e9ni +fieldname.prefix=Mez\u0151 +fieldname.distance=T\u00e1vols\u00e1g +fieldname.movingdistance=Mozg\u00e1si t\u00e1vols\u00e1g +fieldname.duration=Id\u0151tartam +fieldname.speed=Sebess\u00e9g +fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g + +# Measurement units +units.original=Eredeti +units.default=Alap\u00e9rtelmezett +units.metres=m\u00e9ter +units.metres.short=m +units.feet=l\u00e1b +units.feet.short=ft +units.kilometres=kilom\u00e9ter +units.kilometres.short=km +units.kmh=km/h +units.miles=m\u00e9rf\u00f6ld +units.miles.short=mi +units.mph=mph +units.metrespersec=m/s +units.feetpersec=ft/s +units.hours=\u00f3ra +units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc +units.degmin=Sz\u00f6g-sz\u00f6gperc +units.deg=Sz\u00f6g +units.iso8601=ISO 8601 + +# External urls +url.googlemaps=maps.google.hu +wikipedia.lang=hu + +# Cardinals for 3d plots +cardinal.n=\u00c9 +cardinal.s=D +cardinal.e=K +cardinal.w=Ny + +# Undo operations +undo.load=adatok bet\u00f6lt\u00e9se +undo.loadphotos=f\u00e9nyk\u00e9pek bet\u00f6lt\u00e9se +undo.loadaudios=hangf\u00e1jlok bet\u00f6lt\u00e9se +undo.editpoint=pont szerkeszt\u00e9se +undo.deletepoint=pont t\u00f6rl\u00e9se +undo.removephoto=f\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa +undo.removeaudio=hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa +undo.deleterange=tartom\u00e1ny t\u00f6rl\u00e9se +undo.compress=nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se +undo.insert=pontok besz\u00far\u00e1sa +undo.reverse=tartom\u00e1ny megford\u00edt\u00e1sa +undo.mergetracksegments=nyomvonalszakaszok egyes\u00edt\u00e9se +undo.addtimeoffset=id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa +undo.addaltitudeoffset=magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa +undo.rearrangewaypoints=\u00fatpontok \u00fajrarendez\u00e9se +undo.cutandmove=kijel\u00f6l\u00e9s mozgat\u00e1sa +undo.connect=\u00f6sszekapcsol\u00e1s +undo.disconnect=lev\u00e1laszt\u00e1s +undo.correlatephotos=f\u00e9nyk\u00e9pek megfeleltet\u00e9se +undo.rearrangephotos=f\u00e9nyk\u00e9pek \u00fajrarendez\u00e9se +undo.createpoint=pont l\u00e9trehoz\u00e1sa +undo.rotatephoto=f\u00e9nyk\u00e9p forgat\u00e1sa +undo.convertnamestotimes=nevek konvert\u00e1l\u00e1sa id\u0151v\u00e9 +undo.lookupsrtm=magass\u00e1gi adatok let\u00f6lt\u00e9se SRTM-r\u0151l +undo.deletefieldvalues=mez\u0151 \u00e9rt\u00e9keinek t\u00f6rl\u00e9se +undo.correlateaudios=hang megfeleltet\u00e9se + +# Error messages +error.save.dialogtitle=Hiba az adatok ment\u00e9sekor +error.save.nodata=Nincs mentend\u0151 adat +error.save.failed=Az adatok f\u00e1jlba ment\u00e9se nem siker\u00fclt +error.saveexif.filenotfound=F\u00e9nyk\u00e9pf\u00e1jl keres\u00e9se nem siker\u00fclt +error.saveexif.cannotoverwrite1=A(z) +error.saveexif.cannotoverwrite2=f\u00e9nyk\u00e9pf\u00e1jl csak olvashat\u00f3, \u00e9s nem \u00edrhat\u00f3 fel\u00fcl. \u00cdrjuk m\u00e1solatba? +error.saveexif.failed1= +error.saveexif.failed2=k\u00e9p ment\u00e9se nem siker\u00fclt +error.saveexif.forced1= +error.saveexif.forced2=k\u00e9p er\u00f6ltet\u00e9st ig\u00e9nyelt +error.load.dialogtitle=Hiba az adatok bet\u00f6lt\u00e9sekor +error.load.noread=A f\u00e1jl nem olvashat\u00f3 +error.load.nopoints=Nem tal\u00e1lhat\u00f3 koordin\u00e1tainform\u00e1ci\u00f3 a f\u00e1jlban +error.load.unknownxml=Ismeretlen xml form\u00e1tum: +error.load.noxmlinzip=Nem tal\u00e1lhat\u00f3 xml f\u00e1jl a zip f\u00e1jlon bel\u00fcl +error.load.othererror=Hiba a f\u00e1jl olvas\u00e1sa sor\u00e1n: +error.jpegload.dialogtitle=Hiba a k\u00e9pek bet\u00f6lt\u00e9sekor +error.jpegload.nofilesfound=Nem tal\u00e1lhat\u00f3 f\u00e1jl +error.jpegload.nojpegsfound=Nem tal\u00e1lhat\u00f3 jpeg f\u00e1jl +error.jpegload.nogpsfound=Nem tal\u00e1lhat\u00f3 GPS inform\u00e1ci\u00f3 +error.jpegload.exifreadfailed=Az EXIF inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvasat\u00f3 EXIF inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl. +error.audioload.nofilesfound=Nem tal\u00e1lhat\u00f3 hangf\u00e1jl +error.gpsload.unknown=Ismeretlen hiba +error.undofailed.title=A visszavon\u00e1s nem siker\u00fclt +error.undofailed.text=A m\u0171velet visszavon\u00e1sa nem siker\u00fclt +error.function.noop.title=A funkci\u00f3 nem eredm\u00e9nyezett v\u00e1ltoz\u00e1st +error.rearrange.noop=A pontok \u00fajrarendez\u00e9se nem eredm\u00e9nyezett v\u00e1ltoz\u00e1st +error.function.notavailable.title=A funkci\u00f3 nem \u00e9rhet\u0151 el +error.function.nojava3d=Ehhez a funkci\u00f3hoz a Java3d f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r sz\u00fcks\u00e9ges,\n amely a Sun.com webhelyr\u0151l \u00e9rhet\u0151 el. +error.3d=Hiba t\u00f6rt\u00e9nt a 3d megjelen\u00edt\u00e9ssel +error.readme.notfound=Az olvassel f\u00e1jl nem tal\u00e1lhat\u00f3 +error.osmimage.dialogtitle=Hiba a t\u00e9rk\u00e9p bet\u00f6lt\u00e9sekor +error.osmimage.failed=A t\u00e9rk\u00e9p bet\u00f6lt\u00e9se nem siker\u00fclt. Ellen\u0151rizze az internetkapcsolatot. +error.language.wrongfile=\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott f\u00e1jl nem egy nyelvi f\u00e1jl a Prune-hoz +error.convertnamestotimes.nonames=A nevek nem konvert\u00e1lhat\u00f3k id\u0151adatokk\u00e1 +error.lookupsrtm.nonefound=Nem \u00e9rhet\u0151 el magass\u00e1gi \u00e9rt\u00e9k ezekhez a pontokhoz +error.lookupsrtm.nonerequired=Az \u00f6sszes pont m\u00e1r rendelkezik magass\u00e1gadatokkal, \u00edgy nincs mit keresni +error.gpsies.uploadnotok=A gpsies szerver a k\u00f6vetkez\u0151 \u00fczenetet adta vissza +error.gpsies.uploadfailed=A felt\u00f6lt\u00e9s nem siker\u00fclt a k\u00f6vetkez\u0151 hib\u00e1val +error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt diff --git a/tim/prune/lang/prune-texts_in.properties b/tim/prune/lang/prune-texts_in.properties index 42e2b37..ec4bc25 100644 --- a/tim/prune/lang/prune-texts_in.properties +++ b/tim/prune/lang/prune-texts_in.properties @@ -17,9 +17,9 @@ menu.range.all=Pilih semua menu.range.none=Tidak memilih menu.photo=Foto menu.photo.saveexif=Simpan ke Exif -menu.photo.connect=Hubungkan ke titik -menu.photo.disconnect=Putuskan dari titik -menu.photo.delete=Menghapus foto +function.connecttopoint=Hubungkan ke titik +function.disconnectfrompoint=Putuskan dari titik +function.removephoto=Menghapus foto menu.view=Lihat menu.settings=Pengaturan menu.view.browser=Peta di browser @@ -105,7 +105,7 @@ details.pointdetails=Rincian titik details.nopointselection=Tidak ada titik details.norangeselection=Tidak ada jangkauan details.rangedetails=Rincian jangkauan -details.waypointsphotos.photos=Foto +details.lists.photos=Foto details.photodetails=Rincian foto details.nophoto=Tidak ada foto details.photo.loading=Membuka diff --git a/tim/prune/lang/prune-texts_it.properties b/tim/prune/lang/prune-texts_it.properties index 6c07c73..bf19b95 100644 --- a/tim/prune/lang/prune-texts_it.properties +++ b/tim/prune/lang/prune-texts_it.properties @@ -1,5 +1,5 @@ # Text entries for the Prune application -# Italian entries as extra +# Italian entries thanks to josatoc # Menu entries menu.file=File @@ -30,10 +30,8 @@ menu.point.editpoint=Edita Punto menu.point.deletepoint=Cancella Punto menu.photo=Foto menu.photo.saveexif=Salva su Exif -menu.photo.connect=Collega al punto -menu.photo.disconnect=Scollega dal punto -menu.photo.delete=Rimuovi foto -menu.view=Visualizza +menu.audio=Audio +menu.view=Vista menu.view.showsidebars=Mostra barre laterali menu.view.browser=Mappa sul browser menu.view.browser.google=Google maps @@ -49,6 +47,7 @@ menu.map.zoomin=Zoom + menu.map.zoomout=Zoom - menu.map.zoomfull=Zoom tutto menu.map.newpoint=Crea nuovo punto +menu.map.drawpoints=Crea serie di punti menu.map.connect=Aggancia ai punti menu.map.autopan=Autopan menu.map.showmap=Mostra sulla mappa @@ -61,6 +60,7 @@ altkey.menu.range=S altkey.menu.point=P altkey.menu.view=V altkey.menu.photo=O +altkey.menu.audio=U altkey.menu.settings=R altkey.menu.help=A @@ -99,14 +99,27 @@ function.setpaths=Configura percorsi programmi function.getgpsies=Ottieni traccie da Gpsies function.uploadgpsies=Carica traccia su Gpsies function.lookupsrtm=Ottieni quote da SRTM +function.getwikipedia=Ottieni informazioni da Wikipedia +function.searchwikipedianames=Cerca il nome in Wikipedia +function.downloadosm=Scarica dati OSM dell'area function.duplicatepoint=Duplica punto corrente in coda function.setcolours=Scegli colori +function.setlinewidth=Scegli la lo spessore function.setlanguage=Scegli la lingua +function.connecttopoint=Collega al punto +function.disconnectfrompoint=Scollega dal punto +function.removephoto=Rimuovi foto function.correlatephotos=Correla le foto function.rearrangephotos=Riordina foto function.rotatephotoleft=Ruota foto a sinistra function.rotatephotoright=Ruota foto a destra +function.photopopup=Mostra la foto in un riquadro function.ignoreexifthumb=Ignora anteprima foto exif +function.loadaudio=Aggiungi ripresa audio +function.removeaudio=Rimuovi ripresa audio +function.correlateaudios=Correla ripresa audio +function.playaudio=Riproduci ripresa audio +function.stopaudio=Ferma ripresa audio function.help=Aiuto function.showkeys=Visualizza tasti scelta rapida function.about=Informazioni su Prune @@ -193,6 +206,7 @@ dialog.pointtype.desc=Salva i tipi di punti seguenti: dialog.pointtype.track=Punti traccia dialog.pointtype.waypoint=Waypoints dialog.pointtype.photo=Punti foto +dialog.pointtype.audio=Punti audio dialog.pointtype.selection=Solo la selezione dialog.confirmreversetrack.title=Conferma l'inversione dialog.confirmreversetrack.text=Questa traccia contiene informazioni sull'orario di scatto che possono essere messe fuori sequenza dopo l'inversione.\nSei sicuro di voler invertire questa sezione? @@ -278,12 +292,14 @@ dialog.gpsies.activity.motorbiking=Motocicletta dialog.gpsies.activity.snowshoe=Trekking sulla neve dialog.gpsies.activity.sailing=Navigazione dialog.gpsies.activity.skating=Pattinaggio +dialog.wikipedia.column.name=Titolo articolo +dialog.wikipedia.column.distance=Distanza dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto. dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare? dialog.correlate.photoselect.intro=Selezione una delle foto correlate da usare come scarto dell'orario -dialog.correlate.photoselect.photoname=Nome della foto -dialog.correlate.photoselect.timediff=Differenza di orario -dialog.correlate.photoselect.photolater=Foto scattata dopo il punto +dialog.correlate.select.photoname=Nome della foto +dialog.correlate.select.timediff=Differenza di orario +dialog.correlate.select.photolater=Foto scattata dopo il punto dialog.correlate.options.tip=Consiglio: Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te dialog.correlate.options.intro=Selezione le opzioni per la correlazione automatica dialog.correlate.options.offsetpanel=Scarto di orario @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=ore, dialog.correlate.options.offset.minutes=minuti e dialog.correlate.options.offset.seconds=secondi dialog.correlate.options.photolater=Foto scattata dopo il punto -dialog.correlate.options.pointlater=Punto successivo allo scatto della foto +dialog.correlate.options.pointlaterphoto=Punto successivo allo scatto della foto +dialog.correlate.options.audiolater=Ripresa audio dopo il punto +dialog.correlate.options.pointlateraudio=Punto successivo alla ripresa audio dialog.correlate.options.limitspanel=Limiti di correlamento dialog.correlate.options.notimelimit=Nessun limite di tempo dialog.correlate.options.timelimit=Limite di tempo @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Nessun limite di distanza dialog.correlate.options.distancelimit=Distanza limite dialog.correlate.options.correlate=Correlate dialog.correlate.alloutsiderange=Tutte le foto sono fuori dall'orario della traccia, e nessuna pu\u00f2 essere correlata.\nProva a cambiare lo scarto o correla manualmente almeno una foto. +dialog.correlate.filetimes=I dati temporali mostrano: +dialog.correlate.filetimes2=della ripresa audio +dialog.correlate.correltimes=Per la correlazione usa: +dialog.correlate.timestamp.beginning=Inizio +dialog.correlate.timestamp.middle=Met\u00e0 +dialog.correlate.timestamp.end=Fine +dialog.correlate.audioselect.intro=Seleziona una di queste riprese audio correlate come scarto temporale +dialog.correlate.select.audioname=Nome ripresa audio +dialog.correlate.select.audiolater=Ripresa audio successiva dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto dialog.rearrangephotos.tostart=Sposta all'inizio dialog.rearrangephotos.toend=Sposta alla fine @@ -380,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Cache delle mappe dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ dialog.saveconfig.prune.colourscheme=Schema colori +dialog.saveconfig.prune.linewidth=Spessore linea dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML dialog.setpaths.intro=Se necessario, puoi indicare il percorso delle applicazioni esterne: dialog.setpaths.found=trovato? @@ -409,10 +437,12 @@ dialog.diskcache.dir=Cartella della cache dialog.diskcache.createdir=Crea cartella dialog.diskcache.nocreate=Cartella della cache non creata dialog.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo corrente +dialog.setlinewidth.text=Specifica il tratteggio delle linee per disegnare la traccia (1-4) +dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata: +dialog.searchwikipedianames.search=Cerca per: # 3d window dialog.3d.title=Visione Prune in 3D -dialog.3d.altitudecap=Intervallo altitudine minimo dialog.3d.altitudefactor=Fattore di moltiplicazione della quota dialog.3dlines.title=Griglia di Prune dialog.3dlines.empty=Nessuna griglia mostrata! @@ -439,16 +469,21 @@ confirm.undo.single=operazione annullate confirm.undo.multi=operazioni annullate confirm.jpegload.single=foto \u00e8 stata aggiunta confirm.jpegload.multi=foto sono state aggiunte -confirm.photo.connect=foto collegata +confirm.media.connect=media collegata confirm.photo.disconnect=foto scollegata -confirm.correlate.single=foto era correlata -confirm.correlate.multi=foto erano correlate +confirm.audio.disconnect=ripresa audio scollegata +confirm.media.removed=rimosso +confirm.correlatephotos.single=foto era correlata +confirm.correlatephotos.multi=foto erano correlate confirm.createpoint=punto creato confirm.rotatephoto=foto ruotata confirm.running=Operazione in corso... confirm.lookupsrtm1=Trovato confirm.lookupsrtm2=valori di quota confirm.deletefieldvalues=Valori del campo cancellati +confirm.audioload=Ripresa audio aggiunta +confirm.correlateaudios.single=la ripresa audio era correlata +confirm.correlateaudios.multi=le riprese audio erano correlate # Buttons button.ok=OK @@ -491,6 +526,7 @@ filetype.kmz=File KMZ filetype.gpx=File GPX filetype.pov=File POV filetype.svg=File SVG +filetype.audio=File MP3, OGG, WAV # Display components display.nodata=Nessun dato caricato @@ -525,12 +561,17 @@ details.range.maxspeed=Velocit\u00e0 massima details.range.numsegments=Numero di segmenti details.range.pace=Passo details.range.gradient=Gradiente -details.waypointsphotos.waypoints=Waypoint -details.waypointsphotos.photos=Foto +details.lists.waypoints=Waypoint +details.lists.photos=Foto +details.lists.audio=Ripresa audio details.photodetails=Dettagli foto details.nophoto=Nessuna foto selezionata details.photo.loading=Caricamento -details.photo.connected=Collegata +details.media.connected=Collegata +details.audiodetails=Dettagli ripresa audio +details.noaudio=Nessuna ripresa audio selezionata +details.audio.file=Ripresa audio +details.audio.playing=Riproduzione... map.overzoom=Mappa non disponibile a questo livello di zoom # Field names @@ -573,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.it +wikipedia.lang=it # Cardinals for 3d plots cardinal.n=N @@ -583,9 +625,11 @@ cardinal.w=O # Undo operations undo.load=carica dati undo.loadphotos=carica foto +undo.loadaudios=carica riprese audio undo.editpoint=edita punto undo.deletepoint=cancella punto -undo.deletephoto=rimuovi foto +undo.removephoto=rimuovi foto +undo.removeaudio=rimuovi riprese audio undo.deleterange=cancella l'intervallo undo.compress=comprimi traccia undo.insert=inserisci punti @@ -595,15 +639,16 @@ undo.addtimeoffset=aggiungi scarto temporale undo.addaltitudeoffset=aggiungi scarto altitudine undo.rearrangewaypoints=riorganizza waypoint undo.cutandmove=muovi selezione -undo.connectphoto=collega foto -undo.disconnectphoto=scollega foto -undo.correlate=correla foto +undo.connect=collega +undo.disconnect=scollega +undo.correlatephotos=correla foto undo.rearrangephotos=riorganizza foto undo.createpoint=crea punto undo.rotatephoto=ruota foto undo.convertnamestotimes=converti nomi in orari undo.lookupsrtm=cerca quote in SRTM undo.deletefieldvalues=cancellare i valori del campo +undo.correlateaudios=correla riprese audio # Error messages error.save.dialogtitle=Errore nel salvataggio dati @@ -625,9 +670,9 @@ error.load.othererror=Errore nella lettura del file: error.jpegload.dialogtitle=Errore nel caricamento delle foto error.jpegload.nofilesfound=File non trovato error.jpegload.nojpegsfound=File jpeg non trovato -error.jpegload.noexiffound=Informazioni EXIF non trovate error.jpegload.nogpsfound=Informazioni GPS non trovate error.jpegload.exifreadfailed=Lettera dei dati EXIF fallita. I dati EXIF non possono\n essere letti senza una libreria interna o esterna. +error.audioload.nofilesfound=Riprese audio non trovate error.gpsload.unknown=Errore sconosciuto error.undofailed.title=Impossibile annullare error.undofailed.text=Impossibile annullare l'operazione @@ -645,3 +690,4 @@ error.lookupsrtm.nonefound=Valori di quota non trovati error.lookupsrtm.nonerequired=Tutti i punti hanno gi\u00e0 una quota, non c'\u00e8 niente da cercare error.gpsies.uploadnotok=Il server Gpsies ha riportato il messaggio error.gpsies.uploadfailed=Il caricamento \u00e8 fallito con l'errore +error.playaudiofailed=Ripresa audio non riprodotta diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties index 0c0d5eb..c6b1653 100644 --- a/tim/prune/lang/prune-texts_ja.properties +++ b/tim/prune/lang/prune-texts_ja.properties @@ -30,9 +30,9 @@ menu.range.start=\u958b\u59cb\u70b9\u3092\u7f6e\u304f menu.range.end=\u7d42\u4e86\u70b9\u3092\u7f6e\u304f menu.photo=\u5199\u771f menu.photo.saveexif=Exif\u306b\u4fdd\u5b58 -menu.photo.connect=\u70b9\u306b\u63a5\u7d9a -menu.photo.disconnect=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664 -menu.photo.delete=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f +function.connecttopoint=\u70b9\u306b\u63a5\u7d9a +function.disconnectfrompoint=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664 +function.removephoto=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f menu.view=\u30d3\u30e5\u30fc menu.view.browser=\u5730\u56f3\u3092\u30d6\u30e9\u30a6\u30b6\u30fc\u3067\u898b\u308b menu.view.browser.google=Google \u30de\u30c3\u30d7 @@ -240,9 +240,9 @@ dialog.gpsies.activity.skating=\u30d5\u30a3\u30ae\u30e5\u30a2\u30b9\u30b1\u30fc\ dialog.correlate.notimestamps=\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u306b\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u304c\u306a\u3044\u306e\u3067\u3001\u5199\u771f\u3092\u95a2\u9023\u4ed8\u3051\u3089\u308c\u308b\u7269\u304c\u3042\u308a\u307e\u305b\u3093\u3002 dialog.correlate.nouncorrelatedphotos=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u306a\u304b\u3063\u305f\u5199\u771f\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u7d9a\u3051\u307e\u3059\u304b\uff1f dialog.correlate.photoselect.intro=\u6642\u9593\u504f\u4f4d\u3092\u4f5c\u308b\u305f\u3081\u306e\u5199\u771f\u3092\u4e00\u3064\u9078\u3093\u3067\u304f\u3060\u3055\u3044\u3002 -dialog.correlate.photoselect.photoname=\u5199\u771f\u540d -dialog.correlate.photoselect.timediff=\u6642\u9593\u5dee -dialog.correlate.photoselect.photolater=\u5199\u771f\u304c\u5f8c +dialog.correlate.select.photoname=\u5199\u771f\u540d +dialog.correlate.select.timediff=\u6642\u9593\u5dee +dialog.correlate.select.photolater=\u5199\u771f\u304c\u5f8c dialog.correlate.options.tip=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002 dialog.correlate.options.intro=\u81ea\u52d5\u95a2\u9023\u4ed8\u3051\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 dialog.correlate.options.offsetpanel=\u6642\u9593\u504f\u4f4d @@ -251,7 +251,7 @@ dialog.correlate.options.offset.hours=\u6642\u9593 dialog.correlate.options.offset.minutes=\u5206 dialog.correlate.options.offset.seconds=\u79d2 dialog.correlate.options.photolater=\u3053\u306e\u70b9\u3088\u308a\u5f8c\u308d\u306e\u5199\u771f -dialog.correlate.options.pointlater=\u5199\u771f\u3088\u308a\u5f8c\u308d\u306e\u70b9 +dialog.correlate.options.pointlaterphoto=\u5199\u771f\u3088\u308a\u5f8c\u308d\u306e\u70b9 dialog.correlate.options.limitspanel=\u95a2\u9023\u4ed8\u3051\u5236\u9650 dialog.correlate.options.notimelimit=\u6642\u9593\u5236\u9650\u306a\u3057 dialog.correlate.options.timelimit=\u6642\u9593\u5236\u9650 @@ -356,7 +356,6 @@ dialog.setlanguage.endmessage=\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3001\u8a00\u # 3d window dialog.3d.title=Prune 3D \u8868\u793a -dialog.3d.altitudecap=\u6700\u4f4e\u9ad8\u5ea6\u7bc4\u56f2 dialog.3dlines.title=Prune \u683c\u5b50\u7dda dialog.3dlines.empty=\u683c\u5b50\u7dda\u304c\u8868\u793a\u3055\u308c\u307e\u305b\u3093 dialog.3dlines.intro=\u3053\u308c\u3089\u304c 3D \u8868\u793a\u7528\u306e\u683c\u5b50\u7dda\u3067\u3059\u3002 @@ -382,10 +381,9 @@ confirm.undo.single=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f confirm.undo.multi=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f confirm.jpegload.single=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f confirm.jpegload.multi=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f -confirm.photo.connect=\u5199\u771f\u304c\u63a5\u7d9a\u3055\u308c\u305f confirm.photo.disconnect=\u5199\u771f\u304c\u63a5\u7d9a\u3055\u308c\u305f -confirm.correlate.single=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f -confirm.correlate.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f +confirm.correlatephotos.single=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f +confirm.correlatephotos.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f confirm.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f confirm.running=\u5b9f\u884c\u4e2d... @@ -460,12 +458,12 @@ details.range.avemovingspeed=\u5e73\u5747\u79fb\u52d5 details.range.numsegments=\u30bb\u30b0\u30e1\u30f3\u30c8\u6570 details.range.pace=\u30da\u30fc\u30b9 details.range.gradient=\u52fe\u914d -details.waypointsphotos.waypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8 -details.waypointsphotos.photos=\u5199\u771f +details.lists.waypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8 +details.lists.photos=\u5199\u771f details.photodetails=\u5199\u771f\u8a73\u7d30 details.nophoto=\u5199\u771f\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093 details.photo.loading=\u8aad\u307f\u8fbc\u307f\u4e2d -details.photo.connected=\u63a5\u7d9a\u6e08 +details.media.connected=\u63a5\u7d9a\u6e08 map.overzoom=\u3053\u306e\u30ba\u30fc\u30e0\u30ec\u30d9\u30eb\u3067\u306f\u5730\u56f3\u304c\u5165\u624b\u3067\u304d\u307e\u305b\u3093\u3002 # Field names @@ -508,6 +506,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.co.jp +wikipedia.lang=ja # Cardinals for 3d plots cardinal.n=N @@ -520,7 +519,7 @@ undo.load=\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f undo.loadphotos=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f undo.editpoint=\u70b9\u306e\u7de8\u96c6 undo.deletepoint=\u70b9\u306e\u524a\u9664 -undo.deletephoto=\u5199\u771f\u306e\u53d6\u308a\u9664\u304d +undo.removephoto=\u5199\u771f\u306e\u53d6\u308a\u9664\u304d undo.deleterange=\u7bc4\u56f2\u306e\u524a\u9664 undo.compress=\u30c8\u30e9\u30c3\u30af\u306e\u5727\u7e2e undo.insert=\u70b9\u306e\u633f\u5165 @@ -530,9 +529,9 @@ undo.addtimeoffset=\u6642\u9593\u504f\u4f4d\u3092\u52a0\u3048\u308b undo.addaltitudeoffset=\u9ad8\u5ea6\u504f\u4f4d\u3092\u52a0\u3048\u308b undo.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u633f\u5165 undo.cutandmove=\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u79fb\u52d5 -undo.connectphoto=\u5199\u771f\u306e\u63a5\u7d9a -undo.disconnectphoto=\u5199\u771f\u306e\u63a5\u7d9a\u89e3\u9664 -undo.correlate=\u5199\u771f\u306e\u95a2\u9023\u4ed8\u3051 +undo.connect=\u5199\u771f\u306e\u63a5\u7d9a +undo.disconnect=\u5199\u771f\u306e\u63a5\u7d9a\u89e3\u9664 +undo.correlatephotos=\u5199\u771f\u306e\u95a2\u9023\u4ed8\u3051 undo.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u66ff\u3048 undo.createpoint=\u70b9\u306e\u4f5c\u6210 undo.rotatephoto=\u5199\u771f\u306e\u56de\u8ee2 @@ -558,7 +557,6 @@ error.load.othererror=\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u30 error.jpegload.dialogtitle=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc: error.jpegload.nofilesfound=\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 error.jpegload.nojpegsfound=Jpeg\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 -error.jpegload.noexiffound=EXIF\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 error.jpegload.nogpsfound=GPS\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 error.gpsload.unknown=\u4e0d\u660e\u306a\u30a8\u30e9\u30fc error.undofailed.title=\u30a2\u30f3\u30c9\u30a5\u5931\u6557 diff --git a/tim/prune/lang/prune-texts_ko.properties b/tim/prune/lang/prune-texts_ko.properties new file mode 100644 index 0000000..eb4ef92 --- /dev/null +++ b/tim/prune/lang/prune-texts_ko.properties @@ -0,0 +1,693 @@ +# Text entries for the Prune application +# Korean entries thanks to HooAU + +# Menu entries +menu.file=\ud30c\uc77c +menu.file.addphotos=\uc0ac\uc9c4 \ucd94\uac00\ud558\uae30 +menu.file.save=\ud0dd\uc2a4\ud2b8\ub85c \uc800\uc7a5\ud558\uae30 +menu.file.exit=\uc885\ub8cc +menu.track=\ud2b8\ub799 +menu.track.undo=\uc2e4\ud589\ucde8\uc18c +menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c +menu.track.deletemarked=\ud45c\uc2dc\ub41c \uc9c0\uc810 \uc9c0\uc6b0\uae30 +menu.track.rearrange=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c +menu.track.rearrange.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 +menu.track.rearrange.end=\ub05d \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 +menu.track.rearrange.nearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9 +menu.range=\uc5f0\uacb0\uc120 +menu.range.all=\ubaa8\ub450 \uc120\ud0dd +menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30 +menu.range.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc9c0\uc815 +menu.range.end=\ub05d\uc9c0\uc810\uc73c\ub85c \uc9c0\uc815 +menu.range.deleterange=\uc5f0\uacb0\uc120 \uc0ad\uc81c +menu.range.interpolate=\uc911\uac04\uc5d0 \ub123\uae30 +menu.range.average=\ud3c9\uade0\uc9c0\uc810\uc120\ud0dd +menu.range.reverse=\ucc98\uc74c\uacfc \ub05d \ubc14\uafb8\uae30 +menu.range.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569 +menu.range.cutandmove=\uc790\ub974\uace0 \uc62e\uae30\uae30 +menu.point=\uc9c0\uc810 +menu.point.editpoint=\uc9c0\uc810 \uc218\uc815 +menu.point.deletepoint=\uc9c0\uc810 \uc0ad\uc81c +menu.photo=\uc0ac\uc9c4 +menu.photo.saveexif=Exif\ub85c \uc800\uc7a5 +menu.audio=\uc18c\ub9ac +menu.view=\ubcf4\uae30 +menu.view.showsidebars=\uc0ac\uc774\ub4dc\ubc14 \ubcf4\uae30 +menu.view.browser=\uc6f9\ube0c\ub77c\uc6b0\uc800\ub85c \uc9c0\ub3c4\ubcf4\uae30 +menu.view.browser.google=\uad6c\uae00 \uc9c0\ub3c4 +menu.view.browser.openstreetmap=\uc624\ud508\uc2a4\ud2b8\ub9ac\ud2b8\ub9f5 +menu.view.browser.mapquest=\ub9f5\ud018\uc2a4\ud2b8 +menu.view.browser.yahoo=\uc57c\ud6c4 \uc9c0\ub3c4 +menu.view.browser.bing=\ube59 \uc9c0\ub3c4 +menu.settings=\uc124\uc815 +menu.settings.onlinemode=\uc778\ud130\ub137\uc5d0\uc11c \uc9c0\ub3c4 \ubd88\ub7ec\uc624\uae30 +menu.help=\ub3c4\uc6c0\ub9d0 +# Popup menu for map +menu.map.zoomin=\ud655\ub300 +menu.map.zoomout=\ucd95\uc18c +menu.map.zoomfull=\uc804\uccb4\ud06c\uae30 \ubcf4\uae30 +menu.map.newpoint=\uc0c8 \uc9c0\uc810 \uc0dd\uc131 +menu.map.drawpoints=\uc0c8 \uc9c0\uc810\ub4e4 \uc0dd\uc131 +menu.map.connect=\ud2b8\ub809 \uc9c0\uc810 \uc5f0\uacb0\ud558\uae30 +menu.map.autopan=\uc790\ub3d9 \uc2dc\uc810 \ucd94\uc801 +menu.map.showmap=\uc9c0\ub3c4\ubcf4\uae30 +menu.map.showscalebar=\ucd95\uc801 \uc870\uc808\ubc14 \ubcf4\uae30 + +# Alt keys for menus +altkey.menu.file=F +altkey.menu.track=F +altkey.menu.range=R +altkey.menu.point=P +altkey.menu.view=V +altkey.menu.photo=O +altkey.menu.audio=A +altkey.menu.settings=S +altkey.menu.help=H + +# Ctrl shortcuts for menu items +shortcut.menu.file.open=O +shortcut.menu.file.load=L +shortcut.menu.file.save=S +shortcut.menu.track.undo=Z +shortcut.menu.edit.compress=C +shortcut.menu.range.all=A +shortcut.menu.help.help=H + +# Functions +function.open=\ud30c\uc77c \uc5f4\uae30 +function.loadfromgps=GPS\uc5d0\uc11c \uc790\ub8cc \uac00\uc838\uc624\uae30 +function.sendtogps=GPS\ub85c \uc790\ub8cc \ubcf4\ub0b4\uae30 +function.exportkml=KML \ub0b4\ubcf4\ub0b4\uae30 +function.exportgpx=GPX \ub0b4\ubcf4\ub0b4\uae30 +function.exportpov=POV \ub0b4\ubcf4\ub0b4\uae30 +function.exportsvg=SVG \ub0b4\ubcf4\ub0b4\uae30 +function.editwaypointname=\uacbd\uc720\uc9c0 \uc774\ub984 \uc218\uc815 +function.compress=\uacbd\ub85c \uc555\ucd95\ud558\uae30 +function.addtimeoffset=\uc624\ud504\uc14b \uc2dc\uac04 \ucd94\uac00 +function.addaltitudeoffset=\uc624\ud504\uc14b \uace0\ub3c4 \ucd94\uac00 +function.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 +function.deletefieldvalues=\ud544\ub4dc\uac12 \uc0ad\uc81c +function.findwaypoint=\uacbd\uc720\uc9c0 \ucc3e\uae30 +function.pastecoordinates=\uc0c8 \uc88c\ud45c \ub123\uae30 +function.charts=\ucc28\ud2b8 +function.show3d=3\ucc28\uc6d0 \ubcf4\uae30 +function.distances=\uac70\ub9ac +function.fullrangedetails=\uc5f0\uacb0\uc120 \uc0c1\uc138 \uc815\ubcf4 \ubcf4\uae30 +function.setmapbg=\ubc30\uacbd \uc9c0\ub3c4 \uc9c0\uc815 +function.setkmzimagesize=KMZ \uadf8\ub9bc \ud06c\uae30 \uc9c0\uc815 +function.setpaths=\uc678\ubd80\ud504\ub85c\uadf8\ub7a8 \uc9c0\uc815 +function.getgpsies=gpsies\uc5d0\uc11c \ud2b8\ub799\ubaa9\ub85d \uc5bb\uae30 +function.uploadgpsies=gpsies\ub85c \ud2b8\ub799 \uc62c\ub9ac\uae30 +function.lookupsrtm=SRTM\uc5d0\uc11c \uace0\ub3c4 \ucc3e\uae30 +function.getwikipedia=\uc704\ud0a4\ud53c\ub514\uc544\uc5d0\uc11c \uadfc\ucc98 \uc815\ubcf4 \ucc3e\uae30 +function.searchwikipedianames=\uc774\ub984\uc73c\ub85c \uc704\ud0a4\ud53c\ub514\uc544 \uac80\uc0c9 +function.downloadosm=OSM \ub370\uc774\ud0c0 \ub2e4\uc6b4\ub85c\ub4dc\ud558\uae30 +function.duplicatepoint=\uc9c0\uc810 \ubcf5\uc0ac\ud558\uae30 +function.setcolours=\uc0c9\uc0c1 \uc9c0\uc815 +function.setlinewidth=\uc120 \ub113\uc774 \uc9c0\uc815 +function.setlanguage=\uc5b8\uc5b4 \uc9c0\uc815 +function.connecttopoint=\uc9c0\uc810\uc73c\ub85c \uc5f0\uacb0 +function.disconnectfrompoint=\uc9c0\uc810\uc5d0\uc11c \uc5f0\uacb0 \ub04a\uae30 +function.removephoto=\ub9ac\uc2a4\ud2b8\uc5d0\uc11c \uc0ac\uc9c4 \uc9c0\uc6b0\uae30 +function.correlatephotos=\uc2dc\uac04\uc815\ubcf4\ub85c \uc0ac\uc9c4 \uc5f0\uacb0\ud558\uae30 +function.rearrangephotos=\uc0ac\uc9c4 \uc7ac\ubc30\uc5f4\ud558\uae30 +function.rotatephotoleft=\uc67c\ucabd\uc73c\ub85c \uc0ac\uc9c4 \ud68c\uc804 +function.rotatephotoright=\uc624\ub978\ucabd\uc73c\ub85c \uc0ac\uc9c4 \ud68c\uc804 +function.photopopup=\ud31d\uc5c5 \uc0ac\uc9c4 \ubcf4\uae30 +function.ignoreexifthumb=exif \uc378\ub124\uc77c \ubb34\uc2dc\ud558\uae30 +function.loadaudio=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ud558\uae30 +function.removeaudio=\ub9ac\uc2a4\ud2b8\uc5d0\uc11c \uc18c\ub9ac\ud30c\uc77c \uc9c0\uc6b0\uae30 +function.correlateaudios=\uc2dc\uac04\uc815\ubcf4\ub85c \uc18c\ub9ac\ud30c\uc77c \uc5f0\uacb0\ud558\uae30 +function.playaudio=\uc18c\ub9ac\ud30c\uc77c \uc7ac\uc0dd +function.stopaudio=\uc18c\ub9ac\ud30c\uc77c \uba48\ucda4 +function.help=\ub3c4\uc6c0\ub9d0 +function.showkeys=\ub2e8\ucd95\ud0a4 \ubcf4\uae30 +function.about=Prune \uc815\ubcf4 +function.checkversion=\uc0c8\ubc84\uc804\uc774 \uc788\ub294\uc9c0 \uac80\uc0ac +function.saveconfig=\uc124\uc815 \uc800\uc7a5\ud558\uae30 +function.diskcache=\ub514\uc2a4\ud06c\uc5d0 \ub9f5 \uc800\uc7a5 + +# Dialogs +dialog.exit.confirm.title=Prune \uc885\ub8cc +dialog.exit.confirm.text=\ub370\uc774\ud0c0\uac00 \uc800\uc7a5\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uadf8\ub798\ub3c4 \ub098\uac00\uc2dc\uac8c\uc2b5\ub2c8\uae4c? +dialog.openappend.title=\ub370\uc774\ud130 \ucd94\uac00\ud558\uae30 +dialog.openappend.text=\uc774\uc804\uc5d0 \ubd88\ub7ec\uc628 \ub370\uc774\ud0c0\ub85c \ucd94\uac00\ud558\uae30\uaca0\uc2b5\ub2c8\uae4c? +dialog.deletepoint.title=\uc9c0\uc810 \uc0ad\uc81c +dialog.deletepoint.deletephoto=\uc9c0\uc810\uc73c\ub85c \ucca8\ubd80\ub41c \uc0ac\uc9c4\uc744 \uc9c0\uc6b0\uae30\uaca0\uc2b5\ub2c8\uae4c? +dialog.deletephoto.title=\uc0ac\uc9c4 \uc0ad\uc81c +dialog.deletephoto.deletepoint=\uc0ac\uc9c4\uc5d0 \ucca8\ubd80\ub41c \uc9c0\uc810\uc744 \uc9c0\uc6b0\uc2dc\uaca0\uc2b5\ub2c8\uae4c? +dialog.openoptions.title=\uc124\uc815 +dialog.openoptions.filesnippet=\ud30c\uc77c\uc5d0\uc11c \ucd94\ucd9c\ud558\uae30 +dialog.load.table.field=\ud544\ub4dc +dialog.load.table.datatype=\ub370\uc774\ud0c0 \ud0c0\uc785 +dialog.load.table.description=\uc124\uba85 +dialog.delimiter.label=\ud544\ub4dc \uad6c\ubd84 \uae30\ud638 +dialog.delimiter.comma=\ucf64\ub9c8 , +dialog.delimiter.tab=\ud0ed +dialog.delimiter.space=\ube48\uce78 +dialog.delimiter.semicolon=\uc138\ubbf8\ucf5c\ub860 ; +dialog.delimiter.other=\uae30\ud0c0 +dialog.openoptions.deliminfo.records=\uac1c\uc758 \ub808\ucf54\ub4dc +dialog.openoptions.deliminfo.fields=\uac1c\uc758 \ud544\ub4dc\uac00 \uc788\ub294 +dialog.openoptions.deliminfo.norecords=\ub808\ucf54\ub4dc\uac00 \uc5c6\uc74c +dialog.openoptions.altitudeunits=\uace0\ub3c4 \ub2e8\uc704 +dialog.open.contentsdoubled=\uc774 \ud30c\uc77c\uc740 \uac01 \uc9c0\uc810\uc774 2\ubc88\uc529 \uae30\ub85d\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4. /n\uac01 \uc9c0\uc810\uacfc \uac01 \uacbd\uc720\uc9c0\ub294 \ud55c\ubc88\uc529\ub9cc \uae30\ub85d\ub418\uc5b4\uc57c \ud569\ub2c8\ub2e4. +dialog.selecttracks.intro=\ubd88\ub7ec\uc62c \uc9c0\uc810\uc774\ub098 \uc9c0\uc810\ub4e4\uc744 \uc120\ud0dd\ud558\uc138\uc694 +dialog.selecttracks.noname=\uc774\ub984 \uc5c6\uc74c +dialog.jpegload.subdirectories=\ud558\uc704 \ud3f4\ub354\ub3c4 \ud3ec\ud568 +dialog.jpegload.loadjpegswithoutcoords=\uc88c\ud45c \uc815\ubcf4\uc5c6\ub294 \uc0ac\uc9c4\ub4e4\ub3c4 \ud3ec\ud568 +dialog.jpegload.loadjpegsoutsidearea=\uc9c0\uae08 \uc9c0\uc5ed\uc678\ubd80 \uc0ac\uc9c4\ub4e4\ub3c4 \ud3ec\ud568 +dialog.jpegload.progress.title=\uc0ac\uc9c4\uc744 \ubd88\ub7ec\uc624\uace0 \uc788\uc2b5\ub2c8\ub2e4. +dialog.jpegload.progress=\uc0ac\uc9c4\uc774 \uac80\uc0c9\ub418\ub294 \ub3d9\uc548 \uae30\ub2e4\ub824 \uc8fc\uc138\uc694. +dialog.gpsload.nogpsbabel=gpsbabel \ud504\ub85c\uadf8\ub7a8\uc744 \ucc3e\uc9c0 \ubabb\ud558\uc600\uc2b5\ub2c8\ub2e4. \uacc4\uc18d \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? +dialog.gpsload.device=\uc7a5\uce58 \uc774\ub984 +dialog.gpsload.format=\ud615\ud0dc +dialog.gpsload.getwaypoints=\uacbd\uc720\uc9c0 \ubd88\ub7ec\uc624\uae30 +dialog.gpsload.gettracks=\ud2b8\ub809 \ubd88\ub7ec\uc624\uae30 +dialog.gpsload.save=\ud30c\uc77c\ub85c \uc800\uc7a5 +dialog.gpssend.sendwaypoints=\uacbd\uc720\uc9c0 \ubcf4\ub0b4\uae30 +dialog.gpssend.sendtracks=\ud2b8\ub809 \ubcf4\ub0b4\uae30 +dialog.gpssend.trackname=\ud2b8\ub809 \uc774\ub984 +dialog.saveoptions.title=\ud30c\uc77c \uc800\uc7a5 +dialog.save.fieldstosave=\ud544\ub4dc\uba85\uc73c\ub85c \uc800\uc7a5 +dialog.save.table.field=\ud544\ub4dc +dialog.save.table.hasdata=\uc790\ub8cc\uac00 \uc788\uc74c +dialog.save.table.save=\uc800\uc7a5 +dialog.save.headerrow=\uccab\ubc88\uc9f8 \uc904\ub3c4 \uc800\uc7a5 +dialog.save.coordinateunits=\uc88c\ud45c \ub2e8\uc704 +dialog.save.altitudeunits=\uace0\ub3c4 \ub2e8\uc704 +dialog.save.timestampformat=\uc2dc\uac04 \ud45c\ud604\ud615\uc2dd +dialog.save.overwrite.title=\ud30c\uc77c\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4. +dialog.save.overwrite.text=\ud30c\uc77c\uc774 \uc774\ubbf8 \uc788\uc2b5\ub2c8\ub2e4. \uadf8\ub798\ub3c4 \uc774 \ud30c\uc77c\uc5d0 \ub36e\uc5b4\uc50c\uc6b0\uc2dc\uaca0\uc2b5\ub2c8\uae4c? +dialog.save.notypesselected=\uc9c0\uc810\uc758 \ud615\uc2dd\uc774 \uc9c0\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. +dialog.exportkml.text=\uc81c\ubaa9\uc73c\ub85c \uc800\uc7a5 +dialog.exportkml.altitude=\uace0\ub3c4 \uace0\uc815(\ube44\ud589) +dialog.exportkml.kmz=kmz \ud30c\uc77c \uc0dd\uc131\uc2dc \uc555\ucd95\ud558\uae30 +dialog.exportkml.exportimages=kmz\ub85c \uc791\uc740 \uc0ac\uc9c4 \ub0b4\ubcf4\ub0b4\uae30 +dialog.exportkml.trackcolour=\ud2b8\ub809 \uc0c9 +dialog.exportgpx.name=\uc774\ub984 +dialog.exportgpx.desc=\uc124\uba85 +dialog.exportgpx.includetimestamps=\uc2dc\uac04 \ud3ec\ud568\ud558\uae30 +dialog.exportgpx.copysource=xml \ud30c\uc77c \ubcf5\uc0ac\ud558\uae30 +dialog.exportpov.text=POV\ub85c \ub0b4\ubcf4\ub0bc \ubcc0\uc218\ub97c \uc801\uc5b4\uc8fc\uc138\uc694. +dialog.exportpov.font=\uae00\uaf34 +dialog.exportpov.camerax=\uce74\uba54\ub77c\uc758 X \uc88c\ud45c +dialog.exportpov.cameray=\uce74\uba54\ub77c\uc758 Y \uc88c\ud45c +dialog.exportpov.cameraz=\uce74\uba54\ub77c\uc758 Z \uc88c\ud45c +dialog.exportpov.modelstyle=\ubaa8\ub378 \uc2a4\ud0c0\uc77c +dialog.exportpov.ballsandsticks=\ub9c9\ub300\uae30\uc640 \uacf5 +dialog.exportpov.tubesandwalls=\ubcbd\uacfc \ud29c\ube0c +dialog.exportpov.warningtracksize=\uc774 \ud2b8\ub799\uc740 \uc9c0\uc810\uc774 \ub108\ubb34 \ub9ce\uc544 Java3D\uac00 \ud45c\ud604 \ubabb\ud560 \uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uacc4\uc18d \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c? +dialog.exportsvg.text=SVG\ub85c \ub0b4\ubcf4\ub0bc \ud30c\ub77c\ubbf8\ud130\ub97c \uc120\ud0dd\ud558\uc138\uc694 +dialog.exportsvg.phi=\ubc29\uc704\uac01(\u03d5) +dialog.exportsvg.theta=\uace0\ub3c4(\u03b8) +dialog.exportsvg.gradients=\uc250\uc774\ub529\uc5d0 \uadf8\ub798\ub514\uc5b8\ud2b8 \uc0ac\uc6a9\ud558\uae30 +dialog.pointtype.desc=\uc9c0\uc810 \ud615\uc2dd \uc800\uc7a5\ud558\uae30 +dialog.pointtype.track=\ud2b8\ub799 \uc9c0\uc810 +dialog.pointtype.waypoint=\uacbd\uc720 \uc9c0\uc810 +dialog.pointtype.photo=\uc0ac\uc9c4 \uc9c0\uc810 +dialog.pointtype.audio=\uc18c\ub9ac \uc9c0\uc810 +dialog.pointtype.selection=\uc120\ud0dd \uc601\uc5ed\ub9cc +dialog.confirmreversetrack.title=\ubc18\uc804\uc778\uc9c0 \ud655\uc778 +dialog.confirmreversetrack.text=\uc774 \ud2b8\ub799\uc740 \ubc18\uc804\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe8\uc744 \uc218\ub3c4 \uc788\ub294\uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774\uc601\uc5ed\uc744 \ubc18\uc804\uc2dc\ud0a4\uc2dc\ub824\ub098\uc694? +dialog.confirmcutandmove.title=\uc790\ub974\uace0 \uc62e\uae30\uae30 \ud655\uc778 +dialog.confirmcutandmove.text=\uc774 \ud2b8\ub799\uc740 \uc774\ub3d9\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe7\uc744 \uc218\ub3c4 \uc788\ub294 \uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774 \uc601\uc5ed\uc744 \uc62e\uae30\uc2dc\ub824\ub098\uc694? +dialog.interpolate.title=\uc0bd\uc785\ud55c \uc9c0\uc810 +dialog.interpolate.parameter.text=\uc120\ud0dd\ud55c \uc9c0\uc810 \uc0ac\uc774\uc5d0 \ub123\uc744 \uc9c0\uc810\uc758 \uc218 +dialog.undo.title=\uc791\uc5c5 \ub418\ub3cc\ub9ac\uae30 +dialog.undo.pretext=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694 +dialog.undo.none.title=\ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694 +dialog.undo.none.text=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc774 \uc5c6\uc5b4\uc694! +dialog.clearundo.title=\ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uae30 +dialog.clearundo.text=\uc815\ub9d0\ub85c \ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uc2e4\uac74\uac00\uc694? /n \ubaa8\ub4e0 \ub418\ub3cc\ub9ac\uae30 \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc9c4\ub2e4\uad6c\uc694! +dialog.pointedit.title=\uc9c0\uc810 \uc218\uc815\ud558\uae30 +dialog.pointedit.text=\uc218\uc815\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd\ud558\uc2dc\uace0, \uc218\uc815\ud558\uae30 \ubc84\ud2bc\uc744 \uc0ac\uc6a9\ud558\uc138\uc694. +dialog.pointedit.table.field=\ud544\ub4dc +dialog.pointedit.table.value=\uac12 +dialog.pointedit.table.changed=\uc218\uc815\ub428 +dialog.pointedit.changevalue.text=\uc774 \ud544\ub4dc\uc5d0 \uc0c8 \uac12\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694 +dialog.pointedit.changevalue.title=\ud544\ub4dc \uc218\uc815 +dialog.pointnameedit.name=\uacbd\uc720\uc9c0 \uc774\ub984 +dialog.pointnameedit.uppercase=\ub300\ubb38\uc790\ub85c +dialog.pointnameedit.lowercase=\uc18c\ubb38\uc790\ub85c +dialog.pointnameedit.sentencecase=\uccab\uae00\uc790\ub9cc \ub300\ubb38\uc790\ub85c +dialog.addtimeoffset.add=\uc2dc\uac04 \ucd94\uac00 +dialog.addtimeoffset.subtract=\uc2dc\uac04 \ube7c\uae30 +dialog.addtimeoffset.days=\uc77c +dialog.addtimeoffset.hours=\uc2dc +dialog.addtimeoffset.minutes=\ubd84 +dialog.addtimeoffset.notimestamps=\uc2dc\uac04 \uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc9c0\uc54a\uc544\uc11c \uc2dc\uac04\uc624\ud504\uc14b\uc744 \ucd94\uac00\ud560 \uc218 \uc5c6\ub124\uc694. +dialog.findwaypoint.intro=\uacbd\uc720\uc9c0 \uc774\ub984\uc744 \uc801\uc73c\uc138\uc694. +dialog.findwaypoint.search=\ucc3e\uae30 +dialog.saveexif.title=Exif \uc800\uc7a5 +dialog.saveexif.intro=\uccb4\ud06c\ubc15\uc2a4\ub97c \uc774\uc6a9\ud574\uc11c \uc800\uc7a5\ud560 \uc0ac\uc9c4\uc744 \uc120\ud0dd\ud558\uc138\uc694. +dialog.saveexif.nothingtosave=\uc88c\ud45c \ub370\uc774\ud0c0\uac00 \ubc14\ub00c\uc9c0 \uc54a\uc558\uc5b4\uc694. \uc800\uc7a5\ud560 \uac83\uc774 \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694. +dialog.saveexif.noexiftool=exiftool \ud504\ub85c\uadf8\ub7a8\uc744 \ucc3e\uc9c0 \ubabb\ud588\uc5b4\uc694. \uacc4\uc18d \ud558\uc2dc\uaca0\uc5b4\uc694? +dialog.saveexif.table.photoname=\uc0ac\uc9c4 \uc774\ub984 +dialog.saveexif.table.status=\uc0c1\ud0dc +dialog.saveexif.table.save=\uc800\uc7a5 +dialog.saveexif.photostatus.connected=\uc9c0\uc810\uacfc \uc5f0\uacb0\ub428 +dialog.saveexif.photostatus.disconnected=\uc9c0\uac80\uacfc\uc758 \uc5f0\uacb0 \ub04a\uae40 +dialog.saveexif.photostatus.modified=\uc218\uc815\ub428 +dialog.saveexif.overwrite=\ud30c\uc77c \ub36e\uc5b4\uc4f0\uae30 +dialog.saveexif.force=\uc624\ub958\ubb34\uc2dc\ud558\uae30 +dialog.charts.xaxis=x \ucd95 +dialog.charts.yaxis=y \ucd95 +dialog.charts.output=\ucd9c\ub825 +dialog.charts.screen=\ud654\uba74\uc73c\ub85c \ucd9c\ub825 +dialog.charts.svg=SVG\ud30c\uc77c\ub85c \ucd9c\ub825 +dialog.charts.svgwidth=SVG \ud3ed +dialog.charts.svgheight=SVG \ub192\uc774 +dialog.charts.needaltitudeortimes=\ucc28\ud2b8\ub9cc\ub4e4\ub54c \ud2b8\ub809\uc5d0\ub294 \uace0\ub3c4\uc640 \uc2dc\uac04 \uc815\ubcf4\uac00 \ud544\uc694\ud574\uc694. +dialog.charts.gnuplotnotfound=\uc54c\ub824\uc900\uacbd\ub85c\uc5d0\uc11c gnuplot\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc5b4\uc694. +dialog.distances.intro=\uc9c0\uc810\uc0ac\uc774\uc758 \uc9c1\uc120 \uac70\ub9ac +dialog.distances.column.from=\uc9c0\uac80\uc5d0\uc11c +dialog.distances.column.to=\uc9c0\uc810\uae4c\uc9c0 +dialog.distances.currentpoint=\uc9c0\uae08 \uc9c0\uc810 +dialog.distances.toofewpoints=\uacbd\uc720\uc9c0 \uc0ac\uc774\uc758 \uac70\ub9ac\ub97c \uc54c\ub824\uba74 \uacbd\uc720\uc9c0\uac00 2\uac1c\uc774\uc0c1 \ud544\uc694\ud574\uc694. +dialog.fullrangedetails.intro=\uc120\ud0dd\ub41c \ubc94\uc704\uc758 \uc790\uc138\ud55c \uc815\ubcf4 +dialog.setmapbg.intro=\ub9f5 \uc18c\uc2a4 \ud558\ub098\ub97c \uace0\ub974\uac70\ub098 \uc0c8\ub85c \ucd94\uac00\ud558\uc138\uc694. +dialog.addmapsource.title=\uc0c8 \ub9f5 \uc18c\uc2a4 \ucd94\uac00\ud558\uae30 +dialog.addmapsource.sourcename=\uc18c\uc2a4 \uc774\ub984 +dialog.addmapsource.layer1url=\uccab \ub808\uc774\uc5b4\uc758 URL +dialog.addmapsource.layer2url=\ub450\ubc88\uc9f8 \ub808\uc774\uc5b4\uc758 URL +dialog.addmapsource.maxzoom=\ucd5c\uace0 \ud655\ub300 +dialog.addmapsource.cloudstyle=\uc2a4\ud0c0\uc77c \uc218 +dialog.addmapsource.noname=\uc774\ub984 \uc5c6\uc74c +dialog.gpsies.column.name=\ud2b8\ub809 \uc774\ub984 +dialog.gpsies.column.length=\uae38\uc774 +dialog.gpsies.description=\uc124\uba85 +dialog.gpsies.nodescription=\uc124\uba85 \uc5c6\uc74c +dialog.gpsies.nonefound=\ud2b8\ub799\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc74c +dialog.gpsies.username=Gpsies username +dialog.gpsies.password=Gpsies \ube44\ubc00\ubc88\ud638 +dialog.gpsies.keepprivate=\ud2b8\ub799 \uac1c\uc778\uc815\ubcf4\ubcf4\ud638 +dialog.gpsies.confirmopenpage=\uc5c5\ub85c\ub4dc\ud55c \ud2b8\ub799\uc744 \uc6f9\ud398\uc774\uc9c0\uc5d0\uc11c \ubcf4\uc2dc\uaca0\uc5b4\uc694? +dialog.gpsies.activities=\ud65c\ub3d9 \ud615\ud0dc +dialog.gpsies.activity.trekking=\ub3c4\ubcf4\uc5ec\ud589 +dialog.gpsies.activity.walking=\uac77\uae30 +dialog.gpsies.activity.jogging=\ub2ec\ub9ac\uae30 +dialog.gpsies.activity.biking=\uc790\uc804\uac70\ud0c0\uae30 +dialog.gpsies.activity.motorbiking=\uc624\ud1a0\ubc14\uc774\ud0c0\uae30 +dialog.gpsies.activity.snowshoe=\ub208\uae38\uac77\uae30 +dialog.gpsies.activity.sailing=\ubc30\ud0c0\uae30 +dialog.gpsies.activity.skating=\uc2a4\ucf00\uc774\ud2b8\ud0c0\uae30 +dialog.wikipedia.column.name=\uac8c\uc2dc\ubb3c \uc774\ub984 +dialog.wikipedia.column.distance=\uac70\ub9ac +dialog.correlate.notimestamps=\uc9c0\uc810 \ub370\uc774\ud130\uc5d0 \uc2dc\uac04\uc815\ubcf4\uac00 \uc5c6\uc5b4\uc11c \uc0ac\uc9c4\uc744 \uc5f0\uacb0 \ud560 \uc218 \uc5c6\uc5b4\uc694. +dialog.correlate.nouncorrelatedphotos=\uc5f0\uacb0\uc774 \uc548\ub41c \uc0ac\uc9c4\uc774 \uc5c6\ub124\uc694. /n \uacc4\uc18d \ud558\uc2e4\uac70\uc8e0? +dialog.correlate.photoselect.intro=\ub300\ud45c\uc0ac\uc9c4\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc0ac\uc9c4\uc744 \ud558\ub098 \uc120\ud0dd\ud558\uc138\uc694. +dialog.correlate.select.photoname=\uc0ac\uc9c4\uc774\ub984 +dialog.correlate.select.timediff=\uc0ac\uae34 \ucc28\uc774 +dialog.correlate.select.photolater=\uc0ac\uc9c4 \uc2dc\uac04\uc774 \uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \ub290\ub9bc +dialog.correlate.options.tip=\ub3c4\uc6c0: \ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694. +dialog.correlate.options.intro=\uc790\ub3d9 \uc5f0\uacb0\uc744 \uc704\ud574 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694. +dialog.correlate.options.offsetpanel=\uc2dc\uac04 \uc624\ud504\uc14b +dialog.correlate.options.offset=\uc624\ud504\uc14b +dialog.correlate.options.offset.hours=\uc2dc\uac04 +dialog.correlate.options.offset.minutes=\ubd84 +dialog.correlate.options.offset.seconds=\ucd08 +dialog.correlate.options.photolater=\uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \uc0ac\uc9c4\uc2dc\uac04\uc774 \ub4a4\uc784 +dialog.correlate.options.pointlaterphoto=\uc0ac\uc9c4\uc2dc\uac04\ubcf4\ub2e4 \uc9c0\uc810\uc2dc\uac04\uc774 \ub4a4\uc784 +dialog.correlate.options.audiolater=\uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \uc18c\ub9ac\uc2dc\uac04\uc774 \ub4a4\uc784 +dialog.correlate.options.pointlateraudio=\uc18c\ub9ac\uc2dc\uac04\ubcf4\ub2e4 \uc9c0\uc810\uc2dc\uac04\uc774 \ub4a4\uc784 +dialog.correlate.options.limitspanel=\uc5f0\uacb0 \uc81c\ud55c +dialog.correlate.options.notimelimit=\uc2dc\uac04 \uc81c\ud55c \uc5c6\uc74c +dialog.correlate.options.timelimit=\uc2dc\uac04 \uc81c\ud55c +dialog.correlate.options.nodistancelimit=\uac70\ub9ac\uc81c\ud55c \uc5c6\uc74c +dialog.correlate.options.distancelimit=\uac70\ub9ac\uc81c\ud55c +dialog.correlate.options.correlate=\uc5f0\uacb0 +dialog.correlate.alloutsiderange=\ubaa8\ub4e0 \uc0ac\uc9c4\ub4e4\uc774 \ud2b8\ub809\uc758 \uc2dc\uac04 \ubc94\uc704\uc548\uc5d0 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4./n\uc624\ud504\uc14b\uc744 \uc218\uc815\ud574\ubcf4\uc2dc\uac70\ub098 \ucd5c\uc18c\ud55c \ud55c \uc7a5\uc758 \uc0ac\uc9c4\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud574\ubcf4\uc138\uc694. +dialog.correlate.filetimes=\ud30c\uc77c\uc758 \uc2dc\uac04\uc774 \ub098\ud0c0\ub0b4\ub294 : +dialog.correlate.filetimes2=\uc18c\ub9ac\ud30c\uc77c\uc758 +dialog.correlate.correltimes=\uc5f0\uacb0\uc744 \uc0ac\uc6a9\ud558\uc138\uc694 +dialog.correlate.timestamp.beginning=\uc2dc\uc791 +dialog.correlate.timestamp.middle=\uc911\uac04 +dialog.correlate.timestamp.end=\ub05d +dialog.correlate.audioselect.intro=\ud0c0\uc784\uc624\ud504\uc14b\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc5f0\uacb0\ub41c \uc18c\ub9ac\ub4e4\uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc138\uc694 +dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984 +dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0 +dialog.rearrangephotos.desc=\uc0ac\uc9c4 \uc9c0\uc810\ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc815\ub82c\ud560 \uac74\uc9c0 \uc120\ud0dd\ud574\uc8fc\uc138\uc694. +dialog.rearrangephotos.tostart=\uc2dc\uc791\uc73c\ub85c +dialog.rearrangephotos.toend=\ub05d\uc73c\ub85c +dialog.rearrangephotos.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30 +dialog.rearrangephotos.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c +dialog.rearrangephotos.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c +dialog.compress.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4. +dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70 +dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218 +dialog.compress.wackypoints.title=\uc5c9\ub6b1\ud558\uac70\ub098 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uc9c0\uc810 \uc81c\uac70 +dialog.compress.wackypoints.paramdesc=\uac70\ub9ac \uacc4\uc218 +dialog.compress.singletons.title=\ud558\ub098\uc529 \uc81c\uac70 +dialog.compress.singletons.paramdesc=\uac70\ub9ac \uacc4\uc218 +dialog.compress.duplicates.title=\ubcf5\uc0ac\ub41c\uac70 \uc81c\uac70 +dialog.compress.summarylabel=\uc0ad\uc81c\ud560 \uc9c0\uc810 +dialog.pastecoordinates.desc=\uc790\ud45c\ub97c \ub123\uc73c\uc138\uc694 +dialog.pastecoordinates.coords=\uc88c\ud45c +dialog.pastecoordinates.nothingfound=\uc88c\ud45c\ub97c \ud655\uc778\ud558\uc2dc\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \ubcf4\uc138\uc694. +dialog.help.help=http://activityworkshop.net/software/prun +dialog.about.version=\ubc84\uc804 +dialog.about.build=\ube4c\ub4dc +dialog.about.summarytext1=Prune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4. +dialog.about.summarytext2=\uc774 \ud504\ub85c\uadf8\ub7a8\uc740 \uc790\uc720\ub86d\uac8c, \uac1c\ubc29\uc801\uc73c\ub85c, \uadf8\ub9ac\uace0 \uc804\uc138\uacc4\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uace0 \uac1c\uc120\ud558\uae30 \uc704\ud574 Gnu GPL \uc5d0 \ub530\ub77c\uc11c \ubc30\ud3ec\ub429\ub2c8\ub2e4.
\uc774 \ud504\ub85c\uadf8\ub7a8\uc5d0 \ud3ec\ud568\ub41c license.txt \ud30c\uc77c\uc758 \uc870\uac74\uc5d0 \ub530\ub77c \ubcf5\uc81c, \uc7ac\ubc30\ud3ec, \uc218\uc815\uc774 \ud5c8\uac00\ub418\uace0, \uc7a5\ub824\ub429\ub2c8\ub2e4. +dialog.about.summarytext3=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.http://activityworkshop.net/ +dialog.about.languages=\uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc5b8\uc5b4\ub4e4 +dialog.about.translatedby=\ud55c\uad6d\uc5b4 Hooau +dialog.about.systeminfo=\uc2dc\uc2a4\ud15c \uc815\ubcf4 +dialog.about.systeminfo.os=\uc6b4\uc601\uccb4\uc81c +dialog.about.systeminfo.java=\uc790\ubc14 \ub7f0\ud0c0\uc784 +dialog.about.systeminfo.java3d=Java3D\uac00 \uc124\uce58\ub418\uc5c8\uc74c +dialog.about.systeminfo.povray=Povray\uac00 \uc124\uce58\ub418\uc5c8\uc74c +dialog.about.systeminfo.exiftool=Exiftool\uc774 \uc124\uce58\ub418\uc5c8\uc74c +dialog.about.systeminfo.gpsbabel=Gpsbabel\uc774 \uc124\uce58\ub418\uc5c8\uc74c +dialog.about.systeminfo.gnuplot=Gnuplot\uc774 \uc124\uce58\ub418\uc5c8\uc74c +dialog.about.systeminfo.exiflib=Exif \ub77c\uc774\ube0c\ub7ec\ub9ac +dialog.about.systeminfo.exiflib.internal=\ub0b4\uc7a5 +dialog.about.systeminfo.exiflib.internal.failed=\ub0b4\uc7a5(\ucc3e\uc9c0\ubabb\ud568) +dialog.about.systeminfo.exiflib.external=\uc678\uc7a5 +dialog.about.systeminfo.exiflib.external.failed=\uc678\uc7a5(\ucc3e\uc9c0\ubabb\ud568) +dialog.about.yes=\uc608 +dialog.about.no=\uc544\ub2c8\uc624 +dialog.about.credits=\uc5d0\uac8c \uac10\uc0ac\ub97c +dialog.about.credits.code=Prune \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900 +dialog.about.credits.exifcode=Exif \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900 +dialog.about.credits.icons=\uc77c\ubd80 \uc544\uc774\ucf58\uc744 \uac00\uc838\uc628 +dialog.about.credits.translators=\ubc88\uc5ed\uc790\ub4e4 +dialog.about.credits.translations=\ubc88\uc5ed\uc5d0 \ub3c4\uc6c0\uc744 \uc900 +dialog.about.credits.devtools=\uac1c\ubc1c \ub3c4\uad6c\ub4e4 +dialog.about.credits.othertools=\ub2e4\ub978 \ub3c4\uad6c\ub4e4 +dialog.about.credits.thanks=\uac10\uc0ac\ud569\ub2c8\ub2e4. +dialog.about.readme=\uc77d\uc5b4\uc8fc\uc138\uc694 +dialog.checkversion.error=\ubc84\uc804\uc774 \ud655\uc778\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694./n \uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694. +dialog.checkversion.uptodate=\ub2f9\uc2e0\uc740 Prune\uc758 \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud558\uace0 \uacc4\uc2ed\ub2c8\ub2e4. +dialog.checkversion.newversion1=Prune\uc758 \uc0c8 \ubc84\uc804\uc744 \uc9c0\uae08 \uc0ac\uc6a9\ud560 \uc218 \uc788\uaca0\ub124\uc694. \ucd5c\uc2e0\ubc84\uc804\uc740 +dialog.checkversion.newversion2=\ubc84\uc804\uc785\ub2c8\ub2e4. +dialog.checkversion.releasedate1=\uc0c8 \ubc84\uc804\uc740 +dialog.checkversion.releasedate2=\uc5d0 \ubc30\ud3ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4. +dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/prune/download.html. +dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc\uace0 \uc544\ub798 \ub2e8\ucd95\ud0a4\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc138\uc694. +dialog.keys.keylist=
\ud654\uc0b4\ud45c \ud0a4\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9
Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9
Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c
Ctrl + PgUp, PgDown\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd
Ctrl + Home, End\ucc98\uc74c \uc9c0\uc810, \ub9c8\uc9c0\ub9c9 \uc9c0\uc810 \uc120\ud0dd
Del\ud604\uc7ac \uc9c0\uc810 \uc0ad\uc81c
+dialog.keys.normalmodifier=Ctrl +dialog.keys.macmodifier=Command +dialog.saveconfig.desc=\uc544\ub798 \uc124\uc815\ub4e4\uc740 \uc124\uc815\ud30c\uc77c\uc5d0 \uc800\uc7a5\ud560 \uc218 \uc788\uc5b4\uc694: +dialog.saveconfig.prune.trackdirectory=\ud2b8\ub809 \ud3f4\ub354 +dialog.saveconfig.prune.photodirectory=\uc0ac\uc9c4 \ud3f4\ub354 +dialog.saveconfig.prune.languagecode=\uc5b8\uc5b4\ucf54\ub4dc (KO) +dialog.saveconfig.prune.languagefile=\uc5b8\uc5b4\ud30c\uc77c +dialog.saveconfig.prune.gpsdevice=GPS\uc7a5\uce58 +dialog.saveconfig.prune.gpsformat=GPS \ud615\uc2dd +dialog.saveconfig.prune.povrayfont=Povray \uae00\uaf34 +dialog.saveconfig.prune.metricunits=\ubbf8\ud130\ubc95 \ub2e8\uc704\ub97c \uc0ac\uc6a9\ud558\uc2dc\ub098\uc694? +dialog.saveconfig.prune.gnuplotpath=gnjuplot \uacbd\ub85c +dialog.saveconfig.prune.gpsbabelpath=gpsbabel \uacbd\ub85c +dialog.saveconfig.prune.exiftoolpath=exiftool \uacbd\ub85c +dialog.saveconfig.prune.mapsource=\uc120\ud0dd\ub41c \uc9c0\ub3c4 \uc704\uce58 +dialog.saveconfig.prune.mapsourcelist=\uc9c0\ub3c4 \uc18c\uc2a4 +dialog.saveconfig.prune.diskcache=\uc9c0\ub3c4 \uce90\uc2dc +dialog.saveconfig.prune.kmzimagewidth=KMZ \uc774\ubbf8\uc9c0 \ub113\uc774 +dialog.saveconfig.prune.kmzimageheight=KMZ \uc774\ubbf8\uc9c0 \ub192\uc774 +dialog.saveconfig.prune.colourscheme=\uc0c9 \uad6c\uc131 +dialog.saveconfig.prune.linewidth=\ud2b8\ub799\uc120 \ub450\uaed8 +dialog.saveconfig.prune.kmltrackcolour=KML \ud2b8\ub799 \uc0c9 +dialog.setpaths.intro=\uc678\ubd80 \ud504\ub85c\uadf8\ub7a8\uc758 \uacbd\ub85c\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\uc5b4\uc694. +dialog.setpaths.found=\uacbd\ub85c\ub97c \ucc3e\uc73c\uc168\ub098\uc694? +dialog.addaltitude.noaltitudes=\uc120\ud0dd\ub41c \ubc94\uc704\uc5d0 \uace0\ub3c4\uac00 \ud3ec\ud568\ub418\uc5b4\uc788\uc9c0 \uc54a\ub124\uc694. +dialog.addaltitude.desc=\ucd94\uac00\ud560 \uace0\ub3c4 \uc624\ud504\uc14b +dialog.lookupsrtm.overwritezeros=\uace0\ub3c4 \uac12\uc744 0\uc73c\ub85c \ub36e\uc5b4\uc4f0\uc2dc\uaca0\uc2b5\ub2c8\uae4c? +dialog.setcolours.intro=\ubc14\uafc0 \uc0c9\uc73c\ub85c \uc0c9 \ud328\uce58\uc5d0\uc11c \ud074\ub9ad\ud558\uc138\uc694. +dialog.setcolours.background=\ubc30\uacbd +dialog.setcolours.borders=\ud14c\ub450\ub9ac +dialog.setcolours.lines=\uc120 +dialog.setcolours.primary=1\ucc28 +dialog.setcolours.secondary=2\ucc28 +dialog.setcolours.point=\uc9c0\uc810 +dialog.setcolours.selection=\uc120\ud0dd +dialog.setcolours.text=\uae00\uc790 +dialog.colourchooser.title=\uc0c9\uc0c1 \uc120\ud0dd +dialog.colourchooser.red=\ube68\uac15 +dialog.colourchooser.green=\ub179\uc0c9 +dialog.colourchooser.blue=\ud30c\ub791 +dialog.setlanguage.firstintro=\ud3ec\ud568\ub41c \uc5b8\uc5b4\ub4e4 \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc2e4 \uc218 \uc788\uc5b4\uc694,

\ud639\uc740 \ub300\uc2e0 \uc0ac\uc6a9\ud560 \ud14d\uc2a4\ud2b8 \ud30c\uc77c\uc744 \uc120\ud0dd\ud558\uc138\uc694. +dialog.setlanguage.secondintro=\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0,

\uc5b8\uc5b4\ub97c \ubc14\uafb8\uae30\uc704\ud574\uc11c\ub294 Prune\uc744 \uc7ac\uc2dc\uc791\ud558\uc154\uc57c \ud574\uc694. +dialog.setlanguage.language=\uc5b8\uc5b4 +dialog.setlanguage.languagefile=\uc5b8\uc5b4 \ud30c\uc77c +dialog.setlanguage.endmessage=\ubcc0\uacbd\ub41c \uc5b8\uc5b4\ub97c \uc801\uc6a9\ud558\uc2e4\ub824\uba74/n\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0 Prune\uc744 \uc7ac\uc2dc\uc791\ud558\uc138\uc694. +dialog.diskcache.save=\ub514\uc2a4\ud06c\ub85c \uc9c0\ub3c4 \uc774\ubbf8\uc9c0\ub97c \uc800\uc7a5 +dialog.diskcache.dir=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac +dialog.diskcache.createdir=\ub514\ub809\ud1a0\ub9ac \ub9cc\ub4e4\uae30 +dialog.diskcache.nocreate=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac\uac00 \ub9cc\ub4e4\uc5b4\uc9c0\uc9c0 \uc54a\uc558\uc5b4\uc694. +dialog.deletefieldvalues.intro=\ud604\uc7ac \ubc94\uc704\uc5d0\uc11c \uc0ad\uc81c\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd +dialog.setlinewidth.text=\ud2b8\ub799\uc744 \uadf8\ub9b4 \uc120\uc758 \ub450\uaed8\ub97c \uc801\uc73c\uc138\uc694(1-4) +dialog.downloadosm.desc=\ud2b9\uc815\uc9c0\uc5ed\uc758 raw OSM \ub370\uc774\ud130\ub97c \ub2e4\uc6b4\ub85c\ub4dc \ud558\uc2dc\uaca0\uc5b4\uc694? +dialog.searchwikipedianames.search=\ucc3e\uae30 + +# 3d window +dialog.3d.title=Prune 3D \ubcf4\uae30 +dialog.3d.altitudefactor=\uace0\ub3c4 \uacfc\uc7a5 \uacc4\uc218 +dialog.3dlines.title=Prune \uaca9\uc790\uc120 +dialog.3dlines.empty=\uaca9\uc790\uc120 \uc5c6\uc774 \ud45c\uc2dc\ud558\uae30 +dialog.3dlines.intro=3D \ubcf4\uae30\ub97c \uc704\ud55c \uaca9\uc790\uc120\uc785\ub2c8\ub2e4. + +# Confirm messages +confirm.loadfile=\ud30c\uc77c\uc5d0\uc11c \uc790\ub8cc\ub97c \ubd88\ub7ec\uc654\uc5b4\uc694. +confirm.save.ok1= +confirm.save.ok2=\uc9c0\uc810\uc774 \uc798 \uc800\uc7a5\ub418\uc5c8\uc5b4\uc694. +confirm.deletepoint.single=\uc9c0\uc810\uc774 \uc81c\uac70\ub418\uc5c8\uc5b4\uc694. +confirm.deletepoint.multi=\uc9c0\uc810\ub4e4\uc774 \uc81c\uc5b4\ub418\uc5c8\uc5b4\uc694. +confirm.point.edit=\uc9c0\uc810\uc774 \uc218\uc815\ub418\uc5c8\uc5b4\uc694. +confirm.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84\ub4e4\uc774 \ubcd1\ud569\ub418\uc5c8\uc5b4\uc694. +confirm.reverserange=\ubc94\uc704\uac00 \ubc18\uc804\ub418\uc5c8\uc5b4\uc694. +confirm.addtimeoffset=\ud0c0\uc784 \uc624\ud504\uc14b\uc774 \ucd94\uac00\ub418\uc5c8\uc5b4\uc694. +confirm.addaltitudeoffset=\uace0\ub3c4 \uc624\ud504\uc14b\uc774 \ucd94\uac00\ub418\uc5c8\uc5b4\uc694. +confirm.rearrangewaypoints=\uacbd\uc720\uc9c0\uac00 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694 +confirm.rearrangephotos=\uc0ac\uc9c4\ub4e4\uc774 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694. +confirm.cutandmove=\uc62e\uaca8\uc84c\uc5b4\uc694. +confirm.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\ub4e4\uc774 \ubcc0\ud658\ub418\uc5c8\uc5b4\uc694. +confirm.saveexif.ok1=\uc800\uc7a5\ub428 +confirm.saveexif.ok2=\uc0ac\uc9c4 \ud30c\uc77c\ub4e4 +confirm.undo.single=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791 +confirm.undo.multi=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791 +confirm.jpegload.single=\uc0ac\uc9c4\uc774 \ucd94\uac00\ub428 +confirm.jpegload.multi=\uc0ac\uc9c4\ub4e4\uc774 \ucd94\uac00\ub428 +confirm.media.connect=\ubbf8\ub514\uc544 \ud30c\uc77c \uc5f0\uacb0\ub428 +confirm.photo.disconnect=\uc0ac\uc9c4 \uc5f0\uacb0 \ub04a\uae40 +confirm.audio.disconnect=\uc18c\ub9ac \uc5f0\uacb0 \ub04a\uae40 +confirm.media.removed=\uc0ad\uc81c\ub428 +confirm.correlatephotos.single=\uc0ac\uc9c4 \uc5f0\uacb0\ub428 +confirm.correlatephotos.multi=\uc0ac\uc9c4\ub4e4 \uc5f0\uacb0\ub428 +confirm.createpoint=\uc9c0\uc810 \uc0dd\uc131\ub428 +confirm.rotatephoto=\uc0ac\uc9c4 \ub3cc\ub824\uc9d0 +confirm.running=\uc2e4\ud589\uc911 +confirm.lookupsrtm1= +confirm.lookupsrtm2=\uace0\ub3c4\uac12 \ubc1c\uacac +confirm.deletefieldvalues=\ud544\ub4dc\uac12 \uc9c0\uc6cc\uc9d0 +confirm.audioload=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ub428 +confirm.correlateaudios.single=\uc18c\ub9ac \uc5f0\uacb0\ub428 +confirm.correlateaudios.multi=\uc18c\ub9ac\ub4e4 \uc5f0\uacb0\ub428 + +# Buttons +button.ok=\ud655\uc778 +button.back=\ub4a4\ub85c +button.next=\ub2e4\uc74c +button.finish=\ub9c8\uce68 +button.cancel=\ucde8\uc18c +button.overwrite=\ub36e\uc5b4\uc4f0\uae30 +button.moveup=\uc704\ub85c +button.movedown=\uc544\ub798\ub85c +button.showlines=\uc120 \ubcf4\uae30 +button.edit=\uc218\uc815 +button.exit=\ub098\uac00\uae30 +button.close=\ub2eb\uae30 +button.continue=\uacc4\uc18d +button.yes=\uc608 +button.no=\uc544\ub2c8\uc624 +button.yestoall=\uc804\ubd80 \uc608 +button.notoall=\uc804\ubd80 \uc544\ub2c8\uc624 +button.select=\uc120\ud0dd +button.selectall=\uc804\ubd80\uc120\ud0dd +button.selectnone=\uc544\ubb34\uac83\ub3c4 \uc120\ud0dd\ud558\uc9c0\uc54a\uae30 +button.preview=\ubbf8\ub9ac\ubcf4\uae30 +button.load=\ubd88\ub7ec\uc624\uae30 +button.upload=\uc62c\ub9ac\uae30 +button.guessfields=\ucd94\uce21 \ud544\ub4dc +button.showwebpage=\uc6f9\ud398\uc774\uc9c0 \ubcf4\uae30 +button.check=\uccb4\ud06c +button.resettodefaults=\uae30\ubcf8\uc124\uc815\uc73c\ub85c +button.browse=\ucc3e\uae30 +button.addnew=\uc0c8\ub85c \ucd94\uac00 +button.delete=\uc9c0\uc6b0\uae30 + +# File types +filetype.txt=TXT \ud30c\uc77c +filetype.jpeg=JPG \ud30c\uc77c +filetype.kmlkmz=KML, KMZ \ud30c\uc77c +filetype.kml=KML \ud30c\uc77c +filetype.kmz=KMZ \ud30c\uc77c +filetype.gpx=GPX \ud30c\uc77c +filetype.pov=POV \ud30c\uc77c +filetype.svg=SVG \ud30c\uc77c +filetype.audio=MP3, OGG, WAV \ud30c\uc77c + +# Display components +display.nodata=\ubd88\ub7ec\uc9c4 \ub370\uc774\ud0c0 \uc5c6\uc74c +display.noaltitudes=\ud2b8\ub799\ub370\uc774\ud0c0\uc5d0 \uace0\ub3c4\uac00 \ud3ec\ud568\uc548\ub418\uc788\uc5b4\uc694. +display.notimestamps=\ud2b8\ub799\ub370\uc774\ud0c0\uc5d0 \uc2dc\uac04\uc774 \ud3ec\ud568\uc548\ub418\uc5b4\uc788\uc5b4\uc694. +details.trackdetails=\ud2b8\ub809 \uc0c1\uc138\uc815\ubcf4 +details.notrack=\ubd88\ub7ec\uc628 \ud2b8\ub799\uc774 \uc5c6\uc5b4\uc694 +details.track.points=\uc9c0\uc810 +details.track.file=\ud30c\uc77c +details.track.numfiles=\ud30c\uc77c\uc218 +details.pointdetails=\uc9c0\uc810 \uc0c1\uc138\uc815\ubcf4 +details.index.selected=\uac1c \uc120\ud0dd\ub428 +details.index.of=\uac1c \uc911\uc5d0 +details.nopointselection=\uc120\ud0dd\ub41c \uc9c0\uc810 \uc5c6\uc74c +details.photofile=\uc0ac\uc9c4 \ud30c\uc77c +details.norangeselection=\ubc94\uc704\uac00 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694. +details.rangedetails=\ubc94\uc704 \uc0c1\uc138\uc815\ubcf4 +details.range.selected=\uc120\ud0dd\ub428 +details.range.to=\ud604\uc7ac \ubc94\uc704 \uc9c0\uc810\uc5d0\uc11c +details.altitude.to=\uc120\ud0dd\ub41c \ubc94\uc704\uc5d0\uc11c \uc774\uc9c0\uc810\uc740 +details.range.climb=\uc624\ub984 +details.range.descent=\ud558\uac15 +details.coordformat=\uc88c\ud45c\ud615\uc2dd +details.distanceunits=\uac70\ub9ac \ub2e8\uc704 +display.range.time.secs=\ucd08 +display.range.time.mins=\ubd84 +display.range.time.hours=\uc2dc\uac04 +display.range.time.days=\uc77c +details.range.avespeed=\ud3c9\uade0 \uc18d\ub3c4 +details.range.avemovingspeed=\ud3c9\uade0 \uc774\ub3d9 +details.range.maxspeed=\ucd5c\uace0 \uc18d\ub3c4 +details.range.numsegments=\ubd80\ubd84\ub4e4\uc758 \uc218 +details.range.pace=\ud398\uc774\uc2a4(1km\ub098 1mile\uc774\ub3d9 \uc2dc\uac04) +details.range.gradient=\uacbd\uc0ac +details.lists.waypoints=\uacbd\uc720\uc9c0 +details.lists.photos=\uc0ac\uc9c4\ub4e4 +details.lists.audio=\uc18c\ub9ac +details.photodetails=\uc0ac\uc9c4 \uc0c1\uc138\uc815\ubcf4 +details.nophoto=\uc0ac\uc9c4\uc774 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc74c +details.photo.loading=\ubd88\ub7ec\uc624\uae30 +details.media.connected=\uc5f0\uacb0\ub428 +details.audiodetails=\uc18c\ub9ac \uc0c1\uc138\uc815\ubcf4 +details.noaudio=\uc18c\ub9ac\ud30c\uc77c\uc774 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc74c +details.audio.file=\uc18c\ub9ac\ud30c\uc77c +details.audio.playing=\uc7ac\uc0dd\uc911... +map.overzoom=\uc774 \ub2e8\uacc4\uc5d0\uc11c\ub294 \uc9c0\ub3c4\ub97c \ud45c\uc2dc\ud560 \uc218 \uc5c6\uc5b4\uc694. + +# Field names +fieldname.latitude=\uc704\ub3c4 +fieldname.longitude=\uacbd\ub3c4 +fieldname.altitude=\uace0\ub3c4 +fieldname.timestamp=\uc2dc\uac04 +fieldname.time=\uc2dc\uac04 +fieldname.waypointname=\uc774\ub984 +fieldname.waypointtype=\ud615\uc2dd +fieldname.newsegment=\ubd80\ubd84 +fieldname.custom=\ucee4\uc2a4\ud140 +fieldname.prefix=\ud544\ub4dc +fieldname.distance=\uac70\ub9ac +fieldname.movingdistance=\uc6c0\uc9c0\uc778\uac70\ub9ac +fieldname.duration=\uae30\uac04 +fieldname.speed=\uc18d\ub3c4 +fieldname.verticalspeed=\uc218\uc9c1\uc18d\ub3c4 + +# Measurement units +units.original=\uc6d0\ubcf8 +units.default=\uae30\ubcf8 +units.metres=\ubbf8\ud130 +units.metres.short=\ubbf8\ud130 +units.feet=\ud53c\ud2b8 +units.feet.short=\ud53c\ud2b8 +units.kilometres=\ud0ac\ub85c\ubbf8\ud130 +units.kilometres.short=\ud0ac\ub85c\ubbf8\ud130 +units.kmh=\ud0ac\ub85c\ubbf8\ud130-\uc2dc\uc18d +units.miles=\ub9c8\uc77c +units.miles.short=\ub9c8\uc77c +units.mph=\ub9c8\uc77c-\uc2dc\uc18d +units.metrespersec=m/s +units.feetpersec=ft/s +units.hours=\uc2dc\uac04 +units.degminsec=\ub3c4-\ubd84-\ucd08 +units.degmin=\ub3c4-\ubd84 +units.deg=\ub3c4 +units.iso8601=ISO 8601 + +# External urls +url.googlemaps=maps.google.co.kr +wikipedia.lang=ko + +# Cardinals for 3d plots +cardinal.n=N +cardinal.s=S +cardinal.e=E +cardinal.w=W + +# Undo operations +undo.load=\uc790\ub8cc \ubd88\ub7ec\uc624\uae30 +undo.loadphotos=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\uae30 +undo.loadaudios=\uc18c\ub9ac\ud30c\uc77c \ubd88\ub7ec\uc624\uae30 +undo.editpoint=\uc9c0\uc810 \uc218\uc815 +undo.deletepoint=\uc9c0\uc810 \uc0ad\uc81c +undo.removephoto=\uc0ac\uc9c4 \uc81c\uac70 +undo.removeaudio=\uc18c\ub9ac\ud30c\uc77c \uc81c\uac70 +undo.deleterange=\ubc94\uc704 \uc0ad\uc81c +undo.compress=\ud2b8\ub799 \uc555\ucd95 +undo.insert=\uc9c0\uc810\ub4e4 \uc0bd\uc785 +undo.reverse=\ubc94\uc704 \ubc18\uc804 +undo.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569 +undo.addtimeoffset=\ud0c0\uc784 \uc624\ud504\uc14b \ucd94\uac00 +undo.addaltitudeoffset=\uace0\ub3c4 \uc624\ud504\uc14b \ucd94\uac00 +undo.rearrangewaypoints=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c +undo.cutandmove=\ubd80\ubd84 \uc774\ub3d9 +undo.connect=\uc5f0\uacb0 +undo.disconnect=\uc5f0\uacb0 \ub04a\uae30 +undo.correlatephotos=\uc0ac\uc9c4 \uc5f0\uacb0 +undo.rearrangephotos=\uc0ac\uc9c4 \uc7ac\uc815\ub82c +undo.createpoint=\uc9c0\uc810 \uc0dd\uc131 +undo.rotatephoto=\uc0ac\uc9c4 \ud68c\uc804 +undo.convertnamestotimes=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 +undo.lookupsrtm=SRTM\uc5d0\uc11c \uace0\ub3c4 \ucc3e\uae30 +undo.deletefieldvalues=\ud544\ub4dc \uac11 \uc218\uc815 +undo.correlateaudios=\uc18c\ub9ac \uc5f0\uacb0 + +# Error messages +error.save.dialogtitle=\uc790\ub8cc \uc800\uc7a5\uc911 \uc624\ub958 +error.save.nodata=\uc800\uc7a5\ud560 \uc790\ub8cc\uac00 \uc5c6\uc5b4\uc694 +error.save.failed=\ud30c\uc77c\uc5d0 \uc790\ub8cc\uc800\uc7a5 \uc2e4\ud328 +error.saveexif.filenotfound=\uc0ac\uc9c4\ud30c\uc77c \ucc3e\uae30 \uc2e4\ud328 +error.saveexif.cannotoverwrite1=\uc0ac\uc9c4\ud30c\uc77c +error.saveexif.cannotoverwrite2=\uc774 \uc77d\uae30\uc804\uc6a9\uc774\ub124\uc694, \ub36e\uc5b4\uc4f8\uc218 \uc5c6\uc5b4\uc694 \ubcf5\uc0ac\ud574\uc11c \uc4f8\uae4c\uc694? +error.saveexif.failed1= +error.saveexif.failed2=\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328 +error.saveexif.forced1= +error.saveexif.forced2=\uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4. +error.load.dialogtitle=\uc790\ub8cc \ubd88\ub7ec\uc624\ub2e4\uac00 \uc5d0\ub7ec +error.load.noread=\ud30c\uc77c\uc744 \uc77d\uc744 \uc218 \uc5c6\ub124\uc694. +error.load.nopoints=\ud30c\uc77c\uc5d0 \uc88c\ud45c \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc694. +error.load.unknownxml=\uc54c\uc218\uc5c6\ub294 xml \ud3ec\uba67\uc774\uc5d0\uc694.(kml\uc774\ub098 gpx\uac00 \uc544\ub2cc\uac70 \uac19\uc544\uc694) +error.load.noxmlinzip=zip\ud30c\uc77c\uc548\uc5d0 xml\ud30c\uc77c\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc5b4\uc694. +error.load.othererror=\ud30c\uc77c \uc77d\ub294 \uc911 \uc5d0\ub7ec : +error.jpegload.dialogtitle=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\ub294 \uc911 \uc5d0\ub7ec +error.jpegload.nofilesfound=\ucc3e\uc740 \ud30c\uc77c \uc5c6\uc74c. +error.jpegload.nojpegsfound=\ucc3e\uc740 jpeg\ud30c\uc77c \uc5c6\uc74c. +error.jpegload.nogpsfound=GPS \uc815\ubcf4\ub97c \ucc3e\uc9c0 \ubabb\ud568. +error.jpegload.exifreadfailed=EXIF\uc815\ubcf4 \uc77d\uae30 \uc2e4\ud328./n\ub0b4\uc7a5\uc774\ub098 \uc678\uc7a5 \ub77c\uc774\ube0c\ub7ec\uc774\uac00 \uc5c6\uc73c\uba74/nEXIF\uc815\ubcf4\ub97c \uc77d\uc744 \uc218 \uc5c6\uc5b4\uc694. +error.audioload.nofilesfound=\ucc3e\uc740 \uc18c\ub9ac\ud30c\uc77c \uc5c6\uc74c. +error.gpsload.unknown=\uc54c\ub824\uc9c0\uc9c0 \uc54a\uc740 \uc624\ub958. +error.undofailed.title=\ub418\ub3cc\ub9ac\uae30 \uc2e4\ud328. +error.undofailed.text=\ub418\ub3cc\ub9ac\uae30 \ub3d9\uc791\uc774 \uc2e4\ud328\ud558\uc600\uc5b4\uc694. +error.function.noop.title=\uae30\ub2a5\uc774 \uc544\ubb34 \ud6a8\uacfc \uc5c6\ub124\uc694. +error.rearrange.noop=\uc7ac\uc815\ub82c\ud55c \uc9c0\uc810\ub4e4\uc774 \ud6a8\uacfc \uc5c6\uc5b4\uc694.(\uacbd\uc720\uc9c0\uac00 \uc544\ub2cc\uac70 \uac19\uc544\uc694.) +error.function.notavailable.title=\uae30\ub2a5\uc744 \uc791\ub3d9\ud560 \uc218 \uc5c6\uc74c. +error.function.nojava3d=\uc774 \uae30\ub2a5\uc740 Java3D \ub77c\uc774\ube0c\ub7ec\ub9ac\uac00 \ud544\uc694\ud574\uc694./n Sun.com\uc5d0\uc11c \uad6c\ud560 \uc218 \uc788\uc5b4\uc694. +error.3d=3D \ud45c\uc2dc\ud558\ub2e4\uac00 \uc5d0\ub7ec\ubc1c\uc0dd +error.readme.notfound=Readme \ud30c\uc77c\uc744 \ubabb \ucc3e\uc74c +error.osmimage.dialogtitle=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30\uc911 \uc624\ub958 +error.osmimage.failed=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30 \uc2e4\ud328./n\uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694. +error.language.wrongfile=\uc120\ud0dd\ud558\uc2e0 \ud30c\uc77c\uc774 Prune\uc744 \uc704\ud55c \uc5b8\uc5b4\ud30c\uc77c\uc774 \uc544\ub2cc\uac70 \uac19\uc544\uc694. +error.convertnamestotimes.nonames=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 \ud560 \uc218 \uc5c6\uc5b4\uc694./n(\uacbd\uc720\uc9c0\uc774\ub984\uc744 \ucc3e\uc9c0 \ubabb\ud588\uac70\ub098 \ubcc0\ud658\ud560 \uc218 \uc5c6\ub294 \uacbd\uc6b0\uc5d0\uc694.) +error.lookupsrtm.nonefound=\uc774 \uc9c0\uc810\ub4e4\uc5d0\uc11c \uace0\ub3c4\uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5b4\uc694./n\uc774 \uc601\uc5ed\uc5d0\uc11c \ud0c0\uc77c\uc774 \uc5c6\uac70\ub098, \uc790\ub8cc\uac00 \uc54c\uc218 \uc5c6\ub294 \uac83\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \uacbd\uc720. +error.lookupsrtm.nonerequired=\ubaa8\ub4e0 \uc9c0\uc810\uc774 \uace0\ub3c4 \uc815\ubcf4\uac00 \uc788\uc5b4\uc11c, \ucc3e\uc744\uac8c \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694. +error.gpsies.uploadnotok=gpsies \uc11c\ubc84\uac00 \uba54\uc138\uc9c0\ub97c \ub2e4\uc74c\uacfc \uac19\uc740 \ub3cc\ub824\uc90d\ub2c8\ub2e4 +error.gpsies.uploadfailed=\ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc720\ub85c \uc5c5\ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4 +error.playaudiofailed=\uc18c\ub9ac\ud30c\uc77c \uc7ac\uc0dd \uc2e4\ud328 diff --git a/tim/prune/lang/prune-texts_nl.properties b/tim/prune/lang/prune-texts_nl.properties index f7b7578..162b160 100644 --- a/tim/prune/lang/prune-texts_nl.properties +++ b/tim/prune/lang/prune-texts_nl.properties @@ -30,9 +30,7 @@ menu.point.editpoint=Wijzig punt menu.point.deletepoint=Verwijder punt menu.photo=Foto menu.photo.saveexif=Opslaan naar Exif -menu.photo.connect=Aan punt vastzetten -menu.photo.disconnect=Van punt losmaken -menu.photo.delete=Verwijder foto +menu.audio=Audio menu.view=Bekijken menu.view.showsidebars=Toon panelen menu.view.browser=Kaart in browser @@ -49,6 +47,7 @@ menu.map.zoomin=Zoom + menu.map.zoomout=Zoom - menu.map.zoomfull=Zoom alles menu.map.newpoint=Maak nieuw punt +menu.map.drawpoints=Maak een serie punten menu.map.connect=Verbind route punten menu.map.autopan=Autopan menu.map.showmap=Toon kaart @@ -61,6 +60,7 @@ altkey.menu.range=E altkey.menu.point=P altkey.menu.view=B altkey.menu.photo=F +altkey.menu.audio=A altkey.menu.settings=I altkey.menu.help=H @@ -99,14 +99,27 @@ function.setpaths=Instellen programmapaden function.getgpsies=Routes van Gpsies ophalen function.uploadgpsies=Upload routes naar Gpsies function.lookupsrtm=Hoogtes van SRTM ophalen +function.getwikipedia=Wikipedia artikelen uit de buurt ophalen +function.searchwikipedianames=Wikipedia zoeken op naam +function.downloadosm=Downloaden OSM data voor gebied function.duplicatepoint=Dupliceer punt function.setcolours=Instellen kleuren +function.setlinewidth=Instellen lijndikte function.setlanguage=Instellen taal +function.connecttopoint=Aan punt vastzetten +function.disconnectfrompoint=Van punt losmaken +function.removephoto=Verwijder foto function.correlatephotos=Correleer foto's function.rearrangephotos=Foto's herschikken function.rotatephotoleft=Roteer foto linksom function.rotatephotoright=Roteer foto rechtsom +function.photopopup=Toon foto in pop-up function.ignoreexifthumb=Negeer exif thumbnail +function.loadaudio=Toevoegen audiobestanden +function.removeaudio=Verwijder audiobestand +function.correlateaudios=Correleeer audiobestanden +function.playaudio=Afspelen audiobestand +function.stopaudio=Stop audiobestand function.help=Help function.showkeys=Toon sneltoetsen function.about=Over Prune @@ -138,7 +151,7 @@ dialog.openoptions.deliminfo.records=bestanden, met dialog.openoptions.deliminfo.fields=velden dialog.openoptions.deliminfo.norecords=Geen bestanden dialog.openoptions.altitudeunits=Hoogte eenheden -dialog.open.contentsdoubled=Dit bestand bevat twee kopie\u00ebn van ieder punt,\neenkeer als als waypoint en eenkeer als punt +dialog.open.contentsdoubled=Dit bestand bevat twee kopie\u00ebn van ieder punt,\neen keer als waypoint en een keer als punt dialog.selecttracks.intro=Selecteer route of routes om te laden dialog.selecttracks.noname=Onbenoemd dialog.jpegload.subdirectories=Submappen meenemen @@ -183,7 +196,7 @@ dialog.exportpov.cameray=Camera Y dialog.exportpov.cameraz=Camera Z dialog.exportpov.modelstyle=Model stijl dialog.exportpov.ballsandsticks=Balletjes en stokjes -dialog.exportpov.tubesandwalls=Tubes en muren +dialog.exportpov.tubesandwalls=Buizen en muren dialog.exportpov.warningtracksize=Deze route heeft een groot aantal punten. Java3D kan deze mogelijk niet tonen.\nWeet u zeker dat u door wilt gaan? dialog.exportsvg.text=Selecteer de camera hoeken voor SVG export dialog.exportsvg.phi=Azimut hoek \u03d5 @@ -193,6 +206,7 @@ dialog.pointtype.desc=Sla de volgende punttypen op: dialog.pointtype.track=Routepunten dialog.pointtype.waypoint=Waypoints dialog.pointtype.photo=Fotopunten +dialog.pointtype.audio=Audiopunten dialog.pointtype.selection=Alleen selectie dialog.confirmreversetrack.title=Bevestig omkering dialog.confirmreversetrack.text=Deze route bevat tijd-informatie die niet meer klopt na een omkering.\nWeet u zeker dat u deze sectie wilt omkeren? @@ -274,16 +288,18 @@ dialog.gpsies.activity.trekking=Trekking dialog.gpsies.activity.walking=Lopen dialog.gpsies.activity.jogging=Hardlopen dialog.gpsies.activity.biking=Fietsen -dialog.gpsies.activity.motorbiking=Motor -dialog.gpsies.activity.snowshoe=Sneeuwschoen +dialog.gpsies.activity.motorbiking=Motorrijden +dialog.gpsies.activity.snowshoe=Sneeuwschoen-lopen dialog.gpsies.activity.sailing=Zeilen dialog.gpsies.activity.skating=Skating +dialog.wikipedia.column.name=Artikelnaam +dialog.wikipedia.column.distance=Afstand dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden. dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan? dialog.correlate.photoselect.intro=Selecteer \u00e9\u00e9n van deze gekoppelde foto's om het tijdsverschil te gebruiken -dialog.correlate.photoselect.photoname=Fotonaam -dialog.correlate.photoselect.timediff=Tijdsverschil -dialog.correlate.photoselect.photolater=Foto later +dialog.correlate.select.photoname=Fotonaam +dialog.correlate.select.timediff=Tijdsverschil +dialog.correlate.select.photolater=Foto later dialog.correlate.options.tip=Tip: Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden. dialog.correlate.options.intro=Selecteer de opties voor automatisch koppelen dialog.correlate.options.offsetpanel=Tijdverschil @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=uren, dialog.correlate.options.offset.minutes=minuten en dialog.correlate.options.offset.seconds=seconden dialog.correlate.options.photolater=Foto later dan punt -dialog.correlate.options.pointlater=Punt later dan foto +dialog.correlate.options.pointlaterphoto=Punt later dan foto +dialog.correlate.options.audiolater=Audio later dan punt +dialog.correlate.options.pointlateraudio=Punt later dan audio dialog.correlate.options.limitspanel=Koppelingslimieten dialog.correlate.options.notimelimit=Geen tijdslimiet dialog.correlate.options.timelimit=Tijdslimiet @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Geen afstandlimiet dialog.correlate.options.distancelimit=Afstandlimeit dialog.correlate.options.correlate=Koppelen dialog.correlate.alloutsiderange=Alle foto's zijn buiten het bereik van de route en kunnen dus niet gekoppeld worden.\nPas het tijdsverschil aan, of maak minimaal \u00e9\u00e9n koppeling handmatig. +dialog.correlate.filetimes=Tijdstip bestand geeft aan: +dialog.correlate.filetimes2=van audiobestand +dialog.correlate.correltimes=Voor correleren, gebruik: +dialog.correlate.timestamp.beginning=Begin +dialog.correlate.timestamp.middle=Midden +dialog.correlate.timestamp.end=Einde +dialog.correlate.audioselect.intro=Gebruk \u00e9\u00e9n van deze gecorreleerde audiobestanden als tijdsveschil +dialog.correlate.select.audioname=Naam audiobestsnd +dialog.correlate.select.audiolater=Audio later dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten dialog.rearrangephotos.tostart=Naar begin dialog.rearrangephotos.toend=Naar einde @@ -380,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Kaartcache dialog.saveconfig.prune.kmzimagewidth=KMZ afbeelding breedte dialog.saveconfig.prune.kmzimageheight=KMZ afbeelding hoogte dialog.saveconfig.prune.colourscheme=Kleurenschema +dialog.saveconfig.prune.linewidth=Lijndikte dialog.saveconfig.prune.kmltrackcolour=KML routekleur dialog.setpaths.intro=Indien nodig kan je de paden naar externe applicaties kiezen: dialog.setpaths.found=Pad gevonden? @@ -409,10 +437,12 @@ dialog.diskcache.dir=Cache map dialog.diskcache.createdir=Cre\u00eber map dialog.diskcache.nocreate=Cache map niet aangemaakt dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks +dialog.setlinewidth.text=Geef lijndikte voor routes (1-4) +dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied: +dialog.searchwikipedianames.search=Zoeken naar: # 3d window dialog.3d.title=Prune in 3D -dialog.3d.altitudecap=Minimale hoogte dialog.3d.altitudefactor=Hoogte overdrijvingsfactor dialog.3dlines.title=Prune raster dialog.3dlines.empty=Geen raster om af te beelden @@ -439,16 +469,21 @@ confirm.undo.single=Actie geannuleerd confirm.undo.multi=Acties geannuleerd confirm.jpegload.single=foto was toegevoegd confirm.jpegload.multi=foto's waren toegevoegd -confirm.photo.connect=foto's gekoppeld +confirm.media.connect=Media gekoppeld confirm.photo.disconnect=foto's ontkoppeld -confirm.correlate.single=Foto was gecorreleerd -confirm.correlate.multi=Foto's waren gecorreleerd +confirm.audio.disconnect=audio ontkoppeld +confirm.media.removed=verwijderd +confirm.correlatephotos.single=Foto was gecorreleerd +confirm.correlatephotos.multi=Foto's waren gecorreleerd confirm.createpoint=punt aangemaakt -confirm.rotatephoto=foto rgeroteerd +confirm.rotatephoto=foto geroteerd confirm.running=Bezig... confirm.lookupsrtm1=Gevonden confirm.lookupsrtm2=hoote waarden confirm.deletefieldvalues=Veldwaarden gewist +confirm.audioload=Audiobestanden toegevoegd +confirm.correlateaudios.single=audiobestand gecorreleerd +confirm.correlateaudios.multi=audiobestanden gecorreleerd # Buttons button.ok=OK @@ -473,6 +508,7 @@ button.selectall=Selecteer alles button.selectnone=Selecteer niets button.preview=Voorbeeld button.load=Laden +button.upload=Upload button.guessfields=Raad velden button.showwebpage=Toon webpagina button.check=Controleren @@ -490,6 +526,7 @@ filetype.kmz=KMZ bestand filetype.gpx=GPX bestand filetype.pov=POV bestand filetype.svg=SVG bestand +filetype.audio=MP3, OGG, WAV bestanden # Display components display.nodata=Geen gegevens geladen @@ -524,12 +561,17 @@ details.range.maxspeed=Max snelheid details.range.numsegments=Aantal segmenten details.range.pace=Tempo details.range.gradient=Helling -details.waypointsphotos.waypoints=Waypoints -details.waypointsphotos.photos=Foto +details.lists.waypoints=Waypoints +details.lists.photos=Foto +details.lists.audio=Audio details.photodetails=Foto details details.nophoto=Geen foto geselecteerd details.photo.loading=Bezig met laden -details.photo.connected=Geconnecteerd +details.media.connected=Geconnecteerd +details.audiodetails=Audio details +details.noaudio=Geen audiobestand geselecteerd +details.audio.file=Audiobestand +details.audio.playing=Afspelen... map.overzoom=Geen kaarten beschikbaar op dit zoom-niveau # Field names @@ -572,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.nl +wikipedia.lang=nl # Cardinals for 3d plots cardinal.n=N @@ -582,27 +625,30 @@ cardinal.w=W # Undo operations undo.load=gegevens laden undo.loadphotos=foto's laden +undo.loadaudios=audiobestanden laden undo.editpoint=wijzigen punt undo.deletepoint=verwijderen punt -undo.deletephoto=verwijderen foto +undo.removephoto=verwijderen foto +undo.removeaudio=verwijderen audiobestand undo.deleterange=verwijderen reeks undo.compress=comprimeren route undo.insert=punten invoegen undo.reverse=reeks omkeren -undo.mergetracksegments=Samenvoegen route segmenten -undo.addtimeoffset=Tijdsverschil toevoegen -undo.addaltitudeoffset=Hoogteverschil toevoegen -undo.rearrangewaypoints=Herschikken waypoint +undo.mergetracksegments=samenvoegen route segmenten +undo.addtimeoffset=tijdsverschil toevoegen +undo.addaltitudeoffset=hoogteverschil toevoegen +undo.rearrangewaypoints=herschikken waypoint undo.cutandmove=Verschuif sectie -undo.connectphoto=Koppel foto -undo.disconnectphoto=Loskoppelen foto -undo.correlate=Correleer foto -undo.rearrangephotos=Herschikken foto's -undo.createpoint=Cre\u00eber punt -undo.rotatephoto=Roteer foto -undo.convertnamestotimes=Converteer namen naar tijden -undo.lookupsrtm=Opzoeken hoogtes in SRTM -undo.deletefieldvalues=Verwijder veldwaarden +undo.connect=koppel +undo.disconnect=loskoppelen +undo.correlatephotos=correleer foto +undo.rearrangephotos=herschikken foto's +undo.createpoint=cre\u00eber punt +undo.rotatephoto=roteer foto +undo.convertnamestotimes=converteer namen naar tijden +undo.lookupsrtm=opzoeken hoogtes in SRTM +undo.deletefieldvalues=verwijder veldwaarden +undo.correlateaudios=correleer audiobestanden # Error messages error.save.dialogtitle=Fout bij opslaan gegevens @@ -624,9 +670,9 @@ error.load.othererror=Fout bij lezen bestand: error.jpegload.dialogtitle=Fout bij inlezen foto's error.jpegload.nofilesfound=Bestanden niet gevonden error.jpegload.nojpegsfound=Geen jpeg-bestanden gevonden -error.jpegload.noexiffound=Geen EXIF informatie gevonden error.jpegload.nogpsfound=Geen GPS informatie gevonden error.jpegload.exifreadfailed=Kon geen EXIF informatie inlezen. EXIF informatie kan niet worden gelezen\n zonder interne of externe bibliotheek. +error.audioload.nofilesfound=Geen audiobestanden gevonden error.gpsload.unknown=Onbekende fout error.undofailed.title=Terugdraaien mislukt error.undofailed.text=Kon actie niet terugdraaien @@ -644,3 +690,4 @@ error.lookupsrtm.nonefound=Geen hoogtewaarden beschikbaar voor deze punten error.lookupsrtm.nonerequired=Alle punten hebben reeds hoogte, er hoeft niets te worden opgezocht. error.gpsies.uploadnotok=Gpsies server antwoordde met error.gpsies.uploadfailed=De upload is mislukt. Fout +error.playaudiofailed=Kon audiobestand niet afspelen diff --git a/tim/prune/lang/prune-texts_pl.properties b/tim/prune/lang/prune-texts_pl.properties index 46ae39b..378c08d 100644 --- a/tim/prune/lang/prune-texts_pl.properties +++ b/tim/prune/lang/prune-texts_pl.properties @@ -30,9 +30,7 @@ menu.point.editpoint=Edytuj punkt menu.point.deletepoint=Usu\u0144 punkt menu.photo=Zdj\u0119cie menu.photo.saveexif=Zapisz Exif -menu.photo.connect=Przy\u0142\u0105cz do punktu -menu.photo.disconnect=Od\u0142\u0105cz od punktu -menu.photo.delete=Usu\u0144 zdj\u0119cie +menu.audio=Audio menu.view=Widok menu.view.showsidebars=Poka\u017c boczne panele menu.view.browser=Mapa w przegl\u0105darce @@ -49,6 +47,7 @@ menu.map.zoomin=Powi\u0119ksz menu.map.zoomout=Pomniejsz menu.map.zoomfull=Dostosuj powi\u0119kszenie menu.map.newpoint=Stw\u00f3rz nowy punkt +menu.map.drawpoints=Stw\u00f3rz seri\u0119 punkt\u00f3w menu.map.connect=Po\u0142\u0105cz punkty \u015bcie\u017cki menu.map.autopan=Przesuwanie mapy menu.map.showmap=Poka\u017c map\u0119 @@ -61,6 +60,7 @@ altkey.menu.range=Z altkey.menu.point=U altkey.menu.view=W altkey.menu.photo=Z +altkey.menu.audio=A altkey.menu.settings=T altkey.menu.help=M @@ -99,14 +99,27 @@ function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies function.lookupsrtm=Pobierz wysoko\u015bci z SRTM +function.getwikipedia=Szukaj w Wikipedii o okolicy +function.searchwikipedianames=Szukaj nazwy w Wikipedii +function.downloadosm=Za\u0142aduj dane obszaru z OSM function.duplicatepoint=Duplikuj plik function.setcolours=Ustaw kolory +function.setlinewidth=Ustaw szeroko\u015b\u0107 linii function.setlanguage=Zmie\u0144 j\u0119zyk +function.connecttopoint=Przy\u0142\u0105cz do punktu +function.disconnectfrompoint=Od\u0142\u0105cz od punktu +function.removephoto=Usu\u0144 zdj\u0119cie function.correlatephotos=Powi\u0105\u017c zdj\u0119cia function.rearrangephotos=Zmie\u0144 kolejno\u015b\u0107 zdj\u0119\u0107 function.rotatephotoleft=Obr\u00f3\u0107 zdj\u0119cie w lewo function.rotatephotoright=Obr\u00f3\u0107 zdj\u0119cie wprawo +function.photopopup=Poka\u017c zdj\u0119cie function.ignoreexifthumb=Ignoruj miniaturk\u0119 z exif +function.loadaudio=Dodaj pliki audio +function.removeaudio=Usu\u0144 pliki audio +function.correlateaudios=Powi\u0105\u017c audio +function.playaudio=Odtw\u00f3rz plik audio +function.stopaudio=Zatrzymaj plik audio function.help=Pomoc function.showkeys=Poka\u017c klawisze skr\u00f3tu function.about=O Prune @@ -193,6 +206,7 @@ dialog.pointtype.desc=Zapisz punkty nast\u0119puj\u0105cych typ\u00f3w: dialog.pointtype.track=punkty \u015bcie\u017cki dialog.pointtype.waypoint=punkty po\u015brednie dialog.pointtype.photo=punkty zdj\u0119\u0107 +dialog.pointtype.audio=punkty audio dialog.pointtype.selection=Tylko wybrane dialog.confirmreversetrack.title=Potwierd\u017a odwr\u00f3cenie dialog.confirmreversetrack.text=Ta \u015bcie\u017cka zawiera znaczniki czasu, kt\u00f3re po odwr\u00f3ceniu nie b\u0119d\u0105 ustawione w kolejno\u015bci.\nCzy na pewno chcesz odwr\u00f3ci\u0107 ten fragment? @@ -278,12 +292,14 @@ dialog.gpsies.activity.motorbiking=Wycieczka motocyklowa dialog.gpsies.activity.snowshoe=Snowshoeing dialog.gpsies.activity.sailing=\u017beglarstwo dialog.gpsies.activity.skating=Wrotki/rolki +dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u +dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107 dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami. dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107? dialog.correlate.photoselect.intro=Wybierz jedno z powi\u0105zanych zdj\u0119\u0107 i u\u017cyj go jako wzorca do przesuni\u0119cia czasu -dialog.correlate.photoselect.photoname=Nazwa zdj\u0119cia -dialog.correlate.photoselect.timediff=R\u00f3\u017cnica czasowa -dialog.correlate.photoselect.photolater=P\u00f3\u017aniejsze zdj\u0119cie +dialog.correlate.select.photoname=Nazwa zdj\u0119cia +dialog.correlate.select.timediff=R\u00f3\u017cnica czasowa +dialog.correlate.select.photolater=P\u00f3\u017aniejsze zdj\u0119cie dialog.correlate.options.tip=Porada: Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie. dialog.correlate.options.intro=Wybierz opcje dla automatycznej korelacji dialog.correlate.options.offsetpanel=Przesuni\u0119cie czasowe @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=godzin, dialog.correlate.options.offset.minutes=minut i dialog.correlate.options.offset.seconds=sekund dialog.correlate.options.photolater=Zdj\u0119cie p\u00f3\u017aniejsze ni\u017c punkt -dialog.correlate.options.pointlater=Punkt p\u00f3\u017aniejszy ni\u017c zdj\u0119cie +dialog.correlate.options.pointlaterphoto=Punkt p\u00f3\u017aniejszy ni\u017c zdj\u0119cie +dialog.correlate.options.audiolater=Plik audio p\u00f3\u017aniejszy ni\u017c zdj\u0119cie +dialog.correlate.options.pointlateraudio=Punkt p\u00f3\u017aniejszy ni\u017c plik audio dialog.correlate.options.limitspanel=Ograniczenia korelacji dialog.correlate.options.notimelimit=Bez limitu czasu dialog.correlate.options.timelimit=Limit czasu @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Bez limitu odleg\u0142o\u015bci dialog.correlate.options.distancelimit=Limit odleg\u0142o\u015bci dialog.correlate.options.correlate=Powi\u0105\u017c ze sob\u0105 dialog.correlate.alloutsiderange=Wszystkie zdj\u0119cia s\u0105 poza zakresem czasu \u015bcie\u017cki, tak \u017ce \u017cadne nie mo\u017ce zosta\u0107 z ni\u0105 skorelowane.\nSpr\u00f3buj zmieni\u0107 przesuni\u0119cie lub r\u0119cznie skoreluj przynajmniej jedno zdj\u0119cie. +dialog.correlate.filetimes=Czas pliku oznacza: +dialog.correlate.filetimes2=pliku audio +dialog.correlate.correltimes=Do korelacji u\u017cyj: +dialog.correlate.timestamp.beginning=pocz\u0105tku +dialog.correlate.timestamp.middle=\u015brodka +dialog.correlate.timestamp.end=ko\u0144ca +dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu +dialog.correlate.select.audioname=nazwa pliku audio +dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio dialog.rearrangephotos.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w ze zdj\u0119ciami dialog.rearrangephotos.tostart=Przesu\u0144 na pocz\u0105tek dialog.rearrangephotos.toend=Przesu\u0144 na koniec @@ -360,6 +387,8 @@ dialog.checkversion.releasedate2=. dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/prune/download.html. dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w zamiast myszki dialog.keys.keylist=
klawisze strza\u0142ekPrzesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142
Ctrl + lewa, prawa strza\u0142kaWybierz punkt poprzedni lub nast\u0119pny
Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142Powi\u0119ksz, pomniejsz
DelUsun bie\u017c\u0105cy punkt
+dialog.keys.normalmodifier=Ctrl +dialog.keys.macmodifier=Command dialog.saveconfig.desc=Nast\u0119puj\u0105ce ustawienia mog\u0105 zosta\u0107 zapisane w pliku konfiguracyjnym: dialog.saveconfig.prune.trackdirectory=Katalog ze \u015bcie\u017ckami dialog.saveconfig.prune.photodirectory=Katalog ze zdj\u0119ciami @@ -378,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Pami\u0119\u0107 podr\u0119czna map dialog.saveconfig.prune.kmzimagewidth=szeroko\u015b\u0107 obrazka w KMZ dialog.saveconfig.prune.kmzimageheight=wysoko\u015b\u0107 obrazka w KMZ dialog.saveconfig.prune.colourscheme=Schemat kolor\u00f3w +dialog.saveconfig.prune.linewidth=Szeroko\u015b\u0107 linii dialog.saveconfig.prune.kmltrackcolour=Kolor \u015bcie\u017cki w pliku KML dialog.setpaths.intro=Je\u015bli zachodzi tak potrzeba, mo\u017cesz wybra\u0107 \u015bcie\u017cki do aplikacji zewn\u0119trznych dialog.setpaths.found=Znalezione \u015bcie\u017cki? @@ -407,10 +437,12 @@ dialog.diskcache.dir=katalog pami\u0119ci podr\u0119cznej dialog.diskcache.createdir=stw\u00f3rz katalog dialog.diskcache.nocreate=Nie utworzono katalogu pami\u0119ci podr\u0119cznej dialog.deletefieldvalues.intro=Wybierz pola do skasowania z wybranego zakresu +dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek +dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM: +dialog.searchwikipedianames.search=Szukaj # 3d window dialog.3d.title=Prune widok tr\u00f3jwymiarowy -dialog.3d.altitudecap=Minimalny zakres wysoko\u015bci dialog.3d.altitudefactor=Wsp\u00f3\u0142czynnik skalowania wysoko\u015bci dialog.3dlines.title=Linie siatki dialog.3dlines.empty=Brak siatki do wy\u015bwietlenia! @@ -437,16 +469,21 @@ confirm.undo.single=cofni\u0119to operacj\u0119 confirm.undo.multi=operacje zosta\u0142y cofni\u0119te confirm.jpegload.single=dodano zdj\u0119cie confirm.jpegload.multi=zdj\u0119\u0107 dodano -confirm.photo.connect=przy\u0142\u0105czono zdj\u0119cie +confirm.media.connect=przy\u0142\u0105czono confirm.photo.disconnect=od\u0142\u0105czono zdj\u0119cie -confirm.correlate.single=zdj\u0119cie zosta\u0142o po\u0142\u0105czone -confirm.correlate.multi=zdj\u0119cia zosta\u0142y po\u0142\u0105czone +confirm.audio.disconnect=od\u0142\u0105czono +confirm.media.removed=usuni\u0119to +confirm.correlatephotos.single=zdj\u0119cie zosta\u0142o po\u0142\u0105czone +confirm.correlatephotos.multi=zdj\u0119cia zosta\u0142y po\u0142\u0105czone confirm.createpoint=stworzono punkt confirm.rotatephoto=obr\u00f3cono zdj\u0119cie confirm.running=Przetwarzam dane ... confirm.lookupsrtm1=Znaleziono confirm.lookupsrtm2=warto\u015bci wysoko\u015bci confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to +confirm.audioload=dodano +confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone +confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone # Buttons button.ok=OK @@ -489,6 +526,7 @@ filetype.kmz=Pliki KMZ filetype.gpx=Pliki GPX filetype.pov=Pliki POV filetype.svg=Pliki SVG +filetype.audio=Pliki MP3, OGG, WAV # Display components display.nodata=Nie za\u0142adowano danych @@ -523,12 +561,17 @@ details.range.maxspeed=Pr\u0119dko\u015b\u0107 maksymalna details.range.numsegments=Liczba segment\u00f3w details.range.pace=Tempo details.range.gradient=Nachylenie -details.waypointsphotos.waypoints=Punkty po\u015brednie -details.waypointsphotos.photos=Zdj\u0119cia +details.lists.waypoints=Punkty po\u015brednie +details.lists.photos=Zdj\u0119cia +details.lists.audio=Audio details.photodetails=Szczeg\u00f3\u0142y zdj\u0119cia details.nophoto=Brak zaznaczonego zdj\u0119cia details.photo.loading=Wczytywanie -details.photo.connected=Pod\u0142\u0105czony +details.media.connected=Pod\u0142\u0105czony +details.audiodetails=Szczeg\u00f3\u0142y audio +details.noaudio=Brak zaznaczonego audio +details.audio.file=Plik audio +details.audio.playing=odtwarzam... map.overzoom=Brak map dla danego powi\u0119kszenia # Field names @@ -571,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.pl +wikipedia.lang=pl # Cardinals for 3d plots cardinal.n=N @@ -581,9 +625,11 @@ cardinal.w=W # Undo operations undo.load=za\u0142aduj dane undo.loadphotos=za\u0142aduj zdj\u0119cia +undo.loadaudios=za\u0142aduj audio undo.editpoint=edycja punktu undo.deletepoint=usu\u0144 punkt -undo.deletephoto=usu\u0144 zdj\u0119cie (nie z dysku) +undo.removephoto=usu\u0144 zdj\u0119cie (nie z dysku) +undo.removeaudio=usu\u0144 audio undo.deleterange=usu\u0144 zakres undo.compress=skompresuj \u015bcie\u017ck\u0119 undo.insert=wstaw punkty @@ -593,15 +639,16 @@ undo.addtimeoffset=dodaj przesuni\u0119cie czasowe undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci undo.rearrangewaypoints=przestaw punkty po\u015brednie undo.cutandmove=przesu\u0144 fragment -undo.connectphoto=do\u0142\u0105cz zdj\u0119cie -undo.disconnectphoto=od\u0142\u0105cz zdj\u0119cie -undo.correlate=po\u0142\u0105cz ze zdj\u0119ciami +undo.connect=do\u0142\u0105cz +undo.disconnect=od\u0142\u0105cz +undo.correlatephotos=po\u0142\u0105cz ze zdj\u0119ciami undo.rearrangephotos=zmie\u0144 kolejno\u015b\u0107 zdj\u0119\u0107 undo.createpoint=stw\u00f3rz punkt undo.rotatephoto=obr\u00f3\u0107 zdj\u0119cie undo.convertnamestotimes=zamie\u0144 nazwy punkt\u00f3w undo.lookupsrtm=szukaj wysoko\u015bci w SRTM undo.deletefieldvalues=usu\u0144 warto\u015bci p\u00f3l +undo.correlateaudios=skoreluj audio # Error messages error.save.dialogtitle=B\u0142\u0105d zapisu danych @@ -623,9 +670,9 @@ error.load.othererror=B\u0142\u0105d czytania pliku: error.jpegload.dialogtitle=B\u0142\u0105d \u0142adowania zdj\u0119cia error.jpegload.nofilesfound=Nie znaleziono plik\u00f3w error.jpegload.nojpegsfound=Nie znaleziono plik\u00f3w jpeg -error.jpegload.noexiffound=Nie znaleziono informacji EXIF error.jpegload.nogpsfound=Nie znaleziono informacji GPS error.jpegload.exifreadfailed=Nie powiod\u0142o si\u0119 odczytanie informacji EXIF\nInformacji tych nie mo\u017cna przeczyta\u0107 bez wewn\u0119trznej lub zewn\u0119trznej biblioteki. +error.audioload.nofilesfound=Nie znaleziono plik\u00f3w audio error.gpsload.unknown=Nieznany b\u0142\u0105d error.undofailed.title=Cofnij nie powiod\u0142o si\u0119 error.undofailed.text=Nie mo\u017cna cofn\u0105\u0107 @@ -643,3 +690,4 @@ error.lookupsrtm.nonefound=Nie znaleziono danych o wysoko\u015bci. error.lookupsrtm.nonerequired=Wszystkie pola maj\u0105 informacj\u0119 o wysoko\u015bci, nie ma czego szuka\u0107 error.gpsies.uploadnotok=Serwer Gpsies zwr\u00f3ci\u0142 informacj\u0119 error.gpsies.uploadfailed=B\u0142\u0105d wysy\u0142ania +error.playaudiofailed=Nie powiod\u0142o si\u0119 odtwarzanie pliku audio diff --git a/tim/prune/lang/prune-texts_pt.properties b/tim/prune/lang/prune-texts_pt.properties index dc60934..8596b39 100644 --- a/tim/prune/lang/prune-texts_pt.properties +++ b/tim/prune/lang/prune-texts_pt.properties @@ -6,7 +6,7 @@ menu.file=Arquivo menu.file.addphotos=Adicionar fotos menu.file.save=Salvar menu.file.exit=Sair -menu.track=Track +menu.track=Rota menu.track.undo=Desfazer menu.track.clearundo=Limpar lista de desfazer menu.track.deletemarked=Remover pontos marcados @@ -30,9 +30,7 @@ menu.point.editpoint=Editar ponto menu.point.deletepoint=Remover ponto menu.photo=Foto menu.photo.saveexif=Salvar para Exif -menu.photo.connect=Conectar ao ponto -menu.photo.disconnect=Desconectar do ponto -menu.photo.delete=Remover foto +menu.audio=\u00c1udio menu.view=Exibir menu.view.showsidebars=Mostrar barras laterais menu.view.browser=Mapear no navegador @@ -49,6 +47,7 @@ menu.map.zoomin=Ampliar menu.map.zoomout=Reduzir menu.map.zoomfull=Ajustar para tela menu.map.newpoint=Criar novo ponto +menu.map.drawpoints=Criar s\u00e9ries de pontos menu.map.connect=Conectar pontos da rota menu.map.autopan=Auto-ajustar menu.map.showmap=Mostrar o mapa @@ -56,11 +55,12 @@ menu.map.showscalebar=Mostrar barra de escala # Alt keys for menus altkey.menu.file=A -altkey.menu.track=T +altkey.menu.track=R altkey.menu.range=I altkey.menu.point=P altkey.menu.view=X altkey.menu.photo=F +altkey.menu.audio=U altkey.menu.settings=C altkey.menu.help=J @@ -99,14 +99,27 @@ function.setpaths=Definir caminhos do programa function.getgpsies=Obter rotas Gpsies function.uploadgpsies=Enviar rotas para o Gpsies function.lookupsrtm=Obter altitudes a partir do SRTM +function.getwikipedia=Obter artigos da Wikipedia das redondezas +function.searchwikipedianames=Procurar na Wikipedia por nome +function.downloadosm=Baixar dados OSM para a \u00e1rea function.duplicatepoint=Duplicar ponto function.setcolours=Definir cores +function.setlinewidth=Definir espessura da linha function.setlanguage=Definir idioma +function.connecttopoint=Conectar ao ponto +function.disconnectfrompoint=Desconectar do ponto +function.removephoto=Remover foto function.correlatephotos=Correlacionar fotos function.rearrangephotos=Rearrumar fotos function.rotatephotoleft=Roda foto \u00e0 esquerda function.rotatephotoright=Roda foto \u00e0 direita +function.photopopup=Mostrar janela da foto function.ignoreexifthumb=Ignorar miniatura do exif +function.loadaudio=Adicionar arquivos de \u00e1udio +function.removeaudio=Remover arquivo de \u00e1udio +function.correlateaudios=Correlacionar \u00e1udios +function.playaudio=Reproduzir arquivo de \u00e1udio +function.stopaudio=Parar arquivo de \u00e1udio function.help=Ajuda function.showkeys=Mostrar atalhos de teclado function.about=Sobre o Prune @@ -193,6 +206,7 @@ dialog.pointtype.desc=Salvar os seguintes tipos de ponto: dialog.pointtype.track=Pontos de rotas dialog.pointtype.waypoint=Pontos dialog.pointtype.photo=Pontos de foto +dialog.pointtype.audio=Pontos de \u00e1udio dialog.pointtype.selection=Apenas sele\u00e7\u00e3o dialog.confirmreversetrack.title=Confirmar invers\u00e3o dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s a revers\u00e3o.\n Voc\u00ea tem certeza que deseja reverter esta se\u00e7\u00e3o? @@ -278,12 +292,14 @@ dialog.gpsies.activity.motorbiking=Motocross dialog.gpsies.activity.snowshoe=Snowshoeing dialog.gpsies.activity.sailing=Sailing dialog.gpsies.activity.skating=Patina\u00e7\u00e3o +dialog.wikipedia.column.name=Nome do artigo +dialog.wikipedia.column.distance=Dist\u00e2ncia dialog.correlate.notimestamps=N\u00e3o existem data-hora nos dados dos pontos, assim n\u00e3o h\u00e1 nada para correlacionar com as fotos dialog.correlate.nouncorrelatedphotos=Existem fotos n\u00e3o correlacionadas.\nVoc\u00ea tem certeza que deseja continuar? dialog.correlate.photoselect.intro=Selecione uma destas fotos correlacionadas para usar como diferen\u00e7a de tempo -dialog.correlate.photoselect.photoname=Nome da foto -dialog.correlate.photoselect.timediff=Diferen\u00e7a de tempo -dialog.correlate.photoselect.photolater=Foto \u00e9 mais recente +dialog.correlate.select.photoname=Nome da foto +dialog.correlate.select.timediff=Diferen\u00e7a de tempo +dialog.correlate.select.photolater=Foto \u00e9 mais recente dialog.correlate.options.tip=Dica: Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea. dialog.correlate.options.intro=Selecione as op\u00e7\u00f5es para correla\u00e7\u00e3o autom\u00e1tica dialog.correlate.options.offsetpanel=Diferen\u00e7a de tempo @@ -292,7 +308,9 @@ dialog.correlate.options.offset.hours=horas. dialog.correlate.options.offset.minutes=minutos e dialog.correlate.options.offset.seconds=segundos dialog.correlate.options.photolater=Foto mais recente que o ponto -dialog.correlate.options.pointlater=Ponto mais recente que a foto +dialog.correlate.options.pointlaterphoto=Ponto mais recente que a foto +dialog.correlate.options.audiolater=\u00e1udio mais recente que o ponto +dialog.correlate.options.pointlateraudio=Ponto mais recente que a \u00e1udio dialog.correlate.options.limitspanel=Limites de correla\u00e7\u00e3o dialog.correlate.options.notimelimit=Nenhum limite de tempo dialog.correlate.options.timelimit=Limite de tempo @@ -300,6 +318,15 @@ dialog.correlate.options.nodistancelimit=Nenhum limite de dist\u00e2ncia dialog.correlate.options.distancelimit=Limite de dist\u00e2ncia dialog.correlate.options.correlate=Correlacionar dialog.correlate.alloutsiderange=Todas as fotos est\u00e3o fora do intervalo de tempo da rota, assim nenhuma pode ser correlacionada.\n Tente mudar a diferen\u00e7a de tempo ou manualmente correlacionar pelo menos uma foto. +dialog.correlate.filetimes=Hora do arquivo denota: +dialog.correlate.filetimes2=do clipe de \u00e1udio +dialog.correlate.correltimes=Para correlacionar, usar: +dialog.correlate.timestamp.beginning=In\u00edcio +dialog.correlate.timestamp.middle=Meio +dialog.correlate.timestamp.end=Fim +dialog.correlate.audioselect.intro=Selecione um destes \u00e1udios correlacionados para usar como intervalo de tempo +dialog.correlate.select.audioname=Nome do \u00e1udio +dialog.correlate.select.audiolater=\u00c1udio posterior dialog.rearrangephotos.desc=Selecione o destino e a ordena\u00e7\u00e3o dos pontos das fotos dialog.rearrangephotos.tostart=Mover para o in\u00edcio dialog.rearrangephotos.toend=Mover para o fim @@ -380,6 +407,7 @@ dialog.saveconfig.prune.diskcache=Cache de mapas dialog.saveconfig.prune.kmzimagewidth=Largura da imagem KMZ dialog.saveconfig.prune.kmzimageheight=Altura da imagem KMZ dialog.saveconfig.prune.colourscheme=Esquema de cores +dialog.saveconfig.prune.linewidth=Espessura da linha dialog.saveconfig.prune.kmltrackcolour=Cor da rota KML dialog.setpaths.intro=Se voc\u00ea precisar, voc\u00ea pode escolher os caminhos para as aplica\u00e7\u00f5es externas: dialog.setpaths.found=Caminho encontrado? @@ -409,10 +437,12 @@ dialog.diskcache.dir=Diret\u00f3rio da cache dialog.diskcache.createdir=Criar diret\u00f3rio dialog.diskcache.nocreate=Diret\u00f3rio da cache n\u00e3o foi criado dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual +dialog.setlinewidth.text=Insira a espessura das linhas para desenhar as rotas (1-4) +dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada: +dialog.searchwikipedianames.search=Procurar por: # 3d window dialog.3d.title=Vista 3D do Prune -dialog.3d.altitudecap=Intervalo de altitude m\u00ednimo dialog.3d.altitudefactor=Fator de exagera\u00e7\u00e3o de altitude dialog.3dlines.title=Linhas da grade do Prune dialog.3dlines.empty=Nenhuma linha de grade para exibir! @@ -439,16 +469,21 @@ confirm.undo.single=opera\u00e7\u00e3o desfeita confirm.undo.multi=opera\u00e7\u00f5es desfeitas confirm.jpegload.single=foto foi adicionada confirm.jpegload.multi=fotos foram adicionadas -confirm.photo.connect=foto conectada +confirm.media.connect=m\u00eddia conectada confirm.photo.disconnect=foto desconectada -confirm.correlate.single=foto foi correlacionada -confirm.correlate.multi=fotos foram correlacionadas +confirm.audio.disconnect=\u00e1udio desconectado +confirm.media.removed=removido +confirm.correlatephotos.single=foto foi correlacionada +confirm.correlatephotos.multi=fotos foram correlacionadas confirm.createpoint=ponto criado confirm.rotatephoto=foto rotacionada confirm.running=Rodando... confirm.lookupsrtm1=Encontrado confirm.lookupsrtm2=valores de altitude confirm.deletefieldvalues=Valores do campo removidos +confirm.audioload=Arquivos de \u00e1udio adicionados +confirm.correlateaudios.single=\u00e1udio foi correlacionado +confirm.correlateaudios.multi=\u00e1udios foram correlacionados # Buttons button.ok=Ok @@ -528,10 +563,15 @@ details.range.pace=Passo details.range.gradient=Gradiente details.lists.waypoints=Pontos details.lists.photos=Fotos +details.lists.audio=\u00c1udio details.photodetails=Detalhes da foto details.nophoto=Nenhuma foto selecionada details.photo.loading=Carregando -details.photo.connected=Conectada +details.media.connected=Conectada +details.audiodetails=Detalhes do \u00e1udio +details.noaudio=Nenhum arquivo de \u00e1udio selecionado +details.audio.file=Arquivo de \u00e1udio +details.audio.playing=reproduzindo... map.overzoom=Nenhum mapa dispon\u00edvel neste n\u00edvel de amplia\u00e7\u00e3o # Field names @@ -574,6 +614,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=maps.google.com.br +wikipedia.lang=pt # Cardinals for 3d plots cardinal.n=N @@ -584,9 +625,11 @@ cardinal.w=O # Undo operations undo.load=carregar dados undo.loadphotos=carregar fotos +undo.loadaudios=carregar arquivos de \u00e1udio undo.editpoint=editar ponto undo.deletepoint=remover ponto -undo.deletephoto=remover foto +undo.removephoto=remover foto +undo.removeaudio=remover arquivo de \u00e1udio undo.deleterange=remover intervalo undo.compress=comprimir rota undo.insert=inserir pontos @@ -596,15 +639,16 @@ undo.addtimeoffset=adicionar diferen\u00e7a de tempo undo.addaltitudeoffset=adicionar diferen\u00e7a de altitude undo.rearrangewaypoints=rearrumar pontos undo.cutandmove=mover se\u00e7\u00e3o -undo.connectphoto=conectar foto -undo.disconnectphoto=desconectar foto -undo.correlate=conectar fotos +undo.connect=conectar +undo.disconnect=desconectar +undo.correlatephotos=conectar fotos undo.rearrangephotos=rearrumar fotos undo.createpoint=criar ponto undo.rotatephoto=rotacionar foto undo.convertnamestotimes=converter nomes para tempos undo.lookupsrtm=procurar altitudes a partir do STRM undo.deletefieldvalues=remover valores do campo +undo.correlateaudios=\u00e1udios correlacionados # Error messages error.save.dialogtitle=Erro ao salvar dados @@ -626,9 +670,9 @@ error.load.othererror=Erro ao ler arquivo: error.jpegload.dialogtitle=Erro ao carregar fotos error.jpegload.nofilesfound=Nenhum arquivo encontrado error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado -error.jpegload.noexiffound=Nenhuma informa\u00e7\u00e3o EXIF encontrada error.jpegload.nogpsfound=Nenhuma informa\u00e7\u00e3o de GPS encontrada error.jpegload.exifreadfailed=Falha ao ler informa\u00e7\u00f5es do EXIF. Nenhuma informa\u00e7\u00e3o do EXIF pode ser lida\nseja na biblioteca interna, seja na externa. +error.audioload.nofilesfound=Nenhum arquivo de \u00e1udio encontrado error.gpsload.unknown=Erro desconhecido error.undofailed.title=Falha ao desfazer error.undofailed.text=Falha para desfazer opera\u00e7\u00e3o @@ -646,3 +690,4 @@ error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar error.gpsies.uploadnotok=O servidor Gpsies retornou a mensagem error.gpsies.uploadfailed=O envio falhou com o erro +error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio diff --git a/tim/prune/lang/prune-texts_ro.properties b/tim/prune/lang/prune-texts_ro.properties index ea0d138..f6dd803 100644 --- a/tim/prune/lang/prune-texts_ro.properties +++ b/tim/prune/lang/prune-texts_ro.properties @@ -29,9 +29,9 @@ menu.range.start=Seteaza inceputul selectiei menu.range.end=Seteaza sfarsitul selectiei menu.photo=Foto menu.photo.saveexif=Salveaza la Exif -menu.photo.connect=Conecteaza la punct -menu.photo.disconnect=Deconecteaza de la punct -menu.photo.delete=Elimina foto +function.connecttopoint=Conecteaza la punct +function.disconnectfrompoint=Deconecteaza de la punct +function.removephoto=Elimina foto menu.view=Vizualizare menu.view.browser=Harta in browser menu.view.browser.google=Harti Google diff --git a/tim/prune/lang/prune-texts_tr.properties b/tim/prune/lang/prune-texts_tr.properties index d000f5d..ad0ea20 100644 --- a/tim/prune/lang/prune-texts_tr.properties +++ b/tim/prune/lang/prune-texts_tr.properties @@ -30,9 +30,9 @@ menu.range.start=S\u0131ran\u0131n ba\u015fkang\u0131c\u0131 se\u00e7 menu.range.end=S\u0131ran\u0131n sonu se\u00e7 menu.photo=Foto menu.photo.saveexif=Exif'te kaydet -menu.photo.connect=Noktaya ba\u011flan -menu.photo.disconnect=Noktadan kopart -menu.photo.delete=Fotoyu kald\u0131r +function.connecttopoint=Noktaya ba\u011flan +function.disconnectfrompoint=Noktadan kopart +function.removephoto=Fotoyu kald\u0131r menu.view=G\u00f6r\u00fcn\u00fcm menu.view.browser=Taray\u0131c\u0131n\u0131n haritas\u0131 menu.view.browser.google=Google haritalar\u0131 @@ -223,13 +223,13 @@ dialog.gpsies.activity.jogging=Ko\u015fma dialog.gpsies.activity.biking=Bisiklet dialog.gpsies.activity.sailing=Yelken dialog.gpsies.activity.skating=Paten -dialog.correlate.photoselect.photoname=Foto ad\u0131 -dialog.correlate.photoselect.photolater=Foto sonra +dialog.correlate.select.photoname=Foto ad\u0131 +dialog.correlate.select.photolater=Foto sonra dialog.correlate.options.offset.hours=saat, dialog.correlate.options.offset.minutes=dakika ve dialog.correlate.options.offset.seconds=saniye dialog.correlate.options.photolater=Foto noktadan sonra -dialog.correlate.options.pointlater=Nokta fotodan sonra +dialog.correlate.options.pointlaterphoto=Nokta fotodan sonra dialog.pastecoordinates.coords=Koordinatlar dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/prune/\n sitesinde bak. dialog.about.version=S\u00fcr\u00fcm @@ -290,12 +290,12 @@ dialog.colourchooser.red=K\u0131rm\u0131z\u0131 dialog.colourchooser.green=Ye\u015fil dialog.colourchooser.blue=Mavi -# Confirm messages || These are displayed as confirmation in the status bar +# Confirm messages confirm.save.ok1=Ba\u011far\u0131yla kaydedildi confirm.save.ok2=points to file confirm.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftirildi -# Buttons || These are all the texts for buttons +# Buttons button.ok=Tamam button.back=Geri button.next=\u0130leri @@ -332,7 +332,7 @@ filetype.gpx=GPX dosyalar\u0131 filetype.pov=POV dosyalar\u0131 filetype.svg=SVG dosyalar\u0131 -# Display components || These are all for the side panels showing point/range details +# Display components display.nodata=Hergangi veri y\u00fcklenmedi display.noaltitudes=Track verisinde y\u00fckseklik bilgisi yok details.trackdetails=\u0130z ayr\u0131nt\u0131lar\u0131 @@ -360,12 +360,12 @@ display.range.time.hours=saat display.range.time.days=g\u011fn details.range.avespeed=Ortalama h\u0131z\u0131 details.range.avemovingspeed=Ortalama hareketi -details.waypointsphotos.waypoints=Noktalar -details.waypointsphotos.photos=Fotolar +details.lists.waypoints=Noktalar +details.lists.photos=Fotolar details.photodetails=Foto ayr\u0131nt\u0131lar\u0131 details.nophoto=Herhangi foto se\u00e7ili de\u011fil details.photo.loading=Y\u00fckleniyor -details.photo.connected=Ba\u011fland\u0131 +details.media.connected=Ba\u011fland\u0131 map.overzoom=Fazla yak\u0131nla\u015ft\u0131n salak - harita g\u00f6r\u00fcnt\u00fclenmiyor # Field names @@ -414,12 +414,12 @@ cardinal.s=G cardinal.e=D cardinal.w=B -# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did +# Undo operations undo.load=veri y\u00fckle undo.loadphotos=fotolar y\u00fckle undo.editpoint=noktay\u0131 d\u00fczenle undo.deletepoint=noktay\u0131 sil -undo.deletephoto=foto kald\u0131r +undo.removephoto=foto kald\u0131r undo.deleterange=s\u0131ra sil undo.compress=izi s\u0131k\u0131\u015ft\u0131r undo.insert=noktalar\u0131 ekle @@ -429,9 +429,9 @@ undo.addtimeoffset=zaman de\u011fi\u015ftir undo.addaltitudeoffset=y\u00fckseklik de\u011fi\u015ftir undo.rearrangewaypoints=noktalar\u0131 yeniden diz undo.cutandmove=par\u00e7as\u0131 ta\u015f\u0131 -undo.connectphoto=fotoyu ba\u011flan -undo.disconnectphoto=fotonun ba\u011flant\u0131s\u0131 kes -undo.correlate=fotolar\u0131n ba\u011fl\u0131la\u015f\u0131m\u0131 +undo.connect=ba\u011flan +undo.disconnect=ba\u011flant\u0131s\u0131 kes +undo.correlatephotos=fotolar\u0131n ba\u011fl\u0131la\u015f\u0131m\u0131 undo.rearrangephotos=fotolar\u0131 yeniden diz undo.createpoint=noktay\u0131 olu\u015ftur undo.rotatephoto=fotoyu d\u00f6nder diff --git a/tim/prune/lang/prune-texts_zh.properties b/tim/prune/lang/prune-texts_zh.properties index 9dd050e..9c8d7b2 100644 --- a/tim/prune/lang/prune-texts_zh.properties +++ b/tim/prune/lang/prune-texts_zh.properties @@ -30,10 +30,11 @@ menu.point.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9 menu.point.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9 menu.photo=\u76f8\u7247 menu.photo.saveexif=\u5750\u6807\u4fdd\u5b58\u81f3Exif -menu.photo.connect=\u94fe\u63a5\u76f8\u7247 -menu.photo.disconnect=\u64a4\u9500\u94fe\u63a5 -menu.photo.delete=\u5220\u9664\u7167\u7247 +function.connecttopoint=\u94fe\u63a5\u76f8\u7247 +function.disconnectfrompoint=\u64a4\u9500\u94fe\u63a5 +function.removephoto=\u5220\u9664\u7167\u7247 menu.view=\u67e5\u770b +menu.view.showsidebars=\u663e\u793a\u8fb9\u6846 menu.view.browser=\u5728\u6d4f\u89c8\u5668\u4e2d\u6253\u5f00\u5730\u56fe menu.view.browser.google=Google\u5730\u56fe menu.view.browser.openstreetmap=Openstreet\u5730\u56fe @@ -60,11 +61,13 @@ function.sendtogps=\u53d1\u9001\u81f3GPS function.exportkml=\u8f93\u51faKML\u6587\u4ef6 function.exportgpx=\u8f93\u51faGPX\u6587\u4ef6 function.exportpov=\u8f93\u51faPOV\u6587\u4ef6 +function.exportsvg=\u8f93\u51faSVG\u6587\u4ef6 function.editwaypointname=\u7f16\u8f91\u822a\u70b9\u540d function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u793a\u8981\u5220\u9664\u822a\u70b9\uff09 function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4 +function.deletefieldvalues=\u5220\u9664\u533a\u57df\u6570\u503c function.findwaypoint=\u67e5\u627e\u822a\u70b9 function.pastecoordinates=\u8f93\u5165\u65b0\u5750\u6807 function.charts=\u9ad8\u5ea6\u901f\u5ea6\u56fe\u8868 @@ -75,6 +78,7 @@ function.setmapbg=\u80cc\u666f\u5730\u56fe function.setkmzimagesize=\u8bbe\u7f6eKMZ\u56fe\u50cf\u5c3a\u5bf8 function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84 function.getgpsies=Gpsies\u8f68\u8ff9 +function.uploadgpsies=\u8f68\u8ff9\u4e0a\u4f20\u5230 Gpsies function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f function.duplicatepoint=\u590d\u5236\u70b9 function.setcolours=\u8bbe\u7f6e\u989c\u8272 @@ -116,6 +120,8 @@ dialog.openoptions.deliminfo.fields=\u6570\u636e\u6bb5 dialog.openoptions.deliminfo.norecords=\u65e0\u7eaa\u5f55 dialog.openoptions.altitudeunits=\u9ad8\u5ea6\u5355\u4f4d dialog.open.contentsdoubled=\u6587\u4ef6\u542b\u6709\u4e24\u5957\u70b9\u4fe1\u606f\uff0c\u4e00\u5957\u822a\u70b9\u4fe1\u606f\u548c\u4e00\u5957\u8f68\u8ff9\u70b9\u4fe1\u606f +dialog.selecttracks.intro=\u9009\u62e9\u8981\u5bfc\u5165\u7684\u8f68\u8ff9 +dialog.selecttracks.noname=\u672a\u547d\u540d dialog.jpegload.subdirectories=\u542b\u6b21\u7ea7\u65b9\u4f4d dialog.jpegload.loadjpegswithoutcoords=\u542b\u65e0\u5750\u6807\u70b9\u76f8\u7247 dialog.jpegload.loadjpegsoutsidearea=\u542b\u533a\u57df\u5916\u76f8\u7247 @@ -160,6 +166,10 @@ dialog.exportpov.modelstyle=\u6a21\u578b\u7c7b\u578b dialog.exportpov.ballsandsticks=\u7403\u548c\u6746 dialog.exportpov.tubesandwalls=\u7ba1\u548c\u5899 dialog.exportpov.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\n\u662f\u5426\u7ee7\u7eed\uff1f +dialog.exportsvg.text=\u9009\u62e9\u8f93\u51faSVG\u6587\u4ef6\u7684\u53c2\u6570 +dialog.exportsvg.phi=\u963f\u6cfd\u8def\u89d2\u5ea6 +dialog.exportsvg.theta=\u9ad8\u7a0b\u89d2\u5ea6 +dialog.exportsvg.gradients=\u4f7f\u7528\u6e10\u53d8\u8272 dialog.pointtype.desc=\u4fdd\u5b58\u4e0b\u5217\u70b9\uff1a dialog.pointtype.track=\u8f68\u8ff9\u70b9 dialog.pointtype.waypoint=\u822a\u70b9 @@ -236,7 +246,11 @@ dialog.gpsies.column.length=\u957f\u5ea6 dialog.gpsies.description=\u63cf\u8ff0 dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0 dialog.gpsies.nonefound=\u672a\u627e\u5230\u8f68\u8ff9 -dialog.gpsies.activities=\u6d3b\u52a8,\u9002\u5408 +dialog.gpsies.username=Gpsies\u7f51\u7ad9\u7528\u6237\u540d +dialog.gpsies.password=Gpsies\u7f51\u7ad9\u5bc6\u7801 +dialog.gpsies.keepprivate=\u8f68\u8ff9\u4fdd\u5bc6\uff08\u975e\u516c\u5f00\uff09 +dialog.gpsies.confirmopenpage=\u6253\u5f00\u4e0a\u4f20\u8f68\u8ff9\u7684\u7f51\u7ad9\uff1f +dialog.gpsies.activities=\u6d3b\u52a8\u7c7b\u578b dialog.gpsies.activity.trekking=\u5f92\u6b65 dialog.gpsies.activity.walking=\u7ade\u8d70 dialog.gpsies.activity.jogging=\u8dd1\u6b65 @@ -248,9 +262,9 @@ dialog.gpsies.activity.skating=\u6ed1\u51b0 dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u76f8\u7247\u65e0\u6cd5\u94fe\u63a5 dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u76f8\u7247\u5df2\u94fe\u63a5\n\u7ee7\u7eed\uff1f dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u76f8\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb -dialog.correlate.photoselect.photoname=\u76f8\u7247\u540d -dialog.correlate.photoselect.timediff=\u65f6\u95f4\u5dee -dialog.correlate.photoselect.photolater=\u76f8\u7247\u5ef6\u540e +dialog.correlate.select.photoname=\u76f8\u7247\u540d +dialog.correlate.select.timediff=\u65f6\u95f4\u5dee +dialog.correlate.select.photolater=\u76f8\u7247\u5ef6\u540e dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb dialog.correlate.options.intro=\u9009\u62e9\u81ea\u52a8\u94fe\u63a5\u8bbe\u7f6e dialog.correlate.options.offsetpanel=\u65f6\u95f4\u504f\u79fb @@ -259,7 +273,7 @@ dialog.correlate.options.offset.hours=\u5c0f\u65f6 dialog.correlate.options.offset.minutes=\u5206\u949f dialog.correlate.options.offset.seconds=\u79d2 dialog.correlate.options.photolater=\u76f8\u7247\u6ede\u540e\u4e8e\u8f68\u8ff9\u70b9 -dialog.correlate.options.pointlater=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u76f8\u7247 +dialog.correlate.options.pointlaterphoto=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u76f8\u7247 dialog.correlate.options.limitspanel=\u94fe\u63a5\u9650\u5236 dialog.correlate.options.notimelimit=\u65e0\u65f6\u95f4\u9650\u5236 dialog.correlate.options.timelimit=\u65f6\u95f4\u9650\u5236 @@ -352,6 +366,7 @@ dialog.setpaths.intro=\u82e5\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a dialog.setpaths.found=\u627e\u5230\u8def\u5f84\uff1f dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f dialog.addaltitude.desc=\u9ad8\u5ea6\u504f\u79fb +dialog.lookupsrtm.overwritezeros=\u8986\u76d6\u9ad8\u5ea6\u503c\u4e3a\u96f6\u7684\u70b9\uff1f dialog.setcolours.intro=\u70b9\u6309\u8272\u677f\u4ee5\u6539\u53d8\u989c\u8272 dialog.setcolours.background=\u80cc\u666f dialog.setcolours.borders=\u8fb9\u754c @@ -374,15 +389,15 @@ dialog.diskcache.save=\u5730\u56fe\u56fe\u7247\u4fdd\u5b58\u5230\u7535\u8111 dialog.diskcache.dir=\u4fdd\u5b58\u8def\u5f84 dialog.diskcache.createdir=\u65b0\u5efa\u8def\u5f84 dialog.diskcache.nocreate=\u672a\u65b0\u5efa\u8def\u5f84 +dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u533a\u57df # 3d window dialog.3d.title=Prune 3D \u663e\u793a -dialog.3d.altitudecap=\u9ad8\u5ea6\u6bd4\u4f8b dialog.3dlines.title=Prune \u7f51\u683c\u7ebf dialog.3dlines.empty=\u65e0\u6cd5\u663e\u793a\u7f51\u683c\u7ebf dialog.3dlines.intro=3D \u7f51\u683c\u7ebf -# Confirm messages || These are displayed as confirmation in the status bar +# Confirm messages confirm.loadfile=\u6570\u636e\u5df2\u4ece\u6587\u4ef6\u5bfc\u5165 confirm.save.ok1=\u4fdd\u5b58\u6210\u529f confirm.save.ok2=\u5df2\u4fdd\u5b58\u7684\u8f68\u8ff9\u70b9 @@ -403,17 +418,16 @@ confirm.undo.single=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c confirm.undo.multi=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c confirm.jpegload.single=\u5df2\u52a0\u5165\u76f8\u7247 confirm.jpegload.multi=\u5df2\u52a0\u5165\u76f8\u7247 -confirm.photo.connect=\u76f8\u7247\u5df2\u94fe\u63a5 confirm.photo.disconnect=\u76f8\u7247\u672a\u94fe\u63a5 -confirm.correlate.single=\u76f8\u7247\u5df2\u94fe\u63a5 -confirm.correlate.multi=\u76f8\u7247\u5df2\u94fe\u63a5 +confirm.correlatephotos.single=\u76f8\u7247\u5df2\u94fe\u63a5 +confirm.correlatephotos.multi=\u76f8\u7247\u5df2\u94fe\u63a5 confirm.createpoint=\u5df2\u521b\u5efa\u70b9 confirm.rotatephoto=\u76f8\u7247\u5df2\u65cb\u8f6c confirm.running=\u8bf7\u7a0d\u7b49... confirm.lookupsrtm1=\u627e\u5230 confirm.lookupsrtm2=\u9ad8\u5ea6\u503c -# Buttons || These are all the texts for buttons +# Buttons button.ok=\u786e\u5b9a button.back=\u8fd4\u56de button.next=\u4e0b\u4e00\u6b65 @@ -453,8 +467,9 @@ filetype.kmz=KMZ\u6587\u4ef6 filetype.gpx=GPX\u6587\u4ef6 filetype.pov=POV\u6587\u4ef6 filetype.svg=SVG\u6587\u4ef6 +filetype.audio=WAV,OGG,MP3\u6587\u4ef6 -# Display components || These are all for the side panels showing point/range details +# Display components display.nodata=\u65e0\u6570\u636e display.noaltitudes=\u8f68\u8ff9\u6570\u636e\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f display.notimestamps=\u8f68\u8ff9\u6570\u636e\u672a\u542b\u65f6\u95f4\u4fe1\u606f @@ -487,12 +502,12 @@ details.range.maxspeed=\u6700\u5927\u901f\u5ea6 details.range.numsegments=\u6bb5\u6570 details.range.pace=\u6b65\u901f details.range.gradient=\u5761\u5ea6 -details.waypointsphotos.waypoints=\u822a\u70b9 -details.waypointsphotos.photos=\u76f8\u7247 +details.lists.waypoints=\u822a\u70b9 +details.lists.photos=\u76f8\u7247 details.photodetails=\u76f8\u7247\u4fe1\u606f details.nophoto=\u65e0\u76f8\u7247\u88ab\u9009\u4e2d details.photo.loading=\u6b63\u5bfc\u5165 -details.photo.connected=\u5df2\u94fe\u63a5 +details.media.connected=\u5df2\u94fe\u63a5 map.overzoom=\u5728\u6b64\u653e\u5927\u5c3a\u5bf8\u4e0b\u65e0\u5730\u56fe\u8d44\u6599 # Field names @@ -535,6 +550,7 @@ units.iso8601=ISO 8601 # External urls url.googlemaps=ditu.google.cn +wikipedia.lang=zh # Cardinals for 3d plots cardinal.n=N @@ -542,12 +558,12 @@ cardinal.s=S cardinal.e=E cardinal.w=W -# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did +# Undo operations undo.load=\u5bfc\u5165\u6570\u636e undo.loadphotos=\u5bfc\u5165\u76f8\u7247 undo.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9 undo.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9 -undo.deletephoto=\u5220\u9664\u76f8\u7247 +undo.removephoto=\u5220\u9664\u76f8\u7247 undo.deleterange=\u5220\u9664\u6bb5 undo.compress=\u538b\u7f29\u8f68\u8ff9 undo.insert=\u63d2\u5165\u822a\u70b9 @@ -557,9 +573,9 @@ undo.addtimeoffset=\u6dfb\u52a0\u65f6\u95f4\u504f\u79fb undo.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb undo.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u822a\u70b9 undo.cutandmove=\u79fb\u52a8\u6bb5 -undo.connectphoto=\u94fe\u63a5\u76f8\u7247 -undo.disconnectphoto=\u65ad\u5f00\u94fe\u63a5 -undo.correlate=\u94fe\u63a5\u76f8\u7247 +undo.connect=\u94fe\u63a5\u76f8\u7247 +undo.disconnect=\u65ad\u5f00\u94fe\u63a5 +undo.correlatephotos=\u94fe\u63a5\u76f8\u7247 undo.rearrangephotos=\u91cd\u62cd\u76f8\u7247 undo.createpoint=\u521b\u5efa\u8f68\u8ff9\u70b9 undo.rotatephoto=\u65cb\u8f6c\u76f8\u7247 @@ -586,7 +602,6 @@ error.load.othererror=\u8bfb\u6587\u4ef6\u9519\u8bef error.jpegload.dialogtitle=\u5bfc\u5165\u76f8\u7247\u9519\u8bef error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6 error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6 -error.jpegload.noexiffound=\u627e\u4e0d\u5230EXIF\u4fe1\u606f error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\u3002\u9700\u8981\u5185\u90e8\n\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6 error.gpsload.unknown=\u672a\u77e5\u9519\u8bef diff --git a/tim/prune/load/AudioFileFilter.java b/tim/prune/load/AudioFileFilter.java new file mode 100644 index 0000000..3d8675e --- /dev/null +++ b/tim/prune/load/AudioFileFilter.java @@ -0,0 +1,13 @@ +package tim.prune.load; + +/** + * File filter for audio files + */ +public class AudioFileFilter extends GenericFileFilter +{ + /** Constructor */ + public AudioFileFilter() + { + super("filetype.audio", new String[] {"mp3", "ogg", "wav"}); + } +} diff --git a/tim/prune/load/AudioLoader.java b/tim/prune/load/AudioLoader.java new file mode 100644 index 0000000..1997db2 --- /dev/null +++ b/tim/prune/load/AudioLoader.java @@ -0,0 +1,99 @@ +package tim.prune.load; + +import java.io.File; +import java.util.TreeSet; +import javax.swing.JFileChooser; + +import tim.prune.App; +import tim.prune.GenericFunction; +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.config.Config; +import tim.prune.data.AudioFile; +import tim.prune.undo.UndoLoadAudios; + +/** + * Class to manage the loading of audio files + */ +public class AudioLoader extends GenericFunction +{ + private JFileChooser _fileChooser = null; + private GenericFileFilter _fileFilter = null; + private TreeSet _fileList = null; + + + /** + * Constructor + * @param inApp Application object to inform of data load + */ + public AudioLoader(App inApp) + { + super(inApp); + _fileFilter = new AudioFileFilter(); + } + + /** Get the name key */ + public String getNameKey() { + return "function.loadaudio"; + } + + /** + * Open the GUI to select options and start the load + */ + public void begin() + { + // Create file chooser if necessary + if (_fileChooser == null) + { + _fileChooser = new JFileChooser(); + _fileChooser.setMultiSelectionEnabled(true); + _fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + _fileChooser.setFileFilter(_fileFilter); + _fileChooser.setDialogTitle(I18nManager.getText(getNameKey())); + // start from directory in config if already set by other operations + String configDir = Config.getConfigString(Config.KEY_PHOTO_DIR); + if (configDir == null) {configDir = Config.getConfigString(Config.KEY_TRACK_DIR);} + if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));} + } + // Show file dialog to choose file / directory(ies) + if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION) + { + _fileList = new TreeSet(new MediaSorter()); + processFileList(_fileChooser.getSelectedFiles()); + final int numFiles = _fileList.size(); + if (numFiles == 0) { + _app.showErrorMessage(getNameKey(), "error.audioload.nofilesfound"); + } + else + { + // Construct undo object + UndoLoadAudios undo = new UndoLoadAudios(numFiles); + _app.getTrackInfo().addAudios(_fileList); + _app.completeFunction(undo, I18nManager.getText("confirm.audioload")); + UpdateMessageBroker.informSubscribers(); + } + } + } + + /** + * Process an array of File objects to load + * @param inFiles array of selected Files + */ + private void processFileList(File[] inFiles) + { + for (File file : inFiles) + { + if (file.exists() && file.canRead()) + { + if (file.isFile()) { + if (_fileFilter.accept(file)) { + _fileList.add(new AudioFile(file)); + } + } + else if (file.isDirectory()) { + processFileList(file.listFiles()); + } + } + } + } +} diff --git a/tim/prune/load/GenericFileFilter.java b/tim/prune/load/GenericFileFilter.java index 3af4d9e..733dee6 100644 --- a/tim/prune/load/GenericFileFilter.java +++ b/tim/prune/load/GenericFileFilter.java @@ -31,9 +31,8 @@ public class GenericFileFilter extends FileFilter _threeCharSuffixes = new String[inSuffixes.length]; _fourCharSuffixes = new String[inSuffixes.length]; int threeIndex = 0, fourIndex = 0; - for (int i=0; i 4) { // Check for three character suffixes @@ -93,15 +92,14 @@ public class GenericFileFilter extends FileFilter * @param inAllowedSuffixes array of allowed suffixes * @return true if accepted, false otherwise */ - public boolean acceptFilename(String inSuffixToCheck, String[] inAllowedSuffixes) + private static boolean acceptFilename(String inSuffixToCheck, String[] inAllowedSuffixes) { if (inSuffixToCheck != null && inAllowedSuffixes != null) { // Loop over allowed suffixes - for (int i=0; i(new PhotoSorter()); + _fileCounts = new int[3]; // files, jpegs, gps + _photos = new TreeSet(new MediaSorter()); File[] files = _fileChooser.getSelectedFiles(); // Loop recursively over selected files/directories to count files int numFiles = countFileList(files, true, _subdirCheckbox.isSelected()); @@ -166,8 +165,6 @@ public class JpegLoader implements Runnable _progressDialog.dispose(); // Sometimes dialog doesn't disappear without this dispose if (_cancelled) {return;} - //System.out.println("Finished - counts are: " + _fileCounts[0] + ", " + _fileCounts[1] - // + ", " + _fileCounts[2] + ", " + _fileCounts[3]); if (_fileCounts[0] == 0) { // No files found at all @@ -179,11 +176,6 @@ public class JpegLoader implements Runnable _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nojpegsfound"); } else if (!_noExifCheckbox.isSelected() && _fileCounts[2] == 0) - { - // Need coordinates but no exif found - _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.noexiffound"); - } - else if (!_noExifCheckbox.isSelected() && _fileCounts[3] == 0) { // Need coordinates but no gps information found _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nogpsfound"); @@ -247,18 +239,36 @@ public class JpegLoader implements Runnable if (!_fileFilter.acceptFilename(inFile.getName())) {return;} // If it's a Jpeg, we can use ExifReader to get coords, otherwise we could try exiftool (if it's installed) - // Create Photo object - Photo photo = new Photo(inFile); if (inFile.exists() && inFile.canRead()) { _fileCounts[1]++; // jpeg found } + Photo photo = createPhoto(inFile); + if (photo.getDataPoint() != null) { + _fileCounts[2]++; // photo has coordinates + } + // Check the criteria for adding the photo - check whether the photo has coordinates and if so if they're within the rectangle + if ( (photo.getDataPoint() != null || _noExifCheckbox.isSelected()) + && (photo.getDataPoint() == null || !_outsideAreaCheckbox.isEnabled() + || _outsideAreaCheckbox.isSelected() || _trackRectangle.containsPoint(photo.getDataPoint()))) + { + _photos.add(photo); + } + } + + /** + * Create a Photo object for the given file, including reading exif information + * @param inFile file object + * @return Photo object + */ + public static Photo createPhoto(File inFile) + { + // Create Photo object + Photo photo = new Photo(inFile); // Try to get information out of exif JpegData jpegData = ExifGateway.getJpegData(inFile); Timestamp timestamp = null; if (jpegData != null) { - if (jpegData.getExifDataPresent()) - {_fileCounts[2]++;} // exif found if (jpegData.isGpsValid()) { timestamp = createTimestamp(jpegData.getGpsDatestamp(), jpegData.getGpsTimestamp()); @@ -268,7 +278,6 @@ public class JpegLoader implements Runnable point.setSegmentStart(true); photo.setDataPoint(point); photo.setOriginalStatus(Photo.Status.TAGGED); - _fileCounts[3]++; } // Use exif timestamp if gps timestamp not available if (timestamp == null && jpegData.getOriginalTimestamp() != null) { @@ -290,13 +299,7 @@ public class JpegLoader implements Runnable if (photo.getDataPoint() != null) { photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.FORMAT_ISO_8601), false); } - // Check the criteria for adding the photo - check whether the photo has coordinates and if so if they're within the rectangle - if ( (photo.getDataPoint() != null || _noExifCheckbox.isSelected()) - && (photo.getDataPoint() == null || !_outsideAreaCheckbox.isEnabled() - || _outsideAreaCheckbox.isSelected() || _trackRectangle.containsPoint(photo.getDataPoint()))) - { - _photos.add(photo); - } + return photo; } diff --git a/tim/prune/load/MediaHelper.java b/tim/prune/load/MediaHelper.java new file mode 100644 index 0000000..9b029d7 --- /dev/null +++ b/tim/prune/load/MediaHelper.java @@ -0,0 +1,70 @@ +package tim.prune.load; + +import java.io.File; + +import tim.prune.data.AudioFile; +import tim.prune.data.MediaFile; +import tim.prune.data.MediaList; +import tim.prune.data.Photo; +import tim.prune.data.Track; + +/** + * Class to provide helper functions for loading media + */ +public abstract class MediaHelper +{ + /** File filters */ + private static GenericFileFilter _jpegFilter = null, _audioFilter = null; + + /** + * Construct a MediaFile object for the given path + * @param inPath path to file + * @return either Photo or AudioFile object as appropriate, or null + */ + public static MediaFile createMediaFile(String inPath) + { + if (inPath == null) {return null;} + File file = new File(inPath); + if (!file.exists() || !file.canRead() || !file.isFile()) {return null;} + // Initialise filters if necessary + if (_jpegFilter == null) { + _jpegFilter = new JpegFileFilter(); + _audioFilter = new AudioFileFilter(); + } + // Check if filename looks like a jpeg + if (_jpegFilter.acceptFilename(file.getName())) { + return JpegLoader.createPhoto(file); + } + // Check if filename looks like an audio file + if (_audioFilter.acceptFilename(file.getName())) { + return new AudioFile(file); + } + // Neither photo nor audio + return null; + } + + /** + * Add all the media from the given track into the specified list + * @param inTrack track from which media to take + * @param inMediaList list to which media should be added + * @param inMediaClass class of media, either Photo or AudioFile + */ + public static void addMediaFromTrack(Track inTrack, MediaList inMediaList, + Class inMediaClass) + { + final int numPoints = inTrack.getNumPoints(); + for (int i=0; i +public class MediaSorter implements Comparator { /** - * Compare two photos + * Compare two media files * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ - public int compare(Photo o1, Photo o2) + public int compare(MediaFile o1, MediaFile o2) { File file1 = o1.getFile(); File file2 = o2.getFile(); diff --git a/tim/prune/load/TrackNameList.java b/tim/prune/load/TrackNameList.java index 287e71d..2f2b7dc 100644 --- a/tim/prune/load/TrackNameList.java +++ b/tim/prune/load/TrackNameList.java @@ -30,10 +30,9 @@ public class TrackNameList _pointNum++; if (inIsTrackpoint) { - // System.out.println("Point " + _pointNum + " is in track '" + inTrackName + "' (" + inTrackNum + ")"); if (inTrackNum != _trackNum) { _trackNames.add(inTrackName); - _startIndices.add(new Integer(_pointNum)); + _startIndices.add(Integer.valueOf(_pointNum)); } } _trackNum = inTrackNum; @@ -81,16 +80,4 @@ public class TrackNameList if (inTrackNum < 0 || inTrackNum >= getNumTracks()) {return 0;} return _startIndices.get(inTrackNum); } - - /** - * Print summary for debug - * @deprecated - */ - public void summarize() - { - System.out.println("File has " + getNumTracks() + " tracks:"); - for (int i=0; i _pointList = new ArrayList(); + private ArrayList _linkList = new ArrayList(); private TrackNameList _trackNameList = new TrackNameList(); @@ -40,48 +37,47 @@ public class GpxHandler extends XmlHandler Attributes attributes) throws SAXException { // Read the parameters for waypoints and track points - if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept")) + String tag = qName.toLowerCase(); + if (tag.equals("wpt") || tag.equals("trkpt") || tag.equals("rtept")) { _insidePoint = true; - _insideWaypoint = qName.equalsIgnoreCase("wpt"); - _isTrackPoint = qName.equalsIgnoreCase("trkpt"); - int numAttributes = attributes.getLength(); + _insideWaypoint = tag.equals("wpt"); + _isTrackPoint = tag.equals("trkpt"); + final int numAttributes = attributes.getLength(); for (int i=0; i=selStart && p<=selEnd)); if (!savePoint) {continue;} numSaved++; @@ -512,9 +513,8 @@ public class FileSaver info = _model.getFieldInfo(f); if (info.isSelected()) { - if (!firstField) - { - // output field separator + // output field separator + if (!firstField) { buffer.append(delimiter); } saveField(buffer, point, info.getField(), coordFormat, altitudeFormat, timestampFormat); diff --git a/tim/prune/save/GpsSaver.java b/tim/prune/save/GpsSaver.java index 944a7e4..76ec88f 100644 --- a/tim/prune/save/GpsSaver.java +++ b/tim/prune/save/GpsSaver.java @@ -6,6 +6,8 @@ import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; @@ -121,6 +123,19 @@ public class GpsSaver extends GenericFunction implements Runnable gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT); gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20)); mainPanel.add(gridPanel); + // close dialog when escape pressed + KeyAdapter closer = new KeyAdapter() { + public void keyReleased(KeyEvent e) + { + // close dialog if escape pressed + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + _dialog.dispose(); + } + } + }; + _deviceField.addKeyListener(closer); + _formatField.addKeyListener(closer); + _trackNameField.addKeyListener(closer); // checkboxes ChangeListener checkboxListener = new ChangeListener() { @@ -257,7 +272,7 @@ public class GpsSaver extends GenericFunction implements Runnable if (trackName == null || trackName.equals("")) {trackName = "prune";} // Generate the GPX file and send to the GPS OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream()); - boolean[] saveFlags = {true, true, true, false, true}; // export everything + boolean[] saveFlags = {true, true, true, true, false, true}; // export everything GpxExporter.exportData(writer, _app.getTrackInfo(), trackName, null, saveFlags, false); writer.close(); diff --git a/tim/prune/save/GpxExporter.java b/tim/prune/save/GpxExporter.java index 8203593..d57d5f9 100644 --- a/tim/prune/save/GpxExporter.java +++ b/tim/prune/save/GpxExporter.java @@ -6,8 +6,6 @@ import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -35,11 +33,15 @@ import tim.prune.I18nManager; import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; import tim.prune.data.Altitude; +import tim.prune.data.AudioFile; import tim.prune.data.Coordinate; import tim.prune.data.DataPoint; import tim.prune.data.Field; +import tim.prune.data.MediaFile; +import tim.prune.data.Photo; import tim.prune.data.Timestamp; import tim.prune.data.TrackInfo; +import tim.prune.gui.DialogCloser; import tim.prune.load.GenericFileFilter; import tim.prune.save.xml.GpxCacherList; @@ -133,13 +135,7 @@ public class GpxExporter extends GenericFunction implements Runnable dialogPanel.add(mainPanel, BorderLayout.CENTER); // close dialog if escape pressed - _nameField.addKeyListener(new KeyAdapter() { - public void keyReleased(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - _dialog.dispose(); - } - } - }); + _nameField.addKeyListener(new DialogCloser(_dialog)); // button panel at bottom JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT)); @@ -248,9 +244,9 @@ public class GpxExporter extends GenericFunction implements Runnable { // normal writing to file writer = new OutputStreamWriter(new FileOutputStream(_exportFile)); - boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(), - _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getJustSelection(), - _timestampsCheckbox.isSelected()}; + final boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(), + _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getAudiopointsSelected(), + _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()}; // write file final int numPoints = exportData(writer, _trackInfo, _nameField.getText(), _descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected()); @@ -289,7 +285,7 @@ public class GpxExporter extends GenericFunction implements Runnable * @param inInfo track info object * @param inName name of track (optional) * @param inDesc description of track (optional) - * @param inSaveFlags array of booleans to export tracks, waypoints, photos, timestamps + * @param inSaveFlags array of booleans to export tracks, waypoints, photos, audios, selection, timestamps * @param inUseCopy true to copy source if available * @return number of points written * @throws IOException if io errors occur on write @@ -319,12 +315,12 @@ public class GpxExporter extends GenericFunction implements Runnable int i = 0; DataPoint point = null; - boolean hasTrackpoints = false; final boolean exportTrackpoints = inSaveFlags[0]; final boolean exportWaypoints = inSaveFlags[1]; final boolean exportPhotos = inSaveFlags[2]; - final boolean exportSelection = inSaveFlags[3]; - final boolean exportTimestamps = inSaveFlags[4]; + final boolean exportAudios = inSaveFlags[3]; + final boolean exportSelection = inSaveFlags[4]; + final boolean exportTimestamps = inSaveFlags[5]; // Examine selection int selStart = -1, selEnd = -1; if (exportSelection) { @@ -348,27 +344,25 @@ public class GpxExporter extends GenericFunction implements Runnable inWriter.write('\n'); } else { - exportWaypoint(point, inWriter, exportTimestamps); + exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios); } numSaved++; } } - else { - hasTrackpoints = true; - } } } // Export both route points and then track points - if (hasTrackpoints && (exportTrackpoints || exportPhotos)) + if (exportTrackpoints || exportPhotos || exportAudios) { // Output all route points (if any) numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos, - exportTimestamps, true, gpxCachers, "1\n", null, "\t\n"); + exportAudios, exportTimestamps, true, gpxCachers, "1\n", + null, "\t\n"); // Output all track points, if any String trackStart = "\t" + trackName + "1\n"; numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos, - exportTimestamps, false, gpxCachers, "\n\t\n", - "\t\n"); + exportAudios, exportTimestamps, false, gpxCachers, "\n\t\n", "\t\n"); } inWriter.write("\n"); @@ -382,7 +376,8 @@ public class GpxExporter extends GenericFunction implements Runnable * @param inExportSelection true to just output current selection * @param inExportTrackpoints true to output track points * @param inExportPhotos true to output photo points - * @param exportTimestamps true to include timestamps in export + * @param inExportAudios true to output audio points + * @param inExportTimestamps true to include timestamps in export * @param inOnlyCopies true to only export if source can be copied * @param inCachers list of GpxCachers * @param inPointTag tag to match for each point @@ -392,9 +387,9 @@ public class GpxExporter extends GenericFunction implements Runnable */ private static int writeTrackPoints(OutputStreamWriter inWriter, TrackInfo inInfo, boolean inExportSelection, boolean inExportTrackpoints, - boolean inExportPhotos, boolean exportTimestamps, boolean inOnlyCopies, - GpxCacherList inCachers, String inPointTag, String inStartTag, - String inSegmentTag, String inEndTag) + boolean inExportPhotos, boolean inExportAudios, boolean exportTimestamps, + boolean inOnlyCopies, GpxCacherList inCachers, String inPointTag, + String inStartTag, String inSegmentTag, String inEndTag) throws IOException { // Note: far too many input parameters to this method but avoids duplication @@ -409,13 +404,15 @@ public class GpxExporter extends GenericFunction implements Runnable DataPoint point = inInfo.getTrack().getPoint(i); if ((!inExportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint()) { - if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos)) + if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos) + || (point.getAudio()!=null && inExportAudios)) { + if (point.getPhoto() != null) System.out.println("Writetrackpoints: Point has photo " + point.getPhoto().getFile().getName()); // get the source from the point (if any) String pointSource = getPointSource(inCachers, point); - boolean writePoint = (pointSource != null && pointSource.toLowerCase().startsWith(inPointTag)) - || (pointSource == null && !inOnlyCopies); - if (writePoint) + // Clear point source if it's the wrong type of point (eg changed from waypoint or route point) + if (pointSource != null && !pointSource.toLowerCase().startsWith(inPointTag)) {pointSource = null;} + if (pointSource != null || !inOnlyCopies) { // restart track segment if necessary if ((numSaved > 0) && point.getSegmentStart() && (inSegmentTag != null)) { @@ -423,11 +420,12 @@ public class GpxExporter extends GenericFunction implements Runnable } if (numSaved == 0) {inWriter.write(inStartTag);} if (pointSource != null) { + if (point.getPhoto() != null) System.out.println("Point has photo but using source"); inWriter.write(pointSource); inWriter.write('\n'); } else { - if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps);} + if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps, inExportPhotos, inExportAudios);} } numSaved++; } @@ -456,6 +454,10 @@ public class GpxExporter extends GenericFunction implements Runnable source = replaceGpxTags(source, "", "", inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); source = replaceGpxTags(source, "", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601)); if (inPoint.isWaypoint()) {source = replaceGpxTags(source, "", "", inPoint.getWaypointName());} // only for waypoints + // photo / audio links + if (source != null && (inPoint.hasMedia() || source.indexOf("") > 0)) { + source = replaceMediaLinks(source, makeMediaLink(inPoint)); + } return source; } @@ -494,6 +496,43 @@ public class GpxExporter extends GenericFunction implements Runnable return null; } + /** + * Replace the media tags in the given XML string + * @param inSource source XML for point + * @param inValue value for the current point + * @return modified String, or null if not possible + */ + private static String replaceMediaLinks(String inSource, String inValue) + { + if (inSource == null) {return null;} + // Note that this method is very similar to replaceGpxTags except there can be multiple link tags + // and the tags must have attributes. So either one heavily parameterized method or two. + // Look for start and end tags within source + final String STARTTEXT = " 0 && endPos > 0) + { + String origValue = inSource.substring(startPos, endPos + ENDTEXT.length()); + if (inValue != null && origValue.equals(inValue)) { + // Value unchanged + return inSource; + } + else if (inValue == null || inValue.equals("")) { + // Need to delete value + return inSource.substring(0, startPos) + inSource.substring(endPos + ENDTEXT.length()); + } + else { + // Need to replace value + return inSource.substring(0, startPos) + inValue + inSource.substring(endPos + ENDTEXT.length()); + } + } + // Value not found for this field in original source + if (inValue == null || inValue.equals("")) {return inSource;} + return null; + } + /** * Get the header string for the xml document including encoding * @param inWriter writer object @@ -534,9 +573,12 @@ public class GpxExporter extends GenericFunction implements Runnable * @param inPoint waypoint to export * @param inWriter writer object * @param inTimestamps true to export timestamps too + * @param inPhoto true to export link to photo + * @param inAudio true to export link to audio * @throws IOException on write failure */ - private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps) + private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps, + boolean inPhoto, boolean inAudio) throws IOException { inWriter.write("\t"); inWriter.write(inPoint.getWaypointName().trim()); inWriter.write("\n"); + // Media links, if any + if (inPhoto && inPoint.getPhoto() != null) + { + inWriter.write("\t\t"); + inWriter.write(makeMediaLink(inPoint.getPhoto())); + inWriter.write('\n'); + } + if (inAudio && inPoint.getAudio() != null) + { + inWriter.write("\t\t"); + inWriter.write(makeMediaLink(inPoint.getAudio())); + inWriter.write('\n'); + } // write waypoint type if any String type = inPoint.getFieldValue(Field.WAYPT_TYPE); if (type != null) @@ -583,10 +638,14 @@ public class GpxExporter extends GenericFunction implements Runnable * @param inPoint trackpoint to export * @param inWriter writer object * @param inTimestamps true to export timestamps too + * @param inExportPhoto true to export photo link + * @param inExportAudio true to export audio link */ - private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps) + private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps, + boolean inExportPhoto, boolean inExportAudio) throws IOException { + if (inPoint.getPhoto() != null) System.out.println("Point has photo " + inPoint.getPhoto().getFile().getName()); inWriter.write("\t\t"); } + // photo, audio + if (inPoint.getPhoto() != null && inExportPhoto) { + inWriter.write(makeMediaLink(inPoint.getPhoto())); + } + if (inPoint.getAudio() != null && inExportAudio) { + inWriter.write(makeMediaLink(inPoint.getAudio())); + } inWriter.write("\n"); } + + /** + * Make the xml for the media link(s) + * @param inPoint point to generate text for + * @return link tags, or null if no links + */ + private static String makeMediaLink(DataPoint inPoint) + { + Photo photo = inPoint.getPhoto(); + AudioFile audio = inPoint.getAudio(); + if (photo == null && audio == null) { + return null; + } + String linkText = ""; + if (photo != null) { + linkText = makeMediaLink(photo); + } + if (audio != null) { + linkText += makeMediaLink(audio); + } + return linkText; + } + + /** + * Make the media link for a single media item + * @param inMedia media item, either photo or audio + * @return link for this media + */ + private static String makeMediaLink(MediaFile inMedia) + { + return "" + inMedia.getFile().getName() + ""; + } } diff --git a/tim/prune/save/KmlExporter.java b/tim/prune/save/KmlExporter.java index a5a4ee2..7f90dbc 100644 --- a/tim/prune/save/KmlExporter.java +++ b/tim/prune/save/KmlExporter.java @@ -49,6 +49,7 @@ import tim.prune.data.Track; import tim.prune.data.TrackInfo; import tim.prune.gui.ColourChooser; import tim.prune.gui.ColourPatch; +import tim.prune.gui.DialogCloser; import tim.prune.gui.ImageUtils; import tim.prune.load.GenericFileFilter; @@ -81,9 +82,6 @@ public class KmlExporter extends GenericFunction implements Runnable // Default width and height of thumbnail images in Kmz private static final int DEFAULT_THUMBNAIL_WIDTH = 240; private static final int DEFAULT_THUMBNAIL_HEIGHT = 240; - // Actual selected width and height of thumbnail images in Kmz - private static int THUMBNAIL_WIDTH = 0; - private static int THUMBNAIL_HEIGHT = 0; // Default track colour private static final Color DEFAULT_TRACK_COLOUR = new Color(204, 0, 0); // red @@ -143,6 +141,7 @@ public class KmlExporter extends GenericFunction implements Runnable descPanel.setLayout(new FlowLayout()); descPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.text"))); _descriptionField = new JTextField(20); + _descriptionField.addKeyListener(new DialogCloser(_dialog)); descPanel.add(_descriptionField); descPanel.setAlignmentX(Component.CENTER_ALIGNMENT); mainPanel.add(descPanel); @@ -325,10 +324,10 @@ public class KmlExporter extends GenericFunction implements Runnable _progressBar.setMaximum(exportImages?getNumPhotosToExport():1); // Determine photo thumbnail size from config - THUMBNAIL_WIDTH = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH); - if (THUMBNAIL_WIDTH < DEFAULT_THUMBNAIL_WIDTH) {THUMBNAIL_WIDTH = DEFAULT_THUMBNAIL_WIDTH;} - THUMBNAIL_HEIGHT = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT); - if (THUMBNAIL_HEIGHT < DEFAULT_THUMBNAIL_HEIGHT) {THUMBNAIL_HEIGHT = DEFAULT_THUMBNAIL_HEIGHT;} + int thumbWidth = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH); + if (thumbWidth < DEFAULT_THUMBNAIL_WIDTH) {thumbWidth = DEFAULT_THUMBNAIL_WIDTH;} + int thumbHeight = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT); + if (thumbHeight < DEFAULT_THUMBNAIL_HEIGHT) {thumbHeight = DEFAULT_THUMBNAIL_HEIGHT;} // Create array for image dimensions in case it's required _imageDimensions = new Dimension[_track.getNumPoints()]; @@ -351,7 +350,7 @@ public class KmlExporter extends GenericFunction implements Runnable { // Create thumbnails of each photo in turn and add to zip as images/image.jpg // This is done first so that photo sizes are known for later - exportThumbnails(zipOutputStream); + exportThumbnails(zipOutputStream, thumbWidth, thumbHeight); } writer = new OutputStreamWriter(zipOutputStream); // Make an entry in the zip file for the kml file @@ -414,6 +413,7 @@ public class KmlExporter extends GenericFunction implements Runnable boolean writeTrack = _pointTypeSelector.getTrackpointsSelected(); boolean writeWaypoints = _pointTypeSelector.getWaypointsSelected(); boolean writePhotos = _pointTypeSelector.getPhotopointsSelected(); + boolean writeAudios = _pointTypeSelector.getAudiopointsSelected(); boolean justSelection = _pointTypeSelector.getJustSelection(); inWriter.write("\n\n\n"); inWriter.write("\t"); @@ -421,8 +421,7 @@ public class KmlExporter extends GenericFunction implements Runnable { inWriter.write(_descriptionField.getText()); } - else - { + else { inWriter.write("Export from Prune"); } inWriter.write("\n"); @@ -438,7 +437,7 @@ public class KmlExporter extends GenericFunction implements Runnable int i = 0; DataPoint point = null; boolean hasTrackpoints = false; - boolean writtenPhotoHeader = false; + boolean writtenPhotoHeader = false, writtenAudioHeader = false; final int numPoints = _track.getNumPoints(); int numSaved = 0; int photoNum = 0; @@ -456,7 +455,7 @@ public class KmlExporter extends GenericFunction implements Runnable numSaved++; } } - else if (point.getPhoto() == null) + else if (!point.hasMedia()) { hasTrackpoints = true; } @@ -473,6 +472,17 @@ public class KmlExporter extends GenericFunction implements Runnable exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes); numSaved++; } + // Make a blob with description for each audio file + if (point.getAudio() != null && writeAudios && writeCurrentPoint) + { + if (!writtenAudioHeader) + { + inWriter.write(""); + writtenAudioHeader = true; + } + exportAudioPoint(point, inWriter, absoluteAltitudes); + numSaved++; + } } // Make a line for the track, if there is one if (hasTrackpoints && writeTrack) @@ -541,28 +551,23 @@ public class KmlExporter extends GenericFunction implements Runnable */ private void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException { - inWriter.write("\t\n\t\t"); - inWriter.write(inPoint.getWaypointName().trim()); - inWriter.write("\n"); - inWriter.write("\t\t\n"); - if (inAbsoluteAltitude && inPoint.hasAltitude()) { - inWriter.write("\t\t\tabsolute\n"); - } - else { - inWriter.write("\t\t\tclampToGround\n"); - } - inWriter.write("\t\t\t"); - inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(','); - inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(","); - if (inPoint.hasAltitude()) { - inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); - } - else { - inWriter.write("0"); - } - inWriter.write("\n\t\t\n\t\n"); + String name = inPoint.getWaypointName().trim(); + exportNamedPoint(inPoint, inWriter, name, null, null, inAbsoluteAltitude); + } + + + /** + * Export the specified audio point into the file + * @param inPoint audio point to export + * @param inWriter writer object + * @param inAbsoluteAltitude true for absolute altitude + * @throws IOException on write failure + */ + private void exportAudioPoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException + { + String name = inPoint.getAudio().getFile().getName(); + String desc = inPoint.getAudio().getFile().getAbsolutePath(); + exportNamedPoint(inPoint, inWriter, name, desc, "audio_icon", inAbsoluteAltitude); } @@ -580,18 +585,51 @@ public class KmlExporter extends GenericFunction implements Runnable int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude) throws IOException { - inWriter.write("\t\n\t\t"); - inWriter.write(inPoint.getPhoto().getFile().getName()); - inWriter.write("\n"); + String name = inPoint.getPhoto().getFile().getName(); + String desc = null; if (inImageLink) { Dimension imageSize = _imageDimensions[inPointNumber]; - // Write out some html for the thumbnail images - inWriter.write("" - + "
" + inPoint.getPhoto().getFile().getName() + "
]]>
"); + + "

" + inPoint.getPhoto().getFile().getName() + "
]]>"; + } + // Export point + exportNamedPoint(inPoint, inWriter, name, desc, "camera_icon", inAbsoluteAltitude); + } + + + /** + * Export the specified named point into the file, like waypoint or photo point + * @param inPoint data point + * @param inWriter writer object + * @param inName name of point + * @param inDesc description of point, or null + * @param inStyle style of point, or null + * @param inAbsoluteAltitude true for absolute altitudes + * @throws IOException on write failure + */ + private void exportNamedPoint(DataPoint inPoint, Writer inWriter, String inName, + String inDesc, String inStyle, boolean inAbsoluteAltitude) + throws IOException + { + inWriter.write("\t\n\t\t"); + inWriter.write(inName); + inWriter.write("\n"); + if (inDesc != null) + { + // Write out description + inWriter.write(""); + inWriter.write(inDesc); + inWriter.write(""); + } + if (inStyle != null) + { + inWriter.write("#"); + inWriter.write(inStyle); + inWriter.write("\n"); } - inWriter.write("#camera_icon\n"); inWriter.write("\t\t\n"); if (inAbsoluteAltitude && inPoint.hasAltitude()) { inWriter.write("\t\t\tabsolute\n"); @@ -603,13 +641,13 @@ public class KmlExporter extends GenericFunction implements Runnable inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); inWriter.write(','); inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); - inWriter.write(","); + inWriter.write(','); // Altitude if point has one if (inPoint.hasAltitude()) { inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); } else { - inWriter.write("0"); + inWriter.write('0'); } inWriter.write("\n\t\t\n\t\n"); } @@ -626,22 +664,25 @@ public class KmlExporter extends GenericFunction implements Runnable inWriter.write(','); inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT)); // Altitude if point has one - inWriter.write(","); + inWriter.write(','); if (inPoint.hasAltitude()) { inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES)); } else { - inWriter.write("0"); + inWriter.write('0'); } - inWriter.write("\n"); + inWriter.write('\n'); } /** * Loop through the photos and create thumbnails * @param inZipStream zip stream to save image files to + * @param inThumbWidth thumbnail width + * @param inThumbHeight thumbnail height */ - private void exportThumbnails(ZipOutputStream inZipStream) throws IOException + private void exportThumbnails(ZipOutputStream inZipStream, int inThumbWidth, int inThumbHeight) + throws IOException { // set up image writer Iterator writers = ImageIO.getImageWritersByFormatName("jpg"); @@ -677,7 +718,7 @@ public class KmlExporter extends GenericFunction implements Runnable // Scale and smooth image to required size BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(), - THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, point.getPhoto().getRotationDegrees()); + inThumbWidth, inThumbHeight, point.getPhoto().getRotationDegrees()); // Store image dimensions so that it doesn't have to be calculated again for the points _imageDimensions[i] = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()); diff --git a/tim/prune/save/PointTypeSelector.java b/tim/prune/save/PointTypeSelector.java index 91632b4..ce7adeb 100644 --- a/tim/prune/save/PointTypeSelector.java +++ b/tim/prune/save/PointTypeSelector.java @@ -14,13 +14,15 @@ import tim.prune.data.TrackInfo; /** * GUI element to allow the selection of point types for saving, - * including checkboxes for track points, waypoints, photo points + * including checkboxes for track points, waypoints, photo points, audio points * and also a checkbox for the current selection */ public class PointTypeSelector extends JPanel { /** Array of checkboxes */ - private JCheckBox[] _checkboxes = new JCheckBox[4]; + private JCheckBox[] _checkboxes = new JCheckBox[5]; + /** Grid panel for top row */ + private JPanel _gridPanel = null; /** @@ -40,23 +42,23 @@ public class PointTypeSelector extends JPanel private void createComponents() { setLayout(new BorderLayout()); - // Need label to explain what it is add(new JLabel(I18nManager.getText("dialog.pointtype.desc")), BorderLayout.NORTH); // panel for the checkboxes - JPanel gridPanel = new JPanel(); - gridPanel.setLayout(new GridLayout(0, 3, 15, 3)); - final String[] keys = {"track", "waypoint", "photo"}; - for (int i=0; i<3; i++) + _gridPanel = new JPanel(); + _gridPanel.setLayout(new GridLayout(0, 3, 15, 3)); + final String[] keys = {"track", "waypoint", "photo", "audio"}; + for (int i=0; i<4; i++) { _checkboxes[i] = new JCheckBox(I18nManager.getText("dialog.pointtype." + keys[i])); _checkboxes[i].setSelected(true); - gridPanel.add(_checkboxes[i]); + if (i<3) {_gridPanel.add(_checkboxes[i]);} } - add(gridPanel, BorderLayout.CENTER); - _checkboxes[3] = new JCheckBox(I18nManager.getText("dialog.pointtype.selection")); - add(_checkboxes[3], BorderLayout.SOUTH); + add(_gridPanel, BorderLayout.CENTER); + _checkboxes[4] = new JCheckBox(I18nManager.getText("dialog.pointtype.selection")); + add(_checkboxes[4], BorderLayout.SOUTH); } + /** * Initialize the checkboxes from the given data * @param inTrackInfo TrackInfo object @@ -64,14 +66,22 @@ public class PointTypeSelector extends JPanel public void init(TrackInfo inTrackInfo) { // Get whether track has track points, waypoints, photos - boolean[] flags = {inTrackInfo.getTrack().hasTrackPoints(), + boolean[] dataFlags = {inTrackInfo.getTrack().hasTrackPoints(), inTrackInfo.getTrack().hasWaypoints(), - inTrackInfo.getPhotoList().getNumPhotos() > 0 + inTrackInfo.getPhotoList().getNumPhotos() > 0, + inTrackInfo.getAudioList().getNumAudios() > 0 }; + // Rearrange grid to just show the appropriate entries + final boolean[] showFlags = {true, true, dataFlags[2] || !dataFlags[3], dataFlags[3]}; + _gridPanel.removeAll(); + for (int i=0; i<4; i++) { + if (showFlags[i]) {_gridPanel.add(_checkboxes[i]);} + } // Enable or disable checkboxes according to data present - for (int i=0; i<3; i++) + for (int i=0; i<4; i++) { - if (flags[i]) { + if (dataFlags[i]) { + if (!_checkboxes[i].isEnabled()) {_checkboxes[i].setSelected(true);} _checkboxes[i].setEnabled(true); } else { @@ -79,8 +89,8 @@ public class PointTypeSelector extends JPanel _checkboxes[i].setEnabled(false); } } - _checkboxes[3].setEnabled(inTrackInfo.getSelection().hasRangeSelected()); - _checkboxes[3].setSelected(false); + _checkboxes[4].setEnabled(inTrackInfo.getSelection().hasRangeSelected()); + _checkboxes[4].setSelected(false); } /** @@ -107,12 +117,20 @@ public class PointTypeSelector extends JPanel return _checkboxes[2].isSelected(); } + /** + * @return true if audio points selected + */ + public boolean getAudiopointsSelected() + { + return _checkboxes[3].isSelected(); + } + /** * @return true if only the current selection should be saved */ public boolean getJustSelection() { - return _checkboxes[3].isSelected(); + return _checkboxes[4].isSelected(); } /** @@ -121,6 +139,6 @@ public class PointTypeSelector extends JPanel public boolean getAnythingSelected() { return getTrackpointsSelected() || getWaypointsSelected() - || getPhotopointsSelected(); + || getPhotopointsSelected() || getAudiopointsSelected(); } } diff --git a/tim/prune/save/PovExporter.java b/tim/prune/save/PovExporter.java index cec38a2..313b72a 100644 --- a/tim/prune/save/PovExporter.java +++ b/tim/prune/save/PovExporter.java @@ -32,6 +32,7 @@ import tim.prune.config.Config; import tim.prune.data.NumberUtils; import tim.prune.data.Track; import tim.prune.function.Export3dFunction; +import tim.prune.gui.DialogCloser; import tim.prune.load.GenericFileFilter; import tim.prune.threedee.LineDialog; import tim.prune.threedee.ThreeDModel; @@ -161,6 +162,7 @@ public class PovExporter extends Export3dFunction } _fontName = new JTextField(defaultFont, 12); _fontName.setAlignmentX(Component.LEFT_ALIGNMENT); + _fontName.addKeyListener(new DialogCloser(_dialog)); centralPanel.add(_fontName); //coordinates of camera JLabel cameraXLabel = new JLabel(I18nManager.getText("dialog.exportpov.camerax")); diff --git a/tim/prune/save/SvgExporter.java b/tim/prune/save/SvgExporter.java index c27b2f3..5bfd7c0 100644 --- a/tim/prune/save/SvgExporter.java +++ b/tim/prune/save/SvgExporter.java @@ -31,6 +31,7 @@ import tim.prune.UpdateMessageBroker; import tim.prune.config.Config; import tim.prune.data.Track; import tim.prune.function.Export3dFunction; +import tim.prune.gui.DialogCloser; import tim.prune.load.GenericFileFilter; import tim.prune.threedee.ThreeDModel; @@ -131,8 +132,7 @@ public class SvgExporter extends Export3dFunction buttonPanel.add(okButton); JButton cancelButton = new JButton(I18nManager.getText("button.cancel")); cancelButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) - { + public void actionPerformed(ActionEvent e) { _dialog.dispose(); } }); @@ -148,6 +148,7 @@ public class SvgExporter extends Export3dFunction phiLabel.setHorizontalAlignment(SwingConstants.TRAILING); centralPanel.add(phiLabel); _phiField = new JTextField("" + _phi); + _phiField.addKeyListener(new DialogCloser(_dialog)); centralPanel.add(_phiField); JLabel thetaLabel = new JLabel(I18nManager.getText("dialog.exportsvg.theta")); thetaLabel.setHorizontalAlignment(SwingConstants.TRAILING); diff --git a/tim/prune/save/SvgFragment.java b/tim/prune/save/SvgFragment.java index fc01974..0d9c5d4 100644 --- a/tim/prune/save/SvgFragment.java +++ b/tim/prune/save/SvgFragment.java @@ -45,4 +45,12 @@ public class SvgFragment implements Comparable { return _fragment.equals(inOther._fragment); } + + /** + * @param inOther other object to compare this one with + * @return true if the objects are equal + */ + public boolean equals(Object inOther) { + return (inOther instanceof SvgFragment?equals((SvgFragment) inOther):false); + } } diff --git a/tim/prune/undo/UndoConnectMedia.java b/tim/prune/undo/UndoConnectMedia.java new file mode 100644 index 0000000..e75f02f --- /dev/null +++ b/tim/prune/undo/UndoConnectMedia.java @@ -0,0 +1,75 @@ +package tim.prune.undo; + +import tim.prune.I18nManager; +import tim.prune.UpdateMessageBroker; +import tim.prune.data.AudioFile; +import tim.prune.data.DataPoint; +import tim.prune.data.Photo; +import tim.prune.data.TrackInfo; + +/** + * Operation to undo the connection of a photo and/or audio to a point + */ +public class UndoConnectMedia implements UndoOperation +{ + private DataPoint _point = null; + private String _photoFilename = null; + private String _audioFilename = null; + + + /** + * Constructor + * @param inPoint data point + * @param inPhotoFilename filename of photo, or null if photo not connected + * @param inAudioFilename filename of audio, or null of audio not connected + */ + public UndoConnectMedia(DataPoint inPoint, String inPhotoFilename, String inAudioFilename) + { + _point = inPoint; + _photoFilename = inPhotoFilename; + _audioFilename = inAudioFilename; + } + + + /** + * @return description of operation including photo and/or audio filename(s) + */ + public String getDescription() + { + String desc = I18nManager.getText("undo.connect") + " " + (_photoFilename==null?"":_photoFilename) + + (_photoFilename!=null && _audioFilename!=null?", ":"") + + (_audioFilename==null?"":_audioFilename); + return desc; + } + + + /** + * Perform the undo operation on the given Track + * @param inTrackInfo TrackInfo object on which to perform the operation + */ + public void performUndo(TrackInfo inTrackInfo) throws UndoException + { + if (_photoFilename != null) + { + // Disconnect photo + Photo photo = _point.getPhoto(); + if (photo != null) + { + _point.setPhoto(null); + photo.setDataPoint(null); + } + } + if (_audioFilename != null) + { + // Disconnect audio + AudioFile audio = _point.getAudio(); + if (audio != null) + { + _point.setAudio(null); + audio.setDataPoint(null); + } + } + // inform subscribers + UpdateMessageBroker.informSubscribers(); + } +} \ No newline at end of file diff --git a/tim/prune/undo/UndoConnectPhoto.java b/tim/prune/undo/UndoConnectPhoto.java deleted file mode 100644 index 3e653ce..0000000 --- a/tim/prune/undo/UndoConnectPhoto.java +++ /dev/null @@ -1,61 +0,0 @@ -package tim.prune.undo; - -import tim.prune.I18nManager; -import tim.prune.UpdateMessageBroker; -import tim.prune.data.DataPoint; -import tim.prune.data.Photo; -import tim.prune.data.TrackInfo; - -/** - * Operation to undo the connection of a photo to a point - */ -public class UndoConnectPhoto implements UndoOperation -{ - private DataPoint _point = null; - private String _filename = null; - - - /** - * Constructor - * @param inPoint data point - * @param inFilename filename of photo - */ - public UndoConnectPhoto(DataPoint inPoint, String inFilename) - { - _point = inPoint; - _filename = inFilename; - } - - - /** - * @return description of operation including photo filename - */ - public String getDescription() - { - String desc = I18nManager.getText("undo.connectphoto") + " " + _filename; - return desc; - } - - - /** - * Perform the undo operation on the given Track - * @param inTrackInfo TrackInfo object on which to perform the operation - */ - public void performUndo(TrackInfo inTrackInfo) throws UndoException - { - // Disconnect again - Photo photo = _point.getPhoto(); - if (photo != null) - { - _point.setPhoto(null); - photo.setDataPoint(null); - // inform subscribers - UpdateMessageBroker.informSubscribers(); - } - else - { - // throw exception if failed - throw new UndoException(getDescription()); - } - } -} \ No newline at end of file diff --git a/tim/prune/undo/UndoCorrelateAudios.java b/tim/prune/undo/UndoCorrelateAudios.java new file mode 100644 index 0000000..e0c1f95 --- /dev/null +++ b/tim/prune/undo/UndoCorrelateAudios.java @@ -0,0 +1,81 @@ +package tim.prune.undo; + +import tim.prune.I18nManager; +import tim.prune.data.AudioFile; +import tim.prune.data.DataPoint; +import tim.prune.data.TrackInfo; + +/** + * Operation to undo an auto-correlation of audios with points + * (very similar to UndoCorrelatePhotos) + */ +public class UndoCorrelateAudios implements UndoOperation +{ + private DataPoint[] _contents = null; + private DataPoint[] _audioPoints = null; + private int _numCorrelated = -1; + + + /** + * Constructor + * @param inTrackInfo track information + */ + public UndoCorrelateAudios(TrackInfo inTrackInfo) + { + // Copy track contents + _contents = inTrackInfo.getTrack().cloneContents(); + // Copy points associated with audios before correlation + int numAudios = inTrackInfo.getAudioList().getNumAudios(); + _audioPoints = new DataPoint[numAudios]; + for (int i=0; i -1) {inTrackInfo.getPhotoList().cropTo(_numPhotos);} + if (_numAudios > -1) {inTrackInfo.getAudioList().cropTo(_numAudios);} // replace track contents with old if (!inTrackInfo.getTrack().replaceContents(_contents)) { @@ -94,4 +108,4 @@ public class UndoLoad implements UndoOperation // clear selection inTrackInfo.getSelection().clearAll(); } -} \ No newline at end of file +} diff --git a/tim/prune/undo/UndoLoadAudios.java b/tim/prune/undo/UndoLoadAudios.java new file mode 100644 index 0000000..c5b49fc --- /dev/null +++ b/tim/prune/undo/UndoLoadAudios.java @@ -0,0 +1,49 @@ +package tim.prune.undo; + +import tim.prune.I18nManager; +import tim.prune.data.TrackInfo; + +/** + * Operation to undo a load audios operation + */ +public class UndoLoadAudios implements UndoOperation +{ + /** Number of audio files added */ + private int _numAudios = -1; + + + /** + * Constructor + * @param inNumAudios number of audios loaded + */ + public UndoLoadAudios(int inNumAudios) + { + _numAudios = inNumAudios; + } + + + /** + * @return description of operation including number of audios loaded + */ + public String getDescription() + { + String desc = I18nManager.getText("undo.loadaudios"); + if (_numAudios > 0) + desc = desc + " (" + _numAudios + ")"; + return desc; + } + + + /** + * Perform the undo operation on the given Track + * @param inTrackInfo TrackInfo object on which to perform the operation + */ + public void performUndo(TrackInfo inTrackInfo) throws UndoException + { + // crop audio list to previous size + int cropIndex = inTrackInfo.getAudioList().getNumAudios() - _numAudios; + inTrackInfo.getAudioList().cropTo(cropIndex); + // clear selection + inTrackInfo.getSelection().clearAll(); + } +} -- 2.43.0