]> gitweb.fperrin.net Git - GpsPrune.git/commitdiff
Version 9, February 2010
authoractivityworkshop <mail@activityworkshop.net>
Sat, 14 Feb 2015 14:25:57 +0000 (15:25 +0100)
committeractivityworkshop <mail@activityworkshop.net>
Sat, 14 Feb 2015 14:25:57 +0000 (15:25 +0100)
102 files changed:
tim/prune/App.java
tim/prune/ExternalTools.java
tim/prune/FunctionLibrary.java
tim/prune/GpsPruner.java
tim/prune/I18nManager.java
tim/prune/config/ColourScheme.java [new file with mode: 0644]
tim/prune/config/ColourUtils.java [new file with mode: 0644]
tim/prune/config/Config.java [moved from tim/prune/Config.java with 82% similarity]
tim/prune/config/ConfigException.java [moved from tim/prune/ConfigException.java with 82% similarity]
tim/prune/copyright.txt [new file with mode: 0644]
tim/prune/data/Coordinate.java
tim/prune/data/DataPoint.java
tim/prune/data/FileInfo.java
tim/prune/data/Photo.java
tim/prune/data/Selection.java
tim/prune/data/SourceInfo.java [new file with mode: 0644]
tim/prune/data/Track.java
tim/prune/data/TrackInfo.java
tim/prune/drew/jpeg/ExifReader.java
tim/prune/drew/jpeg/JpegData.java
tim/prune/function/AboutScreen.java
tim/prune/function/AddAltitudeOffset.java
tim/prune/function/ConvertNamesToTimes.java [new file with mode: 0644]
tim/prune/function/DuplicatePoint.java [new file with mode: 0644]
tim/prune/function/FullRangeDetails.java [new file with mode: 0644]
tim/prune/function/IgnoreExifThumb.java [new file with mode: 0644]
tim/prune/function/PasteCoordinates.java [new file with mode: 0644]
tim/prune/function/RearrangePhotosFunction.java [new file with mode: 0644]
tim/prune/function/RotatePhoto.java [new file with mode: 0644]
tim/prune/function/SaveConfig.java
tim/prune/function/SetColours.java [new file with mode: 0644]
tim/prune/function/SetKmzImageSize.java
tim/prune/function/SetLanguage.java [new file with mode: 0644]
tim/prune/function/SetMapBgFunction.java
tim/prune/function/SetPathsFunction.java
tim/prune/function/browser/UrlGenerator.java
tim/prune/function/charts/Charter.java
tim/prune/function/compress/CompressTrackFunction.java
tim/prune/function/distance/DistanceTableModel.java
tim/prune/function/edit/PointEditor.java
tim/prune/function/gpsies/GetGpsiesFunction.java
tim/prune/function/gpsies/TrackListModel.java
tim/prune/gui/ColourChooser.java [new file with mode: 0644]
tim/prune/gui/ColourPatch.java [new file with mode: 0644]
tim/prune/gui/DetailsDisplay.java
tim/prune/gui/DisplayUtils.java [new file with mode: 0644]
tim/prune/gui/GenericChart.java
tim/prune/gui/GuiGridLayout.java [new file with mode: 0644]
tim/prune/gui/IconManager.java
tim/prune/gui/ImageUtils.java
tim/prune/gui/MenuManager.java
tim/prune/gui/PhotoThumbnail.java
tim/prune/gui/ProfileChart.java
tim/prune/gui/WaypointListModel.java
tim/prune/gui/images/rotate_left_icon.png [new file with mode: 0644]
tim/prune/gui/images/rotate_right_icon.png [new file with mode: 0644]
tim/prune/gui/map/MapCanvas.java
tim/prune/gui/map/MapTileConfig.java
tim/prune/gui/map/ScaleBar.java
tim/prune/lang/prune-texts_af.properties
tim/prune/lang/prune-texts_de.properties
tim/prune/lang/prune-texts_de_CH.properties
tim/prune/lang/prune-texts_en.properties
tim/prune/lang/prune-texts_es.properties
tim/prune/lang/prune-texts_fr.properties
tim/prune/lang/prune-texts_in.properties
tim/prune/lang/prune-texts_it.properties
tim/prune/lang/prune-texts_ja.properties [new file with mode: 0644]
tim/prune/lang/prune-texts_pl.properties
tim/prune/lang/prune-texts_pt.properties
tim/prune/lang/prune-texts_ro.properties
tim/prune/lang/prune-texts_tr.properties [new file with mode: 0644]
tim/prune/lang/prune-texts_zh.properties
tim/prune/load/FieldGuesser.java
tim/prune/load/FileLoader.java
tim/prune/load/GpsLoader.java
tim/prune/load/JpegLoader.java
tim/prune/load/NmeaFileLoader.java
tim/prune/load/TextFileLoader.java
tim/prune/load/xml/GpxHandler.java
tim/prune/load/xml/GzipFileLoader.java [new file with mode: 0644]
tim/prune/load/xml/XmlFileLoader.java
tim/prune/load/xml/ZipFileLoader.java
tim/prune/readme.txt
tim/prune/save/ExifSaver.java
tim/prune/save/FileSaver.java
tim/prune/save/GpsSaver.java
tim/prune/save/GpxCacher.java [new file with mode: 0644]
tim/prune/save/GpxCacherList.java [new file with mode: 0644]
tim/prune/save/GpxExporter.java
tim/prune/save/KmlExporter.java
tim/prune/save/PointTypeSelector.java
tim/prune/save/PovExporter.java
tim/prune/undo/UndoConnectPhotoWithClone.java [deleted file]
tim/prune/undo/UndoConvertNamesToTimes.java [new file with mode: 0644]
tim/prune/undo/UndoCutAndMove.java
tim/prune/undo/UndoEditPoint.java
tim/prune/undo/UndoLoad.java
tim/prune/undo/UndoRearrangePhotos.java [new file with mode: 0644]
tim/prune/undo/UndoRearrangeWaypoints.java
tim/prune/undo/UndoReorder.java [new file with mode: 0644]
tim/prune/undo/UndoRotatePhoto.java [new file with mode: 0644]

index 091a37cb4a70bb1e8f149c1cb286a9098c7f26f3..ab9724b3bb90bd99de28f882a0ef4337bac39e42 100644 (file)
@@ -1,24 +1,22 @@
 package tim.prune;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.EmptyStackException;
 import java.util.Set;
 import java.util.Stack;
-import java.io.File;
 
 import javax.swing.JFrame;
 import javax.swing.JOptionPane;
 
 import tim.prune.data.Altitude;
-import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.LatLonRectangle;
-import tim.prune.data.Latitude;
-import tim.prune.data.Longitude;
 import tim.prune.data.NumberUtils;
 import tim.prune.data.Photo;
 import tim.prune.data.PhotoList;
+import tim.prune.data.SourceInfo;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
 import tim.prune.function.browser.BrowserLauncher;
@@ -32,7 +30,24 @@ import tim.prune.load.FileLoader;
 import tim.prune.load.JpegLoader;
 import tim.prune.save.ExifSaver;
 import tim.prune.save.FileSaver;
-import tim.prune.undo.*;
+import tim.prune.undo.UndoAddAltitudeOffset;
+import tim.prune.undo.UndoAddTimeOffset;
+import tim.prune.undo.UndoCompress;
+import tim.prune.undo.UndoConnectPhoto;
+import tim.prune.undo.UndoCreatePoint;
+import tim.prune.undo.UndoCutAndMove;
+import tim.prune.undo.UndoDeletePhoto;
+import tim.prune.undo.UndoDeletePoint;
+import tim.prune.undo.UndoDeleteRange;
+import tim.prune.undo.UndoDisconnectPhoto;
+import tim.prune.undo.UndoEditPoint;
+import tim.prune.undo.UndoException;
+import tim.prune.undo.UndoInsert;
+import tim.prune.undo.UndoLoad;
+import tim.prune.undo.UndoLoadPhotos;
+import tim.prune.undo.UndoMergeTrackSegments;
+import tim.prune.undo.UndoOperation;
+import tim.prune.undo.UndoReverseSection;
 
 
 /**
@@ -178,7 +193,7 @@ public class App
                else
                {
                        if (_fileSaver == null) {
-                               _fileSaver = new FileSaver(this, _frame, _track);
+                               _fileSaver = new FileSaver(this, _frame);
                        }
                        char delim = ',';
                        if (_fileLoader != null) {delim = _fileLoader.getLastUsedDelimiter();}
@@ -239,7 +254,7 @@ public class App
                        // add information to undo stack
                        UndoOperation undo = new UndoEditPoint(currentPoint, inUndoList);
                        // pass to track for completion
-                       if (_track.editPoint(currentPoint, inEditList))
+                       if (_track.editPoint(currentPoint, inEditList, false))
                        {
                                _undoStack.push(undo);
                                // Confirm point edit
@@ -556,18 +571,18 @@ public class App
 
 
        /**
-        * Create a new point at the given lat/long coordinates
-        * @param inLat latitude
-        * @param inLong longitude
+        * Create a new point at the given position
+        * @param inPoint point to add
         */
-       public void createPoint(double inLat, double inLong)
+       public void createPoint(DataPoint inPoint)
        {
                // create undo object
                UndoCreatePoint undo = new UndoCreatePoint();
-               // create point and add to track
-               DataPoint point = new DataPoint(new Latitude(inLat, Coordinate.FORMAT_NONE), new Longitude(inLong, Coordinate.FORMAT_NONE), null);
-               point.setSegmentStart(true);
-               _track.appendPoints(new DataPoint[] {point});
+               // add point to track
+               inPoint.setSegmentStart(true);
+               _track.appendPoints(new DataPoint[] {inPoint});
+               // ensure track's field list contains point's fields
+               _track.extendFieldList(inPoint.getFieldList());
                _trackInfo.selectPoint(_trackInfo.getTrack().getNumPoints()-1);
                // add undo object to stack
                _undoStack.add(undo);
@@ -630,10 +645,10 @@ public class App
         * @param inFieldArray array of fields
         * @param inDataArray array of data
         * @param inAltFormat altitude format
-        * @param inFilename filename used
+        * @param inSourceInfo information about the source of the data
         */
        public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray, Altitude.Format inAltFormat,
-               String inFilename)
+               SourceInfo inSourceInfo)
        {
                // Check whether loaded array can be properly parsed into a Track
                Track loadedTrack = new Track();
@@ -665,15 +680,9 @@ public class App
                                // append data to current Track
                                _undoStack.add(new UndoLoad(_track.getNumPoints(), loadedTrack.getNumPoints()));
                                _track.combine(loadedTrack);
-                               // set filename if currently empty
-                               if (_trackInfo.getFileInfo().getNumFiles() == 0)
-                               {
-                                       _trackInfo.getFileInfo().setFile(inFilename);
-                               }
-                               else
-                               {
-                                       _trackInfo.getFileInfo().addFile();
-                               }
+                               // set source information
+                               inSourceInfo.populatePointObjects(_track, loadedTrack.getNumPoints());
+                               _trackInfo.getFileInfo().addSource(inSourceInfo);
                        }
                        else if (answer == JOptionPane.NO_OPTION)
                        {
@@ -687,7 +696,8 @@ public class App
                                _lastSavePosition = _undoStack.size();
                                _trackInfo.getSelection().clearAll();
                                _track.load(loadedTrack);
-                               _trackInfo.getFileInfo().setFile(inFilename);
+                               inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                               _trackInfo.getFileInfo().replaceSource(inSourceInfo);
                                if (photos != null)
                                {
                                        _trackInfo.getPhotoList().removeCorrelatedPhotos();
@@ -701,11 +711,12 @@ public class App
                        _lastSavePosition = _undoStack.size();
                        _trackInfo.getSelection().clearAll();
                        _track.load(loadedTrack);
-                       _trackInfo.getFileInfo().setFile(inFilename);
+                       inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
+                       _trackInfo.getFileInfo().addSource(inSourceInfo);
                }
                UpdateMessageBroker.informSubscribers();
                // Update status bar
-               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile") + " '" + inFilename + "'");
+               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile") + " '" + inSourceInfo.getName() + "'");
                // update menu
                _menuManager.informFileLoaded();
                // load next file if there's a queue
@@ -784,29 +795,7 @@ public class App
                DataPoint point = _trackInfo.getCurrentPoint();
                if (photo != null && point != null)
                {
-                       if (point.getPhoto() != null)
-                       {
-                               // point already has a photo, confirm cloning of new point
-                               if (JOptionPane.showConfirmDialog(_frame,
-                                       I18nManager.getText("dialog.connectphoto.clonepoint"),
-                                       I18nManager.getText("dialog.connect.title"),
-                                       JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)
-                               {
-                                       // Create undo, clone point and attach
-                                       int pointIndex = _trackInfo.getSelection().getCurrentPointIndex() + 1;
-                                       // insert new point after current one
-                                       point = point.clonePoint();
-                                       UndoConnectPhotoWithClone undo = new UndoConnectPhotoWithClone(
-                                               point, photo.getFile().getName(), pointIndex);
-                                       _track.insertPoint(point, pointIndex);
-                                       photo.setDataPoint(point);
-                                       point.setPhoto(photo);
-                                       _undoStack.add(undo);
-                                       UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
-                                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.connect"));
-                               }
-                       }
-                       else
+                       if (point.getPhoto() == null)
                        {
                                // point doesn't currently have a photo, so just connect it
                                _undoStack.add(new UndoConnectPhoto(point, photo.getFile().getName()));
index a2d3e56b6f75c2315832f8c89809a3d442fd6159..31c03026813a046293db5cfa870b614f52ec6692 100644 (file)
@@ -2,6 +2,8 @@ package tim.prune;
 
 import java.io.IOException;
 
+import tim.prune.config.Config;
+
 
 /**
  * Class to manage interfaces to external tools, like exiftool
@@ -65,7 +67,6 @@ public abstract class ExternalTools
         */
        private static boolean check(String inCommand)
        {
-               // System.out.println("Checking tool '" + inCommand + "'");
                try
                {
                        Runtime.getRuntime().exec(inCommand);
index 1a2518c4008b4d4a13e8751bd23ac7ee45bc93c3..0e642dd37a0348e9e91254ce54265eca6a616616 100644 (file)
@@ -1,24 +1,12 @@
 package tim.prune;
 
 import tim.prune.correlate.PhotoCorrelator;
-import tim.prune.function.AboutScreen;
-import tim.prune.function.AddAltitudeOffset;
-import tim.prune.function.AddTimeOffset;
-import tim.prune.function.CheckVersionScreen;
-import tim.prune.function.FindWaypoint;
-import tim.prune.function.HelpScreen;
-import tim.prune.function.RearrangeWaypointsFunction;
-import tim.prune.function.SaveConfig;
-import tim.prune.function.SetKmzImageSize;
-import tim.prune.function.SetMapBgFunction;
-import tim.prune.function.ShowKeysScreen;
-import tim.prune.function.ShowThreeDFunction;
+import tim.prune.function.*;
 import tim.prune.function.charts.Charter;
 import tim.prune.function.compress.CompressTrackFunction;
 import tim.prune.function.distance.DistanceFunction;
 import tim.prune.function.edit.PointNameEditor;
 import tim.prune.function.gpsies.GetGpsiesFunction;
-import tim.prune.function.SetPathsFunction;
 import tim.prune.load.GpsLoader;
 import tim.prune.save.GpsSaver;
 import tim.prune.save.GpxExporter;
@@ -38,18 +26,28 @@ public abstract class FunctionLibrary
        public static GenericFunction FUNCTION_SAVECONFIG  = null;
        public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
        public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
+       public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
        public static GenericFunction FUNCTION_COMPRESS = 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;
+       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_CORRELATE_PHOTOS = null;
+       public static GenericFunction FUNCTION_ROTATE_PHOTO_LEFT = null;
+       public static GenericFunction FUNCTION_ROTATE_PHOTO_RIGHT = null;
+       public static GenericFunction FUNCTION_IGNORE_EXIF_THUMB = null;
        public static GenericFunction FUNCTION_CHARTS = null;
        public static GenericFunction FUNCTION_3D     = null;
        public static GenericFunction FUNCTION_DISTANCES  = null;
+       public static GenericFunction FUNCTION_FULL_RANGE_DETAILS = null;
        public static GenericFunction FUNCTION_GET_GPSIES = null;
        public static GenericFunction FUNCTION_SET_MAP_BG = 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_LANGUAGE = null;
        public static GenericFunction FUNCTION_HELP   = null;
        public static GenericFunction FUNCTION_SHOW_KEYS = null;
        public static GenericFunction FUNCTION_ABOUT  = null;
@@ -70,18 +68,28 @@ public abstract class FunctionLibrary
                FUNCTION_SAVECONFIG = new SaveConfig(inApp);
                FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
                FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
+               FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
                FUNCTION_COMPRESS = new CompressTrackFunction(inApp);
                FUNCTION_ADD_TIME_OFFSET = new AddTimeOffset(inApp);
                FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp);
+               FUNCTION_CONVERT_NAMES_TO_TIMES = new ConvertNamesToTimes(inApp);
+               FUNCTION_PASTE_COORDINATES = new PasteCoordinates(inApp);
                FUNCTION_FIND_WAYPOINT = new FindWaypoint(inApp);
+               FUNCTION_DUPLICATE_POINT = new DuplicatePoint(inApp);
                FUNCTION_CORRELATE_PHOTOS = new PhotoCorrelator(inApp);
+               FUNCTION_ROTATE_PHOTO_LEFT = new RotatePhoto(inApp, false);
+               FUNCTION_ROTATE_PHOTO_RIGHT = new RotatePhoto(inApp, true);
+               FUNCTION_IGNORE_EXIF_THUMB = new IgnoreExifThumb(inApp);
                FUNCTION_CHARTS = new Charter(inApp);
                FUNCTION_3D     = new ShowThreeDFunction(inApp);
                FUNCTION_DISTANCES = new DistanceFunction(inApp);
+               FUNCTION_FULL_RANGE_DETAILS = new FullRangeDetails(inApp);
                FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
                FUNCTION_SET_MAP_BG = new SetMapBgFunction(inApp);
                FUNCTION_SET_PATHS = new SetPathsFunction(inApp);
                FUNCTION_SET_KMZ_IMAGE_SIZE = new SetKmzImageSize(inApp);
+               FUNCTION_SET_COLOURS = new SetColours(inApp);
+               FUNCTION_SET_LANGUAGE = new SetLanguage(inApp);
                FUNCTION_HELP   = new HelpScreen(inApp);
                FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp);
                FUNCTION_ABOUT  = new AboutScreen(inApp);
index 29f7eb592681131438714de9122b7c7caea0a601..7a172a4a64ceeb0a2576de987a4240b3018cc423 100644 (file)
@@ -12,6 +12,8 @@ import javax.swing.JSplitPane;
 import javax.swing.JToolBar;
 import javax.swing.WindowConstants;
 
+import tim.prune.config.Config;
+import tim.prune.config.ConfigException;
 import tim.prune.gui.DetailsDisplay;
 import tim.prune.gui.IconManager;
 import tim.prune.gui.MenuManager;
@@ -22,16 +24,18 @@ import tim.prune.gui.Viewport;
 import tim.prune.gui.map.MapCanvas;
 
 /**
- * Tool to visualize, edit, convert and prune GPS data
+ * Prune is a tool to visualize, edit, convert and prune GPS data
  * Please see the included readme.txt or http://activityworkshop.net
- * This software is copyright activityworkshop.net and made available through the Gnu GPL
+ * This software is copyright activityworkshop.net 2006-2010 and made available through the Gnu GPL version 2.
+ * For license details please see the included license.txt.
+ * GpsPruner is the main entry point to the application, including initialisation and launch
  */
 public class GpsPruner
 {
        /** Version number of application, used in about screen and for version check */
-       public static final String VERSION_NUMBER = "8";
+       public static final String VERSION_NUMBER = "9";
        /** Build number, just used for about screen */
-       public static final String BUILD_NUMBER = "155";
+       public static final String BUILD_NUMBER = "176";
        /** Static reference to App object */
        private static App APP = null;
 
@@ -104,7 +108,8 @@ public class GpsPruner
                catch (ConfigException ce) {
                        System.err.println("Failed to load config file: " + configFilename);
                }
-               if (locale != null) {
+               boolean overrideLang = (locale != null);
+               if (overrideLang) {
                        // Make sure Config holds chosen language
                        Config.setConfigString(Config.KEY_LANGUAGE_CODE, localeCode);
                }
@@ -117,9 +122,15 @@ public class GpsPruner
                        }
                }
                I18nManager.init(locale);
-               if (langFilename != null) {
+               // Load the external language file, either from config file or from command line params
+               if (langFilename == null && !overrideLang) {
+                       // If langfilename is blank on command line parameters then don't use setting from config
+                       langFilename = Config.getConfigString(Config.KEY_LANGUAGE_FILE);
+               }
+               if (langFilename != null && !langFilename.equals("")) {
                        try {
                                I18nManager.addLanguageFile(langFilename);
+                               Config.setConfigString(Config.KEY_LANGUAGE_FILE, langFilename);
                        }
                        catch (FileNotFoundException fnfe) {
                                System.err.println("Failed to load language file: " + langFilename);
index b9ce3cc0e59e07888f7bd2cbfef679dcc065fea8..143d7c884b418584d9ffba8a242dffb95e404b0e 100644 (file)
@@ -86,22 +86,19 @@ public abstract class I18nManager
         */
        public static String getText(String inKey)
        {
-               String value = null;
                // look in external props file if available
-               if (ExternalPropsFile != null)
+               if (ExternalPropsFile != null && ExternalPropsFile.containsKey(inKey))
                {
-                       value = ExternalPropsFile.getProperty(inKey);
-                       if (value != null && !value.equals(""))
-                               return value;
+                       return ExternalPropsFile.getProperty(inKey);
                }
                // look in extra texts if available
                if (LocalTexts != null)
                {
                        try
                        {
-                               value = LocalTexts.getString(inKey);
-                               if (value != null && !value.equals(""))
-                                       return value;
+                               if (LocalTexts.containsKey(inKey)) {
+                                       return LocalTexts.getString(inKey);
+                               }
                        }
                        catch (MissingResourceException mre) {}
                }
@@ -110,9 +107,9 @@ public abstract class I18nManager
                {
                        try
                        {
-                               value = EnglishTexts.getString(inKey);
-                               if (value != null && !value.equals(""))
-                                       return value;
+                               if (EnglishTexts.containsKey(inKey)) {
+                                       return EnglishTexts.getString(inKey);
+                               }
                        }
                        catch (MissingResourceException mre) {}
                }
diff --git a/tim/prune/config/ColourScheme.java b/tim/prune/config/ColourScheme.java
new file mode 100644 (file)
index 0000000..5f3e0cd
--- /dev/null
@@ -0,0 +1,89 @@
+package tim.prune.config;
+
+import java.awt.Color;
+
+/**
+ * Class to hold a colour scheme for Prune, including
+ * colours for background, points, selections and texts
+ */
+public class ColourScheme
+{
+       // Current colours
+       private Color[] _colours = new Color[NUM_COLOURS];
+
+       // Default colours
+       private static final Color[] DEFAULT_COLOURS = {Color.WHITE, Color.BLUE, Color.GREEN,
+               Color.BLACK, Color.RED, Color.ORANGE, Color.BLACK, Color.GRAY};
+
+       // Colour indices
+       public static final int IDX_BACKGROUND = 0;
+       public static final int IDX_POINT      = 1;
+       public static final int IDX_SELECTION  = 2;
+       public static final int IDX_TEXT       = 3;
+       public static final int IDX_PRIMARY    = 4;
+       public static final int IDX_SECONDARY  = 5;
+       public static final int IDX_BORDERS    = 6;
+       public static final int IDX_LINES      = 7;
+       // Number of colours
+       private static final int NUM_COLOURS = 8;
+
+
+       /**
+        * Load the colour scheme from the given String
+        * @param inCodes comma-separated hex codes describing colours
+        */
+       public void loadFromHex(String inCodes)
+       {
+               if (inCodes != null && inCodes.length() > 5)
+               {
+                       String[] codes = inCodes.split(",");
+                       final int numCodes = (codes.length > NUM_COLOURS ? NUM_COLOURS : codes.length);
+                       for (int i=0; i<numCodes; i++) {
+                               _colours[i] = ColourUtils.colourFromHex(codes[i]);
+                       }
+               }
+       }
+
+       /**
+        * @return colour to use for given index
+        */
+       public Color getColour(int inIndex)
+       {
+               assert (inIndex >= 0 && inIndex < NUM_COLOURS);
+               Color currColour = _colours[inIndex];
+               return (currColour != null ? currColour : DEFAULT_COLOURS[inIndex]);
+       }
+
+       /**
+        * @return default colour for given index
+        */
+       public static Color getDefaultColour(int inIndex)
+       {
+               assert (inIndex >= 0 && inIndex < NUM_COLOURS);
+               return DEFAULT_COLOURS[inIndex];
+       }
+
+       /**
+        * Edit one of the colours to a new value
+        * @param inIndex index of colour
+        * @param inColour colour to set
+        */
+       public void setColour(int inIndex, Color inColour)
+       {
+               assert (inIndex >= 0 && inIndex < NUM_COLOURS);
+               _colours[inIndex] = inColour;
+       }
+
+       /**
+        * @return colour scheme as string of concatenated hex codes
+        */
+       public String toString()
+       {
+               StringBuffer buff = new StringBuffer();
+               for (int i=0; i<NUM_COLOURS; i++) {
+                       buff.append(ColourUtils.makeHexCode(getColour(i)));
+                       buff.append(',');
+               }
+               return buff.toString();
+       }
+}
diff --git a/tim/prune/config/ColourUtils.java b/tim/prune/config/ColourUtils.java
new file mode 100644 (file)
index 0000000..a3c1188
--- /dev/null
@@ -0,0 +1,65 @@
+package tim.prune.config;
+
+import java.awt.Color;
+
+/**
+ * Class to hold static methods for handling colours
+ * including converting to and from hex code Strings
+ */
+public abstract class ColourUtils
+{
+       /**
+        * Convert a string into a Color object
+        * @param inValue 6-character hex code
+        * @return corresponding colour
+        */
+       public static Color colourFromHex(String inValue)
+       {
+               Color retVal = null;
+               if (inValue != null && inValue.length() == 6)
+               {
+                       try
+                       {
+                               final int redness = convertToInt(inValue.substring(0, 2));
+                               final int greenness = convertToInt(inValue.substring(2, 4));
+                               final int blueness = convertToInt(inValue.substring(4, 6));
+                               retVal = new Color(redness, greenness, blueness);
+                       }
+                       catch (NumberFormatException nfe) {} // colour stays null
+               }
+               return retVal;
+       }
+
+       /**
+        * @param inPair two-digit String representing hex code
+        * @return corresponding integer (0 to 255)
+        */
+       private static int convertToInt(String inPair)
+       {
+               int val = Integer.parseInt(inPair, 16);
+               if (val < 0) val = 0;
+               return val;
+       }
+
+       /**
+        * Make a hex code string for the given colour
+        * @param inColour colour
+        * @return 6-character hex code
+        */
+       public static String makeHexCode(Color inColour)
+       {
+               return convertToHex(inColour.getRed()) + convertToHex(inColour.getGreen()) + convertToHex(inColour.getBlue());
+       }
+
+       /**
+        * @param inValue integer value from 0 to 255
+        * @return two-character hex code
+        */
+       private static String convertToHex(int inValue)
+       {
+               // Uses lower case a-f
+               String code = Integer.toHexString(inValue);
+               // Pad with leading 0 if necessary
+               return (inValue < 16 ? "0" + code : code);
+       }
+}
similarity index 82%
rename from tim/prune/Config.java
rename to tim/prune/config/Config.java
index befbaccc1b4819216576f8d6a3c653ad7baca9dd..a822b6e34ebef38da13b798f6afa7460c288f0eb 100644 (file)
@@ -1,9 +1,10 @@
-package tim.prune;
+package tim.prune.config;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.util.Properties;
 
+
 /**
  * Abstract class to hold application-wide configuration
  */
@@ -14,6 +15,8 @@ public abstract class Config
 
        /** Hashtable containing all config values */
        private static Properties _configValues = new Properties();
+       /** Colour scheme object is also part of config */
+       private static ColourScheme _colourScheme = new ColourScheme();
 
        /** Default config file */
        private static final File DEFAULT_CONFIG_FILE = new File(".pruneconfig");
@@ -24,6 +27,8 @@ public abstract class Config
        public static final String KEY_PHOTO_DIR = "prune.photodirectory";
        /** Key for language code */
        public static final String KEY_LANGUAGE_CODE = "prune.languagecode";
+       /** Key for language file */
+       public static final String KEY_LANGUAGE_FILE = "prune.languagefile";
        /** Key for GPS device */
        public static final String KEY_GPS_DEVICE = "prune.gpsdevice";
        /** Key for GPS format */
@@ -36,8 +41,8 @@ public abstract class Config
        public static final String KEY_MAPSERVERINDEX = "prune.mapserverindex";
        /** Key for map server url */
        public static final String KEY_MAPSERVERURL = "prune.mapserverurl";
-       /** Key for show pace flag */
-       public static final String KEY_SHOW_PACE = "prune.showpace";
+       /** Key for show map flag */
+       public static final String KEY_SHOW_MAP = "prune.showmap";
        /** Key for width of thumbnails in kmz */
        public static final String KEY_KMZ_IMAGE_WIDTH = "prune.kmzimagewidth";
        /** Key for height of thumbnails in kmz */
@@ -48,6 +53,10 @@ public abstract class Config
        public static final String KEY_GNUPLOT_PATH = "prune.gnuplotpath";
        /** Key for exiftool path */
        public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath";
+       /** Key for colour scheme */
+       public static final String KEY_COLOUR_SCHEME = "prune.colourscheme";
+       /** Key for kml track colour */
+       public static final String KEY_KML_TRACK_COLOUR = "prune.kmltrackcolour";
 
 
        /**
@@ -91,6 +100,7 @@ public abstract class Config
                }
                // Save all properties from file
                _configValues.putAll(props);
+               _colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
                if (loadFailed) {
                        throw new ConfigException();
                }
@@ -108,12 +118,12 @@ public abstract class Config
                props.put(KEY_GPS_DEVICE, "usb:");
                props.put(KEY_GPS_FORMAT, "garmin");
                props.put(KEY_POVRAY_FONT, "crystal.ttf"); // alternative: DejaVuSans-Bold.ttf
-               props.put(KEY_SHOW_PACE, "0"); // hide by default
+               props.put(KEY_SHOW_MAP, "0"); // hide by default
                props.put(KEY_EXIFTOOL_PATH, "exiftool");
                props.put(KEY_GNUPLOT_PATH, "gnuplot");
                props.put(KEY_GPSBABEL_PATH, "gpsbabel");
                props.put(KEY_KMZ_IMAGE_WIDTH, "240");
-               props.put(KEY_KMZ_IMAGE_HEIGHT, "180");
+               props.put(KEY_KMZ_IMAGE_HEIGHT, "240");
                return props;
        }
 
@@ -145,6 +155,14 @@ public abstract class Config
                return _configValues;
        }
 
+       /**
+        * @return the current colour scheme
+        */
+       public static ColourScheme getColourScheme()
+       {
+               return _colourScheme;
+       }
+
        /**
         * Store the given configuration setting
         * @param inKey key (from constants)
@@ -152,8 +170,10 @@ public abstract class Config
         */
        public static void setConfigString(String inKey, String inValue)
        {
-               if (inKey != null && !inKey.equals(""))
-               {
+               if (inValue == null || inValue.equals("")) {
+                       _configValues.remove(inKey);
+               }
+               else {
                        _configValues.put(inKey, inValue);
                }
        }
@@ -223,6 +243,15 @@ public abstract class Config
        public static boolean isKeyBoolean(String inKey)
        {
                // Only two boolean keys so far
-               return inKey != null && (inKey.equals(KEY_METRIC_UNITS) || inKey.equals(KEY_SHOW_PACE));
+               return inKey != null && (
+                       inKey.equals(KEY_METRIC_UNITS) || inKey.equals(KEY_SHOW_MAP));
+       }
+
+       /**
+        * Update the colour scheme property from the current settings
+        */
+       public static void updateColourScheme()
+       {
+               setConfigString(KEY_COLOUR_SCHEME, _colourScheme.toString());
        }
 }
similarity index 82%
rename from tim/prune/ConfigException.java
rename to tim/prune/config/ConfigException.java
index edf6bee2f702304185637f3f22c30a66be1c313b..a619a50a071d4dfb167dc33bf15e56af6b7d27e3 100644 (file)
@@ -1,4 +1,4 @@
-package tim.prune;
+package tim.prune.config;
 
 /**
  * Exception thrown when something went wrong with the config
diff --git a/tim/prune/copyright.txt b/tim/prune/copyright.txt
new file mode 100644 (file)
index 0000000..7839745
--- /dev/null
@@ -0,0 +1,8 @@
+The source code of Prune is copyright 2006-2010 activityworkshop.net
+and distributed under the terms of the Gnu GPL version 2.
+
+The package drew.jpeg is taken from Drew Noakes' "Metadata extractor"
+which is copyright Drew Noakes 2002-2006 and was placed in the public domain.
+
+Translations are copyright various contributors, some of whom are named
+in the source code and some preferred to remain anonymous.
\ No newline at end of file
index 8b7904910b96e5da13445c7daa0aa352f15cdf41..190c66573b13e2c251a434407bc02315d34c0984 100644 (file)
@@ -35,6 +35,7 @@ public abstract class Coordinate
 
        // Instance variables
        private boolean _valid = false;
+       private boolean _cardinalGuessed = false;
        protected int _cardinal = NORTH;
        private int _degrees = 0;
        private int _minutes = 0;
@@ -68,6 +69,12 @@ public abstract class Coordinate
                                hasCardinal = false;
                                // use default from concrete subclass
                                _cardinal = getDefaultCardinal();
+                               _cardinalGuessed = true;
+                       }
+                       else if (isJustNumber(inString)) {
+                               // it's just a number
+                               hasCardinal = false;
+                               _cardinalGuessed = true;
                        }
 
                        // count numeric fields - 1=d, 2=dm, 3=dm.m/dms, 4=dms.s
@@ -181,6 +188,12 @@ public abstract class Coordinate
                return cardinal;
        }
 
+       /**
+        * @return true if cardinal was guessed, false if parsed
+        */
+       public boolean getCardinalGuessed() {
+               return _cardinalGuessed;
+       }
 
        /**
         * Get the cardinal from the given character
@@ -304,7 +317,9 @@ public abstract class Coordinate
                                case FORMAT_DECIMAL_FORCE_POINT:
                                {
                                        // Forcing a decimal point instead of system-dependent commas etc
-                                       answer = EIGHT_DP.format(_asDouble);
+                                       if (_originalFormat != FORMAT_DEG_WITHOUT_CARDINAL || answer.indexOf('.') < 0) {
+                                               answer = EIGHT_DP.format(_asDouble);
+                                       }
                                        break;
                                }
                                case FORMAT_DEG_MIN_SEC_WITH_SPACES:
@@ -409,6 +424,21 @@ public abstract class Coordinate
         */
        protected abstract Coordinate makeNew(double inValue, int inFormat);
 
+       /**
+        * Try to parse the given string
+        * @param inString string to check
+        * @return true if it can be parsed as a number
+        */
+       private static boolean isJustNumber(String inString)
+       {
+               boolean justNum = false;
+               try {
+                       double x = Double.parseDouble(inString);
+                       justNum = (x >= -180.0 && x <= 360.0);
+               }
+               catch (NumberFormatException nfe) {} // flag remains false
+               return justNum;
+       }
 
        /**
         * Create a String representation for debug
index 966b5606b7d2bdfcd2c608a4c36136acd28a3f77..7acd4f9e364314985d0ea2abb85996901352037b 100644 (file)
@@ -1,6 +1,6 @@
 package tim.prune.data;
 
-import tim.prune.Config;
+import tim.prune.config.Config;
 
 /**
  * Class to represent a single data point in the series
@@ -21,7 +21,7 @@ public class DataPoint
        private String _waypointName = null;
        private boolean _startOfSegment = false;
        private boolean _markedForDeletion = false;
-
+       private int _modifyCount = 0;
 
        /**
         * Constructor
@@ -62,7 +62,8 @@ public class DataPoint
                if (inField == null || inField == Field.WAYPT_NAME) {
                        _waypointName = getFieldValue(Field.WAYPT_NAME);
                }
-               if (inField == null || inField == Field.NEW_SEGMENT) {
+               if (inField == null || inField == Field.NEW_SEGMENT)
+               {
                        String segmentStr = getFieldValue(Field.NEW_SEGMENT);
                        if (segmentStr != null) {segmentStr = segmentStr.trim();}
                        _startOfSegment = (segmentStr != null && (segmentStr.equals("1") || segmentStr.toUpperCase().equals("Y")));
@@ -83,15 +84,15 @@ public class DataPoint
                Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE};
                _fieldList = new FieldList(fields);
                _latitude = inLatitude;
-               _fieldValues[0] = inLatitude.output(Coordinate.FORMAT_DEG_MIN_SEC);
+               _fieldValues[0] = inLatitude.output(Coordinate.FORMAT_NONE);
                _longitude = inLongitude;
-               _fieldValues[1] = inLongitude.output(Coordinate.FORMAT_DEG_MIN_SEC);
+               _fieldValues[1] = inLongitude.output(Coordinate.FORMAT_NONE);
                if (inAltitude == null) {
                        _altitude = Altitude.NONE;
                }
                else {
                        _altitude = inAltitude;
-                       _fieldValues[2] = "" + inAltitude.getValue(Altitude.Format.METRES); // units are ignored
+                       _fieldValues[2] = "" + inAltitude.getValue();
                }
                _timestamp = new Timestamp(null);
        }
@@ -125,8 +126,9 @@ public class DataPoint
         * Set (or edit) the specified field value
         * @param inField Field to set
         * @param inValue value to set
+        * @param inUndo true if undo operation, false otherwise
         */
-       public void setFieldValue(Field inField, String inValue)
+       public void setFieldValue(Field inField, String inValue, boolean inUndo)
        {
                // See if this data point already has this field
                int fieldIndex = _fieldList.getFieldIndex(inField);
@@ -148,6 +150,15 @@ public class DataPoint
                }
                // Set field value in array
                _fieldValues[fieldIndex] = inValue;
+               // Increment edit count on all field edits except segment
+               if (inField != Field.NEW_SEGMENT) {
+                       if (!inUndo) {
+                               _modifyCount++;
+                       }
+                       else {
+                               _modifyCount--;
+                       }
+               }
                // Change Coordinate, Altitude, Name or Timestamp fields after edit
                if (_altitude != null && _altitude.getFormat() != Altitude.Format.NO_FORMAT) {
                        // Altitude already present so reuse format
@@ -159,10 +170,18 @@ public class DataPoint
                }
        }
 
+       /**
+        * @return field list for this point
+        */
+       public FieldList getFieldList()
+       {
+               return _fieldList;
+       }
+
        /** @param inFlag true for start of track segment */
        public void setSegmentStart(boolean inFlag)
        {
-               setFieldValue(Field.NEW_SEGMENT, inFlag?"1":null);
+               setFieldValue(Field.NEW_SEGMENT, inFlag?"1":null, false);
        }
 
        /**
@@ -229,6 +248,13 @@ public class DataPoint
                return (_waypointName != null && !_waypointName.equals(""));
        }
 
+       /**
+        * @return true if point has been modified since loading
+        */
+       public boolean isModified()
+       {
+               return _modifyCount > 0;
+       }
 
        /**
         * Compare two DataPoint objects to see if they are duplicates
@@ -287,7 +313,6 @@ public class DataPoint
                return _latitude.isValid() && _longitude.isValid();
        }
 
-
        /**
         * Interpolate a set of points between this one and the given one
         * @param inEndPoint end point of interpolation
@@ -373,7 +398,7 @@ public class DataPoint
         */
        public DataPoint clonePoint()
        {
-               // Copy all values
+               // Copy all values (note that photo not copied)
                String[] valuesCopy = new String[_fieldValues.length];
                System.arraycopy(_fieldValues, 0, valuesCopy, 0, _fieldValues.length);
                // Make new object to hold cloned data
index 41fd10c8f893c0fa109b5afea12ffc9a20318e89..1fd56b94b76aca28739c809bb11f3f78d48ce77f 100644 (file)
@@ -1,60 +1,94 @@
 package tim.prune.data;
 
+import java.util.ArrayList;
+
 /**
  * Class to hold the information about the file(s)
  * from which the data was loaded from / saved to
  */
 public class FileInfo
 {
-       private String _filename = null;
-       private int _numFiles = 0;
+       /** List of sources */
+       private ArrayList<SourceInfo> _sources = new ArrayList<SourceInfo>();
 
 
        /**
-        * Set the file information to the specified file
-        * @param inFilename filename of file
+        * Empty constructor
         */
-       public void setFile(String inFilename)
+       public FileInfo()
+       {}
+
+       /**
+        * Private constructor for creating clone
+        * @param inList list of sources
+        */
+       private FileInfo(ArrayList<SourceInfo> inList)
        {
-               _filename = inFilename;
-               _numFiles = 1;
+               _sources = inList;
        }
 
-
        /**
-        * Add a file to the data
+        * Add a data source to the list
+        * @param inInfo info object to add
         */
-       public void addFile()
+       public void addSource(SourceInfo inInfo)
        {
-               _numFiles++;
+               _sources.add(inInfo);
        }
 
-
        /**
-        * Undo a load file
+        * Replace the list of data sources with the given source
+        * @param inInfo new source
         */
-       public void removeFile()
+       public void replaceSource(SourceInfo inInfo)
        {
-               _numFiles--;
+               _sources.clear();
+               addSource(inInfo);
        }
 
+       /**
+        * remove the last source added
+        */
+       public void removeSource()
+       {
+               _sources.remove(_sources.size()-1);
+       }
 
        /**
         * @return the number of files loaded
         */
        public int getNumFiles()
        {
-               return _numFiles;
+               return _sources.size();
        }
 
 
        /**
-        * @return The filename, if a single file
+        * @return The source name, if a single file
         */
        public String getFilename()
        {
-               if (_numFiles == 1 && _filename != null)
-                       return _filename;
+               if (getNumFiles() == 1)
+                       return _sources.get(0).getName();
                return "";
        }
+
+       /**
+        * @param inIndex index number
+        * @return source info object
+        */
+       public SourceInfo getSource(int inIndex) {
+               return _sources.get(inIndex);
+       }
+
+       /**
+        * Clone contents of file info
+        */
+       @SuppressWarnings("unchecked")
+       public FileInfo clone()
+       {
+               // copy source list
+               ArrayList<SourceInfo> copy = (ArrayList<SourceInfo>) _sources.clone();
+               return new FileInfo(copy);
+       }
 }
index 4fdb61261b64dbc21beb3ed060b28894903e479d..9eacac2706a87d1c2ef451c879ddc717b6c565d6 100644 (file)
@@ -22,6 +22,8 @@ public class Photo
        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;
@@ -36,7 +38,6 @@ public class Photo
                CONNECTED
        };
 
-
        /**
         * Constructor
         * @param inFile File object for photo
@@ -225,4 +226,32 @@ public class Photo
                return (inOther != null && inOther.getFile() != null && getFile() != null
                        && inOther.getFile().equals(getFile()));
        }
+
+       /**
+        * @param inRotation initial rotation value (from exif)
+        */
+       public void setRotation(int inRotation)
+       {
+               if (inRotation >= 0 && inRotation <= 3) {
+                       _rotation = inRotation;
+               }
+       }
+
+       /**
+        * Rotate the image by 90 degrees
+        * @param inRight true to rotate right, false for left
+        */
+       public void rotate(boolean inRight)
+       {
+               int dir = inRight?1:3;
+               _rotation = (_rotation + dir) % 4;
+       }
+
+       /**
+        * @return rotation status
+        */
+       public int getRotationDegrees()
+       {
+               return _rotation * 90;
+       }
 }
index 6924d133abca3c541a44d5bf257955a7e1bd3440..cbfb3504b4049f1597ca4dba8337102dc7ff5385 100644 (file)
@@ -19,7 +19,7 @@ public class Selection
        private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT;
        private long _totalSeconds = 0L, _movingSeconds = 0L;
        private double _angDistance = -1.0, _angMovingDistance = -1.0;
-       private boolean _multipleSegments = false;
+       private int _numSegments = 0;
 
 
        /**
@@ -65,7 +65,7 @@ public class Selection
        private void recalculate()
        {
                _altitudeFormat = Altitude.Format.NO_FORMAT;
-               _multipleSegments = false;
+               _numSegments = 0;
                if (_track.getNumPoints() > 0 && hasRangeSelected())
                {
                        _altitudeRange = new IntegerRange();
@@ -121,13 +121,15 @@ public class Selection
                                                double radians = DataPoint.calculateRadiansBetween(lastPoint, currPoint);
                                                _angDistance += radians;
                                                if (currPoint.getSegmentStart()) {
-                                                       _multipleSegments = true;
+                                                       _numSegments++;
                                                }
                                                else {
                                                        _angMovingDistance += radians;
                                                }
                                        }
                                        lastPoint = currPoint;
+                                       // If it's a track point then there must be at least one segment
+                                       if (_numSegments == 0) {_numSegments = 1;}
                                }
                        }
                        if (endTime != null) {
@@ -232,11 +234,11 @@ public class Selection
        }
 
        /**
-        * @return true if track has multiple segments
+        * @return number of segments in selection
         */
-       public boolean getHasMultipleSegments()
+       public int getNumSegments()
        {
-               return _multipleSegments;
+               return _numSegments;
        }
 
        /**
@@ -252,7 +254,7 @@ public class Selection
 
 
        /**
-        * Deselect range
+        * Select range from start to end
         * @param inStartIndex index of start of range
         * @param inEndIndex index of end of range
         */
diff --git a/tim/prune/data/SourceInfo.java b/tim/prune/data/SourceInfo.java
new file mode 100644 (file)
index 0000000..bbe8baa
--- /dev/null
@@ -0,0 +1,111 @@
+package tim.prune.data;
+
+import java.io.File;
+
+/**
+ * Class to hold the source of the point data,
+ * including original file and file type, and
+ * also file offsets for source copying
+ */
+public class SourceInfo
+{
+       /** File type of source file */
+       public enum FILE_TYPE {TEXT, GPX, KML, NMEA, GPSBABEL, GPSIES};
+
+       /** Source file */
+       private File _sourceFile = null;
+       /** Name of source */
+       private String _sourceName = null;
+       /** File type */
+       private FILE_TYPE _fileType = null;
+
+       /** Array of datapoints */
+       private DataPoint[] _points = null;
+
+
+       /**
+        * Constructor
+        * @param inFile source file
+        * @param inType type of file
+        */
+       public SourceInfo(File inFile, FILE_TYPE inType)
+       {
+               _sourceFile = inFile;
+               _sourceName = inFile.getName();
+               _fileType = inType;
+       }
+
+       /**
+        * Constructor
+        * @param inName name of source (without file)
+        * @param inType type of file
+        */
+       public SourceInfo(String inName, FILE_TYPE inType)
+       {
+               _sourceFile = null;
+               _sourceName = inName;
+               _fileType = inType;
+       }
+
+       /**
+        * @return source file
+        */
+       public File getFile()
+       {
+               return _sourceFile;
+       }
+
+       /**
+        * @return source name
+        */
+       public String getName()
+       {
+               return _sourceName;
+       }
+
+       /**
+        * @return file type of source
+        */
+       public FILE_TYPE getFileType()
+       {
+               return _fileType;
+       }
+
+       /**
+        * @return number of points from this source
+        */
+       public int getNumPoints()
+       {
+               return _points.length;
+       }
+
+       /**
+        * Take the points from the given track and store
+        * @param inTrack track object containing points
+        * @param inNumPoints number of points loaded
+        */
+       public void populatePointObjects(Track inTrack, int inNumPoints)
+       {
+               if (inNumPoints > 0)
+               {
+                       _points = new DataPoint[inNumPoints];
+                       int trackLen = inTrack.getNumPoints();
+                       System.arraycopy(inTrack.cloneContents(), trackLen-inNumPoints, _points, 0, inNumPoints);
+                       // Note data copied twice here but still more efficient than looping
+               }
+       }
+
+       /**
+        * Look for the given point in the array
+        * @param inPoint point to look for
+        * @return index, or -1 if not found
+        */
+       public int getIndex(DataPoint inPoint)
+       {
+               int idx = -1;
+               for (int i=0; i<_points.length && (idx < 0); i++) {
+                       if (_points[i] == inPoint) {idx = i;}
+               }
+               return idx;
+       }
+}
index 08168d1fb6c348a43cc97eff367021b9c7241ab8..d33886ea3f22eb2eee5057cba3bbd6a2cd9a8524 100644 (file)
@@ -2,8 +2,8 @@ package tim.prune.data;
 
 import java.util.List;
 
-import tim.prune.Config;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.function.edit.FieldEdit;
 import tim.prune.function.edit.FieldEditList;
 import tim.prune.gui.map.MapUtils;
@@ -101,6 +101,23 @@ public class Track
                _scaled = false;
        }
 
+       /**
+        * Request that a rescale be done to recalculate derived values
+        */
+       public void requestRescale()
+       {
+               _scaled = false;
+       }
+
+       /**
+        * Extend the track's field list with the given additional fields
+        * @param inFieldList list of fields to be added
+        */
+       public void extendFieldList(FieldList inFieldList)
+       {
+               _masterFieldList = _masterFieldList.merge(inFieldList);
+       }
+
        ////////////////// Modification methods //////////////////////
 
 
@@ -331,8 +348,6 @@ public class Track
                return foundAlt;
        }
 
-       // TODO: Function to collect and sort photo points by time or photo filename
-       // TODO: Function to convert waypoint names into timestamps
 
        /**
         * Collect all waypoints to the start or end of the track
@@ -1120,9 +1135,10 @@ public class Track
         * Edit the specified point
         * @param inPoint point to edit
         * @param inEditList list of edits to make
+        * @param inUndo true if undo operation, false otherwise
         * @return true if successful
         */
-       public boolean editPoint(DataPoint inPoint, FieldEditList inEditList)
+       public boolean editPoint(DataPoint inPoint, FieldEditList inEditList, boolean inUndo)
        {
                if (inPoint != null && inEditList != null && inEditList.getNumEdits() > 0)
                {
@@ -1134,7 +1150,7 @@ public class Track
                        {
                                FieldEdit edit = inEditList.getEdit(i);
                                Field editField = edit.getField();
-                               inPoint.setFieldValue(editField, edit.getValue());
+                               inPoint.setFieldValue(editField, edit.getValue(), inUndo);
                                // Check that master field list has this field already (maybe point name has been added)
                                if (!_masterFieldList.contains(editField)) {
                                        _masterFieldList.extendList(editField);
index eddea09468c58f7f0cfda69772ce88c690462d7e..254de9341e6119d098ef327196cefdfa0b20a7fa 100644 (file)
@@ -55,6 +55,14 @@ public class TrackInfo
                return _fileInfo;
        }
 
+       /**
+        * Replace the file info with a previously made clone
+        * @param inInfo cloned file info
+        */
+       public void setFileInfo(FileInfo inInfo) {
+               _fileInfo = inInfo;
+       }
+
        /**
         * @return the PhotoList object
         */
@@ -382,4 +390,19 @@ public class TrackInfo
                        }
                }
        }
+
+       /**
+        * Extend the current selection to end at the given point, eg by shift-clicking
+        * @param inPointNum index of end point
+        */
+       public void extendSelection(int inPointNum)
+       {
+               // See whether to start selection from current range start or current point
+               int rangeStart = _selection.getStart();
+               if (rangeStart < 0) {rangeStart = _selection.getCurrentPointIndex();}
+               selectPoint(inPointNum);
+               if (rangeStart < inPointNum) {
+                       _selection.selectRange(rangeStart, inPointNum);
+               }
+       }
 }
index 9e0fd5980fd0f33fa64a61a46b0db6fc345c53b4..0084b87b169a6b0913f386e677c34e15aaeb757f 100644 (file)
@@ -78,6 +78,8 @@ public class ExifReader
        private static final int TAG_THUMBNAIL_OFFSET = 0x0201;\r
        /** Thumbnail length */\r
        private static final int TAG_THUMBNAIL_LENGTH = 0x0202;\r
+       /** Orientation of image */\r
+       private static final int TAG_ORIENTATION = 0x0112;\r
 \r
 \r
        /**\r
@@ -383,6 +385,11 @@ public class ExifReader
                        _thumbnailLength = get16Bits(inTagValueOffset);\r
                        extractThumbnail(inMetadata);\r
                }\r
+               else if (inTagType == TAG_ORIENTATION) {\r
+                       if (inMetadata.getOrientationCode() < 1) {\r
+                               inMetadata.setOrientationCode(get16Bits(inTagValueOffset));\r
+                       }\r
+               }\r
        }\r
 \r
        /**\r
@@ -411,7 +418,7 @@ public class ExifReader
                if (inByteCount > 4)\r
                {\r
                        // If it's bigger than 4 bytes, the dir entry contains an offset.\r
-                       // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an\r
+                       // dirEntryOffset must be passed, as some makers (e.g. FujiFilm) incorrectly use an\r
                        // offset relative to the start of the makernote itself, not the TIFF segment.\r
                        final int offsetVal = get32Bits(inDirEntryOffset + 8);\r
                        if (offsetVal + inByteCount > _data.length)\r
@@ -506,7 +513,8 @@ public class ExifReader
        private int get16Bits(int offset)\r
        {\r
                if (offset<0 || offset+2>_data.length)\r
-                       throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");\r
+                       throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index "\r
+                               + offset + " where max index is " + (_data.length - 1) + ")");\r
 \r
                if (_isMotorolaByteOrder) {\r
                        // Motorola - MSB first\r
index f8be61a88f21150032fb74413205ec2f296de74a..623fc3480709c3658c28ee74551a98af56483961 100644 (file)
@@ -5,7 +5,6 @@ import java.util.List;
 
 /**
  * Class to hold the GPS data extracted from a Jpeg including position and time
- * All contents are in Rational format
  */
 public class JpegData
 {
@@ -19,6 +18,7 @@ public class JpegData
        private Rational[] _gpsTimestamp = null;
        private Rational[] _gpsDatestamp = null;
        private String _originalTimestamp = null;
+       private int _orientationCode = -1;
        private byte[] _thumbnail = null;
        private ArrayList<String> _errors = null;
 
@@ -141,6 +141,17 @@ public class JpegData
                _originalTimestamp = inStamp;
        }
 
+       /**
+        * Set the orientation code
+        * @param inCode code from exif (1 to 8)
+        */
+       public void setOrientationCode(int inCode)
+       {
+               if (inCode >= 1 && inCode <= 8) {
+                       _orientationCode = inCode;
+               }
+       }
+
        /** @return latitude ref as char */
        public char getLatitudeRef() { return _latitudeRef; }
        /** @return latitude as array of 3 Rationals */
@@ -157,6 +168,8 @@ public class JpegData
        public Rational[] getGpsTimestamp() { return _gpsTimestamp; }
        /** @return Gps datestamp as array of 3 Rationals */
        public Rational[] getGpsDatestamp() { return _gpsDatestamp; }
+       /** @return orientation code (1 to 8) */
+       public int getOrientationCode() { return _orientationCode; }
        /** @return original timestamp as string */
        public String getOriginalTimestamp() { return _originalTimestamp; }
 
@@ -172,6 +185,17 @@ public class JpegData
                return _thumbnail;
        }
 
+       /**
+        * @return rotation required to display photo properly (0 to 3)
+        */
+       public int getRequiredRotation()
+       {
+               if (_orientationCode <= 2) { return 0; } // no rotation required
+               if (_orientationCode <= 4) { return 2; } // 180 degrees
+               if (_orientationCode <= 6) { return 1; } // 270 degrees, so need to rotate by 90
+               return 3; // 90 degrees, so need to rotate by 270
+       }
+
        /**
         * @return true if data looks valid, ie has at least lat and long
         *  (altitude and timestamp optional).
index 2835d410c787dca4c4a77fc31f58e33fad27c8b0..3c97a6d79c21e52ac1c7adc430d7999a6a7167f7 100644 (file)
@@ -91,7 +91,8 @@ public class AboutScreen extends GenericFunction
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext3")).append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
                        .append("deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, polski, \u4e2d\u6587; (chinese)<br>" +
-                               "schwiizerd\u00FC\u00FCtsch, portugu\u00EAs, bahasa indonesia, rom\u00E2n\u0103").append("</p>");
+                               "schwiizerd\u00FC\u00FCtsch, \u65E5\u672C\u8A9E (japanese), t\u00FCrk\u00E7e, portugu\u00EAs, " +
+                               "bahasa indonesia, rom\u00E2n\u0103").append("</p>");
                descBuffer.append("<p>").append(I18nManager.getText("dialog.about.translatedby")).append("</p>");
                JEditorPane descPane = new JEditorPane("text/html", descBuffer.toString());
                descPane.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
@@ -177,7 +178,7 @@ public class AboutScreen extends GenericFunction
                        new JLabel("Ramon, Miguel, In\u00E9s, Piotr, Petrovsk, Josatoc, Weehal,"),
                        1, 3);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
-                       new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph"),
+                       new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph, nazotoko, katpatuka"),
                        1, 4);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
                        new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
@@ -189,13 +190,13 @@ public class AboutScreen extends GenericFunction
                        new JLabel(I18nManager.getText("dialog.about.credits.devtools") + " : "),
                        0, 6);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
-                       new JLabel("Mandriva Linux, Sun Java, Eclipse, Svn, Gimp"),
+                       new JLabel("Debian Linux, Sun Java, Eclipse, Svn, Gimp, Inkscape"),
                        1, 6);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
                        new JLabel(I18nManager.getText("dialog.about.credits.othertools") + " : "),
                        0, 7);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
-                       new JLabel("Kate, Povray, Exiftool, Inkscape, Google Earth, Gpsbabel, Gnuplot"),
+                       new JLabel("Openstreetmap, Povray, Exiftool, Google Earth, Gpsbabel, Gnuplot"),
                        1, 7);
                addToGridBagPanel(creditsPanel, gridBag, constraints,
                        new JLabel(I18nManager.getText("dialog.about.credits.thanks") + " : "),
@@ -242,7 +243,7 @@ public class AboutScreen extends GenericFunction
        }
 
        /**
-        * Helper function to reduce complexity of gui making code
+        * Helper function to reduce complexity of gui-making code
         * when adding labels to a GridBagLayout
         * @param inPanel panel to add to
         * @param inLayout GridBagLayout object
@@ -251,8 +252,8 @@ public class AboutScreen extends GenericFunction
         * @param inX grid x
         * @param inY grid y
         */
-       private static void addToGridBagPanel(JPanel inPanel, GridBagLayout inLayout, GridBagConstraints inConstraints,
-               JLabel inLabel, int inX, int inY)
+       private static void addToGridBagPanel(JPanel inPanel, GridBagLayout inLayout,
+               GridBagConstraints inConstraints, JLabel inLabel, int inX, int inY)
        {
                // set x and y in constraints
                inConstraints.gridx = inX;
index 14adb8364189b949d580020bd7c51534e396d07f..1032fcfca84f2ea8e3105973dbe7c5731f1996c0 100644 (file)
@@ -1,7 +1,6 @@
 package tim.prune.function;
 
 import java.awt.BorderLayout;
-import java.awt.Component;
 import java.awt.FlowLayout;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
@@ -81,7 +80,7 @@ public class AddAltitudeOffset extends GenericFunction
         * Create dialog components
         * @return Panel containing all gui elements in dialog
         */
-       private Component makeDialogComponents()
+       private JPanel makeDialogComponents()
        {
                JPanel dialogPanel = new JPanel();
                dialogPanel.setLayout(new BorderLayout());
diff --git a/tim/prune/function/ConvertNamesToTimes.java b/tim/prune/function/ConvertNamesToTimes.java
new file mode 100644 (file)
index 0000000..c0286ab
--- /dev/null
@@ -0,0 +1,73 @@
+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.Field;
+import tim.prune.data.Timestamp;
+import tim.prune.data.Track;
+import tim.prune.undo.UndoConvertNamesToTimes;
+
+/**
+ * Class to provide the function to convert waypoint names to timestamps
+ */
+public class ConvertNamesToTimes extends GenericFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public ConvertNamesToTimes(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.convertnamestotimes";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               int selStart = _app.getTrackInfo().getSelection().getStart();
+               int selEnd = _app.getTrackInfo().getSelection().getEnd();
+               final Track track = _app.getTrackInfo().getTrack();
+               if (!track.hasData(Field.WAYPT_NAME, selStart, selEnd))
+               {
+                       _app.showErrorMessage(getNameKey(), "error.convertnamestotimes.nonames");
+                       return;
+               }
+               UndoConvertNamesToTimes undo = new UndoConvertNamesToTimes(_app.getTrackInfo());
+               int numConverted = 0;
+               // Loop over all points in selection
+               for (int i=selStart; i<=selEnd; i++)
+               {
+                       DataPoint point = track.getPoint(i);
+                       if (point.isWaypoint())
+                       {
+                               Timestamp tstamp = new Timestamp(point.getWaypointName());
+                               if (tstamp.isValid()) {
+                                       // timestamp could be parsed!
+                                       point.setFieldValue(Field.TIMESTAMP, point.getWaypointName(), false);
+                                       // set waypoint name to nothing (track point)
+                                       point.setFieldValue(Field.WAYPT_NAME, null, false);
+                                       // increment counter
+                                       numConverted++;
+                               }
+                       }
+               }
+               if (numConverted > 0)
+               {
+                       _app.getTrackInfo().getTrack().requestRescale();
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_EDITED);
+                       _app.completeFunction(undo, I18nManager.getText("confirm.convertnamestotimes"));
+               }
+       }
+
+}
diff --git a/tim/prune/function/DuplicatePoint.java b/tim/prune/function/DuplicatePoint.java
new file mode 100644 (file)
index 0000000..341066a
--- /dev/null
@@ -0,0 +1,38 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.data.DataPoint;
+
+/**
+ * Class to provide the function to duplicate
+ * the current point and add to the end of the track
+ */
+public class DuplicatePoint extends GenericFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public DuplicatePoint(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.duplicatepoint";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               DataPoint point = _app.getTrackInfo().getCurrentPoint();
+               if (point != null) {
+                       // Pass information back to App to complete function
+                       _app.createPoint(point.clonePoint());
+               }
+       }
+}
diff --git a/tim/prune/function/FullRangeDetails.java b/tim/prune/function/FullRangeDetails.java
new file mode 100644 (file)
index 0000000..ed86ef9
--- /dev/null
@@ -0,0 +1,219 @@
+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.text.NumberFormat;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.Altitude;
+import tim.prune.data.Distance;
+import tim.prune.data.Selection;
+import tim.prune.gui.DisplayUtils;
+
+/**
+ * Class to show the full range details in a separate popup
+ */
+public class FullRangeDetails extends GenericFunction
+{
+       /** Dialog */
+       private JDialog _dialog = null;
+       /** Label for number of segments */
+       private JLabel _numSegsLabel = null;
+       /** Label for pace */
+       private JLabel _paceLabel = null;
+       /** Label for gradient */
+       private JLabel _gradientLabel = null;
+       /** Moving distance, speed */
+       private JLabel _movingDistanceLabel = null, _aveMovingSpeedLabel = null;
+       /** Number formatter for one decimal place */
+       private static final NumberFormat FORMAT_ONE_DP = NumberFormat.getNumberInstance();
+       /** Flexible number formatter for different decimal places */
+       private NumberFormat _distanceFormatter = NumberFormat.getInstance();
+
+       /**
+        * Constructor
+        * @param inApp App object
+        */
+       public FullRangeDetails(App inApp)
+       {
+               super(inApp);
+               FORMAT_ONE_DP.setMaximumFractionDigits(1);
+               FORMAT_ONE_DP.setMinimumFractionDigits(1);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.fullrangedetails";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               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();
+               }
+               updateDetails();
+               _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(5, 5));
+               // Label at top
+               JLabel topLabel = new JLabel(I18nManager.getText("dialog.fullrangedetails.intro"));
+               topLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+               dialogPanel.add(topLabel, BorderLayout.NORTH);
+
+               // Details panel in middle
+               JPanel midPanel = new JPanel();
+               midPanel.setLayout(new GridLayout(0, 2, 6, 2));
+               midPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               // Number of segments
+               JLabel segLabel = new JLabel(I18nManager.getText("details.range.numsegments") + ": ");
+               segLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(segLabel);
+               _numSegsLabel = new JLabel("100");
+               midPanel.add(_numSegsLabel);
+               // Pace
+               JLabel paceLabel = new JLabel(I18nManager.getText("details.range.pace") + ": ");
+               paceLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(paceLabel);
+               _paceLabel = new JLabel("8 min/km");
+               midPanel.add(_paceLabel);
+               // Gradient
+               JLabel gradientLabel = new JLabel(I18nManager.getText("details.range.gradient") + ": ");
+               gradientLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(gradientLabel);
+               _gradientLabel = new JLabel("10 %");
+               midPanel.add(_gradientLabel);
+               // Moving distance
+               JLabel movingDistLabel = new JLabel(I18nManager.getText("fieldname.movingdistance") + ": ");
+               movingDistLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(movingDistLabel);
+               _movingDistanceLabel = new JLabel("5 km");
+               midPanel.add(_movingDistanceLabel);
+               // Moving speed
+               JLabel movingSpeedLabel = new JLabel(I18nManager.getText("details.range.avemovingspeed") + ": ");
+               movingSpeedLabel.setHorizontalAlignment(JLabel.RIGHT);
+               midPanel.add(movingSpeedLabel);
+               _aveMovingSpeedLabel = new JLabel("5 km/h");
+               midPanel.add(_aveMovingSpeedLabel);
+
+               dialogPanel.add(midPanel, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton closeButton = new JButton(I18nManager.getText("button.close"));
+               closeButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               buttonPanel.add(closeButton);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+
+       /**
+        * Update the labels with the current details
+        */
+       private void updateDetails()
+       {
+               Selection selection = _app.getTrackInfo().getSelection();
+               // Number of segments
+               _numSegsLabel.setText("" + selection.getNumSegments());
+               // Pace value
+               if (selection.getNumSeconds() > 0)
+               {
+                       boolean useMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
+                       Distance.Units distUnits = useMetric?Distance.Units.KILOMETRES:Distance.Units.MILES;
+                       String distUnitsStr = I18nManager.getText(useMetric?"units.kilometres.short":"units.miles.short");
+                       _paceLabel.setText(DisplayUtils.buildDurationString(
+                                       (long) (selection.getNumSeconds()/selection.getDistance(distUnits)))
+                               + " / " + distUnitsStr);
+               }
+               else {
+                       _paceLabel.setText("");
+               }
+               // Gradient
+               Altitude firstAlt = _app.getTrackInfo().getTrack().getPoint(selection.getStart()).getAltitude();
+               Altitude lastAlt = _app.getTrackInfo().getTrack().getPoint(selection.getEnd()).getAltitude();
+               double metreDist = selection.getDistance(Distance.Units.METRES);
+               if (firstAlt.isValid() && lastAlt.isValid() && metreDist > 0.0)
+               {
+                       // got an altitude and range
+                       int altDiffInMetres = lastAlt.getValue(Altitude.Format.METRES) - firstAlt.getValue(Altitude.Format.METRES);
+                       double gradient = altDiffInMetres * 100.0 / metreDist;
+                       _gradientLabel.setText(FORMAT_ONE_DP.format(gradient) + " %");
+               }
+               else {
+                       // no altitude given
+                       _gradientLabel.setText("");
+               }
+
+               // Show moving distance and average even when number of segments is 1
+               final boolean isMetric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
+               final Distance.Units distUnits = isMetric?Distance.Units.KILOMETRES:Distance.Units.MILES;
+               final String distUnitsStr = I18nManager.getText(isMetric?"units.kilometres.short":"units.miles.short");
+               final String speedUnitsStr = I18nManager.getText(isMetric?"units.kmh":"units.mph");
+               // Moving distance
+               _movingDistanceLabel.setText(roundedNumber(selection.getMovingDistance(distUnits)) + " " + distUnitsStr);
+               // Moving average speed
+               long numSecs = selection.getMovingSeconds();
+               if (numSecs > 0) {
+                       _aveMovingSpeedLabel.setText(roundedNumber(selection.getMovingDistance(distUnits)/numSecs*3600.0)
+                               + " " + speedUnitsStr);
+               }
+               else {
+                       _aveMovingSpeedLabel.setText("");
+               }
+       }
+
+       /**
+        * Format a number to a sensible precision
+        * @param inDist distance
+        * @return formatted String
+        */
+       private String roundedNumber(double inDist)
+       {
+               // Set precision of formatter
+               int numDigits = 0;
+               if (inDist < 1.0)
+                       numDigits = 3;
+               else if (inDist < 10.0)
+                       numDigits = 2;
+               else if (inDist < 100.0)
+                       numDigits = 1;
+               // set formatter
+               _distanceFormatter.setMaximumFractionDigits(numDigits);
+               _distanceFormatter.setMinimumFractionDigits(numDigits);
+               return _distanceFormatter.format(inDist);
+       }
+}
diff --git a/tim/prune/function/IgnoreExifThumb.java b/tim/prune/function/IgnoreExifThumb.java
new file mode 100644 (file)
index 0000000..8b292e5
--- /dev/null
@@ -0,0 +1,42 @@
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.Photo;
+
+/**
+ * Class to provide the function to disable the exif thumbnail
+ * for the current photo so that the full image must be loaded
+ */
+public class IgnoreExifThumb extends GenericFunction
+{
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public IgnoreExifThumb(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.ignoreexifthumb";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               Photo photo = _app.getTrackInfo().getCurrentPhoto();
+               if (photo != null)
+               {
+                       // no undo necessary, no data being edited
+                       photo.setExifThumbnail(null);
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.PHOTOS_MODIFIED);
+               }
+       }
+}
diff --git a/tim/prune/function/PasteCoordinates.java b/tim/prune/function/PasteCoordinates.java
new file mode 100644 (file)
index 0000000..1106001
--- /dev/null
@@ -0,0 +1,269 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+
+import javax.swing.BorderFactory;
+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.JTextField;
+import javax.swing.SwingConstants;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.Altitude;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Field;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
+import tim.prune.gui.GuiGridLayout;
+
+/**
+ * Class to provide the function to paste coordinates
+ * - see wikipedia, opencaching.de, waymarking.com etc
+ */
+public class PasteCoordinates extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JTextField _nameField = null;
+       private JTextField _coordField = null;
+       private JButton _okButton = null;
+       private JComboBox _altUnitsDropDown;
+
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        */
+       public PasteCoordinates(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return "function.pastecoordinates";
+       }
+
+       /**
+        * 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();
+               }
+               // MAYBE: Paste clipboard into the edit field
+               _coordField.setText("");
+               _nameField.setText("");
+               boolean metric = Config.getConfigBoolean(Config.KEY_METRIC_UNITS);
+               _altUnitsDropDown.setSelectedIndex(metric?0:1);
+               enableOK();
+               _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.pastecoordinates.desc")), BorderLayout.NORTH);
+               JPanel mainPanel = new JPanel();
+               GuiGridLayout grid = new GuiGridLayout(mainPanel);
+               _coordField = new JTextField("", 25);
+               // Listeners to enable/disable ok button
+               KeyAdapter keyListener = new KeyAdapter() {
+                       /** Key released */
+                       public void keyReleased(KeyEvent arg0) {
+                               enableOK();
+                       }
+               };
+               MouseAdapter mouseListener = new MouseAdapter() {
+                       public void mouseReleased(java.awt.event.MouseEvent arg0) {
+                               enableOK();
+                       };
+               };
+               _coordField.addKeyListener(keyListener);
+               _coordField.addMouseListener(mouseListener);
+               JLabel coordLabel = new JLabel(I18nManager.getText("dialog.pastecoordinates.coords"));
+               coordLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(coordLabel);
+               grid.add(_coordField);
+               // Altitude format (if any)
+               JLabel formatLabel = new JLabel(I18nManager.getText("dialog.openoptions.altitudeunits"));
+               formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(formatLabel);
+               final String[] altunits = {I18nManager.getText("units.metres"), I18nManager.getText("units.feet")};
+               _altUnitsDropDown = new JComboBox(altunits);
+               grid.add(_altUnitsDropDown);
+               // Waypoint name
+               JLabel nameLabel = new JLabel(I18nManager.getText("dialog.pointnameedit.name"));
+               nameLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+               grid.add(nameLabel);
+               _nameField = new JTextField("", 12);
+               grid.add(_nameField);
+               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);
+               _okButton.setEnabled(false);
+               _coordField.addActionListener(okListener);
+               _nameField.addActionListener(okListener);
+               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);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+               return dialogPanel;
+       }
+
+       /**
+        * Enable or disable the OK button based on the contents of the text field
+        */
+       private void enableOK()
+       {
+               String text = _coordField.getText();
+               _okButton.setEnabled(text != null && text.length() > 10
+                       && (text.indexOf(' ') >= 0 || text.indexOf(',') >= 0));
+       }
+
+       /**
+        * Finish the dialog when OK pressed
+        */
+       private void finish()
+       {
+               DataPoint point = null;
+               // Try to split using commas
+               String[] items = _coordField.getText().split(",");
+               if (items.length == 2) {
+                       point = parseValues(items[0].trim(), items[1].trim(), null);
+               }
+               else if (items.length == 3) {
+                       point = parseValues(items[0].trim(), items[1].trim(), items[2].trim());
+               }
+               else {
+                       // Splitting with commas didn't work, so try spaces
+                       items = _coordField.getText().split(" ");
+                       if (items.length == 2) {
+                               point = parseValues(items[0], items[1], null);
+                       }
+                       else if (items.length == 3 && items[1].length() == 1) {
+                               point = parseValues(items[0], items[2], null);
+                       }
+                       else if (items.length == 4) {
+                               point = parseValues(items[0] + " " + items[1],
+                                       items[2] + " " + items[3], null);
+                       }
+                       else if (items.length == 6) {
+                               point = parseValues(items[0] + " " + items[1] + " " + items[2],
+                                       items[3] + " " + items[4] + " " + items[5], null);
+                       }
+                       else if (items.length == 8) {
+                               point = parseValues(items[0] + " " + items[1] + " " + items[2] + " " + items[3],
+                                       items[4] + " " + items[5] + " " + items[6] + " " + items[7], null);
+                       }
+               }
+
+               if (point == null) {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("dialog.pastecoordinates.nothingfound"),
+                               I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
+               }
+               else {
+                       // See if name was entered
+                       String name = _nameField.getText();
+                       if (name != null && name.length() > 0) {
+                               point.setFieldValue(Field.WAYPT_NAME, name, false);
+                       }
+                       // Pass information back to App to complete function
+                       _app.createPoint(point);
+                       _dialog.dispose();
+               }
+       }
+
+
+       /**
+        * Try to parse the three given Strings into lat, lon and alt
+        * @param inValue1 first value (either lat/lon)
+        * @param inValue2 second value (either lon/lat)
+        * @param inValue3 altitude value or null if absent
+        * @return DataPoint object or null if failed
+        */
+       private DataPoint parseValues(String inValue1, String inValue2, String inValue3)
+       {
+               // Check for parseable altitude
+               Altitude alt = null;
+               if (inValue3 != null)
+               {
+                       // Look at altitude units dropdown
+                       final Altitude.Format altFormat = (_altUnitsDropDown.getSelectedIndex()==0?
+                               Altitude.Format.METRES:Altitude.Format.FEET);
+                       alt = new Altitude(inValue3, altFormat);
+                       if (!alt.isValid()) {alt = null;}
+               }
+               // See if value1 can be lat and value2 lon:
+               Latitude coord1 = new Latitude(inValue1);
+               Longitude coord2 = new Longitude(inValue2);
+               if (coord1.isValid() && !coord1.getCardinalGuessed()
+                       && coord2.isValid() && !coord2.getCardinalGuessed())
+               {
+                       return new DataPoint(coord1, coord2, alt);
+               }
+               // Now see if lat/lon are reversed
+               Longitude coord3 = new Longitude(inValue1);
+               Latitude coord4 = new Latitude(inValue2);
+               if (coord3.isValid() && !coord3.getCardinalGuessed()
+                       && coord4.isValid() && !coord4.getCardinalGuessed())
+               {
+                       // reversed order
+                       return new DataPoint(coord4, coord3, alt);
+               }
+               // Didn't work without guessing cardinals, so accept latitude, longitude order (if valid)
+               if (coord1.isValid() && coord2.isValid()) {
+                       return new DataPoint(coord1, coord2, alt);
+               }
+               // Or accept other order (if valid)
+               if (coord3.isValid() && coord4.isValid()) {
+                       // reversed order
+                       return new DataPoint(coord4, coord3, alt);
+               }
+               // Couldn't be parsed either way
+               return null;
+       }
+}
diff --git a/tim/prune/function/RearrangePhotosFunction.java b/tim/prune/function/RearrangePhotosFunction.java
new file mode 100644 (file)
index 0000000..7a408c0
--- /dev/null
@@ -0,0 +1,220 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+import tim.prune.undo.UndoRearrangePhotos;
+
+/**
+ * Class to provide the function for rearranging photo points
+ */
+public class RearrangePhotosFunction extends GenericFunction
+{
+       /** Function dialog */
+       private JDialog _dialog = null;
+       /** Radio buttons for start/end */
+       private JRadioButton[] _positionRadios = null;
+       /** Radio buttons for sorting */
+       private JRadioButton[] _sortRadios = null;
+
+
+       /**
+        * Constructor
+        * @param inApp app object
+        */
+       public RearrangePhotosFunction(App inApp)
+       {
+               super(inApp);
+       }
+
+       /** Begin the rearrange */
+       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();
+               }
+               // Reset dialog and show
+               _dialog.setVisible(true);
+       }
+
+       /** Get the name key (not needed) */
+       public String getNameKey() {
+               return "function.rearrangephotos";
+       }
+
+
+       /**
+        * Create dialog components
+        * @return Panel containing all gui elements in dialog
+        */
+       private JPanel makeDialogComponents()
+       {
+               JPanel dialogPanel = new JPanel();
+               dialogPanel.setLayout(new BorderLayout());
+               dialogPanel.add(new JLabel(I18nManager.getText("dialog.rearrangephotos.desc")), BorderLayout.NORTH);
+               // Radios for position (start / end)
+               _positionRadios = new JRadioButton[2];
+               final String[] posNames = {"tostart", "toend"};
+               ButtonGroup posGroup = new ButtonGroup();
+               JPanel posPanel = new JPanel();
+               posPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               for (int i=0; i<2; i++)
+               {
+                       _positionRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + posNames[i]));
+                       posGroup.add(_positionRadios[i]);
+                       posPanel.add(_positionRadios[i]);
+               }
+               _positionRadios[0].setSelected(true);
+               // Radios for sort (none / filename / time)
+               _sortRadios = new JRadioButton[3];
+               final String[] sortNames = {"nosort", "sortbyfilename", "sortbytime"};
+               ButtonGroup sortGroup = new ButtonGroup();
+               JPanel sortPanel = new JPanel();
+               sortPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
+               for (int i=0; i<3; i++)
+               {
+                       _sortRadios[i] = new JRadioButton(I18nManager.getText("dialog.rearrangephotos." + sortNames[i]));
+                       sortGroup.add(_sortRadios[i]);
+                       sortPanel.add(_sortRadios[i]);
+               }
+               _sortRadios[0].setSelected(true);
+               // add to middle of dialog
+               JPanel centrePanel = new JPanel();
+               centrePanel.setLayout(new BoxLayout(centrePanel, BoxLayout.Y_AXIS));
+               centrePanel.add(posPanel);
+               centrePanel.add(sortPanel);
+               dialogPanel.add(centrePanel, BorderLayout.CENTER);
+               // button panel at bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton okButton = new JButton(I18nManager.getText("button.ok"));
+               okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               finish();
+                               _dialog.dispose();
+                       }
+               });
+               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);
+               dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return dialogPanel;
+       }
+
+       /**
+        * Perform the rearrange
+        */
+       private void finish()
+       {
+               Track track = _app.getTrackInfo().getTrack();
+               UndoRearrangePhotos undo = new UndoRearrangePhotos(track);
+               // Loop through track collecting non-photo points and photo points
+               final int numPoints = track.getNumPoints();
+               DataPoint[] nonPhotos = new DataPoint[numPoints];
+               DataPoint[] photos = new DataPoint[numPoints];
+               int numNonPhotos = 0;
+               int numPhotos = 0;
+               for (int i=0; i<numPoints; i++)
+               {
+                       DataPoint point = track.getPoint(i);
+                       if (point.getPhoto() != null) {
+                               photos[numPhotos] = point;
+                               numPhotos++;
+                       }
+                       else {
+                               nonPhotos[numNonPhotos] = point;
+                               numNonPhotos++;
+                       }
+               }
+               // Sort photos if necessary
+               if (!_sortRadios[0].isSelected() && numPhotos > 1) {
+                       sortPhotos(photos, _sortRadios[1].isSelected());
+               }
+               // Put the non-photo points and photo points together
+               DataPoint[] neworder = new DataPoint[numPoints];
+               if (_positionRadios[0].isSelected()) {
+                       // photos at front
+                       System.arraycopy(photos, 0, neworder, 0, numPhotos);
+                       System.arraycopy(nonPhotos, 0, neworder, numPhotos, numNonPhotos);
+               }
+               else {
+                       // photos at end
+                       System.arraycopy(nonPhotos, 0, neworder, 0, numNonPhotos);
+                       System.arraycopy(photos, 0, neworder, numNonPhotos, numPhotos);
+               }
+               // Give track the new point order
+               if (track.replaceContents(neworder))
+               {
+                       _app.getTrackInfo().getSelection().clearAll();
+                       _app.completeFunction(undo, I18nManager.getText("confirm.rearrangephotos"));
+                       // Note: subscribers are informed up to three times now
+               }
+               else
+               {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("error.rearrange.noop"),
+                               I18nManager.getText("error.function.noop.title"), JOptionPane.WARNING_MESSAGE);
+               }
+       }
+
+       /**
+        * Sort the given photo list either by filename or by time
+        * @param inPhotos array of DataPoint objects to sort
+        * @param inSortByFile true to sort by filename, false to sort by timestamp
+        * @return sorted array
+        */
+       private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile)
+       {
+               Comparator<DataPoint> comparator = null;
+               if (inSortByFile) {
+                       // sort by filename
+                       comparator = new Comparator<DataPoint>() {
+                               public int compare(DataPoint inP1, DataPoint inP2) {
+                                       if (inP2 == null) return -1; // all nulls at end
+                                       if (inP1 == null) return 1;
+                                       return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
+                               }
+                       };
+               }
+               else {
+                       // sort by photo timestamp
+                       comparator = new Comparator<DataPoint>() {
+                               public int compare(DataPoint inP1, DataPoint inP2) {
+                                       if (inP2 == null) return -1; // all nulls at end
+                                       if (inP1 == null) return 1;
+                                       long secDiff = inP1.getPhoto().getTimestamp().getSecondsSince(inP2.getPhoto().getTimestamp());
+                                       return (secDiff<0?-1:(secDiff==0?0:1));
+                               }
+                       };
+               }
+               Arrays.sort(inPhotos, comparator);
+       }
+}
diff --git a/tim/prune/function/RotatePhoto.java b/tim/prune/function/RotatePhoto.java
new file mode 100644 (file)
index 0000000..493f7ce
--- /dev/null
@@ -0,0 +1,50 @@
+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.Photo;
+import tim.prune.undo.UndoRotatePhoto;
+
+/**
+ * Class to provide the function to rotate a photo
+ * either clockwise or anticlockwise
+ */
+public class RotatePhoto extends GenericFunction
+{
+       /** Direction of rotation */
+       private boolean _direction = true;
+
+       /**
+        * Constructor
+        * @param inApp application object for callback
+        * @param inDir true for clockwise, false for anticlockwise
+        */
+       public RotatePhoto(App inApp, boolean inDir)
+       {
+               super(inApp);
+               _direction = inDir;
+       }
+
+       /** Get the name key */
+       public String getNameKey() {
+               return _direction?"function.rotatephotoright":"function.rotatephotoleft";
+       }
+
+       /**
+        * Begin the function
+        */
+       public void begin()
+       {
+               Photo photo = _app.getTrackInfo().getCurrentPhoto();
+               if (photo != null)
+               {
+                       UndoRotatePhoto undo = new UndoRotatePhoto(photo, _direction);
+                       photo.rotate(_direction);
+                       UpdateMessageBroker.informSubscribers(DataSubscriber.PHOTOS_MODIFIED);
+                       _app.completeFunction(undo, I18nManager.getText("confirm.rotatephoto"));
+               }
+       }
+}
index 0f2339f7ea84708cdd44eeb30d08927516d7cbc2..125b4b7496b0558c487cf87c2f63a83fbd2fae70 100644 (file)
@@ -20,9 +20,9 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 
 /**
  * Class to provide the function to save the config settings
diff --git a/tim/prune/function/SetColours.java b/tim/prune/function/SetColours.java
new file mode 100644 (file)
index 0000000..1329d05
--- /dev/null
@@ -0,0 +1,205 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.ColourScheme;
+import tim.prune.config.Config;
+import tim.prune.gui.ColourChooser;
+import tim.prune.gui.ColourPatch;
+
+/**
+ * Class to show the popup window for setting the colours
+ */
+public class SetColours extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JButton _okButton = null;
+       /** Array of 8 colour patches */
+       private ColourPatch[] _patches = null;
+       /** Single colour chooser */
+       private ColourChooser _colourChooser = null;
+
+       /** Array of label keys for the 8 patches */
+       private static final String[] LABEL_KEYS = {"background", "primary", "point", "secondary",
+               "selection", "borders", "text", "lines"};
+       /** Array of indices for the 8 patches */
+       private static final int[] INDICES = {ColourScheme.IDX_BACKGROUND, ColourScheme.IDX_PRIMARY,
+               ColourScheme.IDX_POINT, ColourScheme.IDX_SECONDARY,
+               ColourScheme.IDX_SELECTION, ColourScheme.IDX_BORDERS,
+               ColourScheme.IDX_TEXT, ColourScheme.IDX_LINES
+       };
+
+       /**
+        * Inner class to react to patch clicks
+        */
+       class PatchListener extends MouseAdapter
+       {
+               /** Associated patch */
+               private ColourPatch _patch = null;
+               /** Constructor */
+               public PatchListener(ColourPatch inPatch) {
+                       _patch = inPatch;
+               }
+               /** React to mouse clicks */
+               public void mouseClicked(MouseEvent e)
+               {
+                       _colourChooser.showDialog(_patch.getBackground());
+                       Color colour = _colourChooser.getChosenColour();
+                       if (colour != null) _patch.setColour(colour);
+               }
+       }
+
+       /**
+        * Constructor
+        * @param inApp app object
+        */
+       public SetColours(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * Return the name key for this function
+        */
+       public String getNameKey()
+       {
+               return "function.setcolours";
+       }
+
+       /**
+        * @return the contents of the window as a Component
+        */
+       private Component makeContents()
+       {
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BorderLayout(0, 10));
+
+               JLabel intro = new JLabel(I18nManager.getText("dialog.setcolours.intro"));
+               intro.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));
+               mainPanel.add(intro, BorderLayout.NORTH);
+               JPanel centralPanel = new JPanel();
+               centralPanel.setLayout(new GridLayout());
+               _patches = new ColourPatch[8];
+
+               ColourScheme scheme = Config.getColourScheme();
+               ColourPatch patch = null;
+               // Loop over four columns of patches
+               for (int i=0; i<4; i++)
+               {
+                       JPanel colPanel = new JPanel();
+                       colPanel.setLayout(new BoxLayout(colPanel, BoxLayout.Y_AXIS));
+                       // Top label and patch
+                       colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2])));
+                       patch = new ColourPatch(scheme.getColour(INDICES[i*2]));
+                       patch.addMouseListener(new PatchListener(patch));
+                       colPanel.add(patch);
+                       _patches[i*2] = patch;
+                       // separator
+                       colPanel.add(Box.createRigidArea(new Dimension(0, 5)));
+                       // Bottom label and patch
+                       colPanel.add(new JLabel(I18nManager.getText("dialog.setcolours." + LABEL_KEYS[i*2+1])));
+                       patch = new ColourPatch(scheme.getColour(INDICES[i*2+1]));
+                       patch.addMouseListener(new PatchListener(patch));
+                       colPanel.add(patch);
+                       _patches[i*2+1] = patch;
+
+                       // Add column to panel
+                       colPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+                       centralPanel.add(colPanel);
+               }
+               mainPanel.add(centralPanel, BorderLayout.CENTER);
+
+               // Buttons at the bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
+               _okButton = new JButton(I18nManager.getText("button.ok"));
+               _okButton.addActionListener(new ActionListener()
+               {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               updateConfigColours();
+                               _dialog.dispose();
+                       }
+               });
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _dialog.dispose();
+                       }
+               });
+               JButton resetButton = new JButton(I18nManager.getText("button.resettodefaults"));
+               resetButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               for (int i=0; i<8; i++) {
+                                       _patches[i].setColour(ColourScheme.getDefaultColour(INDICES[i]));
+                               }
+                       }
+               });
+               buttonPanel.add(resetButton);
+               buttonPanel.add(Box.createHorizontalGlue());
+               buttonPanel.add(_okButton);
+               buttonPanel.add(Box.createHorizontalStrut(5));
+               buttonPanel.add(cancelButton);
+               buttonPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
+               mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return mainPanel;
+       }
+
+
+       /**
+        * Show window
+        */
+       public void begin()
+       {
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()));
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.getContentPane().add(makeContents());
+                       _dialog.pack();
+                       _colourChooser = new ColourChooser(_dialog);
+               }
+               // Reset colours to current ones
+               ColourScheme scheme = Config.getColourScheme();
+               for (int i=0; i<8; i++) {
+                       _patches[i].setColour(scheme.getColour(INDICES[i]));
+               }
+               _dialog.setVisible(true);
+               _okButton.requestFocus();
+       }
+
+       /**
+        * Update the current colour scheme with the selected colours
+        */
+       private void updateConfigColours()
+       {
+               ColourScheme scheme = Config.getColourScheme();
+               for (int i=0; i<_patches.length; i++)
+               {
+                       scheme.setColour(INDICES[i], _patches[i].getBackground());
+               }
+               Config.updateColourScheme();
+               UpdateMessageBroker.informSubscribers();
+       }
+}
index 1677e542fcdae3a72c6d0d89861acc20f52d9b78..c63d4fe2ac8d65f5e72081d00e4c3a058fb5a135 100644 (file)
@@ -18,9 +18,9 @@ import javax.swing.JPanel;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.gui.WholeNumberField;
 
 /**
diff --git a/tim/prune/function/SetLanguage.java b/tim/prune/function/SetLanguage.java
new file mode 100644 (file)
index 0000000..d54e1be
--- /dev/null
@@ -0,0 +1,332 @@
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.Locale;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.border.EtchedBorder;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.load.GenericFileFilter;
+
+/**
+ * Class to show the popup window for setting the language code / file
+ */
+public class SetLanguage extends GenericFunction
+{
+       private JDialog _dialog = null;
+       private JComboBox _languageDropDown = null;
+       private JTextField _langFileBox = null;
+       private int _startIndex = 0;
+
+       /** Names of languages for display in dropdown (not translated) */
+       private static final String[] LANGUAGE_NAMES = {"deutsch", "english", "espa\u00F1ol", "fran\u00E7ais",
+               "italiano", "polski", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
+               "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", "portugu\u00EAs", "bahasa indonesia", "rom\u00E2n\u0103"
+       };
+       /** Associated language codes (must be in same order as names!) */
+       private static final String[] LANGUAGE_CODES = {"de", "en", "es", "fr", "it", "pl", "zh", "ja",
+               "de_ch", "tr", "pt", "in", "ro"
+       };
+
+
+       /**
+        * Constructor
+        * @param inApp app object
+        */
+       public SetLanguage(App inApp)
+       {
+               super(inApp);
+       }
+
+       /**
+        * Return the name key for this function
+        */
+       public String getNameKey()
+       {
+               return "function.setlanguage";
+       }
+
+       /**
+        * @return the contents of the window as a Component
+        */
+       private Component makeContents()
+       {
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BorderLayout(0, 5));
+               JPanel midPanel = new JPanel();
+               midPanel.setLayout(new BoxLayout(midPanel, BoxLayout.Y_AXIS));
+               midPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+               // description labels
+               JLabel label1 = new JLabel("<html>" + I18nManager.getText("dialog.setlanguage.firstintro") + "</html>");
+               label1.setAlignmentX(Component.LEFT_ALIGNMENT);
+               label1.setHorizontalAlignment(SwingConstants.LEFT);
+               midPanel.add(label1);
+               JLabel label2 = new JLabel("<html>" + I18nManager.getText("dialog.setlanguage.secondintro") + "</html>");
+               label2.setAlignmentX(Component.LEFT_ALIGNMENT);
+               label2.setHorizontalAlignment(SwingConstants.LEFT);
+               midPanel.add(label2);
+               midPanel.add(Box.createVerticalStrut(10));
+
+               // built-in languages
+               JPanel builtinPanel = new JPanel();
+               builtinPanel.setBorder(BorderFactory.createCompoundBorder(
+                       BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
+               );
+               builtinPanel.setLayout(new BoxLayout(builtinPanel, BoxLayout.X_AXIS));
+               builtinPanel.add(new JLabel(I18nManager.getText("dialog.setlanguage.language") + " : "));
+               // Language dropdown
+               _languageDropDown = new JComboBox(LANGUAGE_NAMES);
+               builtinPanel.add(_languageDropDown);
+               builtinPanel.add(Box.createHorizontalGlue());
+               JButton selectLangButton = new JButton(I18nManager.getText("button.select"));
+               selectLangButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               selectLanguage();
+                       }
+               });
+               builtinPanel.add(selectLangButton);
+               builtinPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               midPanel.add(builtinPanel);
+               midPanel.add(Box.createVerticalStrut(4));
+
+               // external language file
+               JPanel extraPanel = new JPanel();
+               extraPanel.setBorder(BorderFactory.createCompoundBorder(
+                       BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
+               );
+               extraPanel.setLayout(new BoxLayout(extraPanel, BoxLayout.X_AXIS));
+               extraPanel.add(new JLabel(I18nManager.getText("dialog.setlanguage.languagefile") + " : "));
+               _langFileBox = new JTextField("some_long_example_file_path.txt");
+               extraPanel.add(_langFileBox);
+               // browse button
+               JButton browseButton = new JButton(I18nManager.getText("button.browse"));
+               extraPanel.add(browseButton);
+               browseButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent arg0) {
+                               chooseFile();
+                       }
+               });
+               extraPanel.add(Box.createHorizontalStrut(5));
+               JButton selectFileButton = new JButton(I18nManager.getText("button.select"));
+               selectFileButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               selectLanguageFile();
+                       }
+               });
+               extraPanel.add(selectFileButton);
+               extraPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+               midPanel.add(Box.createVerticalStrut(5));
+               midPanel.add(extraPanel);
+               midPanel.add(Box.createVerticalGlue());
+               mainPanel.add(midPanel, BorderLayout.CENTER);
+
+               // Cancel button at the bottom
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               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;
+       }
+
+
+       /**
+        * Show window
+        */
+       public void begin()
+       {
+               if (_dialog == null)
+               {
+                       _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()));
+                       _dialog.setLocationRelativeTo(_parentFrame);
+                       _dialog.getContentPane().add(makeContents());
+                       _dialog.pack();
+               }
+               // Try to use code from config
+               String code = Config.getConfigString(Config.KEY_LANGUAGE_CODE);
+               int index = getLanguageIndex(code);
+               // If it's not present, then use system locale settings
+               if (index < 0) {
+                       Locale locale = Locale.getDefault();
+                       index = getLanguageIndex(locale.getLanguage() + "_" + locale.getCountry());
+                       if (index < 0) {
+                               index = getLanguageIndex(locale.getLanguage());
+                       }
+               }
+               // Select appropriate language from dropdown
+               if (index >= 0) {
+                       _languageDropDown.setSelectedIndex(index);
+               }
+               _startIndex = _languageDropDown.getSelectedIndex();
+               // Get language file from config
+               String langfile = Config.getConfigString(Config.KEY_LANGUAGE_FILE);
+               _langFileBox.setText(langfile==null?"":langfile);
+               _dialog.setVisible(true);
+       }
+
+       /**
+        * Find the index of the given language code
+        * @param inCode code to look for
+        * @return index if found or -1
+        */
+       private static int getLanguageIndex(String inCode)
+       {
+               int idx = -1;
+               if (inCode != null && !inCode.equals("")) {
+                       for (int i=0; i<LANGUAGE_CODES.length; i++) {
+                               if (LANGUAGE_CODES[i].equalsIgnoreCase(inCode)) {
+                                       idx = i;
+                               }
+                       }
+               }
+               return idx;
+       }
+
+       /**
+        * Set the currently selected language in the Config
+        */
+       private void selectLanguage()
+       {
+               int index = _languageDropDown.getSelectedIndex();
+               // If index hasn't changed then don't dismiss dialog
+               if (index >= 0 && index != _startIndex)
+               {
+                       String code = LANGUAGE_CODES[index];
+                       // Set code and langfile in config
+                       Config.setConfigString(Config.KEY_LANGUAGE_CODE, code);
+                       Config.setConfigString(Config.KEY_LANGUAGE_FILE, null);
+                       _dialog.dispose();
+                       showEndMessage();
+               }
+       }
+
+       /**
+        * Select the currently selected language file to use for texts
+        */
+       private void selectLanguageFile()
+       {
+               final String oldPath = Config.getConfigString(Config.KEY_LANGUAGE_FILE);
+               String filename = _langFileBox.getText();
+               // Check there is an entry in the box
+               if (filename != null && !filename.equals(""))
+               {
+                       // Check the file exists and is readable
+                       File textsFile = new File(filename);
+                       if (!textsFile.exists() || !textsFile.canRead())
+                       {
+                               _app.showErrorMessage(getNameKey(), "error.load.noread");
+                       }
+                       else if (!languageFileLooksOk(textsFile))
+                       {
+                               _app.showErrorMessage(getNameKey(), "error.language.wrongfile");
+                       }
+                       else if (oldPath == null || !textsFile.getAbsolutePath().equalsIgnoreCase(oldPath))
+                       {
+                               // Set in Config
+                               Config.setConfigString(Config.KEY_LANGUAGE_FILE, textsFile.getAbsolutePath());
+                               _dialog.dispose();
+                               showEndMessage();
+                       }
+               }
+               else {
+                       // if file was previously selected, and now it's blank, then reset Config
+                       if (oldPath != null && oldPath.length() > 0)
+                       {
+                               Config.setConfigString(Config.KEY_LANGUAGE_FILE, null);
+                               _dialog.dispose();
+                               showEndMessage();
+                       }
+               }
+       }
+
+       /**
+        * Check the given file to see if it looks like a language file
+        * @param inFile File object to load
+        * @return true if file looks ok
+        */
+       private static boolean languageFileLooksOk(File inFile)
+       {
+               boolean ok = false;
+               boolean wrong = false;
+               BufferedReader reader = null;
+               try
+               {
+                       // Read through text file looking for lines with the right start
+                       reader = new BufferedReader(new FileReader(inFile));
+                       String currLine = reader.readLine();
+                       while (currLine != null && !ok && !wrong)
+                       {
+                               if (currLine.trim().length() > 0 && currLine.matches("[a-z.]+=.+")) {
+                                       ok = true;
+                               }
+                               if (currLine.indexOf('\0', 0) >= 0) {
+                                       wrong = true;
+                               }
+                               currLine = reader.readLine();
+                       }
+               }
+               catch (Exception e) {} // ignore exceptions, flag remains as set
+               finally {
+                       try {reader.close();} catch (Exception e2) {}
+               }
+               return ok && !wrong;
+       }
+
+       /**
+        * Function activated by the "Browse..." button to select a file
+        */
+       private void chooseFile()
+       {
+               JFileChooser chooser = new JFileChooser();
+               chooser.addChoosableFileFilter(
+                       new GenericFileFilter("filetype.txt", new String[] {"txt", "text"}));
+               chooser.setAcceptAllFileFilterUsed(true);
+               // Set start path from currently selected file
+               String currPath = _langFileBox.getText();
+               if (currPath != null && currPath.length() > 1) {
+                       chooser.setSelectedFile(new File(currPath));
+               }
+               if (chooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
+               {
+                       _langFileBox.setText(chooser.getSelectedFile().getAbsolutePath());
+               }
+       }
+
+       /**
+        * Show message to remind about saving settings and restarting
+        */
+       private void showEndMessage()
+       {
+               JOptionPane.showMessageDialog(_parentFrame,
+                       I18nManager.getText("dialog.setlanguage.endmessage"),
+                       I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+       }
+}
index a683634383d30767fe17edf7638dbd3b3624dff2..cadacf9962274010d0a4505c52bcc0f9fc739b3d 100644 (file)
@@ -18,11 +18,11 @@ import javax.swing.JRadioButton;
 import javax.swing.JTextField;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.DataSubscriber;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 
 /**
  * Function to set the tile server for the map backgrounds
index 65c3c411068e94cbf2e65fe70f16297887787ace..3ded22310994a6850a033423fb3d546f979db4d1 100644 (file)
@@ -15,10 +15,10 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.ExternalTools;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 
 /**
  * Function to set the paths for the external programs (eg gnuplot)
index 14d15776a8be3d19213cf0419ea240349146b9d6..8d264beb4f5e4098a7e5fd3caf3cb1de3995881c 100644 (file)
@@ -30,8 +30,8 @@ public abstract class UrlGenerator
        public static final int MAP_SOURCE_MAPQUEST = 2;
        /** Constant for Yahoo */
        public static final int MAP_SOURCE_YAHOO  = 3;
-
-       // TODO: Add other map sources, eg MSN, search.ch ?
+       /** Constant for Bing */
+       public static final int MAP_SOURCE_BING = 4;
 
        /**
         * Generate a URL for the given source and track info
@@ -50,6 +50,9 @@ public abstract class UrlGenerator
                else if (inSource == MAP_SOURCE_YAHOO) {
                        return generateYahooUrl(inTrackInfo);
                }
+               else if (inSource == MAP_SOURCE_BING) {
+                       return generateBingUrl(inTrackInfo);
+               }
                return generateOpenStreetMapUrl(inTrackInfo);
        }
 
@@ -82,7 +85,6 @@ public abstract class UrlGenerator
                                url = url + "(" + currPoint.getWaypointName() + ")";
                        }
                }
-               //System.out.println(url);
                return url;
        }
 
@@ -154,7 +156,30 @@ public abstract class UrlGenerator
                return url;
        }
 
-/**
+       /**
+        * Generate a url for Bing maps
+        * @param inTrackInfo track information
+        * @return URL
+        */
+       private static String generateBingUrl(TrackInfo inTrackInfo)
+       {
+               // Check if any data to display
+               if (inTrackInfo == null || inTrackInfo.getTrack() == null || inTrackInfo.getTrack().getNumPoints() < 1)
+               {
+                       return null;
+               }
+               double medianLat = getMedianValue(inTrackInfo.getTrack().getLatRange());
+               double medianLon = getMedianValue(inTrackInfo.getTrack().getLonRange());
+               // Build basic url with centre position
+               String latStr = FIVE_DP.format(medianLat);
+               String lonStr = FIVE_DP.format(medianLon);
+               String url = "http://bing.com/maps/default.aspx?cp=" + latStr + "~" + lonStr
+                       + "&where1=" + latStr + "%2C%20" + lonStr;
+               return url;
+       }
+
+
+       /**
         * Get the median value from the given lat/long range
         * @param inRange range of values
         * @return median value
index 01bf16c48d27a6f6b5b22bfc1ed4a080dcd9c55e..4b3fd370ee9c6866d6280236127c5a94881914b8 100644 (file)
@@ -25,10 +25,10 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.ExternalTools;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
index 62bf1f14a4802d41b40355b4a67250f0fc0cda95..35ff61da1ea3b73cd7618315d73af34647096a32 100644 (file)
@@ -72,7 +72,6 @@ public class CompressTrackFunction extends GenericFunction
         */
        private boolean[] preview()
        {
-               // System.out.println("track dialog preview");
                int numToDelete = 0;
                boolean[] deleteFlags = new boolean[_track.getNumPoints()];
                for (int i=0; i<_algorithms.length; i++)
index 8dadc3a12bc06cc2bf804631eb8e145b0c47c5e6..48ba1722be3bedf224ec28ed14fe362129d8fedf 100644 (file)
@@ -1,7 +1,7 @@
 package tim.prune.function.distance;
 
-import tim.prune.Config;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
 
index 6808e6a98837c4b692457a0b3940545c388a0481..9e02a1e69691d35dbbbeaff051f7f751f71258f3 100644 (file)
@@ -89,7 +89,7 @@ public class PointEditor
        private Component makeDialogComponents()
        {
                JPanel panel = new JPanel();
-               panel.setLayout(new BorderLayout());
+               panel.setLayout(new BorderLayout(1, 10));
                // Create GUI layout for point editor
                _table = new JTable(_model);
                _table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
index 493aedf0be642d4c181d407eaedb7a724729b23f..d1d83f34b053d03b7bfca5dc6755c815e9f416c7 100644 (file)
@@ -9,6 +9,7 @@ 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;
 
@@ -227,6 +228,7 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable
                ArrayList<GpsiesTrack> trackList = null;
                URL url = null;
                String descMessage = "";
+               InputStream inStream = null;
                // Loop for each page of the results
                do
                {
@@ -240,12 +242,16 @@ public class GetGpsiesFunction extends GenericFunction implements Runnable
                        {
                                url = new URL(urlString);
                                SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
-                               saxParser.parse(url.openStream(), xmlHandler);
+                               inStream = url.openStream();
+                               saxParser.parse(inStream, xmlHandler);
                        }
                        catch (Exception e) {
                                descMessage = e.getClass().getName() + " - " + e.getMessage();
                        }
-                       // TODO: Close streams somehow?  Haven't got a reference to the input stream to close it!
+                       // Close stream and ignore errors
+                       try {
+                               inStream.close();
+                       } catch (Exception e) {}
                        // Add track list to model
                        trackList = xmlHandler.getTrackList();
                        _trackListModel.addTracks(trackList);
index 275ae83ab715117841efc47dae90d0bf18160354..3bdeb162eb73372f32916dfcf05da4e82ff17f26 100644 (file)
@@ -5,8 +5,8 @@ import java.util.ArrayList;
 
 import javax.swing.table.AbstractTableModel;
 
-import tim.prune.Config;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Distance;
 
 /**
diff --git a/tim/prune/gui/ColourChooser.java b/tim/prune/gui/ColourChooser.java
new file mode 100644 (file)
index 0000000..93691ac
--- /dev/null
@@ -0,0 +1,151 @@
+package tim.prune.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import tim.prune.I18nManager;
+
+/**
+ * Class to offer a dialog to choose a colour.
+ * Normally a JColorChooser would be used, but this is too buggy
+ * in Java 1.6 and extremely prone to thread-locking, meaning
+ * that the application has to be killed (and all data lost).
+ */
+public class ColourChooser
+{
+       /** main dialog object */
+       private JDialog _dialog = null;
+       /** array of three slider objects for rgb */
+       private JSlider[] _rgbSliders = null;
+       /** array of labels for rgb values */
+       private JLabel[] _rgbLabels = null;
+       /** colour patch */
+       private ColourPatch _patch = null;
+       /** chosen colour */
+       private Color _chosenColour = null;
+
+
+       /**
+        * Constructor
+        * @param inParent parent dialog
+        */
+       public ColourChooser(JDialog inParent)
+       {
+               _dialog = new JDialog(inParent, I18nManager.getText("dialog.colourchooser.title"), true);
+               _dialog.setLocationRelativeTo(inParent);
+               _dialog.getContentPane().add(makeContents());
+               _dialog.pack();
+       }
+
+       /**
+        * Make the dialog contents
+        * @return JPanel containing dialog elements
+        */
+       private JPanel makeContents()
+       {
+               JPanel mainPanel = new JPanel();
+               mainPanel.setLayout(new BorderLayout());
+               _rgbSliders = new JSlider[3];
+               _rgbLabels = new JLabel[3];
+               _patch = new ColourPatch(Color.WHITE);
+               JPanel centrePanel = new JPanel();
+               centrePanel.setLayout(new BorderLayout());
+               centrePanel.add(_patch, BorderLayout.CENTER);
+
+               JPanel sliderPanel = new JPanel();
+               sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));
+               final String[] labelKeys = {"red", "green", "blue"};
+               for (int i=0; i<3; i++)
+               {
+                       String key = I18nManager.getText("dialog.colourchooser." + labelKeys[i]);
+                       sliderPanel.add(new JLabel(key));
+                       _rgbSliders[i] = new JSlider(0, 255);
+                       _rgbSliders[i].addChangeListener(new ChangeListener() {
+                               public void stateChanged(ChangeEvent arg0) {
+                                       updatePatch();
+                               }
+                       });
+                       _rgbSliders[i].setToolTipText(key);
+                       JPanel sliderHolder = new JPanel();
+                       sliderHolder.setLayout(new BorderLayout(5, 0));
+                       sliderHolder.add(_rgbSliders[i], BorderLayout.CENTER);
+                       _rgbLabels[i] = new JLabel("255");
+                       _rgbLabels[i].setPreferredSize(new Dimension(40, 1));
+                       sliderHolder.add(_rgbLabels[i], BorderLayout.EAST);
+                       sliderPanel.add(sliderHolder);
+               }
+               centrePanel.add(sliderPanel, BorderLayout.SOUTH);
+               mainPanel.add(centrePanel, BorderLayout.CENTER);
+
+               // Button panel for ok, cancel
+               JPanel buttonPanel = new JPanel();
+               buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+               JButton okButton = new JButton(I18nManager.getText("button.ok"));
+               okButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _chosenColour = _patch.getBackground();
+                               _dialog.setVisible(false);
+                       }
+               });
+               buttonPanel.add(okButton);
+               JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+               cancelButton.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               _chosenColour = null;
+                               _dialog.setVisible(false);
+                       }
+               });
+               buttonPanel.add(cancelButton);
+               mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+               return mainPanel;
+       }
+
+       /**
+        * Show the dialog to choose a colour
+        * @param inStartColour current colour
+        */
+       public void showDialog(Color inStartColour)
+       {
+               // Initialise sliders
+               _rgbSliders[0].setValue(inStartColour.getRed());
+               _rgbSliders[1].setValue(inStartColour.getGreen());
+               _rgbSliders[2].setValue(inStartColour.getBlue());
+               updatePatch();
+               _dialog.setLocationRelativeTo(_dialog.getParent());
+               _dialog.setVisible(true);
+       }
+
+       /**
+        * Update the patch colour from the slider values
+        */
+       private void updatePatch()
+       {
+               for (int i=0; i<3; i++) {
+                       _rgbLabels[i].setText("" + _rgbSliders[i].getValue());
+               }
+               final Color colour = new Color(_rgbSliders[0].getValue(),
+                       _rgbSliders[1].getValue(),_rgbSliders[2].getValue());
+               _patch.setColour(colour);
+       }
+
+       /**
+        * @return the selected colour, or null if cancel pressed
+        */
+       public Color getChosenColour()
+       {
+               return _chosenColour;
+       }
+}
diff --git a/tim/prune/gui/ColourPatch.java b/tim/prune/gui/ColourPatch.java
new file mode 100644 (file)
index 0000000..d5b9fd2
--- /dev/null
@@ -0,0 +1,34 @@
+package tim.prune.gui;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import javax.swing.JPanel;
+
+import tim.prune.config.ColourUtils;
+
+/**
+ * Class to act as a colour patch to illustrate a chosen colour
+ */
+public class ColourPatch extends JPanel
+{
+       /**
+        * Constructor
+        */
+       public ColourPatch(Color inColour)
+       {
+               Dimension size = new Dimension(80, 50);
+               setMinimumSize(size);
+               setPreferredSize(size);
+               setColour(inColour);
+       }
+
+       /**
+        * Set the colour of the patch
+        * @param inColour Color to use
+        */
+       public void setColour(Color inColour)
+       {
+               super.setBackground(inColour);
+               setToolTipText(ColourUtils.makeHexCode(inColour));
+       }
+}
index c40f8c2b4891360144bfe60d55dfd4817fd9a456..5e31a4e97aee5086ba60d184bc69e6e6d276d0be 100644 (file)
@@ -4,6 +4,7 @@ import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
 import java.awt.Font;
+import java.awt.Insets;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.text.NumberFormat;
@@ -11,18 +12,22 @@ import java.text.NumberFormat;
 import javax.swing.BorderFactory;
 import javax.swing.Box;
 import javax.swing.BoxLayout;
+import javax.swing.JButton;
 import javax.swing.JComboBox;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.border.EtchedBorder;
 
-import tim.prune.Config;
 import tim.prune.DataSubscriber;
+import tim.prune.FunctionLibrary;
+import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
+import tim.prune.data.Field;
 import tim.prune.data.IntegerRange;
 import tim.prune.data.Photo;
 import tim.prune.data.Selection;
@@ -37,22 +42,23 @@ public class DetailsDisplay extends GenericDisplay
        // Point details
        private JLabel _indexLabel = null;
        private JLabel _latLabel = null, _longLabel = null;
-       private JLabel _altLabel = null, _nameLabel = null;
+       private JLabel _altLabel = null;
        private JLabel _timeLabel = null, _speedLabel = null;
+       private JLabel _nameLabel = null, _typeLabel = null;
 
        // Range details
        private JLabel _rangeLabel = null;
-       private JLabel _distanceLabel = null, _movingDistanceLabel = null;
+       private JLabel _distanceLabel = null;
        private JLabel _durationLabel = null;
        private JLabel _altRangeLabel = null, _updownLabel = null;
-       private JLabel _aveSpeedLabel = null, _aveMovingSpeedLabel = null;
-       private JLabel _paceLabel = null;
+       private JLabel _aveSpeedLabel = null;
 
        // Photo details
        private JLabel _photoLabel = null;
        private PhotoThumbnail _photoThumbnail = null;
        private JLabel _photoTimestampLabel = null;
        private JLabel _photoConnectedLabel = null;
+       private JPanel _rotationButtons = null;
 
        // Units
        private JComboBox _coordFormatDropdown = null;
@@ -67,10 +73,10 @@ public class DetailsDisplay extends GenericDisplay
        private static final String LABEL_POINT_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
        private static final String LABEL_POINT_TIMESTAMP = I18nManager.getText("fieldname.timestamp") + ": ";
        private static final String LABEL_POINT_WAYPOINTNAME = I18nManager.getText("fieldname.waypointname") + ": ";
+       private static final String LABEL_POINT_WAYPOINTTYPE = I18nManager.getText("fieldname.waypointtype") + ": ";
        private static final String LABEL_RANGE_SELECTED = I18nManager.getText("details.range.selected") + ": ";
        private static final String LABEL_RANGE_DURATION = I18nManager.getText("fieldname.duration") + ": ";
        private static final String LABEL_RANGE_DISTANCE = I18nManager.getText("fieldname.distance") + ": ";
-       private static final String LABEL_RANGE_MOVINGDISTANCE = I18nManager.getText("fieldname.movingdistance") + ": ";
        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") + ": ";
@@ -116,6 +122,8 @@ public class DetailsDisplay extends GenericDisplay
                pointDetailsPanel.add(_speedLabel);
                _nameLabel = new JLabel("");
                pointDetailsPanel.add(_nameLabel);
+               _typeLabel = new JLabel("");
+               pointDetailsPanel.add(_typeLabel);
                pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
 
                // range details panel
@@ -131,16 +139,10 @@ public class DetailsDisplay extends GenericDisplay
                rangeDetailsPanel.add(_rangeLabel);
                _distanceLabel = new JLabel("");
                rangeDetailsPanel.add(_distanceLabel);
-               _movingDistanceLabel = new JLabel("");
-               rangeDetailsPanel.add(_movingDistanceLabel);
                _durationLabel = new JLabel("");
                rangeDetailsPanel.add(_durationLabel);
                _aveSpeedLabel = new JLabel("");
                rangeDetailsPanel.add(_aveSpeedLabel);
-               _aveMovingSpeedLabel = new JLabel("");
-               rangeDetailsPanel.add(_aveMovingSpeedLabel);
-               _paceLabel = new JLabel("");
-               rangeDetailsPanel.add(_paceLabel);
                _altRangeLabel = new JLabel("");
                rangeDetailsPanel.add(_altRangeLabel);
                _updownLabel = new JLabel("");
@@ -166,6 +168,15 @@ public class DetailsDisplay extends GenericDisplay
                _photoThumbnail.setVisible(false);
                _photoThumbnail.setPreferredSize(new Dimension(100, 100));
                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);
+               _rotationButtons = new JPanel();
+               _rotationButtons.add(rotLeft);
+               _rotationButtons.add(rotRight);
+               _rotationButtons.setAlignmentX(Component.LEFT_ALIGNMENT);
+               _rotationButtons.setVisible(false);
+               photoDetailsPanel.add(_rotationButtons);
 
                // add the details panels to the main panel
                mainPanel.add(pointDetailsPanel);
@@ -235,6 +246,7 @@ public class DetailsDisplay extends GenericDisplay
                        _altLabel.setText("");
                        _timeLabel.setText("");
                        _nameLabel.setText("");
+                       _typeLabel.setText("");
                }
                else
                {
@@ -271,12 +283,19 @@ public class DetailsDisplay extends GenericDisplay
                        else {
                                _timeLabel.setText("");
                        }
-                       String name = currentPoint.getWaypointName();
+                       // Waypoint name
+                       final String name = currentPoint.getWaypointName();
                        if (name != null && !name.equals(""))
                        {
                                _nameLabel.setText(LABEL_POINT_WAYPOINTNAME + name);
                        }
                        else _nameLabel.setText("");
+                       // Waypoint type
+                       final String type = currentPoint.getFieldValue(Field.WAYPT_TYPE);
+                       if (type != null && !type.equals("")) {
+                               _typeLabel.setText(LABEL_POINT_WAYPOINTTYPE + type);
+                       }
+                       else _typeLabel.setText("");
                }
 
                // Update range details
@@ -284,13 +303,10 @@ public class DetailsDisplay extends GenericDisplay
                {
                        _rangeLabel.setText(I18nManager.getText("details.norangeselection"));
                        _distanceLabel.setText("");
-                       _movingDistanceLabel.setText("");
                        _durationLabel.setText("");
                        _altRangeLabel.setText("");
                        _updownLabel.setText("");
                        _aveSpeedLabel.setText("");
-                       _aveMovingSpeedLabel.setText("");
-                       _paceLabel.setText("");
                }
                else
                {
@@ -298,38 +314,15 @@ public class DetailsDisplay extends GenericDisplay
                                + (selection.getStart()+1) + " " + I18nManager.getText("details.range.to")
                                + " " + (selection.getEnd()+1));
                        _distanceLabel.setText(LABEL_RANGE_DISTANCE + roundedNumber(selection.getDistance(distUnits)) + " " + distUnitsStr);
-                       if (selection.getHasMultipleSegments()) {
-                               _movingDistanceLabel.setText(LABEL_RANGE_MOVINGDISTANCE + roundedNumber(selection.getMovingDistance(distUnits)) + " " + distUnitsStr);
-                       }
-                       else {
-                               _movingDistanceLabel.setText(null);
-                       }
                        if (selection.getNumSeconds() > 0)
                        {
-                               _durationLabel.setText(LABEL_RANGE_DURATION + buildDurationString(selection.getNumSeconds()));
+                               _durationLabel.setText(LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(selection.getNumSeconds()));
                                _aveSpeedLabel.setText(I18nManager.getText("details.range.avespeed") + ": "
                                        + roundedNumber(selection.getDistance(distUnits)/selection.getNumSeconds()*3600.0) + " " + speedUnitsStr);
-                               if (selection.getHasMultipleSegments()) {
-                                       _aveMovingSpeedLabel.setText(I18nManager.getText("details.range.avemovingspeed") + ": "
-                                               + roundedNumber(selection.getMovingDistance(distUnits)/selection.getMovingSeconds()*3600.0) + " " + speedUnitsStr);
-                               }
-                               else {
-                                       _aveMovingSpeedLabel.setText(null);
-                               }
-                               if (Config.getConfigBoolean(Config.KEY_SHOW_PACE)) {
-                                       _paceLabel.setText(I18nManager.getText("details.range.pace") + ": "
-                                               + buildDurationString((long) (selection.getNumSeconds()/selection.getDistance(distUnits)))
-                                               + " / " + distUnitsStr);
-                               }
-                               else {
-                                       _paceLabel.setText("");
-                               }
                        }
                        else {
                                _durationLabel.setText("");
                                _aveSpeedLabel.setText("");
-                               _aveMovingSpeedLabel.setText("");
-                               _paceLabel.setText("");
                        }
                        String altUnitsLabel = getAltitudeUnitsLabel(selection.getAltitudeFormat());
                        IntegerRange altRange = selection.getAltitudeRange();
@@ -357,6 +350,7 @@ public class DetailsDisplay extends GenericDisplay
                        _photoTimestampLabel.setText("");
                        _photoConnectedLabel.setText("");
                        _photoThumbnail.setVisible(false);
+                       _rotationButtons.setVisible(false);
                }
                else
                {
@@ -368,6 +362,8 @@ public class DetailsDisplay extends GenericDisplay
                                        I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
                        _photoThumbnail.setVisible(true);
                        _photoThumbnail.setPhoto(currentPhoto);
+                       _rotationButtons.setVisible(true);
+                       if ((inUpdateType & DataSubscriber.PHOTOS_MODIFIED) > 0) {_photoThumbnail.refresh();}
                }
                _photoThumbnail.repaint();
        }
@@ -413,26 +409,6 @@ public class DetailsDisplay extends GenericDisplay
        }
 
 
-       /**
-        * Build a String to describe a time duration
-        * @param inNumSecs number of seconds
-        * @return time as a string, days, hours, mins, secs as appropriate
-        */
-       private static String buildDurationString(long inNumSecs)
-       {
-               if (inNumSecs <= 0L) return "";
-               if (inNumSecs < 60L) return "" + inNumSecs + I18nManager.getText("display.range.time.secs");
-               if (inNumSecs < 3600L) return "" + (inNumSecs / 60) + I18nManager.getText("display.range.time.mins")
-                       + " " + (inNumSecs % 60) + I18nManager.getText("display.range.time.secs");
-               if (inNumSecs < 86400L) return "" + (inNumSecs / 60 / 60) + I18nManager.getText("display.range.time.hours")
-                       + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
-               if (inNumSecs < 432000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days")
-                       + " " + (inNumSecs / 60 / 60) % 24 + I18nManager.getText("display.range.time.hours");
-               if (inNumSecs < 8640000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days");
-               return "big";
-       }
-
-
        /**
         * Format a number to a sensible precision
         * @param inDist distance
@@ -453,4 +429,19 @@ public class DetailsDisplay extends GenericDisplay
                _distanceFormatter.setMinimumFractionDigits(numDigits);
                return _distanceFormatter.format(inDist);
        }
+
+       /**
+        * Create a little button for rotating the current photo
+        * @param inIcon icon to use (from IconManager)
+        * @param inFunction function to call (from FunctionLibrary)
+        * @return button object
+        */
+       private static JButton makeRotateButton(String inIcon, GenericFunction inFunction)
+       {
+               JButton button = new JButton(IconManager.getImageIcon(inIcon));
+               button.setToolTipText(I18nManager.getText(inFunction.getNameKey()));
+               button.setMargin(new Insets(0, 2, 0, 2));
+               button.addActionListener(new FunctionLauncher(inFunction));
+               return button;
+       }
 }
diff --git a/tim/prune/gui/DisplayUtils.java b/tim/prune/gui/DisplayUtils.java
new file mode 100644 (file)
index 0000000..f34afd7
--- /dev/null
@@ -0,0 +1,28 @@
+package tim.prune.gui;
+
+import tim.prune.I18nManager;
+
+/**
+ * Class to provide general display util methods
+ */
+public abstract class DisplayUtils
+{
+       /**
+        * Build a String to describe a time duration
+        * @param inNumSecs number of seconds
+        * @return time as a string, days, hours, mins, secs as appropriate
+        */
+       public static String buildDurationString(long inNumSecs)
+       {
+               if (inNumSecs <= 0L) return "";
+               if (inNumSecs < 60L) return "" + inNumSecs + I18nManager.getText("display.range.time.secs");
+               if (inNumSecs < 3600L) return "" + (inNumSecs / 60) + I18nManager.getText("display.range.time.mins")
+                       + " " + (inNumSecs % 60) + I18nManager.getText("display.range.time.secs");
+               if (inNumSecs < 86400L) return "" + (inNumSecs / 60 / 60) + I18nManager.getText("display.range.time.hours")
+                       + " " + ((inNumSecs / 60) % 60) + I18nManager.getText("display.range.time.mins");
+               if (inNumSecs < 432000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days")
+                       + " " + (inNumSecs / 60 / 60) % 24 + I18nManager.getText("display.range.time.hours");
+               if (inNumSecs < 8640000L) return "" + (inNumSecs / 86400L) + I18nManager.getText("display.range.time.days");
+               return "big";
+       }
+}
index 731b91c47221b26959751d8e39a3db1f47d3d8d2..ae2feacb81fc5e1728b9230093cfd8767f074bda 100644 (file)
@@ -7,6 +7,8 @@ import java.awt.event.MouseEvent;
 import java.awt.event.MouseListener;
 
 import tim.prune.I18nManager;
+import tim.prune.config.ColourScheme;
+import tim.prune.config.Config;
 import tim.prune.data.TrackInfo;
 
 
@@ -19,9 +21,6 @@ public abstract class GenericChart extends GenericDisplay implements MouseListen
        protected static final int BORDER_WIDTH = 8;
 
        // Colours
-       private static final Color COLOR_BORDER_BG   = Color.WHITE;
-       private static final Color COLOR_CHART_BG    = Color.WHITE;
-       private static final Color COLOR_CHART_LINE  = Color.BLACK;
        private static final Color COLOR_NODATA_TEXT = Color.GRAY;
 
 
@@ -51,12 +50,17 @@ public abstract class GenericChart extends GenericDisplay implements MouseListen
                super.paint(inG);
                int width = getWidth();
                int height = getHeight();
+               // Get colours
+               ColourScheme colourScheme = Config.getColourScheme();
+               final Color borderColour = colourScheme.getColour(ColourScheme.IDX_BORDERS);
+               final Color backgroundColour = colourScheme.getColour(ColourScheme.IDX_BACKGROUND);
+               final Color insideColour = backgroundColour;
                // border background
-               inG.setColor(COLOR_BORDER_BG);
+               inG.setColor(backgroundColour);
                inG.fillRect(0, 0, width, height);
                if (width < 2*BORDER_WIDTH || height < 2*BORDER_WIDTH) return;
                // blank graph area, with line border
-               inG.setColor(COLOR_CHART_BG);
+               inG.setColor(insideColour);
                inG.fillRect(BORDER_WIDTH, BORDER_WIDTH, width - 2*BORDER_WIDTH, height-2*BORDER_WIDTH);
                // Display message if no data to be displayed
                if (_track == null || _track.getNumPoints() <= 0)
@@ -65,7 +69,7 @@ public abstract class GenericChart extends GenericDisplay implements MouseListen
                        inG.drawString(I18nManager.getText("display.nodata"), 50, height/2);
                }
                else {
-                       inG.setColor(COLOR_CHART_LINE);
+                       inG.setColor(borderColour);
                        inG.drawRect(BORDER_WIDTH, BORDER_WIDTH, width - 2*BORDER_WIDTH, height-2*BORDER_WIDTH);
                }
        }
diff --git a/tim/prune/gui/GuiGridLayout.java b/tim/prune/gui/GuiGridLayout.java
new file mode 100644 (file)
index 0000000..cb3572b
--- /dev/null
@@ -0,0 +1,61 @@
+package tim.prune.gui;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+/**
+ * Class to make it easier to use GridBagLayout
+ * for a two-column, non-equal-width layout
+ */
+public class GuiGridLayout
+{
+       private GridBagLayout _layout = null;
+       private GridBagConstraints _constraints = null;
+       private JPanel _panel = null;
+       private int _x = 0;
+       private int _y = 0;
+
+       /**
+        * Constructor
+        * @param inPanel panel using layout
+        */
+       public GuiGridLayout(JPanel inPanel)
+       {
+               _panel = inPanel;
+               _layout = new GridBagLayout();
+               _constraints = new GridBagConstraints();
+               _constraints.weightx = 1.0;
+               _constraints.weighty = 0.0;
+               _constraints.ipadx = 10;
+               _constraints.ipady = 1;
+               _constraints.insets = new Insets(1, 5, 1, 5);
+               // Apply layout to panel
+               _panel.setLayout(_layout);
+       }
+
+       /**
+        * Add the given component to the grid
+        * @param inComponent component to add
+        */
+       public void add(JComponent inComponent)
+       {
+               _constraints.gridx = _x;
+               _constraints.gridy = _y;
+               _constraints.weightx = (_x==0?0.5:1.0);
+               // set anchor
+               _constraints.anchor = (_x == 0?GridBagConstraints.LINE_END:GridBagConstraints.LINE_START);
+               _layout.setConstraints(inComponent, _constraints);
+               // add to panel
+               _panel.add(inComponent);
+               // work out next position
+               _x++;
+               if (_x > 1) {
+                       _x = 0;
+                       _y++;
+               }
+       }
+}
index 85a182a87cf23022de0114718d1c9b2f08c17b83..a42d759a8dc8f123d090cfa105ba94ba7902a9bf 100644 (file)
@@ -56,6 +56,10 @@ public abstract class IconManager
        /** Icon for cut range and move */
        public static final String CUT_AND_MOVE = "cut_and_move.gif";
 
+       /** Icon for rotating photos leftwards */
+       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";
 
        /**
         * Get the specified image
index 4b46959f55c575d46fe0670851925fd91580cdf6..3fa7a1a8fdb2ca822f4a7e226d7459f3b80eca5c 100644 (file)
@@ -2,6 +2,7 @@ package tim.prune.gui;
 
 import java.awt.Dimension;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.Image;
 import java.awt.image.BufferedImage;
 import java.awt.image.ConvolveOp;
@@ -69,11 +70,7 @@ public abstract class ImageUtils
         */
        public static Dimension getThumbnailSize(int inOrigWidth, int inOrigHeight, int inMaxWidth, int inMaxHeight)
        {
-               if (inMaxWidth <= 0 || inMaxHeight <= 0)
-               {
-                       //System.out.println("Can't do it - maxwidth=" + inMaxWidth + ", maxheight=" + inMaxHeight);
-                       return new Dimension(0,0);
-               }
+               assert(inMaxWidth > 0 && inMaxHeight > 0);
                // work out maximum zoom ratio available so that thumbnail isn't too big
                double xZoom = inMaxWidth * 1.0 / inOrigWidth;
                double yZoom = inMaxHeight * 1.0 / inOrigHeight;
@@ -83,4 +80,53 @@ public abstract class ImageUtils
                // calculate new width and height
                return new Dimension ((int) (zoom * inOrigWidth), (int) (zoom * inOrigHeight));
        }
+
+
+       /**
+        * Create a new image by rotating and scaling the given one
+        * @param inImage input image
+        * @param inMaxWidth maximum width of output image
+        * @param inMaxHeight maximum height of output image
+        * @param inRotationDegrees number of degrees to rotate clockwise (0, 90, 180 or 270)
+        * @return rotated, scaled image
+        */
+       public static BufferedImage rotateImage(Image inImage, int inMaxWidth, int inMaxHeight, int inRotationDegrees)
+       {
+               // Create scaled image of suitable size
+               boolean isRotated = (inRotationDegrees % 180 != 0);
+               int origWidth = inImage.getWidth(null);
+               int origHeight = inImage.getHeight(null);
+               int thumbWidth = isRotated?origHeight:origWidth;
+               int thumbHeight = isRotated?origWidth:origHeight;
+               Dimension scaledSize = getThumbnailSize(thumbWidth, thumbHeight, inMaxWidth, inMaxHeight);
+               BufferedImage result = new BufferedImage(scaledSize.width, scaledSize.height, BufferedImage.TYPE_INT_RGB);
+               // Do different things according to rotation angle (a bit messy, sorry!)
+               if (inRotationDegrees == 0)
+               {
+                       // Not rotated, so just copy image directly
+                       result.getGraphics().drawImage(inImage, 0, 0, scaledSize.width, scaledSize.height, null);
+               }
+               else
+               {
+                       // Need to use Graphics2D for rotation, not Graphics
+                       Graphics2D g2d = result.createGraphics();
+                       switch (inRotationDegrees)
+                       {
+                               case 90:
+                                       g2d.rotate(Math.PI / 2, 0.0, 0.0);
+                                       g2d.drawImage(inImage, 0, -scaledSize.width, scaledSize.height, scaledSize.width, null);
+                                       break;
+                               case 180:
+                                       g2d.rotate(Math.PI, scaledSize.width/2.0, scaledSize.height/2.0);
+                                       g2d.drawImage(inImage, 0, 0, scaledSize.width, scaledSize.height, null);
+                                       break;
+                               case 270:
+                                       g2d.rotate(Math.PI * 3/2, 0.0, 0.0);
+                                       g2d.drawImage(inImage, -scaledSize.height, 0, scaledSize.height, scaledSize.width, null);
+                       }
+                       // Clear up memory
+                       g2d.dispose();
+               }
+               return result;
+       }
 }
index cbf25af7df581e91940ff1a64b76da3fbc487b83..998a533ce4a73f58c1ae43cb95a2e5af84a24b7a 100644 (file)
@@ -14,12 +14,13 @@ import javax.swing.JToolBar;
 import javax.swing.KeyStroke;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.DataSubscriber;
 import tim.prune.FunctionLibrary;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.Photo;
 import tim.prune.data.PhotoList;
 import tim.prune.data.Selection;
 import tim.prune.data.Track;
@@ -59,23 +60,30 @@ public class MenuManager implements DataSubscriber
        private JMenuItem _selectStartItem = null;
        private JMenuItem _selectEndItem = null;
        private JMenuItem _findWaypointItem = null;
+       private JMenuItem _duplicatePointItem = null;
        private JMenuItem _reverseItem = null;
        private JMenuItem _addTimeOffsetItem = null;
        private JMenuItem _addAltitudeOffsetItem = null;
        private JMenuItem _mergeSegmentsItem = null;
        private JMenu     _rearrangeMenu = null;
        private JMenuItem _cutAndMoveItem = null;
+       private JMenuItem _convertNamesToTimesItem = null;
+       private JCheckBoxMenuItem _mapCheckbox = null;
        private JMenuItem _show3dItem = null;
        private JMenu     _browserMapMenu = null;
        private JMenuItem _chartItem = null;
-       private JCheckBoxMenuItem _paceCheckbox = null;
        private JMenuItem _getGpsiesItem = null;
        private JMenuItem _distanceItem = null;
+       private JMenuItem _fullRangeDetailsItem = null;
        private JMenuItem _saveExifItem = null;
        private JMenuItem _connectPhotoItem = null;
        private JMenuItem _deletePhotoItem = 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;
 
        // ActionListeners for reuse by menu and toolbar
        private ActionListener _openFileAction = null;
@@ -196,9 +204,9 @@ public class MenuManager implements DataSubscriber
                });
                fileMenu.add(exitMenuItem);
                menubar.add(fileMenu);
-               // Edit menu
-               JMenu editMenu = new JMenu(I18nManager.getText("menu.edit"));
-               setAltKey(editMenu, "altkey.menu.edit");
+               // Track menu
+               JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
+               setAltKey(trackMenu, "altkey.menu.track");
                _undoItem = new JMenuItem(I18nManager.getText("menu.edit.undo"));
                setShortcut(_undoItem, "shortcut.menu.edit.undo");
                _undoAction = new ActionListener() {
@@ -209,7 +217,7 @@ public class MenuManager implements DataSubscriber
                };
                _undoItem.addActionListener(_undoAction);
                _undoItem.setEnabled(false);
-               editMenu.add(_undoItem);
+               trackMenu.add(_undoItem);
                _clearUndoItem = new JMenuItem(I18nManager.getText("menu.edit.clearundo"));
                _clearUndoItem.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
@@ -218,46 +226,12 @@ public class MenuManager implements DataSubscriber
                        }
                });
                _clearUndoItem.setEnabled(false);
-               editMenu.add(_clearUndoItem);
-               editMenu.addSeparator();
-               _editPointItem = new JMenuItem(I18nManager.getText("menu.edit.editpoint"));
-               _editPointAction = new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.editCurrentPoint();
-                       }
-               };
-               _editPointItem.addActionListener(_editPointAction);
-               _editPointItem.setEnabled(false);
-               editMenu.add(_editPointItem);
-               _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME);
-               _editWaypointNameItem.setEnabled(false);
-               editMenu.add(_editWaypointNameItem);
-               _deletePointItem = new JMenuItem(I18nManager.getText("menu.edit.deletepoint"));
-               _deletePointAction = new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.deleteCurrentPoint();
-                       }
-               };
-               _deletePointItem.addActionListener(_deletePointAction);
-               _deletePointItem.setEnabled(false);
-               editMenu.add(_deletePointItem);
-               _deleteRangeItem = new JMenuItem(I18nManager.getText("menu.edit.deleterange"));
-               _deleteRangeAction = new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.deleteSelectedRange();
-                       }
-               };
-               _deleteRangeItem.addActionListener(_deleteRangeAction);
-               _deleteRangeItem.setEnabled(false);
-               editMenu.add(_deleteRangeItem);
-               editMenu.addSeparator();
+               trackMenu.add(_clearUndoItem);
+               trackMenu.addSeparator();
                _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS);
                setShortcut(_compressItem, "shortcut.menu.edit.compress");
                _compressItem.setEnabled(false);
-               editMenu.add(_compressItem);
+               trackMenu.add(_compressItem);
                _deleteMarkedPointsItem = new JMenuItem(I18nManager.getText("menu.edit.deletemarked"));
                _deleteMarkedPointsItem.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
@@ -266,50 +240,8 @@ public class MenuManager implements DataSubscriber
                        }
                });
                _deleteMarkedPointsItem.setEnabled(false);
-               editMenu.add(_deleteMarkedPointsItem);
-               editMenu.addSeparator();
-               _interpolateItem = new JMenuItem(I18nManager.getText("menu.edit.interpolate"));
-               _interpolateItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.interpolateSelection();
-                       }
-               });
-               _interpolateItem.setEnabled(false);
-               editMenu.add(_interpolateItem);
-               _averageItem = new JMenuItem(I18nManager.getText("menu.edit.average"));
-               _averageItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.averageSelection();
-                       }
-               });
-               _averageItem.setEnabled(false);
-               editMenu.add(_averageItem);
-               _reverseItem = new JMenuItem(I18nManager.getText("menu.edit.reverse"));
-               _reverseItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.reverseRange();
-                       }
-               });
-               _reverseItem.setEnabled(false);
-               editMenu.add(_reverseItem);
-               _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET);
-               _addTimeOffsetItem.setEnabled(false);
-               editMenu.add(_addTimeOffsetItem);
-               _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET);
-               _addAltitudeOffsetItem.setEnabled(false);
-               editMenu.add(_addAltitudeOffsetItem);
-               _mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.edit.mergetracksegments"));
-               _mergeSegmentsItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.mergeTrackSegments();
-                       }
-               });
-               _mergeSegmentsItem.setEnabled(false);
-               editMenu.add(_mergeSegmentsItem);
+               trackMenu.add(_deleteMarkedPointsItem);
+               trackMenu.addSeparator();
                // Rearrange waypoints
                _rearrangeMenu = new JMenu(I18nManager.getText("menu.edit.rearrange"));
                _rearrangeMenu.setEnabled(false);
@@ -340,21 +272,16 @@ public class MenuManager implements DataSubscriber
                });
                rearrangeNearestItem.setEnabled(true);
                _rearrangeMenu.add(rearrangeNearestItem);
-               editMenu.add(_rearrangeMenu);
-               _cutAndMoveItem = new JMenuItem(I18nManager.getText("menu.edit.cutandmove"));
-               _cutAndMoveItem.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e)
-                       {
-                               _app.cutAndMoveSelection();
-                       }
-               });
-               _cutAndMoveItem.setEnabled(false);
-               editMenu.add(_cutAndMoveItem);
-               menubar.add(editMenu);
+               trackMenu.add(_rearrangeMenu);
+               // Get gpsies tracks
+               _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES);
+               _getGpsiesItem.setEnabled(false);
+               trackMenu.add(_getGpsiesItem);
+               menubar.add(trackMenu);
 
-               // Select menu
-               JMenu selectMenu = new JMenu(I18nManager.getText("menu.select"));
-               setAltKey(selectMenu, "altkey.menu.select");
+               // Range menu
+               JMenu rangeMenu = new JMenu(I18nManager.getText("menu.range"));
+               setAltKey(rangeMenu, "altkey.menu.range");
                _selectAllItem = new JMenuItem(I18nManager.getText("menu.select.all"));
                setShortcut(_selectAllItem, "shortcut.menu.select.all");
                _selectAllItem.setEnabled(false);
@@ -364,7 +291,7 @@ public class MenuManager implements DataSubscriber
                                _selection.selectRange(0, _track.getNumPoints()-1);
                        }
                });
-               selectMenu.add(_selectAllItem);
+               rangeMenu.add(_selectAllItem);
                _selectNoneItem = new JMenuItem(I18nManager.getText("menu.select.none"));
                _selectNoneItem.setEnabled(false);
                _selectNoneItem.addActionListener(new ActionListener() {
@@ -373,8 +300,8 @@ public class MenuManager implements DataSubscriber
                                _app.selectNone();
                        }
                });
-               selectMenu.add(_selectNoneItem);
-               selectMenu.addSeparator();
+               rangeMenu.add(_selectNoneItem);
+               rangeMenu.addSeparator();
                _selectStartItem = new JMenuItem(I18nManager.getText("menu.select.start"));
                _selectStartItem.setEnabled(false);
                _selectStartAction = new ActionListener() {
@@ -384,7 +311,7 @@ public class MenuManager implements DataSubscriber
                        }
                };
                _selectStartItem.addActionListener(_selectStartAction);
-               selectMenu.add(_selectStartItem);
+               rangeMenu.add(_selectStartItem);
                _selectEndItem = new JMenuItem(I18nManager.getText("menu.select.end"));
                _selectEndItem.setEnabled(false);
                _selectEndAction = new ActionListener() {
@@ -394,16 +321,128 @@ public class MenuManager implements DataSubscriber
                        }
                };
                _selectEndItem.addActionListener(_selectEndAction);
-               selectMenu.add(_selectEndItem);
-               selectMenu.addSeparator();
+               rangeMenu.add(_selectEndItem);
+               rangeMenu.addSeparator();
+               _deleteRangeItem = new JMenuItem(I18nManager.getText("menu.edit.deleterange"));
+               _deleteRangeAction = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.deleteSelectedRange();
+                       }
+               };
+               _deleteRangeItem.addActionListener(_deleteRangeAction);
+               _deleteRangeItem.setEnabled(false);
+               rangeMenu.add(_deleteRangeItem);
+               _reverseItem = new JMenuItem(I18nManager.getText("menu.edit.reverse"));
+               _reverseItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.reverseRange();
+                       }
+               });
+               _reverseItem.setEnabled(false);
+               rangeMenu.add(_reverseItem);
+               _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET);
+               _addTimeOffsetItem.setEnabled(false);
+               rangeMenu.add(_addTimeOffsetItem);
+               _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET);
+               _addAltitudeOffsetItem.setEnabled(false);
+               rangeMenu.add(_addAltitudeOffsetItem);
+               _mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.edit.mergetracksegments"));
+               _mergeSegmentsItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.mergeTrackSegments();
+                       }
+               });
+               _mergeSegmentsItem.setEnabled(false);
+               rangeMenu.add(_mergeSegmentsItem);
+               rangeMenu.addSeparator();
+               _interpolateItem = new JMenuItem(I18nManager.getText("menu.edit.interpolate"));
+               _interpolateItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.interpolateSelection();
+                       }
+               });
+               _interpolateItem.setEnabled(false);
+               rangeMenu.add(_interpolateItem);
+               _averageItem = new JMenuItem(I18nManager.getText("menu.edit.average"));
+               _averageItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.averageSelection();
+                       }
+               });
+               _averageItem.setEnabled(false);
+               rangeMenu.add(_averageItem);
+               _cutAndMoveItem = new JMenuItem(I18nManager.getText("menu.edit.cutandmove"));
+               _cutAndMoveItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.cutAndMoveSelection();
+                       }
+               });
+               _cutAndMoveItem.setEnabled(false);
+               rangeMenu.add(_cutAndMoveItem);
+               _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES);
+               _convertNamesToTimesItem.setEnabled(false);
+               rangeMenu.add(_convertNamesToTimesItem);
+               menubar.add(rangeMenu);
+
+               // Point menu
+               JMenu pointMenu = new JMenu(I18nManager.getText("menu.point"));
+               setAltKey(pointMenu, "altkey.menu.point");
+               _editPointItem = new JMenuItem(I18nManager.getText("menu.edit.editpoint"));
+               _editPointAction = new ActionListener() {
+                       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);
+               pointMenu.add(_editWaypointNameItem);
+               _deletePointItem = new JMenuItem(I18nManager.getText("menu.edit.deletepoint"));
+               _deletePointAction = new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.deleteCurrentPoint();
+                       }
+               };
+               _deletePointItem.addActionListener(_deletePointAction);
+               _deletePointItem.setEnabled(false);
+               pointMenu.add(_deletePointItem);
+               pointMenu.addSeparator();
+               // find a waypoint
                _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT);
                _findWaypointItem.setEnabled(false);
-               selectMenu.add(_findWaypointItem);
-               menubar.add(selectMenu);
+               pointMenu.add(_findWaypointItem);
+               // duplicate current point
+               _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT);
+               _duplicatePointItem.setEnabled(false);
+               pointMenu.add(_duplicatePointItem);
+               // paste coordinates function
+               JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES);
+               pointMenu.add(pasteCoordsItem);
+               menubar.add(pointMenu);
 
                // Add view menu
                JMenu viewMenu = new JMenu(I18nManager.getText("menu.view"));
                setAltKey(viewMenu, "altkey.menu.view");
+               // Turn map display on/off
+               _mapCheckbox = new JCheckBoxMenuItem(
+                       I18nManager.getText("menu.map.showmap"), false);
+               _mapCheckbox.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e) {
+                               Config.setConfigBoolean(Config.KEY_SHOW_MAP, _mapCheckbox.isSelected());
+                               UpdateMessageBroker.informSubscribers();
+                       }
+               });
+               viewMenu.add(_mapCheckbox);
                _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D);
                _show3dItem.setEnabled(false);
                viewMenu.add(_show3dItem);
@@ -442,6 +481,14 @@ public class MenuManager implements DataSubscriber
                        }
                });
                _browserMapMenu.add(yahooMapsItem);
+               JMenuItem bingMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.bing"));
+               bingMapsItem.addActionListener(new ActionListener() {
+                       public void actionPerformed(ActionEvent e)
+                       {
+                               _app.showExternalMap(UrlGenerator.MAP_SOURCE_BING);
+                       }
+               });
+               _browserMapMenu.add(bingMapsItem);
                viewMenu.add(_browserMapMenu);
                // Charts
                _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS);
@@ -451,10 +498,10 @@ public class MenuManager implements DataSubscriber
                _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES);
                _distanceItem.setEnabled(false);
                viewMenu.add(_distanceItem);
-               // Get gpsies tracks
-               _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES);
-               _getGpsiesItem.setEnabled(false);
-               viewMenu.add(_getGpsiesItem);
+               // full range details
+               _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS);
+               _fullRangeDetailsItem.setEnabled(false);
+               viewMenu.add(_fullRangeDetailsItem);
                menubar.add(viewMenu);
 
                // Add photo menu
@@ -502,11 +549,25 @@ public class MenuManager implements DataSubscriber
                });
                _deletePhotoItem.setEnabled(false);
                photoMenu.add(_deletePhotoItem);
+               // Rotate current photo
+               _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT);
+               _rotatePhotoLeft.setEnabled(false);
+               photoMenu.add(_rotatePhotoLeft);
+               _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT);
+               _rotatePhotoRight.setEnabled(false);
+               photoMenu.add(_rotatePhotoRight);
+               _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB);
+               _ignoreExifThumb.setEnabled(false);
+               photoMenu.add(_ignoreExifThumb);
                photoMenu.addSeparator();
                // correlate all photos
                _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS);
                _correlatePhotosItem.setEnabled(false);
                photoMenu.add(_correlatePhotosItem);
+               // rearrange photo points
+               _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS);
+               _rearrangePhotosItem.setEnabled(false);
+               photoMenu.add(_rearrangePhotosItem);
                menubar.add(photoMenu);
 
                // Settings menu
@@ -515,22 +576,18 @@ public class MenuManager implements DataSubscriber
                // Set the map background
                JMenuItem mapBgItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_MAP_BG);
                settingsMenu.add(mapBgItem);
-               // Turn pace display on/off
-               _paceCheckbox = new JCheckBoxMenuItem(
-                       I18nManager.getText("menu.settings.showpace"), false);
-               _paceCheckbox.addActionListener(new ActionListener() {
-                       public void actionPerformed(ActionEvent e) {
-                               Config.setConfigBoolean(Config.KEY_SHOW_PACE, _paceCheckbox.isSelected());
-                               UpdateMessageBroker.informSubscribers();
-                       }
-               });
-               settingsMenu.add(_paceCheckbox);
                // Set kmz image size
                JMenuItem setKmzImageSizeItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_KMZ_IMAGE_SIZE);
                settingsMenu.add(setKmzImageSizeItem);
                // Set program paths
                JMenuItem setPathsItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS);
                settingsMenu.add(setPathsItem);
+               // Set colours
+               JMenuItem setColoursItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS);
+               settingsMenu.add(setColoursItem);
+               // Set language
+               JMenuItem setLanguageItem = makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE);
+               settingsMenu.add(setLanguageItem);
                settingsMenu.addSeparator();
                // Save configuration
                JMenuItem saveConfigMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG);
@@ -729,20 +786,25 @@ public class MenuManager implements DataSubscriber
                _selectStartButton.setEnabled(hasPoint);
                _selectEndItem.setEnabled(hasPoint);
                _selectEndButton.setEnabled(hasPoint);
+               _duplicatePointItem.setEnabled(hasPoint);
                // are there any photos?
                boolean anyPhotos = _photos != null && _photos.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
-               boolean connectAvailable = hasPhoto && hasPoint && _photos.getPhoto(_selection.getCurrentPhotoIndex()) != null
-                       && _photos.getPhoto(_selection.getCurrentPhotoIndex()).getDataPoint() == null;
+               Photo currentPhoto = _photos.getPhoto(_selection.getCurrentPhotoIndex());
+               boolean connectAvailable = hasPhoto && hasPoint && currentPhoto != null
+                       && currentPhoto.getDataPoint() == null;
                _connectPhotoItem.setEnabled(connectAvailable);
                _connectPhotoButton.setEnabled(connectAvailable);
-               _disconnectPhotoItem.setEnabled(hasPhoto && _photos.getPhoto(_selection.getCurrentPhotoIndex()) != null
-                       && _photos.getPhoto(_selection.getCurrentPhotoIndex()).getDataPoint() != null);
+               _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getDataPoint() != null);
                _correlatePhotosItem.setEnabled(anyPhotos && hasData);
+               _rearrangePhotosItem.setEnabled(anyPhotos && hasData && _track.getNumPoints() > 1);
                _deletePhotoItem.setEnabled(hasPhoto);
+               _rotatePhotoLeft.setEnabled(hasPhoto);
+               _rotatePhotoRight.setEnabled(hasPhoto);
+               _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getExifThumbnail() != null);
                // is there a current range?
                boolean hasRange = (hasData && _selection.hasRangeSelected());
                _deleteRangeItem.setEnabled(hasRange);
@@ -754,10 +816,17 @@ public class MenuManager implements DataSubscriber
                _reverseItem.setEnabled(hasRange);
                _addTimeOffsetItem.setEnabled(hasRange);
                _addAltitudeOffsetItem.setEnabled(hasRange);
+               _convertNamesToTimesItem.setEnabled(hasRange && _track.hasWaypoints());
+               _fullRangeDetailsItem.setEnabled(hasRange);
                // Is the currently selected point outside the current range?
                _cutAndMoveItem.setEnabled(hasRange && hasPoint &&
                        (_selection.getCurrentPointIndex() < _selection.getStart()
                                || _selection.getCurrentPointIndex() > (_selection.getEnd()+1)));
+               // Has the map been switched on/off?
+               boolean mapsOn = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+               if (_mapCheckbox.isSelected() != mapsOn) {
+                       _mapCheckbox.setSelected(mapsOn);
+               }
        }
 
 
index 2ad22fa3a36e00be8979a2b23281490211e7618f..101f1a39c64aefcada6069c146765d4938ce3f34 100644 (file)
@@ -18,11 +18,9 @@ public class PhotoThumbnail extends JPanel implements Runnable
 {
        private Photo _photo = null;
        private BufferedImage _thumbnail = null;
-       private int _lastWidth = -1;
-       private int _lastHeight = -1;
        private boolean _loadingImage = false;
        /** String to show before photo is loaded */
-       private static final String _loadingString = I18nManager.getText("details.photo.loading") + " ...";
+       private static final String LOADING_STRING = I18nManager.getText("details.photo.loading") + " ...";
 
 
        /**
@@ -30,7 +28,6 @@ public class PhotoThumbnail extends JPanel implements Runnable
         */
        public PhotoThumbnail()
        {
-               // TODO: Make size of thumbnail dynamic, as big as it can be
                setOpaque(true);
        }
 
@@ -42,11 +39,19 @@ public class PhotoThumbnail extends JPanel implements Runnable
        public void setPhoto(Photo inPhoto)
        {
                // Check whether the photo has changed
-               if (_photo == inPhoto) {return;}
-               _photo = inPhoto;
-               _thumbnail = null;
+               if (_photo != inPhoto) {
+                       _photo = inPhoto;
+                       _thumbnail = null;
+               }
+               repaint();
        }
 
+       /**
+        * Force a refresh / reload
+        */
+       public void refresh() {
+               _thumbnail = null;
+       }
 
        /**
         * Override paint method
@@ -57,29 +62,34 @@ public class PhotoThumbnail extends JPanel implements Runnable
                super.paint(inG);
                if (_photo != null)
                {
-                       // recalculate thumbnail if photo has changed
-                       if (_thumbnail == null || getWidth() != _lastWidth || getHeight() != _lastHeight)
+                       // read thumbnail in separate thread
+                       if (_thumbnail == null && !_loadingImage)
                        {
-                               // initiate load if not already started
-                               if (!_loadingImage)
-                               {
-                                       _loadingImage = true;
-                                       new Thread(this).start();
-                               }
+                               _loadingImage = true;
+                               new Thread(this).start();
                        }
-                       // Set width and height
-                       _lastWidth = getWidth();
-                       _lastHeight = getHeight();
-                       // if loading, display image
+                       // if loading, display message
                        if (_loadingImage)
                        {
                                inG.setColor(Color.BLACK);
-                               inG.drawString(_loadingString, 10, 30);
+                               inG.drawString(LOADING_STRING, 10, 30);
                        }
                        else
                        {
-                               // Copy scaled, smoothed image onto the screen
-                               inG.drawImage(_thumbnail, 0, 0, _thumbnail.getWidth(), _thumbnail.getHeight(), null);
+                               // Copy scaled, smoothed (and rotated) image into scaled
+                               int usableWidth = getParent().getWidth()-10;
+                               Image scaled = ImageUtils.rotateImage(_thumbnail, usableWidth, usableWidth, _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())
+                               {
+                                       setPreferredSize(new Dimension(usableWidth, usableWidth));
+                                       invalidate();
+                               }
                        }
                }
        }
@@ -104,24 +114,15 @@ public class PhotoThumbnail extends JPanel implements Runnable
                        int picHeight = _photo.getHeight();
                        if (picWidth > -1 && picHeight > -1)
                        {
-                               int displayWidth = Math.min(getWidth(), getParent().getWidth());
-                               int displayHeight = Math.min(getHeight(), getParent().getHeight());
-
+                               // Just set a "reasonable" thumbnail size for now
+                               final int DEFAULT_THUMB_SIZE = 400;
                                // calculate maximum thumbnail size
-                               Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, displayWidth, displayHeight);
-                               // Work out if need to remake image
-                               boolean needToRemake = (_thumbnail == null)
-                                || _thumbnail.getWidth() != thumbSize.width || _thumbnail.getHeight() != thumbSize.height;
-                               if (thumbSize.width > 0 && thumbSize.height > 0 && needToRemake)
-                               {
-                                       // 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;
-                                       // TODO: Calculate and set size of thumbnail here
-                                       // setPreferredSize(new Dimension(200, 200));
-                               }
+                               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;
                        }
                }
                _loadingImage = false;
index f7ae105cfae85665f848287d37fc85ec0f59c7d1..7c7b4c513f09156dd14709c9ca5df346da871193 100644 (file)
@@ -6,6 +6,8 @@ import java.awt.Graphics;
 import java.awt.event.MouseEvent;
 
 import tim.prune.I18nManager;
+import tim.prune.config.ColourScheme;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.AltitudeRange;
 import tim.prune.data.Track;
@@ -16,14 +18,10 @@ import tim.prune.data.TrackInfo;
  */
 public class ProfileChart extends GenericChart
 {
+       /** Current scale factor in x direction*/
        private double _xScaleFactor = 0.0;
+       /** Possible altitude scales to use */
        private static final int[] ALTITUDE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50};
-       private static final Color COLOR_LINES       = Color.GRAY;
-       private static final Color COLOR_ALT_BARS    = Color.BLUE;
-       private static final Color COLOR_CURR_RANGE  = Color.GREEN;
-       private static final Color COLOR_SELECTED    = Color.RED;
-       private static final Color COLOR_SELECTED_BG = Color.ORANGE;
-       private static final Color COLOR_ALT_SCALE   = Color.RED;
 
 
        /**
@@ -50,10 +48,17 @@ public class ProfileChart extends GenericChart
                        int width = getWidth();
                        int height = getHeight();
 
+                       // Set up colours
+                       final Color barColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT);
+                       final Color rangeColour = Config.getColourScheme().getColour(ColourScheme.IDX_SELECTION);
+                       final Color currentColour = Config.getColourScheme().getColour(ColourScheme.IDX_PRIMARY);
+                       final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY);
+                       final Color lineColour = Config.getColourScheme().getColour(ColourScheme.IDX_LINES);
+
                        // message if no altitudes in track
                        if (!_track.hasAltitudeData())
                        {
-                               g.setColor(COLOR_LINES);
+                               g.setColor(lineColour);
                                g.drawString(I18nManager.getText("display.noaltitudes"), 50, height/2);
                                return;
                        }
@@ -63,7 +68,7 @@ public class ProfileChart extends GenericChart
                        int minAltitude = altitudeRange.getMinimum();
                        int maxAltitude = altitudeRange.getMaximum();
                        int numPoints = _track.getNumPoints();
-                       _xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH) / numPoints;
+                       _xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH - 1) / numPoints;
                        double yScaleFactor = 1.0 * (height - 2 * BORDER_WIDTH) / (maxAltitude - minAltitude);
                        int barWidth = (int) (_xScaleFactor + 1.0);
                        int selectedPoint = _trackInfo.getSelection().getCurrentPointIndex();
@@ -77,10 +82,10 @@ public class ProfileChart extends GenericChart
                        // horizontal lines for scale - set to round numbers eg 500m
                        int lineScale = getLineScale(minAltitude, maxAltitude);
                        int altitude = 0;
-                       int y = 0;
+                       int x = 0, y = 0;
                        if (lineScale > 1)
                        {
-                               g.setColor(COLOR_LINES);
+                               g.setColor(lineColour);
                                while (altitude < maxAltitude)
                                {
                                        if (altitude > minAltitude)
@@ -96,22 +101,14 @@ public class ProfileChart extends GenericChart
                        {
                                // loop through points
                                Altitude.Format chartFormat = altitudeRange.getFormat();
+                               g.setColor(barColour);
                                for (int p = 0; p < numPoints; p++)
                                {
-                                       int x = (int) (_xScaleFactor * p);
-                                       if (p == selectedPoint)
-                                       {
-                                               g.setColor(COLOR_SELECTED_BG);
-                                               g.fillRect(BORDER_WIDTH + x, BORDER_WIDTH+1, barWidth, height-2*BORDER_WIDTH-2);
-                                               g.setColor(COLOR_SELECTED);
-                                       }
-                                       else
-                                       {
-                                               g.setColor(COLOR_ALT_BARS);
-                                               if (p >= selectionStart && p <= selectionEnd) {
-                                                       g.setColor(COLOR_CURR_RANGE);
-                                               }
-                                       }
+                                       x = (int) (_xScaleFactor * p) + 1;
+                                       if (p == selectionStart)
+                                               g.setColor(rangeColour);
+                                       else if (p == (selectionEnd+1))
+                                               g.setColor(barColour);
                                        if (_track.getPoint(p).getAltitude().isValid())
                                        {
                                                altitude = _track.getPoint(p).getAltitude().getValue(chartFormat);
@@ -119,6 +116,21 @@ public class ProfileChart extends GenericChart
                                                g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
                                        }
                                }
+                               // current point (make sure it's drawn last)
+                               if (selectedPoint > -1)
+                               {
+                                       Altitude alt = _track.getPoint(selectedPoint).getAltitude();
+                                       if (alt.isValid())
+                                       {
+                                               x = (int) (_xScaleFactor * selectedPoint) + 1;
+                                               g.setColor(secondColour);
+                                               g.fillRect(BORDER_WIDTH + x, BORDER_WIDTH+1, barWidth, height-2*BORDER_WIDTH-2);
+                                               g.setColor(currentColour);
+                                               altitude = alt.getValue(chartFormat);
+                                               y = (int) (yScaleFactor * (altitude - minAltitude));
+                                               g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
+                                       }
+                               }
                        }
                        catch (NullPointerException npe) { // ignore, probably due to data being changed
                        }
@@ -128,7 +140,7 @@ public class ProfileChart extends GenericChart
                                int textHeight = g.getFontMetrics().getHeight();
                                altitude = 0;
                                y = 0;
-                               g.setColor(COLOR_ALT_SCALE);
+                               g.setColor(currentColour);
                                while (altitude < maxAltitude)
                                {
                                        if (altitude > minAltitude)
@@ -217,7 +229,13 @@ public class ProfileChart extends GenericChart
                        {
                                // work out which data point is nearest and select it
                                int pointNum = (int) ((e.getX() - BORDER_WIDTH) / _xScaleFactor);
-                               _trackInfo.selectPoint(pointNum);
+                               // If shift clicked, then extend selection
+                               if (e.isShiftDown()) {
+                                       _trackInfo.extendSelection(pointNum);
+                               }
+                               else {
+                                       _trackInfo.selectPoint(pointNum);
+                               }
                        }
                }
        }
index cbab59cbfa162446e4fb5c305adf4d7c8ec55e2e..e5b41865e739a0cb08dc95382fc82eb692472ffd 100644 (file)
@@ -38,6 +38,7 @@ public class WaypointListModel extends AbstractListModel
         */
        public Object getElementAt(int inIndex)
        {
+               if (inIndex < 0 || inIndex >= getSize()) return "";
                return _waypoints.get(inIndex).getWaypointName();
        }
 
diff --git a/tim/prune/gui/images/rotate_left_icon.png b/tim/prune/gui/images/rotate_left_icon.png
new file mode 100644 (file)
index 0000000..7bd3ae1
Binary files /dev/null and b/tim/prune/gui/images/rotate_left_icon.png differ
diff --git a/tim/prune/gui/images/rotate_right_icon.png b/tim/prune/gui/images/rotate_right_icon.png
new file mode 100644 (file)
index 0000000..cf2fd40
Binary files /dev/null and b/tim/prune/gui/images/rotate_right_icon.png differ
index 2af4ae8e6a9a53c4561b89c62614debb9e9b718e..d2f36ce165b45cfd5c315da68c6a70b5726a1f51 100644 (file)
@@ -37,7 +37,14 @@ import tim.prune.App;
 import tim.prune.DataSubscriber;
 import tim.prune.FunctionLibrary;
 import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.ColourScheme;
+import tim.prune.config.Config;
+import tim.prune.data.Coordinate;
+import tim.prune.data.DataPoint;
 import tim.prune.data.DoubleRange;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
 import tim.prune.data.Selection;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
@@ -114,14 +121,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
        private static final int AUTOPAN_DISTANCE = 75;
 
        // Colours
-       private static final Color COLOR_BG         = Color.WHITE;
        private static final Color COLOR_MESSAGES   = Color.GRAY;
-       private static final Color COLOR_POINT      = Color.BLUE;
-       private static final Color COLOR_POINT_DELETED = Color.RED;
-       private static final Color COLOR_CURR_RANGE = Color.GREEN;
-       private static final Color COLOR_CROSSHAIRS = Color.RED;
-       private static final Color COLOR_WAYPT_NAME = Color.BLACK;
-       private static final Color COLOR_PHOTO_PT   = Color.ORANGE;
 
 
        /**
@@ -155,7 +155,8 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        {
                                _tileCacher.clearAll();
                                _recalculate = true;
-                               repaint();
+                               Config.setConfigBoolean(Config.KEY_SHOW_MAP, e.getStateChange() == ItemEvent.SELECTED);
+                               UpdateMessageBroker.informSubscribers(); // to let menu know
                        }
                };
                _topPanel = new JPanel();
@@ -304,8 +305,10 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                newPointItem.addActionListener(new ActionListener() {
                        public void actionPerformed(ActionEvent e)
                        {
-                               _app.createPoint(MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_popupMenuY, getHeight())),
-                                       MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_popupMenuX, getWidth())));
+                               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));
                        }});
                newPointItem.setEnabled(true);
                _popup.add(newPointItem);
@@ -391,7 +394,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
                else
                {
-                       inG.setColor(COLOR_BG);
+                       inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND));
                        inG.fillRect(0, 0, getWidth(), getHeight());
                        inG.setColor(COLOR_MESSAGES);
                        inG.drawString(I18nManager.getText("display.nodata"), 50, getHeight()/2);
@@ -414,14 +417,18 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
 
                // Clear map
                Graphics g = _mapImage.getGraphics();
-               // Clear to white
-               g.setColor(COLOR_BG);
+               // Clear to background
+               g.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND));
                g.fillRect(0, 0, getWidth(), getHeight());
 
+               // Check whether maps are on or not
+               boolean showMap = Config.getConfigBoolean(Config.KEY_SHOW_MAP);
+               _mapCheckBox.setSelected(showMap);
+
                // reset error message
-               if (!_mapCheckBox.isSelected()) {_shownOsmErrorAlready = false;}
+               if (!showMap) {_shownOsmErrorAlready = false;}
                // Only get map tiles if selected
-               if (_mapCheckBox.isSelected())
+               if (showMap)
                {
                        // init tile cacher
                        _tileCacher.centreMap(_mapPosition.getZoom(), _mapPosition.getCentreTileX(), _mapPosition.getCentreTileY());
@@ -487,7 +494,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
                _checkBounds = false;
                // enable / disable transparency slider
-               _transparencySlider.setEnabled(_mapCheckBox.isSelected());
+               _transparencySlider.setEnabled(showMap);
        }
 
 
@@ -498,33 +505,44 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
         */
        private int paintPoints(Graphics inG)
        {
+               // Set up colours
+               final Color pointColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT);
+               final Color rangeColour = Config.getColourScheme().getColour(ColourScheme.IDX_SELECTION);
+               final Color currentColour = Config.getColourScheme().getColour(ColourScheme.IDX_PRIMARY);
+               final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY);
+               final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
+
                int pointsPainted = 0;
                // draw track points
-               inG.setColor(COLOR_POINT);
+               inG.setColor(pointColour);
                int prevX = -1, prevY = -1;
                boolean connectPoints = _connectCheckBox.isSelected();
                boolean prevPointVisible = false, currPointVisible = false;
+               boolean anyWaypoints = false;
+               boolean isWaypoint = false;
                for (int i=0; i<_track.getNumPoints(); i++)
                {
                        int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
                        int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
                        currPointVisible = px >= 0 && px < getWidth() && py >= 0 && py < getHeight();
+                       isWaypoint = _track.getPoint(i).isWaypoint();
+                       anyWaypoints = anyWaypoints || isWaypoint;
                        if (currPointVisible)
                        {
-                               if (!_track.getPoint(i).isWaypoint())
+                               if (!isWaypoint)
                                {
                                        // Draw rectangle for track point
                                        if (_track.getPoint(i).getDeleteFlag()) {
-                                               inG.setColor(COLOR_POINT_DELETED);
+                                               inG.setColor(currentColour);
                                        }
                                        else {
-                                               inG.setColor(COLOR_POINT);
+                                               inG.setColor(pointColour);
                                        }
                                        inG.drawRect(px-2, py-2, 3, 3);
                                        pointsPainted++;
                                }
                        }
-                       if (!_track.getPoint(i).isWaypoint())
+                       if (!isWaypoint)
                        {
                                // Connect track points if either of them are visible
                                if (connectPoints && (currPointVisible || prevPointVisible)
@@ -539,56 +557,58 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                }
 
                // Loop over points, just drawing blobs for waypoints
-               inG.setColor(COLOR_WAYPT_NAME);
+               inG.setColor(textColour);
                FontMetrics fm = inG.getFontMetrics();
                int nameHeight = fm.getHeight();
                int width = getWidth();
                int height = getHeight();
-               for (int i=0; i<_track.getNumPoints(); i++)
-               {
-                       if (_track.getPoint(i).isWaypoint())
+               if (anyWaypoints) {
+                       for (int i=0; i<_track.getNumPoints(); i++)
                        {
-                               int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                               int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                               if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                               if (_track.getPoint(i).isWaypoint())
                                {
-                                       inG.fillRect(px-3, py-3, 6, 6);
-                                       pointsPainted++;
+                                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
+                                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
+                                       if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                                       {
+                                               inG.fillRect(px-3, py-3, 6, 6);
+                                               pointsPainted++;
+                                       }
                                }
                        }
-               }
-               // Loop over points again, now draw names for waypoints
-               for (int i=0; i<_track.getNumPoints(); i++)
-               {
-                       if (_track.getPoint(i).isWaypoint())
+                       // Loop over points again, now draw names for waypoints
+                       for (int i=0; i<_track.getNumPoints(); i++)
                        {
-                               int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
-                               int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
-                               if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
+                               if (_track.getPoint(i).isWaypoint())
                                {
-                                       // Figure out where to draw waypoint name so it doesn't obscure track
-                                       String waypointName = _track.getPoint(i).getWaypointName();
-                                       int nameWidth = fm.stringWidth(waypointName);
-                                       boolean drawnName = false;
-                                       // Make arrays for coordinates right left up down
-                                       int[] nameXs = {px + 2, px - nameWidth - 2, px - nameWidth/2, px - nameWidth/2};
-                                       int[] nameYs = {py + (nameHeight/2), py + (nameHeight/2), py - 2, py + nameHeight + 2};
-                                       for (int extraSpace = 4; extraSpace < 13 && !drawnName; extraSpace+=2)
+                                       int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
+                                       int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
+                                       if (px >= 0 && px < getWidth() && py >= 0 && py < getHeight())
                                        {
-                                               // Shift arrays for coordinates right left up down
-                                               nameXs[0] += 2; nameXs[1] -= 2;
-                                               nameYs[2] -= 2; nameYs[3] += 2;
-                                               // Check each direction in turn right left up down
-                                               for (int a=0; a<4; a++)
+                                               // Figure out where to draw waypoint name so it doesn't obscure track
+                                               String waypointName = _track.getPoint(i).getWaypointName();
+                                               int nameWidth = fm.stringWidth(waypointName);
+                                               boolean drawnName = false;
+                                               // Make arrays for coordinates right left up down
+                                               int[] nameXs = {px + 2, px - nameWidth - 2, px - nameWidth/2, px - nameWidth/2};
+                                               int[] nameYs = {py + (nameHeight/2), py + (nameHeight/2), py - 2, py + nameHeight + 2};
+                                               for (int extraSpace = 4; extraSpace < 13 && !drawnName; extraSpace+=2)
                                                {
-                                                       if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < width
-                                                               && nameYs[a] < height && (nameYs[a] - nameHeight) > 0
-                                                               && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight))
+                                                       // Shift arrays for coordinates right left up down
+                                                       nameXs[0] += 2; nameXs[1] -= 2;
+                                                       nameYs[2] -= 2; nameYs[3] += 2;
+                                                       // Check each direction in turn right left up down
+                                                       for (int a=0; a<4; a++)
                                                        {
-                                                               // Found a rectangle to fit - draw name here and quit
-                                                               inG.drawString(waypointName, nameXs[a], nameYs[a]);
-                                                               drawnName = true;
-                                                               break;
+                                                               if (nameXs[a] > 0 && (nameXs[a] + nameWidth) < width
+                                                                       && nameYs[a] < height && (nameYs[a] - nameHeight) > 0
+                                                                       && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight, textColour))
+                                                               {
+                                                                       // Found a rectangle to fit - draw name here and quit
+                                                                       inG.drawString(waypointName, nameXs[a], nameYs[a]);
+                                                                       drawnName = true;
+                                                                       break;
+                                                               }
                                                        }
                                                }
                                        }
@@ -596,7 +616,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                        }
                }
                // Loop over points, drawing blobs for photo points
-               inG.setColor(COLOR_PHOTO_PT);
+               inG.setColor(secondColour);
                for (int i=0; i<_track.getNumPoints(); i++)
                {
                        if (_track.getPoint(i).getPhoto() != null)
@@ -615,7 +635,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                // Draw selected range
                if (_selection.hasRangeSelected())
                {
-                       inG.setColor(COLOR_CURR_RANGE);
+                       inG.setColor(rangeColour);
                        for (int i=_selection.getStart(); i<=_selection.getEnd(); i++)
                        {
                                int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
@@ -630,7 +650,7 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                {
                        int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(selectedPoint));
                        int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(selectedPoint));
-                       inG.setColor(COLOR_CROSSHAIRS);
+                       inG.setColor(currentColour);
                        // crosshairs
                        inG.drawLine(px, 0, px, getHeight());
                        inG.drawLine(0, py, getWidth(), py);
@@ -649,12 +669,17 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
         * @param inY bottom Y coordinate
         * @param inWidth width of rectangle
         * @param inHeight height of rectangle
-        * @return true if there's at least one data point in the rectangle
+        * @param inTextColour colour of text
+        * @return true if the rectangle overlaps stuff too close to the given colour
         */
-       private boolean overlapsPoints(int inX, int inY, int inWidth, int inHeight)
+       private boolean overlapsPoints(int inX, int inY, int inWidth, int inHeight, Color inTextColour)
        {
-               // each of the colour channels must be brighter than this to count as empty
-               final int BRIGHTNESS_LIMIT = 210;
+               // each of the colour channels must be further away than this to count as empty
+               final int BRIGHTNESS_LIMIT = 80;
+               final int textRGB = inTextColour.getRGB();
+               final int textLow = textRGB & 255;
+               final int textMid = (textRGB >> 8) & 255;
+               final int textHigh = (textRGB >> 16) & 255;
                try
                {
                        // loop over x coordinate of rectangle
@@ -665,11 +690,14 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                {
                                        int pixelColor = _mapImage.getRGB(inX + x, inY - y);
                                        // split into four components rgba
-                                       int lowestBit = pixelColor & 255;
-                                       int secondBit = (pixelColor >> 8) & 255;
-                                       int thirdBit = (pixelColor >> 16) & 255;
+                                       int pixLow = pixelColor & 255;
+                                       int pixMid = (pixelColor >> 8) & 255;
+                                       int pixHigh = (pixelColor >> 16) & 255;
                                        //int fourthBit = (pixelColor >> 24) & 255; // alpha ignored
-                                       if (lowestBit < BRIGHTNESS_LIMIT || secondBit < BRIGHTNESS_LIMIT || thirdBit < BRIGHTNESS_LIMIT) return true;
+                                       // If colours are too close in any channel then it's an overlap
+                                       if (Math.abs(pixLow-textLow) < BRIGHTNESS_LIMIT ||
+                                               Math.abs(pixMid-textMid) < BRIGHTNESS_LIMIT ||
+                                               Math.abs(pixHigh-textHigh) < BRIGHTNESS_LIMIT) {return true;}
                                }
                        }
                }
@@ -767,7 +795,13 @@ public class MapCanvas extends JPanel implements MouseListener, MouseMotionListe
                                         _mapPosition.getXFromPixels(inE.getX(), getWidth()),
                                         _mapPosition.getYFromPixels(inE.getY(), getHeight()),
                                         _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false);
-                               _trackInfo.selectPoint(pointIndex);
+                               // Extend selection for shift-click
+                               if (inE.isShiftDown()) {
+                                       _trackInfo.extendSelection(pointIndex);
+                               }
+                               else {
+                                       _trackInfo.selectPoint(pointIndex);
+                               }
                        }
                        else
                        {
index 1f9c3872e2e12de84cb60a22d083a7493e1f38b7..e545125256db43c46d19dc9b7e2e1f23653c9beb 100644 (file)
@@ -3,7 +3,7 @@ package tim.prune.gui.map;
 import java.net.MalformedURLException;
 import java.net.URL;
 
-import tim.prune.Config;
+import tim.prune.config.Config;
 
 /**
  * Class to hold the config for the map tiles
index 0f65db91c5e9c2fe1eb78a20b01646f3561155a0..1daa19ebd926dc05c852290fb2e653760532c00d 100644 (file)
@@ -5,8 +5,9 @@ import java.awt.Dimension;
 import java.awt.Graphics;
 import javax.swing.JPanel;
 
-import tim.prune.Config;
 import tim.prune.I18nManager;
+import tim.prune.config.ColourScheme;
+import tim.prune.config.Config;
 
 /**
  * Class to show a scale bar on the main map of Prune
@@ -81,18 +82,37 @@ public class ScaleBar extends JPanel
                                        if (scale < 1) {return;}
                                }
 
+                               // Determine colours to use
+                               Color barColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
+                               Color blankColour = new Color(255-barColour.getRed(), 255-barColour.getGreen(), 255-barColour.getBlue());
+                               // Should this blank colour be set to saturation zero?
+                               // Draw blank bars behind
+                               inG.setColor(blankColour);
+                               inG.drawLine(LEFT_OFFSET, Y_OFFSET-1, rightSide+2, Y_OFFSET-1);
+                               inG.drawLine(LEFT_OFFSET, Y_OFFSET+2, rightSide+2, Y_OFFSET+2);
+                               inG.drawLine(LEFT_OFFSET-1, Y_OFFSET+2, LEFT_OFFSET-1, Y_OFFSET-TICK_HEIGHT);
+                               inG.drawLine(LEFT_OFFSET+2, Y_OFFSET+2, LEFT_OFFSET+2, Y_OFFSET-TICK_HEIGHT);
+                               inG.drawLine(rightSide-1, Y_OFFSET+2, rightSide-1, Y_OFFSET-TICK_HEIGHT);
+                               inG.drawLine(rightSide+2, Y_OFFSET+2, rightSide+2, Y_OFFSET-TICK_HEIGHT);
                                // horizontal
-                               inG.setColor(Color.BLACK);
+                               inG.setColor(barColour);
                                inG.drawLine(LEFT_OFFSET, Y_OFFSET, rightSide, Y_OFFSET);
                                inG.drawLine(LEFT_OFFSET, Y_OFFSET+1, rightSide, Y_OFFSET+1);
                                // 0 tick
                                inG.drawLine(LEFT_OFFSET, Y_OFFSET, LEFT_OFFSET, Y_OFFSET-TICK_HEIGHT);
                                inG.drawLine(LEFT_OFFSET+1, Y_OFFSET, LEFT_OFFSET+1, Y_OFFSET-TICK_HEIGHT);
                                // end tick
-                               inG.drawLine(rightSide, Y_OFFSET, rightSide, Y_OFFSET-TICK_HEIGHT);
-                               inG.drawLine(rightSide+1, Y_OFFSET, rightSide+1, Y_OFFSET-TICK_HEIGHT);
+                               inG.drawLine(rightSide, Y_OFFSET+1, rightSide, Y_OFFSET-TICK_HEIGHT);
+                               inG.drawLine(rightSide+1, Y_OFFSET+1, rightSide+1, Y_OFFSET-TICK_HEIGHT);
                                // text
-                               String text = (scale>0?(""+scale):("1/"+(-scale))) + " " + I18nManager.getText(useMetric?"units.kilometres.short":"units.miles.short");
+                               String text = (scale>0?(""+scale):("1/"+(-scale))) + " "
+                                       + I18nManager.getText(useMetric?"units.kilometres.short":"units.miles.short");
+                               inG.setColor(blankColour);
+                               inG.drawString(text, rightSide+MARGIN_WIDTH-1, Y_OFFSET);
+                               inG.drawString(text, rightSide+MARGIN_WIDTH+1, Y_OFFSET);
+                               inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET-1);
+                               inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET+1);
+                               inG.setColor(barColour);
                                inG.drawString(text, rightSide+MARGIN_WIDTH, Y_OFFSET);
                        }
                        catch (ArrayIndexOutOfBoundsException ai) {}
index 42875f6aac028112b394d86bfd71fbefd7e1b048..11de10f6e98214d40cee388dd7a4a54dc3417fd2 100644 (file)
@@ -39,6 +39,7 @@ menu.view.browser.google=Google Kaarte
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo Kaarte
+menu.view.browser.bing=Bing Kaarte
 menu.help=Hulp
 # Popup menu for map
 menu.map.zoomin=Zoom in
index daf49fefcea82f2bd7ed36220fd23fde94797ba8..26e2b659f2f03eeddd322de36aace4f2cdcf90ab 100644 (file)
@@ -7,7 +7,7 @@ menu.file.open=Datei 
 menu.file.addphotos=Fotos laden
 menu.file.save=Speichern
 menu.file.exit=Beenden
-menu.edit=Bearbeiten
+menu.track=Track
 menu.edit.undo=Rückgängig
 menu.edit.clearundo=Liste der letzten Ã„nderungen löschen
 menu.edit.editpoint=Punkt bearbeiten
@@ -23,24 +23,20 @@ menu.edit.rearrange.start=Alle Wegpunkte zum Anfang
 menu.edit.rearrange.end=Alle Wegpunkte ans Ende
 menu.edit.rearrange.nearest=Jeden Wegpunkt zum nächsten Trackpunkt verschieben
 menu.edit.cutandmove=Schneiden und verschieben
-menu.select=Markieren
+menu.range=Bereich
+menu.point=Punkt
 menu.select.all=Alles markieren
 menu.select.none=Nichts markieren
 menu.select.start=Startpunkt setzen
 menu.select.end=Endpunkt setzen
 menu.photo=Foto
 menu.photo.saveexif=Exif Daten speichern
-menu.photo.connect=Mit Punkt verbinden
+menu.photo.connect=Mit Punkt verknüpfen
 menu.photo.disconnect=Vom Punkt trennen
 menu.photo.delete=Foto entfernen
 menu.view=Ansicht
 menu.view.browser=Karte in Browser
-menu.view.browser.google=Google Maps
-menu.view.browser.openstreetmap=Openstreetmap
-menu.view.browser.mapquest=
-menu.view.browser.yahoo=
 menu.settings=Einstellungen
-menu.settings.showpace=Tempo anzeigen
 menu.help=Hilfe
 # Popup menu for map
 menu.map.zoomin=Hineinzoomen
@@ -54,8 +50,9 @@ menu.map.showscalebar=Ma
 
 # Alt keys for menus
 altkey.menu.file=D
-altkey.menu.edit=B
-altkey.menu.select=M
+altkey.menu.track=T
+altkey.menu.range=B
+altkey.menu.point=P
 altkey.menu.view=A
 altkey.menu.photo=F
 altkey.menu.settings=E
@@ -81,14 +78,24 @@ function.compress=Track komprimieren
 function.addtimeoffset=Zeitverschiebung aufrechnen
 function.addaltitudeoffset=Höhenverschiebung aufrechnen
 function.findwaypoint=Wegpunkt finden
+function.convertnamestotimes=Wegpunktenamen in Zeitstempeln verwandeln
+function.pastecoordinates=Neue Koordinaten eingeben
 function.charts=Diagramme
 function.show3d=3D Ansicht
-function.distances=Distanzen
+function.distances=Entfernungen
+function.fullrangedetails=Zusätzliche Bereichdetails
 function.setmapbg=Karte Hintergrund setzen
 function.setkmzimagesize=Bildgröße im KMZ setzen
-function.setpaths=Programmenpfade setzen
+function.setpaths=Programmpfade setzen
+function.setcolours=Farben einstellen
+function.setlanguage=Sprache einstellen
 function.getgpsies=Gpsies Tracks holen
+function.duplicatepoint=Punkt verdoppeln
 function.correlatephotos=Fotos korrelieren
+function.rearrangephotos=Fotos reorganisieren
+function.rotatephotoleft=Foto nach Links drehen
+function.rotatephotoright=Foto nach Rechts drehen
+function.ignoreexifthumb=Exif Vorschaubild ignorieren
 function.help=Hilfe
 function.showkeys=Tastenkombinationen anzeigen
 function.about=Ãœber Prune
@@ -107,19 +114,19 @@ dialog.deletephoto.deletepoint=Den zu diesem Foto geh
 dialog.openoptions.title=Öffnen
 dialog.openoptions.filesnippet=Dateiausschnitt
 dialog.load.table.field=Feld
-dialog.load.table.datatype=Daten Typ
+dialog.load.table.datatype=Datentyp
 dialog.load.table.description=Beschreibung
 dialog.delimiter.label=Feld Trennzeichen
 dialog.delimiter.comma=Komma ,
 dialog.delimiter.tab=Tab
-dialog.delimiter.space=Leerstelle
+dialog.delimiter.space=Leerzeichen
 dialog.delimiter.semicolon=Strichpunkt ;
 dialog.delimiter.other=Andere
 dialog.openoptions.deliminfo.records=Aufnahmen, mit
 dialog.openoptions.deliminfo.fields=Feldern
 dialog.openoptions.deliminfo.norecords=Keine Rekords
 dialog.openoptions.altitudeunits=Höhe Maßeinheiten
-dialog.jpegload.subdirectories=Unterordner auch durchsuchen
+dialog.jpegload.subdirectories=Unterordner mit durchsuchen
 dialog.jpegload.loadjpegswithoutcoords=Auch Fotos ohne Koordinaten laden
 dialog.jpegload.loadjpegsoutsidearea=Auch Fotos ausserhalb vom Track laden
 dialog.jpegload.progress.title=Fotos werden geladen
@@ -129,6 +136,7 @@ dialog.gpsload.device=Ger
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Wegpunkte laden
 dialog.gpsload.gettracks=Tracks laden
+dialog.gpsload.save=nach Datei speichern
 dialog.gpssend.sendwaypoints=Wegpunkte senden
 dialog.gpssend.sendtracks=Tracks senden
 dialog.gpssend.trackname=Track Name
@@ -143,13 +151,16 @@ dialog.save.altitudeunits=H
 dialog.save.timestampformat=Zeitstempelformat
 dialog.save.overwrite.title=Datei schon vorhanden
 dialog.save.overwrite.text=Diese Datei gibt es schon. Wollen Sie die vorhandene Datei Ã¼berschreiben?
+dialog.save.notypesselected=Keine Punktetypen sind ausgewählt
 dialog.exportkml.text=Titel für die Daten
 dialog.exportkml.altitude=Absolute Höheninformation (für Luftfahrt)
 dialog.exportkml.kmz=Daten in kmz Datei komprimieren
 dialog.exportkml.exportimages=Vorschaubilder mit in kmz exportieren
+dialog.exportkml.trackcolour=Trackfarbe
 dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Beschreibung
 dialog.exportgpx.includetimestamps=Zeitstempel mit exportieren
+dialog.exportgpx.copysource=Xml von Quelle kopieren
 dialog.exportpov.text=Geben Sie die Parameter für den POV Export ein
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Kamera X
@@ -163,6 +174,7 @@ dialog.pointtype.desc=Folgende Punkttypen speichern:
 dialog.pointtype.track=Trackpunkte
 dialog.pointtype.waypoint=Wegpunkte
 dialog.pointtype.photo=Fotopunkte
+dialog.pointtype.selection=Nur aktuellen Bereich
 dialog.confirmreversetrack.title=Umkehrung bestätigen
 dialog.confirmreversetrack.text=Diese Daten enthalten Zeitangaben, die bei einer Umkehrung in falscher Reihenfolge erscheinen würden.\nSind Sie sicher, dass Sie diesen Bereich umkehren wollen?
 dialog.confirmcutandmove.title=Verschieben bestätigen
@@ -174,7 +186,7 @@ dialog.undo.pretext=Bitte die Operationen, die r
 dialog.undo.none.title=Undo nicht möglich
 dialog.undo.none.text=Keine Operationen können rückgängig gemacht werden.
 dialog.clearundo.title=Undo-Liste löschen
-dialog.clearundo.text=Sind Sie sicher, Sie wollen die Undo-Liste löschen?\nAlle Undo Information wird veloren gehen!
+dialog.clearundo.text=Wollen Sie wirklich die Undo-Liste löschen?\nAlle Undo- Informationen werden verloren gehen!
 dialog.pointedit.title=Punkt bearbeiten
 dialog.pointedit.text=Wählen Sie die Felder aus, die Sie bearbeiten möchten, und verwenden Sie den 'Bearbeiten' Knopf, um den Wert zu Ã¤ndern
 dialog.pointedit.table.field=Feld
@@ -191,11 +203,9 @@ dialog.addtimeoffset.subtract=Zeit subtrahieren
 dialog.addtimeoffset.days=Tage
 dialog.addtimeoffset.hours=Stunde
 dialog.addtimeoffset.minutes=Minute
-dialog.addtimeoffset.notimestamps=Zeitdifferenz kann nicht addiert werden weil dieser Bereich keine Zeitinformation hat
-dialog.findwaypoint.intro=Geben Sie einen Teil von dem Namen ein
+dialog.addtimeoffset.notimestamps=Zeitdifferenz kann nicht addiert werden, weil dieser Bereich keine Zeitinformation hat
+dialog.findwaypoint.intro=Geben Sie einen Teil des Namens ein
 dialog.findwaypoint.search=Suche
-dialog.connect.title=Foto mit Punkt verbinden
-dialog.connectphoto.clonepoint=Diesem Punkt ist schon ein Foto zugeordnet.\nWollen Sie eine Kopie dieses Punktes machen?
 dialog.saveexif.title=Exif speichern
 dialog.saveexif.intro=Wählen Sie die Fotos zum Speichern aus
 dialog.saveexif.nothingtosave=Koordinaten sind unverändert. Es gibt nichts zu speichern.
@@ -207,23 +217,22 @@ dialog.saveexif.photostatus.connected=Verbunden
 dialog.saveexif.photostatus.disconnected=Getrennt
 dialog.saveexif.photostatus.modified=Modifiziert
 dialog.saveexif.overwrite=Dateien Ã¼berschreiben
+dialog.saveexif.force=Forzieren trotz Warnungen
 dialog.charts.xaxis=X Achse
 dialog.charts.yaxis=Y Achse
 dialog.charts.output=Ausgabe
-dialog.charts.screen=zum Bildschirm
-dialog.charts.svg=zur SVG Datei
+dialog.charts.screen=Ausgabe zum Bildschirm
+dialog.charts.svg=Ausgabe in SVG Datei
 dialog.charts.svgwidth=SVG Breite
 dialog.charts.svgheight=SVG Höhe
 dialog.charts.needaltitudeortimes=Ohne Daten Ã¼ber Höhe und Zeit kann kein Diagramm erzeugt werden.
-dialog.charts.gnuplotpath=Gnuplot Pfad
 dialog.charts.gnuplotnotfound=Gnuplot konnte im angegebenen Pfad nicht gefunden werden
-dialog.distances.intro=Luftlinienentfernung zwischen Punkte
+dialog.distances.intro=Luftlinienentfernung zwischen Punkten
 dialog.distances.column.from=Vom Punkt
 dialog.distances.column.to=Zum Punkt
 dialog.distances.currentpoint=Aktueller Punkt
 dialog.distances.toofewpoints=Diese Funktion braucht Wegpunkte um die Distanzen zu berechnen
-dialog.setmapbg.mapnik=
-dialog.setmapbg.osma=
+dialog.fullrangedetails.intro=Hier sind die Details vom markierten Bereich
 dialog.setmapbg.cyclemap=Fahrradkarte
 dialog.setmapbg.other=Andere
 dialog.setmapbg.server=Server URL
@@ -254,6 +263,12 @@ dialog.correlate.options.nodistancelimit=Keine Distanzgrenzen
 dialog.correlate.options.distancelimit=Distanzgrenzen
 dialog.correlate.options.correlate=Korrelieren
 dialog.correlate.alloutsiderange=Alle Fotos sind außerhalb vom Track Zeitraum.  Sie können nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Foto ein.
+dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
+dialog.rearrangephotos.tostart=Am Anfang
+dialog.rearrangephotos.toend=Am Ende
+dialog.rearrangephotos.nosort=Nicht sortieren
+dialog.rearrangephotos.sortbyfilename=per Dateiname sortieren
+dialog.rearrangephotos.sortbytime=per Zeitstempel sortieren
 dialog.compress.nonefound=Es konnten keine Punkte entfernt werden
 dialog.compress.duplicates.title=Duplikate entfernen
 dialog.compress.closepoints.title=Nahegelegene Punkte entfernen
@@ -263,7 +278,10 @@ dialog.compress.wackypoints.paramdesc=Distanzfaktor
 dialog.compress.singletons.title=Singletons (isolierte Punkte) entfernen
 dialog.compress.singletons.paramdesc=Distanzfaktor
 dialog.compress.summarylabel=Punkte zu entfernen
-dialog.help.help=Bitte sehen Sie\n http://activityworkshop.net/software/prune/\nfür weitere Information und Benutzeranleitungen.
+dialog.pastecoordinates.desc=Geben Sie die Koordinaten ein
+dialog.pastecoordinates.coords=Koordinaten
+dialog.pastecoordinates.nothingfound=Bitte prüfen Sie die Koordinaten und versuchen nochmals
+dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/prune/
 dialog.about.version=Version
 dialog.about.build=Build
 dialog.about.summarytext1=Prune ist ein Programm zum Laden, Darstellen und Editieren von Daten von GPS Geräten.
@@ -304,6 +322,7 @@ dialog.saveconfig.desc=Die folgende Einstellungen k
 dialog.saveconfig.prune.trackdirectory=Datenverzeichnis
 dialog.saveconfig.prune.photodirectory=Fotoverzeichnis
 dialog.saveconfig.prune.languagecode=Sprachecode (DE)
+dialog.saveconfig.prune.languagefile=Sprachedatei
 dialog.saveconfig.prune.gpsdevice=GPS Gerätename
 dialog.saveconfig.prune.gpsformat=GPS Format
 dialog.saveconfig.prune.povrayfont=Povray Font
@@ -313,12 +332,31 @@ dialog.saveconfig.prune.gpsbabelpath=Gpsbabel Pfad
 dialog.saveconfig.prune.exiftoolpath=Exiftool Pfad
 dialog.saveconfig.prune.mapserverindex=Kartenserver Index
 dialog.saveconfig.prune.mapserverurl=Kartenserver URL
-dialog.saveconfig.prune.showpace=Tempo anzeigen
 dialog.saveconfig.prune.kmzimagewidth=Bildbreite in KMZ
 dialog.saveconfig.prune.kmzimageheight=Bildhöhe in KMZ
+dialog.saveconfig.prune.colourscheme=Farbenschema
+dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe
 dialog.setpaths.intro=Sie können hier die Pfade für externe Applikationen setzen:
 dialog.addaltitude.noaltitudes=Der markierten Bereich enthält keine Höhenangaben
 dialog.addaltitude.desc=Höhenverschiebung aufzurechnen
+dialog.setcolours.intro=Klicken Sie auf einer Farbe um sie zu Ã¤ndern
+dialog.setcolours.background=Hintergrund
+dialog.setcolours.borders=Umrandungen
+dialog.setcolours.lines=Linien
+dialog.setcolours.primary=Primär
+dialog.setcolours.secondary=Secondär
+dialog.setcolours.point=Punkte
+dialog.setcolours.selection=Bereich
+dialog.setcolours.text=Texte
+dialog.colourchooser.title=Farbe auswählen
+dialog.colourchooser.red=Rot
+dialog.colourchooser.green=Grün
+dialog.colourchooser.blue=Blau
+dialog.setlanguage.firstintro=Sie können entweder eine von den eingebauten Sprachen<p>oder eine Text-Datei auswählen.
+dialog.setlanguage.secondintro=Sie müssen Ihre Einstellungen speichern und dann<p>Prune wieder neustarten um die Sprache zu Ã¤ndern.
+dialog.setlanguage.language=Sprache
+dialog.setlanguage.languagefile=Sprache Datei
+dialog.setlanguage.endmessage=Nun speichern Sie Ihre Einstellungen und starten Sie Prune neu\num die neue Sprache zu verwenden.
 
 # 3d window
 dialog.3d.title=Prune 3D Ansicht
@@ -339,7 +377,9 @@ confirm.reverserange=Bereich umgekehrt
 confirm.addtimeoffset=Zeitverschiebung aufgerechnet
 confirm.addaltitudeoffset=Höhenverschiebung aufgerechnet
 confirm.rearrangewaypoints=Wegpunkte reorganisiert
+confirm.rearrangephotos=Fotos reorganisiert
 confirm.cutandmove=Bereich verschoben
+confirm.convertnamestotimes=Wegpunktnamen verwandelt
 confirm.saveexif.ok1=Es wurden
 confirm.saveexif.ok2=Fotodateien geschrieben
 confirm.undo.single=Operation rückgängig gemacht
@@ -351,6 +391,7 @@ confirm.photo.disconnect=Foto getrennt
 confirm.correlate.single=Foto wurde korreliert
 confirm.correlate.multi=Fotos wurden korreliert
 confirm.createpoint=Punkt kreiert
+confirm.rotatephoto=Foto umgedreht
 confirm.running=In Bearbeitung ...
 
 # Buttons
@@ -371,13 +412,16 @@ button.yes=Ja
 button.no=Nein
 button.yestoall=Ja für alle
 button.notoall=Nein für alle
-button.selectall=Alle selektieren
-button.selectnone=Nichts selektieren
+button.select=Auswählen
+button.selectall=Alle auswählen
+button.selectnone=Nichts auswählen
 button.preview=Vorschau
 button.load=Laden
 button.guessfields=Felder erraten
 button.showwebpage=Webseite anzeigen
 button.check=Prüfen
+button.resettodefaults=Zurücksetzen
+button.browse=Durchsuchen...
 
 # File types
 filetype.txt=TXT Dateien
@@ -417,7 +461,9 @@ display.range.time.hours=h
 display.range.time.days=T
 details.range.avespeed=Geschwindigkeit
 details.range.avemovingspeed=Geschwindigkeit unterwegs
+details.range.numsegments=Anzahl Abschnitte
 details.range.pace=Tempo
+details.range.gradient=Gefälle
 details.waypointsphotos.waypoints=Wegpunkte
 details.waypointsphotos.photos=Fotos
 details.photodetails=Fotodetails
@@ -447,15 +493,9 @@ fieldname.verticalspeed=Vertikale Geschwindigkeit
 units.original=Original
 units.default=Standard
 units.metres=Meter
-units.metres.short=
-units.feet=
-units.feet.short=
 units.kilometres=Kilometer
 units.kilometres.short=km
 units.kmh=km/h
-units.miles=
-units.miles.short=
-units.mph=
 units.metrespersec=m/s
 units.feetpersec=ft/s
 units.hours=Std
@@ -491,15 +531,22 @@ undo.cutandmove=Bereich verschieben
 undo.connectphoto=Foto verbinden
 undo.disconnectphoto=Foto trennen
 undo.correlate=Fotos korrelieren
-undo.createpoint=Punkt kreieren
+undo.rearrangephotos=Fotos reorganisieren
+undo.rotatephoto=Foto umdrehen
+undo.createpoint=Punkt erzeugen
+undo.convertnamestotimes=Namen in Zeitstempeln verwandeln
 
 # Error messages
 error.save.dialogtitle=Fehler beim Speichern
 error.save.nodata=Keine Daten zum Sichern vorhanden
-error.save.failed=Speichern von der Datei fehlgeschlagen
+error.save.failed=Speichern von Daten in Datei fehlgeschlagen
 error.saveexif.filenotfound=Foto Datei nicht gefunden
 error.saveexif.cannotoverwrite1=Foto Datei
 error.saveexif.cannotoverwrite2=ist schreib-geschützt. Als Kopie speichern?
+error.saveexif.failed1=
+error.saveexif.failed2=von den Bildern konnten nicht gespeichert werden
+error.saveexif.forced1=
+error.saveexif.forced2=von den Bildern mussten forziert werden
 error.load.dialogtitle=Fehler beim Laden
 error.load.noread=Datei konnte nicht gelesen werden
 error.load.nopoints=Keine gültigen Daten in Datei gefunden
@@ -511,13 +558,16 @@ 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.gpsload.unknown=Unbekanntes Fehler
 error.undofailed.title=Undo fehlgeschlagen
 error.undofailed.text=Operation konnte nicht rückgängig gemacht werden
 error.function.noop.title=Funktion hat nichts bewirkt
-error.rearrange.noop=Die Wegpunkte zu reorganisieren hatte keinen Effekt
+error.rearrange.noop=Die Punkte zu reorganisieren hatte keinen Effekt
 error.function.notavailable.title=Funktion nicht verfügbar
 error.function.nojava3d=Diese Funktion benötigt die Java3d Library,\nvon Sun.com erhältlich.
 error.3d=Ein Fehler ist bei der 3D Darstellung aufgetreten
 error.readme.notfound=Liesmich Datei nicht gefunden
 error.osmimage.dialogtitle=Laden von Karten-Bildern fehlgeschlagen
 error.osmimage.failed=Laden von Karten-Bildern fehlgeschlagen. Bitte prüfen Sie die Internetverbindung.
+error.language.wrongfile=Die ausgewählte Datei scheint keine Sprache-Datei für Prune zu sein
+error.convertnamestotimes.nonames=Keine Namen konnten verwandelt werden
index b00f39dc9d901bb4eda5dce93dcd6c39fb86dbd4..5ce66f6cd689c65d35b30b5407143cc3487c93a0 100644 (file)
@@ -7,7 +7,7 @@ menu.file.open=File 
 menu.file.addphotos=Fötelis innätue
 menu.file.save=Speichere
 menu.file.exit=Beände
-menu.edit=Editiere
+menu.track=Track
 menu.edit.undo=Undo
 menu.edit.clearundo=Undo-Liste lösche
 menu.edit.editpoint=Punkt editiere
@@ -23,7 +23,8 @@ menu.edit.rearrange.start=Alli zum Aafang
 menu.edit.rearrange.end=Alli zum Ã„nde
 menu.edit.rearrange.nearest=Jede zum nöchsti Trackpunkt
 menu.edit.cutandmove=Schniide und move
-menu.select=Selektiere
+menu.range=Beriich
+menu.point=Punkt
 menu.select.all=Alles selektiere
 menu.select.none=Nüüt selektiere
 menu.select.start=Start setzä
@@ -35,12 +36,7 @@ menu.photo.disconnect=Vonem Punkt tr
 menu.photo.delete=Föteli entfernä
 menu.view=Aasicht
 menu.view.browser=Karte inem Browser
-menu.view.browser.google=
-menu.view.browser.openstreetmap=
-menu.view.browser.mapquest=
-menu.view.browser.yahoo=
 menu.settings=Iistellige
-menu.settings.showpace=Tempo aazeige
 menu.help=Hilfe
 # Popup menu for map
 menu.map.zoomin=Innezoome
@@ -49,13 +45,14 @@ menu.map.zoomfull=Zoome zum ganzes Bild
 menu.map.newpoint=Noii Punkt
 menu.map.connect=Trackpünktli verbindä
 menu.map.autopan=Autopan
-menu.map.showmap=Kate zeigä
+menu.map.showmap=Karte zeigä
 menu.map.showscalebar=Massstab aazeige
 
 # Alt keys for menus
 altkey.menu.file=F
-altkey.menu.edit=E
-altkey.menu.select=S
+altkey.menu.track=T
+altkey.menu.range=B
+altkey.menu.point=P
 altkey.menu.view=A
 altkey.menu.photo=F
 altkey.menu.settings=I
@@ -80,15 +77,25 @@ function.editwaypointname=Waypoint Name editiere
 function.compress=Track komprimierä
 function.addtimeoffset=Ziitverschiebig zutue
 function.addaltitudeoffset=Höchiverschiebig zutue
-function.findwaypoint=Waypoint findä
+function.findwaypoint=Waypoint suechä
+function.convertnamestotimes=Waypointname ins Ziitstämple verwondle
+function.pastecoordinates=Noii Koordinaten iigebe
 function.charts=Diagramme
 function.show3d=Drüü-D Aasicht
 function.distances=Distanze
+function.fullrangedetails=Zuesätzlichi Beriichinfos
 function.setmapbg=Karte Hintegrund setzä
 function.getgpsies=Gpsies Tracks holä
+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.ignoreexifthumb=Exif Vorschaubildli ignorierä
 function.setkmzimagesize=Bildligrösse inem KMZ setze
 function.setpaths=Programmepfade setze
+function.setcolours=Farben setze
+function.setlanguage=Sproch setze
 function.help=Hilfe
 function.showkeys=Tastekombinatione aazeige
 function.about=Ãœber Prune
@@ -129,6 +136,7 @@ dialog.gpsload.device=Device Name
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Waypoints lade
 dialog.gpsload.gettracks=Tracks lade
+dialog.gpsload.save=nach nem File speicherä
 dialog.gpssend.sendwaypoints=Waypoints schicke
 dialog.gpssend.sendtracks=Tracks schicke
 dialog.gpssend.trackname=Track Name
@@ -143,13 +151,16 @@ dialog.save.altitudeunits=H
 dialog.save.timestampformat=Ziitstämpelformat
 dialog.save.overwrite.title=s'File existiert scho
 dialog.save.overwrite.text=s'File existiert scho. Sind Sie sicher, Sie wend s'File Ã¼berschriibe?
+dialog.save.notypesselected=Kei Punktetype sin uusgewählt worde
 dialog.exportkml.text=Titel für die Date
 dialog.exportkml.altitude=Absolut Höchiinformation (fürs Fliege)
 dialog.exportkml.kmz=Date ins kmz File komprimierä
 dialog.exportkml.exportimages=Bildli ins Kmz exportierä
+dialog.exportkml.trackcolour=Trackfarb
 dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Beschriibig
 dialog.exportgpx.includetimestamps=Au Ziitstämpel
+dialog.exportgpx.copysource=Xml-Quälle kopierä
 dialog.exportpov.text=Gäbet Sie die Parameter ii fürs POV Export
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Kamera X
@@ -163,6 +174,7 @@ dialog.pointtype.desc=Folgende Punkttype speichere:
 dialog.pointtype.track=Trackpunkte
 dialog.pointtype.waypoint=Waypoints
 dialog.pointtype.photo=Fötelipunkte
+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?
 dialog.confirmcutandmove.title=Move bestätige
@@ -194,8 +206,6 @@ dialog.addtimeoffset.minutes=Minute
 dialog.addtimeoffset.notimestamps=Ziitverschiebig nöd möglech wil dr Beriich kei Ziitinfo hät
 dialog.findwaypoint.intro=Gebet Sie en Teil vonem Namen ina
 dialog.findwaypoint.search=Sueche
-dialog.connect.title=Föteli verknüpfe
-dialog.connectphoto.clonepoint=Derer Punkt hät scho s Föteli.\nWend sie ne Kopie vonem Punkt machä?
 dialog.saveexif.title=Exif go speicherä
 dialog.saveexif.intro=Wählet Sie die Fötelis uus zum speicherä
 dialog.saveexif.nothingtosave=Koordinaten sin nöd geänderet, nüüt zum speicherä
@@ -207,6 +217,7 @@ dialog.saveexif.photostatus.connected=Verbund
 dialog.saveexif.photostatus.disconnected=Gtrännt
 dialog.saveexif.photostatus.modified=Gänderet
 dialog.saveexif.overwrite=Files Ã¼berschriebä
+dialog.saveexif.force=Forzierä trotz Warnige
 dialog.charts.xaxis=X Achse
 dialog.charts.yaxis=Y Achse
 dialog.charts.output=Uusgabe
@@ -215,15 +226,13 @@ dialog.charts.svg=SVG File
 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.gnuplotpath=Gnuplot Pfad
 dialog.charts.gnuplotnotfound=Gnuplot isch mit dem Pfad nöd gfunde worde
 dialog.distances.intro=Dischtanze per Luftlinie zwüschet Punkte
 dialog.distances.column.from=Vom Punkt
 dialog.distances.column.to=Zum Punkt
 dialog.distances.currentpoint=Aktuelli Punkt
 dialog.distances.toofewpoints=d'Funktion bruucht Waypoints um die Dischtanze z berächne
-dialog.setmapbg.mapnik=
-dialog.setmapbg.osma=
+dialog.fullrangedetails.intro=Hier sind die Infos vonem aktuelli Beriich
 dialog.setmapbg.cyclemap=Velokarte
 dialog.setmapbg.other=Anderi
 dialog.setmapbg.server=Server URL
@@ -254,6 +263,12 @@ 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.rearrangephotos.desc=Bitte Ziel und Reihefolge von den Punkten setze
+dialog.rearrangephotos.tostart=zum Aafang
+dialog.rearrangephotos.toend=zum Ende
+dialog.rearrangephotos.nosort=Nöd sortiere
+dialog.rearrangephotos.sortbyfilename=per Filename sortiere
+dialog.rearrangephotos.sortbytime=per Ziit sortiere
 dialog.compress.nonefound=Kei Punkte hätte gelöscht werde könne
 dialog.compress.duplicates.title=Duplikate entfärnä
 dialog.compress.closepoints.title=Nöchiglägeni Punkte entfärnä
@@ -263,6 +278,9 @@ dialog.compress.wackypoints.paramdesc=Distanz Faktor
 dialog.compress.singletons.title=Singletons entfärnä
 dialog.compress.singletons.paramdesc=Distanz faktor
 dialog.compress.summarylabel=Punkte zu entfärnä
+dialog.pastecoordinates.desc=Gäbet Sie hier die Koordinaten innä
+dialog.pastecoordinates.coords=Koordinate
+dialog.pastecoordinates.nothingfound=Prüefet Sie die Koordinate und versuechet nomal
 dialog.help.help=Bitte lueg na\n http://activityworkshop.net/software/prune/\nfür wiitere Information und Benutzeraaleitige.
 dialog.about.version=Version
 dialog.about.build=Build
@@ -304,6 +322,7 @@ dialog.saveconfig.desc=Die folgendi Iinstellige k
 dialog.saveconfig.prune.trackdirectory=Trackverzeichnis
 dialog.saveconfig.prune.photodirectory=Föteliverzeichnis
 dialog.saveconfig.prune.languagecode=Sprochecode (DE_ch)
+dialog.saveconfig.prune.languagefile=Sprochedatei
 dialog.saveconfig.prune.gpsdevice=GPS Gerätename
 dialog.saveconfig.prune.gpsformat=GPS Format
 dialog.saveconfig.prune.povrayfont=Povray Font
@@ -313,12 +332,31 @@ dialog.saveconfig.prune.gpsbabelpath=Gpsbabel Pfad
 dialog.saveconfig.prune.exiftoolpath=Exiftool Pfad
 dialog.saveconfig.prune.mapserverindex=Kartenserver Index
 dialog.saveconfig.prune.mapserverurl=Kartenserver URL
-dialog.saveconfig.prune.showpace=Tempo aazeige
 dialog.saveconfig.prune.kmzimagewidth=Bildbreiti im KMZ
 dialog.saveconfig.prune.kmzimageheight=Bildhöchi im KMZ
+dialog.saveconfig.prune.colourscheme=Farbeschema
+dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb
 dialog.setpaths.intro=Sie könnet dann die Pfade für dia Applikatione setzä:
 dialog.addaltitude.noaltitudes=Dr seläktierte Beriich hät keini Höchiinformation
 dialog.addaltitude.desc=Höchiverschiebig zuzutue
+dialog.setcolours.intro=Klicket Sie uuf ne Farb um sie z'verändere
+dialog.setcolours.background=Hintergrund
+dialog.setcolours.borders=Rande
+dialog.setcolours.lines=Linie
+dialog.setcolours.primary=Primär
+dialog.setcolours.secondary=Secondär
+dialog.setcolours.point=Punkte
+dialog.setcolours.selection=Beriich
+dialog.setcolours.text=Texte
+dialog.colourchooser.title=Farbe uuswähle
+dialog.colourchooser.red=Rot
+dialog.colourchooser.green=Grüen
+dialog.colourchooser.blue=Blau
+dialog.setlanguage.firstintro=Sie könnet entweder eini vo den iigebouti Sproche<p>oder ne Text-Datei uuswähle.
+dialog.setlanguage.secondintro=Sie münt Ihri Iistellige speichere und dann<p>Prune wieder neustarte um die Sproch z'ändere.
+dialog.setlanguage.language=Sproch
+dialog.setlanguage.languagefile=Sproch Datei
+dialog.setlanguage.endmessage=Jetze speicheret Sie Ihri Iistellige und startet Sie Prune neu\num t noii Sproch z' verwände.
 
 # 3d window
 dialog.3d.title=Prune Drüü-d Aasicht
@@ -339,7 +377,9 @@ confirm.reverserange=Beriich umgdr
 confirm.addtimeoffset=Ziitverschiebig zutue
 confirm.addaltitudeoffset=Höchiverschiebig zutue
 confirm.rearrangewaypoints=Waypoints umorganisiert
+confirm.rearrangephotos=Fotos umorganisiert
 confirm.cutandmove=Beriich gmoved
+confirm.convertnamestotimes=Waypointname verwondlet
 confirm.saveexif.ok1=Es sin
 confirm.saveexif.ok2=Fötelis gschriebe worde
 confirm.undo.single=Operation rückgängig gmacht worde.
@@ -351,6 +391,7 @@ confirm.photo.disconnect=F
 confirm.correlate.single=Föteli isch korreliert worde
 confirm.correlate.multi=Fötelis sin korreliert worde
 confirm.createpoint=Punkt kreiert worde
+confirm.rotatephoto=Föteli umgedräit worde
 confirm.running=Am Laufe ...
 
 # Buttons
@@ -371,14 +412,16 @@ button.yes=Ja
 button.no=Nei
 button.yestoall=Ja für alli
 button.notoall=Nei für alli
-button.selectall=Alli selektierä
-button.selectnone=Nüüt selektierä
+button.select=Uuswähle
+button.selectall=Alli uuswähle
+button.selectnone=Nüüt uuswähle
 button.preview=Vorschauä
 button.load=Ladä
 button.guessfields=Fälde erratä
 button.showwebpage=Websiite aazeigä
 button.check=Prüefa
-
+button.resettodefaults=Zurücksetzä
+button.browse=Durasuechä...
 
 # File types
 filetype.txt=TXT Dateie
@@ -418,7 +461,9 @@ display.range.time.hours=Std
 display.range.time.days=T
 details.range.avespeed=Gschwindikeit
 details.range.avemovingspeed=Gschwindikeit ufem Wäg
+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.photodetails=Details vonem Föteli
@@ -448,15 +493,9 @@ fieldname.verticalspeed=Uf/Ab Gschwindikeit
 units.original=Original
 units.default=Default
 units.metres=Meter
-units.metres.short=
-units.feet=
-units.feet.short=
 units.kilometres=Kilometer
 units.kilometres.short=km
 units.kmh=km/h
-units.miles=
-units.miles.short=
-units.mph=
 units.metrespersec=m/s
 units.feetpersec=ft/s
 units.hours=Std
@@ -492,7 +531,10 @@ undo.cutandmove=Selektion mov
 undo.connectphoto=Föteli verbindä
 undo.disconnectphoto=Föteli trännä
 undo.correlate=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ä
 
 # Error messages
 error.save.dialogtitle=Fähle bim Speichere
@@ -501,6 +543,10 @@ error.save.failed=Speichere vom File fehlgschlage
 error.saveexif.filenotfound=Föteli File nöd gfunde
 error.saveexif.cannotoverwrite1=Föteli File
 error.saveexif.cannotoverwrite2=isch nöd schriibbar. Speichere na einer Kopie?
+error.saveexif.failed1=
+error.saveexif.failed2=von d Bilder han i nöd speichere könne
+error.saveexif.forced1=
+error.saveexif.forced2=von d Bilder han i müsse forziere
 error.load.dialogtitle=Fähle bim Lade
 error.load.noread=File cha nöd glase werde
 error.load.nopoints=Kei gültigi Information inem File gfunde
@@ -512,13 +558,16 @@ error.jpegload.nofilesfound=Kei Dateie gfunde
 error.jpegload.nojpegsfound=Kei Jpegs gfunde
 error.jpegload.noexiffound=Kei EXIF Information gfunde
 error.jpegload.nogpsfound=Kei GPS Information 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
 error.function.noop.title=Funktion hät gar nüüt gmacht
-error.rearrange.noop=Waypoints Reorganisierig hät kei Effäkt gha
+error.rearrange.noop=Punkte Reorganisierig hät kei Effäkt gha
 error.function.notavailable.title=Funktion nöd verfüegbar
 error.function.nojava3d=Sorry, d'Funktion brucht d Java3d Library,\nvo Sun.com erhältlech.
-error.3d=N Fähler isch mitere 3d Darstellig ufgtrete
+error.3d=N Fähler isch mitere 3d Darstellig ufgträte
 error.readme.notfound=Läs mi File nöd gfunde
 error.osmimage.dialogtitle=Fähle bim Bildli-Lade
 error.osmimage.failed=Map Bildli könne nöd glade werde.  Gits ne Internet Verbindig?
+error.language.wrongfile=Die uusgewählti Datei scheint kei Sproch-Datei für Prune z'sii
+error.convertnamestotimes.nonames=Kei Namen han könnet verwondlet werde
index 85f4024aab364c33ac36e795607fb991c3a9aa51..47ccdc68496c989551d40bc9c9c8eab09584d5f6 100644 (file)
@@ -7,7 +7,7 @@ menu.file.open=Open file
 menu.file.addphotos=Add photos
 menu.file.save=Save
 menu.file.exit=Exit
-menu.edit=Edit
+menu.track=Track
 menu.edit.undo=Undo
 menu.edit.clearundo=Clear undo list
 menu.edit.editpoint=Edit point
@@ -23,7 +23,8 @@ menu.edit.rearrange.start=All to start of file
 menu.edit.rearrange.end=All to end of file
 menu.edit.rearrange.nearest=Each to nearest track point
 menu.edit.cutandmove=Cut and move selection
-menu.select=Select
+menu.range=Range
+menu.point=Point
 menu.select.all=Select all
 menu.select.none=Select none
 menu.select.start=Set range start
@@ -39,8 +40,8 @@ menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo maps
+menu.view.browser.bing=Bing maps
 menu.settings=Settings
-menu.settings.showpace=Show pace in range display
 menu.help=Help
 # Popup menu for map
 menu.map.zoomin=Zoom in
@@ -54,11 +55,12 @@ menu.map.showscalebar=Show scalebar
 
 # Alt keys for menus
 altkey.menu.file=F
-altkey.menu.edit=E
-altkey.menu.select=S
+altkey.menu.range=R
+altkey.menu.track=T
+altkey.menu.point=P
 altkey.menu.view=V
-altkey.menu.photo=P
-altkey.menu.settings=T
+altkey.menu.photo=O
+altkey.menu.settings=S
 altkey.menu.help=H
 
 # Ctrl shortcuts for menu items
@@ -81,14 +83,24 @@ function.compress=Compress track
 function.addtimeoffset=Add time offset
 function.addaltitudeoffset=Add altitude offset
 function.findwaypoint=Find waypoint
+function.convertnamestotimes=Convert waypoint names to times
+function.pastecoordinates=Enter new coordinates
 function.charts=Charts
 function.show3d=Three-D view
 function.distances=Distances
+function.fullrangedetails=Full range details
 function.getgpsies=Get Gpsies tracks
+function.duplicatepoint=Duplicate point
 function.correlatephotos=Correlate photos
+function.rearrangephotos=Rearrange photos
+function.rotatephotoleft=Rotate photo left
+function.rotatephotoright=Rotate photo right
+function.ignoreexifthumb=Ignore exif thumbnail
 function.setmapbg=Set map background
 function.setkmzimagesize=Set KMZ image size
 function.setpaths=Set program paths
+function.setcolours=Set colours
+function.setlanguage=Set language
 function.help=Help
 function.showkeys=Show shortcut keys
 function.about=About Prune
@@ -129,6 +141,7 @@ dialog.gpsload.device=Device name
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=Load waypoints
 dialog.gpsload.gettracks=Load tracks
+dialog.gpsload.save=Save to file
 dialog.gpssend.sendwaypoints=Send waypoints
 dialog.gpssend.sendtracks=Send tracks
 dialog.gpssend.trackname=Track name
@@ -143,13 +156,16 @@ dialog.save.altitudeunits=Altitude units
 dialog.save.timestampformat=Timestamp format
 dialog.save.overwrite.title=File already exists
 dialog.save.overwrite.text=This file already exists. Are you sure you want to overwrite the file?
+dialog.save.notypesselected=No point types have been selected
 dialog.exportkml.text=Title for the data
 dialog.exportkml.altitude=Absolute altitudes (for aviation)
 dialog.exportkml.kmz=Compress to make kmz file
 dialog.exportkml.exportimages=Export image thumbnails to kmz
+dialog.exportkml.trackcolour=Track colour
 dialog.exportgpx.name=Name
 dialog.exportgpx.desc=Description
 dialog.exportgpx.includetimestamps=Include timestamps
+dialog.exportgpx.copysource=Copy source xml
 dialog.exportpov.text=Please enter the parameters for the POV export
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Camera X
@@ -163,6 +179,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.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?
 dialog.confirmcutandmove.title=Confirm cut and move
@@ -194,8 +211,6 @@ dialog.addtimeoffset.minutes=Minutes
 dialog.addtimeoffset.notimestamps=Cannot add a time offset as this selection doesn't contain any timestamp information
 dialog.findwaypoint.intro=Enter part of the waypoint name
 dialog.findwaypoint.search=Search
-dialog.connect.title=Connect photo to point
-dialog.connectphoto.clonepoint=This point already has a photo.\nDo you want to make a copy of the point?
 dialog.saveexif.title=Save Exif
 dialog.saveexif.intro=Select the photos to save using the checkboxes
 dialog.saveexif.nothingtosave=Coordinate data is unchanged, nothing to save
@@ -207,6 +222,7 @@ dialog.saveexif.photostatus.connected=Connected
 dialog.saveexif.photostatus.disconnected=Disconnected
 dialog.saveexif.photostatus.modified=Modified
 dialog.saveexif.overwrite=Overwrite files
+dialog.saveexif.force=Force despite minor errors
 dialog.charts.xaxis=X axis
 dialog.charts.yaxis=Y axes
 dialog.charts.output=Output
@@ -215,13 +231,13 @@ dialog.charts.svg=Output to SVG file
 dialog.charts.svgwidth=SVG width
 dialog.charts.svgheight=SVG height
 dialog.charts.needaltitudeortimes=The track must have either altitudes or time information in order to create charts
-dialog.charts.gnuplotpath=Path to gnuplot
 dialog.charts.gnuplotnotfound=Could not find gnuplot with the given path
 dialog.distances.intro=Straight line distances between points
 dialog.distances.column.from=From point
 dialog.distances.column.to=To point
 dialog.distances.currentpoint=Current point
 dialog.distances.toofewpoints=This function needs waypoints in order to calculate the distances between them
+dialog.fullrangedetails.intro=Here are the details for the selected range
 dialog.setmapbg.mapnik=Mapnik (default)
 dialog.setmapbg.osma=Osma
 dialog.setmapbg.cyclemap=Cyclemap
@@ -254,6 +270,12 @@ 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.rearrangephotos.desc=Select the destination and sort order of the photo points
+dialog.rearrangephotos.tostart=Move to start
+dialog.rearrangephotos.toend=Move to end
+dialog.rearrangephotos.nosort=Don't sort
+dialog.rearrangephotos.sortbyfilename=Sort by filename
+dialog.rearrangephotos.sortbytime=Sort by time
 dialog.compress.nonefound=No data points could be removed
 dialog.compress.duplicates.title=Duplicate removal
 dialog.compress.closepoints.title=Nearby point removal
@@ -263,6 +285,9 @@ dialog.compress.wackypoints.paramdesc=Distance factor
 dialog.compress.singletons.title=Singleton removal
 dialog.compress.singletons.paramdesc=Distance factor
 dialog.compress.summarylabel=Points to delete
+dialog.pastecoordinates.desc=Enter or paste the coordinates here
+dialog.pastecoordinates.coords=Coordinates
+dialog.pastecoordinates.nothingfound=Please check the coordinates and try again
 dialog.help.help=Please see\n http://activityworkshop.net/software/prune/\nfor more information and user guides.
 dialog.about.version=Version
 dialog.about.build=Build
@@ -304,6 +329,7 @@ dialog.saveconfig.desc=The following settings can be saved to a configuration fi
 dialog.saveconfig.prune.trackdirectory=Track directory
 dialog.saveconfig.prune.photodirectory=Photo directory
 dialog.saveconfig.prune.languagecode=Language code (EN)
+dialog.saveconfig.prune.languagefile=Language file
 dialog.saveconfig.prune.gpsdevice=GPS device
 dialog.saveconfig.prune.gpsformat=GPS format
 dialog.saveconfig.prune.povrayfont=Povray font
@@ -313,12 +339,31 @@ dialog.saveconfig.prune.gpsbabelpath=Path to gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Path to exiftool
 dialog.saveconfig.prune.mapserverindex=Index of map server
 dialog.saveconfig.prune.mapserverurl=URL of map server
-dialog.saveconfig.prune.showpace=Show pace
 dialog.saveconfig.prune.kmzimagewidth=KMZ image width
 dialog.saveconfig.prune.kmzimageheight=KMZ image height
+dialog.saveconfig.prune.colourscheme=Colour scheme
+dialog.saveconfig.prune.kmltrackcolour=KML track colour
 dialog.setpaths.intro=If you need to, you can choose the paths to the external applications:
 dialog.addaltitude.noaltitudes=The selected range does not contain altitudes
 dialog.addaltitude.desc=Altitude offset to add
+dialog.setcolours.intro=Click on a colour patch to change the colour
+dialog.setcolours.background=Background
+dialog.setcolours.borders=Borders
+dialog.setcolours.lines=Lines
+dialog.setcolours.primary=Primary
+dialog.setcolours.secondary=Secondary
+dialog.setcolours.point=Points
+dialog.setcolours.selection=Selection
+dialog.setcolours.text=Text
+dialog.colourchooser.title=Choose colour
+dialog.colourchooser.red=Red
+dialog.colourchooser.green=Green
+dialog.colourchooser.blue=Blue
+dialog.setlanguage.firstintro=You can either select one of the included languages,<p>or select a text file to use instead.
+dialog.setlanguage.secondintro=You need to save your settings and then<p>restart Prune to change the language.
+dialog.setlanguage.language=Language
+dialog.setlanguage.languagefile=Language file
+dialog.setlanguage.endmessage=Now save your settings and restart Prune\nfor the language change to take effect.
 
 # 3d window
 dialog.3d.title=Prune Three-d view
@@ -339,7 +384,9 @@ confirm.reverserange=Range reversed
 confirm.addtimeoffset=Time offset added
 confirm.addaltitudeoffset=Altitude offset added
 confirm.rearrangewaypoints=Waypoints rearranged
+confirm.rearrangephotos=Photos rearranged
 confirm.cutandmove=Selection moved
+confirm.convertnamestotimes=Waypoint names converted
 confirm.saveexif.ok1=Saved
 confirm.saveexif.ok2=photo files
 confirm.undo.single=operation undone
@@ -350,6 +397,7 @@ confirm.photo.connect=photo connected
 confirm.photo.disconnect=photo disconnected
 confirm.correlate.single=photo was correlated
 confirm.correlate.multi=photos were correlated
+confirm.rotatephoto=photo rotated
 confirm.createpoint=point created
 confirm.running=Running ...
 
@@ -371,6 +419,7 @@ button.yes=Yes
 button.no=No
 button.yestoall=Yes to all
 button.notoall=No to all
+button.select=Select
 button.selectall=Select all
 button.selectnone=Select none
 button.preview=Preview
@@ -378,6 +427,8 @@ button.load=Load
 button.guessfields=Guess fields
 button.showwebpage=Show webpage
 button.check=Check
+button.resettodefaults=Reset to defaults
+button.browse=Browse...
 
 # File types
 filetype.txt=TXT files
@@ -417,7 +468,9 @@ display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Ave speed
 details.range.avemovingspeed=Moving ave
+details.range.numsegments=Number of segments
 details.range.pace=Pace
+details.range.gradient=Gradient
 details.waypointsphotos.waypoints=Waypoints
 details.waypointsphotos.photos=Photos
 details.photodetails=Photo details
@@ -491,7 +544,10 @@ undo.cutandmove=move section
 undo.connectphoto=connect photo
 undo.disconnectphoto=disconnect photo
 undo.correlate=correlate photos
+undo.rearrangephotos=rearrange photos
+undo.rotatephoto=rotate photo
 undo.createpoint=create point
+undo.convertnamestotimes=convert names to times
 
 # Error messages
 error.save.dialogtitle=Error saving data
@@ -500,6 +556,10 @@ error.save.failed=Failed to save the data to file
 error.saveexif.filenotfound=Failed to find photo file
 error.saveexif.cannotoverwrite1=Photo file
 error.saveexif.cannotoverwrite2=is read-only and can't be overwritten. Write to copy?
+error.saveexif.failed1=Failed to save
+error.saveexif.failed2=of the images
+error.saveexif.forced1=
+error.saveexif.forced2=of the images required forcing
 error.load.dialogtitle=Error loading data
 error.load.noread=Cannot read file
 error.load.nopoints=No coordinate information found in the file
@@ -511,13 +571,16 @@ 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.gpsload.unknown=Unknown error
 error.undofailed.title=Undo failed
 error.undofailed.text=Failed to undo operation
 error.function.noop.title=Function had no effect
-error.rearrange.noop=Rearranging waypoints had no effect
+error.rearrange.noop=Rearranging points had no effect
 error.function.notavailable.title=Function not available
 error.function.nojava3d=This function requires the Java3d library,\navailable from Sun.com.
 error.3d=An error occurred with the 3d display
 error.readme.notfound=Readme file not found
 error.osmimage.dialogtitle=Error loading map images
 error.osmimage.failed=Failed to load map images. Please check internet connection.
+error.language.wrongfile=The selected file doesn't appear to be a language file for Prune
+error.convertnamestotimes.nonames=No names could be converted into times
index b8baa470d7e2c1a423fb39dad9c76bd03026ffd2..e610064ccb12233c91496da4991cc03b89e2aca7 100644 (file)
@@ -8,6 +8,7 @@ menu.file.addphotos=Cargar fotos
 menu.file.save=Guardar
 menu.file.exit=Salir
 menu.edit=Editar
+menu.track=Track
 menu.edit.undo=Deshacer
 menu.edit.clearundo=Despejar la lista de deshacer
 menu.edit.editpoint=Editar punto
@@ -24,6 +25,8 @@ menu.edit.rearrange.end=Ir al final
 menu.edit.rearrange.nearest=Ir al m\u00e1s pr\u00f3ximo
 menu.edit.cutandmove=Cortar y mover selecci\u00f3n
 menu.select=Seleccionar
+menu.range=Rango
+menu.point=Punto
 menu.select.all=Seleccionar todo
 menu.select.none=No seleccionar nada
 menu.select.start=Fijar comienzo
@@ -38,9 +41,9 @@ menu.view.browser=Mapa en un navegador
 menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
-menu.view.browser.yahoo=mapas Yahoo
+menu.view.browser.yahoo=Mapas Yahoo
+menu.view.browser.bing=Mapas Bing
 menu.settings=Preferencias
-menu.settings.showpace=
 menu.help=Ayuda
 # Popup menu for map
 menu.map.zoomin=Ampliar zoom
@@ -50,12 +53,15 @@ menu.map.newpoint=Crear uno punto nuevo
 menu.map.connect=Conectar puntos de track
 menu.map.autopan=Posicionar autom\u00e1ticamente
 menu.map.showmap=Mostrar el mapa
-menu.map.showscalebar=
+menu.map.showscalebar=Mostrar barra de escala
 
 # Alt keys for menus
 altkey.menu.file=A
 altkey.menu.edit=E
 altkey.menu.select=S
+altkey.menu.track=T
+altkey.menu.range=R
+altkey.menu.point=U
 altkey.menu.view=V
 altkey.menu.photo=F
 altkey.menu.settings=P
@@ -80,17 +86,27 @@ function.editwaypointname=Editar nombre de waypoint
 function.compress=Comprimir track
 function.addtimeoffset=A\u00f1adir compensar tiempo
 function.addaltitudeoffset=A\u00f1adir compensar altitud
+function.convertnamestotimes=Convertir los nombres de los "waypoints" a tiempo
 function.findwaypoint=Buscar waypoint
+function.pastecoordinates=Insertar nuevas coordenadas
 function.charts=Diagramas
 function.show3d=Mostrar en 3-D
 function.distances=Distancias
-function.getgpsies=Bajar ruta de Gpsies
-function.correlatephotos=Correlacionar fotos
+function.fullrangedetails=Detalles adicionales de rango
 function.setmapbg=Configurar fondo de mapa
 function.setkmzimagesize=Configurar tama\u00f1os de las im\u00e1genes KMZ
 function.setpaths=Configurar rutas del programas
+function.getgpsies=Bajar ruta de Gpsies
+function.duplicatepoint=Duplicar punto
+function.setcolours=Establecer color
+function.setlanguage=Establecer lenguaje
+function.correlatephotos=Correlacionar fotos
+function.rearrangephotos=Reacomodar fotos
+function.rotatephotoleft=Girar a la izquierda
+function.rotatephotoright=Girar a la derecha
+function.ignoreexifthumb=Ignorar "thumbnail" de exif
 function.help=Ayuda
-function.showkeys=
+function.showkeys=Mostrar teclas o combinaciones de atajo
 function.about=Acerca de Prune
 function.checkversion=Buscar una nueva versi\u00f3n
 function.saveconfig=Guardar preferencias
@@ -125,12 +141,13 @@ 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=gpsbabel program no encontrado. Desea continuar?
-dialog.gpsload.device=
+dialog.gpsload.device=Dispositivo
 dialog.gpsload.format=Formato
 dialog.gpsload.getwaypoints=Cargar waypoints
 dialog.gpsload.gettracks=Cargar tracks
-dialog.gpssend.sendwaypoints=
-dialog.gpssend.sendtracks=
+dialog.gpsload.save=Salvar al archivo
+dialog.gpssend.sendwaypoints=enviar "waypoints"
+dialog.gpssend.sendtracks=enviar tracks
 dialog.gpssend.trackname=Nombre del track
 dialog.saveoptions.title=Guardar archivo
 dialog.save.fieldstosave=Campos a guardar
@@ -143,13 +160,16 @@ dialog.save.altitudeunits=Unidades de las altitudes
 dialog.save.timestampformat=Format del tiempo
 dialog.save.overwrite.title=El archivo ya existe
 dialog.save.overwrite.text=El archivo ya existe, desea 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)
 dialog.exportkml.kmz=Comprimir al archivo kmz
 dialog.exportkml.exportimages=Exportar fotos al kmz
+dialog.exportkml.trackcolour=Color del track
 dialog.exportgpx.name=Nombre
 dialog.exportgpx.desc=Descripci\u00f3n
 dialog.exportgpx.includetimestamps=Tiempo tambien
+dialog.exportgpx.copysource=Copiar la fuente
 dialog.exportpov.text=Introdzca los Parametros para exportar
 dialog.exportpov.font=Fuente
 dialog.exportpov.camerax=C\u00e1mara X
@@ -159,13 +179,13 @@ dialog.exportpov.modelstyle=Estilo
 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.pointtype.desc=
-dialog.pointtype.track=
-dialog.pointtype.waypoint=
-dialog.pointtype.photo=
+dialog.pointtype.desc=Salvar los siguientes tipos de puntos:
+dialog.pointtype.track=Puntos de track
+dialog.pointtype.photo=Puntos de foto
+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. Esta seguro que desea invertir esta secci\u00f3n?
-dialog.confirmcutandmove.title=Confirmar ...
+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 la .... Esta seguro que desea ... esta secci\u00f3n?
 dialog.interpolate.title=Interpolar puntos
 dialog.interpolate.parameter.text=N\u00famero de los puntos a insertar entre los puntos elegidos
@@ -186,16 +206,16 @@ dialog.pointnameedit.name=Nombre de waypoint
 dialog.pointnameedit.uppercase=May\u00fasculas
 dialog.pointnameedit.lowercase=min\u00fasculas
 dialog.pointnameedit.sentencecase=Mezcla
-dialog.addtimeoffset.add=
-dialog.addtimeoffset.subtract=
+dialog.addtimeoffset.add=a\u00f1adir tiempo
+dialog.addtimeoffset.subtract=sustraer tiempo
 dialog.addtimeoffset.days=Dias
 dialog.addtimeoffset.hours=Horas
 dialog.addtimeoffset.minutes=Minutos
-dialog.addtimeoffset.notimestamps=
-dialog.findwaypoint.intro=
-dialog.findwaypoint.search=
+dialog.addtimeoffset.notimestamps=No se puede a\u00f1adir tiempo de puesta a esta selecci\u00f3n si \u00e9sta no contiene ninguna informaci\u00f3n de "timestamp"
+dialog.findwaypoint.intro=Ingresar parte del nombre de "waypoint"
+dialog.findwaypoint.search=Buscar
 dialog.connect.title=Conectar foto
-dialog.connectphoto.clonepoint=
+dialog.connectphoto.clonepoint=Este punto ya tiene una foto.\n Quisiera usted hacer una copia de este?
 dialog.saveexif.title=Guardar Exif
 dialog.saveexif.intro=Seleccione fotos a guardar
 dialog.saveexif.nothingtosave=Coordenadas no modificadas, nada que guardar
@@ -207,31 +227,30 @@ dialog.saveexif.photostatus.connected=Conectada
 dialog.saveexif.photostatus.disconnected=Desconectada
 dialog.saveexif.photostatus.modified=Modificada
 dialog.saveexif.overwrite=Sobreescribirlar archivos?
+dialog.saveexif.force=Fuerza despreciar errores menores
 dialog.charts.xaxis=Eje de abscisas
 dialog.charts.yaxis=Eje vertical
 dialog.charts.output=Resultado
-dialog.charts.screen=
-dialog.charts.svg=
-dialog.charts.svgwidth=
-dialog.charts.svgheight=
-dialog.charts.needaltitudeortimes=
-dialog.charts.gnuplotpath=
-dialog.charts.gnuplotnotfound=
-dialog.distances.intro=
+dialog.charts.screen=Salida a pantalla
+dialog.charts.svg=Salida a archivo SVG
+dialog.charts.svgwidth=Ancho de SVG
+dialog.charts.svgheight=Alto de SVG
+dialog.charts.needaltitudeortimes=La pista debe tener altitudes o informaci\u00f3n de tiempo en orden para crear las tablas
+dialog.charts.gnuplotnotfound=No pudo ser encontrado gnuplot con el camino o la direcci\u00f3n proporcionada
+dialog.distances.intro=L\u00edneas rectas entre puntos
 dialog.distances.column.from=De punto
 dialog.distances.column.to=Al punto
 dialog.distances.currentpoint=Punto actual
-dialog.distances.toofewpoints=
-dialog.setmapbg.mapnik=Mapnik (por defecto)
-dialog.setmapbg.osma=
-dialog.setmapbg.cyclemap=
+dialog.distances.toofewpoints=Esta funcion necesita "waypoints" para poder calcular las distancias entre ellos
+dialog.fullrangedetails.intro=Aqui estan los detalles para la selecci\u00f3n de rangos
+dialog.setmapbg.mapnik=Mapnik (predeterminado)
 dialog.setmapbg.other=Otro
-dialog.setmapbg.server=
-dialog.gpsies.column.name=Nombre
-dialog.gpsies.column.length=
+dialog.setmapbg.server=Direcci\u00f3n URL del servidor
+dialog.gpsies.column.name=Nombre del track
+dialog.gpsies.column.length=Distancia
 dialog.gpsies.description=Descripci\u00f3n
-dialog.gpsies.nodescription=Ning\u00fan descripci\u00f3n
-dialog.gpsies.nonefound=
+dialog.gpsies.nodescription=Sin Descripci\u00f3n
+dialog.gpsies.nonefound=No se encontraron pistas
 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.\nEst\u00e1 seguro de que desea continuar?
 dialog.correlate.photoselect.intro=Seleccione una de estas fotos correlacionadas para usar como margen de tiempo
@@ -254,18 +273,24 @@ 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.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
+dialog.rearrangephotos.nosort=No sortear
+dialog.rearrangephotos.sortbyfilename=Sortear por nombre del archivo
+dialog.rearrangephotos.sortbytime=Sortear por tiempo
 dialog.compress.nonefound=Ning\u00fan punto eliminado
+dialog.compress.closepoints.title=remover puntos cercanos
+dialog.compress.wackypoints.paramdesc=Factor distancia
+dialog.compress.singletons.paramdesc=Factor distancia
 dialog.compress.duplicates.title=Eliminar duplicados
-dialog.compress.closepoints.title=
-dialog.compress.closepoints.paramdesc=
-dialog.compress.wackypoints.title=
-dialog.compress.wackypoints.paramdesc=
-dialog.compress.singletons.title=
-dialog.compress.singletons.paramdesc=
-dialog.compress.summarylabel=
+dialog.compress.summarylabel=Puntos para eliminar
+dialog.pastecoordinates.desc=Ingresar o pegar las coordenadas aqu\u00ed
+dialog.pastecoordinates.coords=Coordenadas
+dialog.pastecoordinates.nothingfound=Por favor verificar las coordenadas e intentar nuevamente
 dialog.help.help=Por favor, ver\n http://activityworkshop.net/software/prune/\npara m\u00e1s informaci\u00f3n y gu\u00edas del usuario.
 dialog.about.version=Versi\u00f3n
-dialog.about.build=Construir
+dialog.about.build=Construcci\u00f3n
 dialog.about.summarytext1=Prune es un programa para cargar, mostrar y editar datos de receptores GPS.
 dialog.about.summarytext2=Distribuido bajo el GNU GPL para uso libre y gratuito.<br>Se permite (y se anima) la copia, redistribuci\u00f3n y modificaci\u00f3n de acuerdo<br>a las condiciones incluidas en el archivo <code>licence.txt</code>.
 dialog.about.summarytext3=Por favor, ver <code style="font-weight:bold">http://activityworkshop.net/</code> para m\u00e1s informaci\u00f3n y gu\u00edas del usuario.
@@ -281,7 +306,7 @@ dialog.about.systeminfo.gpsbabel=Gpsbabel instalado
 dialog.about.systeminfo.gnuplot=Gnuplot instalado
 dialog.about.yes=Si
 dialog.about.no=No
-dialog.about.credits=Credits
+dialog.about.credits=Creditos
 dialog.about.credits.code=El c\u00f3digo de Prune fue escrito por
 dialog.about.credits.exifcode=El c\u00f3digo Exif por
 dialog.about.credits.icons=Algunos iconos se tomaron de
@@ -291,34 +316,52 @@ dialog.about.credits.devtools=Herramientas de desarrollo
 dialog.about.credits.othertools=Otras herramientas
 dialog.about.credits.thanks=Gracias a
 dialog.about.readme=Readme
-dialog.checkversion.error=
-dialog.checkversion.uptodate=
-dialog.checkversion.newversion1=
-dialog.checkversion.newversion2=
-dialog.checkversion.releasedate1=
-dialog.checkversion.releasedate2=
-dialog.checkversion.download=To download the new version, go to http://activityworkshop.net/software/prune/download.html.
-dialog.keys.intro=
-dialog.keys.keylist=
-dialog.saveconfig.desc=
-dialog.saveconfig.prune.trackdirectory=
-dialog.saveconfig.prune.photodirectory=
-dialog.saveconfig.prune.languagecode=
-dialog.saveconfig.prune.gpsdevice=
-dialog.saveconfig.prune.gpsformat=
+dialog.checkversion.error=El numero de versi\u00f3n no pudo ser verificada.\n Por favor verificar la conexi\u00f3n de internet
+dialog.checkversion.uptodate=Esta usted utilizando la \u00faltima versi\u00f3n de Prune
+dialog.checkversion.newversion1=Una nueva versi\u00f3n de Prune est\u00e1 disponible! La \u00daltima versi\u00f3n es ahora versi\u00f3n
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=La nueva versi\u00f3n fue lanzada en
+dialog.checkversion.releasedate2=.
+dialog.keys.intro=Usted puede usar el siguiente atajo en lugar de usar el rat\u00f3n
+dialog.saveconfig.desc=La siguiente configuraci\u00f3n puede ser salvada en un archivo de configuraci\u00f3n
+dialog.saveconfig.prune.trackdirectory=Directorio de pista
+dialog.saveconfig.prune.photodirectory=Directorio de foto
+dialog.saveconfig.prune.languagecode=C\u00f3digo de lenguaje (ES)
+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=
-dialog.saveconfig.prune.gnuplotpath=
-dialog.saveconfig.prune.gpsbabelpath=
-dialog.saveconfig.prune.exiftoolpath=
-dialog.saveconfig.prune.mapserverindex=
-dialog.saveconfig.prune.mapserverurl=
-dialog.saveconfig.prune.showpace=
-dialog.saveconfig.prune.kmzimagewidth=
-dialog.saveconfig.prune.kmzimageheight=
-dialog.setpaths.intro=
-dialog.addaltitude.noaltitudes=
+dialog.saveconfig.prune.metricunits=Usar unidades m\u00e9tricas?
+dialog.saveconfig.prune.gnuplotpath=Camino a gnuplot
+dialog.saveconfig.prune.gpsbabelpath=Camino a gpsbabel
+dialog.saveconfig.prune.exiftoolpath=Camino a exiftool
+dialog.saveconfig.prune.mapserverindex=\u00cdndice de mapa del servidor
+dialog.saveconfig.prune.mapserverurl=Direcci\u00f3n URL de mapa del servidor
+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.kmltrackcolour=Color de pista de KML
+dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas
+dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes
 dialog.addaltitude.desc=
+dialog.setcolours.intro=Clickear sobre una placa de color para cambiar el color
+dialog.setcolours.background=Fondo
+dialog.setcolours.borders=Bordes
+dialog.setcolours.lines=L\u00edneas
+dialog.setcolours.primary=Primario
+dialog.setcolours.secondary=Secundario
+dialog.setcolours.point=Puntos
+dialog.setcolours.selection=Selecci\u00f3n
+dialog.setcolours.text=Texto
+dialog.colourchooser.title=Elegir color
+dialog.colourchooser.red=Rojo
+dialog.colourchooser.green=Verde
+dialog.colourchooser.blue=Azul
+dialog.setlanguage.firstintro=Puede usted seleccionar algunos de los lenguajes incluidos,<p>o puede en lugar de esto seleccionar un archivo de texto
+dialog.setlanguage.secondintro=Usted necesita salvar su configuraci\u00f3n y luego<p>reiniciar Prune para cambiar el lenguaje
+dialog.setlanguage.language=Lenguaje
+dialog.setlanguage.languagefile=Archivo de lenguaje
+dialog.setlanguage.endmessage=Ahora salve su configuraci\u00f3n y reinicie Prune\npara que los cambios tomen efecto.
 
 # 3d window
 dialog.3d.title=Prune vista 3-D
@@ -336,10 +379,9 @@ confirm.deletepoint.multi=puntos eliminados
 confirm.point.edit=Punto editado
 confirm.mergetracksegments=Segmentos unidos
 confirm.reverserange=Rango invertido
-confirm.addtimeoffset=
-confirm.addaltitudeoffset=
 confirm.rearrangewaypoints=Waypoints reorganizados
-confirm.cutandmove=
+confirm.rearrangephotos=Fotos reacomodadas
+confirm.cutandmove=Mover Selecci\u00f3n
 confirm.saveexif.ok1=Guardando
 confirm.saveexif.ok2=fotos
 confirm.undo.single=operaci\u00f3n no realizada
@@ -351,9 +393,10 @@ confirm.photo.disconnect=Foto desconectado
 confirm.correlate.single=foto fue correlada
 confirm.correlate.multi=fotos fueron correladas
 confirm.createpoint=punto creado
+confirm.rotatephoto=Foto rotada
 confirm.running=Trabajando ...
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=Aceptar
 button.back=Anterior
 button.next=Siguiente
@@ -371,6 +414,7 @@ button.yes=Si
 button.no=No
 button.yestoall=Si por todo
 button.notoall=No por todo
+button.select=Seleccionar
 button.selectall=Seleccionar todo
 button.selectnone=Seleccionar nada
 button.preview=Previsualizaci\u00f3n
@@ -378,6 +422,7 @@ button.load=Cargar
 button.guessfields=Adivinar campos
 button.showwebpage=Mostrar p\u00e1gina web
 button.check=Verificar
+button.resettodefaults=Restablecer valores a los predeterminados
 
 # File types
 filetype.txt=Archivos TXT
@@ -389,7 +434,7 @@ filetype.gpx=Archivos GPX
 filetype.pov=Archivos POV
 filetype.svg=Archivos SVG
 
-# Display components
+# Display components || These are all for the side panels showing point/range details
 display.nodata=Ning\u00fan dato cargado
 display.noaltitudes=Los datos del track no incluyen altitudes
 details.trackdetails=Detalles del track
@@ -416,15 +461,16 @@ display.range.time.mins=m
 display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=Velocidad media
-details.range.avemovingspeed=
-details.range.pace=
+details.range.avemovingspeed=Moviendo promedio
+details.range.numsegments=N\u00famero de segmentos
+details.range.gradient=Gradiente
 details.waypointsphotos.waypoints=Waypoints
 details.waypointsphotos.photos=Fotos
 details.photodetails=Detalles del Foto
 details.nophoto=Ninguna foto seleccionada
 details.photo.loading=Cargando
 details.photo.connected=Conectada
-map.overzoom=
+map.overzoom=No existen mapas disponibles con este nivel de enfoque
 
 # Field names
 fieldname.latitude=Latitud
@@ -438,7 +484,7 @@ fieldname.newsegment=Segmento
 fieldname.custom=Personalizado
 fieldname.prefix=Campo
 fieldname.distance=Distancia
-fieldname.movingdistance=
+fieldname.movingdistance=Distancia en movimiento
 fieldname.duration=Duraci\u00f3n
 fieldname.speed=Velocidad
 fieldname.verticalspeed=Velocidad vertical
@@ -473,7 +519,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=O
 
-# Undo operations
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
 undo.load=cargar datos
 undo.loadphotos=cargar fotos
 undo.editpoint=editar punto
@@ -484,13 +530,11 @@ undo.compress=comprimir track
 undo.insert=insertar puntos
 undo.reverse=invertir rango
 undo.mergetracksegments=unir los segmentos de track
-undo.addtimeoffset=
-undo.addaltitudeoffset=
 undo.rearrangewaypoints=reordenar waypoints
-undo.cutandmove=
 undo.connectphoto=conectar foto
 undo.disconnectphoto=desconectar foto
 undo.correlate=correlacionar fotos
+undo.rotatephoto=girar foto
 undo.createpoint=crear punto
 
 # Error messages
@@ -500,6 +544,8 @@ error.save.failed=Fallo al guardar datos al archivo
 error.saveexif.filenotfound=Archivo no encontrado
 error.saveexif.cannotoverwrite1=No se puede guardar
 error.saveexif.cannotoverwrite2=. Guardar a una copia?
+error.saveexif.failed1=Fall\u00f3 al salvar
+error.saveexif.failed2=de las im\u00e1genes
 error.load.dialogtitle=Fallo al cargar datos
 error.load.noread=No se puede leer el fichero
 error.load.nopoints=Ninguna informaci\u00f3n coordenadas encontrada
@@ -511,13 +557,16 @@ error.jpegload.nofilesfound=Ning\u00fan archivo encontrado
 error.jpegload.nojpegsfound=Ning\u00fan archivo jpeg encontrado
 error.jpegload.noexiffound=Ninguna informaci\u00f3n EXIF encontrada
 error.jpegload.nogpsfound=Ninguna informaci\u00f3n GPS encontrada
+error.gpsload.unknown=Error desconocido
 error.undofailed.title=Fallo al deshacer
 error.undofailed.text=No ha sido posible deshacer la operaci\u00f3n
 error.function.noop.title=La funci\u00f3n no se ha efectuado
-error.rearrange.noop=Reordenaci\u00f3n de waypoints no se ha efectuado
+error.rearrange.noop=Reordenaci\u00f3n de puntos no se ha efectuado
 error.function.notavailable.title=Funci\u00f3n no disponible
 error.function.nojava3d=Esta funci\u00f3n requiere la librer\u00eda Java3d, disponible en Sun.com.
 error.3d=Ha ocurrido un error con la funci\u00f3n 3-D
 error.readme.notfound=Archivo readme no encontrado
 error.osmimage.dialogtitle=Error al cargar el mapa
 error.osmimage.failed=Imposible cargar el mapa. Por favor, compruebe la conexi\u00f3n a internet.
+error.language.wrongfile=El archivo seleccionado no parece ser un archivo de lenguaje para Prune
+error.convertnamestotimes.nonames=Los nombres no pudieron ser convertidos en tiempos
index a3e16ec22b9ff921320bb681a8ce642e2b39ec0f..b5d9e276e1e499f51bb81c2b21e62315b888f1f8 100644 (file)
@@ -8,6 +8,7 @@ menu.file.addphotos=Ajouter photos
 menu.file.save=Enregistrer
 menu.file.exit=Quitter
 menu.edit=\u00c9dition
+menu.track=Trace
 menu.edit.undo=Annuler
 menu.edit.clearundo=Purger la liste d'annulation
 menu.edit.editpoint=Editer le point
@@ -24,6 +25,8 @@ menu.edit.rearrange.end=Tous \u00e0 la fin du fichier
 menu.edit.rearrange.nearest=Chacun au point de trace le plus proche
 menu.edit.cutandmove=Couper et bouger la s\u00e9lection
 menu.select=S\u00e9lectionner
+menu.range=\u00c9tendue
+menu.point=Point
 menu.select.all=Tout s\u00e9lectionner
 menu.select.none=Rien s\u00e9lectionner
 menu.select.start=D\u00e9finir le d\u00e9but de l'\u00e9tendue
@@ -39,6 +42,7 @@ menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Yahoo maps
+menu.view.browser.bing=Cartes dans Bing
 menu.settings=Pr\u00e9f\u00e9rences
 menu.settings.showpace=Montrer allure dans les d\u00e9tails
 menu.help=Aide
@@ -56,8 +60,11 @@ menu.map.showscalebar=Montrer l'echelle
 altkey.menu.file=F
 altkey.menu.edit=E
 altkey.menu.select=S
+altkey.menu.track=T
+altkey.menu.range=E
+altkey.menu.point=P
 altkey.menu.view=A
-altkey.menu.photo=P
+altkey.menu.photo=H
 altkey.menu.settings=R
 altkey.menu.help=I
 
@@ -80,15 +87,25 @@ function.editwaypointname=Editer le nom du waypoint
 function.compress=Compresser la trace
 function.addtimeoffset=Ajouter un d\u00e9calage d'horaire
 function.addaltitudeoffset=Ajouter un d\u00e9calage d'altitude
+function.convertnamestotimes=Convertir les noms de waypoints en horodatages
 function.findwaypoint=Trouver un waypoint
+function.pastecoordinates=Coller les coordonn\u00e9es
 function.charts=Graphiques
 function.show3d=Montrer en 3D
 function.distances=Distances
-function.getgpsies=R\u00e9cup\u00e9rer les traces Gpsies
-function.correlatephotos=Corr\u00e9ler les photos
+function.fullrangedetails=Montrer tous les d\u00e9tails
 function.setmapbg=D\u00e9finir le fond de carte
 function.setkmzimagesize=D\u00e9finir la taille de l'image KMZ
 function.setpaths=D\u00e9finir les chemins des programmes
+function.getgpsies=R\u00e9cup\u00e9rer les traces Gpsies
+function.duplicatepoint=Duppliquer le point
+function.setcolours=R\u00e9gler les couleurs
+function.setlanguage=R\u00e9gler la langue
+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.ignoreexifthumb=Ignorer l\u2019aper\u00e7u Exif
 function.help=Aide
 function.showkeys=Montrer les raccourcis clavier
 function.about=À propos de Prune
@@ -129,6 +146,7 @@ dialog.gpsload.device=Chemin du p\u00e9riph\u00e9rique
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=T\u00e9l\u00e9charger les waypoints
 dialog.gpsload.gettracks=T\u00e9l\u00e9charger les traces
+dialog.gpsload.save=Enregistrer dans un fichier
 dialog.gpssend.sendwaypoints=Envoyer les waypoints
 dialog.gpssend.sendtracks=Envoyer les traces
 dialog.gpssend.trackname=Nom de trace
@@ -143,13 +161,16 @@ dialog.save.altitudeunits=Unit\u00e9s d'altitude
 dialog.save.timestampformat=Format de l'heure
 dialog.save.overwrite.title=Le fichier existe d\u00e9j\u00e0
 dialog.save.overwrite.text=Ce fichier existe d\u00e9j\u00e0. \u00cates-vous s\u00fbr de vouloir \u00e9craser ce fichier ?
+dialog.save.notypesselected=Aucun type de point n\u2019a \u00e9t\u00e9 s\u00e9lectionn\u00e9
 dialog.exportkml.text=Titre pour les donn\u00e9es
 dialog.exportkml.altitude=Absolues altitudes (pour aviation)
 dialog.exportkml.kmz=Compresser au format kmz
 dialog.exportkml.exportimages=Exporter les vignettes au format kmz
+dialog.exportkml.trackcolour=Couleur de la trace
 dialog.exportgpx.name=Nom
 dialog.exportgpx.desc=L\u00e9gende
 dialog.exportgpx.includetimestamps=Inclure l'heure pour chaque point
+dialog.exportgpx.copysource=Copier le XML source
 dialog.exportpov.text=Entrez les param\u00e8tres pour l'export POV
 dialog.exportpov.font=Police
 dialog.exportpov.camerax=Cam\u00e9ra X
@@ -163,6 +184,7 @@ dialog.pointtype.desc=Sauvegarder ces types de points:
 dialog.pointtype.track=Points de la trace
 dialog.pointtype.waypoint=Waypoints
 dialog.pointtype.photo=Points de photos
+dialog.pointtype.selection=Uniquement la s\u00e9lection
 dialog.confirmreversetrack.title=Confirmer l'inversion
 dialog.confirmreversetrack.text=Cette trace contient des informations temporelles qui seront d\u00e9sordonn\u00e9es apr\u00e8s une inversion.\n\u00cates-vous s\u00fbr de vouloir inverser cette section ?
 dialog.confirmcutandmove.title=Confirmer le d\u00e9placement
@@ -207,6 +229,7 @@ dialog.saveexif.photostatus.connected=Connect\u00e9
 dialog.saveexif.photostatus.disconnected=D\u00e9connect\u00e9
 dialog.saveexif.photostatus.modified=Modifi\u00e9
 dialog.saveexif.overwrite=Ecraser les fichiers
+dialog.saveexif.force=Ignorer les erreurs mineures
 dialog.charts.xaxis=Axe des x
 dialog.charts.yaxis=Axe des y
 dialog.charts.output=Sortie
@@ -215,13 +238,13 @@ dialog.charts.svg=Sortie dans un fichier SVG
 dialog.charts.svgwidth=Largeur de l'image SVG
 dialog.charts.svgheight=Hauteur de l'image SVG
 dialog.charts.needaltitudeortimes=La trace ne peut g\u00e9n\u00e9rer des graphiques sans disposer d'altitudes ou d'indications temporelles
-dialog.charts.gnuplotpath=Chemin gnuplot
 dialog.charts.gnuplotnotfound=Gnuplot est introuvable dans le chemin indiqu\u00e9
 dialog.distances.intro=Distances \u00e0 vol d'oiseau entre des points
 dialog.distances.column.from=Du point
 dialog.distances.column.to=Vers le point
 dialog.distances.currentpoint=Point courant
 dialog.distances.toofewpoints=Cette fonction a besoin de waypoints pour calculer les distances entre eux
+dialog.fullrangedetails.intro=Voici les d\u00e9tails pour l\u2019\u00e9tendue s\u00e9lectionn\u00e9e
 dialog.setmapbg.mapnik=Mapnik (d\u00e9faut)
 dialog.setmapbg.osma=Osma
 dialog.setmapbg.cyclemap=Cyclemap
@@ -254,21 +277,30 @@ dialog.correlate.options.nodistancelimit=Pas de limite de distance
 dialog.correlate.options.distancelimit=Limite de distance
 dialog.correlate.options.correlate=Corr\u00e9ler
 dialog.correlate.alloutsiderange=Les photos ne correspondent pas \u00e0 la plage de temps de la trace, aucune ne peut \u00eatre corr\u00e9l\u00e9e.\nEssayez de modifier le d\u00e9calage ou de corr\u00e9ler manuellement au moins une photo.
+dialog.rearrangephotos.desc=Choisissez la destination et l\u2019ordre des points des photos
+dialog.rearrangephotos.tostart=Aller au d\u00e9but
+dialog.rearrangephotos.toend=Aller \u00e0 la fin
+dialog.rearrangephotos.nosort=Ne pas trier
+dialog.rearrangephotos.sortbyfilename=Trier par nom de fichier
+dialog.rearrangephotos.sortbytime=Trier par horodatage
 dialog.compress.nonefound=Pas de donn\u00e9es \u00e0 effacer
-dialog.compress.duplicates.title=Suppression des doublons
 dialog.compress.closepoints.title=Suppression des points voisins
 dialog.compress.closepoints.paramdesc=Taille du voisinage
 dialog.compress.wackypoints.title=Suppression des points anormaux
 dialog.compress.wackypoints.paramdesc=Distance
 dialog.compress.singletons.title=Suppression des points isol\u00e9s
 dialog.compress.singletons.paramdesc=Distance
+dialog.compress.duplicates.title=Suppression des doublons
 dialog.compress.summarylabel=Points \u00e0 supprimer
+dialog.pastecoordinates.desc=Entrez ou collez les coordonn\u00e9es ici
+dialog.pastecoordinates.coords=Coordonn\u00e9es
+dialog.pastecoordinates.nothingfound=V\u00e9rifier les coordonn\u00e9es et essayez \u00e0 nouveau
 dialog.help.help=Consultez la page\n http://activityworkshop.net/software/prune/\npour plus de d\u00e9tails et des manuels utilisateur.
 dialog.about.version=Version
 dialog.about.build=Build
 dialog.about.summarytext1=Prune est un programme pour charger, afficher et \u00e9diter des donn\u00e9es de r\u00e9cepteurs GPS.
 dialog.about.summarytext2=Distribu\u00e9 sous license Gnu GPL pour un usage et une am\u00e9lioration libres, ouverts et mondiaux.<br>La copie, la redistribution et la modification sont autoris\u00e9es et encourag\u00e9es<br>selon les conditions d\u00e9taill\u00e9es dans le fichier <code>license.txt</code> inclus.
-dialog.about.summarytext3=Consultez la page <code style="font-weight:bold">http://activityworkshop.net/</code> pour plus de d\u00e9tails et des manuels utilisateur.
+dialog.about.summarytext3=Consultez la page <code style``=``"font-weight:bold">http://activityworkshop.net/</code> pour plus de d\u00e9tails et des manuels utilisateur.
 dialog.about.languages=Langues disponibles
 dialog.about.translatedby=Texte en fran\u00e7ais par Petrovsk et theYinYeti.
 dialog.about.systeminfo=Info Syst\u00e8me
@@ -304,6 +336,7 @@ dialog.saveconfig.desc=Les param\u00e8tres suivants peuvent \u00eatre sauvegard\
 dialog.saveconfig.prune.trackdirectory=Dossier des traces
 dialog.saveconfig.prune.photodirectory=Dossier des Photos
 dialog.saveconfig.prune.languagecode=Code langue (FR)
+dialog.saveconfig.prune.languagefile=Fichier de langue
 dialog.saveconfig.prune.gpsdevice=Chemin du p\u00e9riph\u00e9rique GPS
 dialog.saveconfig.prune.gpsformat=Format GPS
 dialog.saveconfig.prune.povrayfont=Police povray
@@ -316,9 +349,26 @@ dialog.saveconfig.prune.mapserverurl=URL du serveur de carte
 dialog.saveconfig.prune.showpace=Montrer l'allure
 dialog.saveconfig.prune.kmzimagewidth=Largeur de l'image KMZ
 dialog.saveconfig.prune.kmzimageheight=Hauteur de l'image KMZ
+dialog.saveconfig.prune.colourscheme=Mod\u00e8le de couleurs
+dialog.saveconfig.prune.kmltrackcolour=Couleur de la trace KML
 dialog.setpaths.intro=Si vous le souhaitez, vous pouvez d\u00e9finir les chemins des applications externes:
 dialog.addaltitude.noaltitudes=L'\u00e9tendue s\u00e9lectionn\u00e9e de contient pas d'altitudes
 dialog.addaltitude.desc=D\u00e9callage d'altitude \u00e0 ajouter
+dialog.setcolours.intro=Cliquez sur une couleur pour la changer
+dialog.setcolours.background=Arri\u00e8re-plan
+dialog.setcolours.borders=Bordures
+dialog.setcolours.lines=Lignes
+dialog.setcolours.primary=Primaires
+dialog.setcolours.secondary=Secondaires
+dialog.setcolours.point=Points
+dialog.setcolours.selection=S\u00e9lection
+dialog.setcolours.text=Texte
+dialog.colourchooser.title=Choisissez la couleur
+dialog.colourchooser.red=Rouge
+dialog.colourchooser.green=Vert
+dialog.colourchooser.blue=Bleu
+dialog.setlanguage.language=Langue
+dialog.setlanguage.languagefile=Fichier de langue
 
 # 3d window
 dialog.3d.title=Vue 3D de Prune
@@ -339,7 +389,9 @@ confirm.reverserange=Etendue invers\u00e9e
 confirm.addtimeoffset=D\u00e9calage ajout\u00e9
 confirm.addaltitudeoffset=D\u00e9calage d'altitude ajout\u00e9
 confirm.rearrangewaypoints=Waypoints r\u00e9arrang\u00e9s
+confirm.rearrangephotos=Photos r\u00e9arrang\u00e9es
 confirm.cutandmove=S\u00e9lection d\u00e9plac\u00e9e
+confirm.convertnamestotimes=Noms de waypoints convertis
 confirm.saveexif.ok1=Enregistrement de
 confirm.saveexif.ok2=fichiers photo
 confirm.undo.single=op\u00e9ration annul\u00e9e
@@ -351,9 +403,10 @@ 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.createpoint=Point cr\u00e9\u00e9
+confirm.rotatephoto=Photo tourn\u00e9e
 confirm.running=En cours...
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=OK
 button.back=Retour
 button.next=Prochain
@@ -371,6 +424,7 @@ button.yes=Oui
 button.no=Non
 button.yestoall=Oui pour tous
 button.notoall=Non pour tous
+button.select=S\u00e9lectionner
 button.selectall=Tout s\u00e9lectionner
 button.selectnone=Ne rien s\u00e9lectionner
 button.preview=Aper\u00e7u
@@ -378,6 +432,7 @@ button.load=T\u00e9l\u00e9charger
 button.guessfields=Deviner les champs
 button.showwebpage=Montrer page web
 button.check=V\u00e9rifier
+button.resettodefaults=Revenir aux valeurs par d\u00e9faut
 
 # File types
 filetype.txt=Fichiers TXT
@@ -389,7 +444,7 @@ filetype.gpx=Fichiers GPX
 filetype.pov=Fichiers POV
 filetype.svg=Fichiers SVG
 
-# Display components
+# Display components || These are all for the side panels showing point/range details
 display.nodata=Pas de donn\u00e9es charg\u00e9es
 display.noaltitudes=La trace ne comporte pas d'information d'altitude
 details.trackdetails=D\u00e9tails de la trace
@@ -417,7 +472,9 @@ display.range.time.hours=h
 display.range.time.days=j
 details.range.avespeed=Vitesse moyenne
 details.range.avemovingspeed=Moyenne continue
+details.range.numsegments=Nombre de segments
 details.range.pace=Allure
+details.range.gradient=Pente
 details.waypointsphotos.waypoints=Waypoints
 details.waypointsphotos.photos=Photos
 details.photodetails=D\u00e9tails de la photo
@@ -473,7 +530,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=O
 
-# Undo operations
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
 undo.load=charger les donn\u00e9es
 undo.loadphotos=charger les photos
 undo.editpoint=\u00e9diter le point
@@ -491,7 +548,10 @@ 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.rearrangephotos=R\u00e9arranger les photos
 undo.createpoint=ajouter un point
+undo.rotatephoto=Tourner la photo
+undo.convertnamestotimes=Convertir les noms en points
 
 # Error messages
 error.save.dialogtitle=Erreur \u00e0 l'enregistrement des donn\u00e9es
@@ -500,6 +560,10 @@ error.save.failed=Echec de l'enregistrement des donn\u00e9es dans le fichier
 error.saveexif.filenotfound=Fichier photo introuvable
 error.saveexif.cannotoverwrite1=Le fichier photo
 error.saveexif.cannotoverwrite2=est en lecture seule et ne peut pas \u00eatre \u00e9craser. Enregistrer sur une copie ?
+error.saveexif.failed1=\u00c9chec de la sauvegarde de
+error.saveexif.failed2=images
+error.saveexif.forced1=Enregistrement forc\u00e9 pour
+error.saveexif.forced2=images
 error.load.dialogtitle=Erreur au chargement des donn\u00e9es
 error.load.noread=Fichier illisible
 error.load.nopoints=Aucune coordonn\u00e9e trouv\u00e9e dans le fichier
@@ -511,10 +575,11 @@ 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.gpsload.unknown=Erreur inconnue
 error.undofailed.title=Echec de l'annulation
 error.undofailed.text=Echec de l'op\u00e9ration d'annulation
 error.function.noop.title=Fonction sans effet
-error.rearrange.noop=R\u00e9arrangement des waypoints sans effet
+error.rearrange.noop=R\u00e9arrangement des points sans effet
 error.function.notavailable.title=Function non-disponible
 error.function.nojava3d=Cette fonction n\u00e9cessite la librairie Java3d,\ndisponible sur Sun.com.
 error.3d=Un probl\u00e8me est survenu avec l'affichage 3D
index d31df3fa5366b4b774d1be8a55da7705b8c3684e..d76fa2e1ae845757c107c8d37cadfef088a484f6 100644 (file)
@@ -7,13 +7,16 @@ menu.file.open=Buka
 menu.file.addphotos=Muat foto
 menu.file.save=Simpan
 menu.file.exit=Keluar
-menu.edit=Ubah
+#menu.edit=Ubah
+menu.track=Track
 menu.edit.undo=Batal
 
 menu.edit.editpoint=Perbaiki titik
 menu.edit.deletepoint=Hapus titik
 menu.edit.deleterange=Hapus jarak
-menu.select=Pilih
+#menu.select=Pilih
+menu.range=Jangkauan
+menu.point=Titik
 menu.select.all=Pilih semua
 menu.select.none=Tidak memilih
 menu.photo=Foto
@@ -34,8 +37,11 @@ menu.map.showmap=Tampilkan peta
 
 # Alt keys for menus
 altkey.menu.file=B
-altkey.menu.edit=U
-altkey.menu.select=P
+#altkey.menu.edit=U
+#altkey.menu.select=P
+altkey.menu.track=T
+altkey.menu.range=J
+altkey.menu.point=K
 altkey.menu.view=L
 altkey.menu.photo=F
 altkey.menu.settings=G
@@ -85,6 +91,7 @@ button.next=Lanjut
 button.close=Tutup
 button.yes=Ya
 button.no=Tidak
+button.select=Pilih
 button.guessfields=Deteksi otomatis
 
 # File types
index a219fabd062228c9c6ef6bffa3d1753b1885e681..0eb5179b41ddede3c80b116709a179b84dcf6876 100644 (file)
@@ -8,6 +8,7 @@ menu.file.addphotos=Aggiungi foto
 menu.file.save=Salva
 menu.file.exit=Esci
 menu.edit=Edita
+menu.track=Traccia
 menu.edit.undo=Annulla
 menu.edit.clearundo=Cancella lista ultime modifiche
 menu.edit.editpoint=Edita punto
@@ -24,6 +25,8 @@ menu.edit.rearrange.end=Tutti alla fine del file
 menu.edit.rearrange.nearest=Sul punto pi\u00f9 vicino
 menu.edit.cutandmove=Taglia e muovi la selezione
 menu.select=Seleziona
+menu.range=Serie
+menu.point=Punto
 menu.select.all=Seleziona tutto
 menu.select.none=Deseleziona tutto
 menu.select.start=Imposta inizio serie
@@ -39,8 +42,9 @@ menu.view.browser.google=Google maps
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=mappe Yahoo
+menu.view.browser.bing=mappe Bing
 menu.settings=Preferenze
-menu.settings.showpace=
+menu.settings.showpace=Mostra passo nel display serie
 menu.help=Aiuto
 # Popup menu for map
 menu.map.zoomin=Zoom +
@@ -50,15 +54,18 @@ menu.map.newpoint=Crea punto nuovo
 menu.map.connect=Aggancia ai punti
 menu.map.autopan=Autopan
 menu.map.showmap=Mostra sulla mappa
-menu.map.showscalebar=
+menu.map.showscalebar=Mostra scala
 
 # Alt keys for menus
 altkey.menu.file=F
 altkey.menu.edit=E
 altkey.menu.select=S
+altkey.menu.track=T
+altkey.menu.range=S
+altkey.menu.point=P
 altkey.menu.view=V
 altkey.menu.photo=O
-altkey.menu.settings=P
+altkey.menu.settings=R
 altkey.menu.help=A
 
 # Ctrl shortcuts for menu items
@@ -79,21 +86,21 @@ function.exportpov=Esporta in POV
 function.editwaypointname=Edita nome waypoint
 function.compress=Comprimi la traccia
 function.addtimeoffset=Aggiungi uno scarto temporale
-function.addaltitudeoffset=
-function.findwaypoint=
+function.addaltitudeoffset=Aggiungi uno scarto di altitudine
+function.findwaypoint=Trova waypoint
 function.charts=Diagrammi
 function.show3d=Mostra in 3D
 function.distances=Mostra distanze
-function.getgpsies=
-function.correlatephotos=Correla le foto
 function.setmapbg=Configura sfondo mappa
-function.setkmzimagesize=
-function.setpaths=
+function.setkmzimagesize=Configura dimensione immagine KMZ
+function.setpaths=Configura percorsi programmi
+function.getgpsies=Ottieni traccie da Gpsies
+function.correlatephotos=Correla le foto
 function.help=Aiuto
-function.showkeys=
+function.showkeys=Visualizza tasti scelta rapida
 function.about=Informazioni su Prune
 function.checkversion=Controlla gli aggiornamenti
-function.saveconfig=
+function.saveconfig=Salva configurazione
 
 # Dialogs
 dialog.exit.confirm.title=Esci da Prune
@@ -159,10 +166,10 @@ dialog.exportpov.modelstyle=Stile del modello
 dialog.exportpov.ballsandsticks=Palle e bacchette
 dialog.exportpov.tubesandwalls=Tubi e pareti
 dialog.exportpov.warningtracksize=Questa traccia ha un elevato numero di punti, e Java3D pu\u00f2 non essere in grado di visualizzarli.\nSei sicuro di voler continuare?
-dialog.pointtype.desc=
-dialog.pointtype.track=
-dialog.pointtype.waypoint=
-dialog.pointtype.photo=
+dialog.pointtype.desc=Salva i tipi di punti seguenti:
+dialog.pointtype.track=Punti traccia
+dialog.pointtype.waypoint=Waypoints
+dialog.pointtype.photo=Punti foto
 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?
 dialog.confirmcutandmove.title=Conferma il taglio e lo spostamento
@@ -192,8 +199,8 @@ dialog.addtimeoffset.days=Giorni
 dialog.addtimeoffset.hours=Ore
 dialog.addtimeoffset.minutes=Minuti
 dialog.addtimeoffset.notimestamps=Non posso aggiungere uno scarto temporale a questa selezione perch\u00e9 non contiene informazioni temporali
-dialog.findwaypoint.intro=
-dialog.findwaypoint.search=
+dialog.findwaypoint.intro=Inserisci parte del nome del waypoint
+dialog.findwaypoint.search=Cerca
 dialog.connect.title=Collega la foto al punto
 dialog.connectphoto.clonepoint=Questo punto ha gi\u00e0 una foto collegata.\nVuoi fare una copia del punto?
 dialog.saveexif.title=Salva Exif
@@ -215,7 +222,6 @@ dialog.charts.svg=Output a file SVG
 dialog.charts.svgwidth=larghezza SVG
 dialog.charts.svgheight=altezza SVG
 dialog.charts.needaltitudeortimes=La traccia necessita di dati altitudine o tempo per creare diagrammi
-dialog.charts.gnuplotpath=Path gnuplot
 dialog.charts.gnuplotnotfound=Gnuplot non trovato nel path indicato
 dialog.distances.intro=Distanze dirette tra punti
 dialog.distances.column.from=Dal punto
@@ -227,11 +233,10 @@ dialog.setmapbg.osma=Osma
 dialog.setmapbg.cyclemap=Mappa ciclistica
 dialog.setmapbg.other=Altro
 dialog.setmapbg.server=URL server
-dialog.gpsies.column.name=
-dialog.gpsies.column.length=
-dialog.gpsies.description=
-dialog.gpsies.nodescription=
-dialog.gpsies.nonefound=
+dialog.gpsies.column.name=Nome traccia
+dialog.gpsies.column.length=Lunghezza
+dialog.gpsies.description=Descrizione
+dialog.gpsies.nodescription=Senza descrizione
 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
@@ -255,13 +260,13 @@ 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.compress.nonefound=Nessun punto rimosso
-dialog.compress.duplicates.title=Cancella duplicati
 dialog.compress.closepoints.title=Cancella punti vicini
 dialog.compress.closepoints.paramdesc=Fattore vicinanza
 dialog.compress.wackypoints.title=Cancella punti strani
 dialog.compress.wackypoints.paramdesc=Fattore distanza
 dialog.compress.singletons.title=Cancella solitari
 dialog.compress.singletons.paramdesc=Fattore distanza
+dialog.compress.duplicates.title=Cancella duplicati
 dialog.compress.summarylabel=Punti da cancellare
 dialog.help.help=Per favore vedi\n http://activityworkshop.net/software/prune/\nper maggiori informazioni e per la guida utente.
 dialog.about.version=Versione
@@ -298,31 +303,31 @@ dialog.checkversion.newversion2=.
 dialog.checkversion.releasedate1=Questa nuova versione \u00e8 stata rilasciata il
 dialog.checkversion.releasedate2=.
 dialog.checkversion.download=Per scaricare la nuova versione vai a http://activityworkshop.net/software/prune/download.html.
-dialog.keys.intro=
-dialog.keys.keylist=
-dialog.saveconfig.desc=
-dialog.saveconfig.prune.trackdirectory=
-dialog.saveconfig.prune.photodirectory=
-dialog.saveconfig.prune.languagecode=
+dialog.keys.intro=Puoi utilizzare i seguenti tast di scelta rapida al posto del mouse
+dialog.keys.keylist=<table><tr><td>Tasti freccia</td><td>Muovi mappa destra, sinistra, su, giu'</td></tr><tr><td>Ctrl + freccia destra, sinistra</td><td>Selezione punto successivo o precedente</td></tr><tr><td>Ctrl + freccia su, giu'</td><td>Zoom in o out</td></tr><tr><td>Del</td><td>Cancella punto attuale</td></tr></table>
+dialog.saveconfig.desc=Le configurazioni seguenti possono essere salvati in un file di configurazione:
+dialog.saveconfig.prune.trackdirectory=Cartella traccie
+dialog.saveconfig.prune.photodirectory=Cartella foto
+dialog.saveconfig.prune.languagecode=Codice linguaggio (EN)
 dialog.saveconfig.prune.gpsdevice=Nome del Dispositivo GPS
 dialog.saveconfig.prune.gpsformat=Formato GPS
 dialog.saveconfig.prune.povrayfont=Font povray
-dialog.saveconfig.prune.metricunits=
+dialog.saveconfig.prune.metricunits=Utilizza unita' metrica?
 dialog.saveconfig.prune.gnuplotpath=Path gnuplot
 dialog.saveconfig.prune.gpsbabelpath=Path gpsbabel
 dialog.saveconfig.prune.exiftoolpath=Path exiftool
-dialog.saveconfig.prune.mapserverindex=
-dialog.saveconfig.prune.mapserverurl=
-dialog.saveconfig.prune.showpace=
-dialog.saveconfig.prune.kmzimagewidth=
-dialog.saveconfig.prune.kmzimageheight=
-dialog.setpaths.intro=
-dialog.addaltitude.noaltitudes=
-dialog.addaltitude.desc=
+dialog.saveconfig.prune.mapserverindex=Indice server mappe
+dialog.saveconfig.prune.mapserverurl=URL del server mappe
+dialog.saveconfig.prune.showpace=Mostra passo
+dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ
+dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ
+dialog.setpaths.intro=Se necessario, puoi indicare il percorso delle applicazioni esterne:
+dialog.addaltitude.noaltitudes=L'intervallo selezionato non contiene altitudini
+dialog.addaltitude.desc=Scarto altitudine da aggiungere
 
 # 3d window
 dialog.3d.title=Visione Prune in 3D
-dialog.3d.altitudecap=Minimo intervallo si altitudine
+dialog.3d.altitudecap=Intervallo altitudine minimo
 dialog.3dlines.title=Griglia di Prune
 dialog.3dlines.empty=Nessuna griglia mostrata!
 dialog.3dlines.intro=Queste sono le linee della griglia per la visione 3D
@@ -337,7 +342,7 @@ confirm.point.edit=punto editato
 confirm.mergetracksegments=segmeni di traccia uniti
 confirm.reverserange=Intervallo invertito
 confirm.addtimeoffset=Scarto temporale aggiunto
-confirm.addaltitudeoffset=
+confirm.addaltitudeoffset=Scarto altitudine aggiunto
 confirm.rearrangewaypoints=Waypoint riorganizzati
 confirm.cutandmove=Selezione spostata
 confirm.saveexif.ok1=Salvato
@@ -351,9 +356,9 @@ confirm.photo.disconnect=foto scollegata
 confirm.correlate.single=foto era correlata
 confirm.correlate.multi=foto erano correlate
 confirm.createpoint=punto creato
-confirm.running=
+confirm.running=Operazione in corso...
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=OK
 button.back=Precedente
 button.next=Prossimo
@@ -371,13 +376,14 @@ button.yes=S\u00ec
 button.no=No
 button.yestoall=Si a tutto
 button.notoall=No a tutto
+button.select=Seleziona
 button.selectall=Seleziona tutto
 button.selectnone=Deseleziona tutto
 button.preview=Anteprima
 button.load=Carica
 button.guessfields=Campi soluzione
 button.showwebpage=Mostra pagina
-button.check=
+button.check=Controlla
 
 # File types
 filetype.txt=File TXT
@@ -389,7 +395,7 @@ filetype.gpx=File GPX
 filetype.pov=File POV
 filetype.svg=File SVG
 
-# Display components
+# Display components || These are all for the side panels showing point/range details
 display.nodata=Nessun dato caricato
 display.noaltitudes=I dati della traccia non includono l'altitudine
 details.trackdetails=Dettagli della traccia
@@ -417,7 +423,7 @@ display.range.time.hours=h
 display.range.time.days=g
 details.range.avespeed=Velocit\u00e0 media
 details.range.avemovingspeed=Velocit\u00e0 media in movimento
-details.range.pace=
+details.range.pace=Passo
 details.waypointsphotos.waypoints=Waypoint
 details.waypointsphotos.photos=Foto
 details.photodetails=Dettagli foto
@@ -473,7 +479,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=O
 
-# Undo operations
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
 undo.load=carica dati
 undo.loadphotos=carica foto
 undo.editpoint=edita punto
@@ -485,7 +491,7 @@ undo.insert=inserisci punti
 undo.reverse=inverti l'intervallo
 undo.mergetracksegments=unisci segmenti traccia
 undo.addtimeoffset=aggiungi scarto temporale
-undo.addaltitudeoffset=
+undo.addaltitudeoffset=aggiungi scarto altitudine
 undo.rearrangewaypoints=riorganizza waypoint
 undo.cutandmove=muovi selezione
 undo.connectphoto=collega foto
@@ -520,4 +526,4 @@ error.function.nojava3d=Questa funzione richiede la libreria Java3d,\ndisponibil
 error.3d=\u00c8 avvenuto un errore nella visualizzazione 3D
 error.readme.notfound=Non ho trovato il file Leggimi
 error.osmimage.dialogtitle=Errore nel caricamento nelle immagini della mappa
-error.osmimage.failed=Impossibile caricare le immagini della mappa. Per favore verifica la connessione a internet.
\ No newline at end of file
+error.osmimage.failed=Impossibile caricare le immagini della mappa. Per favore verifica la connessione a internet.
diff --git a/tim/prune/lang/prune-texts_ja.properties b/tim/prune/lang/prune-texts_ja.properties
new file mode 100644 (file)
index 0000000..a54fa2e
--- /dev/null
@@ -0,0 +1,573 @@
+# Text entries for the Prune application
+# Japanese entries as extra
+
+# Menu entries
+menu.file=\u30d5\u30a1\u30a4\u30eb
+menu.file.open=\u30d5\u30a1\u30a4\u30eb\u3092\u958b\u304f
+menu.file.addphotos=\u5199\u771f\u3092\u8ffd\u52a0
+menu.file.save=\u4fdd\u5b58
+menu.file.exit=\u7d42\u4e86
+menu.edit=\u7de8\u96c6
+menu.track=\u30c8\u30e9\u30c3\u30af
+menu.edit.undo=\u30a2\u30f3\u30c9\u30a5
+menu.edit.clearundo=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
+menu.edit.editpoint=\u70b9\u3092\u7de8\u96c6
+menu.edit.deletepoint=\u70b9\u3092\u524a\u9664
+menu.edit.deleterange=\u7bc4\u56f2\u3092\u524a\u9664
+menu.edit.deletemarked=\u5370\u306e\u4ed8\u3044\u305f\u70b9\u3092\u524a\u9664
+menu.edit.interpolate=\u88dc\u5b8c
+menu.edit.average=\u9078\u629e\u7bc4\u56f2\u3092\u5e73\u5747\u5316
+menu.edit.reverse=\u7bc4\u56f2\u3092\u53cd\u8ee2
+menu.edit.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u3092\u7d71\u5408
+menu.edit.rearrange=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u4e26\u3079\u66ff\u3048
+menu.edit.rearrange.start=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u59cb\u70b9\u306b
+menu.edit.rearrange.end=\u5168\u3066\u3092\u30d5\u30a1\u30a4\u30eb\u306e\u7d42\u70b9\u306b
+menu.edit.rearrange.nearest=\u305d\u308c\u305e\u308c\u3092\u6700\u8fd1\u306e\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b
+menu.edit.cutandmove=\u9078\u629e\u7bc4\u56f2\u3092\u79fb\u52d5
+menu.select=\u9078\u629e
+menu.range=\u7bc4\u56f2
+menu.point=\u70b9
+menu.select.all=\u5168\u3066\u9078\u629e
+menu.select.none=\u9078\u629e\u89e3\u9664
+menu.select.start=\u958b\u59cb\u70b9\u3092\u7f6e\u304f
+menu.select.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
+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
+menu.view.browser.openstreetmap=OpenStreetMap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Yahoo \u5730\u56f3
+menu.view.browser.bing=Bing \u5730\u56f3
+menu.settings=\u8a2d\u5b9a
+menu.settings.showpace=\u8868\u793a\u7bc4\u56f2\u306e\u901f\u5ea6\u3092\u8868\u793a
+menu.help=\u30d8\u30eb\u30d7
+# Popup menu for map
+menu.map.zoomin=\u62e1\u5927
+menu.map.zoomout=\u7e2e\u5c0f
+menu.map.zoomfull=\u6700\u5927\u62e1\u5927
+menu.map.newpoint=\u70b9\u3092\u4f5c\u308b
+menu.map.connect=\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306b\u63a5\u7d9a
+menu.map.autopan=\u81ea\u52d5\u79fb\u52d5
+menu.map.showmap=\u5730\u56f3\u3092\u8868\u793a
+menu.map.showscalebar=\u7e2e\u5c3a\u8868\u793a
+
+# Functions
+function.loadfromgps=GPS\u304b\u3089\u30c7\u30fc\u30bf\u3092\u8aad\u3080
+function.sendtogps=GPS\u3078\u4fdd\u5b58
+function.exportkml=KML\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
+function.exportgpx=GPX\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
+function.exportpov=POV\u306b\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
+function.editwaypointname=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u3092\u7de8\u96c6
+function.compress=\u30c8\u30e9\u30c3\u30af\u3092\u5727\u7e2e
+function.addtimeoffset=\u6642\u9593\u306e\u504f\u4f4d\u3092\u52a0\u3048\u308b
+function.addaltitudeoffset=\u9ad8\u5ea6\u306b\u504f\u4f4d\u3092\u52a0\u3048\u308b
+function.convertnamestotimes=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d\u3092\u6642\u9593\u306b\u5909\u63db
+function.findwaypoint=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u63a2\u3059
+function.pastecoordinates=\u65b0\u3057\u3044\u5ea7\u6a19\u3092\u5165\u529b
+function.charts=\u9ad8\u5ea6\u901f\u5ea6\u30c1\u30e3\u30fc\u30c8
+function.show3d=3-D\u30d3\u30e5\u30fc
+function.distances=\u8ddd\u96e2
+function.fullrangedetails=\u5168\u7bc4\u56f2\u8a73\u7d30
+function.setmapbg=\u80cc\u666f\u5730\u56f3
+function.setkmzimagesize=KML \u30a4\u30e1\u30fc\u30b8\u30b5\u30a4\u30ba
+function.setpaths=\u5916\u90e8\u30d7\u30ed\u30b0\u30e9\u30e0\u30d1\u30b9\u3092\u8a2d\u5b9a
+function.getgpsies=Gpsies\u30c8\u30e9\u30c3\u30af\u3092\u5f97\u308b
+function.duplicatepoint=\u91cd\u8907\u70b9
+function.setcolours=\u8272\u3092\u8a2d\u5b9a
+function.setlanguage=\u8a00\u8a9e\u8a2d\u5b9a
+function.correlatephotos=\u5199\u771f\u3068\u95a2\u9023\u3055\u305b\u308b
+function.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u76f4\u3057
+function.rotatephotoleft=\u5199\u771f\u3092\u5de6\u306b\u56de\u3059
+function.rotatephotoright=\u5199\u771f\u3092\u53f3\u306b\u56de\u3059
+function.ignoreexifthumb=EXIF\u30b5\u30e0\u30cd\u30a4\u30eb\u3092\u7121\u8996
+function.help=\u30d8\u30eb\u30d7
+function.showkeys=\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u8868\u793a
+function.about=Prune \u306b\u3064\u3044\u3066
+function.checkversion=\u65b0\u3057\u3044\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u8868\u793a
+function.saveconfig=\u8a2d\u5b9a\u3092\u4fdd\u5b58
+
+# Dialogs
+dialog.exit.confirm.title=Prune \u3092\u7d42\u4e86
+dialog.exit.confirm.text=\u30c7\u30fc\u30bf\u306f\u4fdd\u5b58\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u672c\u5f53\u306b\u7d42\u4e86\u3057\u307e\u3059\u304b\uff1f
+dialog.openappend.title=\u65e2\u5b58\u306e\u30c7\u30fc\u30bf\u3092\u52a0\u3048\u308b
+dialog.openappend.text=\u3053\u306e\u30c7\u30fc\u30bf\u306b\u65e2\u5b58\u306e\u30c7\u30fc\u30bf\u30fc\u3092\u8aad\u307f\u8fbc\u3080\u3002
+dialog.deletepoint.title=\u70b9\u3092\u524a\u9664
+dialog.deletepoint.deletephoto=\u3053\u306e\u70b9\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u5199\u771f\u3092\u53d6\u308a\u9664\u304d\u307e\u3059\u304b\uff1f
+dialog.deletephoto.title=\u5199\u771f\u3092\u524a\u9664
+dialog.deletephoto.deletepoint=\u3053\u306e\u5199\u771f\u306b\u63a5\u7d9a\u3055\u308c\u3066\u3044\u308b\u5199\u771f\u3092\u53d6\u308a\u9664\u304d\u307e\u3059\u304b\uff1f
+dialog.openoptions.title=\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u958b\u304f
+dialog.openoptions.filesnippet=\u30d5\u30a1\u30a4\u30eb\u3092\u5c55\u958b
+dialog.load.table.field=\u30d5\u30a3\u30fc\u30eb\u30c9
+dialog.load.table.datatype=\u30c7\u30fc\u30bf\u30bf\u30a4\u30d7
+dialog.load.table.description=\u8a18\u8ff0
+dialog.delimiter.label=\u30d5\u30a3\u30fc\u30eb\u30c9\u533a\u5207\u308a
+dialog.delimiter.comma=\u30b3\u30f3\u30de ,
+dialog.delimiter.tab=\u30bf\u30d6
+dialog.delimiter.space=\u30b9\u30da\u30fc\u30b9
+dialog.delimiter.semicolon=\u30bb\u30df\u30b3\u30ed\u30f3 ;
+dialog.delimiter.other=\u305d\u306e\u4ed6
+dialog.openoptions.deliminfo.records=\u8a18\u9332\uff0c
+dialog.openoptions.deliminfo.fields=\u30d5\u30a3\u30fc\u30eb\u30c9
+dialog.openoptions.deliminfo.norecords=\u8a18\u9332\u306a\u3057
+dialog.openoptions.altitudeunits=\u9ad8\u5ea6\u5358\u4f4d
+dialog.jpegload.subdirectories=\u30b5\u30d6\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u542b\u3081\u308b
+dialog.jpegload.loadjpegswithoutcoords=\u5ea7\u6a19\u3092\u542b\u307e\u306a\u3044\u5199\u771f\u3092\u542b\u3081\u308b
+dialog.jpegload.loadjpegsoutsidearea=\u73fe\u5728\u898b\u3066\u3044\u308b\u5730\u57df\u5916\u306e\u5199\u771f\u3092\u542b\u3081\u308b
+dialog.jpegload.progress.title=\u5199\u771f\u3092\u8aad\u307f\u8fbc\u307f
+dialog.jpegload.progress=\u691c\u7d22\u4e2d\u3067\u3059\u3002\u3057\u3070\u3057\u304a\u5f85\u3061\u304f\u3060\u3055\u3044\u3002
+dialog.gpsload.nogpsbabel=Gpsbabel\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u7d9a\u3051\u307e\u3059\u304b\uff1f
+dialog.gpsload.device=GPS\u3068\u7d99\u304c\u3063\u3066\u3044\u308b\u30c7\u30d0\u30a4\u30b9\u540d
+dialog.gpsload.format=GPS \u30c7\u30d0\u30a4\u30b9\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
+dialog.gpsload.getwaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u8aad\u307f\u8fbc\u3080
+dialog.gpsload.gettracks=\u30c8\u30e9\u30c3\u30af\u3092\u8aad\u307f\u8fbc\u3080
+dialog.gpsload.save=\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58
+dialog.gpssend.sendwaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u9001\u308b
+dialog.gpssend.sendtracks=\u30c8\u30e9\u30c3\u30af\u3092\u9001\u308b
+dialog.gpssend.trackname=\u30c8\u30e9\u30c3\u30af\u540d
+dialog.saveoptions.title=\u4fdd\u5b58
+dialog.save.fieldstosave=\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u4fdd\u5b58
+dialog.save.table.field=\u30d5\u30a3\u30fc\u30eb\u30c9
+dialog.save.table.hasdata=\u30c7\u30fc\u30bf\u304c\u3042\u308b
+dialog.save.table.save=\u4fdd\u5b58
+dialog.save.headerrow=\u30d8\u30c3\u30c0\u30fc\u884c\u3092\u51fa\u529b
+dialog.save.coordinateunits=\u5ea7\u6a19\u5358\u4f4d
+dialog.save.altitudeunits=\u9ad8\u5ea6\u5358\u4f4d
+dialog.save.timestampformat=\u6642\u9593\u8a18\u9332\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
+dialog.save.overwrite.title=\u30d5\u30a1\u30a4\u30eb\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002
+dialog.save.overwrite.text=\u30d5\u30a1\u30a4\u30eb\u304c\u65e2\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u4e0a\u66f8\u304d\u3057\u307e\u3059\u304b\uff1f
+dialog.save.notypesselected=\u70b9\u306e\u7a2e\u985e\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002
+dialog.exportkml.text=\u30c7\u30fc\u30bf\u306e\u30bf\u30a4\u30c8\u30eb
+dialog.exportkml.altitude=\u7d76\u5bfe\u9ad8\u5ea6\uff08\u822a\u7a7a\u7528\uff09
+dialog.exportkml.kmz=KMZ\u30d5\u30a1\u30a4\u30eb\u3092\u5727\u7e2e
+dialog.exportkml.exportimages=KMZ\u3078\u753b\u50cf\u306e\u7e2e\u5c0f\u7248\u3092\u4fdd\u5b58
+dialog.exportkml.trackcolour=\u30c8\u30e9\u30c3\u30af\u306e\u8272
+dialog.exportgpx.name=\u540d\u524d
+dialog.exportgpx.desc=\u8a18\u8ff0
+dialog.exportgpx.includetimestamps=\u6642\u9593\u8a18\u9332\u3092\u542b\u3080
+dialog.exportgpx.copysource=XML\u30bd\u30fc\u30b9\u3092\u30b3\u30d4\u30fc
+dialog.exportpov.text=POV\u7528\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.exportpov.font=\u30d5\u30a9\u30f3\u30c8
+dialog.exportpov.camerax=\u30ab\u30e1\u30e9 X
+dialog.exportpov.cameray=\u30ab\u30e1\u30e9 Y
+dialog.exportpov.cameraz=\u30ab\u30e1\u30e9 Z
+dialog.exportpov.modelstyle=\u30e2\u30c7\u30eb\u30b9\u30bf\u30a4\u30eb
+dialog.exportpov.ballsandsticks=\u7403\u3068\u68d2
+dialog.exportpov.tubesandwalls=\u7ba1\u3068\u58c1
+dialog.exportpov.warningtracksize=\u3053\u306e\u30c8\u30e9\u30c3\u30af\u306f\u975e\u5e38\u306b\u591a\u304f\u306e\u70b9\u304c\u3042\u308b\u306e\u3067\u3001Java3D \u3067\u306f\u8868\u793a\u3057\u5207\u308c\u306a\u3044\u3068\u601d\u308f\u308c\u307e\u3059\u3002\n\u7d9a\u3051\u307e\u3059\u304b\uff1f
+dialog.pointtype.desc=\u6b21\u306e\u70b9\u3092\u4fdd\u5b58\u3057\u307e\u3059\uff1a
+dialog.pointtype.track=\u30c8\u30e9\u30c3\u30af\u30dd\u30a4\u30f3\u30c8
+dialog.pointtype.waypoint=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8
+dialog.pointtype.photo=\u5199\u771f\u70b9
+dialog.pointtype.selection=\u9078\u629e\u7bc4\u56f2\u306e\u307f
+dialog.confirmreversetrack.title=\u53cd\u8ee2\u306e\u78ba\u8a8d
+dialog.confirmreversetrack.text=\u3053\u306e\u30c8\u30e9\u30c3\u30af\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3093\u3067\u3044\u3066\u3001\u53cd\u8ee2\u3059\u308b\u3068\u306a\u304f\u306a\u308a\u307e\u3059\u3002\n\u672c\u5f53\u306b\u53cd\u8ee2\u3055\u305b\u307e\u3059\u304b\uff1f
+dialog.confirmcutandmove.title=\u5207\u308a\u53d6\u308a\u3068\u79fb\u52d5\u306e\u78ba\u8a8d
+dialog.confirmcutandmove.text=\u3053\u306e\u30c8\u30e9\u30c3\u30af\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u542b\u3093\u3067\u3044\u3066\u3001\u79fb\u52d5\u3059\u308b\u3068\u306a\u304f\u306a\u308a\u307e\u3059\u3002\n\u672c\u5f53\u306b\u53cd\u8ee2\u3055\u305b\u307e\u3059\u304b\uff1f
+dialog.interpolate.title=\u70b9\u3092\u88dc\u5b8c\u3059\u308b
+dialog.interpolate.parameter.text=\u9078\u629e\u3057\u305f\u70b9\u306e\u9593\u306b\u633f\u5165\u3055\u308c\u308b\u70b9\u306e\u6570
+dialog.undo.title=\u4f5c\u696d\u3092\u30a2\u30f3\u30c9\u30a5
+dialog.undo.pretext=\u30a2\u30f3\u30c9\u30a5\u3059\u308b\u4f5c\u696d\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.undo.none.title=\u30a2\u30f3\u30c9\u30a5\u3067\u304d\u307e\u305b\u3093
+dialog.undo.none.text=\u30a2\u30f3\u30c9\u30a5\u3067\u304d\u308b\u4f5c\u696d\u753b\u3042\u308a\u307e\u305b\u3093!
+dialog.clearundo.title=\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3059\u308b
+dialog.clearundo.text=\u672c\u5f53\u306b\u30a2\u30f3\u30c9\u30a5\u30ea\u30b9\u30c8\u3092\u7a7a\u306b\u3057\u307e\u3059\u304b?\n\u5168\u3066\u306e\u30a2\u30f3\u30c9\u30a5\u60c5\u5831\u304c\u5931\u308f\u308c\u307e\u3059\u3002
+dialog.pointedit.title=\u70b9\u3092\u7de8\u96c6
+dialog.pointedit.text=\u7de8\u96c6\u3059\u308b\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u9078\u629e\u3057\u3001'\u7de8\u96c6' \u30dc\u30bf\u30f3\u3067\u5024\u3092\u7de8\u96c6\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.pointedit.table.field=\u30d5\u30a3\u30fc\u30eb\u30c9
+dialog.pointedit.table.value=\u503c
+dialog.pointedit.table.changed=\u5909\u66f4\u6e08
+dialog.pointedit.changevalue.text=\u65b0\u3057\u3044\u5024\u3092\u30d5\u30a3\u30fc\u30eb\u30c9\u306b\u5165\u308c\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.pointedit.changevalue.title=\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u7de8\u96c6
+dialog.pointnameedit.name=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d
+dialog.pointnameedit.uppercase=\u5927\u6587\u5b57
+dialog.pointnameedit.lowercase=\u5c0f\u6587\u5b57
+dialog.pointnameedit.sentencecase=\u982d\u6587\u5b57\u306e\u307f\u5927\u6587\u5b57
+dialog.addtimeoffset.add=\u6642\u9593\u3092\u8db3\u3059
+dialog.addtimeoffset.subtract=\u6642\u9593\u3092\u5f15\u304f
+dialog.addtimeoffset.days=\u65e5
+dialog.addtimeoffset.hours=\u6642\u9593
+dialog.addtimeoffset.minutes=\u5206
+dialog.addtimeoffset.notimestamps=\u3053\u306e\u9078\u629e\u7bc4\u56f2\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u3092\u6301\u3063\u3066\u306a\u3044\u306e\u3067\u3001\u504f\u4f4d\u3092\u8db3\u305b\u307e\u305b\u3093\u3002
+dialog.findwaypoint.intro=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u540d\u306e\u4e00\u90e8\u3092\u5165\u529b
+dialog.findwaypoint.search=\u691c\u7d22
+dialog.connect.title=\u70b9\u306b\u5199\u771f\u3092\u63a5\u7d9a
+dialog.connectphoto.clonepoint=\u3053\u306e\u70b9\u306f\u65e2\u306b\u5199\u771f\u3092\u6301\u3063\u3066\u307e\u3059\u3002\n\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u30b3\u30d4\u30fc\u3057\u307e\u3059\u304b\uff1f
+dialog.saveexif.title=EXIF\u3092\u4fdd\u5b58
+dialog.saveexif.intro=\u30c1\u30a7\u30c3\u30af\u30dc\u30c3\u30af\u30b9\u3092\u4f7f\u3063\u3066\u3001\u4fdd\u5b58\u3059\u308b\u5199\u771f\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.saveexif.nothingtosave=\u5ea7\u6a19\u60c5\u5831\u306f\u7121\u5909\u66f4\u3067\u3059\u3002\u4fdd\u5b58\u3059\u308b\u3082\u306e\u306f\u3042\u308a\u307e\u305b\u3093\u3002
+dialog.saveexif.noexiftool=exiftool \u30d7\u30ed\u30b0\u30e9\u30e0\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u7d9a\u3051\u307e\u3059\u304b\uff1f
+dialog.saveexif.table.photoname=\u5199\u771f\u540d
+dialog.saveexif.table.status=\u72b6\u614b
+dialog.saveexif.table.save=\u4fdd\u5b58
+dialog.saveexif.photostatus.connected=\u63a5\u7d9a\u6e08
+dialog.saveexif.photostatus.disconnected=\u672a\u63a5\u7d9a
+dialog.saveexif.photostatus.modified=\u6539\u5909\u6e08
+dialog.saveexif.overwrite=\u30d5\u30a1\u30a4\u30eb\u306e\u4e0a\u66f8\u304d
+dialog.saveexif.force=\u5c0f\u3055\u306a\u8aa4\u5dee\u3092\u7121\u8996\u3057\u3066\u5f37\u884c
+dialog.charts.xaxis=X\u8ef8
+dialog.charts.yaxis=Y\u8ef8
+dialog.charts.output=\u51fa\u529b
+dialog.charts.screen=\u753b\u9762\u3078\u51fa\u529b
+dialog.charts.svg=SVG\u30d5\u30a1\u30a4\u30eb\u306b\u51fa\u529b
+dialog.charts.svgwidth=SVG\u5e45
+dialog.charts.svgheight=SVG\u9ad8
+dialog.charts.needaltitudeortimes=\u30c1\u30e3\u30fc\u30c8\u3092\u4f5c\u308b\u306b\u306f\u3001\u9ad8\u5ea6\u306e\u60c5\u5831\u304b\u3001\u6642\u9593\u306e\u60c5\u5831\u304c\u30c8\u30e9\u30c3\u30af\u306b\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093\u3002
+dialog.charts.gnuplotnotfound=gnuplot\u304c\u6307\u5b9a\u3055\u308c\u305f\u30d1\u30b9\u306b\u3042\u308a\u307e\u305b\u3093\u3002
+dialog.distances.intro=\u70b9\u9593\u306e\u76f4\u7dda\u8ddd\u96e2
+dialog.distances.column.from=\u958b\u59cb\u70b9
+dialog.distances.column.to=\u7d42\u70b9
+dialog.distances.currentpoint=\u73fe\u5728\u306e\u70b9
+dialog.distances.toofewpoints=\u3053\u306e\u6a5f\u80fd\u306f\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u9593\u306e\u8ddd\u96e2\u3092\u8a08\u7b97\u3059\u308b\u305f\u3081\u306b\u3001\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u304c\u5fc5\u8981\u3067\u3059\u3002
+dialog.fullrangedetails.intro=\u9078\u629e\u7bc4\u56f2\u306b\u306f\u8a73\u7d30\u304c\u3042\u308a\u307e\u3059\u3002
+dialog.setmapbg.mapnik=Mapnik(\u521d\u671f\u5024)
+dialog.setmapbg.osma=Osma
+dialog.setmapbg.cyclemap=Cyclemap
+dialog.setmapbg.other=\u305d\u306e\u4ed6(URL\u306b\u5165\u529b)
+dialog.setmapbg.server=\u5730\u56f3\u30b5\u30fc\u30d0\u30fcURL
+dialog.gpsies.column.name=\u30c8\u30e9\u30c3\u30af\u540d
+dialog.gpsies.column.length=\u9577\u3055
+dialog.gpsies.description=\u8a18\u8ff0
+dialog.gpsies.nodescription=\u8a18\u8ff0\u304c\u3042\u308a\u307e\u305b\u3093
+dialog.gpsies.nonefound=\u30c8\u30e9\u30c3\u30af\u304c\u3042\u308a\u307e\u305b\u3093
+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.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
+dialog.correlate.options.offset=\u504f\u4f4d
+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.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
+dialog.correlate.options.nodistancelimit=\u8ddd\u96e2\u5236\u9650\u306a\u3057
+dialog.correlate.options.distancelimit=\u8ddd\u96e2\u5236\u9650
+dialog.correlate.options.correlate=\u95a2\u9023\u4ed8\u3051
+dialog.correlate.alloutsiderange=\u5168\u3066\u306e\u5199\u771f\u304c\u30c8\u30e9\u30c3\u30af\u306e\u7bc4\u56f2\u5916\u3067\u3059\u3002\u3060\u304b\u3089\u3001\u4f55\u3082\u95a2\u9023\u4ed8\u3051\u3089\u308c\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6642\u9593\u504f\u4f4d\u3092\u5909\u3048\u308b\u304b\u3001\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u95a2\u9023\u4ed8\u3051\u3092\u3059\u308b\u306a\u3069\u3057\u3066\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.rearrangephotos.desc=\u5411\u304b\u3046\u5148\u3092\u9078\u629e\u3057\u3066\u3001\u5199\u771f\u306e\u70b9\u3092\u4e26\u3079\u76f4\u3059\u3002
+dialog.rearrangephotos.tostart=\u79fb\u52d5\u958b\u59cb
+dialog.rearrangephotos.toend=\u79fb\u52d5\u7d42\u4e86
+dialog.rearrangephotos.nosort=\u4e26\u3079\u66ff\u3048\u306a\u3044
+dialog.rearrangephotos.sortbyfilename=\u30d5\u30a1\u30a4\u30eb\u540d\u3067\u4e26\u3079\u66ff\u3048
+dialog.rearrangephotos.sortbytime=\u6642\u9593\u3067\u4e26\u3079\u66ff\u3048
+dialog.compress.nonefound=\u9664\u304f\u3053\u3068\u304c\u3067\u304d\u308b\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
+dialog.compress.closepoints.title=\u8fd1\u508d\u70b9\u3092\u524a\u9664
+dialog.compress.closepoints.paramdesc=\u8fd1\u508d\u4fc2\u6570
+dialog.compress.wackypoints.title=\u304a\u304b\u3057\u306a\u70b9\u306e\u524a\u9664
+dialog.compress.wackypoints.paramdesc=\u8ddd\u96e2\u4fc2\u6570
+dialog.compress.singletons.title=\u96e2\u6563\u70b9\u306e\u524a\u9664
+dialog.compress.singletons.paramdesc=\u8ddd\u96e2\u4fc2\u6570
+dialog.compress.duplicates.title=\u91cd\u8907\u70b9\u3092\u524a\u9664
+dialog.compress.summarylabel=\u524a\u9664\u3059\u308b\u70b9
+dialog.pastecoordinates.desc=\u5ea7\u6a19\u3092\u3053\u3053\u306b\u5165\u529b\u3059\u308b\u304b\u30da\u30fc\u30b9\u30c8\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.pastecoordinates.coords=\u5ea7\u6a19
+dialog.pastecoordinates.nothingfound=\u5ea7\u6a19\u306e\u78ba\u8a8d\u3092\u3057\u3066\u3001\u3082\u3046\u4e00\u5ea6\u8a66\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://activityworkshop.net/software/prune/ \u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.about.version=\u30d0\u30fc\u30b8\u30e7\u30f3
+dialog.about.build=Build
+dialog.about.summarytext1=Prune \u306f\u3001GPS\u53d7\u4fe1\u6a5f\u304b\u3089\u306e\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u307f\u3001\u8868\u793a\u3057\u3001\u7de8\u96c6\u3059\u308b\u305f\u3081\u306e\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u3059\u3002
+dialog.about.summarytext2=\u3053\u308c\u306f\u3001Gnu GPL\u306e\u4e0b\u306b\u516c\u958b\u3055\u308c\u3001\u81ea\u7531\u3067\u3001\u30aa\u30fc\u30d7\u30f3\u3067\u3001\u4e16\u754c\u4e2d\u3067\u3064\u304b\u3048\u3001\u62e1\u5f35\u53ef\u80fd\u3067\u3059\u3002<br><code>"license.txt"</code>\u306b\u304b\u304b\u308c\u3066\u3044\u308b\u6761\u4ef6\u306b\u3088\u308b\u3068\u3001<br>\u8907\u88fd\u30fb\u518d\u914d\u5e03\u30fb\u6539\u5909\u306f\u3001\u8a31\u3055\u308c\u3066\u304a\u308a\u3001\u304b\u3064\u5968\u52b1\u3055\u308c\u3066\u3044\u307e\u3059\u3002
+dialog.about.summarytext3=\u3082\u3063\u3068\u8a73\u3057\u3044\u60c5\u5831\u3084\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001<code style="font-weight:bold">http://activityworkshop.net/</code>\u3092\u898b\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.about.languages=\u5229\u7528\u53ef\u80fd\u306a\u8a00\u8a9e
+dialog.about.translatedby=\u65e5\u672c\u8a9e\u7ffb\u8a33\uff1aOpenStreetMap \u8ca2\u732e\u8005\u306enazotoko
+dialog.about.systeminfo=\u30b7\u30b9\u30c6\u30e0\u60c5\u5831
+dialog.about.systeminfo.os=\u30aa\u30da\u30ec\u30fc\u30c6\u30a3\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0
+dialog.about.systeminfo.java=Java Runtime
+dialog.about.systeminfo.java3d=Java 3D \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.systeminfo.povray=Povray \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.systeminfo.exiftool=Exiftool \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.systeminfo.gpsbabel=Gpsbabel \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.systeminfo.gnuplot=Gnuplot \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u6e08
+dialog.about.yes=\u306f\u3044
+dialog.about.no=\u3044\u3044\u3048
+dialog.about.credits=\u30af\u30ec\u30b8\u30c3\u30c8
+dialog.about.credits.code=Prune \u306e\u30b3\u30fc\u30c9\u306e\u4f5c\u8005
+dialog.about.credits.exifcode=Exif \u306e\u30b3\u30fc\u30c9\u306e\u4f5c\u8005
+dialog.about.credits.icons=\u3044\u304f\u3064\u304b\u306e\u30a2\u30a4\u30b3\u30f3
+dialog.about.credits.translators=\u7ffb\u8a33\u8005
+dialog.about.credits.translations=\u7ffb\u8a33\u52a9\u8005
+dialog.about.credits.devtools=\u958b\u767a\u30c4\u30fc\u30eb
+dialog.about.credits.othertools=\u305d\u306e\u4ed6\u30c4\u30fc\u30eb
+dialog.about.credits.thanks=\u611f\u8b1d
+dialog.about.readme=\u521d\u3081\u306b\u8aad\u3093\u3067\u304f\u3060\u3055\u3044
+dialog.checkversion.error=\u30d0\u30fc\u30b8\u30e7\u30f3\u756a\u53f7\u304c\u30c1\u30a7\u30c3\u30af\u3067\u304d\u307e\u305b\u3093\u3002\n\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u63a5\u7d9a\u306e\u78ba\u8a8d\u3092\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.checkversion.uptodate=\u3042\u306a\u305f\u306f\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306ePrune\u3092\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002
+dialog.checkversion.newversion1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e Prune \u304c\u5165\u624b\u53ef\u80fd\u3067\u3059\u3002\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u73fe\u5728
+dialog.checkversion.newversion2=\u3067\u3059\u3002
+dialog.checkversion.releasedate1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306f\u3001
+dialog.checkversion.releasedate2=\u306b\u30ea\u30ea\u30fc\u30b9\u3057\u307e\u3057\u305f\u3002
+dialog.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://activityworkshop.net/software/prune/download.html \u3078\u884c\u3063\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.keys.intro=\u30de\u30a6\u30b9\u306e\u4ee3\u308f\u308a\u306b\u6b21\u306e\u30b7\u30e7\u30fc\u30c8\u30ab\u30c3\u30c8\u30ad\u30fc\u3092\u4f7f\u3046\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
+dialog.keys.keylist=<table><tr><td>\u77e2\u5370\u30ad\u30fc</td><td>\u5730\u56f3\u3092\u4e0a\u4e0b\u5de6\u53f3\u306b\u79fb\u52d5</td></tr><tr><td>Ctrl + \u5de6\u30fb\u53f3\u77e2\u5370</td><td>\u524d\u30fb\u6b21\u306e\u70b9\u3092\u9078\u629e</td></tr><tr><td>Ctrl + \u4e0a\u30fb\u4e0b\u77e2\u5370</td><td>\u62e1\u5927\u30fb\u7e2e\u5c0f</td></tr><tr><td>Del</td><td>\u73fe\u5728\u306e\u70b9\u3092\u524a\u9664</td></tr></table>
+dialog.saveconfig.desc=\u4e0b\u8a18\u306e\u8a2d\u5b9a\u304c\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3055\u308c\u307e\u3059
+dialog.saveconfig.prune.trackdirectory=\u30c8\u30e9\u30c3\u30af\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
+dialog.saveconfig.prune.photodirectory=\u5199\u771f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea
+dialog.saveconfig.prune.languagecode=\u8a00\u8a9e\u30b3\u30fc\u30c9(JA)
+dialog.saveconfig.prune.languagefile=\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb
+dialog.saveconfig.prune.gpsdevice=GPS\u30c7\u30d0\u30a4\u30b9
+dialog.saveconfig.prune.gpsformat=GPS\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
+dialog.saveconfig.prune.povrayfont=Povray \u30d5\u30a9\u30f3\u30c8
+dialog.saveconfig.prune.metricunits=\u30e1\u30fc\u30c8\u30eb\u6cd5\u3092\u4f7f\u3046\uff1f
+dialog.saveconfig.prune.gnuplotpath=gnuplot\u3078\u306e\u30d1\u30b9
+dialog.saveconfig.prune.gpsbabelpath=gpsbabel\u3078\u306e\u30d1\u30b9
+dialog.saveconfig.prune.exiftoolpath=exiftool\u3078\u306e\u30d1\u30b9
+dialog.saveconfig.prune.mapserverindex=\u80cc\u666f\u5730\u56f3\u30b5\u30fc\u30d0\u30fc\u306e\u7d22\u5f15(1-4)
+dialog.saveconfig.prune.mapserverurl=\u5730\u56f3\u30b5\u30fc\u30d0\u30fc\u306eURL
+dialog.saveconfig.prune.showpace=\u30da\u30fc\u30b9\u3092\u8868\u793a
+dialog.saveconfig.prune.kmzimagewidth=KML \u753b\u50cf\u5e45
+dialog.saveconfig.prune.kmzimageheight=KML \u753b\u50cf\u9ad8
+dialog.saveconfig.prune.colourscheme=\u8272\u306e\u30b9\u30ad\u30fc\u30e0
+dialog.saveconfig.prune.kmltrackcolour=KML \u30c8\u30e9\u30c3\u30af\u306e\u8272
+dialog.setpaths.intro=\u5fc5\u8981\u306a\u3089\u3001\u5916\u90e8\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30b9\u3092\u9078\u3076\u4e8b\u304c\u3067\u304d\u307e\u3059
+dialog.addaltitude.noaltitudes=\u9078\u629e\u7bc4\u56f2\u306f\u3001\u9ad8\u5ea6\u3092\u542b\u3093\u3067\u307e\u305b\u3093\u3002
+dialog.addaltitude.desc=\u52a0\u3048\u305f\u9ad8\u5ea6\u504f\u4f4d
+dialog.setcolours.intro=\u8272\u3092\u5909\u3048\u308b\u305f\u3081\u306b\u3001\u8272\u306e\u7bb1\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.setcolours.background=\u80cc\u666f
+dialog.setcolours.borders=\u5883\u754c
+dialog.setcolours.lines=\u7dda
+dialog.setcolours.primary=\u4e3b\u8981
+dialog.setcolours.secondary=\u526f\u6b21
+dialog.setcolours.point=\u70b9
+dialog.setcolours.selection=\u9078\u629e
+dialog.setcolours.text=\u30c6\u30ad\u30b9\u30c8
+dialog.colourchooser.title=\u8272\u3092\u9078\u3093\u3067\u304f\u3060\u3055\u3044
+dialog.colourchooser.red=\u8d64
+dialog.colourchooser.green=\u7dd1
+dialog.colourchooser.blue=\u9752
+dialog.setlanguage.firstintro=\u642d\u8f09\u3055\u308c\u3066\u3044\u308b\u8a00\u8a9e\u306e\u4e00\u3064\u3092\u9078\u3076\u304b\u3001<p>\u4ee3\u308f\u308a\u306b\u4f7f\u3046\u30c6\u30ad\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u3092\u9078\u3073\u307e\u3059\u3002
+dialog.setlanguage.secondintro=\u8a00\u8a9e\u3092\u5207\u308a\u66ff\u3048\u308b\u306b\u306f\u3001\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3066<p>Prune \u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+dialog.setlanguage.language=\u8a00\u8a9e
+dialog.setlanguage.languagefile=\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb
+dialog.setlanguage.endmessage=\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3001\u8a00\u8a9e\u306e\u5909\u66f4\u3092\u6709\u52b9\u306b\u3059\u308b\u305f\u3081\u306b\nPrune \u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+
+# 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
+
+# Confirm messages || These are displayed as confirmation in the status bar
+confirm.loadfile=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30c7\u30fc\u30bf\u3092\u8aad\u307f\u8fbc\u3080
+confirm.save.ok1=\u4fdd\u5b58\u6210\u529f
+confirm.save.ok2=\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u70b9
+confirm.deletepoint.single=\u30c7\u30fc\u30bf\u70b9\u306f\u524a\u9664\u3055\u308c\u305f
+confirm.deletepoint.multi=\u30c7\u30fc\u30bf\u70b9\u306f\u524a\u9664\u3055\u308c\u305f
+confirm.point.edit=\u7de8\u96c6\u3055\u308c\u305f\u70b9
+confirm.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u304c\u7d71\u5408\u6e08\u307f
+confirm.reverserange=\u7bc4\u56f2\u304c\u53cd\u8ee2\u3055\u308c\u305f
+confirm.addtimeoffset=\u6642\u9593\u504f\u4f4d\u304c\u52a0\u3048\u3089\u308c\u3066\u3044\u308b
+confirm.addaltitudeoffset=\u9ad8\u5ea6\u504f\u4f4d\u304c\u52a0\u3048\u3089\u308c\u3066\u3044\u308b
+confirm.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u304c\u4e26\u3079\u66ff\u3048\u3089\u308c\u305f
+confirm.rearrangephotos=\u5199\u771f\u304c\u4e26\u3079\u66ff\u3048\u3089\u308c\u305f
+confirm.cutandmove=\u9078\u629e\u304c\u52d5\u304b\u3055\u308c\u305f
+confirm.convertnamestotimes=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u306e\u540d\u524d\u304c\u5909\u63db\u3055\u308c\u305f
+confirm.saveexif.ok1=\u4fdd\u5b58\u3055\u308c\u305f
+confirm.saveexif.ok2=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
+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.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f
+confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f
+confirm.running=\u5b9f\u884c\u4e2d...
+
+# Buttons || These are all the texts for buttons
+button.ok=\u6c7a\u5b9a
+button.back=\u623b\u308b
+button.next=\u6b21
+button.finish=\u5b8c\u6210
+button.cancel=\u53d6\u6d88
+button.overwrite=\u4e0a\u66f8\u304d
+button.moveup=\u4e0a\u3078
+button.movedown=\u4e0b\u3078
+button.showlines=\u7dda\u3092\u8868\u793a
+button.edit=\u7de8\u96c6
+button.exit=\u7d42\u4e86
+button.close=\u9589\u3058\u308b
+button.continue=\u7d9a\u3051\u308b
+button.yes=\u306f\u3044
+button.no=\u3044\u3044\u3048
+button.yestoall=\u5168\u90e8\u306b\u306f\u3044
+button.notoall=\u5168\u90e8\u306b\u3044\u3044\u3048
+button.select=\u9078\u629e
+button.selectall=\u5168\u9078\u629e
+button.selectnone=\u9078\u629e\u89e3\u9664
+button.preview=\u30d7\u30ec\u30d3\u30e5\u30fc
+button.load=\u8aad\u307f\u8fbc\u307f
+button.guessfields=\u30d5\u30a3\u30fc\u30eb\u30c9\u4e88\u6e2c
+button.showwebpage=\u30a6\u30a7\u30d6\u30da\u30fc\u30b8\u3092\u8868\u793a
+button.check=\u691c\u67fb
+button.resettodefaults=\u521d\u671f\u5024\u306b\u623b\u3059
+button.browse=\u95b2\u89a7...
+
+# File types
+filetype.txt=TXT\u30d5\u30a1\u30a4\u30eb
+filetype.jpeg=JPG\u30d5\u30a1\u30a4\u30eb
+filetype.kmlkmz=KML,KMZ\u30d5\u30a1\u30a4\u30eb
+filetype.kml=KML\u30d5\u30a1\u30a4\u30eb
+filetype.kmz=KMZ\u30d5\u30a1\u30a4\u30eb
+filetype.gpx=GPX\u30d5\u30a1\u30a4\u30eb
+filetype.pov=POV\u30d5\u30a1\u30a4\u30eb
+filetype.svg=SVG\u30d5\u30a1\u30a4\u30eb
+
+# Display components || These are all for the side panels showing point/range details
+display.nodata=\u8aad\u307f\u8fbc\u307e\u308c\u305f\u30c7\u30fc\u30bf\u306a\u3057
+display.noaltitudes=\u30c8\u30e9\u30c3\u30af\u30c7\u30fc\u30bf\u306f\u9ad8\u5ea6\u3092\u542b\u307f\u307e\u305b\u3093
+details.trackdetails=\u30c8\u30e9\u30c3\u30af\u8a73\u7d30
+details.notrack=\u8aad\u307f\u8fbc\u307e\u308c\u305f\u30c8\u30e9\u30c3\u30af\u306a\u3057
+details.track.points=\u70b9
+details.track.file=\u30d5\u30a1\u30a4\u30eb
+details.track.numfiles=\u30d5\u30a1\u30a4\u30eb\u6570
+details.pointdetails=\u70b9\u306e\u8a73\u7d30
+details.index.selected=\u7b2c
+details.index.of=\u756a \u5168:
+details.nopointselection=\u70b9\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+details.photofile=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
+details.norangeselection=\u7bc4\u56f2\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093
+details.rangedetails=\u7bc4\u56f2\u8a73\u7d30
+details.range.selected=\u9078\u629e\u6e08
+details.range.to=\u304b\u3089
+details.altitude.to=\u304b\u3089
+details.range.climb=\u4e0a\u308a
+details.range.descent=\u4e0b\u308a
+details.coordformat=\u5ea7\u6a19\u30d5\u30a9\u30fc\u30de\u30c3\u30c8
+details.distanceunits=\u8ddd\u96e2\u5358\u4f4d
+display.range.time.secs=\u79d2
+display.range.time.mins=\u5206
+display.range.time.hours=\u6642
+display.range.time.days=\u65e5
+details.range.avespeed=\u5e73\u5747\u901f\u5ea6
+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.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
+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
+fieldname.latitude=\u7def\u5ea6
+fieldname.longitude=\u7d4c\u5ea6
+fieldname.altitude=\u9ad8\u5ea6
+fieldname.timestamp=\u6642\u9593
+fieldname.time=\u6642\u9593
+fieldname.waypointname=\u540d\u524d
+fieldname.waypointtype=\u7a2e\u985e
+fieldname.newsegment=\u30bb\u30b0\u30e1\u30f3\u30c8
+fieldname.custom=\u30ab\u30b9\u30bf\u30e0
+fieldname.prefix=\u30d5\u30a3\u30fc\u30eb\u30c9
+fieldname.distance=\u8ddd\u96e2
+fieldname.movingdistance=\u79fb\u52d5\u8ddd\u96e2
+fieldname.duration=\u9593\u9694
+fieldname.speed=\u901f\u5ea6
+fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
+
+# Measurement units
+units.original=\u30aa\u30ea\u30b8\u30ca\u30eb
+units.default=\u521d\u671f\u5024
+units.metres=\u30e1\u30fc\u30c8\u30eb
+units.metres.short=m
+units.feet=\u30d5\u30a3\u30fc\u30c8
+units.feet.short=ft
+units.kilometres=\u30ad\u30ed\u30e1\u30fc\u30c8\u30eb
+units.kilometres.short=km
+units.kmh=km/h
+units.miles=\u30de\u30a4\u30eb
+units.miles.short=mi
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=\u6642\u9593
+units.degminsec=\u5ea6-\u5206-\u79d2
+units.degmin=\u5ea6-\u5206
+units.deg=\u5ea6
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.co.jp
+
+# Cardinals for 3d plots
+cardinal.n=N
+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.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.deleterange=\u7bc4\u56f2\u306e\u524a\u9664
+undo.compress=\u30c8\u30e9\u30c3\u30af\u306e\u5727\u7e2e
+undo.insert=\u70b9\u306e\u633f\u5165
+undo.reverse=\u7bc4\u56f2\u306e\u53cd\u8ee2
+undo.mergetracksegments=\u30c8\u30e9\u30c3\u30af\u30bb\u30b0\u30e1\u30f3\u30c8\u306e\u7d71\u5408
+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.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u66ff\u3048
+undo.createpoint=\u70b9\u306e\u4f5c\u6210
+undo.rotatephoto=\u5199\u771f\u306e\u56de\u8ee2
+undo.convertnamestotimes=\u540d\u524d\u3092\u6642\u9593\u306b\u5909\u63db\u3059\u308b
+
+# Error messages
+error.save.dialogtitle=\u30c7\u30fc\u30bf\u4fdd\u5b58\u306e\u30a8\u30e9\u30fc
+error.save.nodata=\u4fdd\u5b58\u3059\u308b\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093
+error.save.failed=\u30c7\u30fc\u30bf\u3092\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\u3059\u308b\u306e\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+error.saveexif.filenotfound=\u5199\u771f\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3067\u3057\u305f
+error.saveexif.cannotoverwrite1=\u5199\u771f\u30d5\u30a1\u30a4\u30eb
+error.saveexif.cannotoverwrite2=\u8aad\u307f\u8fbc\u307f\u5c02\u7528\u3067\u4e0a\u66f8\u304d\u3067\u304d\u307e\u305b\u3093\u3002\u8907\u88fd\u3092\u4f5c\u308a\u307e\u3059\u304b\uff1f
+error.saveexif.failed1=
+error.saveexif.failed2=\u679a\u306e\u753b\u50cf\u306e\u4fdd\u5b58\u306b\u5931\u6557\u3057\u307e\u3057\u305f
+error.saveexif.forced1=
+error.saveexif.forced2=\u679a\u306e\u753b\u50cf\u304c\u5f37\u884c\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002
+error.load.dialogtitle=\u30c7\u30fc\u30bf\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc
+error.load.noread=\u30d5\u30a1\u30a4\u30eb\u304c\u8aad\u3081\u307e\u305b\u3093
+error.load.nopoints=\u30d5\u30a1\u30a4\u30eb\u306e\u4e2d\u306b\u5ea7\u6a19\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+error.load.unknownxml=XML\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u304c\u89e3\u91c8\u3067\u304d\u307e\u305b\u3093
+error.load.noxmlinzip=Zip\u30d5\u30a1\u30a4\u30eb\u4e2d\u306bXML\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+error.load.othererror=\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc:
+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
+error.undofailed.text=\u30a2\u30f3\u30c9\u30a5\u64cd\u4f5c\u306e\u5931\u6557
+error.function.noop.title=\u305d\u306e\u6a5f\u80fd\u306f\u52b9\u679c\u3042\u308a\u307e\u305b\u3093
+error.rearrange.noop=\u70b9\u306e\u518d\u914d\u7f6e\u306f\u52b9\u679c\u3042\u308a\u307e\u305b\u3093
+error.function.notavailable.title=\u305d\u306e\u6a5f\u80fd\u306f\u4f7f\u3048\u307e\u305b\u3093
+error.function.nojava3d=\u3053\u306e\u6a5f\u80fd\u306f Java3d \u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u5fc5\u8981\u3068\u3057\u307e\u3059\u3002\nSun.com \u3088\u308a\u5165\u624b\u3057\u3066\u304f\u3060\u3055\u3044
+error.3d=3D \u8868\u793a\u3067\u30a8\u30e9\u30fc\u304c\u8d77\u304d\u307e\u3057\u305f
+error.readme.notfound=Readme \u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
+error.osmimage.dialogtitle=\u5730\u56f3\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u307f\u6642\u306e\u30a8\u30e9\u30fc
+error.osmimage.failed=\u5730\u56f3\u753b\u50cf\u3092\u8aad\u307f\u8fbc\u307f\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u63a5\u7d9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
+error.language.wrongfile=\u9078\u629e\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306fPrune \u7528\u306e\u8a00\u8a9e\u30d5\u30a1\u30a4\u30eb\u306b\u898b\u3048\u307e\u305b\u3093\u3002
+error.convertnamestotimes.nonames=\u3069\u306e\u540d\u524d\u3082\u6642\u9593\u306b\u5909\u63db\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
index 8e05b67d816ce7262ad973953bfc356f69117ffd..c731f843750ef072cb7b8bdcc8be6bf23e85790a 100644 (file)
@@ -7,7 +7,7 @@ menu.file.open=Otw\u00f3rz
 menu.file.addphotos=Dodaj zdj\u0119cia
 menu.file.save=Zapisz
 menu.file.exit=Zako\u0144cz
-menu.edit=Edycja
+menu.track=\u015acie\u017cka
 menu.edit.undo=Cofnij
 menu.edit.clearundo=Wyczy\u015b\u0107 list\u0119 zmian
 menu.edit.editpoint=Edytuj punkt
@@ -23,7 +23,8 @@ menu.edit.rearrange.start=Wszystkie na pocz\u0105tek \u015bcie\u017cki
 menu.edit.rearrange.end=Wszystkie na koniec \u015bcie\u017cki
 menu.edit.rearrange.nearest=Do najbli\u017cszego punktu
 menu.edit.cutandmove=Wytnij i przesu\u0144 zaznaczenie
-menu.select=Zaznacz
+menu.range=Zakres
+menu.point=Punkt
 menu.select.all=Zaznacz wszystko
 menu.select.none=Usu\u0144 zaznaczenie
 menu.select.start=Zaznacz pocz\u0105tek zakresu
@@ -39,8 +40,8 @@ menu.view.browser.google=Mapy Google
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Mapy Yahoo
+menu.view.browser.bing=Mapy Bing
 menu.settings=Ustawienia
-menu.settings.showpace=Poka\u017c tempo przy wy\u015bwietlaniu zakresu
 menu.help=Pomoc
 # Popup menu for map
 menu.map.zoomin=Powi\u0119ksz
@@ -54,8 +55,9 @@ menu.map.showscalebar=Poka\u017c skal\u0119
 
 # Alt keys for menus
 altkey.menu.file=P
-altkey.menu.edit=E
-altkey.menu.select=A
+altkey.menu.track=C
+altkey.menu.range=Z
+altkey.menu.point=U
 altkey.menu.view=W
 altkey.menu.photo=Z
 altkey.menu.settings=T
@@ -80,15 +82,25 @@ function.editwaypointname=Zmie\u0144 nazw\u0119 punktu po\u015bredniego
 function.compress=Kompresuj \u015bcie\u017ck\u0119
 function.addtimeoffset=Dodaj przesuni\u0119cie czasu
 function.addaltitudeoffset=Dodaj przesuni\u0119cie wysoko\u015bci
+function.convertnamestotimes=Zamie\u0144 nazwy punkt\u00f3w na czas
 function.findwaypoint=Znajd\u017a punkt po\u015bredni
+function.pastecoordinates=Wprowad\u017a nowe wsp\u00f3\u0142rz\u0119dne
 function.charts=Wykres
 function.show3d=Poka\u017c model 3D
 function.distances=Odleg\u0142o\u015bci
-function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
-function.correlatephotos=Powi\u0105\u017c zdj\u0119cia
-function.setmapbg=Wybierz map\u0119
+function.fullrangedetails=Wszystkie detale
+function.setmapbg=Wybierz map\u0119 t\u0142a
 function.setkmzimagesize=Ustaw rozmiar zdj\u0119\u0107 w KMZ
 function.setpaths=Ustaw \u015bcie\u017cki do program\u00f3w
+function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
+function.duplicatepoint=Duplikuj plik
+function.setcolours=Ustaw kolory
+function.setlanguage=Zmie\u0144 j\u0119zyk
+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.ignoreexifthumb=Ignoruj miniaturk\u0119 z exif
 function.help=Pomoc
 function.showkeys=Poka\u017c klawisze skr\u00f3tu
 function.about=O Prune
@@ -129,6 +141,7 @@ dialog.gpsload.device=Nazwa urz\u0105dzenia
 dialog.gpsload.format=Format
 dialog.gpsload.getwaypoints=\u0141aduj punkty po\u015brednie
 dialog.gpsload.gettracks=\u0141aduj \u015bcie\u017cki
+dialog.gpsload.save=Zapisz do pliku
 dialog.gpssend.sendwaypoints=Wy\u015blij punkty po\u015brednie
 dialog.gpssend.sendtracks=Wy\u015blij \u015bcie\u017cki
 dialog.gpssend.trackname=Nazwa \u015bcie\u017cki
@@ -143,13 +156,16 @@ dialog.save.altitudeunits=Jednostki wysoko\u015bci
 dialog.save.timestampformat=Format znacznika czasu
 dialog.save.overwrite.title=Plik ju\u017c istnieje
 dialog.save.overwrite.text=Ten plik ju\u017c istnieje. Czy na pewno chcesz go nadpisa\u0107?
+dialog.save.notypesselected=Nie zosta\u0142 wybrany \u017caden typ punkt\u00f3w
 dialog.exportkml.text=Tytu\u0142 dla danych
 dialog.exportkml.altitude=Do\u0142\u0105cz wysoko\u015bci (dla cel\u00f3w lotniczych)
 dialog.exportkml.kmz=Skompresuj do pliku KMZ
 dialog.exportkml.exportimages=Eksportuj miniaturki zdj\u0119\u0107 do KMZ
+dialog.exportkml.trackcolour=Kolor \u015bcie\u017cki
 dialog.exportgpx.name=Nazwa
 dialog.exportgpx.desc=Opis
 dialog.exportgpx.includetimestamps=Do\u0142\u0105cz znaczniki czasu
+dialog.exportgpx.copysource=Kopiuj \u017ar\u00f3d\u0142owy xml
 dialog.exportpov.text=Wprowad\u017a dodatkowe parametry eksportu do formatu POV
 dialog.exportpov.font=Czcionka
 dialog.exportpov.camerax=Kamera X
@@ -163,6 +179,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.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?
 dialog.confirmcutandmove.title=Potwierd\u017a wytnij i przesu\u0144
@@ -192,10 +209,8 @@ dialog.addtimeoffset.days=Dni
 dialog.addtimeoffset.hours=Godziny
 dialog.addtimeoffset.minutes=Minuty
 dialog.addtimeoffset.notimestamps=Nie mo\u017cna przesun\u0105\u0107 czasu, poniewa\u017c zaznaczenie nie zawiera znacznik\u00f3w czasu
-dialog.findwaypoint.intro=
-dialog.findwaypoint.search=
-dialog.connect.title=Po\u0142\u0105cz zdj\u0119cie do punktu
-dialog.connectphoto.clonepoint=Ten punkt po\u0142\u0105czono ju\u017c ze zdj\u0119ciem.\nCzy chcesz zrobi\u0107 kopi\u0119 tego punktu?
+dialog.findwaypoint.intro=Wprowad\u017a nazw\u0119 punktu po\u015bredniego
+dialog.findwaypoint.search=Szukaj
 dialog.saveexif.title=Zapisz Exif
 dialog.saveexif.intro=Zaznacz zdj\u0119cia do zapisu u\u017cywaj\u0105c znacznik\u00f3w
 dialog.saveexif.nothingtosave=Wsp\u00f3\u0142rz\u0119dne nie zosta\u0142y zmienione, nie ma nic do zapisu
@@ -207,6 +222,7 @@ dialog.saveexif.photostatus.connected=Po\u0142\u0105czony
 dialog.saveexif.photostatus.disconnected=Roz\u0142\u0105czony
 dialog.saveexif.photostatus.modified=Zmodyfikowany
 dialog.saveexif.overwrite=Nadpisz pliki
+dialog.saveexif.force=Wymu\u015b pomimo niewielkich b\u0142\u0119d\u00f3w
 dialog.charts.xaxis=O\u015b X
 dialog.charts.yaxis=O\u015b Y
 dialog.charts.output=Wykres
@@ -215,13 +231,13 @@ dialog.charts.svg=Zapisz do pliku SVG
 dialog.charts.svgwidth=szeroko\u015b\u0107 SVG
 dialog.charts.svgheight=wysoko\u015b\u0107 SVG
 dialog.charts.needaltitudeortimes=\u015acie\u017cka musi posiada\u0107 zapisane wysoko\u015bci lub znaczniki czasu aby mo\u017cliwe by\u0142o utworzenie wykres\u00f3w
-dialog.charts.gnuplotpath=\u015acie\u017cka do programu gnuplot
 dialog.charts.gnuplotnotfound=Nie znalaz\u0142em programu gnuplot
 dialog.distances.intro=Odleg\u0142o\u015b\u0107 mi\u0119dzy punktami w linii prostej
 dialog.distances.column.from=Z punktu
 dialog.distances.column.to=Do punktu
 dialog.distances.currentpoint=Wybrany punkt
 dialog.distances.toofewpoints=Ta funkcja wymaga przynajmniej dw\u00f3ch punkt\u00f3w po\u015brednich, aby mo\u017cna by\u0142o obliczy\u0107 odleg\u0142o\u015bci
+dialog.fullrangedetails.intro=Szczeg\u00f3\u0142y wybranego zakresu
 dialog.setmapbg.mapnik=Mapnik (domy\u015blny)
 dialog.setmapbg.osma=Osma
 dialog.setmapbg.cyclemap=Cyclemap
@@ -231,7 +247,7 @@ dialog.gpsies.column.name=Nazwa \u015bcie\u017cki
 dialog.gpsies.column.length=D\u0142ugo\u015b\u0107
 dialog.gpsies.description=Opis
 dialog.gpsies.nodescription=Brak opisu
-dialog.gpsies.nonefound=
+dialog.gpsies.nonefound=Nie znalaz\u0142em \u015bcie\u017cek
 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
@@ -254,15 +270,24 @@ 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.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
+dialog.rearrangephotos.nosort=Nie sortuj
+dialog.rearrangephotos.sortbyfilename=Sortuj po nazwie pliku
+dialog.rearrangephotos.sortbytime=Sortuj wed\u0142ug czasu
 dialog.compress.nonefound=Nie mo\u017cna usun\u0105\u0107 \u017cadnych punkt\u00f3w
-dialog.compress.duplicates.title=Usuwanie duplikat\u00f3w
-dialog.compress.closepoints.title=Usuwanie punkt\u00f3w bliskich sobie
+dialog.compress.closepoints.title=Usuwanie bliskich sobie punkt\u00f3w
 dialog.compress.closepoints.paramdesc=Wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci
 dialog.compress.wackypoints.title=Usuwanie dziwacznych punkt\u00f3w
 dialog.compress.wackypoints.paramdesc=Wsp\u00f3\u0142czynnik odleg\u0142o\u015bci
 dialog.compress.singletons.title=Usuwanie odosobnionych punkt\u00f3w
 dialog.compress.singletons.paramdesc=Wsp\u00f3\u0142czynnik odleg\u0142o\u015bci
+dialog.compress.duplicates.title=Usuwanie duplikat\u00f3w
 dialog.compress.summarylabel=Punkty do usuni\u0119cia
+dialog.pastecoordinates.desc=Wprowad\u017a lub wklej wsp\u00f3\u0142rz\u0119dne
+dialog.pastecoordinates.coords=Wsp\u00f3\u0142rz\u0119dne
+dialog.pastecoordinates.nothingfound=Sprawd\u017a wsp\u00f3\u0142rz\u0119dne i spr\u00f3buj jeszcze raz
 dialog.help.help=Na stronie\n http://activityworkshop.net/software/prune/ \nznajdziesz wi\u0119cej informacji oraz podr\u0119cznik u\u017cytkownika
 dialog.about.version=Wersja
 dialog.about.build=Build
@@ -304,6 +329,7 @@ dialog.saveconfig.desc=Nast\u0119puj\u0105ce ustawienia mog\u0105 zosta\u0107 za
 dialog.saveconfig.prune.trackdirectory=Katalog ze \u015bcie\u017ckami
 dialog.saveconfig.prune.photodirectory=Katalog ze zdj\u0119ciami
 dialog.saveconfig.prune.languagecode=Kod j\u0119zyka (PL)
+dialog.saveconfig.prune.languagefile=Plik t\u0142umaczenia
 dialog.saveconfig.prune.gpsdevice=Urz\u0105dzenie GPS
 dialog.saveconfig.prune.gpsformat=Format pliku GPS
 dialog.saveconfig.prune.povrayfont=czcionka dla Povray-a
@@ -313,12 +339,31 @@ dialog.saveconfig.prune.gpsbabelpath=\u015bcie\u017cka do gpsbabel
 dialog.saveconfig.prune.exiftoolpath=\u015bcie\u017cka do exiftool
 dialog.saveconfig.prune.mapserverindex=kolejny numer serwera map
 dialog.saveconfig.prune.mapserverurl=URL serwera map
-dialog.saveconfig.prune.showpace=Pokazuj tempo
 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.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.addaltitude.noaltitudes=Wybrany zakres nie zawiera danych o wysoko\u015bciach
 dialog.addaltitude.desc=Warto\u015b\u0107 przesuni\u0119cia wysoko\u015bci
+dialog.setcolours.intro=Kliknij na kolor by go wybra\u0107
+dialog.setcolours.background=T\u0142o
+dialog.setcolours.borders=Ramki
+dialog.setcolours.lines=Linie
+dialog.setcolours.primary=G\u0142\u00f3wny
+dialog.setcolours.secondary=Drugorz\u0119dny
+dialog.setcolours.point=Punkty
+dialog.setcolours.selection=Wyb\u00f3r
+dialog.setcolours.text=Tekst
+dialog.colourchooser.title=Wybierz kolor
+dialog.colourchooser.red=Czerwony
+dialog.colourchooser.green=Zielony
+dialog.colourchooser.blue=Niebieski
+dialog.setlanguage.firstintro=Mo\u017cesz wybra\u0107 jeden z do\u0142\u0105czonych j\u0119zyk\u00f3w<p>Albo wybra\u0107 wybrany przez siebie plik tekstowy.
+dialog.setlanguage.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia<p>i zrestartowa\u0107 Prune by zmieni\u0107 j\u0119zyk.
+dialog.setlanguage.language=J\u0119zyk
+dialog.setlanguage.languagefile=Plik t\u0142umaczenia
+dialog.setlanguage.endmessage=Zapisz ustawienia i zrestartuj Prune\n by zmiana j\u0119zyka odnios\u0142a skutek.
 
 # 3d window
 dialog.3d.title=Prune widok tr\u00f3jwymiarowy
@@ -339,7 +384,9 @@ confirm.reverserange=Odwr\u00f3cono zakres
 confirm.addtimeoffset=Dodano przesuni\u0119cie czasowe
 confirm.addaltitudeoffset=Dodano przesuni\u0119cie wysoko\u015bci
 confirm.rearrangewaypoints=Przestawiono punkty po\u015brednie
+confirm.rearrangephotos=Zmieniono kolejno\u015b\u0107 zdj\u0119\u0107
 confirm.cutandmove=Przesuni\u0119to zaznaczenie
+confirm.convertnamestotimes=Zmieniono nazwy punkt\u00f3w po\u015brednich
 confirm.saveexif.ok1=Zapisano
 confirm.saveexif.ok2=pliki zdj\u0119\u0107
 confirm.undo.single=cofni\u0119to operacj\u0119
@@ -351,9 +398,10 @@ 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.createpoint=stworzono punkt
+confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
 confirm.running=Przetwarzam dane ...
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=OK
 button.back=Poprzedni
 button.next=Nast\u0119pny
@@ -371,6 +419,7 @@ button.yes=Tak
 button.no=Nie
 button.yestoall=Tak na wszystko
 button.notoall=Nie na wszystko
+button.select=Zaznacz
 button.selectall=Zaznacz wszystko
 button.selectnone=Odznacz
 button.preview=Podgl\u0105d
@@ -378,6 +427,8 @@ button.load=\u0141aduj
 button.guessfields=Zgadnij pola
 button.showwebpage=Poka\u017c stron\u0119 web
 button.check=Sprawd\u017a
+button.resettodefaults=Przywr\u00f3\u0107 domy\u015blne
+button.browse=Przegl\u0105daj...
 
 # File types
 filetype.txt=Pliki TXT
@@ -389,7 +440,7 @@ filetype.gpx=Pliki GPX
 filetype.pov=Pliki POV
 filetype.svg=Pliki SVG
 
-# Display components
+# Display components || These are all for the side panels showing point/range details
 display.nodata=Nie za\u0142adowano danych
 display.noaltitudes=\u015acie\u017cki nie zawieraj\u0105 informacji o wysoko\u015bci
 details.trackdetails=Szczeg\u00f3\u0142y \u015bcie\u017cki
@@ -417,7 +468,9 @@ display.range.time.hours=h
 display.range.time.days=d
 details.range.avespeed=\u015arednia pr\u0119dko\u015b\u0107
 details.range.avemovingspeed=\u015arednie przesuni\u0119cie
+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.photodetails=Szczeg\u00f3\u0142y zdj\u0119cia
@@ -473,7 +526,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=W
 
-# Undo operations
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
 undo.load=za\u0142aduj dane
 undo.loadphotos=za\u0142aduj zdj\u0119cia
 undo.editpoint=edycja punktu
@@ -491,15 +544,22 @@ 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.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
 
 # Error messages
 error.save.dialogtitle=B\u0142\u0105d zapisu danych
 error.save.nodata=Brak danych do zapisu
-error.save.failed=Nie powi\u00f3d\u0142 si\u0119 zapis danych do pliku:
-error.saveexif.filenotfound=Nie znaleziono pliku zdj\u0119cia
+error.save.failed=Nie powi\u00f3d\u0142 si\u0119 zapis danych do pliku
+error.saveexif.filenotfound=Nie znaleziono pliku ze zdj\u0119ciem
 error.saveexif.cannotoverwrite1=Plik ze zdj\u0119ciem
-error.saveexif.cannotoverwrite2=jest w trybie tylko do odczytu i nie mo\u017ce zosta\u0107 nadpisany. Zapisa\u0107 do kopii?
+error.saveexif.cannotoverwrite2=jest w trybie tylko do odczytu i nie mo\u017ce zosta\u0107 nadpisany. Zapisa\u0107 kopi\u0119?
+error.saveexif.failed1=B\u0142\u0105d zapisu
+error.saveexif.failed2=zdj\u0119\u0107
+error.saveexif.forced1=
+error.saveexif.forced2=zdj\u0119\u0107 z wymuszonym zapisem
 error.load.dialogtitle=B\u0142\u0105d \u0142adowania danych
 error.load.noread=Nie mo\u017cna przeczyta\u0107 pliku
 error.load.nopoints=Nie znaleziono informacji o wsp\u00f3\u0142rz\u0119dnych w pliku
@@ -511,13 +571,16 @@ 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.gpsload.unknown=Nieznany b\u0142\u0105d
 error.undofailed.title=Cofnij nie powiod\u0142o si\u0119
 error.undofailed.text=Nie mo\u017cna cofn\u0105\u0107
 error.function.noop.title=Funkcja nie ma skutku
-error.rearrange.noop=Przestawienie punkt\u00f3w po\u015brednich nie przyniesie skutku
+error.rearrange.noop=Przestawienie punkt\u00f3w nie przyniesie skutku
 error.function.notavailable.title=Funkcja nie dost\u0119pna
 error.function.nojava3d=Ta funkcja wymaga biblioteki Java3d,\ndost\u0119pnej na Sun.com.
 error.3d=Nast\u0105pi\u0142 b\u0142\u0105d z wy\u015bwietlaniem 3D
 error.readme.notfound=Nie znaleziono pliku Readme
 error.osmimage.dialogtitle=B\u0142\u0105d przy \u0142adowaniu obraz\u00f3w map
-error.osmimage.failed=B\u0142\u0105d przy \u0142adowaniu obraz\u00f3w map. Sprawd\u017a po\u0142\u0105czenie z internetem.
\ No newline at end of file
+error.osmimage.failed=B\u0142\u0105d przy \u0142adowaniu obraz\u00f3w map. Sprawd\u017a po\u0142\u0105czenie z internetem.
+error.language.wrongfile=Wybrany plik nie jest plikiem z t\u0142umaczeniem dla Prune
+error.convertnamestotimes.nonames=\u017badne nazwy nie mog\u0142y zosta\u0107 zmienione na czas
index 53e0142d1d36bf310aa707c7aeeac64926274c8c..cc444d064cd3475e6c14b00bd5cb8e0dbef4df56 100644 (file)
 # Text entries for the Prune application
-# Portuguese entries as extra
+# (Brazilian) Portuguese entries as extra
 
 # Menu entries
 menu.file=Arquivo
 menu.file.open=Abrir
+menu.file.addphotos=Adicionar fotos
 menu.file.save=Salvar
 menu.file.exit=Sair
 menu.edit=Editar
+menu.track=Track
 menu.edit.undo=Desfazer
+menu.edit.clearundo=Limpar lista de desfazer
 menu.edit.editpoint=Editar ponto
+menu.edit.deletepoint=Remover ponto
+menu.edit.deleterange=Remover intervalo
+menu.edit.deletemarked=Remover pontos marcados
+menu.edit.interpolate=Interpolar
+menu.edit.average=Sele\u00e7\u00e3o m\u00e9dia
+menu.edit.reverse=Reverter intervalo
+menu.edit.mergetracksegments=Mesclar trechos da rota
+menu.edit.rearrange=Rearrumar pontos
+menu.edit.rearrange.start=Tudo para o in\u00edcio do arquivo
+menu.edit.rearrange.end=Tudo para o fim do arquivo
+menu.edit.rearrange.nearest=Cada um para o ponto da rota mais pr\u00f3ximo
+menu.edit.cutandmove=Recortar e mover sele\u00e7\u00e3o
 menu.select=Selecionar
+menu.range=Intervalo
+menu.point=Ponto
 menu.select.all=Selectionar tudo
-menu.select.none=Não selecionar nenhuns
+menu.select.none=N\u00e3o selecionar nenhuns
+menu.select.start=Definir in\u00edcio do intervalo
+menu.select.end=Definir fim do intervalo
 menu.photo=Foto
-menu.photo.saveexif=Salvar exif
-menu.photo.delete=Remover 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.view=Exibir
+menu.view.browser=Mapear no navegador
+menu.view.browser.google=Mapas do Google
+menu.view.browser.openstreetmap=Mapas do Openstreetmap
+menu.view.browser.mapquest=Mapas do Mapquest
+menu.view.browser.yahoo=Mapas do Yahoo
+menu.view.browser.bing=Mapas do Bing
+menu.settings=Configura\u00e7\u00f5es
 menu.help=Ajuda
 # Popup menu for map
-menu.map.zoomin=Ampliar zoom
-menu.map.zoomout=Reduzir zoom
+menu.map.zoomin=Ampliar
+menu.map.zoomout=Reduzir
+menu.map.zoomfull=Ajustar para tela
+menu.map.newpoint=Criar novo ponto
+menu.map.connect=Conectar pontos da rota
+menu.map.autopan=Auto-ajustar
 menu.map.showmap=Mostrar o mapa
+menu.map.showscalebar=Mostrar barra de escala
 
 # Alt keys for menus
 altkey.menu.file=A
 altkey.menu.edit=E
 altkey.menu.select=S
+altkey.menu.track=T
+altkey.menu.range=I
+altkey.menu.point=P
 altkey.menu.view=X
 altkey.menu.photo=F
-altkey.menu.help=A
+altkey.menu.settings=C
+altkey.menu.help=J
 
 # Ctrl shortcuts for menu items
 shortcut.menu.file.open=A
+shortcut.menu.file.load=C
 shortcut.menu.file.save=S
 shortcut.menu.edit.undo=Z
+shortcut.menu.edit.compress=C
 shortcut.menu.select.all=T
-shortcut.menu.help.help=H
+shortcut.menu.help.help=J
 
 # Functions
-function.exportkml=Exportar KML
-function.exportgpx=Exportar GPX
-function.exportpov=Exportar POV
-function.editwaypointname=Editar nome do waypoint
-function.charts=Diagramas
-function.show3d=Visualizar 3d
-function.distances=Distâncias
+function.loadfromgps=Carregar dados do GPS
+function.sendtogps=Enviar dados para o GPS
+function.exportkml=Exportar para KML
+function.exportgpx=Exportar para GPX
+function.exportpov=Exportar para POV
+function.editwaypointname=Editar nome do ponto
+function.compress=Comprimir rota
+function.addtimeoffset=Adicionar diferen\u00e7a de tempo
+function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
+function.convertnamestotimes=Converter nomes dos pontos para tempos
+function.findwaypoint=Encontrar ponto
+function.pastecoordinates=Inserir novas coordenadas
+function.charts=Gr\u00e1ficos
+function.show3d=Visualizar 3D
+function.distances=Dist\u00e2ncias
+function.fullrangedetails=Todos os detalhes
+function.setmapbg=Definir como fundo do mapa
+function.setkmzimagesize=Definir tamanho da imagem KMZ
+function.setpaths=Definir caminhos do programa
+function.getgpsies=Obter rotas Gpsies
+function.duplicatepoint=Duplicar ponto
+function.setcolours=Definir cores
+function.setlanguage=Definir idioma
+function.correlatephotos=Correlacionar fotos
+function.rearrangephotos=Rearrumar fotos
+function.rotatephotoleft=Roda foto \u00e0 esquerda
+function.rotatephotoright=Roda foto \u00e0 direita
+function.ignoreexifthumb=Ignorar miniatura do exif
 function.help=Ajuda
+function.showkeys=Mostrar atalhos de teclado
 function.about=Sobre o Prune
+function.checkversion=Verificar novas vers\u00f5es
+function.saveconfig=Salvar configura\u00e7\u00f5es
 
 # Dialogs
+dialog.exit.confirm.title=Sair do Prune
+dialog.exit.confirm.text=Seus dados n\u00e3o foram salvos. Voc\u00ea tem certeza que deseja sair?
+dialog.openappend.title=Adicionar aos dados existentes
+dialog.openappend.text=Adicionar estes dados aos dados j\u00e1 carregados?
+dialog.deletepoint.title=Remover Ponto
+dialog.deletepoint.deletephoto=Remover foto anexada a este ponto?
+dialog.deletephoto.title=Remover Foto
+dialog.deletephoto.deletepoint=Remover ponto anexado a esta foto?
+dialog.openoptions.title=Op\u00e7\u00f5es de abertura
+dialog.openoptions.filesnippet=Extrair do arquivo
+dialog.load.table.field=Campo
+dialog.load.table.datatype=Tipo de Dado
+dialog.load.table.description=Descri\u00e7\u00e3o
+dialog.delimiter.label=Delimitador do campo
+dialog.delimiter.comma=V\u00edrgula ,
+dialog.delimiter.tab=Tabula\u00e7\u00e3o
+dialog.delimiter.space=Espa\u00e7o
+dialog.delimiter.semicolon=Ponto e v\u00edrgula ;
+dialog.delimiter.other=Outro
+dialog.openoptions.deliminfo.records=registros, com
+dialog.openoptions.deliminfo.fields=campos
+dialog.openoptions.deliminfo.norecords=Sem registros
+dialog.openoptions.altitudeunits=Unidades de altitude
+dialog.jpegload.subdirectories=Incluir subpastas
+dialog.jpegload.loadjpegswithoutcoords=Incluir fotos sem coordenadas
+dialog.jpegload.loadjpegsoutsidearea=Incluir fotos fora da \u00e1rea atual
+dialog.jpegload.progress.title=Carregando fotos
+dialog.jpegload.progress=Por favor, espere enquanto as fotos s\u00e3o procuradas
+dialog.gpsload.nogpsbabel=Nenhum programa gpsbabel pode ser encontrado. Continuar?
+dialog.gpsload.device=Nome do dispositivo
+dialog.gpsload.format=Formato
+dialog.gpsload.getwaypoints=Carregar pontos
+dialog.gpsload.gettracks=Carregar rotas
+dialog.gpsload.save=Salvar para arquivo
+dialog.gpssend.sendwaypoints=Enviar pontos
+dialog.gpssend.sendtracks=Enviar rotas
+dialog.gpssend.trackname=Nome da rota
+dialog.saveoptions.title=Salvar arquivo
+dialog.save.fieldstosave=Campos a salvar
+dialog.save.table.field=Campo
+dialog.save.table.hasdata=Possui dados
+dialog.save.table.save=Salvar
+dialog.save.headerrow=Sa\u00edda da linha de cabe\u00e7alho
+dialog.save.coordinateunits=Unidades das coordenadas
+dialog.save.altitudeunits=Unidades da altitude
+dialog.save.timestampformat=Formato da data-hora
+dialog.save.overwrite.title=Arquivo j\u00e1 existe
+dialog.save.overwrite.text=Este arquivo j\u00e1 existe. Voc\u00ea tem certeza que deseja sobrescrev\u00ea-lo?
+dialog.save.notypesselected=Nenhum tipo de ponto foi selecionado
+dialog.exportkml.text=T\u00edtulo para os dados
+dialog.exportkml.altitude=Incluir altitudes (para avia\u00e7\u00e3o)
+dialog.exportkml.kmz=Comprimir para criar arquivo kmz
+dialog.exportkml.exportimages=Exportar miniaturas de imagem para o kmz
+dialog.exportkml.trackcolour=Cor da rota
+dialog.exportgpx.name=Nome
+dialog.exportgpx.desc=Descri\u00e7\u00e3o
+dialog.exportgpx.includetimestamps=Incluir data-hora
+dialog.exportgpx.copysource=Copiar fonte xml
+dialog.exportpov.text=Por favor, insira os par\u00e2metros para a exporta\u00e7\u00e3o POV
+dialog.exportpov.font=Fonte
+dialog.exportpov.camerax=X da C\u00e2mera
+dialog.exportpov.cameray=Y da C\u00e2mera
+dialog.exportpov.cameraz=Z da C\u00e2mera
+dialog.exportpov.modelstyle=Estilo do modelo
+dialog.exportpov.ballsandsticks=Bolas e galhos
+dialog.exportpov.tubesandwalls=Tubos e muros
+dialog.exportpov.warningtracksize=Esta rota possui um grande n\u00famero de pontos, os quais o Java3D n\u00e3o ser\u00e1 capaz de exibir.\n Voc\u00ea tem certeza que deseja continuar?
+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.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?
+dialog.confirmcutandmove.title=Confirmar cortar e mover
+dialog.confirmcutandmove.text=A rota cont\u00e9m informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s o movimento.\n Voc\u00ea tem certeza que deseja mover esta se\u00e7\u00e3o?
+dialog.interpolate.title=Interpolar pontos
+dialog.interpolate.parameter.text=N\u00famero de pontos para inserir entre os pontos selecionados
+dialog.undo.title=A\u00e7\u00e3o(\u00f5es) de desfazer
+dialog.undo.pretext=Por favor, selecione a a\u00e7\u00e3o(\u00f5es) a desfazer
+dialog.undo.none.title=N\u00e3o foi poss\u00edvel desfazer
+dialog.undo.none.text=Nenhuma opera\u00e7\u00e3o a desfazer!
+dialog.clearundo.title=Limpar lista de desfazer
+dialog.clearundo.text=Voc\u00ea tem certeza que deseja limpar a lista de desfazer?\n Todas as informa\u00e7\u00f5es para desfazer ser\u00e3o perdidas!
+dialog.pointedit.title=Editar ponto
+dialog.pointedit.text=Selecionar cada campo para editar e usar o bot\u00e3o 'Editar' para mudar o valor
+dialog.pointedit.table.field=Campo
+dialog.pointedit.table.value=Valor
+dialog.pointedit.table.changed=Alterado
+dialog.pointedit.changevalue.text=Insira o novo valor para este campo
+dialog.pointedit.changevalue.title=Editar campo
+dialog.pointnameedit.name=Nome do ponto
+dialog.pointnameedit.uppercase=MAI\u00daSCULAS
+dialog.pointnameedit.lowercase=min\u00fasculas
+dialog.pointnameedit.sentencecase=Frase
+dialog.addtimeoffset.add=Adicionar tempo
+dialog.addtimeoffset.subtract=Subtrair tempo
+dialog.addtimeoffset.days=Dias
+dialog.addtimeoffset.hours=Horas
+dialog.addtimeoffset.minutes=Minutos
+dialog.addtimeoffset.notimestamps=N\u00e3o foi poss\u00edvel adicionar uma diferen\u00e7a de tempo uma vez que esta sele\u00e7\u00e3o n\u00e3o possui nenhuma informa\u00e7\u00e3o de data-hora
+dialog.findwaypoint.intro=Insira parte do nome do ponto
+dialog.findwaypoint.search=Pesquisar
+dialog.connect.title=Conectar foto ao ponto
+dialog.connectphoto.clonepoint=Este ponto j\u00e1 possui uma foto.\n Voc\u00ea deseja fazer uma c\u00f3pia deste ponto?
+dialog.saveexif.title=Salvar Exif
+dialog.saveexif.intro=Selecionar as fotos para salvar usando as caixas de sele\u00e7\u00e3o
+dialog.saveexif.nothingtosave=Dados das coordenadas n\u00e3o foram alterados, nada para salvar
+dialog.saveexif.noexiftool=Nenhum programa exiftool pode ser encontrado. Continuar?
+dialog.saveexif.table.photoname=Nome da foto
+dialog.saveexif.table.status=Estado
+dialog.saveexif.table.save=Salvar
+dialog.saveexif.photostatus.connected=Conectada
+dialog.saveexif.photostatus.disconnected=Desconectada
+dialog.saveexif.photostatus.modified=Modificada
+dialog.saveexif.overwrite=Sobrescrever arquivos
+dialog.saveexif.force=For\u00e7ar ignorar erros menores
+dialog.charts.xaxis=Eixo X
+dialog.charts.yaxis=Eixo Y
+dialog.charts.output=Sa\u00edda
+dialog.charts.screen=Sa\u00edda para tela
+dialog.charts.svg=Sa\u00edda para arquivo SVG
+dialog.charts.svgwidth=Largura do SVG
+dialog.charts.svgheight=Altura do SVG
+dialog.charts.needaltitudeortimes=A rota deve possuir informa\u00e7\u00f5es de tempo ou altitude para criar gr\u00e1ficos
+dialog.charts.gnuplotnotfound=N\u00e3o foi poss\u00edvel encontrar o gnuplot com o caminho fornecido
+dialog.distances.intro=Dist\u00e2ncias em linha reta entre pontos
+dialog.distances.column.from=Do ponto
+dialog.distances.column.to=Para o ponto
+dialog.distances.currentpoint=Ponto atual
+dialog.distances.toofewpoints=Esta fun\u00e7\u00e3o precisa de pontos para calcular a dist\u00e3ncia entre eles
+dialog.fullrangedetails.intro=Aqui est\u00e3o os detalhes para o intervalo selecionado
+dialog.setmapbg.mapnik=Mapnik (padr\u00e3o)
+dialog.setmapbg.osma=Osma
+dialog.setmapbg.cyclemap=Cyclemap
+dialog.setmapbg.other=Outro
+dialog.setmapbg.server=URL do Servidor
+dialog.gpsies.column.name=Nome da rota
+dialog.gpsies.column.length=Extens\u00e3o
+dialog.gpsies.description=Descri\u00e7\u00e3o
+dialog.gpsies.nodescription=Sem descri\u00e7\u00e3o
+dialog.gpsies.nonefound=Nenhuma rota encontrada
+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.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
+dialog.correlate.options.offset=Diferen\u00e7a
+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.limitspanel=Limites de correla\u00e7\u00e3o
+dialog.correlate.options.notimelimit=Nenhum limite de tempo
+dialog.correlate.options.timelimit=Limite de tempo
+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.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
+dialog.rearrangephotos.nosort=N\u00e3o ordenar
+dialog.rearrangephotos.sortbyfilename=Ordenar pelo nome do arquivo
+dialog.rearrangephotos.sortbytime=Ordenar pela hora
+dialog.compress.nonefound=Nenhum dado dos pontos pode ser removido
+dialog.compress.closepoints.title=Remo\u00e7\u00e3o de ponto pr\u00f3ximo
+dialog.compress.closepoints.paramdesc=Fator de 'span'
+dialog.compress.wackypoints.title=Remo\u00e7\u00e3o de ponto exc\u00eantrica
+dialog.compress.wackypoints.paramdesc=Fator de dist\u00e3ncia
+dialog.compress.singletons.title=Remo\u00e7\u00e3o avulsa
+dialog.compress.singletons.paramdesc=Fator de dist\u00e2ncia
+dialog.compress.duplicates.title=Remo\u00e7\u00e3o de duplicado
+dialog.compress.summarylabel=Pontos para remover
+dialog.pastecoordinates.desc=Insira ou cole as coordenadas aqui
+dialog.pastecoordinates.coords=Coordenadas
+dialog.pastecoordinates.nothingfound=Por favor, verifique as coordenadas novamente
+dialog.help.help=Por favor, veja\n http://activityworkshop.net/software/prune/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
+dialog.about.version=Vers\u00e3o
+dialog.about.build=Compila\u00e7\u00e3o
+dialog.about.summarytext1=Prune \u00e9 um programa para carregar, exibir e editar dados de receptores de GPS.
+dialog.about.summarytext2=Isto est\u00e1 lan\u00e7ado sob a Gnu GPL para uso e melhoria livre, aberto e em todo o mundo.<br>A c\u00f3pia, redistribui\u00e7\u00e3o e modifica\u00e7\u00e3o s\u00e3o permitidas e encorajadas<br>de acordo coma as condi\u00e7\u00f5es no arquivo <code>license.txt</code>inclu\u00eddo.
+dialog.about.summarytext3=Por favor, veja <code style="font-weight:bold">http://activityworkshop.net/</code> para mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
+dialog.about.languages=Idiomas dispon\u00edveis
+dialog.about.translatedby=Texto em portugu\u00eas por amigo anónimo.
+dialog.about.systeminfo=Informa\u00e7\u00f5es do sistema
+dialog.about.systeminfo.os=Sistema Operacional
+dialog.about.systeminfo.java=M\u00f3dulo Java
+dialog.about.systeminfo.java3d=Java3d instalado
+dialog.about.systeminfo.povray=Povray instalado
+dialog.about.systeminfo.exiftool=Exittool instalado
+dialog.about.systeminfo.gpsbabel=Gpsbabel instalado
+dialog.about.systeminfo.gnuplot=Gnuplot instalado
 dialog.about.yes=Sim
-dialog.about.no=Não
+dialog.about.no=N\u00e3o
+dialog.about.credits=Cr\u00e9ditos
+dialog.about.credits.code=C\u00f3digo do Prune escrito por
+dialog.about.credits.exifcode=C\u00f3digo do Exif por
+dialog.about.credits.icons=Alguns \u00edcones obtidos de
+dialog.about.credits.translators=Tradutores
+dialog.about.credits.translations=Tradu\u00e7\u00f5es auxiliadas por
+dialog.about.credits.devtools=Ferramentas de desenvolvimento
+dialog.about.credits.othertools=Outras ferramentas
+dialog.about.credits.thanks=Agradecimentos a
+dialog.about.readme=Leiame
+dialog.checkversion.error=O n\u00famero da vers\u00e3o n\u00e3o pode ser verificado.\n Por favor, verifique a conex\u00e3o \u00e0 Internet.
+dialog.checkversion.uptodate=Voc\u00ea est\u00e1 usando a \u00faltima vers\u00e3o do Prune.
+dialog.checkversion.newversion1=Uma nova vers\u00e3o do Prune est\u00e1 dispon\u00edvel! A \u00faltima vers\u00e3o \u00e9 agora a vers\u00e3o
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Esta nova vers\u00e3o foi lan\u00e7ada em
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://activityworkshop.net/software/prune/download.html.
+dialog.keys.intro=Voc\u00ea pode usar os seguintes atalhos de teclado ao inv\u00e9s de usar o mouse
+dialog.keys.keylist=<table><tr><td>Cursores</td><td>Move o mapa para esquerda, direita, acima e abaixo</td></tr><tr><td>Ctrl + cursores esquerdo e direito</td><td>Seleciona o pr\u00f3ximo ponto ou o anterior</td></tr><tr><td>Ctrl + cursores acima e abaixo</td><td>Amplia ou reduz</td></tr><tr><td>Del</td><td>Remove o ponto atual</td></tr></table>
+dialog.saveconfig.desc=As seguintes configura\u00e7\u00f5es podem ser salvas para um arquivo de configura\u00e7\u00e3o.
+dialog.saveconfig.prune.trackdirectory=Pasta de rotas
+dialog.saveconfig.prune.photodirectory=Pasta de fotos
+dialog.saveconfig.prune.languagecode=C\u00f3digo do idioma (PT_BR)
+dialog.saveconfig.prune.languagefile=Arquivo de idioma
+dialog.saveconfig.prune.gpsdevice=Dispositivo de GPS
+dialog.saveconfig.prune.gpsformat=Formato do GPS
+dialog.saveconfig.prune.povrayfont=Fonte Povray
+dialog.saveconfig.prune.metricunits=Usar unidades m\u00e9tricas?
+dialog.saveconfig.prune.gnuplotpath=Caminho para o gnuplot
+dialog.saveconfig.prune.gpsbabelpath=Caminho para o gpsbabel
+dialog.saveconfig.prune.exiftoolpath=Caminho para o exiftool
+dialog.saveconfig.prune.mapserverindex=\u00cdndice do servidor de mapas
+dialog.saveconfig.prune.mapserverurl=URL do servidor 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.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.addaltitude.noaltitudes=O intervalo selecionado n\u00e3o cont\u00e9m altitudes
+dialog.addaltitude.desc=Diferen\u00e7a de altitude a adicionar
+dialog.setcolours.intro=Clique em um trecho de cor para mudar a cor
+dialog.setcolours.background=Fundo
+dialog.setcolours.borders=Bordas
+dialog.setcolours.lines=Linhas
+dialog.setcolours.primary=Prim\u00e1rio
+dialog.setcolours.secondary=Secund\u00e1rio
+dialog.setcolours.point=Pontos
+dialog.setcolours.selection=Sele\u00e7\u00e3o
+dialog.setcolours.text=Texto
+dialog.colourchooser.title=Selecionar cor
+dialog.colourchooser.red=Vermelho
+dialog.colourchooser.green=Verde
+dialog.colourchooser.blue=Azul
+dialog.setlanguage.firstintro=Voc\u00ea pode selecionar um dos idiomas inclu\u00eddos,<p>ou selecionar um arquivo de texto para usar.
+dialog.setlanguage.secondintro=Voc\u00ea precisa salvar suas configura\u00e7\u00f5es e ent\u00e3o<p>reiniciar o Prune para mudar o idioma.
+dialog.setlanguage.language=Idioma
+dialog.setlanguage.languagefile=Arquivo de idioma
+dialog.setlanguage.endmessage=Agora salve suas configura\u00e7\u00f5es e reinicie o Prune\npara que a mudan\u00e7a de idioma ocorra.
 
-# Buttons
+# 3d window
+dialog.3d.title=Vista 3D do Prune
+dialog.3d.altitudecap=Intervalo de altitude m\u00ednimo
+dialog.3dlines.title=Linhas da grade do Prune
+dialog.3dlines.empty=Nenhuma linha de grade para exibir!
+dialog.3dlines.intro=Estas s\u00e3o as linhas da grade para a vista 3D.
+
+# Confirm messages || These are displayed as confirmation in the status bar
+confirm.loadfile=Dados carregados do arquivo
+confirm.save.ok1=Salvo com sucesso
+confirm.save.ok2=pontos para arquivo
+confirm.deletepoint.single=dados do ponto foram removidos
+confirm.deletepoint.multi=dados dos pontos foram removidos
+confirm.point.edit=ponto editado
+confirm.mergetracksegments=Segmento da rota mesclado
+confirm.reverserange=Intervalo invertido
+confirm.addtimeoffset=Diferen\u00e7a de tempo adicionada
+confirm.addaltitudeoffset=Diferen\u00e7a de altitude adicionadas
+confirm.rearrangewaypoints=Pontos rearrumados
+confirm.rearrangephotos=Fotos rearrumadas
+confirm.cutandmove=Sele\u00e7\u00e3o movida
+confirm.convertnamestotimes=Nomes dos pontos convertidos
+confirm.saveexif.ok1=Salvo
+confirm.saveexif.ok2=arquivos de foto
+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.photo.disconnect=foto desconectada
+confirm.correlate.single=foto foi correlacionada
+confirm.correlate.multi=fotos foram correlacionadas
+confirm.createpoint=ponto criado
+confirm.rotatephoto=foto rotacionada
+confirm.running=Rodando...
+
+# Buttons || These are all the texts for buttons
+button.ok=Ok
+button.back=Voltar
+button.next=P\u0155oximo
+button.finish=Terminar
 button.cancel=Cancelar
+button.overwrite=Sobrescrever
+button.moveup=Mover acima
+button.movedown=Mover abaixo
+button.showlines=Mostrar linhas
 button.edit=Editar
 button.exit=Sair
 button.close=Fechar
+button.continue=Continuar
 button.yes=Sim
-button.no=Não
+button.no=N\u00e3o
+button.yestoall=Sim para todos
+button.notoall=N\u00e3o para todos
+button.select=Selecionar
+button.selectall=Selecionar todos
+button.selectnone=Selecionar nenhum
+button.preview=Previs\u00e3o
+button.load=Carregar
+button.guessfields=Campos adivinhados
+button.showwebpage=Mostrar p\u00e1gina Web
+button.check=Verificar
+button.resettodefaults=Restaurar para os padr\u00f5es
+button.browse=Navegar...
 
-# Display components
-display.nodata=Nenhum dados foi carregado
+# File types
+filetype.txt=Arquivos TXT
+filetype.jpeg=Arquivos JPG
+filetype.kmlkmz=Arquivos KML, KMZ
+filetype.kml=Arquivos KML
+filetype.kmz=Arquivos KMZ
+filetype.gpx=Arquivos GPX
+filetype.pov=Arquivos POV
+filetype.svg=Arquivos SVG
+
+# Display components || These are all for the side panels showing point/range details
+display.nodata=Nenhum dado carregado
+display.noaltitudes=Dados da rota n\u00e3o incluem altitudes
 details.trackdetails=Detalhes da track
+details.notrack=Nenhuma rota carrgeada
 details.track.points=Pontos
 details.track.file=Arquivo
-details.track.numfiles=Número de arquivos
+details.track.numfiles=N\u00famero de arquivos
 details.pointdetails=Detalhes da ponto
+details.index.selected=\u00cdndice
 details.index.of=de
+details.nopointselection=Nenhum ponto selecionado
+details.photofile=Arquivo de foto
+details.norangeselection=Nenhum intervalo selecionado
 details.rangedetails=Detalhes da range
 details.range.selected=Selecionado
 details.range.to=a
 details.altitude.to=a
+details.range.climb=Subir
+details.range.descent=Descer
+details.coordformat=Formato das coordenadas
+details.distanceunits=Unidades de dist\u00e2ncia
+display.range.time.secs=s
+display.range.time.mins=m
+display.range.time.hours=h
+display.range.time.days=d
+details.range.avespeed=Velocidade m\u00e9dia
+details.range.avemovingspeed=Movimento m\u00e9dio
+details.range.numsegments=N\u00famero de segmentos
+details.range.pace=Passo
+details.range.gradient=Gradiente
+details.waypointsphotos.waypoints=Pontos
 details.waypointsphotos.photos=Fotos
 details.photodetails=Detalhes da foto
+details.nophoto=Nenhuma foto selecionada
 details.photo.loading=Carregando
+details.photo.connected=Conectada
+map.overzoom=Nenhum mapa dispon\u00edvel neste n\u00edvel de amplia\u00e7\u00e3o
 
 # Field names
 fieldname.latitude=Latitude
 fieldname.longitude=Longitude
 fieldname.altitude=Altura
+fieldname.timestamp=Tempo
 fieldname.time=Tempo
-fieldname.distance=Distância
+fieldname.waypointname=Nome
+fieldname.waypointtype=Tipo
+fieldname.newsegment=Segmento
+fieldname.custom=Personalizado
+fieldname.prefix=Campo
+fieldname.distance=Dist\u00e2ncia
+fieldname.movingdistance=Dist\u00e2ncia de movimento
+fieldname.duration=Dura\u00e7\u00e3o
 fieldname.speed=Velocidade
+fieldname.verticalspeed=Velocidade vertical
+
+# Measurement units
+units.original=Original
+units.default=Padr\u00e3o
+units.metres=Metros
+units.metres.short=m
+units.feet=P\u00e9s
+units.feet.short=ft
+units.kilometres=Quil\u00f4metros
+units.kilometres.short=km
+units.kmh=km/h
+units.miles=Milhas
+units.miles.short=mi
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=horas
+units.degminsec=Graus-min-seg
+units.degmin=Graus-min
+units.deg=Graus
+units.iso8601=ISO 8601
 
 # External urls
 url.googlemaps=maps.google.pt
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=L
+cardinal.w=O
+
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
+undo.load=carregar dados
+undo.loadphotos=carregar fotos
+undo.editpoint=editar ponto
+undo.deletepoint=remover ponto
+undo.deletephoto=remover foto
+undo.deleterange=remover intervalo
+undo.compress=comprimir rota
+undo.insert=inserir pontos
+undo.reverse=inverter intervalo
+undo.mergetracksegments=mesclar segmentos de rota
+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.rearrangephotos=rearrumar fotos
+undo.createpoint=criar ponto
+undo.rotatephoto=rotacionar foto
+undo.convertnamestotimes=converter nomes para tempos
+
+# Error messages
+error.save.dialogtitle=Erro ao salvar dados
+error.save.nodata=Nenhum dado para salvar
+error.save.failed=Falha ao salvar dados para arquivo
+error.saveexif.filenotfound=Falha ao procurar o arquivo de foto
+error.saveexif.cannotoverwrite1=Arquivo de foto
+error.saveexif.cannotoverwrite2=\u00e9 somente leitura e n\u00e3o pode ser sobrescrito. Gravar para c\u00f3pia?
+error.saveexif.failed1=Falha ao salvar
+error.saveexif.failed2=das imagens
+error.saveexif.forced1=
+error.saveexif.forced2=das imagens for\u00e7adas por solicita\u00e7\u00e3o
+error.load.dialogtitle=Erro ao carregar dados
+error.load.noread=N\u00e3o foi poss\u00edvel ler arquivo
+error.load.nopoints=Nenhuma informa\u00e7\u00e3o de coordenadas encontrada no arquivo
+error.load.unknownxml=Formato xml n\u00e3o reconhecido:
+error.load.noxmlinzip=Nenhum arquivo xml encontrado dentro do arquivo zip
+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.gpsload.unknown=Erro desconhecido
+error.undofailed.title=Falha ao desfazer
+error.undofailed.text=Falha para desfazer opera\u00e7\u00e3o
+error.function.noop.title=Fun\u00e7\u00e3o sem nenhum efeito
+error.rearrange.noop=Rearruma\u00e7\u00e3o de pontos n\u00e3o teve efeito
+error.function.notavailable.title=Fun\u00e7\u00e3o n\u00e3o dispon\u00edvel
+error.function.nojava3d=Esta fun\u00e7\u00e3o precisa da biblioteca Java3d,\ndispon\u00edvel em Sun.com
+error.3d=Um erro ocorreu com a exibi\u00e7\u00e3o 3D
+error.readme.notfound=Arquivo Leiame n\u00e3o encontrado
+error.osmimage.dialogtitle=Erro ao carregar imagens do mapa
+error.osmimage.failed=Falha ao carregar imagens do mapa. Por favor, verifique a conex\u00e3o \u00e0 Internet.
+error.language.wrongfile=O arquivo selecionado n\u00e3o parece ser um arquivo de idioma do Prune
+error.convertnamestotimes.nonames=Nenhum nome pode ser convertido para tempo
index 9e9b22e5e0ae4e09bb59e60f88c37bd26120390f..e416762c00bdafdabb8484b572a71b5bb9212b33 100644 (file)
@@ -2,18 +2,19 @@
 # Romanian entries as extra
 
 # Menu entries
-menu.file=Fi\u015Fier
-menu.file.open=Deschidere fi\u015Fier
+menu.file=Fi\u015fier
+menu.file.open=Deschidere fi\u015fier
 menu.file.addphotos=Adaugare foto
 menu.file.save=Salvare
 menu.file.exit=Iesire
+menu.track=Traseu
 menu.edit=Editare
 menu.edit.undo=Anulare
-menu.edit.clearundo=\u015Etergere lista de anulari
+menu.edit.clearundo=\u015etergere lista de anulari
 menu.edit.editpoint=Editare punct
-menu.edit.deletepoint=\u015Etergere punct
-menu.edit.deleterange=\u015Etergere gama
-menu.edit.deletemarked=\u015Etergere puncte marcate
+menu.edit.deletepoint=\u015etergere punct
+menu.edit.deleterange=\u015etergere gama
+menu.edit.deletemarked=\u015etergere puncte marcate
 menu.edit.interpolate=Interpolare
 menu.edit.average=Mediere selectie
 menu.edit.reverse=Inversare selectie
@@ -23,6 +24,7 @@ menu.edit.rearrange.start=Toate la inceputul fisierului
 menu.edit.rearrange.end=Toate la sfarsitul fisierului
 menu.edit.rearrange.nearest=Fiecare la punctul cel mai apropiat al traseului
 menu.edit.cutandmove=Taiere si mutare selectie
+menu.point=Punct
 menu.select=Selectare
 menu.select.all=Selectare toate
 menu.select.none=Nu selecta niciun punct
@@ -39,12 +41,16 @@ menu.view.browser.google=Harti Google
 menu.view.browser.openstreetmap=Openstreetmap
 menu.view.browser.mapquest=Mapquest
 menu.view.browser.yahoo=Harti Yahoo
+menu.view.browser.bing=Harti Bing
 menu.help=Ajutor
 # Popup menu for map
-menu.map.zoomin=Apropie in
-menu.map.zoomout=Apropie out
+menu.map.zoomin=Apropie
+menu.map.zoomout=Departeaza
+menu.map.zoomfull=Departeaza la maxim
 menu.map.newpoint=Adaug\u0103 punct
-menu.map.connect=Traseaz\u0103 linii Ã®ntre puncte
+menu.map.connect=Traseaz\u0103 linii \u00eentre puncte
+menu.map.autopan=Autovizualizare punct ales
+menu.map.showmap=Arata harta
 
 # Alt keys for menus
 altkey.menu.file=F
@@ -61,31 +67,107 @@ shortcut.menu.edit.undo=Z
 shortcut.menu.select.all=T
 
 # Functions
-function.exportkml=Export\u0103 Ã®ntr-un fi\u015Fier KML
-function.exportgpx=Export\u0103 Ã®ntr-un fi\u015Fier GPX
-function.exportpov=Export\u0103 Ã®ntr-un fi\u015Fier POV
+function.loadfromgps=\u00cencarc\u0103 date de la GPS
+function.sendtogps=Trimite date spre GPS
+function.exportkml=Export\u0103 \u00eentr-un fi\u015fier KML
+function.exportgpx=Export\u0103 \u00eentr-un fi\u015fier GPX
+function.exportpov=Export\u0103 \u00eentr-un fi\u015fier POV
 function.editwaypointname=Editare nume waypoint
+function.compress=Comprima traseu
+function.charts=Grafice
+function.show3d=Vizualizare arborescenta
+function.distances=Distan\u0163e
+function.setmapbg=Fundal
+function.correlatephotos=Corela fotografii
+function.setcolours=Selectare culorile
+function.setlanguage=Selectare limba
 function.help=Ajutor
+function.showkeys=Arat\u0103 tastele scurt\u0103turi
 function.about=Despre Prune
+function.checkversion=Verific\u0103 pentru o versiune noua
 
 # Dialogs
-dialog.save.overwrite.text=Fi\u015Fierul exist\u0103. ÃŽl suprascriu?
-dialog.pointedit.text=V\u0103 rog selecta\u0163i rândul care va fi editat
+dialog.exit.confirm.title=Ie\u015fire din programul Prune
+dialog.exit.confirm.text=Datele dumneavoastra nu sunt salvate.\nSunte\u0163i sigur ca\u0103 dori\u0163i s\u0103 ie\u015fiti?
+dialog.openappend.title=Adauga la datele existente
+dialog.openappend.text=Adauga la datele deja incarcate?
+dialog.deletepoint.title=Sterge Punct
+dialog.deletepoint.deletephoto=Sterg fotografiile atasate acestui punct?
+dialog.deletephoto.title=Sterge Foto
+dialog.deletephoto.deletepoint=Sterg punct atasat acestei fotografii?
+dialog.openoptions.title=Optiuni deschidere
+dialog.load.table.field=Cimp
+dialog.load.table.datatype=Tip data
+dialog.load.table.description=Descriere
+dialog.delimiter.label=Delimitator cimp
+dialog.delimiter.comma=Virgula ,
+dialog.delimiter.tab=Tab
+dialog.delimiter.space=Spatiu
+dialog.delimiter.semicolon=Punct si virgula :
+dialog.delimiter.other=Altul
+dialog.openoptions.deliminfo.fields=cimpuri
+dialog.openoptions.deliminfo.norecords=Nu sunt inregistrari
+dialog.save.overwrite.text=Fi\u015fierul exist\u0103. \u00cel suprascriu?
+dialog.pointedit.text=V\u0103 rog selecta\u0163i r\u00e2ndul care va fi editat
+dialog.pointedit.table.field=Cimp
 dialog.pointedit.table.value=Valoare
+dialog.pointedit.table.changed=Schimbat
+dialog.pointedit.changevalue.text=Introdu noua valoare pentru acest cimp
+dialog.pointedit.changevalue.title=Modifica cimp
+dialog.pointnameedit.uppercase=Litere MARI
+dialog.pointnameedit.lowercase=Litere mici
+dialog.addtimeoffset.days=Zile
+dialog.addtimeoffset.hours=Ore
+dialog.addtimeoffset.minutes=Minute
 dialog.findwaypoint.search=C\u0103utare
+dialog.saveexif.table.status=Stare
+dialog.saveexif.table.save=Salveaza
+dialog.saveexif.photostatus.connected=Conectat
+dialog.saveexif.photostatus.disconnected=Deconectat
+dialog.saveexif.photostatus.modified=Modificat
+dialog.saveexif.overwrite=Suprascrie fisiere
+dialog.charts.xaxis=Axa X
+dialog.charts.yaxis=Axa Y
+dialog.distances.currentpoint=Punct curent
 dialog.setmapbg.mapnik=Mapnik (implicit)
 dialog.setmapbg.server=Adres\u0103 server
+dialog.gpsies.column.length=Lungime
+dialog.gpsies.description=Descriere
+dialog.gpsies.nodescription=Fara descriere
 dialog.correlate.options.tip=Indiciu: By manually correlating at least one photo, the time offset can be calculated for you.
 dialog.about.version=Versiunea
-dialog.about.readme=Cite\u015Fte-m\u0103
+dialog.about.readme=Cite\u015fte-m\u0103
+dialog.checkversion.releasedate1=Aceasta versiune noua a fost lansapa pe
+dialog.checkversion.releasedate2=.
+
+# Confirm messages || These are displayed as confirmation in the status bar
+confirm.loadfile=Date incarcate din fisier
+confirm.save.ok1=Salvat cu succes
 
 # Buttons
 button.ok=OK
+button.back=Inapoi
+button.next=Urmator
+button.finish=Terminat
 button.cancel=Renun\u0163\u0103
+button.overwrite=Suprascrie
+button.moveup=Muta in sus
+button.movedown=Muta in jos
 button.edit=Editare
 button.exit=Iesire
+button.close=Inchide
+button.continue=Continua
+button.yes=Da
+button.no=Nu
+button.yestoall=Da pentru tot
+button.notoall=Nu pentru tot
+button.select=Selectare
+button.selectall=Selecteaza tot
+button.selectnone=Deselecteaza tot
+button.load=Incarca
 
 # File types
+filetype.txt=Fisier text
 filetype.jpeg=Imagine JPEG (*.jpg)
 
 # Display components
@@ -94,9 +176,28 @@ details.pointdetails=Punct
 details.range.selected=Selectat
 details.range.to=la
 details.altitude.to=la
+details.coordformat=Format coordonate
+details.distanceunits=Unitati de distanta
+display.range.time.secs=s
+display.range.time.mins=m
+display.range.time.hours=h
+display.range.time.days=z
+details.range.avespeed=Viteza medie
 
 # Field names
+fieldname.latitude=Latitudine
+fieldname.longitude=Longitudine
+fieldname.altitude=Altitudine
 fieldname.waypointname=Nume
+fieldname.distance=Distanta
+fieldname.duration=Durata
+fieldname.speed=Viteza
 
 # Measurement units
 units.default=Implicit
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=E
+cardinal.w=V
diff --git a/tim/prune/lang/prune-texts_tr.properties b/tim/prune/lang/prune-texts_tr.properties
new file mode 100644 (file)
index 0000000..20fe689
--- /dev/null
@@ -0,0 +1,436 @@
+# Text entries for the Prune application
+# Turkish entries as extra
+
+# Menu entries
+menu.file=Dosya
+menu.file.open=Dosya a\u00e7
+menu.file.addphotos=Foto ekle
+menu.file.save=Kaydet
+menu.file.exit=Ç\u0131k\u0131\u015f
+menu.edit=D\u00fczenle
+menu.track=\u0130z
+menu.edit.undo=Geri al
+menu.edit.clearundo=Geri alma listesi s\u0131f\u0131rla
+menu.edit.editpoint=Nokta d\u00fczenle
+menu.edit.deletepoint=Noktay\u0131 sil
+menu.edit.deleterange=S\u0131ray\u0131 sil
+menu.edit.deletemarked=Se\u00e7ili noktalar\u0131 sil
+menu.edit.interpolate=\u0130nterpolasyon
+menu.edit.average=Se\u00e7me ortala
+menu.edit.reverse=S\u0131ra tersine \u00e7evir
+menu.edit.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftir
+menu.edit.rearrange=Yol noktalar\u0131 yeniden diz
+menu.edit.rearrange.start=Hepsini dosyan\u0131n ba\u015f\u0131na
+menu.edit.rearrange.end=Hepsini dosyan\u0131n sonuna
+menu.edit.rearrange.nearest=En yak\u0131n iz noktaya
+menu.edit.cutandmove=Se\u00e7me kes ve ta\u015f\u0131
+menu.select=Se\u00e7
+menu.range=S\u0131ra
+menu.point=Nokta
+menu.select.all=Hepsini se\u00e7
+menu.select.none=Hi\u00e7 se\u00e7
+menu.select.start=S\u0131ran\u0131n ba\u015fkang\u0131c\u0131 se\u00e7
+menu.select.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
+menu.view=G\u00f6r\u00fcn\u00fcm
+menu.view.browser=Taray\u0131c\u0131n\u0131n haritas\u0131
+menu.view.browser.google=Google haritalar\u0131
+menu.view.browser.openstreetmap=OpenStreetMap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Yahoo haritalar\u0131
+menu.view.browser.bing=Bing haritalar\u0131
+menu.settings=Ayarlar
+menu.settings.showpace=H\u0131z\u0131 g\u00f6r\u00fcnt\u00fcle
+menu.help=Yard\u0131m
+# Popup menu for map
+menu.map.zoomin=Yak\u0131nla\u015ft\u0131r
+menu.map.zoomout=Uzakla\u015f
+menu.map.newpoint=Yeni nokta olu\u015ftur
+menu.map.connect=\u0130z noktalar\u0131 ba\u011flan
+menu.map.showmap=Harita g\u00f6ster
+menu.map.showscalebar=\u00d6l\u00e7e\u011fi g\u00f6r\u00fcnt\u00fcle
+
+# Alt keys for menus
+altkey.menu.file=D
+altkey.menu.edit=Z
+altkey.menu.select=S
+altkey.menu.track=Z
+altkey.menu.range=S
+altkey.menu.point=N
+altkey.menu.view=G
+altkey.menu.photo=F
+altkey.menu.settings=A
+altkey.menu.help=Y
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=A
+shortcut.menu.file.load=L
+shortcut.menu.file.save=K
+shortcut.menu.edit.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.select.all=A
+shortcut.menu.help.help=Y
+
+# Functions
+function.loadfromgps=GPS'den veri al
+function.sendtogps=GPS'e veri g\u00f6nder
+function.exportkml=Ver KML olarak
+function.exportgpx=Ver GPX olarak
+function.exportpov=Ver POV olarak
+function.editwaypointname=Noktan\u0131n ad\u0131 d\u00fczenle
+function.compress=Yolunu s\u0131k\u0131\u015ft\u0131r
+function.addtimeoffset=Zaman ofseti ekle
+function.addaltitudeoffset=Y\u00fckseklik ofseti ekle
+function.findwaypoint=Noktay\u0131 bul
+function.pastecoordinates=Yeni korrdinatlar gir
+function.charts=Krokiler
+function.show3d=3B g\u00fcr\u00fcnt\u00fcs\u00fc
+function.distances=Uzakl\u0131klar
+function.fullrangedetails=S\u0131ran\u0131n b\u00fct\u00fcn ayr\u0131nt\u0131lar
+function.setmapbg=Arkafonun haritas\u0131 se\u00e7
+function.setkmzimagesize=KMZ resim boyutu ayarla
+function.setpaths=Uygulamalar\u0131n yollar\u0131 ayarla
+function.getgpsies=Gpsies.com'dan yolu al
+function.duplicatepoint=Noktay\u0131 kopyala
+function.setcolours=Renkleri ayarla
+function.setlanguage=Dil se\u00e7
+function.correlatephotos=Fotolar\u0131n ba\u011fl\u0131la\u015ft\u0131r
+function.rearrangephotos=Fotolar\u0131 yeniden diz
+function.rotatephotoleft=Fotoyu sola d\u00f6nd\u00fcr
+function.rotatephotoright=Fotoyu sa\u011fa d\u00f6nd\u00fcr
+function.ignoreexifthumb=Exif t\u0131rnak resmi bo\u015fver
+function.help=Yard\u0131m
+function.showkeys=K\u0131sayol tu\u015flar\u0131 g\u00f6ster
+function.about=Prune hakk\u0131nda
+function.checkversion=Yeni s\u00fcr\u00fcm\u00fc i\u00e7in denetle
+function.saveconfig=Ayarlar\u0131 kaydet
+
+# Dialogs
+dialog.exit.confirm.title=Prune'dan \u00e7\u0131k
+dialog.exit.confirm.text=Ver\u0131 kaydetmedin -Ger\u00e7ekten \u00e7\u0131kmak istiyor musun?
+dialog.openappend.title=Varolan verisine ekle
+dialog.openappend.text=Append this data to the data already loaded?
+dialog.deletepoint.title=Noktay\u0131 sil
+dialog.deletepoint.deletephoto=Noktaya ba\u011fl\u0131 olan foto silmek ister misin?
+dialog.deletephoto.title=Fotoyu sil
+dialog.deletephoto.deletepoint=Fotoya ba\u011fl\u0131 olan nokta silmek ister misin?
+dialog.openoptions.title=Se\u00e7enekleri a\u00e7
+dialog.openoptions.filesnippet=Dosyan\u0131n par\u00e7as\u0131
+dialog.load.table.field=Alan
+dialog.load.table.datatype=Veri t\u00fcr\u00fc
+dialog.load.table.description=A\u00e7\u0131klama
+dialog.delimiter.label=Alan s\u0131n\u0131rlay\u0131c\u0131
+dialog.delimiter.comma=Virg\u00fcl ,
+dialog.delimiter.tab=Sekme
+dialog.delimiter.space=Bo\u015fluk
+dialog.delimiter.semicolon=Noktal\u0131 virg\u00fcl ;
+dialog.delimiter.other=Di\u011fer
+dialog.openoptions.deliminfo.records=sat\u0131rlar, ve
+dialog.openoptions.deliminfo.fields=alanlar
+dialog.openoptions.deliminfo.norecords=Hi\u00e7 bir sat\u0131r bulunmad\u0131
+dialog.openoptions.altitudeunits=Y\u00fckseklik birimi
+dialog.jpegload.subdirectories=Alt dizileri dahil et
+dialog.jpegload.loadjpegswithoutcoords=Koordinats\u0131z fotolar\u0131 dahil et
+dialog.jpegload.loadjpegsoutsidearea=Se\u00e7ili alan\u0131n d\u0131\u015f\u0131ndaki fotolar\u0131 dahil et
+dialog.jpegload.progress.title=Fotolar y\u00fckleniyor
+dialog.jpegload.progress=Fotolar\u0131 taran\u0131yor - l\u00fctfen bekle
+dialog.gpsload.nogpsbabel=gpsbabel uygulamas\u0131 bulunmad\u0131. Devam?
+dialog.gpsload.device=Ayg\u0131t ad\u0131
+dialog.gpsload.format=Bi\u00e7im
+dialog.gpsload.getwaypoints=Noktalar y\u00fckle
+dialog.gpsload.gettracks=Yollar y\u00fckle
+dialog.gpsload.save=Dosyaya kaydet
+dialog.gpssend.sendwaypoints=Noktalar\u0131 g\u00f6nder
+dialog.gpssend.sendtracks=Yollar g\u00f6nder
+dialog.gpssend.trackname=\u0130z ad\u0131
+dialog.saveoptions.title=Dosya kaydet
+dialog.save.fieldstosave=Kaydedecek alanlar
+dialog.save.table.field=Alan
+dialog.save.table.hasdata=Veri var
+dialog.save.table.save=Kaydet
+dialog.save.headerrow=Ba\u015fl\u0131k sat\u0131r\u0131 dahil et
+dialog.save.coordinateunits=Koordinat birimleri
+dialog.save.altitudeunits=Y\u00fckseklik birimleri
+dialog.save.timestampformat=Tarih ve saat bi\u00e7imi
+dialog.save.overwrite.title=Dosya zaten var
+dialog.save.overwrite.text=Bu dosya zaten var. Ger\u00e7ekten \u00fczerinde yazmak ister misin?
+dialog.exportkml.text=Verinin ba\u015fl\u0131\u011f\u0131
+dialog.exportkml.altitude=Absolut y\u00fckseklikleri (u\u00e7u\u015f i\u00e7in)
+dialog.exportkml.kmz=kmz dosyas\u0131 olu\u015fturmak i\u00e7in s\u0131k\u0131\u015ft\u0131r
+dialog.exportkml.exportimages=Fotolar\u0131n t\u0131rnak resimleri kmz dosyada dahil et
+dialog.exportkml.trackcolour=\u0130z rengi
+dialog.exportgpx.name=Ad\u0131
+dialog.exportgpx.desc=A\u00e7\u0131klama
+dialog.exportgpx.includetimestamps=Tarih ve saat dahil et
+dialog.exportgpx.copysource=Kaynak xml'i kopyala
+dialog.exportpov.font=Yaz\u0131tipi
+dialog.exportpov.camerax=Kamera X
+dialog.exportpov.cameray=Kamera Y
+dialog.exportpov.cameraz=Kamera Z
+dialog.pointtype.track=\u0130z noktalar
+dialog.pointtype.waypoint=Noktalar (POI)
+dialog.undo.title=Geri al
+dialog.undo.pretext=Geri alacaklar\u0131 se\u00e7
+dialog.undo.none.title=Geri alacak bir \u015fey yok
+dialog.undo.none.text=Geri alacak bir \u015fey yok!
+dialog.clearundo.title=Geri alma listesi s\u0131f\u0131rla
+dialog.clearundo.text=Ger\u00e7ekten geri alma listesi s\u0131f\u0131rlamak ister misin?\nGeri alma bilgileri tamamen silinecektir!
+dialog.pointedit.title=Nokta d\u00fczenle
+dialog.pointedit.table.field=Alan
+dialog.pointedit.table.value=De\u011fer
+dialog.pointedit.table.changed=De\u011fi\u015fmi\u015f
+dialog.pointedit.changevalue.text=Yeni de\u011feri gir
+dialog.pointedit.changevalue.title=Alan d\u00fczenle
+dialog.pointnameedit.name=Nokta ad\u0131
+dialog.pointnameedit.uppercase=B\u00dcY\u00dcK HARFLER
+dialog.pointnameedit.lowercase=k\u00fc\u00e7\u00fck harfler
+dialog.pointnameedit.sentencecase=\u0130lk Harfi B\u00fcy\u00fck
+dialog.addtimeoffset.add=Zaman ekle
+dialog.addtimeoffset.subtract=Zaman \u00e7\u0131kart
+dialog.addtimeoffset.days=G\u00fcn
+dialog.addtimeoffset.hours=Saat
+dialog.addtimeoffset.minutes=Dakika
+dialog.findwaypoint.search=Ara
+dialog.saveexif.title=Exif kaydet
+dialog.saveexif.table.photoname=Foto ad\u0131
+dialog.saveexif.table.status=Durum
+dialog.saveexif.table.save=Kaydet
+dialog.saveexif.photostatus.connected=Ba\u011fland\u0131
+dialog.saveexif.photostatus.disconnected=Ba\u011flant\u0131 kesildi
+dialog.saveexif.photostatus.modified=De\u011fi\u015ftirildi
+dialog.saveexif.overwrite=Dosyalar\u0131n \u00fczerinde yaz
+dialog.saveexif.force=Ufak hatalar\u0131 bo\u015fver
+dialog.charts.xaxis=X axis
+dialog.charts.yaxis=Y axis
+dialog.charts.output=Ç\u0131kt\u0131
+dialog.charts.screen=Ekranda g\u00f6ster
+dialog.charts.svg=SVG dosya olarak g\u00f6ster
+dialog.charts.svgwidth=SVG geni\u015fli\u011fi
+dialog.charts.svgheight=SVG y\u00fcksekli\u011fi
+dialog.setmapbg.mapnik=Mapnik (default)
+dialog.setmapbg.osma=Osma
+dialog.setmapbg.cyclemap=Cyclemap
+dialog.setmapbg.other=Di\u011fer
+dialog.setmapbg.server=Sunucu URL
+dialog.gpsies.column.name=Yol ad\u0131
+dialog.gpsies.column.length=Uzunlu\u011fu
+dialog.gpsies.description=A\u00e7\u0131klama
+dialog.gpsies.nodescription=A\u00e7\u0131klama yok
+dialog.gpsies.nonefound=Herhangi bir yol bulunmad\u0131
+dialog.correlate.photoselect.photoname=Foto ad\u0131
+dialog.correlate.photoselect.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.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
+dialog.about.build=Build
+dialog.about.summarytext1=Prune GPS ayg\u0131tlardan veri y\u00fckler, g\u00f6r\u00fcnt\u00fcler ver d\u00fczenler bir uygulamad\u0131r.
+dialog.about.summarytext3=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n <code style="font-weight:bold">http://activityworkshop.net/</code> sitesinde bak.
+dialog.about.languages=Prune ile kullanabilir diller
+dialog.about.translatedby=Turkish text by katpatuka
+dialog.about.systeminfo=Sistem bilgisi
+dialog.about.systeminfo.os=\u0130\u015fletim Sistemi
+dialog.about.systeminfo.java=Java Runtime
+dialog.about.systeminfo.java3d=Java3d kuruldu
+dialog.about.systeminfo.povray=Povray kuruldu
+dialog.about.systeminfo.exiftool=Exiftool kuruldu
+dialog.about.systeminfo.gpsbabel=Gpsbabel kuruldu
+dialog.about.systeminfo.gnuplot=Gnuplot kuruldu
+dialog.about.yes=Evet
+dialog.about.no=Hay\u0131r
+dialog.about.credits.translators=Çevirmen
+dialog.about.credits.thanks=Te\u015fekk\u00fcrler
+dialog.about.readme=Beni oku
+dialog.checkversion.uptodate=Prune'nin so s\u00fcr\u00fcm\u00fc kullan\u0131yorsun.
+dialog.checkversion.newversion1=Prune'nin yeni bir s\u00fcr\u00fcm\u00fc \u00e7\u0131kt\u0131! Son s\u00fcr\u00fcm \u015fimdi
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Yeni s\u00fcr\u00fcm\u00fcn\u00fcn devir tarihi
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://activityworkshop.net/software/prune/download.html adresine git.
+dialog.keys.intro=Fare yerinde a\u015fa\u011f\u0131daki k\u0131sayol tu\u015flar\u0131 kullanabilirsin:
+dialog.keys.keylist=<table><tr><td>Ok tu\u015flar\u0131</td><td>Haritay\u0131 sola/sa\u011fa/a\u015fa\u011f\u0131/yukar\u0131 kayd\u0131r</td></tr><tr><td>Ctrl + sol, sa\u011f</td><td>\u00d6nceki/sonraki noktay\u0131 se\u00e7</td></tr><tr><td>Ctrl + yukar/a\u015fa\u011f\u0131</td><td>Yak\u0131nla\u015ft\u0131r/Uzakla\u015ft\u0131r</td></tr><tr><td>Del</td><td>Se\u00e7ili noltay\u0131 sil</td></tr></table>
+dialog.saveconfig.desc=A\u011fa\u015f\u0131daki ayarlar\u0131 bir dasyada kaydedilir:
+dialog.saveconfig.prune.trackdirectory=\u0130z klas\u00f6r\u00fc
+dialog.saveconfig.prune.photodirectory=Foto klas\u00f6r\u00fc
+dialog.saveconfig.prune.languagecode=Dil kodu (TR)
+dialog.saveconfig.prune.gpsdevice=GPS ayg\u0131t
+dialog.saveconfig.prune.gpsformat=GPS bi\u00e7imi
+dialog.saveconfig.prune.povrayfont=Povray yaz\u0131tipi
+dialog.saveconfig.prune.metricunits=Metrik sistemi
+dialog.saveconfig.prune.gnuplotpath=gnuplot'un yeriyolu
+dialog.saveconfig.prune.gpsbabelpath=gpsbabel'in yeriyolu
+dialog.saveconfig.prune.exiftoolpath=exiftool'un yeriyolu
+dialog.saveconfig.prune.mapserverindex=Harita sunucunun index
+dialog.saveconfig.prune.mapserverurl=Harita sunucunun adresi
+dialog.saveconfig.prune.showpace=H\u0131z\u0131 g\u00f6r\u00fcnt\u00fcle
+dialog.saveconfig.prune.kmzimagewidth=KMZ resim geni\u015fli\u011fi
+dialog.saveconfig.prune.kmzimageheight=KMZ resim y\u00fcksekli\u011fi
+dialog.setpaths.intro=\u0130ste\u011fe ba\u011fl\u0131 a\u015fa\u011f\u0131daki uygulamalar\u0131n veriyolu kaydedebilirsin:
+dialog.addaltitude.noaltitudes=Se\u00e7ili s\u0131rada y\u00fckseklik bilgisi bulunmad\u0131
+dialog.addaltitude.desc=Eklenecek y\u00fckseklik ofseti
+dialog.setcolours.background=Arkafonu
+dialog.setcolours.borders=Kenarlar
+dialog.setcolours.lines=Çizgiler
+dialog.setcolours.primary=Birincil
+dialog.setcolours.secondary=\u0130kincil
+dialog.setcolours.point=Noktalar
+dialog.setcolours.selection=Se\u00e7im
+dialog.setcolours.text=Metin
+dialog.colourchooser.title=Rengi se\u00e7
+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.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
+button.ok=Tamam
+button.back=Geri
+button.next=\u0130leri
+button.finish=Son
+button.cancel=\u0130ptal
+button.overwrite=\u00dczerinde yaz
+button.moveup=Yukar\u0131
+button.movedown=A\u015fa\u011f\u0131
+button.showlines=Çizgiler g\u00f6r\u00fcnt\u00fcle
+button.edit=D\u00fczenle
+button.exit=Ç\u0131k\u0131\u015f
+button.close=Kapat
+button.continue=Devam
+button.yes=Evet
+button.no=Hay\u0131r
+button.yestoall=Hepsini evet
+button.notoall=Hepsini hay\u0131r
+button.select=Se\u00e7
+button.selectall=Hepsini se\u00e7
+button.selectnone=Hi\u00e7 se\u00e7me
+button.preview=\u00d6ng\u00f6r\u00fcn\u00fcm
+button.load=Y\u00fckle
+button.guessfields=Alanlar\u0131 tahmin et
+button.showwebpage=Websayfas\u0131 g\u00f6r\u00fcnt\u00fcle
+button.check=Denetle
+
+# File types
+filetype.txt=TXT dosyalar\u0131
+filetype.jpeg=JPG dosyalar\u0131
+filetype.kmlkmz=KML, KMZ dosyalar\u0131
+filetype.kml=KML dosyalar\u0131
+filetype.kmz=KMZ dosyalar\u0131
+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.nodata=Hergangi veri y\u00fcklenmedi
+display.noaltitudes=Track verisinde y\u00fckseklik bilgisi yok
+details.trackdetails=\u0130z ayr\u0131nt\u0131lar\u0131
+details.notrack=Herhangi track y\u00fcklenmedi
+details.track.points=Noktalar
+details.track.file=Dosya
+details.track.numfiles=Dosya say\u0131s\u0131
+details.pointdetails=Nokta ayr\u0131nt\u0131lar\u0131
+details.index.selected=Endeks
+details.index.of=toplam:
+details.nopointselection=Herhangi nokta se\u00e7ili de\u011fil
+details.photofile=Foto dosyas\u0131
+details.norangeselection=Herhangi s\u0131ra se\u00e7ili de\u011fil
+details.rangedetails=S\u0131ra ayr\u0131nt\u0131lar\u0131
+details.range.selected=Se\u00e7ili
+details.range.to=-
+details.altitude.to=-
+details.range.climb=T\u0131rman\u0131\u015f
+details.range.descent=\u0130ni\u015f
+details.coordformat=Koordinat bi\u00e7imi
+details.distanceunits=Uzakl\u0131k birimi
+display.range.time.secs=san
+display.range.time.mins=dak
+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.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
+map.overzoom=Fazla yak\u0131nla\u015ft\u0131n salak - harita g\u00f6r\u00fcnt\u00fclenmiyor
+
+# Field names
+fieldname.latitude=Enlem (lat)
+fieldname.longitude=Boylam (lon)
+fieldname.altitude=Y\u00fckseklik
+fieldname.timestamp=Zaman
+fieldname.time=Zaman
+fieldname.waypointname=Ad\u0131
+fieldname.waypointtype=T\u00fcr
+fieldname.newsegment=Par\u00e7a
+fieldname.custom=Di\u011fer
+fieldname.prefix=Alan
+fieldname.distance=Uzakl\u0131k
+fieldname.duration=S\u00fcre
+fieldname.speed=H\u0131z
+fieldname.verticalspeed=Dikey h\u0131z\u0131
+
+# Measurement units
+units.original=Orijinal
+units.default=Varsay\u0131lan
+units.metres=Metre
+units.metres.short=m
+units.feet=Ayak
+units.feet.short=ft
+units.kilometres=Kilometre
+units.kilometres.short=km
+units.kmh=km/h
+units.miles=Mil
+units.miles.short=mi
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=saat
+units.degminsec=Derece/Dakika/Saniye
+units.degmin=Derece/Dakika
+units.deg=Derece
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.com
+
+# Cardinals for 3d plots
+cardinal.n=K
+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.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.deleterange=s\u0131ra sil
+undo.compress=izi s\u0131k\u0131\u015ft\u0131r
+undo.insert=noktalar\u0131 ekle
+undo.reverse=s\u0131ra tersine d\u00f6nd\u00fcr
+undo.mergetracksegments=iz par\u00e7alar\u0131 birle\u015ftir
+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.rearrangephotos=fotolar\u0131 yeniden diz
+undo.createpoint=noktay\u0131 olu\u015ftur
+undo.rotatephoto=fotoyu d\u00f6nder
index d91fea75de07524ab5c9c03c6cc638885096d0ae..ba1c391460f3b6b22c62d7e75f890d387ae3b112 100644 (file)
@@ -52,24 +52,6 @@ menu.map.autopan=\u81ea\u52a8\u7f29\u653e
 menu.map.showmap=\u663e\u793a\u5730\u56fe
 menu.map.showscalebar=\u663e\u793a\u6bd4\u4f8b\u5c3a
 
-# Alt keys for menus
-altkey.menu.file=
-altkey.menu.edit=
-altkey.menu.select=
-altkey.menu.view=
-altkey.menu.photo=
-altkey.menu.settings=
-altkey.menu.help=
-
-# Ctrl shortcuts for menu items
-shortcut.menu.file.open=
-shortcut.menu.file.load=
-shortcut.menu.file.save=
-shortcut.menu.edit.undo=
-shortcut.menu.edit.compress=
-shortcut.menu.select.all=
-shortcut.menu.help.help=
-
 # Functions
 function.loadfromgps=\u4eceGPS\u5bfc\u5165
 function.sendtogps=\u53d1\u9001\u81f3GPS
@@ -84,11 +66,11 @@ function.findwaypoint=\u67e5\u627e\u822a\u70b9
 function.charts=\u9ad8\u5ea6\u901f\u5ea6\u56fe\u8868
 function.show3d=3-D\u89c6\u56fe
 function.distances=\u8ddd\u79bb
-function.getgpsies=Gpsies\u8f68\u8ff9
-function.correlatephotos=\u94fe\u63a5\u76f8\u7247
 function.setmapbg=\u80cc\u666f\u5730\u56fe
-function.setkmzimagesize=
+function.setkmzimagesize=\u8bbe\u7f6eKMZ\u56fe\u50cf\u5c3a\u5bf8
 function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
+function.getgpsies=Gpsies\u8f68\u8ff9
+function.correlatephotos=\u94fe\u63a5\u76f8\u7247
 function.help=\u5e2e\u52a9
 function.showkeys=\u663e\u793a\u5feb\u6377\u952e
 function.about=\u5173\u4e8ePrune
@@ -215,7 +197,6 @@ dialog.charts.svg=\u8f93\u51fa\u81f3SVG\u6587\u4ef6
 dialog.charts.svgwidth=SVG\u5bbd\u5ea6
 dialog.charts.svgheight=SVG\u9ad8\u5ea6
 dialog.charts.needaltitudeortimes=\u8f68\u8ff9\u5fc5\u987b\u542b\u6709\u9ad8\u5ea6\u6216\u65f6\u95f4\u4fe1\u606f
-dialog.charts.gnuplotpath=gnuplot\u8def\u5f84
 dialog.charts.gnuplotnotfound=\u8def\u5f84\u9519\u8bef\uff0c\u65e0\u6cd5\u627e\u5230gnuplot
 dialog.distances.intro=\u822a\u70b9\u4e4b\u95f4\u76f4\u7ebf\u8ddd\u79bb
 dialog.distances.column.from=\u4ece\u6b64\u70b9
@@ -231,7 +212,6 @@ dialog.gpsies.column.name=\u8f68\u8ff9\u540d\u79f0
 dialog.gpsies.column.length=\u957f\u5ea6
 dialog.gpsies.description=\u63cf\u8ff0
 dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0
-dialog.gpsies.nonefound=
 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
@@ -255,13 +235,13 @@ dialog.correlate.options.distancelimit=\u8ddd\u79bb\u9650\u5236
 dialog.correlate.options.correlate=\u94fe\u63a5
 dialog.correlate.alloutsiderange=\u65e0\u6cd5\u94fe\u63a5\uff0c\u6240\u6709\u76f8\u7247\u8d85\u51fa\u8f68\u8ff9\u65f6\u95f4\u8303\u56f4\n\u8bf7\u6539\u53d8\u65f6\u95f4\u504f\u79fb\u6216\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247
 dialog.compress.nonefound=\u65e0\u6cd5\u5220\u9664\u6570\u636e\u70b9
-dialog.compress.duplicates.title=\u91cd\u590d\u70b9\u5220\u9664
 dialog.compress.closepoints.title=\u90bb\u8fd1\u70b9\u5220\u9664
 dialog.compress.closepoints.paramdesc=\u8303\u56f4\u7cfb\u6570
 dialog.compress.wackypoints.title=\u5f02\u5e38\u70b9\u5220\u9664
 dialog.compress.wackypoints.paramdesc=\u8ddd\u79bb\u7cfb\u6570
 dialog.compress.singletons.title=\u79bb\u6563\u70b9\u5220\u9664
 dialog.compress.singletons.paramdesc=\u8ddd\u79bb\u7cfb\u6570
+dialog.compress.duplicates.title=\u91cd\u590d\u70b9\u5220\u9664
 dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\u70b9
 dialog.help.help=\u66f4\u591a\u4fe1\u606f\u548c\u7528\u6cd5\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\nhttp://activityworkshop.net/software/prune///
 dialog.about.version=\u7248\u672c
@@ -299,7 +279,6 @@ dialog.checkversion.releasedate1=\u65b0\u7248\u672c\u53d1\u884c\u4e8e
 dialog.checkversion.releasedate2=
 dialog.checkversion.download=\u4e0b\u8f7d\u6700\u65b0\u7248\u672c\uff0c\u8bf7\u767b\u9646\u7f51\u7ad9\uff1a\nhttp://activityworkshop.net/software/prune/download.html
 dialog.keys.intro=\u53ef\u7528\u4e0b\u5217\u5feb\u6377\u952e\u66ff\u4ee3\u9f20\u6807
-dialog.keys.keylist=
 dialog.saveconfig.desc=\u4e0b\u5217\u8bbe\u7f6e\u5c06\u4fdd\u5b58\u5230\u8bbe\u7f6e\u6587\u4ef6
 dialog.saveconfig.prune.trackdirectory=\u8f68\u8ff9\u6587\u4ef6\u5939
 dialog.saveconfig.prune.photodirectory=\u76f8\u7247\u6587\u4ef6\u5939
@@ -314,8 +293,8 @@ dialog.saveconfig.prune.exiftoolpath=exiftool\u8def\u5f84
 dialog.saveconfig.prune.mapserverindex=\u80cc\u666f\u5730\u56fe\u7801(1-4)
 dialog.saveconfig.prune.mapserverurl=\u90094\u65f6\u5730\u56fe\u670d\u52a1\u5668URL
 dialog.saveconfig.prune.showpace=\u663e\u793a\u6b65\u901f
-dialog.saveconfig.prune.kmzimagewidth=
-dialog.saveconfig.prune.kmzimageheight=
+dialog.saveconfig.prune.kmzimagewidth=KMZ\u56fe\u50cf\u5bbd\u5ea6
+dialog.saveconfig.prune.kmzimageheight=KMZ\u56fe\u50cf\u9ad8\u5ea6
 dialog.setpaths.intro=\u82e5\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a0b\u5e8f\u8def\u5f84
 dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
 dialog.addaltitude.desc=\u9ad8\u5ea6\u504f\u79fb
@@ -337,7 +316,6 @@ confirm.point.edit=\u5df2\u7f16\u8f91\u7684\u8f68\u8ff9\u70b9
 confirm.mergetracksegments=\u5df2\u5408\u5e76\u7684\u8f68\u8ff9\u6bb5
 confirm.reverserange=\u53cd\u5411\u7684\u8303\u56f4
 confirm.addtimeoffset=\u5df2\u52a0\u4e0a\u65f6\u95f4\u504f\u5dee
-confirm.addaltitudeoffset=
 confirm.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u7684\u822a\u70b9
 confirm.cutandmove=\u5df2\u79fb\u52a8\u7684\u8f68\u8ff9\u6bb5
 confirm.saveexif.ok1=\u5df2\u4fdd\u5b58
@@ -353,7 +331,7 @@ confirm.correlate.multi=\u76f8\u7247\u5df2\u94fe\u63a5
 confirm.createpoint=\u5df2\u521b\u5efa\u70b9
 confirm.running=\u8bf7\u7a0d\u7b49...
 
-# Buttons
+# Buttons || These are all the texts for buttons
 button.ok=\u786e\u5b9a
 button.back=\u8fd4\u56de
 button.next=\u4e0b\u4e00\u6b65
@@ -371,6 +349,7 @@ button.yes=\u662f
 button.no=\u5426
 button.yestoall=\u5168\u90e8\u662f
 button.notoall=\u5168\u90e8\u5426
+button.select=\u9009\u62e9
 button.selectall=\u5168\u9009
 button.selectnone=\u5168\u4e0d\u9009
 button.preview=\u9884\u89c8
@@ -389,7 +368,7 @@ filetype.gpx=GPX\u6587\u4ef6
 filetype.pov=POV\u6587\u4ef6
 filetype.svg=SVG\u6587\u4ef6
 
-# Display components
+# Display components || These are all for the side panels showing point/range details
 display.nodata=\u65e0\u6570\u636e
 display.noaltitudes=\u8f68\u8ff9\u6570\u636e\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
 details.trackdetails=\u8f68\u8ff9\u4fe1\u606f
@@ -473,7 +452,7 @@ cardinal.s=S
 cardinal.e=E
 cardinal.w=W
 
-# Undo operations
+# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
 undo.load=\u5bfc\u5165\u6570\u636e
 undo.loadphotos=\u5bfc\u5165\u76f8\u7247
 undo.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9
index 7a670968e0e460247ee002c03ce6f48a0be53b4e..6b29379eee308fb0b715a7a682fcab4b8477dcaf 100644 (file)
@@ -86,6 +86,12 @@ public abstract class FieldGuesser
                                        fields[f] = Field.NEW_SEGMENT;
                                        continue;
                                }
+                               // check for waypoint type
+                               if (!checkArrayHasField(fields, Field.WAYPT_TYPE) && fieldLooksLikeWaypointType(value, isHeader))
+                               {
+                                       fields[f] = Field.WAYPT_TYPE;
+                                       continue;
+                               }
                        }
                }
                // Fill in the rest of the fields using just custom fields
@@ -115,6 +121,10 @@ public abstract class FieldGuesser
                else if (!checkArrayHasField(fields, Field.LONGITUDE)) {
                        fields[1] = Field.LONGITUDE;
                }
+               // Longitude _could_ have overwritten latitude in position 1
+               if (!checkArrayHasField(fields, Field.LATITUDE)) {
+                       fields[0] = Field.LATITUDE;
+               }
                return fields;
        }
 
@@ -298,4 +308,27 @@ public abstract class FieldGuesser
                        return false;
                }
        }
+
+       /**
+        * Check whether the given String looks like a waypoint type
+        * @param inValue value from file
+        * @param inIsHeader true if this is a header line, false for data
+        * @return true if it could be a waypoint type
+        */
+       private static boolean fieldLooksLikeWaypointType(String inValue, boolean inIsHeader)
+       {
+               if (inValue == null || inValue.equals("")) {return false;}
+               if (inIsHeader)
+               {
+                       String upperValue = inValue.toUpperCase();
+                       // This is a header line so look for english or local text
+                       return (upperValue.equals("TYPE")
+                               || upperValue.equals(I18nManager.getText("fieldname.waypointtype").toUpperCase()));
+               }
+               else
+               {
+                       // can't reliably identify it just using the value
+                       return false;
+               }
+       }
 }
index 889f1d7933a6ec2357b07fd6c3b9b3ac3affebb9..c6d9827921ffa9a4d64f401411fa670b8134a2e6 100644 (file)
@@ -6,7 +6,8 @@ import javax.swing.JFileChooser;
 import javax.swing.JFrame;
 
 import tim.prune.App;
-import tim.prune.Config;
+import tim.prune.config.Config;
+import tim.prune.load.xml.GzipFileLoader;
 import tim.prune.load.xml.XmlFileLoader;
 import tim.prune.load.xml.ZipFileLoader;
 
@@ -24,6 +25,7 @@ public class FileLoader
        private NmeaFileLoader _nmeaFileLoader = null;
        private XmlFileLoader _xmlFileLoader = null;
        private ZipFileLoader _zipFileLoader = null;
+       private GzipFileLoader _gzipFileLoader = null;
 
 
        /**
@@ -39,6 +41,7 @@ public class FileLoader
                _nmeaFileLoader = new NmeaFileLoader(inApp);
                _xmlFileLoader = new XmlFileLoader(inApp);
                _zipFileLoader = new ZipFileLoader(inApp, _xmlFileLoader);
+               _gzipFileLoader = new GzipFileLoader(inApp, _xmlFileLoader);
        }
 
 
@@ -117,6 +120,11 @@ public class FileLoader
                        // Use zip loader for zipped kml (or zipped gpx)
                        _zipFileLoader.openFile(inFile);
                }
+               else if (fileExtension.endsWith(".gz") || fileExtension.equals("gzip"))
+               {
+                       // Use gzip loader for gzipped xml
+                       _gzipFileLoader.openFile(inFile);
+               }
                else if (fileExtension.equals("nmea"))
                {
                        _nmeaFileLoader.openFile(inFile);
index 3ec3c6bcfddc217d5c77684727bebb6f971eaa09..c0ca7549807b9f354176d9bb5793610a5275753c 100644 (file)
@@ -9,7 +9,9 @@ import java.awt.event.ActionListener;
 import java.awt.event.KeyAdapter;
 import java.awt.event.KeyEvent;
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.InputStreamReader;
+import java.util.ArrayList;
 
 import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
@@ -22,19 +24,22 @@ import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 import javax.swing.JTextField;
 import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.ExternalTools;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
+import tim.prune.data.SourceInfo;
 import tim.prune.load.xml.XmlFileLoader;
 import tim.prune.load.xml.XmlHandler;
+import tim.prune.save.GpxExporter;
 
 /**
  * Class to manage the loading of GPS data using GpsBabel
@@ -45,8 +50,10 @@ public class GpsLoader extends GenericFunction implements Runnable
        private JDialog _dialog = null;
        private JTextField _deviceField = null, _formatField = null;
        private JCheckBox _waypointCheckbox = null, _trackCheckbox = null;
+       private JCheckBox _saveCheckbox = null;
        private JButton _okButton = null;
        private JProgressBar _progressBar = null;
+       private File _saveFile = null;
        private boolean _cancelled = false;
 
 
@@ -146,6 +153,11 @@ public class GpsLoader extends GenericFunction implements Runnable
                _trackCheckbox.addChangeListener(checkboxListener);
                _trackCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
                mainPanel.add(_trackCheckbox);
+               // Checkbox for immediately saving to file
+               _saveCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.save"));
+               _saveCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
+               mainPanel.add(_saveCheckbox);
+
                // progress bar (initially invisible)
                _progressBar = new JProgressBar(0, 10);
                mainPanel.add(_progressBar);
@@ -210,14 +222,13 @@ public class GpsLoader extends GenericFunction implements Runnable
                if (_waypointCheckbox.isSelected() || _trackCheckbox.isSelected())
                {
                        _progressBar.setIndeterminate(true);
+                       _saveFile = null;
                        try
                        {
-                               callGpsBabel(_waypointCheckbox.isSelected(), _trackCheckbox.isSelected());
+                               callGpsBabel();
                        }
                        catch (Exception e)
                        {
-                               // System.err.println("Error: " + e.getClass().getName());
-                               // System.err.println("Error: " + e.getMessage());
                                _app.showErrorMessageNoLookup(getNameKey(), e.getMessage());
                                _cancelled = true;
                        }
@@ -234,70 +245,126 @@ public class GpsLoader extends GenericFunction implements Runnable
 
        /**
         * Execute the call to gpsbabel and pass the results back to the app
-        * @param inWaypoints true to load waypoints
-        * @param inTracks true to load track points
         */
-       private void callGpsBabel(boolean inWaypoints, boolean inTracks) throws Exception
+       private void callGpsBabel() throws Exception
        {
                // Set up command to call gpsbabel
-               String[] commands = null;
                final String device = _deviceField.getText().trim();
                final String format = _formatField.getText().trim();
-               final String command = Config.getConfigString(Config.KEY_GPSBABEL_PATH);
-               if (inWaypoints && inTracks) {
-                       // Both waypoints and track points selected
-                       commands = new String[] {command, "-w", "-t", "-i", format,
-                               "-f", device, "-o", "gpx", "-F", "-"};
-               }
-               else
-               {
-                       // Only waypoints OR track points selected
-                       commands = new String[] {command, "-w", "-i", format,
-                               "-f", device, "-o", "gpx", "-F", "-"};
-                       if (inTracks) {
-                               commands[1] = "-t";
-                       }
-               }
+               String[] commands = getCommandArray(device, format);
                // Save GPS settings in config
                Config.setConfigString(Config.KEY_GPS_DEVICE, device);
                Config.setConfigString(Config.KEY_GPS_FORMAT, format);
 
-               String errorMessage = "";
+               String errorMessage = "", errorMessage2 = "";
                XmlHandler handler = null;
                Process process = Runtime.getRuntime().exec(commands);
+               String line = null;
 
-               // Pass input stream to try to parse the xml
-               try
+               if (_saveFile != null)
                {
-                       XmlFileLoader xmlLoader = new XmlFileLoader(_app);
-                       SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
-                       saxParser.parse(process.getInputStream(), xmlLoader);
-                       handler = xmlLoader.getHandler();
-                       if (handler == null) {
-                               errorMessage = "Null handler";
+                       // data is being saved to file, so need to wait for it to finish
+                       process.waitFor();
+                       // try to read error message, if any
+                       try {
+                               BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+                               while ((line = r.readLine()) != null) {
+                                       errorMessage += line + "\n";
+                               }
+                               // Close error stream
+                               try {
+                                       r.close();
+                               } catch (Exception e) {}
                        }
+                       catch (Exception e) {} // couldn't get error message
+
+                       // Trigger it to be loaded by app
+                       if (process.exitValue() == 0)
+                       {
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               ArrayList<File> fileList = new ArrayList<File>();
+                                               fileList.add(_saveFile);
+                                               _app.loadDataFiles(fileList);
+                                       }
+                               });
+                       }
+                       else if (errorMessage.length() > 0) {
+                               throw new Exception(errorMessage);
+                       }
+                       else throw new Exception(I18nManager.getText("error.gpsload.unknown"));
                }
-               catch (Exception e) {
-                       errorMessage = e.getMessage();
-               }
+               else
+               {
+                       // Pass input stream to try to parse the xml
+                       try
+                       {
+                               XmlFileLoader xmlLoader = new XmlFileLoader(_app);
+                               SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+                               saxParser.parse(process.getInputStream(), xmlLoader);
+                               handler = xmlLoader.getHandler();
+                               if (handler == null) {
+                                       errorMessage = "Null handler";
+                               }
+                       }
+                       catch (Exception e) {
+                               errorMessage = e.getMessage();
+                       }
 
-               // Read the error stream to see if there's a better error message there
-               BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
-               String line = null;
-               String errorMessage2 = "";
-               while ((line = r.readLine()) != null) {
-                       errorMessage2 += line + "\n";
+                       // Read the error stream to see if there's a better error message there
+                       BufferedReader r = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+                       while ((line = r.readLine()) != null) {
+                               errorMessage2 += line + "\n";
+                       }
+                       // Close error stream
+                       try {
+                               r.close();
+                       } catch (Exception e) {}
+
+                       if (errorMessage2.length() > 0) {errorMessage = errorMessage2;}
+                       if (errorMessage.length() > 0) {throw new Exception(errorMessage);}
+
+                       // Send data back to app
+                       _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(), Altitude.Format.METRES,
+                               new SourceInfo(_deviceField.getText(), SourceInfo.FILE_TYPE.GPSBABEL));
                }
-               // Close error stream
-               try {
-                       r.close();
-               } catch (Exception e) {}
+       }
 
-               if (errorMessage2.length() > 0) {errorMessage = errorMessage2;}
-               if (errorMessage.length() > 0) {throw new Exception(errorMessage);}
 
-               // Send data back to app
-               _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                       Altitude.Format.METRES, _deviceField.getText());
+       /**
+        * Get the commands to call
+        * @param inDevice device name to use
+        * @param inFormat format to use
+        * @return String array containing commands
+        */
+       private String[] getCommandArray(String inDevice, String inFormat)
+       {
+               String[] commands = null;
+               final String command = Config.getConfigString(Config.KEY_GPSBABEL_PATH);
+               final boolean loadWaypoints = _waypointCheckbox.isSelected();
+               final boolean loadTrack = _trackCheckbox.isSelected();
+               if (loadWaypoints && loadTrack) {
+                       // Both waypoints and track points selected
+                       commands = new String[] {command, "-w", "-t", "-i", inFormat,
+                               "-f", inDevice, "-o", "gpx", "-F", "-"};
+               }
+               else
+               {
+                       // Only waypoints OR track points selected
+                       commands = new String[] {command, "-w", "-i", inFormat,
+                               "-f", inDevice, "-o", "gpx", "-F", "-"};
+                       if (loadTrack) {
+                               commands[1] = "-t";
+                       }
+               }
+               // Do we want to save the gpx straight to file?
+               if (_saveCheckbox.isSelected()) {
+                       // Select file to save to
+                       _saveFile = GpxExporter.chooseGpxFile(_parentFrame);
+                       if (_saveFile != null) {
+                               commands[commands.length-1] = _saveFile.getAbsolutePath();
+                       }
+               }
+               return commands;
        }
 }
index 194f2b7f08b83a09d2a11b72fb2ac4e48f77038c..08b29fad02e6b32ec2cfc6bafa663869d76cbefa 100644 (file)
@@ -17,8 +17,8 @@ import javax.swing.JPanel;
 import javax.swing.JProgressBar;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.DataPoint;
 import tim.prune.data.LatLonRectangle;
@@ -103,16 +103,23 @@ public class JpegLoader implements Runnable
                if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
                {
                        // Bring up dialog before starting
-                       showDialog();
+                       if (_progressDialog == null) {
+                               createProgressDialog();
+                       }
+                       // reset dialog and show it
+                       _progressBar.setValue(0);
+                       _progressBar.setString("");
+                       _progressDialog.setVisible(true);
+                       // start thread for processing
                        new Thread(this).start();
                }
        }
 
 
        /**
-        * Show the main dialog
+        * Create the dialog to show the progress
         */
-       private void showDialog()
+       private void createProgressDialog()
        {
                _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
                _progressDialog.setLocationRelativeTo(_parentFrame);
@@ -135,7 +142,6 @@ public class JpegLoader implements Runnable
                panel.add(cancelButton);
                _progressDialog.getContentPane().add(panel);
                _progressDialog.pack();
-               _progressDialog.setVisible(true);
        }
 
 
@@ -158,6 +164,7 @@ public class JpegLoader implements Runnable
                // Process the files recursively and build lists of photos
                processFileList(files, true, _subdirCheckbox.isSelected());
                _progressDialog.setVisible(false);
+               _progressDialog.dispose(); // Sometimes dialog doesn't disappear without this dispose
                if (_cancelled) {return;}
 
                //System.out.println("Finished - counts are: " + _fileCounts[0] + ", " + _fileCounts[1]
@@ -201,7 +208,7 @@ public class JpegLoader implements Runnable
                if (inFiles != null)
                {
                        // Loop over elements in array
-                       for (int i=0; i<inFiles.length; i++)
+                       for (int i=0; i<inFiles.length && !_cancelled; i++)
                        {
                                File file = inFiles[i];
                                if (file.exists() && file.canRead())
@@ -219,12 +226,7 @@ public class JpegLoader implements Runnable
                                                processFileList(files, false, inDescend);
                                        }
                                }
-                               else
-                               {
-                                       // file doesn't exist or isn't readable - ignore error
-                               }
-                               // check for cancel button pressed
-                               if (_cancelled) break;
+                               // if file doesn't exist or isn't readable - ignore
                        }
                }
        }
@@ -275,6 +277,8 @@ public class JpegLoader implements Runnable
                                photo.setTimestamp(createTimestamp(jpegData.getOriginalTimestamp()));
                        }
                        photo.setExifThumbnail(jpegData.getThumbnailImage());
+                       // Also extract orientation tag for setting rotation state of photo
+                       photo.setRotation(jpegData.getRequiredRotation());
                }
                catch (JpegException jpe) { // don't list errors, just count them
                }
@@ -381,8 +385,6 @@ public class JpegLoader implements Runnable
         */
        private static Timestamp createTimestamp(Rational[] inDate, Rational[] inTime)
        {
-               //System.out.println("Making timestamp for date (" + inDate[0].toString() + "," + inDate[1].toString() + "," + inDate[2].toString() + ") and time ("
-               //      + inTime[0].toString() + "," + inTime[1].toString() + "," + inTime[2].toString() + ")");
                return new Timestamp(inDate[0].intValue(), inDate[1].intValue(), inDate[2].intValue(),
                        inTime[0].intValue(), inTime[1].intValue(), inTime[2].intValue());
        }
index ad017898ba2b1b1dbbd7d1695f8952506a2f259f..090bb6d3a9a0c738b3ad1e71c64e512a9e0a4922 100644 (file)
@@ -9,6 +9,7 @@ import java.util.ArrayList;
 import tim.prune.App;
 import tim.prune.data.Altitude;
 import tim.prune.data.Field;
+import tim.prune.data.SourceInfo;
 
 /**
  * Class to handle the loading of Nmea files
@@ -76,7 +77,7 @@ public class NmeaFileLoader
                if (messages.size() > 0)
                {
                        _app.informDataLoaded(getFieldArray(), makeDataArray(messages),
-                               Altitude.Format.METRES, inFile.getName());
+                               Altitude.Format.METRES, new SourceInfo(inFile, SourceInfo.FILE_TYPE.NMEA));
                }
        }
 
index 64c9ebadff0e1955fa372a10ddf1e1868c446c82..f72893dd0316cfa458a4c8db501cbb7584393e03 100644 (file)
@@ -21,6 +21,7 @@ import tim.prune.App;
 import tim.prune.I18nManager;
 import tim.prune.data.Altitude;
 import tim.prune.data.Field;
+import tim.prune.data.SourceInfo;
 
 
 /**
@@ -527,9 +528,9 @@ public class TextFileLoader
                }
                _lastAltitudeFormat = altitudeFormat;
                // give data to App
+               SourceInfo sourceInfo = new SourceInfo(_file, SourceInfo.FILE_TYPE.TEXT);
                _app.informDataLoaded(_fieldTableModel.getFieldArray(),
-                       _fileExtractTableModel.getData(), altitudeFormat,
-                       _file.getName());
+                       _fileExtractTableModel.getData(), altitudeFormat, sourceInfo);
                // clear up file cacher
                _fileCacher.clear();
                // dispose of dialog
index 8ab3e17978b878dca0221d8c80c9156fa9b5d1d4..b4ae07a4e662c719d80b7b242be0519cbc532472 100644 (file)
@@ -17,16 +17,14 @@ public class GpxHandler extends XmlHandler
        private boolean _insideName = false;
        private boolean _insideElevation = false;
        private boolean _insideTime = false;
-//     private boolean _insideType = false;
+       private boolean _insideType = false;
        private boolean _startSegment = true;
        private String _name = null, _latitude = null, _longitude = null;
        private String _elevation = null;
        private String _time = null;
-//     private String _type = null;
+       private String _type = null;
        private ArrayList<String[]> _pointList = new ArrayList<String[]>();
 
-       // FIXME: Read waypoint type too
-
        /**
         * Receive the start of a tag
         * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
@@ -35,7 +33,7 @@ public class GpxHandler extends XmlHandler
                Attributes attributes) throws SAXException
        {
                // Read the parameters for waypoints and track points
-               if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt"))
+               if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
                {
                        _insideWaypoint = qName.equalsIgnoreCase("wpt");
                        int numAttributes = attributes.getLength();
@@ -48,6 +46,7 @@ public class GpxHandler extends XmlHandler
                        _elevation = null;
                        _name = null;
                        _time = null;
+                       _type = null;
                }
                else if (qName.equalsIgnoreCase("ele"))
                {
@@ -61,10 +60,10 @@ public class GpxHandler extends XmlHandler
                {
                        _insideTime = true;
                }
-//             else if (qName.equalsIgnoreCase("type"))
-//             {
-//                     _insideType = true;
-//             }
+               else if (qName.equalsIgnoreCase("type"))
+               {
+                       _insideType = true;
+               }
                else if (qName.equalsIgnoreCase("trkseg"))
                {
                        _startSegment = true;
@@ -80,7 +79,7 @@ public class GpxHandler extends XmlHandler
        public void endElement(String uri, String localName, String qName)
                throws SAXException
        {
-               if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt"))
+               if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
                {
                        processPoint();
                }
@@ -96,6 +95,10 @@ public class GpxHandler extends XmlHandler
                {
                        _insideTime = false;
                }
+               else if (qName.equalsIgnoreCase("type"))
+               {
+                       _insideType = false;
+               }
                super.endElement(uri, localName, qName);
        }
 
@@ -111,6 +114,7 @@ public class GpxHandler extends XmlHandler
                if (_insideName && _insideWaypoint) {_name = checkCharacters(_name, value);}
                else if (_insideElevation) {_elevation = checkCharacters(_elevation, value);}
                else if (_insideTime) {_time = checkCharacters(_time, value);}
+               else if (_insideType) {_type = checkCharacters(_type, value);}
                super.characters(ch, start, length);
        }
 
@@ -134,7 +138,7 @@ public class GpxHandler extends XmlHandler
        private void processPoint()
        {
                // Put the values into a String array matching the order in getFieldArray()
-               String[] values = new String[6];
+               String[] values = new String[7];
                values[0] = _latitude; values[1] = _longitude;
                values[2] = _elevation; values[3] = _name;
                values[4] = _time;
@@ -142,6 +146,7 @@ public class GpxHandler extends XmlHandler
                        values[5] = "1";
                        _startSegment = false;
                }
+               values[6] = _type;
                _pointList.add(values);
        }
 
@@ -152,7 +157,7 @@ public class GpxHandler extends XmlHandler
        public Field[] getFieldArray()
        {
                final Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE,
-                       Field.WAYPT_NAME, Field.TIMESTAMP, Field.NEW_SEGMENT};
+                       Field.WAYPT_NAME, Field.TIMESTAMP, Field.NEW_SEGMENT, Field.WAYPT_TYPE};
                return fields;
        }
 
diff --git a/tim/prune/load/xml/GzipFileLoader.java b/tim/prune/load/xml/GzipFileLoader.java
new file mode 100644 (file)
index 0000000..e21e984
--- /dev/null
@@ -0,0 +1,73 @@
+package tim.prune.load.xml;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.zip.GZIPInputStream;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.data.Altitude;
+import tim.prune.data.SourceInfo;
+
+/**
+ * Class to handle the loading of gzipped xml files
+ */
+public class GzipFileLoader
+{
+       /** App for callback of file loading */
+       private App _app = null;
+       /** Object to do the handling of the xml */
+       XmlFileLoader _xmlLoader = null;
+
+       /**
+        * Constructor
+        * @param inApp App object
+        * @param inXmlLoader object to do the xml handling
+        */
+       public GzipFileLoader(App inApp, XmlFileLoader inXmlLoader)
+       {
+               _app = inApp;
+               _xmlLoader = inXmlLoader;
+       }
+
+       /**
+        * Open the selected file and select appropriate xml loader
+        * @param inFile File to open
+        */
+       public void openFile(File inFile)
+       {
+               GZIPInputStream istream = null;
+               try
+               {
+                       istream = new GZIPInputStream(new FileInputStream(inFile));
+                       _xmlLoader.reset();
+                       SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+                       saxParser.parse(istream, _xmlLoader);
+                       XmlHandler handler = _xmlLoader.getHandler();
+                       if (handler == null) {
+                               _app.showErrorMessage("error.load.dialogtitle", "error.load.noread");
+                       }
+                       else {
+                               // Send back to app
+                               SourceInfo sourceInfo = new SourceInfo(inFile,
+                                       (handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
+                               _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
+                                       Altitude.Format.METRES, sourceInfo);
+                       }
+               }
+               catch (Exception e) {
+                       // Error occurred, could be a non-xml file borking the parser
+                       _app.showErrorMessageNoLookup("error.load.dialogtitle",
+                               I18nManager.getText("error.load.othererror") + " " + e.getClass().getName());
+                       // It would be nice to verify the filename of the file inside the gz,
+                       // but the java classes don't give access to this information
+               }
+               finally {
+                       try {
+                               istream.close();
+                       }
+                       catch (Exception e2) {}
+               }
+       }
+}
index 3f1b2eb6fed19246785cf41a883e75a5c4b7f476..7fa5ec1b80f8b1d693686dacb1cd2483709b1f89 100644 (file)
@@ -10,6 +10,7 @@ import org.xml.sax.helpers.DefaultHandler;
 import tim.prune.App;
 import tim.prune.I18nManager;
 import tim.prune.data.Altitude;
+import tim.prune.data.SourceInfo;
 
 /**
  * Class for handling loading of Xml files, and passing the
@@ -76,8 +77,10 @@ public class XmlFileLoader extends DefaultHandler implements Runnable
                        else
                        {
                                // Pass information back to app
+                               SourceInfo sourceInfo = new SourceInfo(_file,
+                                       (_handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
                                _app.informDataLoaded(_handler.getFieldArray(), _handler.getDataArray(),
-                                       Altitude.Format.METRES, _file.getName());
+                                       Altitude.Format.METRES, sourceInfo);
                        }
                }
                catch (Exception e)
index ba9407c8063c9ea9beb44c213c3da53a099af6dd..dfeec526cb0ddfe190d76621cf7ac1747ea41130 100644 (file)
@@ -12,6 +12,7 @@ import javax.xml.parsers.SAXParserFactory;
 
 import tim.prune.App;
 import tim.prune.data.Altitude;
+import tim.prune.data.SourceInfo;
 
 /**
  * Class to handle the loading of zipped xml files
@@ -63,8 +64,10 @@ public class ZipFileLoader
                                                }
                                                else {
                                                        // Send back to app
+                                                       SourceInfo sourceInfo = new SourceInfo(inFile,
+                                                               (handler instanceof GpxHandler?SourceInfo.FILE_TYPE.GPX:SourceInfo.FILE_TYPE.KML));
                                                        _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                                                               Altitude.Format.METRES, inFile.getName());
+                                                               Altitude.Format.METRES, sourceInfo);
                                                        xmlFound = true;
                                                }
                                        }
@@ -110,7 +113,7 @@ public class ZipFileLoader
                                                else {
                                                        // Send back to app
                                                        _app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
-                                                               Altitude.Format.METRES, "gpsies");
+                                                               Altitude.Format.METRES, new SourceInfo("gpsies", SourceInfo.FILE_TYPE.GPSIES));
                                                        xmlFound = true;
                                                }
                                        }
index 51d9cc777da1fee7b8ee3de257bb9303bb8c0535..8108b006b3fd7c749bb03627e634ddf7df390677 100644 (file)
@@ -1,11 +1,11 @@
-Prune version 8
+Prune version 9
 ===============
 
 Prune is an application for viewing, editing and managing coordinate data from GPS systems,
 including format conversion, charting and photo correlation.
 Full details can be found at http://activityworkshop.net/software/prune/
 
-Prune is copyright activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
+Prune is copyright 2006-2010 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
 You may freely use the software, and may help others to freely use it too.  For further information
 on your rights and how they are protected, see the included license.txt file.
 
@@ -17,7 +17,7 @@ Running
 =======
 
 To run Prune from the jar file, simply call it from a command prompt or shell:
-   java -jar prune_08.jar
+   java -jar prune_09.jar
 
 If the jar file is saved in a different directory, you will need to include the path.
 Depending on your system settings, you may be able to click or double-click on the jar file
@@ -25,7 +25,21 @@ in a file manager window to execute it.  A shortcut, menu item, alias, desktop i
 or other link can of course be made should you wish.
 
 To specify a language other than the default, use an additional parameter, eg:
-   java -jar prune_08.jar --lang=DE
+   java -jar prune_09.jar --lang=DE
+
+New with version 9
+==================
+
+The following features were added since version 8:
+  - Ability to paste coordinates (eg from wikipedia or geocaching sites) to create new points
+  - Configurable colour settings
+  - Function to convert waypoint names into timestamps
+  - Function to reorganise and sort photos either by filename or time
+  - Ability to load gzipped xml files
+  - New "full range details" dialog including pace and average gradient
+  - Preservation of unrecognised gpx tags by copying source xml
+  - Improved photo handling and ability to rotate photos
+  - Japanese, Portuguese and Turkish languages thanks to generous user input
 
 New with version 8
 ==================
@@ -119,6 +133,16 @@ The following features were added since version 1:
   - Waypoint list
   - Spanish language
 
+Features of version 1
+=====================
+
+The following features were included in version 1:
+  - Loading of text files, display in overhead and profile views
+  - Display of track details such as distances, speeds
+  - Deletion of points and ranges, and variable compression
+  - Export to KML format
+  - English, German and Swiss German languages
+
 
 Further information and updates
 ===============================
index f8e04de67a5a1308ca6d5706ed0f3ab505de1fec..986a07ee3052eaa33e3d5ad6e2626f93ca282d34 100644 (file)
@@ -19,10 +19,10 @@ import javax.swing.JProgressBar;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
 
-import tim.prune.Config;
 import tim.prune.ExternalTools;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
@@ -38,6 +38,7 @@ public class ExifSaver implements Runnable
        private JDialog _dialog = null;
        private JButton _okButton = null;
        private JCheckBox _overwriteCheckbox = null;
+       private JCheckBox _forceCheckbox = null;
        private JProgressBar _progressBar = null;
        private PhotoTableModel _photoTableModel = null;
        private boolean _saveCancelled = false;
@@ -139,9 +140,16 @@ public class ExifSaver implements Runnable
                JScrollPane scrollPane = new JScrollPane(photoTable);
                scrollPane.setPreferredSize(new Dimension(300, 160));
                tablePanel.add(scrollPane, BorderLayout.CENTER);
+               // Pair of checkboxes
+               JPanel checkPanel = new JPanel();
+               checkPanel.setLayout(new BoxLayout(checkPanel, BoxLayout.Y_AXIS));
                _overwriteCheckbox = new JCheckBox(I18nManager.getText("dialog.saveexif.overwrite"));
                _overwriteCheckbox.setSelected(false);
-               tablePanel.add(_overwriteCheckbox, BorderLayout.SOUTH);
+               checkPanel.add(_overwriteCheckbox);
+               _forceCheckbox = new JCheckBox(I18nManager.getText("dialog.saveexif.force"));
+               _forceCheckbox.setSelected(false);
+               checkPanel.add(_forceCheckbox);
+               tablePanel.add(checkPanel, BorderLayout.SOUTH);
                centrePanel.add(tablePanel, BorderLayout.CENTER);
                // progress bar below main controls
                _progressBar = new JProgressBar(0, 100);
@@ -223,7 +231,7 @@ public class ExifSaver implements Runnable
                _progressBar.setValue(0);
                _progressBar.setVisible(true);
                boolean overwriteFlag = _overwriteCheckbox.isSelected();
-               int numSaved = 0;
+               int numSaved = 0, numFailed = 0, numForced = 0;
                // Loop over all photos in list
                for (int i=0; i<numPhotos; i++)
                {
@@ -235,10 +243,18 @@ public class ExifSaver implements Runnable
                                if (photo != null && photo.getOriginalStatus() != photo.getCurrentStatus())
                                {
                                        // Increment counter if save successful
-                                       if (savePhoto(photo, overwriteFlag))
-                                       {
+                                       if (savePhoto(photo, overwriteFlag, false)) {
                                                numSaved++;
                                        }
+                                       else {
+                                               if (_forceCheckbox.isSelected() && savePhoto(photo, overwriteFlag, true))
+                                               {
+                                                       numForced++;
+                                               }
+                                               else {
+                                                       numFailed++;
+                                               }
+                                       }
                                }
                        }
                        // update progress bar
@@ -248,6 +264,20 @@ public class ExifSaver implements Runnable
                // Show confirmation
                UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.saveexif.ok1") + " "
                        + numSaved + " " + I18nManager.getText("confirm.saveexif.ok2"));
+               if (numFailed > 0)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("error.saveexif.failed1") + " " + numFailed + " "
+                               + I18nManager.getText("error.saveexif.failed2"),
+                               I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
+               }
+               if (numForced > 0)
+               {
+                       JOptionPane.showMessageDialog(_parentFrame,
+                               I18nManager.getText("error.saveexif.forced1") + " " + numForced + " "
+                               + I18nManager.getText("error.saveexif.forced2"),
+                               I18nManager.getText("dialog.saveexif.title"), JOptionPane.WARNING_MESSAGE);
+               }
                // close dialog, all finished
                _dialog.dispose();
        }
@@ -257,9 +287,10 @@ public class ExifSaver implements Runnable
         * Save the details for the given photo
         * @param inPhoto Photo object
         * @param inOverwriteFlag true to overwrite file, false otherwise
+        * @param inForceFlag true to force write, ignoring minor errors
         * @return true if details saved ok
         */
-       private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag)
+       private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag, boolean inForceFlag)
        {
                // Check whether photo file still exists
                if (!inPhoto.getFile().exists())
@@ -299,9 +330,10 @@ public class ExifSaver implements Runnable
                else
                {
                        // Photo is now connected, so write new gps tags
-                       command = getWriteGpsExifTagsCommand(inPhoto.getFile(), inPhoto.getDataPoint(), inOverwriteFlag);
+                       command = getWriteGpsExifTagsCommand(inPhoto.getFile(), inPhoto.getDataPoint(), inOverwriteFlag, inForceFlag);
                }
                // Execute exif command
+               boolean saved = false;
                try
                {
                        Process process = Runtime.getRuntime().exec(command);
@@ -310,15 +342,15 @@ public class ExifSaver implements Runnable
                                process.waitFor();
                        }
                        catch (InterruptedException ie) {}
+                       saved = (process.exitValue() == 0);
                }
                catch (Exception e)
                {
                        // show error message
                        JOptionPane.showMessageDialog(_parentFrame, "Exception: '" + e.getClass().getName() + "' : "
                                + e.getMessage(), I18nManager.getText("dialog.saveexif.title"), JOptionPane.ERROR_MESSAGE);
-                       return false;
                }
-               return true;
+               return saved;
        }
 
 
@@ -348,16 +380,22 @@ public class ExifSaver implements Runnable
         * @param inFile file to which to write the tags
         * @param inPoint DataPoint object containing coordinate information
         * @param inOverwrite true to overwrite file, false to create copy
+        * @param inForce true to force write, ignoring minor errors
         * @return external command to write gps tags
         */
-       private static String[] getWriteGpsExifTagsCommand(File inFile, DataPoint inPoint, boolean inOverwrite)
+       private static String[] getWriteGpsExifTagsCommand(File inFile, DataPoint inPoint,
+               boolean inOverwrite, boolean inForce)
        {
                // Make a string array to construct the command and its parameters
-               String[] result = new String[inOverwrite?10:9];
+               String[] result = new String[(inOverwrite?10:9) + (inForce?1:0)];
                result[0] = Config.getConfigString(Config.KEY_EXIFTOOL_PATH);
                result[1] = "-P";
                if (inOverwrite) {result[2] = "-overwrite_original_in_place";}
                int paramOffset = inOverwrite?3:2;
+               if (inForce) {
+                       result[paramOffset] = "-m";
+                       paramOffset++;
+               }
                // To set latitude : -GPSLatitude='12 34 56.78' -GPSLatitudeRef='N'
                // (latitude as space-separated deg min sec, reference as either N or S)
                result[paramOffset] = "-GPSLatitude='" + inPoint.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC_WITH_SPACES)
index 3ea60db55961c093a826e659aab5a6a0ef79c88c..63810143800e92aca3eeb81944529c563ae76862 100644 (file)
@@ -32,9 +32,9 @@ import javax.swing.ListSelectionModel;
 import javax.swing.table.TableModel;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
@@ -47,13 +47,12 @@ import tim.prune.load.OneCharDocument;
 
 /**
  * Class to manage the saving of track data
- * into a user-specified file
+ * as text into a user-specified file
  */
 public class FileSaver
 {
        private App _app = null;
        private JFrame _parentFrame = null;
-       private Track _track = null;
        private JDialog _dialog = null;
        private JFileChooser _fileChooser = null;
        private JPanel _cards = null;
@@ -80,13 +79,11 @@ public class FileSaver
         * Constructor
         * @param inApp application object to inform of success
         * @param inParentFrame parent frame
-        * @param inTrack track object to save
         */
-       public FileSaver(App inApp, JFrame inParentFrame, Track inTrack)
+       public FileSaver(App inApp, JFrame inParentFrame)
        {
                _app = inApp;
                _parentFrame = inParentFrame;
-               _track = inTrack;
        }
 
 
@@ -104,13 +101,14 @@ public class FileSaver
                        _dialog.pack();
                }
                // Check field list
-               FieldList fieldList = _track.getFieldList();
+               Track track = _app.getTrackInfo().getTrack();
+               FieldList fieldList = track.getFieldList();
                int numFields = fieldList.getNumFields();
                _model = new FieldSelectionTableModel(numFields);
                for (int i=0; i<numFields; i++)
                {
                        Field field = fieldList.getField(i);
-                       FieldInfo info = new FieldInfo(field, _track.hasData(field));
+                       FieldInfo info = new FieldInfo(field, track.hasData(field));
                        _model.addFieldInfo(info, i);
                }
                // Initialise dialog and show it
@@ -384,15 +382,16 @@ public class FileSaver
 
 
        /**
-        * Save the track to file with the chosen options
+        * Start the save process by choosing the file to save to
         * @return true if successful or cancelled, false if failed
         */
        private boolean saveToFile()
        {
-               // TODO: Shorten method
-               if (!_pointTypeSelector.getAnythingSelected()) {return false;}
-               boolean saveOK = true;
-               FileWriter writer = null;
+               if (!_pointTypeSelector.getAnythingSelected()) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
+                               I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
+                       return false;
+               }
                if (_fileChooser == null)
                {
                        _fileChooser = new JFileChooser();
@@ -408,179 +407,209 @@ public class FileSaver
                }
                if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
                {
-                       File saveFile = _fileChooser.getSelectedFile();
-                       String lineSeparator = System.getProperty("line.separator");
-                       // Get coordinate format and altitude format
-                       int coordFormat = Coordinate.FORMAT_NONE;
-                       for (int i=0; i<_coordUnitsRadios.length; i++)
-                               if (_coordUnitsRadios[i].isSelected())
-                                       coordFormat = FORMAT_COORDS[i];
-                       Altitude.Format altitudeFormat = Altitude.Format.NO_FORMAT;
-                       for (int i=0; i<_altitudeUnitsRadios.length; i++)
-                       {
-                               if (_altitudeUnitsRadios[i].isSelected())
-                               {
-                                       altitudeFormat = FORMAT_ALTS[i];
-                               }
+                       return saveToFile(_fileChooser.getSelectedFile());
+               }
+               return true; // cancelled
+       }
+
+
+       /**
+        * Save the track to the specified file using the chosen options
+        * @param inSaveFile file to save to
+        * @return true if save successful, false if failed
+        */
+       private boolean saveToFile(File inSaveFile)
+       {
+               // TODO: Shorten method
+               FileWriter writer = null;
+               final String lineSeparator = System.getProperty("line.separator");
+               boolean saveOK = true;
+               // Get coordinate format and altitude format
+               int coordFormat = Coordinate.FORMAT_NONE;
+               for (int i=0; i<_coordUnitsRadios.length; i++)
+                       if (_coordUnitsRadios[i].isSelected())
+                               coordFormat = FORMAT_COORDS[i];
+               Altitude.Format altitudeFormat = Altitude.Format.NO_FORMAT;
+               for (int i=0; i<_altitudeUnitsRadios.length; i++)
+               {
+                       if (_altitudeUnitsRadios[i].isSelected()) {
+                               altitudeFormat = FORMAT_ALTS[i];
                        }
-                       // Get timestamp formats
-                       int timestampFormat = Timestamp.FORMAT_ORIGINAL;
-                       for (int i=0; i<_timestampUnitsRadios.length; i++)
-                       {
-                               if (_timestampUnitsRadios[i].isSelected())
-                               {
-                                       timestampFormat = FORMAT_TIMES[i];
-                               }
+               }
+               // Get timestamp format
+               int timestampFormat = Timestamp.FORMAT_ORIGINAL;
+               for (int i=0; i<_timestampUnitsRadios.length; i++)
+               {
+                       if (_timestampUnitsRadios[i].isSelected()) {
+                               timestampFormat = FORMAT_TIMES[i];
                        }
+               }
 
-                       // Check if file exists, and confirm overwrite if necessary
-                       Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
-                       if (!saveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
-                                       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)
+               // Check if file exists, and confirm overwrite if necessary
+               Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
+               if (!inSaveFile.exists() || JOptionPane.showOptionDialog(_parentFrame,
+                               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)
+               {
+                       try
                        {
-                               try
-                               {
-                                       // Create output file
-                                       writer = new FileWriter(saveFile);
-                                       // Determine delimiter character to use
-                                       char delimiter = getDelimiter();
-                                       FieldInfo info = null;
-                                       Field field = null;
+                               // Create output file
+                               writer = new FileWriter(inSaveFile);
+                               // Determine delimiter character to use
+                               final char delimiter = getDelimiter();
+                               FieldInfo info = null;
 
-                                       StringBuffer buffer = null;
-                                       int numFields = _model.getRowCount();
-                                       boolean firstField = true;
-                                       // Write header row if required
-                                       if (_headerRowCheckbox.isSelected())
+                               StringBuffer buffer = null;
+                               int numFields = _model.getRowCount();
+                               boolean firstField = true;
+                               // Write header row if required
+                               if (_headerRowCheckbox.isSelected())
+                               {
+                                       buffer = new StringBuffer();
+                                       for (int f=0; f<numFields; f++)
                                        {
-                                               buffer = new StringBuffer();
-                                               for (int f=0; f<numFields; f++)
+                                               info = _model.getFieldInfo(f);
+                                               if (info.isSelected())
                                                {
-                                                       info = _model.getFieldInfo(f);
-                                                       if (info.isSelected())
-                                                       {
-                                                               if (!firstField)
-                                                               {
-                                                                       // output field separator
-                                                                       buffer.append(delimiter);
-                                                               }
-                                                               field = info.getField();
-                                                               buffer.append(field.getName());
-                                                               firstField = false;
+                                                       // output field separator
+                                                       if (!firstField) {
+                                                               buffer.append(delimiter);
                                                        }
+                                                       buffer.append(info.getField().getName());
+                                                       firstField = false;
                                                }
-                                               writer.write(buffer.toString());
-                                               writer.write(lineSeparator);
                                        }
+                                       writer.write(buffer.toString());
+                                       writer.write(lineSeparator);
+                               }
 
-                                       // Loop over points outputting each in turn to buffer
-                                       final int numPoints = _track.getNumPoints();
-                                       int numSaved = 0;
-                                       for (int p=0; p<numPoints; p++)
+                               // Examine selection
+                               int selStart = -1, selEnd = -1;
+                               if (_pointTypeSelector.getJustSelection()) {
+                                       selStart = _app.getTrackInfo().getSelection().getStart();
+                                       selEnd = _app.getTrackInfo().getSelection().getEnd();
+                               }
+                               // Loop over points outputting each in turn to buffer
+                               Track track = _app.getTrackInfo().getTrack();
+                               final int numPoints = track.getNumPoints();
+                               int numSaved = 0;
+                               for (int p=0; p<numPoints; p++)
+                               {
+                                       DataPoint point = track.getPoint(p);
+                                       boolean savePoint = ((point.isWaypoint() && _pointTypeSelector.getWaypointsSelected())
+                                               || (!point.isWaypoint() && point.getPhoto()==null && _pointTypeSelector.getTrackpointsSelected())
+                                               || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected()))
+                                               && (!_pointTypeSelector.getJustSelection() || (p>=selStart && p<=selEnd));
+                                       if (!savePoint) {continue;}
+                                       numSaved++;
+                                       firstField = true;
+                                       buffer = new StringBuffer();
+                                       for (int f=0; f<numFields; f++)
                                        {
-                                               DataPoint point = _track.getPoint(p);
-                                               boolean savePoint = ((point.isWaypoint() && _pointTypeSelector.getWaypointsSelected())
-                                                       || (!point.isWaypoint() && point.getPhoto()==null && _pointTypeSelector.getTrackpointsSelected())
-                                                       || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected()));
-                                               if (!savePoint) {continue;}
-                                               numSaved++;
-                                               firstField = true;
-                                               buffer = new StringBuffer();
-                                               for (int f=0; f<numFields; f++)
+                                               info = _model.getFieldInfo(f);
+                                               if (info.isSelected())
                                                {
-                                                       info = _model.getFieldInfo(f);
-                                                       if (info.isSelected())
+                                                       if (!firstField)
                                                        {
-                                                               if (!firstField)
-                                                               {
-                                                                       // output field separator
-                                                                       buffer.append(delimiter);
-                                                               }
-                                                               field = info.getField();
-                                                               // Output field according to type
-                                                               if (field == Field.LATITUDE)
-                                                               {
-                                                                       buffer.append(point.getLatitude().output(coordFormat));
-                                                               }
-                                                               else if (field == Field.LONGITUDE)
-                                                               {
-                                                                       buffer.append(point.getLongitude().output(coordFormat));
-                                                               }
-                                                               else if (field == Field.ALTITUDE)
-                                                               {
-                                                                       try
-                                                                       {
-                                                                               buffer.append(point.getAltitude().getStringValue(altitudeFormat));
-                                                                       }
-                                                                       catch (NullPointerException npe) {}
-                                                               }
-                                                               else if (field == Field.TIMESTAMP)
-                                                               {
-                                                                       if (point.hasTimestamp())
-                                                                       {
-                                                                               if (timestampFormat == Timestamp.FORMAT_ORIGINAL) {
-                                                                                       // output original string
-                                                                                       buffer.append(point.getFieldValue(Field.TIMESTAMP));
-                                                                               }
-                                                                               else {
-                                                                                       // format value accordingly
-                                                                                       buffer.append(point.getTimestamp().getText(timestampFormat));
-                                                                               }
-                                                                       }
-                                                               }
-                                                               else
-                                                               {
-                                                                       String value = point.getFieldValue(field);
-                                                                       if (value != null)
-                                                                       {
-                                                                               buffer.append(value);
-                                                                       }
-                                                               }
-                                                               firstField = false;
+                                                               // output field separator
+                                                               buffer.append(delimiter);
                                                        }
-                                               }
-                                               // Output to file
-                                               writer.write(buffer.toString());
-                                               writer.write(lineSeparator);
-                                       }
-                                       // Store directory in config for later
-                                       Config.setConfigString(Config.KEY_TRACK_DIR, saveFile.getParentFile().getAbsolutePath());
-                                       // Save successful
-                                       UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
-                                                + " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
-                                                + " " + saveFile.getAbsolutePath());
-                                       _app.informDataSaved();
-                               }
-                               catch (IOException ioe)
-                               {
-                                       saveOK = false;
-                                       _app.showErrorMessageNoLookup("error.save.dialogtitle",
-                                               I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
-                               }
-                               finally
-                               {
-                                       // try to close file if it's open
-                                       try {
-                                               if (writer != null) {
-                                                       writer.close();
+                                                       saveField(buffer, point, info.getField(), coordFormat, altitudeFormat, timestampFormat);
+                                                       firstField = false;
                                                }
                                        }
-                                       catch (Exception e) {}
+                                       // Output to file
+                                       writer.write(buffer.toString());
+                                       writer.write(lineSeparator);
                                }
+                               // Store directory in config for later
+                               Config.setConfigString(Config.KEY_TRACK_DIR, inSaveFile.getParentFile().getAbsolutePath());
+                               // Save successful
+                               UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+                                        + " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
+                                        + " " + inSaveFile.getAbsolutePath());
+                               _app.informDataSaved();
                        }
-                       else
+                       catch (IOException ioe)
                        {
-                               // Overwrite file confirm cancelled
                                saveOK = false;
+                               _app.showErrorMessageNoLookup("error.save.dialogtitle",
+                                       I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
                        }
+                       finally
+                       {
+                               // try to close file if it's open
+                               try {
+                                       writer.close();
+                               }
+                               catch (Exception e) {}
+                       }
+               }
+               else
+               {
+                       // Overwrite file confirm cancelled
+                       saveOK = false;
                }
                return saveOK;
        }
 
 
+       /**
+        * Format the given field and append to the given buffer for saving
+        * @param inBuffer buffer to append to
+        * @param inPoint point object
+        * @param inField field object
+        * @param inCoordFormat coordinate format
+        * @param inAltitudeFormat altitude format
+        * @param inTimestampFormat timestamp format
+        */
+       private void saveField(StringBuffer inBuffer, DataPoint inPoint, Field inField,
+               int inCoordFormat, Altitude.Format inAltitudeFormat, int inTimestampFormat)
+       {
+               // Output field according to type
+               if (inField == Field.LATITUDE)
+               {
+                       inBuffer.append(inPoint.getLatitude().output(inCoordFormat));
+               }
+               else if (inField == Field.LONGITUDE)
+               {
+                       inBuffer.append(inPoint.getLongitude().output(inCoordFormat));
+               }
+               else if (inField == Field.ALTITUDE)
+               {
+                       try
+                       {
+                               inBuffer.append(inPoint.getAltitude().getStringValue(inAltitudeFormat));
+                       }
+                       catch (NullPointerException npe) {}
+               }
+               else if (inField == Field.TIMESTAMP)
+               {
+                       if (inPoint.hasTimestamp())
+                       {
+                               if (inTimestampFormat == Timestamp.FORMAT_ORIGINAL) {
+                                       // output original string
+                                       inBuffer.append(inPoint.getFieldValue(Field.TIMESTAMP));
+                               }
+                               else {
+                                       // format value accordingly
+                                       inBuffer.append(inPoint.getTimestamp().getText(inTimestampFormat));
+                               }
+                       }
+               }
+               else
+               {
+                       String value = inPoint.getFieldValue(inField);
+                       if (value != null)
+                       {
+                               inBuffer.append(value);
+                       }
+               }
+       }
+
+
        /**
         * @return the selected delimiter character
         */
index a9c59a8ce1e169adeb5d2c64fb9eb329023b2869..944a7e4e4f1ff4780263d2a3ec58f80245caa5d6 100644 (file)
@@ -25,10 +25,10 @@ import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.ExternalTools;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.config.Config;
 
 /**
  * Class to manage the loading of GPS data using GpsBabel
@@ -257,8 +257,8 @@ 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, true}; // export everything
-               GpxExporter.exportData(writer, _app.getTrackInfo().getTrack(), trackName, null, saveFlags);
+               boolean[] saveFlags = {true, true, true, false, true}; // export everything
+               GpxExporter.exportData(writer, _app.getTrackInfo(), trackName, null, saveFlags, false);
                writer.close();
 
                // Read the error stream to see if there's a better error message there
diff --git a/tim/prune/save/GpxCacher.java b/tim/prune/save/GpxCacher.java
new file mode 100644 (file)
index 0000000..ef1f3d5
--- /dev/null
@@ -0,0 +1,198 @@
+package tim.prune.save;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.SourceInfo;
+
+/**
+ * Class to read in a GPX file and cache all the point strings
+ */
+public class GpxCacher extends DefaultHandler
+{
+       private SourceInfo _sourceInfo = null;
+       private String _headerString = null;
+       private String[] _strings = null;
+       private int _pointNum = 0;
+       private boolean _insidePoint = false;
+       private StringBuilder _builder = null;
+
+
+       /**
+        * Constructor
+        * @param inSourceInfo source information
+        */
+       public GpxCacher(SourceInfo inInfo)
+       {
+               _sourceInfo = inInfo;
+               _strings = new String[inInfo.getNumPoints()];
+               _pointNum = 0;
+               // Should be a gpx file, but might be raw, zipped or gzipped
+               File gpxFile = inInfo.getFile();
+               String fileName = gpxFile.getName().toLowerCase();
+               if (gpxFile.exists() && gpxFile.canRead())
+               {
+                       try {
+                               SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+                               if (fileName.endsWith(".gpx") || fileName.endsWith(".xml")) {
+                                       saxParser.parse(inInfo.getFile(), this);
+                               }
+                               else if (fileName.endsWith(".zip")) {
+                                       saxParser.parse(getZipInputStream(inInfo.getFile()), this);
+                               }
+                               else if (fileName.endsWith(".gz")) {
+                                       saxParser.parse(new GZIPInputStream(new FileInputStream(inInfo.getFile())), this);
+                               }
+                               else {
+                                       System.out.println("Unrecognised file type: " + inInfo.getFile().getName());
+                               }
+                       } catch (Exception e) {
+                               // TODO: Handle errors here with a list of warnings?
+                               e.printStackTrace();
+                       }
+               }
+               _builder = null;
+       }
+
+
+       /**
+        * Receive the start of a tag
+        */
+       public void startElement(String inUri, String inLocalName, String inTagName,
+               Attributes inAttributes) throws SAXException
+       {
+               if (inTagName.equalsIgnoreCase("gpx"))
+               {
+                       // store initial gpx tag
+                       _builder = new StringBuilder(60);
+                       appendTag(_builder, inTagName, inAttributes);
+                       _headerString = _builder.toString();
+               }
+               else
+               {
+                       if (inTagName.equalsIgnoreCase("wpt") || inTagName.equalsIgnoreCase("trkpt")
+                               || inTagName.equalsIgnoreCase("rtept"))
+                       {
+                               _insidePoint = true;
+                               _builder = new StringBuilder(60);
+                       }
+                       if (_insidePoint) {
+                               appendTag(_builder, inTagName, inAttributes);
+                       }
+               }
+               super.startElement(inUri, inLocalName, inTagName, inAttributes);
+       }
+
+       /**
+        * Receive characters between tags (inside or outside)
+        */
+       public void characters(char[] inChars, int inStart, int inLength)
+               throws SAXException
+       {
+               if (_insidePoint) {
+                       _builder.append(new String(inChars, inStart, inLength));
+               }
+               super.characters(inChars, inStart, inLength);
+       }
+
+       /**
+        * Receive end of xml tag
+        */
+       public void endElement(String inUri, String inLocalName, String inTagName)
+               throws SAXException
+       {
+               if (_insidePoint) {
+                       _builder.append("</").append(inTagName).append('>');
+               }
+               if (inTagName.equalsIgnoreCase("wpt") || inTagName.equalsIgnoreCase("trkpt")
+                       || inTagName.equalsIgnoreCase("rtept"))
+               {
+                       _strings[_pointNum] = _builder.toString();
+                       _pointNum++;
+                       _insidePoint = false;
+               }
+               super.endElement(inUri, inLocalName, inTagName);
+       }
+
+
+       /**
+        * Append the current tag to the supplied StringBuilder
+        * @param inBuilder Stringbuilder object to append tag to
+        * @param inTagName name of tag
+        * @param inAttributes attributes of tag
+        */
+       private static void appendTag(StringBuilder inBuilder, String inTagName, Attributes inAttributes)
+       {
+               inBuilder.append('<').append(inTagName);
+               int numAtts = inAttributes.getLength();
+               for (int i=0; i<numAtts; i++) {
+                       inBuilder.append(' ').append(inAttributes.getQName(i)).append("=\"")
+                               .append(inAttributes.getValue(i)).append('"');
+               }
+               inBuilder.append('>');
+       }
+
+
+       /**
+        * @return the header string from the GPX tag
+        */
+       public String getHeaderString()
+       {
+               return _headerString;
+       }
+
+       /**
+        * Get the source string for the given point
+        * @param inPoint point to retrieve
+        * @return string if found, otherwise null
+        */
+       public String getSourceString(DataPoint inPoint)
+       {
+               int index = _sourceInfo.getIndex(inPoint);
+               if (index >= 0) {
+                       return _strings[index];
+               }
+               return null;
+       }
+
+       /**
+        * Get an inputstream of a GPX file inside a zip
+        * @param inFile File object describing zip file
+        * @return input stream for Xml parser
+        */
+       private static InputStream getZipInputStream(File inFile)
+       {
+               try
+               {
+                       ZipInputStream zis = new ZipInputStream(new FileInputStream(inFile));
+                       while (zis.available() > 0)
+                       {
+                               ZipEntry entry = zis.getNextEntry();
+                               String entryName = entry.toString();
+                               if (entryName != null && entryName.length() > 4)
+                               {
+                                       String suffix = entryName.substring(entryName.length()-4).toLowerCase();
+                                       if (suffix.equals(".gpx") || suffix.equals(".xml")) {
+                                               // First matching file so must be gpx
+                                               return zis;
+                                       }
+                               }
+                       }
+               }
+               catch (Exception e) {} // ignore errors
+               // not found - error!
+               return null;
+       }
+}
diff --git a/tim/prune/save/GpxCacherList.java b/tim/prune/save/GpxCacherList.java
new file mode 100644 (file)
index 0000000..126c22d
--- /dev/null
@@ -0,0 +1,68 @@
+package tim.prune.save;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.FileInfo;
+import tim.prune.data.SourceInfo;
+
+/**
+ * Class to hold a list of GpxCacher objects
+ * and get the original source xml for data points
+ */
+public class GpxCacherList
+{
+       /** Array of Gpx Cachers */
+       private GpxCacher[] _cacherList = null;
+
+       /**
+        * Constructor
+        * @param inInfo file info object
+        */
+       public GpxCacherList(FileInfo inInfo)
+       {
+               int numFiles = inInfo.getNumFiles();
+               _cacherList = new GpxCacher[numFiles];
+               for (int i=0; i<numFiles; i++) {
+                       SourceInfo info = inInfo.getSource(i);
+                       if (info.getFileType() == SourceInfo.FILE_TYPE.GPX) {
+                               _cacherList[i] = new GpxCacher(info);
+                       }
+               }
+       }
+
+       /**
+        * Get the source for the given data point
+        * @param inPoint point to look for
+        * @return source string or null if not found
+        */
+       public String getSourceString(DataPoint inPoint)
+       {
+               String str = null;
+               // Check if point has been modified, if so return null
+               if (inPoint.isModified()) {return null;}
+               // Loop over sources
+               for (int i=0; i<_cacherList.length && (str == null); i++) {
+                       GpxCacher cacher = _cacherList[i];
+                       if (cacher != null) {
+                               str = cacher.getSourceString(inPoint);
+                       }
+               }
+               return str;
+       }
+
+       /**
+        * @return the first non-empty header from the list
+        */
+       public String getFirstHeader()
+       {
+               String str = null;
+               // Loop over sources
+               for (int i=0; i<_cacherList.length && (str == null || str.equals("")); i++)
+               {
+                       GpxCacher cacher = _cacherList[i];
+                       if (cacher != null) {
+                               str = cacher.getHeaderString();
+                       }
+               }
+               return str;
+       }
+}
index 0bd93d2f0926ad09b6cdc73b637b464f006a6011..019cc438bdbf96003595e7c7ebe7d84654839d11 100644 (file)
@@ -13,27 +13,30 @@ import java.io.OutputStreamWriter;
 import java.io.Writer;
 
 import javax.swing.BorderFactory;
+import javax.swing.Box;
 import javax.swing.BoxLayout;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JDialog;
 import javax.swing.JFileChooser;
+import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JTextField;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.GenericFunction;
 import tim.prune.GpsPruner;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
+import tim.prune.data.Field;
 import tim.prune.data.Timestamp;
-import tim.prune.data.Track;
+import tim.prune.data.TrackInfo;
 import tim.prune.load.GenericFileFilter;
 
 /**
@@ -42,17 +45,15 @@ import tim.prune.load.GenericFileFilter;
  */
 public class GpxExporter extends GenericFunction implements Runnable
 {
-       private Track _track = null;
+       private TrackInfo _trackInfo = null;
        private JDialog _dialog = null;
        private JTextField _nameField = null;
        private JTextField _descriptionField = null;
        private PointTypeSelector _pointTypeSelector = null;
        private JCheckBox _timestampsCheckbox = null;
-       private JFileChooser _fileChooser = null;
+       private JCheckBox _copySourceCheckbox = null;
        private File _exportFile = null;
 
-       /** version number of Gpx */
-       private static final String GPX_VERSION_NUMBER = "1.0";
        /** this program name */
        private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
 
@@ -64,7 +65,7 @@ public class GpxExporter extends GenericFunction implements Runnable
        public GpxExporter(App inApp)
        {
                super(inApp);
-               _track = inApp.getTrackInfo().getTrack();
+               _trackInfo = inApp.getTrackInfo();
        }
 
        /** Get name key */
@@ -111,13 +112,19 @@ public class GpxExporter extends GenericFunction implements Runnable
                _descriptionField = new JTextField(10);
                descPanel.add(_descriptionField);
                mainPanel.add(descPanel);
+               mainPanel.add(Box.createVerticalStrut(5));
                // point type selection (track points, waypoints, photo points)
                _pointTypeSelector = new PointTypeSelector();
                mainPanel.add(_pointTypeSelector);
-               // checkbox for timestamps
+               // checkboxes for timestamps and copying
+               JPanel checkPanel = new JPanel();
                _timestampsCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.includetimestamps"));
                _timestampsCheckbox.setSelected(true);
-               mainPanel.add(_timestampsCheckbox);
+               checkPanel.add(_timestampsCheckbox);
+               _copySourceCheckbox = new JCheckBox(I18nManager.getText("dialog.exportgpx.copysource"));
+               _copySourceCheckbox.setSelected(true);
+               checkPanel.add(_copySourceCheckbox);
+               mainPanel.add(checkPanel);
                dialogPanel.add(mainPanel, BorderLayout.CENTER);
 
                // button panel at bottom
@@ -154,28 +161,45 @@ public class GpxExporter extends GenericFunction implements Runnable
        {
                // OK pressed, so check selections
                if (!_pointTypeSelector.getAnythingSelected()) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
+                               I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
                        return;
                }
                // Choose output file
-               if (_fileChooser == null)
+               File saveFile = chooseGpxFile(_parentFrame);
+               if (saveFile != null)
                {
-                       _fileChooser = new JFileChooser();
-                       _fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
-                       _fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
-                       _fileChooser.setAcceptAllFileFilterUsed(false);
-                       // start from directory in config which should be set
-                       String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
-                       if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
+                       // New file or overwrite confirmed, so initiate export in separate thread
+                       _exportFile = saveFile;
+                       new Thread(this).start();
                }
+       }
+
+       /**
+        * Select a GPX file to save to
+        * @param inParentFrame parent frame for file chooser dialog
+        * @return selected File, or null if selection cancelled
+        */
+       public static File chooseGpxFile(JFrame inParentFrame)
+       {
+               File saveFile = null;
+               JFileChooser fileChooser = new JFileChooser();
+               fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
+               fileChooser.setFileFilter(new GenericFileFilter("filetype.gpx", new String[] {"gpx"}));
+               fileChooser.setAcceptAllFileFilterUsed(false);
+               // start from directory in config which should be set
+               String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
+               if (configDir != null) {fileChooser.setCurrentDirectory(new File(configDir));}
+
                // Allow choose again if an existing file is selected
                boolean chooseAgain = false;
                do
                {
                        chooseAgain = false;
-                       if (_fileChooser.showSaveDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
+                       if (fileChooser.showSaveDialog(inParentFrame) == JFileChooser.APPROVE_OPTION)
                        {
                                // OK pressed and file chosen
-                               File file = _fileChooser.getSelectedFile();
+                               File file = fileChooser.getSelectedFile();
                                // Check file extension
                                if (!file.getName().toLowerCase().endsWith(".gpx"))
                                {
@@ -183,25 +207,25 @@ public class GpxExporter extends GenericFunction implements Runnable
                                }
                                // 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(_parentFrame,
+                               if (!file.exists() || JOptionPane.showOptionDialog(inParentFrame,
                                                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, so initiate export in separate thread
-                                       _exportFile = file;
-                                       new Thread(this).start();
+                                       // new file or overwrite confirmed
+                                       saveFile = file;
                                }
                                else
                                {
+                                       // file exists and overwrite cancelled - select again
                                        chooseAgain = true;
                                }
                        }
                } while (chooseAgain);
+               return saveFile;
        }
 
-
        /**
         * Run method for controlling separate thread for exporting
         */
@@ -213,10 +237,11 @@ 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(), _timestampsCheckbox.isSelected()};
+                               _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getJustSelection(),
+                               _timestampsCheckbox.isSelected()};
                        // write file
-                       final int numPoints = exportData(writer, _track, _nameField.getText(),
-                               _descriptionField.getText(), saveFlags);
+                       final int numPoints = exportData(writer, _trackInfo, _nameField.getText(),
+                               _descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected());
 
                        // close file
                        writer.close();
@@ -249,23 +274,22 @@ public class GpxExporter extends GenericFunction implements Runnable
        /**
         * Export the information to the given writer
         * @param inWriter writer object
-        * @param inTrack track object containing data
+        * @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 inUseCopy true to copy source if available
         * @return number of points written
         * @throws IOException if io errors occur on write
         */
-       public static int exportData(OutputStreamWriter inWriter, Track inTrack, String inName,
-               String inDesc, boolean[] inSaveFlags) throws IOException
+       public static int exportData(OutputStreamWriter inWriter, TrackInfo inInfo, String inName,
+               String inDesc, boolean[] inSaveFlags, boolean inUseCopy) throws IOException
        {
-               inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpx version=\"");
-               inWriter.write(GPX_VERSION_NUMBER);
-               inWriter.write("\" creator=\"");
-               inWriter.write(GPX_CREATOR);
-               inWriter.write("\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
-                       + " xmlns=\"http://www.topografix.com/GPX/1/0\""
-                       + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n");
+               // Instantiate source file cachers in case we want to copy output
+               GpxCacherList gpxCachers = null;
+               if (inUseCopy) gpxCachers = new GpxCacherList(inInfo.getFileInfo());
+               // Write or copy header
+               inWriter.write(getHeaderString(gpxCachers));
                // Name field
                String trackName = "PruneTrack";
                if (inName != null && !inName.equals(""))
@@ -280,8 +304,7 @@ public class GpxExporter extends GenericFunction implements Runnable
                if (inDesc != null && !inDesc.equals("")) {
                        inWriter.write(inDesc);
                }
-               else
-               {
+               else {
                        inWriter.write("Export from Prune");
                }
                inWriter.write("</desc>\n");
@@ -292,56 +315,141 @@ public class GpxExporter extends GenericFunction implements Runnable
                final boolean exportTrackpoints = inSaveFlags[0];
                final boolean exportWaypoints = inSaveFlags[1];
                final boolean exportPhotos = inSaveFlags[2];
-               final boolean exportTimestamps = inSaveFlags[3];
+               final boolean exportSelection = inSaveFlags[3];
+               final boolean exportTimestamps = inSaveFlags[4];
+               // Examine selection
+               int selStart = -1, selEnd = -1;
+               if (exportSelection) {
+                       selStart = inInfo.getSelection().getStart();
+                       selEnd = inInfo.getSelection().getEnd();
+               }
                // Loop over waypoints
-               final int numPoints = inTrack.getNumPoints();
+               final int numPoints = inInfo.getTrack().getNumPoints();
                int numSaved = 0;
                for (i=0; i<numPoints; i++)
                {
-                       point = inTrack.getPoint(i);
-                       // Make a wpt element for each waypoint
-                       if (point.isWaypoint())
-                       {
-                               if (exportWaypoints) {
-                                       exportWaypoint(point, inWriter, exportTimestamps);
-                                       numSaved++;
+                       point = inInfo.getTrack().getPoint(i);
+                       if (!exportSelection || (i>=selStart && i<=selEnd)) {
+                               // Make a wpt element for each waypoint
+                               if (point.isWaypoint()) {
+                                       if (exportWaypoints)
+                                       {
+                                               String pointSource = (inUseCopy?gpxCachers.getSourceString(point):null);
+                                               if (pointSource != null) {
+                                                       inWriter.write(pointSource);
+                                                       inWriter.write('\n');
+                                               }
+                                               else {
+                                                       exportWaypoint(point, inWriter, exportTimestamps);
+                                               }
+                                               numSaved++;
+                                       }
+                               }
+                               else {
+                                       hasTrackpoints = true;
                                }
                        }
-                       else
-                       {
-                               hasTrackpoints = true;
-                       }
                }
-               // Output the track, if there is one
-               if (hasTrackpoints)
+               // Export both route points and then track points
+               if (hasTrackpoints && (exportTrackpoints || exportPhotos))
+               {
+                       // Output all route points (if any)
+                       numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
+                               exportTimestamps, true, gpxCachers, "<rtept", "\t<rte><number>1</number>\n", null, "\t</rte>\n");
+                       // Output all track points, if any
+                       String trackStart = "\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n";
+                       numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
+                               exportTimestamps, false, gpxCachers, "<trkpt", trackStart, "\t</trkseg>\n\t<trkseg>\n",
+                               "\t</trkseg></trk>\n");
+               }
+
+               inWriter.write("</gpx>\n");
+               return numSaved;
+       }
+
+       /**
+        * Loop through the track outputting the relevant track points
+        * @param inWriter writer object for output
+        * @param inInfo track info object containing track
+        * @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 inOnlyCopies true to only export if source can be copied
+        * @param inCachers list of GpxCachers
+        * @param inPointTag tag to match for each point
+        * @param inStartTag start tag to output
+        * @param inSegmentTag tag to output between segments (or null)
+        * @param inEndTag end tag to output
+        */
+       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)
+       throws IOException
+       {
+               // Note: far too many input parameters to this method but avoids duplication
+               // of output functionality for writing track points and route points
+               int numPoints = inInfo.getTrack().getNumPoints();
+               int selStart = inInfo.getSelection().getStart();
+               int selEnd = inInfo.getSelection().getEnd();
+               int numSaved = 0;
+               // Loop over track points
+               for (int i=0; i<numPoints; i++)
                {
-                       boolean firstPoint = true;
-                       inWriter.write("\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n");
-                       // Loop over track points
-                       for (i=0; i<numPoints; i++)
+                       DataPoint point = inInfo.getTrack().getPoint(i);
+                       if ((!inExportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint())
                        {
-                               point = inTrack.getPoint(i);
-                               // restart track segment if necessary
-                               if (point.getSegmentStart() && !firstPoint) {
-                                       inWriter.write("\t</trkseg>\n\t<trkseg>\n");
-                               }
-                               if (!point.isWaypoint())
+                               if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos))
                                {
-                                       if ((point.getPhoto()==null && exportTrackpoints) || (point.getPhoto()!=null && exportPhotos))
+                                       // get the source from the point (if any)
+                                       String pointSource = (inCachers!=null?inCachers.getSourceString(point):null);
+                                       boolean writePoint = (pointSource != null && pointSource.toLowerCase().startsWith(inPointTag))
+                                               || (pointSource == null && !inOnlyCopies);
+                                       if (writePoint)
                                        {
-                                               // export the point
-                                               exportTrackpoint(point, inWriter, exportTimestamps);
+                                               // restart track segment if necessary
+                                               if ((numSaved > 0) && point.getSegmentStart() && (inSegmentTag != null)) {
+                                                       inWriter.write(inSegmentTag);
+                                               }
+                                               if (numSaved == 0) {inWriter.write(inStartTag);}
+                                               if (pointSource != null) {
+                                                       inWriter.write(pointSource);
+                                                       inWriter.write('\n');
+                                               }
+                                               else {
+                                                       if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps);}
+                                               }
                                                numSaved++;
-                                               firstPoint = false;
                                        }
                                }
                        }
-                       inWriter.write("\t</trkseg></trk>\n");
                }
-               inWriter.write("</gpx>\n");
+               if (numSaved > 0) {inWriter.write(inEndTag);}
                return numSaved;
        }
 
+       /**
+        * Get the header string for the gpx
+        * @param inCachers cacher list to ask for headers, if available
+        * @return header string from cachers or as default
+        */
+       private static String getHeaderString(GpxCacherList inCachers)
+       {
+               String gpxHeader = null;
+               if (inCachers != null) {gpxHeader = inCachers.getFirstHeader();}
+               if (gpxHeader == null || gpxHeader.length() < 5)
+               {
+                       // Create default (1.0) header
+                       gpxHeader = "<gpx version=\"1.0\" creator=\"" + GPX_CREATOR
+                               + "\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
+                               + " xmlns=\"http://www.topografix.com/GPX/1/0\""
+                               + " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">\n";
+               }
+               return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + gpxHeader + "\n";
+       }
+
        /**
         * Export the specified waypoint into the file
         * @param inPoint waypoint to export
@@ -364,10 +472,6 @@ public class GpxExporter extends GenericFunction implements Runnable
                        inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
                        inWriter.write("</ele>\n");
                }
-               // write waypoint name after elevation
-               inWriter.write("\t\t<name>");
-               inWriter.write(inPoint.getWaypointName().trim());
-               inWriter.write("</name>\n");
                // timestamp if available (point might have timestamp and then be turned into a waypoint)
                if (inPoint.hasTimestamp() && inTimestamps)
                {
@@ -375,7 +479,22 @@ public class GpxExporter extends GenericFunction implements Runnable
                        inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
                        inWriter.write("</time>\n");
                }
-               // TODO: Include waypt type in Gpx
+               // write waypoint name after elevation and time
+               inWriter.write("\t\t<name>");
+               inWriter.write(inPoint.getWaypointName().trim());
+               inWriter.write("</name>\n");
+               // write waypoint type if any
+               String type = inPoint.getFieldValue(Field.WAYPT_TYPE);
+               if (type != null)
+               {
+                       type = type.trim();
+                       if (!type.equals(""))
+                       {
+                               inWriter.write("\t\t<type>");
+                               inWriter.write(type);
+                               inWriter.write("</type>\n");
+                       }
+               }
                inWriter.write("\t</wpt>\n");
        }
 
index 42ba748ff242b06a84b1bb3dccaa22ee479f0d2f..a5a4ee26a87e0bc7d383acf70d24fbc42434f270 100644 (file)
@@ -1,11 +1,14 @@
 package tim.prune.save;
 
 import java.awt.BorderLayout;
+import java.awt.Color;
 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.MouseAdapter;
+import java.awt.event.MouseEvent;
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -33,22 +36,25 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.ColourUtils;
+import tim.prune.config.Config;
 import tim.prune.data.Altitude;
 import tim.prune.data.Coordinate;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Field;
 import tim.prune.data.Track;
 import tim.prune.data.TrackInfo;
+import tim.prune.gui.ColourChooser;
+import tim.prune.gui.ColourPatch;
 import tim.prune.gui.ImageUtils;
 import tim.prune.load.GenericFileFilter;
 
 /**
  * Class to export track information
- * into a specified Kml file
+ * into a specified Kml or Kmz file
  */
 public class KmlExporter extends GenericFunction implements Runnable
 {
@@ -60,21 +66,26 @@ public class KmlExporter extends GenericFunction implements Runnable
        private JCheckBox _altitudesCheckbox = null;
        private JCheckBox _kmzCheckbox = null;
        private JCheckBox _exportImagesCheckbox = null;
+       private ColourPatch _colourPatch = null;
        private JLabel _progressLabel = null;
        private JProgressBar _progressBar = null;
+       private Dimension[] _imageDimensions = null;
        private JFileChooser _fileChooser = null;
        private File _exportFile = null;
        private JButton _okButton = null;
        private boolean _cancelPressed = false;
+       private ColourChooser _colourChooser = null;
 
        // Filename of Kml file within zip archive
        private static final String KML_FILENAME_IN_KMZ = "doc.kml";
        // Default width and height of thumbnail images in Kmz
        private static final int DEFAULT_THUMBNAIL_WIDTH = 240;
-       private static final int DEFAULT_THUMBNAIL_HEIGHT = 180;
+       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
 
 
        /**
@@ -106,6 +117,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                        _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
                        _dialog.getContentPane().add(makeDialogComponents());
                        _dialog.pack();
+                       _colourChooser = new ColourChooser(_dialog);
                }
                enableCheckboxes();
                _descriptionField.setEnabled(true);
@@ -139,6 +151,23 @@ public class KmlExporter extends GenericFunction implements Runnable
                _pointTypeSelector = new PointTypeSelector();
                _pointTypeSelector.setAlignmentX(Component.CENTER_ALIGNMENT);
                mainPanel.add(_pointTypeSelector);
+               // Colour definition
+               Color trackColour = ColourUtils.colourFromHex(Config.getConfigString(Config.KEY_KML_TRACK_COLOUR));
+               if (trackColour == null) {
+                       trackColour = DEFAULT_TRACK_COLOUR;
+               }
+               _colourPatch = new ColourPatch(trackColour);
+               _colourPatch.addMouseListener(new MouseAdapter() {
+                       public void mouseClicked(MouseEvent e) {
+                               _colourChooser.showDialog(_colourPatch.getBackground());
+                               Color colour = _colourChooser.getChosenColour();
+                               if (colour != null) _colourPatch.setColour(colour);
+                       }
+               });
+               JPanel colourPanel = new JPanel();
+               colourPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.trackcolour")));
+               colourPanel.add(_colourPatch);
+               mainPanel.add(colourPanel);
                // Checkbox for altitude export
                _altitudesCheckbox = new JCheckBox(I18nManager.getText("dialog.exportkml.altitude"));
                _altitudesCheckbox.setHorizontalTextPosition(SwingConstants.LEFT);
@@ -217,6 +246,8 @@ public class KmlExporter extends GenericFunction implements Runnable
        {
                // OK pressed, now validate selection checkboxes
                if (!_pointTypeSelector.getAnythingSelected()) {
+                       JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
+                               I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
                        return;
                }
                // Choose output file
@@ -230,12 +261,10 @@ public class KmlExporter extends GenericFunction implements Runnable
                        if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
                }
                String requiredExtension = null, otherExtension = null;
-               if (_kmzCheckbox.isSelected())
-               {
+               if (_kmzCheckbox.isSelected()) {
                        requiredExtension = ".kmz"; otherExtension = ".kml";
                }
-               else
-               {
+               else {
                        requiredExtension = ".kml"; otherExtension = ".kmz";
                }
                _fileChooser.setAcceptAllFileFilterUsed(false);
@@ -300,6 +329,8 @@ public class KmlExporter extends GenericFunction implements Runnable
                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;}
+               // Create array for image dimensions in case it's required
+               _imageDimensions = new Dimension[_track.getNumPoints()];
 
                OutputStreamWriter writer = null;
                ZipOutputStream zipOutputStream = null;
@@ -329,6 +360,8 @@ public class KmlExporter extends GenericFunction implements Runnable
                        }
                        // write file
                        final int numPoints = exportData(writer, exportImages);
+                       // update config with selected track colour
+                       Config.setConfigString(Config.KEY_KML_TRACK_COLOUR, ColourUtils.makeHexCode(_colourPatch.getBackground()));
                        // update progress bar
                        _progressBar.setValue(1);
 
@@ -343,6 +376,7 @@ public class KmlExporter extends GenericFunction implements Runnable
 
                        // close file
                        writer.close();
+                       _imageDimensions = null;
                        // Store directory in config for later
                        Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
                        // show confirmation
@@ -355,7 +389,6 @@ public class KmlExporter extends GenericFunction implements Runnable
                }
                catch (IOException ioe)
                {
-                       // System.out.println("Exception: " + ioe.getClass().getName() + " - " + ioe.getMessage());
                        try {
                                if (writer != null) writer.close();
                        }
@@ -381,6 +414,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                boolean writeTrack = _pointTypeSelector.getTrackpointsSelected();
                boolean writeWaypoints = _pointTypeSelector.getWaypointsSelected();
                boolean writePhotos = _pointTypeSelector.getPhotopointsSelected();
+               boolean justSelection = _pointTypeSelector.getJustSelection();
                inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://earth.google.com/kml/2.1\">\n<Folder>\n");
                inWriter.write("\t<name>");
                if (_descriptionField != null && _descriptionField.getText() != null && !_descriptionField.getText().equals(""))
@@ -393,11 +427,17 @@ public class KmlExporter extends GenericFunction implements Runnable
                }
                inWriter.write("</name>\n");
 
+               // Examine selection if required
+               int selStart = -1, selEnd = -1;
+               if (justSelection) {
+                       selStart = _trackInfo.getSelection().getStart();
+                       selEnd = _trackInfo.getSelection().getEnd();
+               }
+
                boolean absoluteAltitudes = _altitudesCheckbox.isSelected();
                int i = 0;
                DataPoint point = null;
                boolean hasTrackpoints = false;
-               // Loop over waypoints (if any)
                boolean writtenPhotoHeader = false;
                final int numPoints = _track.getNumPoints();
                int numSaved = 0;
@@ -406,10 +446,12 @@ public class KmlExporter extends GenericFunction implements Runnable
                for (i=0; i<numPoints; i++)
                {
                        point = _track.getPoint(i);
+                       boolean writeCurrentPoint = !justSelection || (i>=selStart && i<=selEnd);
                        // Make a blob for each waypoint
                        if (point.isWaypoint())
                        {
-                               if (writeWaypoints) {
+                               if (writeWaypoints && writeCurrentPoint)
+                               {
                                        exportWaypoint(point, inWriter, absoluteAltitudes);
                                        numSaved++;
                                }
@@ -420,7 +462,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                        }
                        // Make a blob with description for each photo
                        // Photos have already been written so picture sizes already known
-                       if (point.getPhoto() != null && writePhotos)
+                       if (point.getPhoto() != null && writePhotos && writeCurrentPoint)
                        {
                                if (!writtenPhotoHeader)
                                {
@@ -428,7 +470,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                                        writtenPhotoHeader = true;
                                }
                                photoNum++;
-                               exportPhotoPoint(point, inWriter, inExportImages, photoNum, absoluteAltitudes);
+                               exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes);
                                numSaved++;
                        }
                }
@@ -437,7 +479,8 @@ public class KmlExporter extends GenericFunction implements Runnable
                {
                        // Set up strings for start and end of track segment
                        String trackStart = "\t<Placemark>\n\t\t<name>track</name>\n\t\t<Style>\n\t\t\t<LineStyle>\n"
-                               + "\t\t\t\t<color>cc0000cc</color>\n\t\t\t\t<width>4</width>\n\t\t\t</LineStyle>\n"
+                               + "\t\t\t\t<color>cc" + reverse(ColourUtils.makeHexCode(_colourPatch.getBackground())) + "</color>\n"
+                               + "\t\t\t\t<width>4</width>\n\t\t\t</LineStyle>\n"
                                + "\t\t\t<PolyStyle><color>33cc0000</color></PolyStyle>\n"
                                + "\t\t</Style>\n\t\t<LineString>\n";
                        if (absoluteAltitudes) {
@@ -456,16 +499,20 @@ public class KmlExporter extends GenericFunction implements Runnable
                        for (i=0; i<numPoints; i++)
                        {
                                point = _track.getPoint(i);
-                               // start new track segment if necessary
-                               if (point.getSegmentStart() && !firstTrackpoint) {
-                                       inWriter.write(trackEnd);
-                                       inWriter.write(trackStart);
-                               }
-                               if (!point.isWaypoint() && point.getPhoto() == null)
+                               boolean writeCurrentPoint = !justSelection || (i>=selStart && i<=selEnd);
+                               if (!point.isWaypoint() && writeCurrentPoint)
                                {
-                                       exportTrackpoint(point, inWriter);
-                                       numSaved++;
-                                       firstTrackpoint = false;
+                                       // start new track segment if necessary
+                                       if (point.getSegmentStart() && !firstTrackpoint) {
+                                               inWriter.write(trackEnd);
+                                               inWriter.write(trackStart);
+                                       }
+                                       if (point.getPhoto() == null)
+                                       {
+                                               exportTrackpoint(point, inWriter);
+                                               numSaved++;
+                                               firstTrackpoint = false;
+                                       }
                                }
                        }
                        // end segment
@@ -475,6 +522,15 @@ public class KmlExporter extends GenericFunction implements Runnable
                return numSaved;
        }
 
+       /**
+        * Reverse the hex code for the colours for KML's stupid backwards format
+        * @param inCode colour code rrggbb
+        * @return kml code bbggrr
+        */
+       private static String reverse(String inCode)
+       {
+               return inCode.substring(4, 6) + inCode.substring(2, 4) + inCode.substring(0, 2);
+       }
 
        /**
         * Export the specified waypoint into the file
@@ -515,12 +571,13 @@ public class KmlExporter extends GenericFunction implements Runnable
         * @param inPoint data point including photo
         * @param inWriter writer object
         * @param inImageLink flag to set whether to export image links or not
+        * @param inPointNumber number of point for accessing dimensions
         * @param inImageNumber number of image for filename
         * @param inAbsoluteAltitude true for absolute altitudes
         * @throws IOException on write failure
         */
        private void exportPhotoPoint(DataPoint inPoint, Writer inWriter, boolean inImageLink,
-               int inImageNumber, boolean inAbsoluteAltitude)
+               int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude)
        throws IOException
        {
                inWriter.write("\t<Placemark>\n\t\t<name>");
@@ -528,13 +585,11 @@ public class KmlExporter extends GenericFunction implements Runnable
                inWriter.write("</name>\n");
                if (inImageLink)
                {
-                       // Work out image dimensions of thumbnail
-                       Dimension picSize = inPoint.getPhoto().getSize();
-                       Dimension thumbSize = ImageUtils.getThumbnailSize(picSize.width, picSize.height, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
+                       Dimension imageSize = _imageDimensions[inPointNumber];
                        // Write out some html for the thumbnail images
                        inWriter.write("<description><![CDATA[<br/><table border='0'><tr><td><center><img src='images/image"
-                               + inImageNumber + ".jpg' width='" + thumbSize.width + "' height='" + thumbSize.height + "'></center></td></tr>"
-                               + "<tr><td><center>Caption for the photo</center></td></tr></table>]]></description>");
+                               + inImageNumber + ".jpg' width='" + imageSize.width + "' height='" + imageSize.height + "'></center></td></tr>"
+                               + "<tr><td><center>" + inPoint.getPhoto().getFile().getName() + "</center></td></tr></table>]]></description>");
                }
                inWriter.write("<styleUrl>#camera_icon</styleUrl>\n");
                inWriter.write("\t\t<Point>\n");
@@ -596,6 +651,14 @@ public class KmlExporter extends GenericFunction implements Runnable
                }
                ImageWriter imageWriter = writers.next();
 
+               // Check selection checkbox
+               boolean justSelection = _pointTypeSelector.getJustSelection();
+               int selStart = -1, selEnd = -1;
+               if (justSelection) {
+                       selStart = _trackInfo.getSelection().getStart();
+                       selEnd = _trackInfo.getSelection().getEnd();
+               }
+
                int numPoints = _track.getNumPoints();
                DataPoint point = null;
                int photoNum = 0;
@@ -603,7 +666,7 @@ public class KmlExporter extends GenericFunction implements Runnable
                for (int i=0; i<numPoints && !_cancelPressed; i++)
                {
                        point = _track.getPoint(i);
-                       if (point.getPhoto() != null)
+                       if (point.getPhoto() != null && (!justSelection || (i>=selStart && i<=selEnd)))
                        {
                                photoNum++;
                                // Make a new entry in zip file
@@ -613,10 +676,10 @@ public class KmlExporter extends GenericFunction implements Runnable
                                ImageIcon icon = new ImageIcon(point.getPhoto().getFile().getAbsolutePath());
 
                                // Scale and smooth image to required size
-                               Dimension outputSize = ImageUtils.getThumbnailSize(
-                                       point.getPhoto().getWidth(), point.getPhoto().getHeight(),
-                                       THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT);
-                               BufferedImage bufferedImage = ImageUtils.createScaledImage(icon.getImage(), outputSize.width, outputSize.height);
+                               BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(),
+                                       THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, 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());
 
                                imageWriter.setOutput(ImageIO.createImageOutputStream(inZipStream));
                                imageWriter.write(bufferedImage);
index 6dcd1eacd3dfc65861d15648f2a69e05aef08f97..91632b49ad0e4c45405938b803dd728a4d7ea867 100644 (file)
@@ -14,12 +14,13 @@ import tim.prune.data.TrackInfo;
 
 /**
  * GUI element to allow the selection of point types for saving,
- * including three checkboxes for track points, waypoints, photo points
+ * including checkboxes for track points, waypoints, photo points
+ * and also a checkbox for the current selection
  */
 public class PointTypeSelector extends JPanel
 {
-       /** Array of three checkboxes */
-       private JCheckBox[] _checkboxes = new JCheckBox[3];
+       /** Array of checkboxes */
+       private JCheckBox[] _checkboxes = new JCheckBox[4];
 
 
        /**
@@ -39,20 +40,21 @@ public class PointTypeSelector extends JPanel
        private void createComponents()
        {
                setLayout(new BorderLayout());
-               // Need JLabel to explain what it is
+               // 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(1, 3, 15, 3));
+               gridPanel.setLayout(new GridLayout(0, 3, 15, 3));
                final String[] keys = {"track", "waypoint", "photo"};
                for (int i=0; i<3; i++)
                {
                        _checkboxes[i] = new JCheckBox(I18nManager.getText("dialog.pointtype." + keys[i]));
-                       _checkboxes[i].setEnabled(true);
                        _checkboxes[i].setSelected(true);
                        gridPanel.add(_checkboxes[i]);
                }
                add(gridPanel, BorderLayout.CENTER);
+               _checkboxes[3] = new JCheckBox(I18nManager.getText("dialog.pointtype.selection"));
+               add(_checkboxes[3], BorderLayout.SOUTH);
        }
 
        /**
@@ -77,6 +79,8 @@ public class PointTypeSelector extends JPanel
                                _checkboxes[i].setEnabled(false);
                        }
                }
+               _checkboxes[3].setEnabled(inTrackInfo.getSelection().hasRangeSelected());
+               _checkboxes[3].setSelected(false);
        }
 
        /**
@@ -103,6 +107,14 @@ public class PointTypeSelector extends JPanel
                return _checkboxes[2].isSelected();
        }
 
+       /**
+        * @return true if only the current selection should be saved
+        */
+       public boolean getJustSelection()
+       {
+               return _checkboxes[3].isSelected();
+       }
+
        /**
         * @return true if at least one type selected
         */
index 654aac6d4c8ed83baadb8ddc099f11527c466596..1b0edfbab07ed76297650040a5fc151e72a5d26d 100644 (file)
@@ -25,10 +25,10 @@ import javax.swing.JTextField;
 import javax.swing.SwingConstants;
 
 import tim.prune.App;
-import tim.prune.Config;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
 import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
 import tim.prune.data.Track;
 import tim.prune.load.GenericFileFilter;
 import tim.prune.threedee.LineDialog;
diff --git a/tim/prune/undo/UndoConnectPhotoWithClone.java b/tim/prune/undo/UndoConnectPhotoWithClone.java
deleted file mode 100644 (file)
index 2fb2ca6..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package tim.prune.undo;
-
-import tim.prune.data.DataPoint;
-import tim.prune.data.TrackInfo;
-
-/**
- * Operation to undo the connection of a photo to a point
- * where the point had to be cloned
- */
-public class UndoConnectPhotoWithClone extends UndoConnectPhoto
-{
-       /** Additional undo object for removing inserted point */
-       private UndoInsert _undoInsert = null;
-
-
-       /**
-        * Constructor
-        * @param inPoint data point
-        * @param inFilename filename of photo
-        * @param inIndex index of cloned point
-        */
-       public UndoConnectPhotoWithClone(DataPoint inPoint, String inFilename, int inIndex)
-       {
-               super(inPoint, inFilename);
-               // Make an undo object for the insert
-               _undoInsert = new UndoInsert(inIndex, 1);
-       }
-
-       /**
-        * 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
-       {
-               //System.out.println("Performing undo: (" + super.getDescription() + ", " + _undoInsert.getDescription() + ")");
-               // Firstly, undo connect
-               super.performUndo(inTrackInfo);
-               // Next, undo insert to remove cloned point
-               _undoInsert.performUndo(inTrackInfo);
-       }
-}
diff --git a/tim/prune/undo/UndoConvertNamesToTimes.java b/tim/prune/undo/UndoConvertNamesToTimes.java
new file mode 100644 (file)
index 0000000..dc0ffbe
--- /dev/null
@@ -0,0 +1,81 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.UpdateMessageBroker;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Field;\r
+import tim.prune.data.Track;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo a conversion from names to times\r
+ */\r
+public class UndoConvertNamesToTimes implements UndoOperation\r
+{\r
+       /** Start and end indices of section */\r
+       private int _startIndex, _endIndex;\r
+       /** Waypoint names before operation */\r
+       private String[] _waypointNames = null;\r
+       /** Timestamp strings before operation */\r
+       private String[] _timestamps = null;\r
+\r
+       /**\r
+        * Constructor\r
+        * @param inTrackInfo track info object to copy values from\r
+        */\r
+       public UndoConvertNamesToTimes(TrackInfo inTrackInfo)\r
+       {\r
+               _startIndex = inTrackInfo.getSelection().getStart();\r
+               _endIndex = inTrackInfo.getSelection().getEnd();\r
+               final int numPoints = _endIndex - _startIndex + 1;\r
+               _waypointNames = new String[numPoints];\r
+               _timestamps = new String[numPoints];\r
+               // Loop over points in selection, and copy names and timestamps\r
+               for (int i=_startIndex; i<=_endIndex; i++)\r
+               {\r
+                       DataPoint point = inTrackInfo.getTrack().getPoint(i);\r
+                       if (point.isWaypoint())\r
+                       {\r
+                               _waypointNames[i-_startIndex] = point.getWaypointName();\r
+                               _timestamps[i-_startIndex] = point.getFieldValue(Field.TIMESTAMP);\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       /**\r
+        * @return description of operation\r
+        */\r
+       public String getDescription()\r
+       {\r
+               return I18nManager.getText("undo.convertnamestotimes");\r
+       }\r
+\r
+\r
+       /**\r
+        * Perform the undo operation on the given Track\r
+        * @param inTrackInfo TrackInfo object on which to perform the operation\r
+        */\r
+       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+       {\r
+               // Sanity check\r
+               Track track = inTrackInfo.getTrack();\r
+               if (track.getNumPoints() <= _endIndex || _endIndex <= _startIndex) {\r
+                       throw new UndoException("Cannot undo conversion, track length doesn't match");\r
+               }\r
+               // Loop over points in selection and replace names and timestamps\r
+               for (int i=_startIndex; i<=_endIndex; i++)\r
+               {\r
+                       String storedName = _waypointNames[i-_startIndex];\r
+                       if (storedName != null)\r
+                       {\r
+                               // point had a name before the operation, so might have been converted\r
+                               DataPoint point = track.getPoint(i);\r
+                               point.setFieldValue(Field.WAYPT_NAME, storedName, true);\r
+                               point.setFieldValue(Field.TIMESTAMP, _timestamps[i-_startIndex], true);\r
+                       }\r
+               }\r
+               track.requestRescale();\r
+               UpdateMessageBroker.informSubscribers();\r
+       }\r
+}\r
index 8f6139260e448e5d5ef8953b5d9f29248af2eceb..4287baba51a50e825abffa7c2df07deb688d4816 100644 (file)
@@ -32,7 +32,6 @@ public class UndoCutAndMove implements UndoOperation
         */
        public UndoCutAndMove(Track inTrack, int inStart, int inEnd, int inMoveTo)
        {
-               // System.out.println("Construct undo with params " + inStart + ", "+  inEnd + ", " + inMoveTo);
                _startIndex = inStart;
                _endIndex = inEnd;
                _moveToIndex = inMoveTo;
index 1ee16dc72ea1ae6baa0732838ee80af2b3ed494a..228795b6d5d5d34e0b15d9bf7e955fc5f3544345 100644 (file)
@@ -49,7 +49,7 @@ public class UndoEditPoint implements UndoOperation
        public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
        {\r
                // Restore contents of point into track\r
-               if (!inTrackInfo.getTrack().editPoint(_originalPoint, _undoFieldList))\r
+               if (!inTrackInfo.getTrack().editPoint(_originalPoint, _undoFieldList, true))\r
                {\r
                        // throw exception if failed\r
                        throw new UndoException(getDescription());\r
index c9aee483868cac1d4f4c20b2e481331f112c18bf..5c0818fc94a65eade07b5d0fd391f482ebbc4469 100644 (file)
@@ -2,6 +2,7 @@ package tim.prune.undo;
 \r
 import tim.prune.I18nManager;\r
 import tim.prune.data.DataPoint;\r
+import tim.prune.data.FileInfo;\r
 import tim.prune.data.PhotoList;\r
 import tim.prune.data.TrackInfo;\r
 \r
@@ -13,8 +14,8 @@ public class UndoLoad implements UndoOperation
        private int _cropIndex = -1;\r
        private int _numLoaded = -1;\r
        private DataPoint[] _contents = null;\r
-       private String _previousFilename = null;\r
        private PhotoList _photoList = null;\r
+       private FileInfo _oldFileInfo = null;\r
 \r
 \r
        /**\r
@@ -27,7 +28,6 @@ public class UndoLoad implements UndoOperation
                _cropIndex = inIndex;\r
                _numLoaded = inNumLoaded;\r
                _contents = null;\r
-               _previousFilename = null;\r
        }\r
 \r
 \r
@@ -42,8 +42,7 @@ public class UndoLoad implements UndoOperation
                _cropIndex = -1;\r
                _numLoaded = inNumLoaded;\r
                _contents = inOldTrackInfo.getTrack().cloneContents();\r
-               if (inOldTrackInfo.getFileInfo().getNumFiles() == 1)\r
-                       _previousFilename = inOldTrackInfo.getFileInfo().getFilename();\r
+               _oldFileInfo = inOldTrackInfo.getFileInfo().clone();\r
                _photoList = inPhotoList;\r
        }\r
 \r
@@ -66,11 +65,12 @@ public class UndoLoad implements UndoOperation
         */\r
        public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
        {\r
-               // remove file from fileinfo\r
-               inTrackInfo.getFileInfo().removeFile();\r
-               if (_previousFilename != null)\r
-               {\r
-                       inTrackInfo.getFileInfo().setFile(_previousFilename);\r
+               // remove source from fileinfo\r
+               if (_oldFileInfo == null) {\r
+                       inTrackInfo.getFileInfo().removeSource();\r
+               }\r
+               else {\r
+                       inTrackInfo.setFileInfo(_oldFileInfo);\r
                }\r
                // Crop / replace\r
                if (_contents == null)\r
diff --git a/tim/prune/undo/UndoRearrangePhotos.java b/tim/prune/undo/UndoRearrangePhotos.java
new file mode 100644 (file)
index 0000000..282611e
--- /dev/null
@@ -0,0 +1,19 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.data.Track;\r
+\r
+/**\r
+ * Operation to undo a photo rearrangement\r
+ */\r
+public class UndoRearrangePhotos extends UndoReorder\r
+{\r
+       /**\r
+        * Constructor\r
+        * @param inTrack track contents to copy\r
+        */\r
+       public UndoRearrangePhotos(Track inTrack)\r
+       {\r
+               super(inTrack, "undo.rearrangephotos");\r
+       }\r
+\r
+}
\ No newline at end of file
index d48dc2a174b72b859f318239b9fb78ff2db3b135..5073bfe0243c55097e2c5027cefe2a94185b0335 100644 (file)
@@ -1,44 +1,19 @@
 package tim.prune.undo;\r
 \r
-import tim.prune.I18nManager;\r
-import tim.prune.data.DataPoint;\r
 import tim.prune.data.Track;\r
-import tim.prune.data.TrackInfo;\r
 \r
 /**\r
  * Operation to undo a waypoint rearrangement\r
  */\r
-public class UndoRearrangeWaypoints implements UndoOperation\r
+public class UndoRearrangeWaypoints extends UndoReorder\r
 {\r
-       private DataPoint[] _contents = null;\r
-\r
-\r
        /**\r
         * Constructor\r
         * @param inTrack track contents to copy\r
         */\r
        public UndoRearrangeWaypoints(Track inTrack)\r
        {\r
-               _contents = inTrack.cloneContents();\r
+               super(inTrack, "undo.rearrangewaypoints");\r
        }\r
 \r
-\r
-       /**\r
-        * @return description of operation\r
-        */\r
-       public String getDescription()\r
-       {\r
-               return I18nManager.getText("undo.rearrangewaypoints");\r
-       }\r
-\r
-\r
-       /**\r
-        * Perform the undo operation on the given Track\r
-        * @param inTrackInfo TrackInfo object on which to perform the operation\r
-        */\r
-       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
-       {\r
-               // restore track to previous values\r
-               inTrackInfo.getTrack().replaceContents(_contents);\r
-       }\r
 }
\ No newline at end of file
diff --git a/tim/prune/undo/UndoReorder.java b/tim/prune/undo/UndoReorder.java
new file mode 100644 (file)
index 0000000..e1d5539
--- /dev/null
@@ -0,0 +1,46 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Track;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Abstract operation to undo a reordering by replacing track contents with a shallow copy\r
+ */\r
+public abstract class UndoReorder implements UndoOperation\r
+{\r
+       /** Shallow copy of whole track contents */\r
+       private DataPoint[] _contents = null;\r
+       /** Description */\r
+       private String _description = null;\r
+\r
+       /**\r
+        * Constructor\r
+        * @param inTrack track contents to copy\r
+        * @param inDescKey description key\r
+        */\r
+       public UndoReorder(Track inTrack, String inDescKey)\r
+       {\r
+               _contents = inTrack.cloneContents();\r
+               _description = I18nManager.getText(inDescKey);\r
+       }\r
+\r
+       /**\r
+        * @return description\r
+        */\r
+       public String getDescription() {\r
+               return _description;\r
+       }\r
+\r
+\r
+       /**\r
+        * Perform the undo operation on the given Track\r
+        * @param inTrackInfo TrackInfo object on which to perform the operation\r
+        */\r
+       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+       {\r
+               // restore track to previous values\r
+               inTrackInfo.getTrack().replaceContents(_contents);\r
+       }\r
+}
\ No newline at end of file
diff --git a/tim/prune/undo/UndoRotatePhoto.java b/tim/prune/undo/UndoRotatePhoto.java
new file mode 100644 (file)
index 0000000..495effa
--- /dev/null
@@ -0,0 +1,48 @@
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.UpdateMessageBroker;\r
+import tim.prune.data.Photo;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo the rotation of a photo\r
+ */\r
+public class UndoRotatePhoto implements UndoOperation\r
+{\r
+       private Photo _photo = null;\r
+       private boolean _rightwards = true;\r
+\r
+\r
+       /**\r
+        * Constructor\r
+        * @param inPhoto photo\r
+        * @param inDir true if original operation was rightwards (clockwise) rotation\r
+        */\r
+       public UndoRotatePhoto(Photo inPhoto, boolean inDir)\r
+       {\r
+               _photo = inPhoto;\r
+               _rightwards = inDir;\r
+       }\r
+\r
+\r
+       /**\r
+        * @return description of operation including photo name\r
+        */\r
+       public String getDescription()\r
+       {\r
+               return I18nManager.getText("undo.rotatephoto") + " " + _photo.getFile().getName();\r
+       }\r
+\r
+\r
+       /**\r
+        * Perform the undo operation on the given Track\r
+        * @param inTrackInfo TrackInfo object on which to perform the operation\r
+        */\r
+       public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+       {\r
+               _photo.rotate(!_rightwards);\r
+               // inform subscribers\r
+               UpdateMessageBroker.informSubscribers();\r
+       }\r
+}
\ No newline at end of file