[Git][debian-gis-team/gpsprune][master] 4 commits: New upstream version 23.1

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Mon Jul 24 04:47:43 BST 2023



Bas Couwenberg pushed to branch master at Debian GIS Project / gpsprune


Commits:
69c32cc7 by Bas Couwenberg at 2023-07-24T05:41:58+02:00
New upstream version 23.1
- - - - -
6bd93b34 by Bas Couwenberg at 2023-07-24T05:41:59+02:00
Update upstream source from tag 'upstream/23.1'

Update to upstream version '23.1'
with Debian dir 37d8b61d551702d35db72b753f6937ce8b6ea257
- - - - -
fcaeec93 by Bas Couwenberg at 2023-07-24T05:43:30+02:00
New upstream release.

- - - - -
ce5a520c by Bas Couwenberg at 2023-07-24T05:44:12+02:00
Set distribution to unstable.

- - - - -


21 changed files:

- build.sh
- debian/changelog
- tim/prune/GpsPrune.java
- tim/prune/cmd/LoadPhotosWithPointsCmd.java
- tim/prune/correlate/AudioCorrelator.java
- tim/prune/correlate/Correlator.java
- tim/prune/correlate/PhotoCorrelator.java
- tim/prune/data/Timestamp.java
- tim/prune/data/TimestampLocal.java
- tim/prune/data/TimestampUtc.java
- tim/prune/function/edit/PointEditor.java
- tim/prune/function/estimate/EstimateTime.java
- tim/prune/function/info/AboutScreen.java
- tim/prune/gui/SidebarController.java
- tim/prune/lang/prune-texts_it.properties
- tim/prune/lang/prune-texts_pt.properties
- tim/prune/load/FileLoader.java
- tim/prune/load/JpegLoader.java
- tim/prune/load/babel/FilterDefinition.java
- tim/prune/readme.txt
- tim/prune/save/GpxWriter.java


Changes:

=====================================
build.sh
=====================================
@@ -1,6 +1,6 @@
 # Build script
 # Version number
-PRUNENAME=gpsprune_23
+PRUNENAME=gpsprune_23.1
 # remove compile directory
 rm -rf compile
 # remove dist directory


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+gpsprune (23.1-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Mon, 24 Jul 2023 05:44:02 +0200
+
 gpsprune (23-1) unstable; urgency=medium
 
   * New upstream release.


=====================================
tim/prune/GpsPrune.java
=====================================
@@ -37,9 +37,9 @@ import tim.prune.gui.profile.ProfileChart;
 public class GpsPrune
 {
 	/** Version number of application, used in about screen and for version check */
-	public static final String VERSION_NUMBER = "23";
+	public static final String VERSION_NUMBER = "23.1";
 	/** Build number, just used for about screen */
-	public static final String BUILD_NUMBER = "401";
+	public static final String BUILD_NUMBER = "406";
 	/** Static reference to App object */
 	private static App APP = null;
 


=====================================
tim/prune/cmd/LoadPhotosWithPointsCmd.java
=====================================
@@ -12,6 +12,10 @@ import java.util.List;
  */
 public class LoadPhotosWithPointsCmd extends CompoundCommand
 {
+	public LoadPhotosWithPointsCmd(Photo inPhoto) {
+		this(List.of(inPhoto));
+	}
+
 	public LoadPhotosWithPointsCmd(List<Photo> inPhotos)
 	{
 		ArrayList<DataPoint> points = new ArrayList<>();


=====================================
tim/prune/correlate/AudioCorrelator.java
=====================================
@@ -3,6 +3,7 @@ package tim.prune.correlate;
 import java.awt.FlowLayout;
 import java.awt.GridLayout;
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
@@ -118,27 +119,27 @@ public class AudioCorrelator extends Correlator
 			PointMediaPair pair = getPointPairForMedia(_app.getTrackInfo().getTrack(), audio, inTimeDiff);
 			MediaPreviewTableRow row = new MediaPreviewTableRow(pair);
 			// Don't try to correlate audios which don't have points either side
-			boolean correlateAudio = pair.isValid();
+			boolean correlate = pair.isValid();
 			// Don't select audios which already have a point
-			if (audio.getCurrentStatus() != AudioClip.Status.NOT_CONNECTED) {correlateAudio = false;}
+			if (audio.getCurrentStatus() != AudioClip.Status.NOT_CONNECTED) {correlate = false;}
 			// Check time limits, distance limits
-			if (timeLimit != null && correlateAudio) {
+			if (timeLimit != null && correlate) {
 				long numSecs = pair.getMinSeconds();
-				correlateAudio = (numSecs <= timeLimit.getTotalSeconds());
+				correlate = (numSecs <= timeLimit.getTotalSeconds());
 			}
-			if (angDistLimit > 0.0 && correlateAudio)
+			if (angDistLimit > 0.0 && correlate)
 			{
 				final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter());
 				double frac = pair.getFraction();
 				if (frac > 0.5) {frac = 1 - frac;}
 				final double angDistPhoto = angDistPair * frac;
-				correlateAudio = (angDistPhoto < angDistLimit);
+				correlate = (angDistPhoto < angDistLimit);
 			}
 			// Don't select audios which are already correlated to the same point
 			if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(audio.getDataPoint())) {
-				correlateAudio = false;
+				correlate = false;
 			}
-			row.setCorrelateFlag(correlateAudio);
+			row.setCorrelateFlag(correlate);
 			model.addRow(row);
 		}
 		_previewTable.setModel(model);
@@ -185,8 +186,8 @@ public class AudioCorrelator extends Correlator
 	}
 
 	/**
-	 * Finish the correlation by modifying the track
-	 * and passing the Undo information back to the App
+	 * Finish the correlation by creating the appropriate command
+	 * and passing it back to the App
 	 */
 	protected void finishCorrelation()
 	{
@@ -197,7 +198,17 @@ public class AudioCorrelator extends Correlator
 
 		ArrayList<DataPoint> pointsToCreate = new ArrayList<>();
 		ArrayList<PointAndMedia> pointAudioPairs = new ArrayList<>();
-		for (PointMediaPair pair : pointPairs)
+		fillListsForCommand(pointPairs, pointsToCreate, pointAudioPairs);
+
+		Command command = new CorrelateMediaCmd(MediaLinkType.LINK_AUDIOS, pointsToCreate, pointAudioPairs);
+		command.setDescription(makeUndoText(pointAudioPairs.size()));
+		command.setConfirmText(makeConfirmText(pointAudioPairs.size()));
+		_app.execute(command);
+	}
+
+	static void fillListsForCommand(PointMediaPair[] inPointPairs, List<DataPoint> inPointsToCreate, List<PointAndMedia> inPointAudioPairs)
+	{
+		for (PointMediaPair pair : inPointPairs)
 		{
 			if (pair != null && pair.isValid())
 			{
@@ -205,21 +216,22 @@ public class AudioCorrelator extends Correlator
 				if (pair.getMinSeconds() == 0L)
 				{
 					// exact match
+					DataPoint point = pair.getPointBefore();
 					AudioClip pointAudio = pair.getPointBefore().getAudio();
-					if (pointAudio == null)
+					if (pointAudio == null && !pointAlreadyBeingConnected(point, inPointAudioPairs))
 					{
 						// audio coincides with audioless point so connect the two
-						DataPoint point = pair.getPointBefore();
-						pointAudioPairs.add(new PointAndMedia(point, null, audioToLink));
+						inPointAudioPairs.add(new PointAndMedia(point, null, audioToLink));
 					}
-					else if (pointAudio.equals(pair.getMedia())) {
+					else if (pointAudio != null && pointAudio.equals(pair.getMedia())) {
 						// audio is already connected, nothing to do
 					}
-					else {
+					else
+					{
 						// point is already connected to a different audio, so need to clone point
 						DataPoint pointToAdd = pair.getPointBefore().clonePoint();
-						pointsToCreate.add(pointToAdd);
-						pointAudioPairs.add(new PointAndMedia(pointToAdd, null, audioToLink));
+						inPointsToCreate.add(pointToAdd);
+						inPointAudioPairs.add(new PointAndMedia(pointToAdd, null, audioToLink));
 					}
 				}
 				else
@@ -227,14 +239,10 @@ public class AudioCorrelator extends Correlator
 					// audio time falls between two points, so need to interpolate new one
 					DataPoint pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
 					pointToAdd.setSegmentStart(true);
-					pointsToCreate.add(pointToAdd);
-					pointAudioPairs.add(new PointAndMedia(pointToAdd, null, audioToLink));
+					inPointsToCreate.add(pointToAdd);
+					inPointAudioPairs.add(new PointAndMedia(pointToAdd, null, audioToLink));
 				}
 			}
 		}
-		Command command = new CorrelateMediaCmd(MediaLinkType.LINK_AUDIOS, pointsToCreate, pointAudioPairs);
-		command.setDescription(makeUndoText(pointAudioPairs.size()));
-		command.setConfirmText(makeConfirmText(pointAudioPairs.size()));
-		_app.execute(command);
 	}
 }


=====================================
tim/prune/correlate/Correlator.java
=====================================
@@ -6,6 +6,7 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.event.ActionListener;
 import java.util.Iterator;
+import java.util.List;
 import java.util.TimeZone;
 import java.util.TreeSet;
 
@@ -26,6 +27,7 @@ import javax.swing.JTextField;
 import tim.prune.App;
 import tim.prune.GenericFunction;
 import tim.prune.I18nManager;
+import tim.prune.cmd.PointAndMedia;
 import tim.prune.config.TimezoneHelper;
 import tim.prune.data.DataPoint;
 import tim.prune.data.Distance;
@@ -691,4 +693,17 @@ public abstract class Correlator extends GenericFunction
 				"confirm.correlate" + getMediaTypeKey() + "s.multi");
 		return confirmDescriber.getDescriptionWithCount(inNumMedia);
 	}
+
+	/** @return true if the given point is present in the list already */
+	protected static boolean pointAlreadyBeingConnected(DataPoint inPoint, List<PointAndMedia> inPairs)
+	{
+		for (PointAndMedia pair : inPairs)
+		{
+			if (pair.getPoint() == inPoint) {
+				// Point is already in the list
+				return true;
+			}
+		}
+		return false;
+	}
 }


=====================================
tim/prune/correlate/PhotoCorrelator.java
=====================================
@@ -1,6 +1,7 @@
 package tim.prune.correlate;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.swing.JOptionPane;
 import javax.swing.JTable;
@@ -66,27 +67,27 @@ public class PhotoCorrelator extends Correlator
 			PointMediaPair pair = getPointPairForMedia(_app.getTrackInfo().getTrack(), photo, inTimeDiff);
 			MediaPreviewTableRow row = new MediaPreviewTableRow(pair);
 			// Don't try to correlate photos which don't have points either side
-			boolean correlatePhoto = pair.isValid();
+			boolean correlate = pair.isValid();
 			// Don't select photos which already have a point
-			if (photo.getCurrentStatus() != Photo.Status.NOT_CONNECTED) {correlatePhoto = false;}
+			if (photo.getCurrentStatus() != Photo.Status.NOT_CONNECTED) {correlate = false;}
 			// Check time limits, distance limits
-			if (timeLimit != null && correlatePhoto) {
+			if (timeLimit != null && correlate) {
 				long numSecs = pair.getMinSeconds();
-				correlatePhoto = (numSecs <= timeLimit.getTotalSeconds());
+				correlate = (numSecs <= timeLimit.getTotalSeconds());
 			}
-			if (angDistLimit > 0.0 && correlatePhoto)
+			if (angDistLimit > 0.0 && correlate)
 			{
 				final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter());
 				double frac = pair.getFraction();
 				if (frac > 0.5) {frac = 1 - frac;}
 				final double angDistPhoto = angDistPair * frac;
-				correlatePhoto = (angDistPhoto < angDistLimit);
+				correlate = (angDistPhoto < angDistLimit);
 			}
 			// Don't select photos which are already correlated to the same point
 			if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(photo.getDataPoint())) {
-				correlatePhoto = false;
+				correlate = false;
 			}
-			row.setCorrelateFlag(correlatePhoto);
+			row.setCorrelateFlag(correlate);
 			model.addRow(row);
 		}
 		_previewTable.setModel(model);
@@ -121,7 +122,21 @@ public class PhotoCorrelator extends Correlator
 
 		ArrayList<DataPoint> pointsToCreate = new ArrayList<>();
 		ArrayList<PointAndMedia> pointPhotoPairs = new ArrayList<>();
-		for (PointMediaPair pair : pointPairs)
+		fillListsForCommand(pointPairs, pointsToCreate, pointPhotoPairs);
+
+		Command command = new CorrelateMediaCmd(MediaLinkType.LINK_PHOTOS, pointsToCreate, pointPhotoPairs);
+		command.setDescription(makeUndoText(pointPhotoPairs.size()));
+		command.setConfirmText(makeConfirmText(pointPhotoPairs.size()));
+		_app.execute(command);
+	}
+
+	/**
+	 * Fill the two lists of objects required by the command.
+	 * This is static so that it can be separately tested.
+	 */
+	static void fillListsForCommand(PointMediaPair[] inPointPairs, List<DataPoint> inPointsToCreate, List<PointAndMedia> inPointPhotoPairs)
+	{
+		for (PointMediaPair pair : inPointPairs)
 		{
 			if (pair != null && pair.isValid())
 			{
@@ -129,22 +144,22 @@ public class PhotoCorrelator extends Correlator
 				if (pair.getMinSeconds() == 0L)
 				{
 					// exact match
-					Photo pointPhoto = pair.getPointBefore().getPhoto();
-					if (pointPhoto == null)
+					DataPoint point = pair.getPointBefore();
+					Photo pointPhoto = point.getPhoto();
+					if (pointPhoto == null && !pointAlreadyBeingConnected(point, inPointPhotoPairs))
 					{
 						// photo coincides with photoless point so connect the two
-						DataPoint point = pair.getPointBefore();
-						pointPhotoPairs.add(new PointAndMedia(point, photoToLink, null));
+						inPointPhotoPairs.add(new PointAndMedia(point, photoToLink, null));
 					}
-					else if (pointPhoto.equals(pair.getMedia())) {
+					else if (pointPhoto != null && pointPhoto.equals(pair.getMedia())) {
 						// photo is already connected, nothing to do
 					}
 					else
 					{
 						// point is already connected to a different photo, so need to clone point
 						DataPoint pointToAdd = pair.getPointBefore().clonePoint();
-						pointsToCreate.add(pointToAdd);
-						pointPhotoPairs.add(new PointAndMedia(pointToAdd, photoToLink, null));
+						inPointsToCreate.add(pointToAdd);
+						inPointPhotoPairs.add(new PointAndMedia(pointToAdd, photoToLink, null));
 					}
 				}
 				else
@@ -152,15 +167,10 @@ public class PhotoCorrelator extends Correlator
 					// photo time falls between two points, so need to interpolate new one
 					DataPoint pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
 					pointToAdd.setSegmentStart(true);
-					pointsToCreate.add(pointToAdd);
-					pointPhotoPairs.add(new PointAndMedia(pointToAdd, photoToLink, null));
+					inPointsToCreate.add(pointToAdd);
+					inPointPhotoPairs.add(new PointAndMedia(pointToAdd, photoToLink, null));
 				}
 			}
 		}
-
-		Command command = new CorrelateMediaCmd(MediaLinkType.LINK_PHOTOS, pointsToCreate, pointPhotoPairs);
-		command.setDescription(makeUndoText(pointPhotoPairs.size()));
-		command.setConfirmText(makeConfirmText(pointPhotoPairs.size()));
-		_app.execute(command);
 	}
 }


=====================================
tim/prune/data/Timestamp.java
=====================================
@@ -4,6 +4,7 @@ package tim.prune.data;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.Locale;
 import java.util.TimeZone;
 
 
@@ -17,10 +18,12 @@ public abstract class Timestamp
 
 	protected static final DateFormat DEFAULT_DATETIME_FORMAT = DateFormat.getDateTimeInstance();
 
+	// These date formats use Locale.US to guarantee that Arabic numerals (0-9) will be used
+	// to format the timestamps, not whatever other numerals are configured on the default system locale.
 	protected static final DateFormat ISO_8601_FORMAT
-		= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+		= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
 	protected static final DateFormat ISO_8601_FORMAT_WITH_MILLIS
-		= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+		= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
 
 	private static boolean _millisAddedToTimeFormat = false;
 
@@ -126,10 +129,8 @@ public abstract class Timestamp
 	/**
 	 * @return date part of timestamp in locale-specific format
 	 */
-	public String getDateText(TimeZone inTimezone)
-	{
-		if (!isValid()) return "";
-		return format(DEFAULT_DATE_FORMAT, inTimezone);
+	public String getDateText(TimeZone inTimezone) {
+		return isValid() ? format(DEFAULT_DATE_FORMAT, inTimezone) : "";
 	}
 
 	/**
@@ -165,8 +166,7 @@ public abstract class Timestamp
 	/**
 	 * @return Description of timestamp in locale-specific format
 	 */
-	public String getText(TimeZone inTimezone)
-	{
+	public String getText(TimeZone inTimezone) {
 		return getText(Format.LOCALE, inTimezone);
 	}
 
@@ -186,8 +186,8 @@ public abstract class Timestamp
 			default:
 				return format(DEFAULT_DATETIME_FORMAT, inTimezone);
 			case ISO8601:
-				return format(hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT,
-					inTimezone);
+				DateFormat dateFormat = hasMilliseconds() ? ISO_8601_FORMAT_WITH_MILLIS : ISO_8601_FORMAT;
+				return format(dateFormat, inTimezone);
 		}
 	}
 


=====================================
tim/prune/data/TimestampLocal.java
=====================================
@@ -13,7 +13,7 @@ import java.util.TimeZone;
  */
 public class TimestampLocal extends Timestamp
 {
-	private boolean _valid = false;
+	private final boolean _valid;
 	private int _year=0, _month=0, _day=0;
 	private int _hour=0, _minute=0, _second=0;
 
@@ -48,8 +48,7 @@ public class TimestampLocal extends Timestamp
 
 
 	/** @return true if valid */
-	public boolean isValid()
-	{
+	public boolean isValid() {
 		return _valid;
 	}
 
@@ -71,8 +70,7 @@ public class TimestampLocal extends Timestamp
 	}
 
 	@Override
-	public long getMilliseconds(TimeZone inZone)
-	{
+	public long getMilliseconds(TimeZone inZone) {
 		return getCalendar(inZone).getTimeInMillis();
 	}
 
@@ -82,8 +80,7 @@ public class TimestampLocal extends Timestamp
 	}
 
 	@Override
-	protected boolean hasMilliseconds()
-	{
+	protected boolean hasMilliseconds() {
 		return false;
 	}
 
@@ -97,8 +94,7 @@ public class TimestampLocal extends Timestamp
 	protected String format(DateFormat inFormat, TimeZone inTimezone)
 	{
 		Calendar cal = getCalendar(inTimezone);
-		if (inTimezone != null)
-		{
+		if (inTimezone != null) {
 			inFormat.setTimeZone(inTimezone);
 		}
 		return inFormat.format(cal.getTime());


=====================================
tim/prune/data/TimestampUtc.java
=====================================
@@ -319,24 +319,21 @@ public class TimestampUtc extends Timestamp
 	/**
 	 * @return true if timestamp is valid
 	 */
-	public boolean isValid()
-	{
+	public boolean isValid() {
 		return _valid;
 	}
 
 	/**
 	 * @return true if the timestamp has non-zero milliseconds
 	 */
-	protected boolean hasMilliseconds()
-	{
+	protected boolean hasMilliseconds() {
 		return isValid() && (_milliseconds % 1000L) > 0;
 	}
 
 	/**
 	 * @return the milliseconds according to the given timezone
 	 */
-	public long getMilliseconds(TimeZone inZone)
-	{
+	public long getMilliseconds(TimeZone inZone) {
 		return _milliseconds;
 	}
 
@@ -376,6 +373,7 @@ public class TimestampUtc extends Timestamp
 	 * @param inTimezone timezone to use
 	 * @return formatted String
 	 */
+	@Override
 	protected String format(DateFormat inFormat, TimeZone inTimezone)
 	{
 		CALENDAR.setTimeZone(TimeZone.getTimeZone("GMT"));


=====================================
tim/prune/function/edit/PointEditor.java
=====================================
@@ -87,12 +87,10 @@ public class PointEditor extends GenericFunction
 		_dialog.getContentPane().add(makeDialogComponents());
 		_dialog.pack();
 		// Init right-hand side
-		SwingUtilities.invokeLater(new Runnable() {
-			public void run() {
-				_valueField.setVisible(false);
-				_valueAreaPane.setVisible(false);
-				_cancelButton.requestFocus();
-			}
+		SwingUtilities.invokeLater(() -> {
+			_valueField.setVisible(false);
+			_valueAreaPane.setVisible(false);
+			_cancelButton.requestFocus();
 		});
 		_prevRowIndex = -1;
 		_dialog.setVisible(true);


=====================================
tim/prune/function/estimate/EstimateTime.java
=====================================
@@ -175,11 +175,7 @@ public class EstimateTime extends GenericFunction
 			I18nManager.getText("dialog.estimatetime.parameters")));
 		KeyAdapter paramChangeListener = new KeyAdapter() {
 			public void keyTyped(KeyEvent inE) {
-				SwingUtilities.invokeLater(new Runnable() {
-					public void run() {
-						calculateEstimatedTime();
-					}
-				});
+				SwingUtilities.invokeLater(() -> calculateEstimatedTime());
 			}
 			public void keyPressed(KeyEvent inE) {
 				if (inE.getKeyCode() == KeyEvent.VK_ESCAPE) {_dialog.dispose();}


=====================================
tim/prune/function/info/AboutScreen.java
=====================================
@@ -10,9 +10,12 @@ import java.awt.GridBagLayout;
 import java.awt.event.KeyEvent;
 import java.awt.event.KeyListener;
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.util.zip.GZIPInputStream;
 
 import javax.swing.BorderFactory;
 import javax.swing.BoxLayout;
@@ -49,16 +52,14 @@ public class AboutScreen extends GenericFunction
 	 * Constructor
 	 * @param inApp app object
 	 */
-	public AboutScreen(App inApp)
-	{
+	public AboutScreen(App inApp) {
 		super(inApp);
 	}
 
 	/**
 	 * Return the name key for this function
 	 */
-	public String getNameKey()
-	{
+	public String getNameKey() {
 		return "function.about";
 	}
 
@@ -216,11 +217,7 @@ public class AboutScreen extends GenericFunction
 		readmePanel.setLayout(new BorderLayout());
 		_aboutTextArea = new JTextArea(I18nManager.getText("details.photo.loading"));
 		// Set readme text in separate thread so that about screen pops up sooner
-		new Thread(new Runnable() {
-			public void run() {
-				_aboutTextArea.setText(getReadmeText());
-			}
-		}).start();
+		new Thread(() -> _aboutTextArea.setText(getReadmeText())).start();
 		_aboutTextArea.setEditable(false);
 		_aboutTextArea.setLineWrap(true); _aboutTextArea.setWrapStyleWord(true);
 		JScrollPane scrollPane = new JScrollPane(_aboutTextArea);
@@ -291,24 +288,53 @@ public class AboutScreen extends GenericFunction
 	 */
 	private String getReadmeText()
 	{
+		// First, try locally-held readme.txt if available (as it normally should be)
 		// Readme file can either be in file system or packed in the same jar as code
+		String errorMessage = null;
 		try (InputStream in = GpsPrune.class.getResourceAsStream("readme.txt");
 			BufferedReader br = new BufferedReader(new InputStreamReader(in)))
 		{
-			StringBuilder builder = new StringBuilder();
-			String strLine;
-			while ((strLine = br.readLine()) != null) {
-				builder.append(strLine).append('\n');
-			}
-			return builder.toString();
+			return readFromReader(br);
 		}
-		catch (IOException | NullPointerException e) {
-			System.err.println("Exception trying to get readme: " + e.getMessage());
+		catch (IOException e) {
+			errorMessage = e.getMessage();
+		}
+		catch (NullPointerException e) {
+			errorMessage = "Local readme file not found";
 		}
 
+		// Locally-held file failed, so try to find gz file installed on system (eg Debian)
+		File gzFile = new File("/usr/share/doc/gpsprune/readme.txt.gz");
+		if (gzFile.exists())
+		{
+			try (InputStream in = new GZIPInputStream(new FileInputStream(gzFile));
+				BufferedReader br = new BufferedReader(new InputStreamReader(in)))
+			{
+				return readFromReader(br);
+			}
+			catch (IOException e) {
+				System.err.println("Exception trying to get readme.gz : " + e.getMessage());
+			}
+		}
+
+		// Only show first error message if couldn't get readme from gz either
+		if (errorMessage != null) {
+			System.err.println("Exception trying to get readme: " + errorMessage);
+		}
 		return I18nManager.getText("error.readme.notfound");
 	}
 
+	/** Read all the lines from the given reader and return the contents as a string */
+	private String readFromReader(BufferedReader inReader) throws IOException
+	{
+		StringBuilder builder = new StringBuilder();
+		String strLine;
+		while ((strLine = inReader.readLine()) != null) {
+			builder.append(strLine).append('\n');
+		}
+		return builder.toString();
+	}
+
 	/**
 	 * Show window
 	 */


=====================================
tim/prune/gui/SidebarController.java
=====================================
@@ -56,14 +56,16 @@ public class SidebarController
 				}
 			}
 			// Hiding of panels has to occur in separate thread to update properly
-			if (visible) SwingUtilities.invokeLater(new Runnable() {
-				public void run() {
-					for (int i=0; i<_components.length; i++) {
+			if (visible)
+			{
+				SwingUtilities.invokeLater(() -> {
+					for (int i=0; i<_components.length; i++)
+					{
 						_splitters[i].setDividerLocation(i==0?0.0:1.0);
 						_splitters[i].invalidate();
 					}
-				}
-			});
+				});
+			}
 		}
 	}
 }


=====================================
tim/prune/lang/prune-texts_it.properties
=====================================
@@ -13,14 +13,12 @@ menu.track=Traccia
 menu.track.undo=Annulla
 menu.track.clearundo=Cancella lista annulla
 menu.track.markrectangle=Segnare i punti nel rettangolo
-function.deletemarked=Cancella punti marcati
 menu.range=Serie
 menu.range.all=Seleziona tutto
 menu.range.none=Deseleziona tutto
 menu.range.start=Imposta inizio serie
 menu.range.end=Imposta fine serie
 menu.range.average=Crea punto medio della selezione
-function.reverserange=Inverti la serie
 menu.range.mergetracksegments=Unisci segmenti traccia
 menu.range.cutandmove=Taglia e muovi la selezione
 menu.point=Punto
@@ -44,6 +42,7 @@ menu.settings=Preferenze
 menu.settings.onlinemode=Carica mappa da internet
 menu.settings.autosave=Salva settaggi con chiusura del programma
 menu.help=Aiuto
+
 # Popup menu for map
 menu.map.zoomin=Zoom +
 menu.map.zoomout=Zoom -
@@ -90,17 +89,19 @@ function.exportimage=Esporta come immagine
 function.editwaypointname=Modifica nome waypoint
 function.togglesegmentflag=Accendere/spegnere il segmento
 function.compress=Comprimi la traccia
+function.deletemarked=Cancella punti marcati
 function.marklifts=Marca seggiovie
 function.deleterange=Cancella la serie
 function.croptrack=Cima la traccia
+function.reverserange=Inverti la serie
 function.interpolate=Interpola i punti
 function.deletebydate=Cancella punti secondo la data
 function.addtimeoffset=Aggiungi uno scarto temporale
 function.addaltitudeoffset=Aggiungi uno scarto di altitudine
-function.deletefieldvalues=Cancella i valori del campo
-function.findwaypoint=Trova waypoint
 function.rearrangewaypoints=Riorganizza waypoint
 function.dedupewaypoints=Cancella i waypoints duplicati
+function.deletefieldvalues=Cancella i valori del campo
+function.findwaypoint=Trova waypoint
 function.pastecoordinates=Aggiungi coordinate
 function.pastecoordinatelist=Inserisci lista di coordinate
 function.enterpluscode=Inserisci pluscode
@@ -126,6 +127,8 @@ function.searchopencachingde=Cerca OpenCaching.de
 function.downloadosm=Scarica dati OSM dell'area
 function.truncatecoords=Tronca le coordinate
 function.duplicatepoint=Duplica punto corrente in coda
+function.projectpoint=Proiettare il punto
+function.projectcircle=Disegnare un cerchio intorno al punto
 function.setcolours=Scegli colori
 function.setdisplaysettings=Opzioni di visualizzazione
 function.setwaypointdisplay=Opzioni per i waypoint
@@ -154,12 +157,11 @@ function.managetilecache=Gestione del cache di tasselli
 function.getweatherforecast=Ottieni previsioni del tempo
 function.setaltitudetolerance=Configura tolleranza di altitudini
 function.selecttimezone=Seleziona fuso orario
-function.projectpoint=Proiettare il punto
-function.projectcircle=Disegnare un cerchio intorno al punto
 
 # Dialogs
 dialog.exit.confirm.title=Esci da GpsPrune
 dialog.exit.unsaveddata.text=Le modifiche non sono state salvate. Sei sicuro di voler uscire?
+dialog.exit.unsavedsettings.text=Si consiglia di eseguire "Salva configurazione" prima di uscire.\nUscire comunque?
 dialog.openappend.title=Aggiungi ai dati esistenti
 dialog.openappend.text=Aggiungi questi dati a quelli gi\u00e0 caricati?
 dialog.deletepoint.title=Cancella Punto
@@ -167,6 +169,7 @@ dialog.deletepoint.deletephoto=Cancella la foto '%s' collegata a questo punto?
 dialog.deletepoint.deleteaudio=Cancella il audio '%s' collegato a questo punto?
 dialog.deletepoint.deletephotoandaudio=Cancella la foto e el audio collegate a questo punto?
 dialog.deletepoints.title=Cancella Punti
+dialog.deletepoints.deletemedia=Ci sono %d elementi collegati a questi punti. Eliminare anche questi elementi?
 dialog.deletephoto.title=Cancella Foto
 dialog.deletephoto.deletepoint=Cancella il punto collegato a questa foto?
 dialog.deletephoto.deletepointandaudio=Cancella il punto e il audio collegate a questa foto?
@@ -198,6 +201,8 @@ dialog.jpegload.loadjpegswithoutcoords=Includi foto senza coordinate
 dialog.jpegload.loadjpegsoutsidearea=Includi foto fuori dall'area corrente
 dialog.jpegload.progress.title=Caricamento foto
 dialog.jpegload.progress=Per favore aspetta, sto cercando le foto
+dialog.loadlinkedmedia.title=Scarica le foto collegate
+dialog.loadlinkedmedia.allowdomain=Consentire le foto da '%s'?
 dialog.gpsload.nogpsbabel=Non ho trovato il programma gpsbabel. Continuo?
 dialog.gpsload.device=Nome del Dispositivo
 dialog.gpsload.format=Formato
@@ -258,8 +263,6 @@ dialog.exportgpx.copysource=Copia xml sorgente
 dialog.exportgpx.encoding=Codifica caratteri
 dialog.exportgpx.encoding.system=Impostazione di diffetto
 dialog.exportgpx.encoding.utf8=UTF-8
-dialog.3d.useterrain=Mostra terreno
-dialog.3d.terraingridsize=Dimensione della griglia
 dialog.exportpov.text=Per favore inserisci i parametri per l'esportazione in POV
 dialog.exportpov.font=Font
 dialog.exportpov.camerax=Camera X
@@ -269,6 +272,8 @@ dialog.exportpov.modelstyle=Stile del modello
 dialog.exportpov.ballsandsticks=Palle e bacchette
 dialog.exportpov.tubesandwalls=Tubi e pareti
 dialog.3d.warningtracksize=Questa traccia ha un elevato numero di punti, e Java3D potrebbe non essere in grado di visualizzarli.\nSei sicuro di voler continuare?
+dialog.3d.useterrain=Mostra terreno
+dialog.3d.terraingridsize=Dimensione della griglia
 dialog.exportpov.baseimage=Immagine di base
 dialog.exportpov.cannotmakebaseimage=Non riesco a scrivere l'immagine di base
 dialog.baseimage.title=Modello immagine di base
@@ -306,6 +311,7 @@ dialog.pointnameedit.name=Nome del waypoint
 dialog.pointnameedit.uppercase=MAIUSCOLE
 dialog.pointnameedit.lowercase=minuscole
 dialog.pointnameedit.titlecase=Iniziali Maiuscole
+dialog.truncatecoords.intro=Selezionare il formato delle coordinate e il numero di cifre
 dialog.truncatecoords.numdigits=Numero di cifre decimali
 dialog.truncatecoords.preview=Anteprima
 dialog.addtimeoffset.add=Scarto in aggiunta
@@ -416,8 +422,8 @@ dialog.correlate.timestamp.end=Fine
 dialog.correlate.audioselect.intro=Seleziona una di queste riprese audio correlate come scarto temporale
 dialog.correlate.select.audioname=Nome ripresa audio
 dialog.correlate.select.audiolater=Ripresa audio successiva
-dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
 dialog.rearrangewaypoints.desc=Seleziona la destinazione e l'ordine dei waypoint
+dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
 dialog.rearrange.tostart=Sposta all'inizio
 dialog.rearrange.toend=Sposta alla fine
 dialog.rearrange.tonearest=Sul punto pi\u00f9 vicino
@@ -542,6 +548,7 @@ dialog.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo
 dialog.deletefieldvalues.nofields=Non ci sono campi da cancellare nell'intervallo corrente
 dialog.displaysettings.linewidth=Tratteggio delle linee per disegnare la traccia (1-4)
 dialog.displaysettings.antialias=Usa antialiasing
+dialog.displaysettings.allowosscaling=Lasciare che l'SO ridimensioni le carte
 dialog.displaysettings.waypointicons=Icone waypoint
 dialog.displaysettings.wpicon.default=Default
 dialog.displaysettings.wpicon.ringpt=Etichetta rotonda
@@ -604,8 +611,11 @@ dialog.markers.halves=Punti a met\u00e0 strada
 dialog.markers.half.distance=Met\u00e0 della distanza
 dialog.markers.half.climb=Met\u00e0 della salita
 dialog.markers.half.descent=Met\u00e0 della discesa
-dialog.projectpoint.bearing=Azimut (gradi da nord)
 dialog.projectpoint.desc=Inserire l'azimut e la distanza per la proiezione
+dialog.projectpoint.bearing=Azimut (gradi da nord)
+dialog.projectcircle.desc=Inserire il raggio del cerchio
+dialog.configuresrtm.intro1=Valori di altitudine possono provenire dai dati a bassa risoluzione senza login,
+dialog.configuresrtm.intro2=sia dai dati ad alta risoluzione con registrazione alla NASA
 dialog.configuresrtm.threesecond=Dati a bassa risoluzione (tre arcsecondi)
 dialog.configuresrtm.threesecond.desc=Dati a bassa risoluzione sono sempre abilitati.
 dialog.configuresrtm.onesecond=Dati ad alta risoluzione (un arcsecondo)
@@ -614,6 +624,7 @@ dialog.configuresrtm.onesecond.desc2=\u00c8 possibile creare un account su https
 dialog.configuresrtm.showregistrationwebsite=Registrarsi ora sul sito della NASA?
 dialog.configuresrtm.userid=Nome utente per NASA Earthdata
 dialog.configuresrtm.password=Password per NASA Earthdata
+dialog.configuresrtm.loginfailed=Il nome utente e la password sono stati rifiutati dal server Earthdata
 
 # 3d window
 dialog.3d.title=Visione GpsPrune in 3D
@@ -663,13 +674,14 @@ confirm.correlateaudios.single=la ripresa audio era correlata
 confirm.correlateaudios.multi=%d riprese audio erano correlate
 confirm.applytimestamps=Le marche temporali sono state applicate
 
-# Tips
+# Tips, shown just once when appropriate
 tip.title=Consiglio
 tip.useamapcache=Usando una cache della mappa (Preferenze -> Salva mappe su disco)\npuoi accelerare la visualizzazione e ridurre il traffico.
 tip.learntimeparams=I risultati saranno pi\u00f9 precisi usando\nTraccia -> Apprendi parametri di stima\ncon le tue tracce.
 tip.downloadsrtm=Puoi accelerare questa funzione salvare i dati nella cache.
 tip.usesrtmfor3d=La traccia non include informazioni sull'altitudine.\nPuoi utilizzare la funzione SRTM per ottenere le altitudini\nper la visione 3D.
 tip.manuallycorrelateone=Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
+tip.nonstandardconfigfilename=A causa del nome del file scelto,\nsar\u00e0 necessario assegnare questo nome a GpsPrune all'avvio\nutilizzando il parametro "--configfile".
 
 # Buttons
 button.ok=OK
@@ -703,6 +715,8 @@ button.addnew=Aggiungi nuovo
 button.delete=Cancella
 button.manage=Gestici
 button.combine=Combina
+button.keepselected=Mantenere quelli selezionati
+button.deleteselected=Eliminare quelli selezionati
 button.apply=Applica
 button.allow=Consentire
 button.block=Declinare
@@ -828,7 +842,7 @@ units.degreesfahrenheit.short=\u00b0F
 logic.and=e
 logic.or=o
 
-# External urls
+# External urls and services
 url.googlemaps=maps.google.it
 wikipedia.lang=it
 openweathermap.lang=it
@@ -900,6 +914,7 @@ error.osmimage.failed=Impossibile caricare le immagini della mappa. Per favore v
 error.language.wrongfile=Il file selezionato non sembra essere un file di lingua per GpsPrune
 error.lookupsrtm.nonefound=Valori di quota non trovati
 error.lookupsrtm.nonerequired=Tutti i punti hanno gi\u00e0 una quota, non c'\u00e8 niente da cercare
+error.srtm.authenticationfailed=L'autenticazione non \u00e8 riuscita. Controllare le impostazioni di SRTM.
 error.showphoto.failed=Caricamento foto fallito
 error.playaudiofailed=Ripresa audio non riprodotta
 error.cache.notthere=Directory del cache di tasselli non trovato


=====================================
tim/prune/lang/prune-texts_pt.properties
=====================================
@@ -13,20 +13,21 @@ menu.track=Rota
 menu.track.undo=Desfazer
 menu.track.clearundo=Limpar lista de desfazer
 menu.track.markrectangle=Marcar pontos no ret\u00e2ngulo
-function.deletemarked=Remover pontos marcados
-function.rearrangewaypoints=Rearrumar pontos
 menu.range=Intervalo
 menu.range.all=Selecionar tudo
 menu.range.none=Desmarcar todas as sele\u00e7\u00f5es
 menu.range.start=Definir in\u00edcio do intervalo
 menu.range.end=Definir fim do intervalo
 menu.range.average=Sele\u00e7\u00e3o m\u00e9dia
-function.reverserange=Reverter intervalo
 menu.range.mergetracksegments=Mesclar trechos da rota
 menu.range.cutandmove=Recortar e mover sele\u00e7\u00e3o
 menu.point=Ponto
 menu.point.editpoint=Editar ponto
 menu.point.deletepoint=Remover ponto
+menu.point.goto=Ir para
+menu.point.goto.highest=o ponto mais alto
+menu.point.goto.lowest=o ponto mais baixo
+menu.point.goto.fastest=o ponto mais r\u00e1pido
 menu.photo=Foto
 menu.photo.saveexif=Salvar para Exif
 menu.audio=\u00c1udio
@@ -41,6 +42,7 @@ menu.settings=Configura\u00e7\u00f5es
 menu.settings.onlinemode=Carregar mapas da Internet
 menu.settings.autosave=Autosalvar configura\u00e7\u00f5es ao sair
 menu.help=Ajuda
+
 # Popup menu for map
 menu.map.zoomin=Ampliar
 menu.map.zoomout=Reduzir
@@ -86,12 +88,16 @@ function.exportpov=Exportar para POV
 function.exportimage=Exportar imagem
 function.editwaypointname=Editar nome do ponto
 function.compress=Comprimir rota
+function.deletemarked=Remover pontos marcados
 function.deleterange=Remover intervalo
 function.croptrack=Cortar rota
+function.reverserange=Reverter intervalo
 function.interpolate=Interpolar pontos
 function.deletebydate=Remover pontos de acordo com a data
 function.addtimeoffset=Adicionar diferen\u00e7a de tempo
 function.addaltitudeoffset=Adicionar diferen\u00e7a de altitude
+function.rearrangewaypoints=Rearrumar pontos
+function.dedupewaypoints=Remover os pontos nomeados duplicados
 function.deletefieldvalues=Remover valores do campo
 function.findwaypoint=Encontrar ponto
 function.pastecoordinates=Inserir novas coordenadas
@@ -110,10 +116,16 @@ function.sewsegments=Reunir segmentos em rota
 function.lookupsrtm=Obter altitudes a partir do SRTM
 function.getwikipedia=Obter artigos da Wikip\u00e9dia das redondezas
 function.searchwikipedianames=Procurar na Wikip\u00e9dia por nome
+function.searchosmpois=Encontrar pontos OSM pr\u00f3ximos
 function.searchopencachingde=Procurar na OpenCaching.de
 function.downloadosm=Baixar dados OSM para a \u00e1rea
+function.truncatecoords=Truncar coordenadas
 function.duplicatepoint=Duplicar ponto
+function.projectpoint=Projetar este ponto
+function.projectcircle=Construir um c\u00edrculo \u00e0 volta deste ponto
 function.setcolours=Definir cores
+function.setdisplaysettings=Op\u00e7\u00f5es de visualiza\u00e7\u00e3o
+function.setwaypointdisplay=Op\u00e7\u00f5es para pontos nomeados
 function.setlanguage=Definir idioma
 function.connecttopoint=Conectar ao ponto
 function.disconnectfrompoint=Desconectar do ponto
@@ -137,6 +149,8 @@ function.saveconfig=Salvar configura\u00e7\u00f5es
 function.diskcache=Salvar mapas para o disco
 function.managetilecache=Gerenciar cache de fundos
 function.getweatherforecast=Baixar a previs\u00e3o do tempo para a \u00e1rea atual
+function.setaltitudetolerance=Toler\u00e2ncia \u00e0 altitude
+function.selecttimezone=Selecionar o fuso hor\u00e1rio
 
 # Dialogs
 dialog.exit.confirm.title=Sair do GpsPrune
@@ -148,7 +162,9 @@ dialog.deletepoint.deletephoto=Remover foto '%s' anexada a este ponto?
 dialog.deletepoints.title=Remover Pontos
 dialog.deletephoto.title=Remover Foto
 dialog.deletephoto.deletepoint=Remover ponto anexado a esta foto?
+dialog.deletephoto.deletepointandaudio=Remover o ponto e o clipe \u00e1udio anexado a esta foto?
 dialog.deleteaudio.deletepoint=Remover ponto anexado a este clipe de \u00e1udio?
+dialog.deleteaudio.deletepointandphoto=Remover o ponto e a foto anexado a este clipe?
 dialog.openoptions.title=Op\u00e7\u00f5es de abertura
 dialog.openoptions.filesnippet=Extrair do arquivo
 dialog.load.table.field=Campo
@@ -283,7 +299,8 @@ dialog.pointnameedit.name=Nome do ponto
 dialog.pointnameedit.uppercase=MAI\u00daSCULAS
 dialog.pointnameedit.lowercase=min\u00fasculas
 dialog.pointnameedit.titlecase=Frase
-dialog.truncatecoords.preview=Pr\u00e9via
+dialog.truncatecoords.numdigits=N\u00famero de d\u00edgitos decimais
+dialog.truncatecoords.preview=Previs\u00e3o
 dialog.addtimeoffset.add=Adicionar tempo
 dialog.addtimeoffset.subtract=Subtrair tempo
 dialog.addtimeoffset.weeks=Semanas
@@ -352,12 +369,13 @@ dialog.addmapsource.maxzoom=N\u00edvel de amplia\u00e7\u00e3o m\u00e1ximo
 dialog.addmapsource.noname=Sem nome
 dialog.pointdownload.description=Descri\u00e7\u00e3o
 dialog.pointdownload.nodescription=Sem descri\u00e7\u00e3o
-dialog.osmpois.column.name=Nome
-dialog.osmpois.column.type=Tipo
-dialog.osmpois.nonefound=N\u00e3o foram encontrados pontos
 dialog.wikipedia.column.name=Nome do artigo
 dialog.wikipedia.column.distance=Dist\u00e2ncia
 dialog.wikipedia.nonefound=Nenhum artigo encontrado
+dialog.osmpois.column.name=Nome
+dialog.osmpois.column.type=Tipo
+dialog.osmpois.nonefound=N\u00e3o foram encontrados pontos
+dialog.geocaching.nonefound=N\u00e3o foram encontradas geocaches
 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 de que deseja continuar?
 dialog.correlate.nouncorrelatedaudios=Existem \u00e1udios n\u00e3o correlacionados.\nVoc\u00ea tem certeza de que deseja continuar?
@@ -413,10 +431,12 @@ dialog.compress.summarylabel=Pontos para remover
 dialog.compress.confirm=%d pontos foram marcados.\nRemover estes pontos marcados agora?
 dialog.compress.confirmnone=nenhum ponto foi marcado
 dialog.deletemarked.nonefound=Nenhum dado dos pontos pode ser removido
+dialog.dedupewaypoints.nonefound=N\u00e3o foram encontrados duplicados
 dialog.pastecoordinates.desc=Insira ou cole as coordenadas aqui
 dialog.pastecoordinates.coords=Coordenadas
 dialog.pastecoordinates.nothingfound=Por favor, verifique as coordenadas novamente
 dialog.pluscode.code=C\u00f3digo
+dialog.pluscode.nothingfound=Verifique o c\u00f3digo e tente novamente
 dialog.help.help=Por favor, veja\n https://gpsprune.activityworkshop.net/\npara mais informa\u00e7\u00f5es e guia do usu\u00e1rio.
 dialog.about.version=Vers\u00e3o
 dialog.about.build=Compila\u00e7\u00e3o
@@ -475,6 +495,9 @@ dialog.colourchooser.red=Vermelho
 dialog.colourchooser.green=Verde
 dialog.colourchooser.blue=Azul
 dialog.colourer.type.none=Nenhum
+dialog.colourer.start=Cor inicial
+dialog.colourer.end=Cor final
+dialog.colourer.maxcolours=N\u00famero m\u00e1ximo de cores
 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 GpsPrune para mudar o idioma.
 dialog.setlanguage.language=Idioma
@@ -500,6 +523,16 @@ dialog.diskcache.deleted=Removidos %d arquivos do cache
 dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual
 dialog.deletefieldvalues.nofields=N\u00e3o existem campos a remover para este intervalo
 dialog.displaysettings.linewidth=Espessura das linhas para desenhar as rotas (1-4)
+dialog.displaysettings.antialias=Utilizar anti-serrilhamento
+dialog.displaysettings.wpicon.ringpt=Marcador circular
+dialog.displaysettings.wpicon.plectrum=Plectro
+dialog.displaysettings.wpicon.ring=Marcador anular
+dialog.displaysettings.wpicon.pin=Tacha
+dialog.displaysettings.wpicon.flag=Bandeira
+dialog.displaysettings.size.small=Pequeno
+dialog.displaysettings.size.medium=M\u00e9dio
+dialog.displaysettings.size.large=Grande
+dialog.waypointsettings.saltvalue=Conjunto de cores
 dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada:
 dialog.searchwikipedianames.search=Procurar por:
 dialog.weather.location=Localiza\u00e7\u00e3o
@@ -524,14 +557,22 @@ dialog.weather.wind=Vento
 dialog.weather.temp=Temp
 dialog.weather.humidity=Umidade
 dialog.weather.creditnotice=Estes dados foram disponibilizados por openweathermap.org. A p\u00e1gina Web possui mais detalhes.
+dialog.deletebydate.nodate=Sem carimbo de hora
 dialog.deletebydate.column.keep=Guardar
 dialog.deletebydate.column.delete=Remover
+dialog.settimezone.selectedzone=Fuso hor\u00e1rio selecionado
 dialog.autoplay.duration=Dura\u00e7\u00e3o (seg)
 dialog.autoplay.usetimestamps=Usar data-hora
 dialog.autoplay.rewind=Rebobinar
 dialog.autoplay.pause=Suspender
 dialog.autoplay.play=Tocar
+dialog.markers.halves=Pontos interm\u00e9dios
+dialog.markers.half.distance=Metade da dist\u00e2ncia
+dialog.markers.half.climb=Metade da subida
+dialog.markers.half.descent=Metade da descida
 dialog.projectpoint.bearing=Azimute (graus de N)
+dialog.configuresrtm.threesecond=Dados de baixa resolu\u00e7\u00e3o (3 segundos de arco)
+dialog.configuresrtm.onesecond=Dados de alta resolu\u00e7\u00e3o (1 arco-segundo)
 
 # 3d window
 dialog.3d.title=Vista 3D do GpsPrune
@@ -553,13 +594,14 @@ confirm.rearrangephotos=Fotos rearrumadas
 confirm.splitsegments=%d divis\u00f5es de segmentos feitas
 confirm.sewsegments=%d reuni\u00f5es de segmentos feitas
 confirm.cutandmove=Sele\u00e7\u00e3o movida
+confirm.pointadded=foi adicionado 1 ponto
 confirm.pointsadded=%d pontos adicionados
-
 confirm.saveexif.ok=Salvo %d arquivos de foto
 confirm.undo.single=opera\u00e7\u00e3o desfeita
 confirm.undo.multi=opera\u00e7\u00f5es desfeitas
 confirm.jpegload.single=1 foto foi adicionada
 confirm.jpegload.multi=%d fotos foram adicionadas
+confirm.loadedmedia=foi adicionado 1 objeto
 confirm.media.connect=m\u00eddia conectada
 confirm.photo.disconnect=foto desconectada
 confirm.audio.disconnect=\u00e1udio desconectado
@@ -573,9 +615,11 @@ confirm.lookupsrtm=Encontrado %d valores de altitude
 confirm.downloadsrtm=%d arquivos baixados para a cache
 confirm.downloadsrtm.1=%d arquivo baixados para a cache
 confirm.deletefieldvalues=Valores do campo removidos
+confirm.audiosloaded.single=foi adicionado 1 \u00e1udio
 confirm.audiosloaded=%d arquivos de \u00e1udio adicionados
 confirm.correlateaudios.single=\u00e1udio foi correlacionado
 confirm.correlateaudios.multi=%d \u00e1udios foram correlacionados
+confirm.applytimestamps=as marcas de tempo foram atribu\u00eddas
 
 # Tips, shown just once when appropriate
 tip.title=Dica
@@ -616,6 +660,9 @@ button.addnew=Adicionar novo
 button.delete=Remover
 button.manage=Gerenciar
 button.combine=Combinar
+button.apply=Aplicar
+button.allow=Permitir
+button.block=Proibir
 
 # File types
 filetype.txt=Arquivos TXT
@@ -697,6 +744,8 @@ fieldname.verticalspeed=Velocidade vertical
 fieldname.description=Descri\u00e7\u00e3o
 fieldname.comment=Coment\u00e1rio
 fieldname.symbol=S\u00edmbolo
+fieldname.photo=Foto
+fieldname.audio=\u00c1udio
 
 # Measurement units
 units.original=Original
@@ -736,7 +785,7 @@ units.degreesfahrenheit.short=\u00b0F
 logic.and=e
 logic.or=ou
 
-# External urls
+# External urls and services
 url.googlemaps=maps.google.com.br
 wikipedia.lang=pt
 openweathermap.lang=pt
@@ -751,7 +800,9 @@ cardinal.w=O
 
 # Undo operations
 undo.loadfile=Carregar dados %s
+undo.loadphoto=Carregar foto
 undo.loadphotos=Carregar %d fotos
+undo.loadaudio=Carregar arquivo de \u00e1udio
 undo.loadaudios=Carregar %d arquivos de \u00e1udio
 undo.editpoint=Editar ponto
 undo.editpoint.withname=Editar ponto '%s'
@@ -768,6 +819,7 @@ undo.createpoint=Criar ponto
 undo.deletefieldvalues=Remover valores do campo '%s'
 undo.correlateaudio=Correlar \u00e1udio
 undo.correlateaudios=Correlar %d \u00e1udios
+undo.applytimestamps=Atribuir os carimbos de hora
 
 # Error messages
 error.save.dialogtitle=Erro ao salvar dados
@@ -783,6 +835,7 @@ error.load.nopoints=Nenhuma informa\u00e7\u00e3o de coordenadas encontrada no ar
 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.load.nopointsintext=N\u00e3o foram encontradas informa\u00e7\u00f5es sobre coordenadas
 error.jpegload.dialogtitle=Erro ao carregar fotos
 error.jpegload.nofilesfound=Nenhum arquivo encontrado
 error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado
@@ -800,7 +853,6 @@ 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 GpsPrune
-
 error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado
 error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar
 error.showphoto.failed=Falha ao carregar foto


=====================================
tim/prune/load/FileLoader.java
=====================================
@@ -7,7 +7,8 @@ import javax.swing.JFileChooser;
 
 import tim.prune.App;
 import tim.prune.I18nManager;
-import tim.prune.cmd.InsertPhotoCmd;
+import tim.prune.cmd.Command;
+import tim.prune.cmd.LoadPhotosWithPointsCmd;
 import tim.prune.config.Config;
 import tim.prune.data.Photo;
 import tim.prune.load.json.JsonFileLoader;
@@ -135,7 +136,7 @@ public class FileLoader
 		else if (fileExtension.equals(".jpg") || fileExtension.equals("jpeg"))
 		{
 			Photo photo = JpegLoader.createPhoto(inFile);
-			InsertPhotoCmd command = new InsertPhotoCmd(photo);
+			Command command = new LoadPhotosWithPointsCmd(photo);
 			command.setDescription(I18nManager.getText("undo.loadphoto", inFile.getName()));
 			command.setConfirmText(I18nManager.getText("confirm.jpegload.single"));
 			_app.execute(command);


=====================================
tim/prune/load/JpegLoader.java
=====================================
@@ -155,8 +155,8 @@ public class JpegLoader
 			Describer confirmDescriber = new Describer("confirm.jpegload.single", "confirm.jpegload.multi");
 			command.setConfirmText(confirmDescriber.getDescriptionWithCount(numPhotos));
 			Describer undoDescriber = new Describer("undo.loadphoto", "undo.loadphotos");
-			String firstAudioName = _photos.get(0).getName();
-			command.setDescription(undoDescriber.getDescriptionWithNameOrCount(firstAudioName, numPhotos));
+			String firstPhotoName = _photos.get(0).getName();
+			command.setDescription(undoDescriber.getDescriptionWithNameOrCount(firstPhotoName, numPhotos));
 			_app.execute(command);
 		}
 	}
@@ -170,16 +170,16 @@ public class JpegLoader
 	 */
 	private void processFileList(File[] inFiles, boolean inFirstDir, boolean inDescend)
 	{
-		if (inFiles == null) return;
+		if (inFiles == null) {
+			return;
+		}
 		// Loop over elements in array
-		for (int i=0; i<inFiles.length && !_cancelled; i++)
+		for (File file : inFiles)
 		{
-			File file = inFiles[i];
-			if (file.exists() && file.canRead())
+			if (!_cancelled && file.exists() && file.canRead())
 			{
 				// Check whether it's a file or a directory
-				if (file.isFile())
-				{
+				if (file.isFile()) {
 					processFile(file);
 				}
 				else if (file.isDirectory() && (inFirstDir || inDescend))
@@ -293,17 +293,16 @@ public class JpegLoader
 				if (file.exists() && file.canRead())
 				{
 					// Store first directory in config for later
-					if (i == 0 && inFirstDir) {
-						File workingDir = file.isDirectory()?file:file.getParentFile();
+					if (i == 0 && inFirstDir)
+					{
+						File workingDir = file.isDirectory() ? file : file.getParentFile();
 						Config.setConfigString(Config.KEY_PHOTO_DIR, workingDir.getAbsolutePath());
 					}
 					// Check whether it's a file or a directory
-					if (file.isFile())
-					{
+					if (file.isFile()) {
 						fileCount++;
 					}
-					else if (file.isDirectory() && (inFirstDir || inDescend))
-					{
+					else if (file.isDirectory() && (inFirstDir || inDescend)) {
 						fileCount += countFileList(file.listFiles(), false, inDescend);
 					}
 				}
@@ -343,14 +342,15 @@ public class JpegLoader
 	 */
 	private static double getCoordinateDoubleValue(double[] inValues, boolean isPositive)
 	{
-		if (inValues == null || inValues.length != 3) return 0.0;
+		if (inValues == null || inValues.length != 3) {
+			return 0.0;
+		}
 		double value = inValues[0]        // degrees
 			+ inValues[1] / 60.0          // minutes
 			+ inValues[2] / 60.0 / 60.0;  // seconds
 		// make sure it's the correct sign
 		value = Math.abs(value);
-		if (!isPositive) value = -value;
-		return value;
+		return isPositive ? value : -value;
 	}
 
 


=====================================
tim/prune/load/babel/FilterDefinition.java
=====================================
@@ -24,13 +24,10 @@ public abstract class FilterDefinition extends JPanel
 	public FilterDefinition(AddFilterDialog inFilterDialog)
 	{
 		_parentDialog = inFilterDialog;
-		_paramChangeListener = new KeyAdapter() {
+		_paramChangeListener = new KeyAdapter()
+		{
 			public void keyTyped(KeyEvent arg0) {
-				SwingUtilities.invokeLater(new Runnable() {
-					public void run() {
-						_parentDialog.filterParamsChanged();
-					}
-				});
+				SwingUtilities.invokeLater(() -> _parentDialog.filterParamsChanged());
 			}
 		};
 	}
@@ -43,8 +40,7 @@ public abstract class FilterDefinition extends JPanel
 	/**
 	 * @return filter definition to pass to gpsbabel
 	 */
-	public String getString()
-	{
+	public String getString() {
 		return "-x " + getFilterName() + getParameters();
 	}
 


=====================================
tim/prune/readme.txt
=====================================
@@ -1,5 +1,5 @@
-GpsPrune version 23
-===================
+GpsPrune version 23.1
+=====================
 
 GpsPrune is an application for viewing, editing and managing coordinate data from GPS systems,
 including format conversion, charting, 3d visualisation, audio and photo correlation, and online resource lookup.
@@ -17,7 +17,7 @@ Running
 =======
 
 To run GpsPrune from the jar file, simply call it from a command prompt or shell:
-   java -jar gpsprune_23.jar
+   java -jar gpsprune_23.1.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,9 +25,18 @@ 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 gpsprune_23.jar --lang=DE
+   java -jar gpsprune_23.1.jar --lang=DE
 
 
+New with version 23.1
+=====================
+The following fixes were made since version 23:
+  - Bugfix for correlating multiple photos to the same point (Issue #80)
+  - Bugfix for exporting waypoint's timestamp from its photo
+  - Bugfix for writing ISO8601 timestamps using US Locale, not current system locale (Issue #79)
+  - Bugfix for loading photo from command line, if it has coordinates
+  - Bugfix for reading readme.txt.gz file on Debian systems
+
 New with version 23
 ===================
 The following fixes and additions were made since version 22:


=====================================
tim/prune/save/GpxWriter.java
=====================================
@@ -256,7 +256,7 @@ public class GpxWriter
 		if (inPoint.hasTimestamp() && _settings.getExportTimestamps())
 		{
 			inWriter.write("\t\t<time>");
-			inWriter.write(inPoint.getTimestamp().getText(Timestamp.Format.ISO8601, null));
+			inWriter.write(getPointTimestamp(inPoint).getText(Timestamp.Format.ISO8601, null));
 			inWriter.write("</time>\n");
 		}
 		// write waypoint name after elevation and time



View it on GitLab: https://salsa.debian.org/debian-gis-team/gpsprune/-/compare/709b7e8559fa5996457c2a831b1febb3d8a474d0...ce5a520cb3004c1a629c1494c9310a8d44b92611

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/gpsprune/-/compare/709b7e8559fa5996457c2a831b1febb3d8a474d0...ce5a520cb3004c1a629c1494c9310a8d44b92611
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20230724/85170d21/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list