import javax.swing.JFrame;
import javax.swing.JOptionPane;
+import tim.prune.config.Config;
import tim.prune.data.Altitude;
-import tim.prune.data.AudioFile;
import tim.prune.data.Checker;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.LatLonRectangle;
-import tim.prune.data.MediaFile;
import tim.prune.data.NumberUtils;
import tim.prune.data.Photo;
import tim.prune.data.PhotoList;
+import tim.prune.data.RecentFile;
import tim.prune.data.SourceInfo;
import tim.prune.data.Track;
import tim.prune.data.TrackInfo;
+import tim.prune.data.SourceInfo.FILE_TYPE;
+import tim.prune.function.AsyncMediaLoader;
+import tim.prune.function.SaveConfig;
import tim.prune.function.SelectTracksFunction;
import tim.prune.function.browser.BrowserLauncher;
import tim.prune.function.browser.UrlGenerator;
import tim.prune.function.edit.FieldEditList;
import tim.prune.function.edit.PointEditor;
-import tim.prune.gui.SidebarController;
import tim.prune.gui.MenuManager;
+import tim.prune.gui.SidebarController;
import tim.prune.gui.UndoManager;
import tim.prune.gui.Viewport;
import tim.prune.load.FileLoader;
import tim.prune.load.JpegLoader;
-import tim.prune.load.MediaHelper;
+import tim.prune.load.MediaLinkInfo;
import tim.prune.load.TrackNameList;
import tim.prune.save.ExifSaver;
import tim.prune.save.FileSaver;
private Viewport _viewport = null;
private ArrayList<File> _dataFiles = null;
private boolean _firstDataFile = true;
+ private boolean _busyLoading = false;
/**
JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
== JOptionPane.YES_OPTION)
{
+ // save settings
+ if (Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS)) {
+ new SaveConfig(this).silentSave();
+ }
System.exit(0);
}
}
{
// Confirm deletion of photo or decoupling
int response = JOptionPane.showConfirmDialog(_frame,
- I18nManager.getText("dialog.deletepoint.deletephoto") + " " + currentPhoto.getFile().getName(),
+ I18nManager.getText("dialog.deletepoint.deletephoto") + " " + currentPhoto.getName(),
I18nManager.getText("dialog.deletepoint.title"),
JOptionPane.YES_NO_CANCEL_OPTION);
if (response == JOptionPane.CANCEL_OPTION || response == JOptionPane.CLOSED_OPTION)
// decouple photo from point
currentPhoto.setDataPoint(null);
}
+ UpdateMessageBroker.informSubscribers();
}
// Confirm
UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.deletepoint.single"));
*/
public void deleteSelectedRange()
{
- if (_track != null)
+ if (_track == null) return;
+ // Find out if photos should be deleted or not
+ int selStart = _trackInfo.getSelection().getStart();
+ int selEnd = _trackInfo.getSelection().getEnd();
+ if (selStart >= 0 && selEnd >= selStart)
{
- // Find out if photos should be deleted or not
- int selStart = _trackInfo.getSelection().getStart();
- int selEnd = _trackInfo.getSelection().getEnd();
- if (selStart >= 0 && selEnd >= selStart)
+ int numToDelete = selEnd - selStart + 1;
+ boolean[] deletePhotos = new boolean[numToDelete];
+ Photo[] photosToDelete = new Photo[numToDelete];
+ boolean deleteAll = false;
+ boolean deleteNone = false;
+ String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
+ I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"),
+ I18nManager.getText("button.cancel")};
+ DataPoint point = null;
+ for (int i=0; i<numToDelete; i++)
{
- int numToDelete = selEnd - selStart + 1;
- boolean[] deletePhotos = new boolean[numToDelete];
- Photo[] photosToDelete = new Photo[numToDelete];
- boolean deleteAll = false;
- boolean deleteNone = false;
- String[] questionOptions = {I18nManager.getText("button.yes"), I18nManager.getText("button.no"),
- I18nManager.getText("button.yestoall"), I18nManager.getText("button.notoall"),
- I18nManager.getText("button.cancel")};
- DataPoint point = null;
- for (int i=0; i<numToDelete; i++)
+ point = _trackInfo.getTrack().getPoint(i + selStart);
+ if (point != null && point.getPhoto() != null)
{
- point = _trackInfo.getTrack().getPoint(i + selStart);
- if (point != null && point.getPhoto() != null)
+ if (deleteAll)
+ {
+ deletePhotos[i] = true;
+ photosToDelete[i] = point.getPhoto();
+ }
+ else if (deleteNone) {deletePhotos[i] = false;}
+ else
{
- if (deleteAll)
+ int response = JOptionPane.showOptionDialog(_frame,
+ I18nManager.getText("dialog.deletepoint.deletephoto") + " " + point.getPhoto().getName(),
+ I18nManager.getText("dialog.deletepoint.title"),
+ JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
+ questionOptions, questionOptions[1]);
+ // check for cancel or close
+ if (response == 4 || response == -1) {return;}
+ // check for yes or yes to all
+ if (response == 0 || response == 2)
{
deletePhotos[i] = true;
photosToDelete[i] = point.getPhoto();
+ if (response == 2) {deleteAll = true;}
}
- else if (deleteNone) {deletePhotos[i] = false;}
- else
- {
- int response = JOptionPane.showOptionDialog(_frame,
- I18nManager.getText("dialog.deletepoint.deletephoto") + " " + point.getPhoto().getFile().getName(),
- I18nManager.getText("dialog.deletepoint.title"),
- JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
- questionOptions, questionOptions[1]);
- // check for cancel or close
- if (response == 4 || response == -1) {return;}
- // check for yes or yes to all
- if (response == 0 || response == 2)
- {
- deletePhotos[i] = true;
- photosToDelete[i] = point.getPhoto();
- if (response == 2) {deleteAll = true;}
- }
- // check for no to all
- if (response == 3) {deleteNone = true;}
- }
+ // check for no to all
+ if (response == 3) {deleteNone = true;}
}
}
- // add information to undo stack
- UndoDeleteRange undo = new UndoDeleteRange(_trackInfo);
- // delete requested photos
- for (int i=0; i<numToDelete; i++)
+ }
+ // add information to undo stack
+ UndoDeleteRange undo = new UndoDeleteRange(_trackInfo);
+ // delete requested photos
+ for (int i=0; i<numToDelete; i++)
+ {
+ point = _trackInfo.getTrack().getPoint(i + selStart);
+ if (point != null && point.getPhoto() != null)
{
- point = _trackInfo.getTrack().getPoint(i + selStart);
- if (point != null && point.getPhoto() != null)
+ if (deletePhotos[i])
{
- if (deletePhotos[i])
- {
- // delete photo from list
- _trackInfo.getPhotoList().deletePhoto(_trackInfo.getPhotoList().getPhotoIndex(point.getPhoto()));
- }
- else
- {
- // decouple from point
- point.getPhoto().setDataPoint(null);
- }
+ // delete photo from list
+ _trackInfo.getPhotoList().deletePhoto(_trackInfo.getPhotoList().getPhotoIndex(point.getPhoto()));
+ }
+ else
+ {
+ // decouple from point
+ point.getPhoto().setDataPoint(null);
}
}
- // call track to delete range
- if (_trackInfo.deleteRange())
- {
- _undoStack.push(undo);
- // Confirm
- UpdateMessageBroker.informSubscribers("" + numToDelete + " "
- + I18nManager.getText("confirm.deletepoint.multi"));
- }
+ }
+ // call track to delete range
+ if (_trackInfo.deleteRange())
+ {
+ _undoStack.push(undo);
+ // Confirm
+ UpdateMessageBroker.informSubscribers("" + numToDelete + " "
+ + I18nManager.getText("confirm.deletepoint.multi"));
}
}
}
* @param inAltFormat altitude format
* @param inSourceInfo information about the source of the data
* @param inTrackNameList information about the track names
- * @param inLinkArray array of links to photo/audio files
+ * @param inLinkInfo links to photo/audio clips
*/
public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
Altitude.Format inAltFormat, SourceInfo inSourceInfo,
- TrackNameList inTrackNameList, String[] inLinkArray)
+ TrackNameList inTrackNameList, MediaLinkInfo inLinkInfo)
{
// Check whether loaded array can be properly parsed into a Track
Track loadedTrack = new Track();
JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"),
I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE);
}
- // Attach photos and/or audio files to points
- if (inLinkArray != null)
+
+ _busyLoading = true;
+ // Attach photos and/or audio clips to points
+ if (inLinkInfo != null)
{
- for (int i=0; i<inLinkArray.length; i++)
- {
- if (inLinkArray[i] != null)
- {
- MediaFile mf = MediaHelper.createMediaFile(inLinkArray[i]);
- if (mf != null) {
- loadedTrack.getPoint(i).attachMedia(mf);
- mf.setOriginalStatus(MediaFile.Status.TAGGED);
- mf.setCurrentStatus(MediaFile.Status.TAGGED);
- }
- }
+ String[] linkArray = inLinkInfo.getLinkArray();
+ if (linkArray != null) {
+ new AsyncMediaLoader(this, inLinkInfo.getZipFile(), linkArray, loadedTrack).begin();
}
}
// Look at TrackNameList, decide whether to filter or not
if (inTrackNameList != null && inTrackNameList.getNumTracks() > 1)
{
// Launch a dialog to let the user choose which tracks to load, then continue
- new SelectTracksFunction(this, inFieldArray, inDataArray, inAltFormat, inSourceInfo,
- inTrackNameList).begin();
+ new SelectTracksFunction(this, loadedTrack, inSourceInfo, inTrackNameList).begin();
}
else {
// go directly to load
undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
_undoStack.add(undo);
_track.combine(inLoadedTrack);
- // Add photos and audios (if any in loaded track) to list(s)
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
// set source information
inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
_trackInfo.getFileInfo().addSource(inSourceInfo);
_trackInfo.getFileInfo().replaceSource(inSourceInfo);
_trackInfo.getPhotoList().removeCorrelatedPhotos();
_trackInfo.getAudioList().removeCorrelatedAudios();
- // Add photos and audios (if any in loaded track) to list(s)
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
}
}
else
_track.load(inLoadedTrack);
inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
_trackInfo.getFileInfo().addSource(inSourceInfo);
- // Add photos and audios (if any in loaded track) to list(s)
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
- MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
}
+ // Update config before subscribers are told
+ boolean isRegularLoad = (inSourceInfo.getFileType() != FILE_TYPE.GPSBABEL);
+ Config.getRecentFileList().addFile(new RecentFile(inSourceInfo.getFile(), isRegularLoad));
UpdateMessageBroker.informSubscribers();
// Update status bar
UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.loadfile")
+ " '" + inSourceInfo.getName() + "'");
// update menu
_menuManager.informFileLoaded();
+ // Remove busy lock
+ _busyLoading = false;
// load next file if there's a queue
loadNextFile();
}
{
_sidebarController.toggle();
}
+
+ /** @return true if App is currently busy with loading data */
+ public boolean isBusyLoading() {
+ return _busyLoading;
+ }
}
import tim.prune.function.gpsies.UploadGpsiesFunction;
import tim.prune.function.srtm.LookupSrtmFunction;
import tim.prune.load.AudioLoader;
-import tim.prune.load.GpsLoader;
+import tim.prune.load.BabelLoadFromFile;
+import tim.prune.load.BabelLoadFromGps;
import tim.prune.save.GpsSaver;
import tim.prune.save.GpxExporter;
import tim.prune.save.KmlExporter;
public static SvgExporter FUNCTION_SVGEXPORT = null;
public static GenericFunction FUNCTION_GPSLOAD = null;
public static GenericFunction FUNCTION_GPSSAVE = null;
+ public static GenericFunction FUNCTION_IMPORTBABEL = null;
public static GenericFunction FUNCTION_SAVECONFIG = null;
public static GenericFunction FUNCTION_EDIT_WAYPOINT_NAME = null;
public static RearrangeWaypointsFunction FUNCTION_REARRANGE_WAYPOINTS = null;
FUNCTION_KMLEXPORT = new KmlExporter(inApp);
FUNCTION_POVEXPORT = new PovExporter(inApp);
FUNCTION_SVGEXPORT = new SvgExporter(inApp);
- FUNCTION_GPSLOAD = new GpsLoader(inApp);
+ FUNCTION_GPSLOAD = new BabelLoadFromGps(inApp);
FUNCTION_GPSSAVE = new GpsSaver(inApp);
+ FUNCTION_IMPORTBABEL = new BabelLoadFromFile(inApp);
FUNCTION_SAVECONFIG = new SaveConfig(inApp);
FUNCTION_EDIT_WAYPOINT_NAME = new PointNameEditor(inApp);
FUNCTION_REARRANGE_WAYPOINTS = new RearrangeWaypointsFunction(inApp);
import tim.prune.gui.profile.ProfileChart;
/**
- * Prune is a tool to visualize, edit, convert and prune GPS data
+ * GpsPrune 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 2006-2010 and made available through the Gnu GPL version 2.
+ * This software is copyright activityworkshop.net 2006-2011 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
+ * GpsPrune is the main entry point to the application, including initialisation and launch
*/
-public class GpsPruner
+public class GpsPrune
{
/** Version number of application, used in about screen and for version check */
- public static final String VERSION_NUMBER = "12.1";
+ public static final String VERSION_NUMBER = "13";
/** Build number, just used for about screen */
- public static final String BUILD_NUMBER = "224";
+ public static final String BUILD_NUMBER = "240";
/** Static reference to App object */
private static App APP = null;
/** Program name, used for Frame title and for Macs also on the system bar */
- private static final String PROGRAM_NAME = "Prune";
+ private static final String PROGRAM_NAME = "GpsPrune";
/**
UpdateMessageBroker.addSubscriber(profileDisp);
StatusBar statusBar = new StatusBar();
UpdateMessageBroker.addSubscriber(statusBar);
- UpdateMessageBroker.informSubscribers("Prune v" + VERSION_NUMBER);
+ UpdateMessageBroker.informSubscribers("GpsPrune v" + VERSION_NUMBER);
// Arrange in the frame using split panes
JSplitPane midSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mapDisp, profileDisp);
frame.setVisible(true);
// Set position of map/profile splitter
midSplit.setDividerLocation(0.75);
+ // Update menu (only needed for recent file list)
+ UpdateMessageBroker.informSubscribers();
// Make a full screen toggler
SidebarController fsc = new SidebarController(new Component[] {leftPanel, profileDisp, rightPanel},
import java.awt.Color;
/**
- * Class to hold a colour scheme for Prune, including
+ * Class to hold a colour scheme for GpsPrune, including
* colours for background, points, selections and texts
*/
public class ColourScheme
import java.io.FileInputStream;
import java.util.Properties;
+import tim.prune.data.RecentFileList;
+
/**
* Abstract class to hold application-wide configuration
private static Properties _configValues = new Properties();
/** Colour scheme object is also part of config */
private static ColourScheme _colourScheme = new ColourScheme();
+ /** Recently-used file list */
+ private static RecentFileList _recentFiles = new RecentFileList();
/** Default config file */
- private static final File DEFAULT_CONFIG_FILE = new File(".pruneconfig");
+ public static final File DEFAULT_CONFIG_FILE = new File(".pruneconfig");
+ public static final File HOME_CONFIG_FILE = new File(System.getProperty("user.home"), ".pruneconfig");
/** Key for track directory */
public static final String KEY_TRACK_DIR = "prune.trackdirectory";
public static final String KEY_LINE_WIDTH = "prune.linewidth";
/** Key for kml track colour */
public static final String KEY_KML_TRACK_COLOUR = "prune.kmltrackcolour";
+ /** Key for autosaving settings */
+ public static final String KEY_AUTOSAVE_SETTINGS = "prune.autosavesettings";
+ /** Key for recently used files */
+ public static final String KEY_RECENT_FILES = "prune.recentfiles";
/**
*/
public static void loadDefaultFile()
{
- try
+ if (DEFAULT_CONFIG_FILE.exists())
+ {
+ try {
+ loadFile(DEFAULT_CONFIG_FILE);
+ return;
+ }
+ catch (ConfigException ce) {} // ignore
+ }
+ if (HOME_CONFIG_FILE.exists())
{
- loadFile(DEFAULT_CONFIG_FILE);
+ try {
+ loadFile(HOME_CONFIG_FILE);
+ }
+ catch (ConfigException ce) {} // ignore
}
- catch (ConfigException ce) {} // ignore
}
// Save all properties from file
_configValues.putAll(props);
_colourScheme.loadFromHex(_configValues.getProperty(KEY_COLOUR_SCHEME));
+ _recentFiles = new RecentFileList(_configValues.getProperty(KEY_RECENT_FILES));
if (loadFailed) {
throw new ConfigException();
}
props.put(KEY_GPSBABEL_PATH, "gpsbabel");
props.put(KEY_KMZ_IMAGE_WIDTH, "240");
props.put(KEY_KMZ_IMAGE_HEIGHT, "240");
+ props.put(KEY_AUTOSAVE_SETTINGS, "0"); // autosave false by default
return props;
}
*/
public static Properties getAllConfig()
{
+ // Update recently-used files
+ _configValues.setProperty(KEY_RECENT_FILES, _recentFiles.getConfigString());
return _configValues;
}
return _colourScheme;
}
+ /**
+ * @return list of recently used files
+ */
+ public static RecentFileList getRecentFileList()
+ {
+ return _recentFiles;
+ }
+
/**
* Store the given configuration setting
* @param inKey key (from constants)
-The source code of Prune is copyright 2006-2010 activityworkshop.net
+The source code of GpsPrune is copyright 2006-2011 activityworkshop.net
and is distributed under the terms of the Gnu GPL version 2.
Portions of the package jpeg.drew (if included in this package) were taken
import tim.prune.DataSubscriber;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.AudioList;
import tim.prune.data.DataPoint;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
import tim.prune.data.MediaList;
import tim.prune.data.TimeDifference;
import tim.prune.data.Timestamp;
import tim.prune.undo.UndoCorrelateAudios;
/**
- * Class to manage the automatic correlation of audio files to points
+ * Class to manage the automatic correlation of audio clips to points
* which is very similar to the PhotoCorrelator apart from the clip lengths
*/
public class AudioCorrelator extends Correlator
{
for (int i=0; i<inAudios.getNumMedia(); i++)
{
- AudioFile a = inAudios.getAudio(i);
+ AudioClip a = inAudios.getAudio(i);
if (a.getLengthInSeconds() > 0) {return true;}
}
return false;
int numAudios = audios.getNumAudios();
for (int i=0; i<numAudios; i++)
{
- AudioFile audio = audios.getAudio(i);
+ AudioClip audio = audios.getAudio(i);
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();
// Don't select audios which already have a point
- if (audio.getCurrentStatus() != AudioFile.Status.NOT_CONNECTED) {correlateAudio = false;}
+ if (audio.getCurrentStatus() != AudioClip.Status.NOT_CONNECTED) {correlateAudio = false;}
// Check time limits, distance limits
if (timeLimit != null && correlateAudio) {
long numSecs = pair.getMinSeconds();
/**
* @return modified timestamp of specified media object
*/
- protected Timestamp getMediaTimestamp(MediaFile inMedia)
+ protected Timestamp getMediaTimestamp(MediaObject inMedia)
{
Timestamp tstamp = super.getMediaTimestamp(inMedia);
try {
- AudioFile audio = (AudioFile) inMedia;
+ AudioClip audio = (AudioClip) inMedia;
int audioLength = audio.getLengthInSeconds();
// Each option is worth half the length of the audio clip, so need to divide by 2
int secsToAdd = audioLength *
if (pair.getMinSeconds() == 0L)
{
// exact match
- AudioFile pointAudio = pair.getPointBefore().getAudio();
+ AudioClip pointAudio = pair.getPointBefore().getAudio();
if (pointAudio == null)
{
// photo coincides with audioless point so connect the two
- pair.getPointBefore().setAudio((AudioFile) pair.getMedia());
+ pair.getPointBefore().setAudio((AudioClip) pair.getMedia());
pair.getMedia().setDataPoint(pair.getPointBefore());
}
else if (pointAudio.equals(pair.getMedia())) {
if (pointToAdd != null)
{
// link audio to point
- pointToAdd.setAudio((AudioFile) pair.getMedia());
+ pointToAdd.setAudio((AudioClip) pair.getMedia());
pair.getMedia().setDataPoint(pointToAdd);
// set to start of segment so not joined in track
pointToAdd.setSegmentStart(true);
/**
* GUI element to allow the selection of timestamp options
- * for audio file correlation
+ * for audio clip correlation
*/
public class AudioTimestampSelector extends JPanel
{
import tim.prune.data.DataPoint;
import tim.prune.data.Distance;
import tim.prune.data.Field;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
import tim.prune.data.MediaList;
import tim.prune.data.TimeDifference;
import tim.prune.data.Timestamp;
int numMedia = mediaList.getNumMedia();
for (int i=0; i<numMedia; i++)
{
- MediaFile media = mediaList.getMedia(i);
+ MediaObject media = mediaList.getMedia(i);
// For working out time differences, can't use media which already had point information
if (media.getDataPoint() != null && media.getDataPoint().hasTimestamp()
- && media.getOriginalStatus() == MediaFile.Status.NOT_CONNECTED)
+ && media.getOriginalStatus() == MediaObject.Status.NOT_CONNECTED)
{
// Calculate time difference, add to table model
long timeDiff = getMediaTimestamp(media).getSecondsSince(media.getDataPoint().getTimestamp());
* @param inTimeDiff time difference to use for time offsets
* @param inFirstMedia first media object to use for calculating timezone
*/
- protected void setupPreviewCard(TimeDifference inTimeDiff, MediaFile inFirstMedia)
+ protected void setupPreviewCard(TimeDifference inTimeDiff, MediaObject inFirstMedia)
{
_previewEnabled = false;
TimeDifference timeDiff = inTimeDiff;
* @param inMedia media object
* @return normally just returns the media timestamp, overridden by audio correlator
*/
- protected Timestamp getMediaTimestamp(MediaFile inMedia)
+ protected Timestamp getMediaTimestamp(MediaObject inMedia)
{
return inMedia.getTimestamp();
}
* @param inOffset time offset to apply
* @return point pair resulting from correlation
*/
- protected PointMediaPair getPointPairForMedia(Track inTrack, MediaFile inMedia, TimeDifference inOffset)
+ protected PointMediaPair getPointPairForMedia(Track inTrack, MediaObject inMedia, TimeDifference inOffset)
{
PointMediaPair pair = new PointMediaPair(inMedia);
// Add/subtract offset to media timestamp
public Object getValueAt(int inRowIndex, int inColumnIndex)
{
MediaPreviewTableRow row = _list.get(inRowIndex);
- if (inColumnIndex == 0) return row.getMedia().getFile().getName();
+ if (inColumnIndex == 0) return row.getMedia().getName();
else if (inColumnIndex == 1) {
return row.getMedia().getTimestamp().getText();
}
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
import tim.prune.I18nManager;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
/**
{
// MAYBE: only show time of photos (not date) if dates all identical
MediaSelectionTableRow row = _list.get(inRowIndex);
- if (inColumnIndex == 0) return row.getMedia().getFile().getName();
+ if (inColumnIndex == 0) return row.getMedia().getName();
else if (inColumnIndex == 1) return row.getMedia().getTimestamp().getText();
else if (inColumnIndex == 2) return row.getTimeDiff().getDescription();
return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") :
* @param inMedia item to add
* @param inTimeDiff time difference
*/
- public void addMedia(MediaFile inMedia, long inTimeDiff)
+ public void addMedia(MediaObject inMedia, long inTimeDiff)
{
_list.add(new MediaSelectionTableRow(inMedia, inTimeDiff));
}
package tim.prune.correlate;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
import tim.prune.data.TimeDifference;
/**
*/
public class MediaSelectionTableRow
{
- private MediaFile _media = null;
+ private MediaObject _media = null;
private TimeDifference _timeDiff = null;
/**
* @param inMedia media item
* @param inNumSecs number of seconds time difference as long
*/
- public MediaSelectionTableRow(MediaFile inMedia, long inNumSecs)
+ public MediaSelectionTableRow(MediaObject inMedia, long inNumSecs)
{
_media = inMedia;
_timeDiff = new TimeDifference(inNumSecs);
/**
* @return Media object
*/
- public MediaFile getMedia() {
+ public MediaObject getMedia() {
return _media;
}
package tim.prune.correlate;
import tim.prune.data.DataPoint;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
/**
* Class to hold a pair of points used to hold the result of correlation
*/
public class PointMediaPair
{
- private MediaFile _media = null;
+ private MediaObject _media = null;
private DataPoint _pointBefore = null;
private DataPoint _pointAfter = null;
private long _secondsBefore = 1L;
* Constructor
* @param inMedia media object
*/
- public PointMediaPair(MediaFile inMedia) {
+ public PointMediaPair(MediaObject inMedia) {
_media = inMedia;
}
/**
* @return Media object
*/
- public MediaFile getMedia() {
+ public MediaObject getMedia() {
return _media;
}
package tim.prune.data;
+import java.io.ByteArrayInputStream;
import java.io.File;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
/**
- * Class to represent an audio file for correlation
+ * Class to represent an audio clip for correlation
*/
-public class AudioFile extends MediaFile
+public class AudioClip extends MediaObject
{
- /** length of current audio file in seconds */
+ /** length of current audio clip in seconds */
private int _lengthInSeconds = LENGTH_UNKNOWN;
private static final int LENGTH_UNKNOWN = -1;
* Constructor
* @param inFile file object
*/
- public AudioFile(File inFile)
+ public AudioClip(File inFile)
{
// Timestamp is always just taken from the file modification stamp
super(inFile, new Timestamp(inFile.lastModified()));
}
/**
- * @return length of this audio file in seconds
+ * Constructor
+ * @param inData byte array of data
+ * @param inName name of source file
+ * @param inUrl url from which it came (or null)
+ */
+ public AudioClip(byte[] inData, String inName, String inUrl)
+ {
+ super(inData, inName, inUrl);
+ }
+
+ /**
+ * @return length of this audio clip in seconds
*/
public int getLengthInSeconds()
{
if (_lengthInSeconds == LENGTH_UNKNOWN)
{
try {
- AudioFileFormat format = AudioSystem.getAudioFileFormat(getFile());
+ AudioFileFormat format = null;
+ if (getFile() != null)
+ format = AudioSystem.getAudioFileFormat(getFile());
+ else
+ format = AudioSystem.getAudioFileFormat(new ByteArrayInputStream(_data));
_lengthInSeconds = (int) (format.getFrameLength() / format.getFormat().getFrameRate());
}
catch (Exception e) {
import java.util.ArrayList;
/**
- * Class to hold a list of audio files, using the MediaList superclass
+ * Class to hold a list of audio clips, using the MediaList superclass
*/
public class AudioList extends MediaList
{
/**
* Constructor
- * @param inList ArrayList containing audio file objects
+ * @param inList ArrayList containing audio clip objects
*/
- private AudioList(ArrayList<MediaFile> inList) {
+ private AudioList(ArrayList<MediaObject> inList) {
super(inList);
}
public AudioList cloneList()
{
if (getNumMedia() == 0) return this;
- ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ ArrayList<MediaObject> listCopy = new ArrayList<MediaObject>();
listCopy.addAll(_media);
return new AudioList(listCopy);
}
/**
- * @return the number of audio files in the list
+ * @return the number of audio clips in the list
*/
public int getNumAudios() {
return getNumMedia();
}
/**
- * Add an audio file to the list
+ * Add an audio clip to the list
* @param inAudio object to add
*/
- public void addAudio(AudioFile inAudio) {
+ public void addAudio(AudioClip inAudio) {
addMedia(inAudio);
}
/**
- * Add an audio file to the list
+ * Add an audio clip to the list
* @param inAudio object to add
* @param inIndex index at which to add
*/
- public void addAudio(AudioFile inAudio, int inIndex) {
+ public void addAudio(AudioClip inAudio, int inIndex) {
addMedia(inAudio, inIndex);
}
/**
- * Remove the selected audio file from the list
+ * Remove the selected audio clip from the list
* @param inIndex index number to remove
*/
public void deleteAudio(int inIndex) {
}
/**
- * Get the index of the given audio file
+ * Get the index of the given audio clip
* @param inAudio object to check
* @return index of this object in the list, or -1 if not found
*/
- public int getAudioIndex(AudioFile inAudio) {
+ public int getAudioIndex(AudioClip inAudio) {
return getMediaIndex(inAudio);
}
* @param inIndex index number, starting at 0
* @return specified object
*/
- public AudioFile getAudio(int inIndex) {
- return (AudioFile) getMedia(inIndex);
+ public AudioClip getAudio(int inIndex) {
+ return (AudioClip) getMedia(inIndex);
}
/**
inString = inString.trim();
strLen = inString.length();
}
- if (strLen > 1)
+ if (strLen > 0)
{
// Check for cardinal character either at beginning or end
boolean hasCardinal = true;
numFields++;
denoms[numFields-1] = 1;
}
- fields[numFields-1] = fields[numFields-1] * 10 + (currChar - '0');
- denoms[numFields-1] *= 10;
+ if (denoms[numFields-1] < 1E18) // ignore trailing characters if too big for long
+ {
+ fields[numFields-1] = fields[numFields-1] * 10 + (currChar - '0');
+ denoms[numFields-1] *= 10;
+ }
}
else
{
private Timestamp _timestamp = null;
/** Attached photo */
private Photo _photo = null;
- /** Attached audio file */
- private AudioFile _audio = null;
+ /** Attached audio clip */
+ private AudioClip _audio = null;
private String _waypointName = null;
private boolean _startOfSegment = false;
private boolean _markedForDeletion = false;
}
/**
- * Set the audio file for this point
+ * Set the audio clip for this point
* @param inAudio audio object
*/
- public void setAudio(AudioFile inAudio) {
+ public void setAudio(AudioClip inAudio) {
_audio = inAudio;
_modifyCount++;
}
/**
* @return associated audio object
*/
- public AudioFile getAudio() {
+ public AudioClip getAudio() {
return _audio;
}
/**
* Attach the given media object according to type
- * @param inMedia either a photo or an audio file
+ * @param inMedia either a photo or an audio clip
*/
- public void attachMedia(MediaFile inMedia)
+ public void attachMedia(MediaObject inMedia)
{
if (inMedia != null) {
if (inMedia instanceof Photo) {
setPhoto((Photo) inMedia);
inMedia.setDataPoint(this);
}
- else if (inMedia instanceof AudioFile) {
- setAudio((AudioFile) inMedia);
+ else if (inMedia instanceof AudioClip) {
+ setAudio((AudioClip) inMedia);
inMedia.setDataPoint(this);
}
}
public static final Field TIMESTAMP = new Field("fieldname.timestamp", true);
public static final Field WAYPT_NAME = new Field("fieldname.waypointname", true);
public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", true);
+ public static final Field DESCRIPTION = new Field("fieldname.description", true);
public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", true);
// TODO: Field for photo filename, ability to load (from text) and save (to text)
private static final Field[] ALL_AVAILABLE_FIELDS = {
- LATITUDE, LONGITUDE, ALTITUDE, TIMESTAMP, WAYPT_NAME, WAYPT_TYPE, NEW_SEGMENT,
+ LATITUDE, LONGITUDE, ALTITUDE, TIMESTAMP, WAYPT_NAME, WAYPT_TYPE, DESCRIPTION, NEW_SEGMENT,
new Field(I18nManager.getText("fieldname.custom"))
};
*/
public abstract class MediaList
{
- /** list of media file objects */
- protected ArrayList<MediaFile> _media = null;
+ /** list of media objects */
+ protected ArrayList<MediaObject> _media = null;
/**
* Constructor
* @param inList ArrayList containing media objects
*/
- protected MediaList(ArrayList<MediaFile> inList)
+ protected MediaList(ArrayList<MediaObject> inList)
{
_media = inList;
if (_media == null) {
- _media = new ArrayList<MediaFile>();
+ _media = new ArrayList<MediaObject>();
}
}
* Add an object to the list
* @param inObject object to add
*/
- public void addMedia(MediaFile inObject)
+ public void addMedia(MediaObject inObject)
{
if (inObject != null) {
_media.add(inObject);
* @param inObject object to add
* @param inIndex index at which to add
*/
- public void addMedia(MediaFile inObject, int inIndex)
+ public void addMedia(MediaObject inObject, int inIndex)
{
if (inObject != null) {
_media.add(inIndex, inObject);
* @param inMedia media object to check
* @return true if it's already in the list
*/
- public boolean contains(MediaFile inMedia) {
+ public boolean contains(MediaObject inMedia) {
return (getMediaIndex(inMedia) > -1);
}
* @param inMedia object to check
* @return index of this object in the list, or -1 if not found
*/
- public int getMediaIndex(MediaFile inMedia)
+ public int getMediaIndex(MediaObject inMedia)
{
// Check if we need to check
final int num = getNumMedia();
- if (num <= 0 || inMedia == null || inMedia.getFile() == null)
+ if (num <= 0 || inMedia == null)
return -1;
// Loop over list
for (int i=0; i<num; i++)
{
- MediaFile m = _media.get(i);
+ MediaObject m = _media.get(i);
if (m != null && m.equals(inMedia)) {
return i;
}
* @param inIndex index number, starting at 0
* @return specified object
*/
- public MediaFile getMedia(int inIndex)
+ public MediaObject getMedia(int inIndex)
{
if (inIndex < 0 || inIndex >= getNumMedia()) return null;
return _media.get(inIndex);
final int num = getNumMedia();
String[] names = new String[num];
for (int i=0; i<num; i++) {
- names[i] = getMedia(i).getFile().getName();
+ names[i] = getMedia(i).getName();
}
return names;
}
*/
public boolean hasCorrelatedMedia()
{
- for (MediaFile m : _media) {
+ for (MediaObject m : _media) {
if (m.getDataPoint() != null) {
return true;
}
*/
public boolean hasUncorrelatedMedia()
{
- for (MediaFile m : _media) {
+ for (MediaObject m : _media) {
if (m.getDataPoint() == null) {
return true;
}
if (getNumMedia() > 0)
{
// Construct new list to copy into
- ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ ArrayList<MediaObject> listCopy = new ArrayList<MediaObject>();
// Loop over list
- for (MediaFile m : _media)
+ for (MediaObject m : _media)
{
// Copy media if it has no point
if (m != null)
}
}
+ /**
+ * @return true if any of the media objects have Files
+ */
+ public boolean hasMediaWithFile()
+ {
+ for (MediaObject m: _media) {
+ if (m.getFile() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* @return clone of list contents
*/
import java.io.File;
/**
- * Class to represent a general media file for correlation.
- * Subclasses are currently Photo and AudioFile
+ * Class to represent a general media object for correlation.
+ * Subclasses are currently Photo and AudioClip
*/
-public abstract class MediaFile
+public abstract class MediaObject
{
- /** File where media is stored */
+ /** File where media is stored (if any) */
protected File _file = null;
+ /** Name of file */
+ protected String _name = null;
+ /** Cached data if downloaded */
+ protected byte[] _data = null;
+ /** URL if media came from net */
+ protected String _url = null;
/** Timestamp, if any */
protected Timestamp _timestamp = null;
/** Associated DataPoint if correlated */
private Status _currentStatus = Status.NOT_CONNECTED;
/** Connection status */
- public enum Status {
+ public enum Status
+ {
/** Media is not connected to any point */
NOT_CONNECTED,
/** Media has been connected to a point since it was loaded */
* @param inFile file object
* @param inStamp timestamp object
*/
- public MediaFile(File inFile, Timestamp inStamp)
+ public MediaObject(File inFile, Timestamp inStamp)
{
_file = inFile;
+ _name = inFile.getName();
+ _data = null;
_timestamp = inStamp;
}
+ /**
+ * Constructor for byte arrays
+ * @param inData byte array containing data
+ * @param inName name of object
+ * @param inUrl source url of object or null
+ */
+ public MediaObject(byte[] inData, String inName, String inUrl)
+ {
+ _file = null;
+ _data = inData;
+ _name = inName;
+ _url = inUrl;
+ _timestamp = null;
+ }
+
/**
* @return the file object
*/
return _file;
}
+ /** @return media name */
+ public String getName() {
+ return _name;
+ }
+
/**
* @return the timestamp object
*/
_timestamp = inTimestamp;
}
+ /**
+ * @return byte data of media
+ */
+ public byte[] getByteData() {
+ return _data;
+ }
+
+ /**
+ * @return source Url (or null)
+ */
+ public String getUrl() {
+ return _url;
+ }
+
/**
* @return true if details are valid (might not have timestamp)
*/
- public boolean isValid() {
- return _file != null && _file.exists() && _file.canRead();
+ public boolean isValid()
+ {
+ return ((_file != null && _file.exists() && _file.canRead())
+ || (_data != null && _data.length > 0));
}
/**
}
/**
- * Check if this object refers to the same File as another
- * @param inOther other MediaFile object
- * @return true if the Files are the same
+ * Check if this object refers to the same object as another
+ * @param inOther other MediaObject
+ * @return true if the objects are the same
*/
- public boolean equals(MediaFile inOther)
+ public boolean equals(MediaObject inOther)
{
- return (inOther != null && inOther.getFile() != null && getFile() != null
- && inOther.getFile().equals(getFile()));
+ if (_file != null)
+ {
+ // compare file objects
+ return (inOther != null && inOther.getFile() != null && getFile() != null
+ && inOther.getFile().equals(getFile()));
+ }
+ // compare data arrays
+ return (inOther != null && _name != null && inOther._name != null && _name.equals(inOther._name)
+ && _data != null && inOther._data != null && _data.length == inOther._data.length);
}
/**
}
/**
- * @return true if file is connected to a point
+ * @return true if this object is connected to a point
*/
public boolean isConnected()
{
}
/**
- * Reset any cached data held by the media file (eg thumbnail)
+ * Reset any cached data (eg thumbnail)
*/
public void resetCachedData() {}
}
/**
* Class to represent a photo and link to DataPoint
*/
-public class Photo extends MediaFile
+public class Photo extends MediaObject
{
/** Size of original image */
private Dimension _size = null;
/** rotation flag (clockwise from 0 to 3) */
private int _rotation = 0;
// TODO: Need to store caption for image?
- // thumbnail for image (from exif)
+ /** Bearing, if any */
+ private double _bearing = -1.0;
+ /** thumbnail for image (from exif) */
private byte[] _exifThumbnail = null;
/**
super(inFile, null);
}
+ /**
+ * Constructor using data, eg from zip file or URL
+ * @param inData data as byte array
+ * @param inName name of file from which it came
+ * @param inUrl url from which it came (or null)
+ */
+ public Photo(byte[] inData, String inName, String inUrl)
+ {
+ super(inData, inName, inUrl);
+ }
+
/**
* Calculate the size of the image (slow)
*/
private void calculateSize()
{
- ImageIcon icon = new ImageIcon(_file.getAbsolutePath());
+ ImageIcon icon = null;
+ if (_file != null)
+ icon = new ImageIcon(_file.getAbsolutePath());
+ else
+ icon = new ImageIcon(_data);
int width = icon.getIconWidth();
int height = icon.getIconHeight();
if (width > 0 && height > 0)
*/
public int getWidth()
{
- if (_size == null)
- {
- calculateSize();
- if (_size == null) {return -1;}
- }
+ if (getSize() == null) {return -1;}
return _size.width;
}
*/
public int getHeight()
{
- if (_size == null)
- {
- calculateSize();
- if (_size == null) {return -1;}
- }
+ if (getSize() == null) {return -1;}
return _size.height;
}
{
return _rotation * 90;
}
+
+ /**
+ * @return a new image icon for the whole image
+ */
+ public ImageIcon createImageIcon()
+ {
+ if (_file != null) {
+ return new ImageIcon(_file.getAbsolutePath());
+ }
+ if (_data != null) {
+ return new ImageIcon(_data);
+ }
+ return null;
+ }
+
+ /**
+ * @param inValue bearing in degrees, 0 to 360
+ */
+ public void setBearing(double inValue) {
+ _bearing = inValue;
+ }
+
+ /** @return bearing in degrees */
+ public double getBearing() {
+ return _bearing;
+ }
}
* Constructor
* @param inList ArrayList containing Photo objects
*/
- private PhotoList(ArrayList<MediaFile> inList) {
+ private PhotoList(ArrayList<MediaObject> inList) {
super(inList);
}
public PhotoList cloneList()
{
if (getNumMedia() == 0) return this;
- ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ ArrayList<MediaObject> listCopy = new ArrayList<MediaObject>();
listCopy.addAll(_media);
return new PhotoList(listCopy);
}
--- /dev/null
+package tim.prune.data;
+
+import java.io.File;
+
+/**
+ * Simple class to represent an entry in the recently-used files list
+ */
+public class RecentFile
+{
+ private boolean _regularLoad = true; // false for load via gpsbabel
+ private File _file = null;
+
+ /**
+ * Constructor
+ * @param inFile file
+ * @param inRegular true for regular load, false for gpsbabel load
+ */
+ public RecentFile(File inFile, boolean inRegular)
+ {
+ _file = inFile;
+ _regularLoad = inRegular;
+ }
+
+ /**
+ * Constructor
+ * @param inDesc String from config
+ */
+ public RecentFile(String inDesc)
+ {
+ if (inDesc != null && inDesc.length() > 3)
+ {
+ _regularLoad = (inDesc.charAt(0) != 'g');
+ _file = new File(inDesc.substring(1));
+ }
+ }
+
+ /**
+ * @return file object
+ */
+ public File getFile() {
+ return _file;
+ }
+
+ /**
+ * @return true for regular load, false for gpsbabel load
+ */
+ public boolean isRegularLoad() {
+ return _regularLoad;
+ }
+
+ /**
+ * @return true if file (still) exists
+ */
+ public boolean isValid() {
+ return _file != null && _file.exists() && _file.isFile();
+ }
+
+ /**
+ * @return string to save in config
+ */
+ public String getConfigString()
+ {
+ if (!isValid()) return "";
+ return (_regularLoad?"r":"g") + _file.getAbsolutePath();
+ }
+
+ /**
+ * Check for equality
+ * @param inOther other RecentFile object
+ * @return true if they both refer to the same file
+ */
+ public boolean isSameFile(RecentFile inOther)
+ {
+ return inOther != null && isValid() && inOther.isValid()
+ && _file.equals(inOther._file);
+ }
+}
--- /dev/null
+package tim.prune.data;
+
+/**
+ * Class to hold and manage the list of recently used files
+ */
+public class RecentFileList
+{
+ private RecentFile[] _files = null;
+ private static final int DEFAULT_SIZE = 6;
+ private static final int MAX_SIZE = 20;
+
+ /**
+ * Default constructor
+ */
+ public RecentFileList()
+ {
+ _files = new RecentFile[DEFAULT_SIZE];
+ }
+
+ /**
+ * Constructor
+ * @param inString String from config
+ */
+ public RecentFileList(String inString)
+ {
+ _files = null;
+ int pos = 0;
+ if (inString != null && inString.length() > 0)
+ {
+ for (String s : inString.split(";"))
+ {
+ if (pos == 0)
+ {
+ int listSize = DEFAULT_SIZE;
+ try
+ {
+ listSize = Integer.parseInt(s);
+ if (listSize < 1 || listSize > MAX_SIZE) {
+ listSize = DEFAULT_SIZE;
+ }
+ }
+ catch (NumberFormatException nfe) {}
+ _files = new RecentFile[listSize];
+ pos++;
+ }
+ else if (pos <= _files.length)
+ {
+ RecentFile rf = new RecentFile(s);
+ if (rf.isValid())
+ {
+ _files[pos-1] = rf;
+ pos++;
+ }
+ }
+ }
+ }
+ if (_files == null) {
+ _files = new RecentFile[DEFAULT_SIZE];
+ }
+ }
+
+ /**
+ * @return size of list (may not have this many entries yet)
+ */
+ public int getSize()
+ {
+ if (_files == null) return 0;
+ return _files.length;
+ }
+
+ /**
+ * @return the number of valid entries in the list
+ */
+ public int getNumEntries()
+ {
+ if (_files == null) return 0;
+ int numFound = 0;
+ for (RecentFile rf : _files) {
+ if (rf != null && rf.isValid())
+ numFound++;
+ }
+ return numFound;
+ }
+
+ /**
+ * @return string to save in config
+ */
+ public String getConfigString()
+ {
+ StringBuilder builder = new StringBuilder(100);
+ int size = getSize();
+ builder.append("" + size);
+ for (RecentFile f : _files)
+ {
+ builder.append(';');
+ if (f != null) builder.append(f.getConfigString());
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Add the given file to the top of the list
+ * @param inRF file to add
+ */
+ public void addFile(RecentFile inRF)
+ {
+ // Build a new array with the latest file at the top
+ RecentFile[] files = new RecentFile[_files.length];
+ int rfIndex = 0;
+ if (inRF != null && inRF.isValid())
+ {
+ files[rfIndex] = inRF;
+ rfIndex++;
+ }
+ // Loop, copying the other files
+ for (RecentFile rf : _files)
+ {
+ if (rf != null && rf.isValid() && (inRF==null || !rf.isSameFile(inRF)))
+ {
+ files[rfIndex] = rf;
+ rfIndex++;
+ if (rfIndex >= files.length) break;
+ }
+ }
+ _files = files;
+ }
+
+ /**
+ * Verify all the entries and remove the invalid ones
+ */
+ public void verifyAll() {
+ addFile(null);
+ }
+
+ /**
+ * Get the RecentFile object at the given index
+ * @param inIndex index, starting at 0
+ * @return RecentFile object or null if out of range
+ */
+ public RecentFile getFile(int inIndex)
+ {
+ if (inIndex < 0 || inIndex >= _files.length) return null;
+ return _files[inIndex];
+ }
+
+ /**
+ * Resize the list to the new size
+ * @param inNewSize new size of list
+ */
+ public void resizeList(int inNewSize)
+ {
+ // don't do anything if size doesn't make sense
+ if (inNewSize > 0 && inNewSize <= MAX_SIZE)
+ {
+ RecentFile[] files = new RecentFile[inNewSize];
+ int numToCopy = _files.length;
+ if (inNewSize < numToCopy) {
+ numToCopy = inNewSize;
+ }
+ System.arraycopy(_files, 0, files, 0, numToCopy);
+ _files = files;
+ }
+ }
+}
for (int i=0; i<_points.length && (idx < 0); i++) {
if (_points[i] == inPoint) {idx = i;}
}
+ if (idx == -1) {return idx;} // point not found
if (_pointIndices == null) {return idx;} // All points loaded
return _pointIndices[idx]; // use point index mapping
}
_scaled = false;
}
+ /**
+ * Constructor using fields and points from another Track
+ * @param inFieldList Field list from another Track object
+ * @param inPoints (edited) point array
+ */
+ public Track(FieldList inFieldList, DataPoint[] inPoints)
+ {
+ _masterFieldList = inFieldList;
+ _dataPoints = inPoints;
+ if (_dataPoints == null) _dataPoints = new DataPoint[0];
+ _numPoints = _dataPoints.length;
+ _scaled = false;
+ }
/**
* Load method, for initialising and reinitialising data
*/
public DataPoint getPreviousTrackPoint(int inStartIndex)
{
+ // end index is given as _numPoints but actually it just counts down to -1
return getNextTrackPoint(inStartIndex, _numPoints, false);
}
}
return false;
}
+
+ /**
+ * @param inPoint point to check
+ * @return true if this track contains the given point
+ */
+ public boolean containsPoint(DataPoint inPoint)
+ {
+ if (inPoint == null) return false;
+ for (int i=0; i < getNumPoints(); i++)
+ {
+ if (getPoint(i) == inPoint) return true;
+ }
+ return false; // not found
+ }
}
}
/**
- * Get the currently selected audio file, if any
- * @return AudioFile if selected, otherwise null
+ * Get the currently selected audio clip, if any
+ * @return AudioClip if selected, otherwise null
*/
- public AudioFile getCurrentAudio() {
+ public AudioClip getCurrentAudio() {
return _audioList.getAudio(_selection.getCurrentAudioIndex());
}
* @param inSet Set containing Audio objects
* @return number of audio objects added
*/
- public int addAudios(Set<AudioFile> inSet)
+ public int addAudios(Set<AudioClip> inSet)
{
int numAudiosAdded = 0;
if (inSet != null && !inSet.isEmpty())
{
- for (AudioFile audio : inSet)
+ for (AudioClip audio : inSet)
{
if (audio != null && !_audioList.contains(audio))
{
if (_track.deletePoint(_selection.getCurrentPointIndex()))
{
_selection.modifyPointDeleted();
- UpdateMessageBroker.informSubscribers();
return true;
}
return false;
int audioIndex = _selection.getCurrentAudioIndex();
if (audioIndex >= 0)
{
- AudioFile audio = _audioList.getAudio(audioIndex);
+ AudioClip audio = _audioList.getAudio(audioIndex);
_audioList.deleteAudio(audioIndex);
// has it got a point?
if (audio.getDataPoint() != null)
selectPoint(_track.getPointIndex(inPoint));
}
+ /**
+ * Increment the selected point index by the given increment
+ * @param inPointIncrement +1 for next point, -1 for previous etc
+ */
+ public void incrementPointIndex(int inPointIncrement)
+ {
+ int index = _selection.getCurrentPointIndex() + inPointIncrement;
+ if (index < 0) index = 0;
+ else if (index >= _track.getNumPoints()) index = _track.getNumPoints()-1;
+ selectPoint(index);
+ }
+
/**
* Select the data point with the given index
* @param inPointIndex index of DataPoint to select, or -1 for none
audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio());
}
else if (audioIndex < 0 || _audioList.getAudio(audioIndex).isConnected()) {
- // deselect current audio file
+ // deselect current audio clip
audioIndex = -1;
}
// give to selection
pointIndex = -1;
}
}
- // Has the new point got an audio file?
+ // Has the new point got an audio clip?
DataPoint selectedPoint = _track.getPoint(pointIndex);
int audioIndex = _selection.getCurrentAudioIndex();
if (selectedPoint != null) {
{
if (_selection.getCurrentAudioIndex() == inAudioIndex) {return;}
// Audio selection takes priority, deselecting point if necessary
- AudioFile audio = _audioList.getAudio(inAudioIndex);
+ AudioClip audio = _audioList.getAudio(inAudioIndex);
int pointIndex = _selection.getCurrentPointIndex();
DataPoint currPoint = getCurrentPoint();
if (audio != null)
import tim.prune.App;
import tim.prune.ExternalTools;
import tim.prune.GenericFunction;
-import tim.prune.GpsPruner;
+import tim.prune.GpsPrune;
import tim.prune.I18nManager;
import tim.prune.jpeg.ExifGateway;
import tim.prune.threedee.WindowFactory;
JPanel aboutPanel = new JPanel();
aboutPanel.setLayout(new BoxLayout(aboutPanel, BoxLayout.Y_AXIS));
aboutPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
- JLabel titleLabel = new JLabel("Prune");
+ JLabel titleLabel = new JLabel("GpsPrune");
titleLabel.setFont(new Font("SansSerif", Font.BOLD, 24));
titleLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
aboutPanel.add(titleLabel);
- JLabel versionLabel = new JLabel(I18nManager.getText("dialog.about.version") + ": " + GpsPruner.VERSION_NUMBER);
+ JLabel versionLabel = new JLabel(I18nManager.getText("dialog.about.version") + ": " + GpsPrune.VERSION_NUMBER);
versionLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
aboutPanel.add(versionLabel);
- JLabel buildLabel = new JLabel(I18nManager.getText("dialog.about.build") + ": " + GpsPruner.BUILD_NUMBER);
+ JLabel buildLabel = new JLabel(I18nManager.getText("dialog.about.build") + ": " + GpsPrune.BUILD_NUMBER);
buildLabel.setAlignmentX(JLabel.CENTER_ALIGNMENT);
aboutPanel.add(buildLabel);
aboutPanel.add(new JLabel(" "));
descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
.append("\u010de\u0161tina, deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, magyar,<br>" +
" nederlands, polski, portugu\u00EAs, \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), \uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean),<br>" +
- " schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, rom\u00E2n\u0103, afrikaans, bahasa indonesia, farsi</p>");
+ " schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, rom\u00E2n\u0103, afrikaans, bahasa indonesia</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));
--- /dev/null
+package tim.prune.function;
+
+import java.io.File;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.AudioClip;
+import tim.prune.data.MediaObject;
+import tim.prune.data.Photo;
+import tim.prune.data.Track;
+import tim.prune.load.MediaHelper;
+import tim.prune.load.MediaLoadProgressDialog;
+import tim.prune.undo.UndoLoadAudios;
+import tim.prune.undo.UndoLoadPhotos;
+
+/**
+ * Function to load media asynchronously,
+ * either from inside a zip/kmz file or remotely
+ */
+public class AsyncMediaLoader extends GenericFunction
+implements Runnable, Cancellable
+{
+ /** Archive from which points were loaded */
+ private File _zipFile = null;
+ /** Array of links */
+ private String[] _linkArray = null;
+ /** Track to use for connecting */
+ private Track _track = null;
+ /** Cancelled flag */
+ private boolean _cancelled = false;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ * @param inLinkArray array of links
+ * @param inTrack Track object for connecting points
+ */
+ public AsyncMediaLoader(App inApp, File inZipFile, String[] inLinkArray, Track inTrack)
+ {
+ super(inApp);
+ _zipFile = inZipFile;
+ _linkArray = inLinkArray;
+ _track = inTrack;
+ }
+
+ /**
+ * Begin the load
+ */
+ public void begin()
+ {
+ _cancelled = false;
+ if (_linkArray != null)
+ new Thread(this).start();
+ }
+
+ /** Cancel */
+ public void cancel() {
+ _cancelled = true;
+ }
+
+ /**
+ * @return the name key
+ */
+ public String getNameKey() {
+ return "function.asyncmediaload";
+ }
+
+ /**
+ * Execute the load in a separate thread
+ */
+ public void run()
+ {
+ // Count links first so that progress bar can be shown
+ int numLinks = 0;
+ for (int i=0; i<_linkArray.length; i++) {
+ if (_linkArray[i] != null) {
+ numLinks++;
+ }
+ }
+ if (numLinks <= 0) return;
+ // Make progress dialog
+ MediaLoadProgressDialog progressDialog = new MediaLoadProgressDialog(_app.getFrame(), this);
+ // Make array to store results
+ MediaObject[] media = new MediaObject[numLinks];
+ int currLink = 0;
+ for (int i=0; i<_linkArray.length && !_cancelled; i++)
+ {
+ if (_linkArray[i] != null)
+ {
+ MediaObject mf = MediaHelper.createMediaObject(_zipFile, _linkArray[i]);
+ if (mf != null)
+ {
+ // attach media to point and set status
+ _track.getPoint(i).attachMedia(mf);
+ mf.setOriginalStatus(MediaObject.Status.TAGGED);
+ mf.setCurrentStatus(MediaObject.Status.TAGGED);
+ media[currLink] = mf;
+ // update progress
+ if (!_app.isBusyLoading())
+ progressDialog.showProgress(currLink, numLinks);
+ currLink++;
+ }
+ try {Thread.sleep(100);} catch (InterruptedException ie) {}
+ }
+ }
+ progressDialog.close();
+
+ // Wait until App is ready to receive media (may have to ask about append/replace etc)
+ waitUntilAppReady();
+
+ // Go through the loaded media and check if the points are still in the track
+ int numPhotos = 0, numAudios = 0;
+ for (currLink=0; currLink<numLinks; currLink++)
+ {
+ MediaObject mo = media[currLink];
+ if (mo != null && _track.containsPoint(mo.getDataPoint()))
+ {
+ if (mo instanceof Photo)
+ {
+ _app.getTrackInfo().getPhotoList().addPhoto((Photo) mo);
+ numPhotos++;
+ }
+ else if (mo instanceof AudioClip)
+ {
+ _app.getTrackInfo().getAudioList().addAudio((AudioClip) mo);
+ numAudios++;
+ }
+ }
+ }
+ // Confirm and update
+ if (numPhotos > 0) {
+ _app.completeFunction(new UndoLoadPhotos(numPhotos, 0), "" + numPhotos + " " +
+ I18nManager.getText(numPhotos == 1?"confirm.jpegload.single":"confirm.jpegload.multi"));
+ }
+ if (numAudios > 0) {
+ _app.completeFunction(new UndoLoadAudios(numAudios), I18nManager.getText("confirm.audioload"));
+ }
+ UpdateMessageBroker.informSubscribers(DataSubscriber.DATA_ADDED_OR_REMOVED);
+ }
+
+
+ /**
+ * Wait until the App is ready
+ */
+ private void waitUntilAppReady()
+ {
+ long waitInterval = 500; // milliseconds
+ while (_app.isBusyLoading())
+ {
+ try {Thread.sleep(waitInterval);} catch (InterruptedException ie) {}
+ waitInterval *= 1.2;
+ }
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+/**
+ * Interface implemented by functions which can be cancelled
+ */
+public interface Cancellable
+{
+ /**
+ * Cancel the function
+ */
+ public void cancel();
+}
import tim.prune.App;
import tim.prune.GenericFunction;
-import tim.prune.GpsPruner;
+import tim.prune.GpsPrune;
import tim.prune.I18nManager;
import tim.prune.function.browser.BrowserLauncher;
/**
- * Class to check the version of Prune
+ * Class to check the version of GpsPrune
* and show an appropriate dialog
*/
public class CheckVersionScreen extends GenericFunction
*/
public void begin()
{
- final String filePathStart = "http://activityworkshop.net/software/prune/prune_versioncheck_";
+ final String filePathStart = "http://activityworkshop.net/software/gpsprune/gpsprune_versioncheck_";
final String filePathEnd = ".txt";
String latestVer = null;
String nextVersion = null;
try
{
// Load properties from the url on the server
- InputStream inStream = new URL(filePathStart + GpsPruner.VERSION_NUMBER + filePathEnd).openStream();
+ InputStream inStream = new URL(filePathStart + GpsPrune.VERSION_NUMBER + filePathEnd).openStream();
props.load(inStream);
// Extract the three fields we want, ignore others
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.checkversion.error"),
I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
}
- else if (latestVer.equals(GpsPruner.VERSION_NUMBER))
+ else if (latestVer.equals(GpsPrune.VERSION_NUMBER))
{
// Version on the server is the same as this one
String displayMessage = I18nManager.getText("dialog.checkversion.uptodate");
== JOptionPane.YES_OPTION)
{
// User selected to launch home page
- BrowserLauncher.launchBrowser("http://activityworkshop.net/software/prune/download.html");
+ BrowserLauncher.launchBrowser("http://activityworkshop.net/software/gpsprune/download.html");
}
}
}
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.DataPoint;
import tim.prune.data.Photo;
import tim.prune.undo.UndoConnectMedia;
import tim.prune.undo.UndoOperation;
/**
- * Function to connect either a photo or an audio file to the current point
+ * Function to connect either a photo or an audio clip to the current point
*/
public class ConnectToPointFunction extends GenericFunction
{
{
Photo photo = _app.getTrackInfo().getCurrentPhoto();
DataPoint point = _app.getTrackInfo().getCurrentPoint();
- AudioFile audio = _app.getTrackInfo().getCurrentAudio();
+ AudioClip audio = _app.getTrackInfo().getCurrentAudio();
boolean connectPhoto = (point != null && photo != null && point.getPhoto() == null);
boolean connectAudio = (point != null && audio != null && point.getAudio() == null);
// TODO: Let user choose whether to connect photo/audio or both
}
// Make undo object
- UndoOperation undo = new UndoConnectMedia(point, connectPhoto?photo.getFile().getName():null,
- connectAudio?audio.getFile().getName():null);
+ UndoOperation undo = new UndoConnectMedia(point, connectPhoto?photo.getName():null,
+ connectAudio?audio.getName():null);
// Connect the media
if (connectPhoto) {
photo.setDataPoint(point);
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.DataPoint;
import tim.prune.undo.UndoDisconnectMedia;
import tim.prune.undo.UndoOperation;
*/
public void begin()
{
- AudioFile audio = _app.getTrackInfo().getCurrentAudio();
+ AudioClip audio = _app.getTrackInfo().getCurrentAudio();
if (audio != null && audio.getDataPoint() != null)
{
DataPoint point = audio.getDataPoint();
- UndoOperation undo = new UndoDisconnectMedia(point, false, true, audio.getFile().getName());
+ UndoOperation undo = new UndoDisconnectMedia(point, false, true, audio.getName());
// disconnect
audio.setDataPoint(null);
point.setAudio(null);
if (photo != null && photo.getDataPoint() != null)
{
DataPoint point = photo.getDataPoint();
- UndoDisconnectMedia undo = new UndoDisconnectMedia(point, true, false, photo.getFile().getName());
+ UndoDisconnectMedia undo = new UndoDisconnectMedia(point, true, false, photo.getName());
// disconnect
photo.setDataPoint(null);
point.setPhoto(null);
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
+import tim.prune.function.cache.ManageCacheFunction;
/**
* Class to show the popup window for setting the path to disk cache
private JCheckBox _cacheCheckbox = null;
private JTextField _cacheDirBox = null;
private JButton _browseButton = null;
- private JButton _okButton = null;
+ private JButton _okButton = null, _manageButton = null;
private boolean _initialCheckState = false;
private String _initialCacheDir = null;
_cacheCheckbox = new JCheckBox(I18nManager.getText("dialog.diskcache.save"));
_cacheCheckbox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
- enableOk();
+ enableButtons();
}
});
topPanel.add(_cacheCheckbox);
// dir panel
JPanel dirPanel = new JPanel();
dirPanel.setLayout(new BorderLayout());
- dirPanel.add(new JLabel(I18nManager.getText("dialog.diskcache.dir")), BorderLayout.WEST);
+ dirPanel.add(new JLabel(I18nManager.getText("dialog.diskcache.dir") + " : "), BorderLayout.WEST);
_cacheDirBox = new JTextField(24);
_cacheDirBox.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent arg0) {
super.keyReleased(arg0);
- enableOk();
+ enableButtons();
}
});
dirPanel.add(_cacheDirBox, BorderLayout.CENTER);
}
});
dirPanel.add(_browseButton, BorderLayout.EAST);
- dialogPanel.add(dirPanel, BorderLayout.CENTER);
+ // holder panel so it doesn't expand vertically
+ JPanel dirHolderPanel = new JPanel();
+ dirHolderPanel.setLayout(new BorderLayout());
+ dirHolderPanel.add(dirPanel, BorderLayout.NORTH);
+ dialogPanel.add(dirHolderPanel, BorderLayout.CENTER);
- // Cancel button at the bottom
- JPanel buttonPanel = new JPanel();
- buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ // OK, Cancel buttons at the bottom right
+ JPanel buttonPanelr = new JPanel();
+ buttonPanelr.setLayout(new FlowLayout(FlowLayout.RIGHT));
_okButton = new JButton(I18nManager.getText("button.ok"));
- _okButton.addActionListener(new ActionListener()
- {
+ _okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
finish();
}
});
- buttonPanel.add(_okButton);
+ buttonPanelr.add(_okButton);
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
_dialog.dispose();
}
});
- buttonPanel.add(cancelButton);
+ buttonPanelr.add(cancelButton);
+
+ // Manage button at the bottom left
+ JPanel buttonPanell = new JPanel();
+ buttonPanell.setLayout(new FlowLayout(FlowLayout.LEFT));
+ _manageButton = new JButton(I18nManager.getText("button.manage"));
+ _manageButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ finish();
+ new ManageCacheFunction(_app).begin();
+ }
+ });
+ buttonPanell.add(_manageButton);
+ // Put them together
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new BorderLayout());
+ buttonPanel.add(buttonPanelr, BorderLayout.EAST);
+ buttonPanel.add(buttonPanell, BorderLayout.WEST);
dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
return dialogPanel;
}
/**
- * Enable or disable the ok button according to what's changed
+ * Enable or disable the buttons according to what's changed
*/
- private void enableOk()
+ private void enableButtons()
{
- boolean checkState = _cacheCheckbox.isSelected();
+ final boolean checkState = _cacheCheckbox.isSelected();
+ final String path = _cacheDirBox.getText();
_cacheDirBox.setEditable(checkState);
_browseButton.setEnabled(checkState);
boolean ok = false;
else {
// If checkbox has been switched off then enable
if (!checkState) {ok = true;}
- else {
+ else
+ {
// checkbox is on, check value
- String path = _cacheDirBox.getText();
if (path.equals("") || (_initialCacheDir != null && path.equals(_initialCacheDir))) {
// Value blank or same as before
ok = false;
}
}
_okButton.setEnabled(ok);
+ // Manage button needs a valid cache
+ boolean cacheDirGood = false;
+ if (checkState && !path.equals(""))
+ {
+ File dir = new File(path);
+ cacheDirGood = dir.exists() && dir.canRead() && dir.isDirectory();
+ }
+ _manageButton.setEnabled(cacheDirGood);
}
/**
String currPath = Config.getConfigString(Config.KEY_DISK_CACHE);
_cacheCheckbox.setSelected(currPath != null);
_cacheDirBox.setText(currPath==null?"":currPath);
- enableOk();
+ enableButtons();
// Remember current state
_initialCheckState = _cacheCheckbox.isSelected();
_dialog.setVisible(true);
{
_cacheDirBox.setText(chooser.getSelectedFile().getAbsolutePath());
}
- enableOk();
+ enableButtons();
}
/**
*/
public void run()
{
- final String url = "http://www.informationfreeway.org/api/0.6/map?bbox=" +
+ final String url = "http://xapi.openstreetmap.org/api/0.6/map?bbox=" +
_latLonLabels[1].getText() + "," + _latLonLabels[3].getText() + "," +
_latLonLabels[2].getText() + "," + _latLonLabels[0].getText();
}
catch (MalformedURLException mue) {}
catch (IOException ioe) {
- // TODO: throw exception or show dialog
- System.out.println("Exception: " + ioe.getClass().getName());
+ _app.showErrorMessageNoLookup(getNameKey(), ioe.getClass().getName() + " - " + ioe.getMessage());
}
// clean up streams
finally {
private static final int MAX_RESULTS = 20;
/** Maximum distance from point in km */
private static final int MAX_DISTANCE = 15;
+ /** Username to use for geonames queries */
+ private static final String GEONAMES_USERNAME = "gpsprune";
/**
String descMessage = "";
InputStream inStream = null;
- // Example http://ws.geonames.org/findNearbyWikipedia?lat=47&lng=9
- String urlString = "http://ws.geonames.org/findNearbyWikipedia?lat=" +
+ // Example http://api.geonames.org/findNearbyWikipedia?lat=47&lng=9
+ String urlString = "http://api.geonames.org/findNearbyWikipedia?lat=" +
lat + "&lng=" + lon + "&maxRows=" + MAX_RESULTS
- + "&radius=" + MAX_DISTANCE + "&lang=" + I18nManager.getText("wikipedia.lang");
+ + "&radius=" + MAX_DISTANCE + "&lang=" + I18nManager.getText("wikipedia.lang")
+ + "&username=" + GEONAMES_USERNAME;
// Parse the returned XML with a special handler
GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler();
try
descMessage = I18nManager.getText("dialog.gpsies.nonefound");
}
_statusLabel.setText(descMessage);
+ // Show error message if any
+ if (trackList == null || trackList.size() == 0) {
+ String error = xmlHandler.getErrorMessage();
+ if (error != null && !error.equals("")) {
+ _app.showErrorMessageNoLookup(getNameKey(), error);
+ }
+ }
}
+
/**
* Load the selected track or point
*/
private ArrayList<GpsiesTrack> _trackList = null;
private GpsiesTrack _track = null;
private String _lat = null, _lon = null;
+ private String _errorMessage = null;
/**
_lat = null;
_lon = null;
}
+ else if (inTagName.equals("status")) {
+ _errorMessage = inAttributes.getValue("message");
+ }
else _value = null;
super.startElement(inUri, inLocalName, inTagName, inAttributes);
}
{
return _trackList;
}
+
+ /**
+ * @return error message, if any
+ */
+ public String getErrorMessage() {
+ return _errorMessage;
+ }
}
== JOptionPane.YES_OPTION)
{
// User selected to launch home page
- BrowserLauncher.launchBrowser("http://activityworkshop.net/software/prune/index.html");
+ BrowserLauncher.launchBrowser("http://activityworkshop.net/software/gpsprune/index.html");
}
}
}
{
_frame.setVisible(false);
Photo photo = _app.getTrackInfo().getCurrentPhoto();
- _frame.setTitle(photo.getFile().getName());
- _label.setText("'" + photo.getFile().getName() + "' ("
+ _frame.setTitle(photo.getName());
+ _label.setText("'" + photo.getName() + "' ("
+ photo.getWidth() + " x " + photo.getHeight() + ")");
_photoThumb.setPhoto(photo);
}
package tim.prune.function;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import tim.prune.App;
import tim.prune.GenericFunction;
+import tim.prune.data.AudioClip;
/**
- * Class to play the current audio file
+ * Class to play the current audio clip
*/
public class PlayAudioFunction extends GenericFunction implements Runnable
{
*/
public void run()
{
- File audioFile = _app.getTrackInfo().getCurrentAudio().getFile();
+ AudioClip audio = _app.getTrackInfo().getCurrentAudio();
+ File audioFile = audio.getFile();
boolean played = false;
- if (audioFile.exists() && audioFile.isFile() && audioFile.canRead())
+ if (audioFile != null && audioFile.exists() && audioFile.isFile() && audioFile.canRead())
{
// First choice is to play using java
- played = playClip(audioFile);
+ played = playClip(audio);
// Second choice is to try the Desktop library from java 6, if available
if (!played) {
try {
}
}
}
+ else if (audioFile == null && audio.getByteData() != null) {
+ // Try to play audio clip using byte array (can't use Desktop or Runtime)
+ played = playClip(audio);
+ }
if (!played)
{
// If still not worked, show error message
/**
* Try to play the sound file using built-in java libraries
+ * @param inAudio audio clip to play
* @return true if play was successful
*/
- private boolean playClip(File inFile)
+ private boolean playClip(AudioClip inClip)
{
boolean success = false;
AudioInputStream audioInputStream = null;
_clip = null;
try
{
- audioInputStream = AudioSystem.getAudioInputStream(inFile);
+ if (inClip.getFile() != null)
+ audioInputStream = AudioSystem.getAudioInputStream(inClip.getFile());
+ else if (inClip.getByteData() != null)
+ audioInputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(inClip.getByteData()));
+ else return false;
_clip = AudioSystem.getClip();
_clip.open(audioInputStream);
// play the clip
_clip.drain();
success = true;
} catch (Exception e) {
+ System.err.println(e.getClass().getName() + " - " + e.getMessage());
} finally {
// close the stream to clean up
try {
private static void sortPhotos(DataPoint[] inPhotos, boolean inSortByFile)
{
Comparator<DataPoint> comparator = null;
- if (inSortByFile) {
+ 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;
+ if (inP1.getPhoto().getFile() == null || inP2.getPhoto().getFile() == null)
+ return inP1.getPhoto().getName().compareTo(inP2.getPhoto().getName());
return inP1.getPhoto().getFile().compareTo(inP2.getPhoto().getFile());
}
};
}
- else {
+ else
+ {
// sort by photo timestamp
comparator = new Comparator<DataPoint>() {
public int compare(DataPoint inP1, DataPoint inP2) {
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.undo.UndoDeleteAudio;
/**
- * Function to remove the currently selected audio file
+ * Function to remove the currently selected audio clip
*/
public class RemoveAudioFunction extends GenericFunction
{
public void begin()
{
// Delete the current audio, and optionally its point too, keeping undo information
- AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio();
+ AudioClip currentAudio = _app.getTrackInfo().getCurrentAudio();
if (currentAudio != null)
{
// Audio is selected, see if it has a point or not
}
// Add undo information to stack if necessary
if (deleted) {
- _app.completeFunction(undoAction, currentAudio.getFile().getName() + " " + I18nManager.getText("confirm.media.removed"));
+ _app.completeFunction(undoAction, currentAudio.getName() + " " + I18nManager.getText("confirm.media.removed"));
}
}
}
}
// Add undo information to stack if necessary
if (photoDeleted) {
- _app.completeFunction(undoAction, currentPhoto.getFile().getName() + " " + I18nManager.getText("confirm.media.removed"));
+ _app.completeFunction(undoAction, currentPhoto.getName() + " " + I18nManager.getText("confirm.media.removed"));
}
}
}
private void finish()
{
File configFile = Config.getConfigFile();
- if (configFile == null) {configFile = new File(".pruneconfig");}
+ if (configFile == null) {configFile = Config.HOME_CONFIG_FILE;}
JFileChooser chooser = new JFileChooser(configFile.getAbsoluteFile().getParent());
chooser.setSelectedFile(configFile);
int response = chooser.showSaveDialog(_parentFrame);
if (response == JFileChooser.APPROVE_OPTION)
{
File saveFile = chooser.getSelectedFile();
- FileOutputStream outStream = null;
- try
- {
- outStream = new FileOutputStream(saveFile);
- Config.getAllConfig().store(outStream, "Prune config file");
- }
- catch (IOException ioe) {
- _app.showErrorMessageNoLookup(getNameKey(),
- I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
- }
- finally {
- try {outStream.close();} catch (Exception e) {}
- }
+ saveConfig(saveFile);
}
_dialog.dispose();
_dialog = null;
}
+
+ /**
+ * Autosave the settings file without any prompts
+ */
+ public void silentSave()
+ {
+ saveConfig(Config.getConfigFile());
+ }
+
+ /**
+ * Actually save the config file
+ * @param inSaveFile file to save to
+ */
+ private void saveConfig(File inSaveFile)
+ {
+ FileOutputStream outStream = null;
+ try
+ {
+ outStream = new FileOutputStream(inSaveFile);
+ Config.getAllConfig().store(outStream, "GpsPrune config file");
+ }
+ catch (IOException ioe) {
+ _app.showErrorMessageNoLookup(getNameKey(),
+ I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
+ }
+ catch (NullPointerException npe) {} // no config file given
+ finally {
+ try {outStream.close();} catch (Exception e) {}
+ }
+ }
}
package tim.prune.function;
import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.net.URL;
+import java.net.URLEncoder;
import java.util.ArrayList;
import javax.swing.JOptionPane;
private String _searchTerm = null;
/** Maximum number of results to get */
private static final int MAX_RESULTS = 20;
+ /** Username to use for geonames queries */
+ private static final String GEONAMES_USERNAME = "gpsprune";
/**
* Constructor
else {
lang = "en";
}
+ // Replace awkward characters with character equivalents
+ String searchTerm;
+ try {
+ searchTerm = URLEncoder.encode(_searchTerm, "UTF-8");
+ } catch (UnsupportedEncodingException e1) {
+ searchTerm = _searchTerm;
+ }
// Example http://ws.geonames.org/wikipediaSearch?q=london&maxRows=10
- String urlString = "http://ws.geonames.org/wikipediaSearch?title=" + _searchTerm + "&maxRows=" + MAX_RESULTS
- + "&lang=" + lang;
+ String urlString = "http://api.geonames.org/wikipediaSearch?title=" + searchTerm
+ + "&maxRows=" + MAX_RESULTS + "&lang=" + lang + "&username=" + GEONAMES_USERNAME;
// Parse the returned XML with a special handler
GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler();
try
import tim.prune.App;
import tim.prune.GenericFunction;
import tim.prune.I18nManager;
-import tim.prune.data.Altitude;
-import tim.prune.data.Field;
+import tim.prune.data.DataPoint;
import tim.prune.data.SourceInfo;
import tim.prune.data.Track;
import tim.prune.load.TrackNameList;
*/
public class SelectTracksFunction extends GenericFunction
{
- private Field[] _fieldArray = null;
- private Object[][] _dataArray = null;
- private Altitude.Format _altFormat = Altitude.Format.NO_FORMAT;
+ private Track _track = null;
private SourceInfo _sourceInfo = null;
private TrackNameList _trackNameList = null;
private JDialog _dialog = null;
/**
* Constructor
* @param inApp app object to use for load
- * @param inFieldArray field array
- * @param inDataArray data array
- * @param inAltFormat altitude format
+ * @param inTrack loaded track object
* @param inSourceInfo source information
* @param inTrackNameList track name list
*/
- public SelectTracksFunction(App inApp, Field[] inFieldArray, Object[][] inDataArray,
- Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList)
+ public SelectTracksFunction(App inApp, Track inTrack, SourceInfo inSourceInfo,
+ TrackNameList inTrackNameList)
{
super(inApp);
- _fieldArray = inFieldArray;
- _dataArray = inDataArray;
- _altFormat = inAltFormat;
+ _track = inTrack;
_sourceInfo = inSourceInfo;
_trackNameList = inTrackNameList;
}
private void finish()
{
_dialog.dispose();
- // Load track as it is, which is expensive but makes interrogating waypoints easier
- Track loadedTrack = new Track();
- loadedTrack.load(_fieldArray, _dataArray, _altFormat);
int[] tracks = _trackList.getSelectedIndices();
// Check if all tracks are selected, then don't have to filter at all
if (tracks.length == _trackNameList.getNumTracks()) {
- _app.informDataLoaded(loadedTrack, _sourceInfo);
+ _app.informDataLoaded(_track, _sourceInfo);
}
else
{
// Loop over all points, counting points which survive filter and making flag array
int numPointsSelected = 0;
int currentTrack = -1;
- final int totalPoints = loadedTrack.getNumPoints();
+ final int totalPoints = _track.getNumPoints();
boolean[] selectedPoints = new boolean[totalPoints];
for (int i=0; i<totalPoints; i++)
{
final int startOfNextTrack = _trackNameList.getStartIndex(currentTrack+1);
if (i == startOfNextTrack) {currentTrack++;}
- if (currentTrack < 0 || selectedTracks[currentTrack] || loadedTrack.getPoint(i).isWaypoint()) {
+ if (currentTrack < 0 || selectedTracks[currentTrack] || _track.getPoint(i).isWaypoint()) {
selectedPoints[i] = true;
numPointsSelected++;
}
if (numPointsSelected <= 0) {
_app.informNoDataLoaded();
}
- else {
- // Create new data array of required length
- Object[][] newDataArray = new String[numPointsSelected][];
- // Loop over all points again, copying surviving points
+ else
+ {
+ // Create new point array of required length
+ DataPoint[] croppedPoints = new DataPoint[numPointsSelected];
+ // Loop over all points again, copying surviving ones
int currPoint = 0;
for (int i=0; i<totalPoints; i++)
{
if (selectedPoints[i]) {
- newDataArray[currPoint] = _dataArray[i];
+ croppedPoints[currPoint] = _track.getPoint(i);
currPoint++;
}
}
// Construct Track and call informDataLoaded
- Track filteredTrack = new Track();
- filteredTrack.load(_fieldArray, newDataArray, _altFormat);
+ Track filteredTrack = new Track(_track.getFieldList(), croppedPoints);
// Tell source info object which points were selected (pass selectedPoints array)
_sourceInfo.setPointIndices(selectedPoints);
_app.informDataLoaded(filteredTrack, _sourceInfo);
"espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski",
"portugu\u00EAs", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
"\uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e",
- "rom\u00E2n\u0103", "afrikaans", "bahasa indonesia", "farsi"
+ "rom\u00E2n\u0103", "afrikaans", "bahasa indonesia"
};
/** Associated language codes (must be in same order as names!) */
private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "hu",
- "nl", "pl", "pt", "zh", "ja", "ko", "de_ch", "tr", "ro", "af", "in", "fa"
+ "nl", "pl", "pt", "zh", "ja", "ko", "de_ch", "tr", "ro", "af", "in"
};
*/
private void showEndMessage()
{
+ final String messageKey = Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS)?
+ "dialog.setlanguage.endmessagewithautosave":"dialog.setlanguage.endmessage";
JOptionPane.showMessageDialog(_parentFrame,
- I18nManager.getText("dialog.setlanguage.endmessage"),
+ I18nManager.getText(messageKey),
I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
}
}
import tim.prune.GenericFunction;
/**
- * Class to stop playing the current audio file
+ * Class to stop playing the current audio clip
*/
public class StopAudioFunction extends GenericFunction
{
--- /dev/null
+package tim.prune.function.cache;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+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.JProgressBar;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.gui.WholeNumberField;
+
+/**
+ * Function class to manage the tile cache on local disk
+ */
+public class ManageCacheFunction extends GenericFunction implements Runnable
+{
+ private JDialog _dialog = null;
+ private CardLayout _cards = null;
+ private JPanel _cardPanel = null;
+ private JProgressBar _progressBar = null;
+ private File _cacheDir = null;
+ private TileCacheModel _model = null;
+ private JTable _setsTable = null;
+ private JButton _deleteSetButton = null;
+ private JLabel _tileSetLabel = null, _zoomLabel = null;
+ private JLabel _ageLabel = null;
+ private JRadioButton _deleteAllRadio = null;
+ private WholeNumberField _daysField = null;
+
+ private static TileFilter _TILEFILTER = new TileFilter();
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public ManageCacheFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.managetilecache";
+ }
+
+ /**
+ * Show the dialog to start
+ */
+ public void begin()
+ {
+ // First check if directory even exists
+ _cacheDir = null;
+ String path = Config.getConfigString(Config.KEY_DISK_CACHE);
+ if (path != null && !path.equals("")) {
+ _cacheDir = new File(path);
+ }
+ if (_cacheDir == null || !_cacheDir.exists() || !_cacheDir.isDirectory())
+ {
+ _app.showErrorMessage(getNameKey(), "error.cache.notthere");
+ return;
+ }
+
+ // Build the dialog if it hasn't already been built
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true); // modal
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _cardPanel = makeContents();
+ _dialog.getContentPane().add(_cardPanel);
+ _dialog.pack();
+ }
+ // Start a new thread to build the model
+ new Thread(this).start();
+ // Show the first panel of the dialog including progress bar
+ _cards.first(_cardPanel);
+ _dialog.setVisible(true);
+ }
+
+
+ /**
+ * Make the components for the dialog
+ * @return contents inside a panel
+ */
+ private JPanel makeContents()
+ {
+ JPanel dialogPanel = new JPanel();
+ _cards = new CardLayout();
+ dialogPanel.setLayout(_cards);
+
+ // Make first card including progress bar
+ JPanel firstCard = new JPanel();
+ firstCard.setLayout(new BorderLayout());
+ JPanel progPanel = new JPanel();
+ progPanel.setLayout(new BoxLayout(progPanel, BoxLayout.Y_AXIS));
+ progPanel.add(Box.createVerticalGlue());
+ progPanel.add(new JLabel(I18nManager.getText("confirm.running")));
+ _progressBar = new JProgressBar(0, 10);
+ _progressBar.setIndeterminate(true);
+ progPanel.add(_progressBar);
+ progPanel.add(Box.createVerticalGlue());
+ firstCard.add(progPanel, BorderLayout.CENTER);
+ // Cancel button at 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) {
+ // Cancel model building and close dialog
+ if (_model != null) _model.cancel();
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ firstCard.add(buttonPanel, BorderLayout.SOUTH);
+ dialogPanel.add(firstCard, "card1");
+
+ // Make second card including tileset table
+ JPanel secondCard = new JPanel();
+ secondCard.setLayout(new BorderLayout());
+ // Table in the middle
+ JPanel midPanel = new JPanel();
+ midPanel.setLayout(new BorderLayout());
+ _setsTable = new JTable();
+ _setsTable.setPreferredScrollableViewportSize(new Dimension(500, 130));
+ _setsTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ midPanel.add(new JScrollPane(_setsTable), BorderLayout.CENTER);
+ midPanel.setBorder(BorderFactory.createEmptyBorder(8, 5, 8, 5));
+ secondCard.add(midPanel, BorderLayout.CENTER);
+ // Activate buttons if a tileset is selected
+ _setsTable.getSelectionModel().addListSelectionListener(
+ new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+ _deleteSetButton.setEnabled(!lsm.isSelectionEmpty());
+ }
+ });
+
+ // button panel at bottom
+ buttonPanel = new JPanel();
+ // left group
+ JPanel leftPanel = new JPanel();
+ leftPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+ _deleteSetButton = new JButton(I18nManager.getText("button.delete"));
+ _deleteSetButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ showDeleteCard();
+ }
+ });
+ leftPanel.add(_deleteSetButton);
+ // right group
+ JPanel rightPanel = new JPanel();
+ rightPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton closeButton = new JButton(I18nManager.getText("button.close"));
+ closeButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (_dialog != null) _dialog.dispose();
+ }
+ });
+ rightPanel.add(closeButton);
+ buttonPanel.add(leftPanel, BorderLayout.WEST);
+ buttonPanel.add(rightPanel, BorderLayout.EAST);
+ secondCard.add(buttonPanel, BorderLayout.SOUTH);
+ dialogPanel.add(secondCard, "card2");
+
+ // Make third card including delete options
+ JPanel thirdCard = new JPanel();
+ thirdCard.setLayout(new BorderLayout());
+ // main panel
+ JPanel mainPanel = new JPanel();
+ GridBagLayout gridbag = new GridBagLayout();
+ GridBagConstraints c = new GridBagConstraints();
+ mainPanel.setLayout(gridbag);
+ c.gridx = 0; c.gridy = 0;
+ c.gridheight = 1; c.gridwidth = 2;
+ c.weightx = 0.0; c.weighty = 0.0;
+ c.anchor = GridBagConstraints.FIRST_LINE_START;
+ _tileSetLabel = new JLabel("tileset label");
+ mainPanel.add(_tileSetLabel, c);
+ c.gridx = 0; c.gridy = 1;
+ c.ipady = 20;
+ _zoomLabel = new JLabel("zoom label");
+ mainPanel.add(_zoomLabel, c);
+
+ JRadioButton deleteOldRadio = new JRadioButton(I18nManager.getText("dialog.diskcache.deleteold"));
+ _deleteAllRadio = new JRadioButton(I18nManager.getText("dialog.diskcache.deleteall"));
+ ButtonGroup bGroup = new ButtonGroup();
+ bGroup.add(_deleteAllRadio);
+ bGroup.add(deleteOldRadio);
+ _deleteAllRadio.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ enableAgeFields();
+ }
+ });
+ c.gridx = 0; c.gridy = 2;
+ c.anchor = GridBagConstraints.LINE_START;
+ c.ipady = 0;
+ mainPanel.add(deleteOldRadio, c);
+ c.gridwidth = 1;
+ c.gridx = 0; c.gridy = 3;
+ c.insets = new Insets(0, 40, 0, 0);
+ _ageLabel = new JLabel("Maximum age (days)");
+ mainPanel.add(_ageLabel, c);
+ _daysField = new WholeNumberField(2);
+ _daysField.setMinimumSize(new Dimension(20, 1));
+ _daysField.setText("30"); // default is 30 days
+ c.gridx = 1; c.gridy = 3;
+ c.ipadx = 20;
+ c.insets = new Insets(0, 15, 0, 0);
+ mainPanel.add(_daysField, c);
+ c.gridx = 0; c.gridy = 4;
+ c.gridwidth = 2;
+ c.ipadx = 0;
+ c.insets = new Insets(0, 0, 0, 0);
+ mainPanel.add(_deleteAllRadio, c);
+ _deleteAllRadio.setSelected(true);
+ thirdCard.add(mainPanel, BorderLayout.CENTER);
+ // button panel
+ buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton deleteButton = new JButton(I18nManager.getText("button.delete"));
+ deleteButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ deleteCurrentSets();
+ }
+ });
+ buttonPanel.add(deleteButton);
+ cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // Go back to second card
+ _cards.show(_cardPanel, "card2");
+ }
+ });
+ buttonPanel.add(cancelButton);
+ thirdCard.add(buttonPanel, BorderLayout.SOUTH);
+ dialogPanel.add(thirdCard, "card3");
+ return dialogPanel;
+ }
+
+
+ /**
+ * Construct the model in a separate thread
+ * and go on to the second panel of the dialog
+ */
+ public void run()
+ {
+ // Check if directory has anything in it
+ _model = new TileCacheModel(_cacheDir);
+ _model.buildTileSets();
+ if (_model.isAborted()) return;
+
+ if (_model.getNumTileSets() <= 0)
+ {
+ _app.showErrorMessage(getNameKey(), "error.cache.empty");
+ return;
+ }
+
+ // Set controls according to current config
+ _setsTable.setModel(new TileSetTableModel(_model));
+ _deleteSetButton.setEnabled(false);
+ // Set column widths after model has been set
+ _setsTable.getColumnModel().getColumn(0).setPreferredWidth(220);
+ _setsTable.getColumnModel().getColumn(1).setPreferredWidth(200);
+ // Show second panel
+ _cards.next(_cardPanel);
+ }
+
+ /**
+ * Prepare and show the delete panel
+ */
+ private void showDeleteCard()
+ {
+ // set tileset label
+ int numSelected = 0;
+ String desc = null;
+ RowInfo totals = new RowInfo();
+ for (int i=0; i<_setsTable.getRowCount(); i++) {
+ if (_setsTable.isRowSelected(i))
+ {
+ if (desc == null) desc = _model.getTileSet(i).getPath();
+ totals.addRow(_model.getTileSet(i).getRowInfo());
+ numSelected++;
+ }
+ }
+ if (numSelected == 0) return;
+ String tileSetDesc = (numSelected == 1?desc:I18nManager.getText("dialog.diskcache.tileset.multiple"));
+ _tileSetLabel.setText(I18nManager.getText("dialog.diskcache.tileset") + " : "
+ + tileSetDesc);
+ _zoomLabel.setText(I18nManager.getText("dialog.diskcache.table.zoom") + " : "
+ + totals.getZoomRange());
+
+ // enable/disable edit fields
+ enableAgeFields();
+ // show the next card
+ _cards.next(_cardPanel);
+ }
+
+ /**
+ * Enable or disable the fields for entering tile age
+ */
+ private void enableAgeFields()
+ {
+ boolean showAgeBoxes = !_deleteAllRadio.isSelected();
+ _ageLabel.setEnabled(showAgeBoxes);
+ _daysField.setEnabled(showAgeBoxes);
+ }
+
+ /**
+ * Try to delete all the files in the currently selected tilesets
+ * (Maybe more than one tileset is selected in the table)
+ */
+ private void deleteCurrentSets()
+ {
+ // Determine age limit if given
+ int ageLimit = -1;
+ if (!_deleteAllRadio.isSelected()) {
+ ageLimit = _daysField.getValue();
+ }
+ // Loop over selected tilesets and delete them
+ int totalDeleted = 0;
+ for (int i=0; i<_setsTable.getRowCount(); i++)
+ {
+ if (_setsTable.isRowSelected(i))
+ {
+ File dir = new File(_model.getCacheDir(), _model.getTileSet(i).getPath());
+ if (dir.exists())
+ {
+ int numFilesDeleted = deleteFilesFrom(dir, ageLimit);
+ if (numFilesDeleted > 0) {
+ totalDeleted += numFilesDeleted;
+ }
+ }
+ }
+ }
+ if (totalDeleted > 0)
+ {
+ // Show confirmation message
+ JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.diskcache.deleted1")
+ + " " + totalDeleted + " " + I18nManager.getText("dialog.diskcache.deleted2"),
+ I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ // reload model
+ _cards.first(_cardPanel);
+ new Thread(this).start();
+ }
+ else {
+ _app.showErrorMessage(getNameKey(), "error.cache.cannotdelete");
+ }
+ }
+
+
+ /**
+ * Delete recursively all files which are older than the age limit
+ * @param inDir directory to delete from
+ * @param inMaxDays age limit in days
+ * @return number of files deleted
+ */
+ private static int deleteFilesFrom(File inDir, int inMaxDays)
+ {
+ int numDeleted = 0;
+ long now = System.currentTimeMillis();
+ if (inDir.exists() && inDir.isDirectory())
+ {
+ for (File subdir : inDir.listFiles())
+ {
+ if (subdir.isDirectory()) {
+ numDeleted += deleteFilesFrom(subdir, inMaxDays);
+ }
+ else if (subdir.isFile() && subdir.exists() && _TILEFILTER.accept(subdir))
+ {
+ long fileAge = (now - subdir.lastModified()) / 1000 / 60 / 60 / 24;
+ if (inMaxDays < 0 || fileAge > inMaxDays)
+ {
+ if (subdir.delete()) {
+ numDeleted++;
+ }
+ }
+ }
+ }
+ // Try to delete the directory (doesn't work if not empty)
+ inDir.delete();
+ }
+ return numDeleted;
+ }
+}
--- /dev/null
+package tim.prune.function.cache;
+
+/**
+ * Class to hold the information for a single table row.
+ * Used to describe a tileset or for a single zoom level of a tileset.
+ */
+public class RowInfo
+{
+ private int _zoom = -1;
+ private int _minZoom = -1, _maxZoom = -1;
+ private int _numTiles = 0;
+ private long _totalSize = 0L;
+ private boolean _unexpected = false;
+
+
+ /**
+ * Set the zoom level
+ * @param inZoom zoom level
+ */
+ public void setZoom(int inZoom) {
+ _zoom = inZoom;
+ }
+
+ /**
+ * Add a zoom level and adjust max/min
+ * @param inZoom zoom level
+ */
+ public void addZoom(int inZoom)
+ {
+ if (_minZoom < 0 || _minZoom > inZoom)
+ _minZoom = inZoom;
+ if (_maxZoom < inZoom)
+ _maxZoom = inZoom;
+ }
+
+ /**
+ * @return the zoom level
+ */
+ public int getZoom() {
+ return _zoom;
+ }
+
+ /**
+ * @return the zoom range as a string
+ */
+ public String getZoomRange()
+ {
+ if (_minZoom < 0 && _maxZoom < 0) return "";
+ if (_minZoom == _maxZoom || _maxZoom < 0) return "" + _minZoom;
+ if (_minZoom < 0) return "" + _maxZoom;
+ return "" + _minZoom + " - " + _maxZoom;
+ }
+
+ /**
+ * Add a single tile of the given size
+ * @param inSize size in bytes
+ */
+ public void addTile(long inSize) {
+ addTiles(1, inSize);
+ }
+
+ /**
+ * Add the given tiles
+ * @param inNumTiles number of tiles to add
+ * @param inSize total size of the tiles in bytes
+ */
+ public void addTiles(int inNumTiles, long inSize)
+ {
+ _numTiles += inNumTiles;
+ _totalSize += inSize;
+ }
+
+ /**
+ * @return the total number of tiles found
+ */
+ public int getNumTiles() {
+ return _numTiles;
+ }
+
+ /**
+ * @return the total size of the tiles in bytes
+ */
+ public long getTotalSize() {
+ return _totalSize;
+ }
+
+ /**
+ * Mark that an unexpected file or directory was found
+ */
+ public void foundUnexpected() {
+ _unexpected = true;
+ }
+
+ /**
+ * @return true if any unexpected files or directories were found
+ */
+ public boolean wasUnexpected() {
+ return _unexpected;
+ }
+
+ /**
+ * Add the given RowInfo object to this one
+ * @param inOther other row object
+ */
+ public void addRow(RowInfo inOther)
+ {
+ if (inOther == null)
+ return;
+ _numTiles += inOther._numTiles;
+ _totalSize += inOther._totalSize;
+ // TODO: Max age
+ // Zoom range
+ if (inOther._minZoom > 0)
+ addZoom(inOther._minZoom);
+ if (inOther._maxZoom > 0)
+ addZoom(inOther._maxZoom);
+ if (inOther._zoom > 0)
+ addZoom(inOther._zoom);
+ _unexpected = _unexpected || inOther._unexpected;
+ }
+}
--- /dev/null
+package tim.prune.function.cache;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import tim.prune.gui.map.MapSource;
+import tim.prune.gui.map.MapSourceLibrary;
+
+/**
+ * Class to obtain and hold information about the current
+ * tile cache including its subdirectories
+ */
+public class TileCacheModel
+{
+ /** Cache directory */
+ private File _cacheDir = null;
+ /** Array of tilesets */
+ private ArrayList<TileSet> _tileSets = null;
+ /** Summary information */
+ private RowInfo _summaryRow = null;
+ /** Cancelled flag */
+ private boolean _cancelled = false;
+
+
+ /**
+ * Constructor
+ * @param inDir start directory
+ */
+ public TileCacheModel(File inDir)
+ {
+ if (inDir != null && inDir.exists() && inDir.isDirectory() && inDir.canRead()) {
+ _cacheDir = inDir;
+ }
+ _cancelled = false;
+ }
+
+ /**
+ * Build the tilesets by searching recursively
+ */
+ public void buildTileSets()
+ {
+ if (_cacheDir == null) return;
+
+ _tileSets = new ArrayList<TileSet>();
+ // go through subdirectories, if any
+ File[] subdirs = _cacheDir.listFiles();
+ for (File subdir : subdirs)
+ {
+ if (subdir != null && subdir.isDirectory() && subdir.exists() && subdir.canRead()
+ && !_cancelled)
+ {
+ getTileSets(subdir, null, _tileSets);
+ }
+ }
+ // Loop over found tile sets and create summary rowinfo
+ _summaryRow = new RowInfo();
+ for (TileSet ts : _tileSets)
+ {
+ _summaryRow.addRow(ts.getRowInfo());
+ }
+ }
+
+ /**
+ * Get all the tilesets from the given directory
+ * @param inDir directory to search
+ * @return array of TileSet objects
+ */
+ private static void getTileSets(File inDir, String inParentPath, ArrayList<TileSet> inTsList)
+ {
+ final String wholePath = (inParentPath == null ? "" : inParentPath)
+ + inDir.getName() + File.separator;
+ // See if any configured backgrounds use this directory
+ // or if the directories match OSM structure
+ String usedByDesc = matchConfig(wholePath);
+ boolean tsFound = false;
+ if (usedByDesc != null || looksLikeCacheDir(inDir))
+ {
+ TileSet ts = new TileSet(inDir, wholePath, usedByDesc);
+ if (usedByDesc != null || ts.getRowInfo().getNumTiles() > 0)
+ {
+ tsFound = true;
+ inTsList.add(ts);
+ }
+ }
+ // If a tileset wasn't found, look through subdirectories
+ if (!tsFound)
+ {
+ // Go through subdirectories and look at each of them too
+ File[] subdirs = inDir.listFiles();
+ if (subdirs != null) {
+ for (File subdir : subdirs)
+ {
+ if (subdir != null && subdir.exists() && subdir.isDirectory()
+ && subdir.canRead())
+ {
+ getTileSets(subdir, wholePath, inTsList);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Match the given directory name to find the configs which use it
+ * @param inName name of directory to match
+ * @return null if not used, otherwise comma-separated list of background names
+ */
+ private static String matchConfig(String inName)
+ {
+ if (inName == null || inName.equals(""))
+ return null;
+ String usedBy = null;
+ for (int i=0; i<MapSourceLibrary.getNumSources(); i++)
+ {
+ MapSource ms = MapSourceLibrary.getSource(i);
+ for (int l=0; l<2; l++)
+ {
+ String msdir = ms.getSiteName(l);
+ if (msdir != null && msdir.equals(inName))
+ {
+ if (usedBy == null)
+ usedBy = ms.getName();
+ else
+ usedBy = usedBy + ", " + ms.getName();
+ }
+ }
+ }
+ return usedBy;
+ }
+
+ /**
+ * @param inDir directory to test
+ * @return true if the subdirectories meet the normal osm layout
+ */
+ private static boolean looksLikeCacheDir(File inDir)
+ {
+ // look for at least one numeric directory, nothing else
+ boolean numFound = false;
+ if (inDir != null && inDir.exists() && inDir.isDirectory() && inDir.canRead())
+ {
+ for (File subdir : inDir.listFiles())
+ {
+ // Only consider readable things which exist
+ if (subdir != null && subdir.exists() && subdir.canRead())
+ {
+ // subdirectories should have numeric names (for the zoom levels)
+ if (subdir.isDirectory() && TileSet.isNumeric(subdir.getName())
+ && subdir.getName().length() < 3)
+ {
+ numFound = true;
+ }
+ else return false; // either a file or non-numeric directory
+ }
+ }
+ }
+ return numFound;
+ }
+
+ /**
+ * @return cache directory
+ */
+ public File getCacheDir() {
+ return _cacheDir;
+ }
+
+ /**
+ * @return number of tile sets
+ */
+ public int getNumTileSets()
+ {
+ if (_tileSets == null) return 0;
+ return _tileSets.size();
+ }
+
+ /**
+ * @return the total number of tile images found
+ */
+ public int getTotalTiles()
+ {
+ return _summaryRow.getNumTiles();
+ }
+
+ /**
+ * @return the total number of bytes taken up with tile images
+ */
+ public long getTotalBytes()
+ {
+ return _summaryRow.getTotalSize();
+ }
+
+ /**
+ * @param inIndex index of tileset
+ * @return requested tileset
+ */
+ public TileSet getTileSet(int inIndex)
+ {
+ if (inIndex >= 0 && inIndex < getNumTileSets()) {
+ return _tileSets.get(inIndex);
+ }
+ return null;
+ }
+
+ /**
+ * Cancel the search
+ */
+ public void cancel() {
+ _cancelled = true;
+ }
+
+ /**
+ * @return true if search was cancelled
+ */
+ public boolean isAborted() {
+ return _cancelled;
+ }
+}
--- /dev/null
+package tim.prune.function.cache;
+
+import tim.prune.load.GenericFileFilter;
+
+/**
+ * File filter for map tiles
+ */
+public class TileFilter extends GenericFileFilter
+{
+ /** Constructor */
+ public TileFilter()
+ {
+ super("filetype.jpeg", new String[] {"jpg", "png", "gif", "temp"});
+ }
+}
--- /dev/null
+package tim.prune.function.cache;
+
+import java.io.File;
+import java.util.ArrayList;
+
+
+/**
+ * Class to hold information about a single tile set
+ * within the overall Tile Cache.
+ */
+public class TileSet
+{
+ /** Summary row info for whole tileset */
+ private RowInfo _rowInfo = null;
+ /** Path relative to mapcache root */
+ private String _path = null;
+ /** Comma-separated list of configs using this tileset */
+ private String _usedBy = null;
+ /** List of infos for each zoom level */
+ private ArrayList<RowInfo> _zoomLevels = null;
+
+
+ /**
+ * Constructor
+ * @param inDir directory of tileset
+ * @param inPath String describing relative path from cache root
+ * @param inUsedBy String describing which configs use this Tileset
+ */
+ public TileSet(File inDir, String inPath, String inUsedBy)
+ {
+ _path = inPath;
+ _usedBy = inUsedBy;
+ _rowInfo = new RowInfo();
+ _zoomLevels = new ArrayList<RowInfo>();
+ // Go through zoom directories and construct row info objects
+ if (inDir != null && inDir.exists() && inDir.isDirectory() && inDir.canRead())
+ {
+ for (File subdir : inDir.listFiles())
+ {
+ if (subdir != null && subdir.exists() && subdir.isDirectory()
+ && subdir.canRead() && isNumeric(subdir.getName()))
+ {
+ RowInfo row = makeRowInfo(subdir);
+ _zoomLevels.add(row);
+ _rowInfo.addRow(row);
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if a directory name is numeric
+ * @param inName name of directory
+ * @return true if it only contains characters 0-9
+ */
+ public static boolean isNumeric(String inName)
+ {
+ if (inName == null || inName.equals("")) return false;
+ for (int i=0; i<inName.length(); i++)
+ {
+ char a = inName.charAt(i);
+ if (a < '0' || a > '9') return false;
+ }
+ return true;
+ }
+
+ /**
+ * Make a RowInfo object from the given directory
+ * @param inDir directory for a single zoom level
+ * @return RowInfo object describing files and size
+ */
+ private static RowInfo makeRowInfo(File inDir)
+ {
+ RowInfo row = new RowInfo();
+ row.setZoom(Integer.parseInt(inDir.getName()));
+ for (File subdir : inDir.listFiles())
+ {
+ if (subdir != null && subdir.exists() && subdir.isDirectory()
+ && subdir.canRead() && isNumeric(subdir.getName()))
+ {
+ // Found a directory of images (finally!)
+ for (File f : subdir.listFiles())
+ {
+ if (f != null && f.exists() && f.isFile() && f.canRead())
+ {
+ final String filename = f.getName();
+ int dotpos = filename.lastIndexOf('.');
+ if (dotpos > 0 && isNumeric(filename.substring(0, dotpos))) {
+ row.addTile(f.length());
+ }
+ }
+ }
+ }
+ }
+ return row;
+ }
+
+ /**
+ * @return row info object
+ */
+ public RowInfo getRowInfo() {
+ return _rowInfo;
+ }
+
+ /** @return relative path to tileset */
+ public String getPath() {
+ return _path;
+ }
+
+ /** @return users of tileset */
+ public String getUsedBy() {
+ return _usedBy;
+ }
+}
--- /dev/null
+package tim.prune.function.cache;
+
+import javax.swing.table.AbstractTableModel;
+
+import tim.prune.I18nManager;
+
+/**
+ * Class to act as a table model for the list of tile sets
+ */
+public final class TileSetTableModel extends AbstractTableModel
+{
+ /** Model from which values are drawn */
+ private TileCacheModel _model = null;
+
+
+ /**
+ * Constructor
+ * @param inModel model to use
+ */
+ public TileSetTableModel(TileCacheModel inModel) {
+ _model = inModel;
+ }
+
+ /** @return the column count (always constant) */
+ public int getColumnCount() {
+ return 5;
+ }
+
+ /** @return name of specified column */
+ public String getColumnName(int inColumnIndex)
+ {
+ switch (inColumnIndex)
+ {
+ case 0: return I18nManager.getText("dialog.diskcache.table.path");
+ case 1: return I18nManager.getText("dialog.diskcache.table.usedby");
+ case 2: return I18nManager.getText("dialog.diskcache.table.zoom");
+ case 3: return I18nManager.getText("dialog.diskcache.table.tiles");
+ case 4: return I18nManager.getText("dialog.diskcache.table.megabytes");
+ }
+ return "";
+ }
+
+ /**
+ * @return number of rows in the table
+ */
+ public int getRowCount()
+ {
+ if (_model == null)
+ return 0;
+ return _model.getNumTileSets();
+ }
+
+ /**
+ * @param inRowIndex row index
+ * @param inColumnIndex column index
+ * @return the value of the specified cell
+ */
+ public Object getValueAt(int inRowIndex, int inColumnIndex)
+ {
+ if (_model != null && inColumnIndex >= 0 && inColumnIndex < getColumnCount())
+ {
+ TileSet set = _model.getTileSet(inRowIndex);
+ if (set != null)
+ {
+ switch (inColumnIndex)
+ {
+ case 0: return set.getPath();
+ case 1: return set.getUsedBy();
+ case 2: return set.getRowInfo().getZoomRange();
+ case 3: return "" + set.getRowInfo().getNumTiles();
+ case 4: return "" + (set.getRowInfo().getTotalSize() / 1024 / 1024) + " MB";
+ }
+ }
+ }
+ return null;
+ }
+}
FileWriter tempFileWriter = null;
try {
tempFileWriter = new FileWriter(tempFile);
- tempFileWriter.write("# Temporary data file for Prune charts\n\n");
+ tempFileWriter.write("# Temporary data file for GpsPrune charts\n\n");
for (int i=0; i<inTrack.getNumPoints(); i++) {
if (xValues.hasData(i) && yValues.hasData(i)) {
tempFileWriter.write("" + xValues.getData(i) + ", " + yValues.getData(i) + "\n");
new DuplicatePointAlgorithm(_track, details, changeListener),
new ClosePointsAlgorithm(_track, details, changeListener),
new WackyPointAlgorithm(_track, details, changeListener),
- new SingletonAlgorithm(_track, details, changeListener)
+ new SingletonAlgorithm(_track, details, changeListener),
+ new DouglasPeuckerAlgorithm(_track, details, changeListener)
};
}
--- /dev/null
+package tim.prune.function.compress;
+
+import java.awt.Component;
+import java.awt.event.ActionListener;
+
+import tim.prune.data.DataPoint;
+import tim.prune.data.Track;
+
+/**
+ * Douglas-Peucker algorithm for compresssion
+ */
+public class DouglasPeuckerAlgorithm extends SingleParameterAlgorithm
+{
+ /**
+ * Constructor
+ * @param inTrack track object
+ * @param inDetails track details object
+ * @param inListener listener to attach to activation control
+ */
+ public DouglasPeuckerAlgorithm(Track inTrack, TrackDetails inDetails, ActionListener inListener)
+ {
+ super(inTrack, inDetails, inListener);
+ }
+
+ /**
+ * Perform the compression and work out which points should be deleted
+ * @param inFlags deletion flags from previous algorithms
+ * @return number of points deleted
+ */
+ protected int compress(boolean[] inFlags)
+ {
+ // Parse parameter
+ double param = getParameter();
+ // Use 1/x if x greater than 1
+ if (param > 1.0) param = 1.0 / param;
+ if (param <= 0.0 || param >= 1.0) {
+ // Parameter isn't valid, don't delete any
+ return 0;
+ }
+ double threshold = _trackDetails.getTrackSpan() * param;
+
+ int numPoints = _track.getNumPoints();
+ int origNumDeleted = countFlags(inFlags);
+ // Convert inFlags into keepFlags
+ int[] keepFlags = new int[numPoints];
+ int segStart = -1, segEnd = -1;
+ // Loop over all points in track
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint currPoint = _track.getPoint(i);
+ if (currPoint.getSegmentStart())
+ {
+ // new segment found, so process previous one
+ if (segStart > -1 && segEnd > segStart)
+ {
+ keepFlags[segEnd] = 1; // keep
+ compressSegment(keepFlags, segStart, segEnd, threshold);
+ segStart = segEnd = -1;
+ }
+ }
+ if (inFlags[i]) keepFlags[i] = -1; // already deleted
+ else if (currPoint.isWaypoint() || currPoint.hasMedia() || currPoint.getSegmentStart()) {
+ keepFlags[i] = 1; // keep
+ }
+ // Don't consider points which are already marked as deleted, ignore waypoints
+ if (!inFlags[i] && !currPoint.isWaypoint())
+ {
+ // remember starts and ends
+ if (segStart < 0) {segStart = i;}
+ else {segEnd = i;}
+ }
+ }
+ // Last segment, if any
+ if (segStart >= 0 && segEnd > segStart) {
+ keepFlags[segEnd] = 1; // keep
+ compressSegment(keepFlags, segStart, segEnd, threshold);
+ }
+ // Convert keepFlags back into inFlags
+ for (int i=1; i<numPoints; i++) {
+ if (keepFlags[i] < 1) inFlags[i] = true;
+ }
+ return countFlags(inFlags) - origNumDeleted;
+ }
+
+
+ /**
+ * Count the number of true flags in the given array
+ * @param inFlags array of boolean flags
+ * @return number of flags which are set to true
+ */
+ private static int countFlags(boolean[] inFlags)
+ {
+ int numDeleted = 0;
+ for (int i=0; i<inFlags.length; i++) {
+ if (inFlags[i]) numDeleted++;
+ }
+ return numDeleted;
+ }
+
+ /**
+ * Compress the given segment (recursively)
+ * @param inFlags int array of deletion flags for entire track
+ * @param inSegStart index of start of segment
+ * @param inSegEnd index of end of segment
+ * @param inThreshold threshold to use
+ */
+ private void compressSegment(int[] inFlags, int inSegStart, int inSegEnd,
+ double inThreshold)
+ {
+ //System.out.println("Compress segment " + inSegStart + "-" + inSegEnd);
+ final int numPoints = inSegEnd - inSegStart + 1;
+ if (numPoints < 3) {return;} // segment too short to compress
+ // Calculate parameters of straight line between first and last
+ XYpoint startxy = new XYpoint(_track.getX(inSegStart), _track.getY(inSegStart));
+ XYpoint endxy = new XYpoint(_track.getX(inSegEnd), _track.getY(inSegEnd));
+ XYpoint ab = startxy.vectorTo(endxy);
+ final double dist2AB = ab.len2();
+ // create unit vector perpendicular to AB
+ final double distAB = ab.len();
+ XYpoint perpendicular = new XYpoint(ab.y/distAB, -ab.x/distAB);
+
+ double maxDist = -1.0, dist = -1.0;
+ int furthestIndex = -1;
+ for (int i=inSegStart+1; i<inSegEnd; i++)
+ {
+ if (inFlags[i] == 0) // unknown status
+ {
+ XYpoint currPoint = new XYpoint(_track.getX(i), _track.getY(i));
+ XYpoint ac = startxy.vectorTo(currPoint);
+ double distAP = ab.dot(ac) / dist2AB;
+ // calc distance from point to line depending on distAP
+ if (distAP < 0.0) {
+ dist = ac.len(); // outside line segment AB on the A side
+ }
+ else if (distAP > 1.0) {
+ dist = endxy.vectorTo(currPoint).len(); // outside on the B side
+ }
+ else {
+ // P lies between A and B so use dot product
+ dist = Math.abs(perpendicular.dot(ac));
+ }
+ if (dist > maxDist)
+ {
+ maxDist = dist;
+ furthestIndex = i;
+ }
+ }
+ }
+ // Check furthest point and see if it's further than the threshold
+ if (maxDist > inThreshold)
+ {
+ inFlags[furthestIndex] = 1;
+ // Make recursive calls for bit before and bit after kept point
+ compressSegment(inFlags, inSegStart, furthestIndex, inThreshold);
+ compressSegment(inFlags, furthestIndex, inSegEnd, inThreshold);
+ }
+ }
+
+
+ /**
+ * @return specific gui components for dialog
+ */
+ protected Component getSpecificGuiComponents()
+ {
+ return getSpecificGuiComponents("dialog.compress.douglaspeucker.paramdesc", "2000");
+ }
+
+ /**
+ * @return title key for box
+ */
+ protected String getTitleTextKey()
+ {
+ return "dialog.compress.douglaspeucker.title";
+ }
+}
--- /dev/null
+package tim.prune.function.compress;
+
+/**
+ * Basic class to hold x and y coordinates
+ * for a point or a vector
+ */
+public class XYpoint
+{
+ // x and y coordinates
+ public double x = 0.0, y = 0.0;
+
+ /**
+ * Empty constructor
+ */
+ public XYpoint() {
+ this(0.0, 0.0);
+ }
+
+ /**
+ * Constructor
+ * @param inX x value
+ * @param inY y value
+ */
+ public XYpoint(double inX, double inY) {
+ x = inX; y = inY;
+ }
+
+ /**
+ * @param inOther other vector
+ * @return scalar dot product
+ */
+ public double dot(XYpoint inOther) {
+ return (x * inOther.x + y * inOther.y);
+ }
+
+ /** @return length of vector */
+ public double len() {return Math.sqrt(len2());}
+
+ /** @return squared length of vector */
+ public double len2() {return (x*x + y*y);}
+
+ /**
+ * @param inOther other point object
+ * @return vector from this one to the other one
+ */
+ public XYpoint vectorTo(XYpoint inOther) {
+ return new XYpoint(inOther.x - x, inOther.y - y);
+ }
+}
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.net.URLConnection;
import java.util.ArrayList;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import tim.prune.App;
+import tim.prune.GpsPrune;
import tim.prune.I18nManager;
import tim.prune.load.xml.XmlFileLoader;
import tim.prune.load.xml.ZipFileLoader;
private static final int RESULTS_PER_PAGE = 20;
/** Maximum number of results to get */
private static final int MAX_RESULTS = 60;
+ /** New API key (specific to this program) */
+ private static final String GPSIES_API_KEY = "oumgvvbckiwpvsnb";
/**
// Loop for each page of the results
do
{
- String urlString = "http://www.gpsies.com/api.do?BBOX=" +
+ String urlString = "http://ws.gpsies.com/api.do?BBOX=" +
coords[1] + "," + coords[0] + "," + coords[3] + "," + coords[2] +
- "&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage;
+ "&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage +
+ "&key=" + GPSIES_API_KEY;
// Parse the returned XML with a special handler
GpsiesXmlHandler xmlHandler = new GpsiesXmlHandler();
try
{
url = new URL(urlString);
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
- inStream = url.openStream();
+ URLConnection conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", "GpsPrune v" + GpsPrune.VERSION_NUMBER);
+ inStream = conn.getInputStream();
saxParser.parse(inStream, xmlHandler);
}
catch (Exception e) {
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
_cancelled = true;
+ _dialog.dispose();
}
});
buttonPanel.add(cancelButton);
_progressBar.setMaximum(inTileList.size());
_progressBar.setIndeterminate(inTileList.size() <= 1);
_progressBar.setValue(0);
+ String errorMessage = null;
// Get urls for each tile
URL[] urls = TileFinder.getUrls(inTileList);
for (int t=0; t<inTileList.size() && !_cancelled; t++)
if (urls[t] != null)
{
SrtmTile tile = inTileList.get(t);
- // System.out.println("tile " + t + " of " + tileList.size() + " = " + urls[t].toString());
- try {
+ try
+ {
_progressBar.setValue(t);
final int ARRLENGTH = 1201*1201;
int[] heights = new int[ARRLENGTH];
}
}
catch (IOException ioe) {
- //System.err.println("eek - " + ioe.getMessage());
+ errorMessage = ioe.getClass().getName() + " - " + ioe.getMessage();
}
}
}
_dialog.dispose();
+ if (_cancelled) {return;}
if (numAltitudesFound > 0)
{
// Inform app including undo information
_app.completeFunction(undo, I18nManager.getText("confirm.lookupsrtm1") + " " + numAltitudesFound
+ " " + I18nManager.getText("confirm.lookupsrtm2"));
}
+ else if (errorMessage != null) {
+ _app.showErrorMessageNoLookup(getNameKey(), errorMessage);
+ }
else if (inTileList.size() > 0) {
_app.showErrorMessage(getNameKey(), "error.lookupsrtm.nonefound");
}
/**
* Class to update the supplied progress bar on the basis of
- * the currently playing audio file (if any)
+ * the currently playing audio clip (if any)
*/
public class AudioListener implements Runnable, ActionListener
{
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.data.Altitude;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.Coordinate;
import tim.prune.data.DataPoint;
import tim.prune.data.Distance;
private PhotoThumbnail _photoThumbnail = null;
private JLabel _photoTimestampLabel = null;
private JLabel _photoConnectedLabel = null;
+ private JLabel _photoBearingLabel = null;
private JPanel _rotationButtons = null;
// Audio details
_photoDetailsPanel.add(_photoTimestampLabel);
_photoConnectedLabel = new JLabel("");
_photoDetailsPanel.add(_photoConnectedLabel);
+ _photoBearingLabel = new JLabel("");
+ _photoDetailsPanel.add(_photoBearingLabel);
_photoThumbnail = new PhotoThumbnail();
_photoThumbnail.setVisible(false);
_photoThumbnail.setPreferredSize(new Dimension(100, 100));
_photoLabel.setText(I18nManager.getText("details.nophoto"));
_photoTimestampLabel.setText("");
_photoConnectedLabel.setText("");
+ _photoBearingLabel.setText("");
_photoThumbnail.setVisible(false);
_rotationButtons.setVisible(false);
}
else
{
if (currentPhoto == null) {currentPhoto = currentPoint.getPhoto();}
- _photoLabel.setText(I18nManager.getText("details.photofile") + ": " + currentPhoto.getFile().getName());
- _photoTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText());
+ _photoLabel.setText(I18nManager.getText("details.photofile") + ": " + currentPhoto.getName());
+ _photoTimestampLabel.setText(currentPhoto.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText()):"");
_photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
+ (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
+ if (currentPhoto.getBearing() >= 0.0 && currentPhoto.getBearing() <= 360.0)
+ {
+ _photoBearingLabel.setText(I18nManager.getText("details.photo.bearing") + ": "
+ + (int) currentPhoto.getBearing() + " \u00B0");
+ }
+ else _photoBearingLabel.setText("");
_photoThumbnail.setVisible(true);
_photoThumbnail.setPhoto(currentPhoto);
_rotationButtons.setVisible(true);
// audio details
_audioDetailsPanel.setVisible(_trackInfo.getAudioList().getNumAudios() > 0);
- AudioFile currentAudio = _trackInfo.getAudioList().getAudio(_trackInfo.getSelection().getCurrentAudioIndex());
+ AudioClip currentAudio = _trackInfo.getAudioList().getAudio(_trackInfo.getSelection().getCurrentAudioIndex());
if (currentAudio == null) {
_audioLabel.setText(I18nManager.getText("details.noaudio"));
_audioTimestampLabel.setText("");
}
else
{
- _audioLabel.setText(LABEL_AUDIO_FILE + currentAudio.getFile().getName());
- _audioTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText());
+ _audioLabel.setText(LABEL_AUDIO_FILE + currentAudio.getName());
+ _audioTimestampLabel.setText(currentAudio.hasTimestamp()?(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText()):"");
int audioLength = currentAudio.getLengthInSeconds();
_audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength));
_audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
public static final String ROTATE_RIGHT = "rotate_right_icon.png";
/** Icon for showing photo popup */
public static final String SHOW_DETAILS = "show_details_icon.gif";
- /** Icon for playing audio file */
+ /** Icon for playing audio clip */
public static final String PLAY_AUDIO = "play_audio.gif";
- /** Icon for stopping the current audio file */
+ /** Icon for stopping the current audio clip */
public static final String STOP_AUDIO = "stop_audio.gif";
/**
import javax.swing.AbstractListModel;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
import tim.prune.data.MediaList;
/**
*/
public Object getElementAt(int inIndex)
{
- MediaFile m = _media.getMedia(inIndex);
+ MediaObject m = _media.getMedia(inIndex);
// * means modified since loading
- return (m.getCurrentStatus() == m.getOriginalStatus()?"":"* ") + m.getFile().getName();
+ return (m.getCurrentStatus() == m.getOriginalStatus()?"":"* ") + m.getName();
}
/**
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.Photo;
+import tim.prune.data.RecentFile;
+import tim.prune.data.RecentFileList;
import tim.prune.data.Selection;
import tim.prune.data.Track;
import tim.prune.data.TrackInfo;
private JMenuItem _exportGpxItem = null;
private JMenuItem _exportPovItem = null;
private JMenuItem _exportSvgItem = null;
+ private JMenu _recentFileMenu = null;
private JMenuItem _undoItem = null;
private JMenuItem _clearUndoItem = null;
private JMenuItem _editPointItem = null;
private JMenuItem _correlateAudiosItem = null;
private JMenuItem _selectNoAudioItem = null;
private JCheckBoxMenuItem _onlineCheckbox = null;
+ private JCheckBoxMenuItem _autosaveSettingsCheckbox = null;
// ActionListeners for reuse by menu and toolbar
private ActionListener _openFileAction = null;
};
openMenuItem.addActionListener(_openFileAction);
fileMenu.add(openMenuItem);
+ // import through gpsbabel
+ JMenuItem importBabelItem = makeMenuItem(FunctionLibrary.FUNCTION_IMPORTBABEL);
+ fileMenu.add(importBabelItem);
// Add photos
JMenuItem addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
_addPhotoAction = new ActionListener() {
};
addPhotosMenuItem.addActionListener(_addPhotoAction);
fileMenu.add(addPhotosMenuItem);
- // Add audio files
+ // Add audio clips
JMenuItem addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
fileMenu.add(addAudioMenuItem);
+ // recent files
+ _recentFileMenu = new JMenu(I18nManager.getText("menu.file.recentfiles"));
+ _recentFileMenu.setEnabled(false);
+ fileMenu.add(_recentFileMenu);
fileMenu.addSeparator();
// Load from GPS
JMenuItem loadFromGpsMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSLOAD);
});
fileMenu.add(exitMenuItem);
menubar.add(fileMenu);
+
// Track menu
JMenu trackMenu = new JMenu(I18nManager.getText("menu.track"));
setAltKey(trackMenu, "altkey.menu.track");
_app.toggleSidebars();
}
});
+ sidebarsCheckbox.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); // shortcut F11
viewMenu.add(sidebarsCheckbox);
// 3d
_show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D, false);
// connect audio
_connectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
audioMenu.add(_connectAudioItem);
- // Disconnect current audio file
+ // Disconnect current audio clip
_disconnectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_AUDIO, false);
audioMenu.add(_disconnectAudioItem);
- // Remove current audio file
+ // Remove current audio clip
_removeAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_AUDIO, false);
audioMenu.add(_removeAudioItem);
audioMenu.addSeparator();
- // Correlate audio files
+ // Correlate audio clips
_correlateAudiosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_AUDIOS, false);
audioMenu.add(_correlateAudiosItem);
menubar.add(audioMenu);
settingsMenu.addSeparator();
// Save configuration
settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SAVECONFIG));
+ _autosaveSettingsCheckbox = new JCheckBoxMenuItem(
+ I18nManager.getText("menu.settings.autosave"), false);
+ _autosaveSettingsCheckbox.setSelected(Config.getConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS));
+ _autosaveSettingsCheckbox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Config.setConfigBoolean(Config.KEY_AUTOSAVE_SETTINGS, _autosaveSettingsCheckbox.isSelected());
+ }
+ });
+ settingsMenu.add(_autosaveSettingsCheckbox);
menubar.add(settingsMenu);
// Help menu
_duplicatePointItem.setEnabled(hasPoint);
// are there any photos?
boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
- _saveExifItem.setEnabled(anyPhotos);
+ _saveExifItem.setEnabled(anyPhotos && _app.getTrackInfo().getPhotoList().hasMediaWithFile());
// is there a current photo, audio?
Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto();
boolean hasPhoto = currentPhoto != null;
- AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio();
+ AudioClip currentAudio = _app.getTrackInfo().getCurrentAudio();
boolean hasAudio = currentAudio != null;
// connect is available if (photo/audio) and point selected, and media has no point
boolean connectAvailable = (hasPhoto && hasPoint && currentPhoto.getDataPoint() == null)
if (_mapCheckbox.isSelected() != mapsOn) {
_mapCheckbox.setSelected(mapsOn);
}
+ // Are there any recently-used files?
+ RecentFileList rfl = Config.getRecentFileList();
+ final int numRecentFiles = rfl.getNumEntries();
+ final boolean hasRecentFiles = numRecentFiles > 0;
+ _recentFileMenu.setEnabled(hasRecentFiles);
+ if (hasRecentFiles)
+ {
+ int numItems = _recentFileMenu.getMenuComponentCount();
+ if (numItems == numRecentFiles)
+ {
+ // Right number of items, just change texts
+ for (int i=0; i<numRecentFiles; i++)
+ {
+ JMenuItem item = _recentFileMenu.getItem(i);
+ item.setText(rfl.getFile(i)==null?"":rfl.getFile(i).getFile().getName());
+ item.setToolTipText(rfl.getFile(i)==null?null:rfl.getFile(i).getFile().getAbsolutePath());
+ }
+ }
+ else
+ {
+ // Rebuild menus
+ _recentFileMenu.removeAll();
+ for (int i=0; i<rfl.getSize(); i++)
+ {
+ RecentFile rf = rfl.getFile(i);
+ if (rf != null && rf.isValid())
+ {
+ JMenuItem menuItem = new JMenuItem(rf.getFile().getName());
+ menuItem.setToolTipText(rf.getFile().getAbsolutePath());
+ menuItem.addActionListener(new RecentFileTrigger(_app, i));
+ _recentFileMenu.add(menuItem);
+ }
+ }
+ }
+ }
}
// calculate maximum thumbnail size
Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, DEFAULT_THUMB_SIZE, DEFAULT_THUMB_SIZE);
// Make icon to load image into
- Image image = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage();
+ Image image = _photo.createImageIcon().getImage();
// save scaled, smoothed thumbnail for reuse
_thumbnail = ImageUtils.createScaledImage(image, thumbSize.width, thumbSize.height);
image = null;
}
}
else {
- _thumbnail = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage();
+ _thumbnail = _photo.createImageIcon().getImage();
}
_loadingImage = false;
repaint();
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.util.ArrayList;
+
+import tim.prune.App;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.RecentFile;
+import tim.prune.load.BabelLoadFromFile;
+
+/**
+ * Class to act as a trigger when a menu item for a recent file is clicked
+ */
+public class RecentFileTrigger implements ActionListener
+{
+ private App _app = null;
+ private int _index = 0;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ * @param inIndex menu index from 0
+ */
+ public RecentFileTrigger(App inApp, int inIndex)
+ {
+ _app = inApp;
+ _index = inIndex;
+ }
+
+ /**
+ * React to click on menu item
+ */
+ public void actionPerformed(ActionEvent arg0)
+ {
+ RecentFile rf = Config.getRecentFileList().getFile(_index);
+ if (rf != null && rf.isValid())
+ {
+ if (rf.isRegularLoad())
+ {
+ // Trigger a regular file load
+ ArrayList<File> dataFiles = new ArrayList<File>();
+ dataFiles.add(rf.getFile());
+ _app.loadDataFiles(dataFiles);
+ }
+ else
+ {
+ // Trigger a load via gpsbabel
+ new BabelLoadFromFile(_app).beginWithFile(rf.getFile());
+ }
+ }
+ else
+ {
+ _app.showErrorMessage("function.open", "error.load.noread");
+ Config.getRecentFileList().verifyAll(); // Called on a file which no longer exists
+ UpdateMessageBroker.informSubscribers();
+ }
+ }
+}
_photoListPanel = makeListPanel("details.lists.photos", _photoList);
// don't add photo list (because there aren't any photos yet)
- // List for audio files
+ // List for audio clips
_audioListModel = new MediaListModel(_trackInfo.getAudioList());
_audioList = new JList(_audioListModel);
_audioList.addListSelectionListener(new ListSelectionListener() {
}
/**
- * Select the specified audio file
- * @param inIndex index of selected audio file
+ * Select the specified audio clip
+ * @param inIndex index of selected audio clip
*/
private void selectAudio(int inIndex)
{
}
}
}
- // Same for audio files
+ // Same for audio clips
if (_audioListModel.getSize() > 0)
{
int audioIndex = _trackInfo.getSelection().getCurrentAudioIndex();
*/
public Object getElementAt(int inIndex)
{
- if (inIndex < 0 || inIndex >= getSize()) return "";
- return _waypoints.get(inIndex).getWaypointName();
+ DataPoint p = null;
+ if (inIndex < 0 || inIndex >= getSize()
+ || _waypoints == null || (p = _waypoints.get(inIndex)) == null)
+ return "";
+ return p.getWaypointName();
}
/**
{
/** Selected style number */
private String _style = null;
- /** Server prefix including API-key unique to Prune application */
+ /** Server prefix including API-key unique to GpsPrune application */
private static final String SERVER_PREFIX = "tile.cloudmade.com/03d86b66f51f4a3b8c236ac06f2a2e57/";
/**
* @param inBasePath base path to disk cache
* @param inTilePath relative path to this tile
* @param inObserver observer to inform when load complete
+ * @return true if successful, false for failure
*/
- public static void saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
+ public static boolean saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
{
- if (inBasePath == null || inTilePath == null) {return;}
+ if (inBasePath == null || inTilePath == null) {return false;}
// save file if possible
File basePath = new File(inBasePath);
if (!basePath.exists() || !basePath.isDirectory() || !basePath.canWrite()) {
- //System.err.println("Can't write");
// Can't write to base path
- return;
+ return false;
}
File tileFile = new File(basePath, inTilePath);
// Check if this file is already being loaded
- if (!isBeingLoaded(tileFile))
+ if (isBeingLoaded(tileFile)) {return true;}
+
+ File dir = tileFile.getParentFile();
+ // Start a new thread to load the image if necessary
+ if ((dir.exists() || dir.mkdirs()) && dir.canWrite())
{
- File dir = tileFile.getParentFile();
- // Start a new thread to load the image if necessary
- if (dir.exists() || dir.mkdirs())
- {
- new DiskTileCacher(inUrl, tileFile, inObserver);
- }
+ new DiskTileCacher(inUrl, tileFile, inObserver);
+ return true;
}
+ return false; // couldn't write the file
}
/**
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
-import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
-import java.awt.image.RescaleOp;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
_topPanel.setLayout(new FlowLayout());
_topPanel.setOpaque(false);
// Make slider for transparency
- _transparencySlider = new JSlider(0, 5, 0);
+ _transparencySlider = new JSlider(-6, 6, 0);
_transparencySlider.setPreferredSize(new Dimension(100, 20));
_transparencySlider.setMajorTickSpacing(1);
_transparencySlider.setSnapToTicks(true);
_transparencySlider.setOpaque(false);
+ _transparencySlider.setValue(0);
_transparencySlider.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e)
{
- _recalculate = true;
- repaint();
+ int val = _transparencySlider.getValue();
+ if (val == 1 || val == -1)
+ _transparencySlider.setValue(0);
+ else {
+ _recalculate = true;
+ repaint();
+ }
}
});
_transparencySlider.setFocusable(false); // stop slider from stealing keyboard focus
_yRange = new DoubleRange(MapUtils.getYFromLatitude(_latRange.getMinimum()),
MapUtils.getYFromLatitude(_latRange.getMaximum()));
_mapPosition.zoomToXY(_xRange.getMinimum(), _xRange.getMaximum(), _yRange.getMinimum(), _yRange.getMaximum(),
- getWidth(), getHeight());
+ getWidth(), getHeight());
}
}
}
- // Make maps brighter / fainter
- final float[] scaleFactors = {1.0f, 1.05f, 1.1f, 1.2f, 1.6f, 2.2f};
- final float scaleFactor = scaleFactors[_transparencySlider.getValue()];
- if (scaleFactor > 1.0f)
+ // Make maps brighter / fainter according to slider
+ final int brightnessIndex = Math.max(1, _transparencySlider.getValue()) - 1;
+ if (brightnessIndex > 0)
{
- RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
- hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
- RescaleOp op = new RescaleOp(scaleFactor, 0, hints);
- op.filter(_mapImage, _mapImage);
+ final int[] alphas = {0, 40, 80, 120, 160, 210};
+ Color bgColor = Config.getColourScheme().getColour(ColourScheme.IDX_BACKGROUND);
+ bgColor = new Color(bgColor.getRed(), bgColor.getGreen(), bgColor.getBlue(), alphas[brightnessIndex]);
+ g.setColor(bgColor);
+ g.fillRect(0, 0, getWidth(), getHeight());
}
}
}
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);
+ final ColourScheme cs = Config.getColourScheme();
+ final int[] opacities = {255, 190, 130, 80, 40, 0};
+ int opacity = 255;
+ if (_transparencySlider.getValue() < 0)
+ opacity = opacities[-1 - _transparencySlider.getValue()];
+ final Color pointColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_POINT), opacity);
+ final Color rangeColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SELECTION), opacity);
+ final Color currentColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_PRIMARY), opacity);
+ final Color secondColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_SECONDARY), opacity);
+ final Color textColour = makeTransparentColour(cs.getColour(ColourScheme.IDX_TEXT), opacity);
// try to set line width for painting
if (inG instanceof Graphics2D)
return false;
}
+ /**
+ * Make a semi-transparent colour for drawing with
+ * @param inColour base colour (fully opaque)
+ * @param inOpacity opacity where 0=invisible and 255=full
+ * @return new colour object
+ */
+ private static Color makeTransparentColour(Color inColour, int inOpacity)
+ {
+ if (inOpacity > 240) return inColour;
+ return new Color(inColour.getRed(), inColour.getGreen(), inColour.getBlue(), inOpacity);
+ }
/**
* Inform that tiles have been updated and the map can be repainted
point.setSegmentStart(false);
}
}
- else if (inE.getClickCount() == 2) {
+ else if (inE.getClickCount() == 2)
+ {
// double click
if (_drawMode == MODE_DEFAULT) {
panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2);
if (_drawMode == MODE_ZOOM_RECT && Math.abs(_dragToX - _dragFromX) > 20
&& Math.abs(_dragToY - _dragFromY) > 20)
{
- //System.out.println("Finished zoom: " + _dragFromX + ", " + _dragFromY + " to " + _dragToX + ", " + _dragToY);
_mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight());
+ }
+ if (_drawMode == MODE_ZOOM_RECT) {
_drawMode = MODE_DEFAULT;
}
_dragFromX = _dragFromY = -1;
// Check for Ctrl key (for Linux/Win) or meta key (Clover key for Mac)
if (inE.isControlDown() || inE.isMetaDown())
{
+ // Shift as well makes things faster
+ final int pointIncrement = inE.isShiftDown()?3:1;
// Check for arrow keys to zoom in and out
if (code == KeyEvent.VK_UP)
zoomIn();
zoomOut();
// Key nav for next/prev point
else if (code == KeyEvent.VK_LEFT && currPointIndex > 0)
- _trackInfo.selectPoint(currPointIndex-1);
+ _trackInfo.incrementPointIndex(-pointIncrement);
else if (code == KeyEvent.VK_RIGHT)
- _trackInfo.selectPoint(currPointIndex+1);
+ _trackInfo.incrementPointIndex(pointIncrement);
else if (code == KeyEvent.VK_PAGE_UP)
_trackInfo.selectPoint(Checker.getPreviousSegmentStart(
_trackInfo.getTrack(), _trackInfo.getSelection().getCurrentPointIndex()));
_sourceList.add(new OsmMapSource("Mapnik", "http://tile.openstreetmap.org/"));
_sourceList.add(new OsmMapSource("Osma", "http://tah.openstreetmap.org/Tiles/tile/"));
_sourceList.add(new OsmMapSource("Cyclemap", "http://andy.sandbox.cloudmade.com/tiles/cycle/"));
- _sourceList.add(new OsmMapSource("Reitkarte", "http://topo.geofabrik.de/hills/",
- "http://topo.openstreetmap.de/topo/", 18));
+ _sourceList.add(new OsmMapSource("Reitkarte", "http://wanderreitkarte.de/hills/",
+ "http://topo2.wanderreitkarte.de/topo/", 18));
_sourceList.add(new MffMapSource("Mapsforfree", "http://maps-for-free.com/layer/relief/", ".jpg",
"http://maps-for-free.com/layer/water/", ".gif", 11));
_sourceList.add(new CloudmadeMapSource("Pale Dawn", "998", 18));
if (useDisk)
{
tile = DiskTileCacher.getTile(diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), onlineMode);
- if (tile != null) {
+ if (tile != null)
+ {
// Pass tile to memory cache
tempCache.setTile(tile, inX, inY);
if (tile.getWidth(this) > 0) {return tile;}
try
{
URL tileUrl = new URL(_mapSource.makeURL(inLayer, _zoom, inX, inY));
- if (useDisk) {
- // Copy image directly from URL stream to disk cache
- DiskTileCacher.saveTile(tileUrl, diskCachePath, _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this);
+ if (useDisk && DiskTileCacher.saveTile(tileUrl, diskCachePath,
+ _mapSource.makeFilePath(inLayer, _zoom, inX, inY), this))
+ {
+ // Image now copied directly from URL stream to disk cache
}
- else {
+ else
+ {
// Load image asynchronously, using observer
tile = Toolkit.getDefaultToolkit().createImage(tileUrl);
// Pass to memory cache
import tim.prune.config.Config;
/**
- * Class to show a scale bar on the main map of Prune
+ * Class to show a scale bar on the main map of GpsPrune
*/
public class ScaleBar extends JPanel
{
initArrays();
_hasData = false;
_altitudeFormat = Altitude.Format.NO_FORMAT;
- if (_track != null) {
+ if (_track != null)
+ {
for (int i=0; i<_track.getNumPoints(); i++)
{
- DataPoint point = _track.getPoint(i);
- if (point != null && point.hasAltitude())
+ try
{
- // Point has an altitude - if it's the first one, use its format
- if (_altitudeFormat == Altitude.Format.NO_FORMAT)
+ DataPoint point = _track.getPoint(i);
+ if (point != null && point.hasAltitude())
{
- _altitudeFormat = point.getAltitude().getFormat();
- _minValue = _maxValue = point.getAltitude().getValue();
+ // Point has an altitude - if it's the first one, use its format
+ if (_altitudeFormat == Altitude.Format.NO_FORMAT)
+ {
+ _altitudeFormat = point.getAltitude().getFormat();
+ _minValue = _maxValue = point.getAltitude().getValue();
+ }
+ // Store the value and maintain max and min values
+ double value = point.getAltitude().getValue(_altitudeFormat);
+ _pointValues[i] = value;
+ if (value < _minValue) {_minValue = value;}
+ if (value > _maxValue) {_maxValue = value;}
+
+ _hasData = true;
+ _pointHasData[i] = true;
}
- // Store the value and maintain max and min values
- double value = point.getAltitude().getValue(_altitudeFormat);
- _pointValues[i] = value;
- if (value < _minValue) {_minValue = value;}
- if (value > _maxValue) {_maxValue = value;}
-
- _hasData = true;
- _pointHasData[i] = true;
+ else _pointHasData[i] = false;
}
- else _pointHasData[i] = false;
+ catch (ArrayIndexOutOfBoundsException obe)
+ {} // must be due to the track size changing during calculation
+ // assume that a redraw will be triggered
}
}
}
import com.drew.lang.Rational;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
+import com.drew.metadata.MetadataException;
import com.drew.metadata.exif.ExifDirectory;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.exif.GpsDirectory;
/**
* Class to act as a gateway into the external exif library functions.
* This should be the only class with dependence on the lib-metadata-extractor-java
- * classes (which are NOT delivered with Prune).
+ * classes (which are NOT delivered with GpsPrune).
* This class will not compile without this extra dependency (but is not required if
* the ExifGateway uses the InternalExifLibrary instead).
* Should not be included if the internal library will be used (from jpeg.drew package).
data.setAltitudeRef(altRef);
}
- // Timestamp and datestamp (if present)
- final int TAG_GPS_DATESTAMP = 0x001d;
- if (gpsdir.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP) && gpsdir.containsTag(TAG_GPS_DATESTAMP))
+ try
{
- Rational[] times = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_TIME_STAMP);
- data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(),
- times[2].intValue()});
- Rational[] dates = gpsdir.getRationalArray(TAG_GPS_DATESTAMP);
- if (dates != null) {
- data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
+ // Timestamp and datestamp (if present)
+ final int TAG_GPS_DATESTAMP = 0x001d;
+ if (gpsdir.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP) && gpsdir.containsTag(TAG_GPS_DATESTAMP))
+ {
+ Rational[] times = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_TIME_STAMP);
+ data.setGpsTimestamp(new int[] {times[0].intValue(), times[1].intValue(),
+ times[2].intValue()});
+ Rational[] dates = gpsdir.getRationalArray(TAG_GPS_DATESTAMP);
+ if (dates != null) {
+ data.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});
+ }
+ }
+ }
+ catch (MetadataException me) {} // ignore, use other tags instead
+
+ // Image bearing (if present)
+ if (gpsdir.containsTag(GpsDirectory.TAG_GPS_IMG_DIRECTION) && gpsdir.containsTag(GpsDirectory.TAG_GPS_IMG_DIRECTION_REF))
+ {
+ Rational bearing = gpsdir.getRational(GpsDirectory.TAG_GPS_IMG_DIRECTION);
+ if (bearing != null) {
+ data.setBearing(bearing.doubleValue());
}
}
}
}
catch (Exception e) {
// Exception reading metadata, just ignore it
+ //System.err.println("Error: " + e.getClass().getName() + " - " + e.getMessage());
}
return data;
}
private String _digitizedTimestamp = null;
private int _orientationCode = -1;
private byte[] _thumbnail = null;
+ private double _bearing = -1.0;
private ArrayList<String> _errors = null;
}
}
+ /**
+ * Set the bearing (0 - 360)
+ * @param inBearing bearing in degrees
+ */
+ public void setBearing(double inBearing)
+ {
+ _bearing = inBearing;
+ }
+
/** @return latitude ref as char */
public char getLatitudeRef() { return _latitudeRef; }
/** @return latitude as array of 3 Rationals */
public String getOriginalTimestamp() { return _originalTimestamp; }
/** @return digitized timestamp as string */
public String getDigitizedTimestamp() { return _digitizedTimestamp; }
+ /** @return bearing in degrees or -1 */
+ public double getBearing() { return _bearing; }
/**
* Set the thumbnail
private static final int TAG_THUMBNAIL_LENGTH = 0x0202;\r
/** Orientation of image */\r
private static final int TAG_ORIENTATION = 0x0112;\r
+ /** Bearing direction of image */\r
+ private static final int TAG_BEARING = 0x0011;\r
\r
\r
/**\r
if (dates != null) {\r
inMetadata.setGpsDatestamp(new int[] {dates[0].intValue(), dates[1].intValue(), dates[2].intValue()});\r
}\r
- else {\r
+ else\r
+ {\r
// Not in rational array format, but maybe as String?\r
String date = readString(inTagValueOffset, inFormatCode, inComponentCount);\r
if (date != null && date.length() == 10) {\r
}\r
}\r
break;\r
+ case TAG_BEARING:\r
+ Rational val = readRational(inTagValueOffset, inFormatCode, inComponentCount);\r
+ if (val != null) {\r
+ inMetadata.setBearing(val.doubleValue());\r
+ }\r
+ break;\r
default: // ignore all other tags\r
}\r
}\r
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Afrikaans entries as extra
# Menu entries
menu.file.addphotos=Voeg Fotos By
menu.file.save=Stoor
menu.file.exit=Gaan Uit
+menu.track=Spoor
menu.track.undo=Herroep
menu.track.clearundo=Herroep Lys Skoonmaak
-menu.point.editpoint=Redigeer Punt
-menu.point.deletepoint=Punt Uitvee
-menu.range.deleterange=Reeks Uitvee
menu.track.deletemarked=Gemerkde Punt Uitvee
-menu.range.interpolate=Interpoleer
-menu.range.average=Gemiddelde Seleksie
-menu.range.reverse=Reeks Omkeer
-menu.range.mergetracksegments=Saamvoeg van spoor segmente
menu.track.rearrange=Herrangskik bakens
menu.track.rearrange.start=Bakens na begin van l\u00eaer
menu.track.rearrange.end=Bakens na einde van l\u00eaer
menu.track.rearrange.nearest=Beweeg elk na naaste spoor punt
-menu.range.cutandmove=Sny en skuif seleksie
menu.range=Reeks
-menu.point=Punt
menu.range.all=Selekteer Alles
menu.range.none=Selekteer Niks
menu.range.start=Stel Reeks Begin
menu.range.end=Stel Reeks Einde
+menu.range.deleterange=Reeks Uitvee
+menu.range.interpolate=Interpoleer
+menu.range.average=Gemiddelde Seleksie
+menu.range.reverse=Reeks Omkeer
+menu.range.mergetracksegments=Saamvoeg van spoor segmente
+menu.range.cutandmove=Sny en skuif seleksie
+menu.point=Punt
+menu.point.editpoint=Redigeer Punt
+menu.point.deletepoint=Punt Uitvee
menu.photo=Foto
menu.photo.saveexif=Stoor na EXIF
-function.connecttopoint=Las foto by huidige punt
-function.disconnectfrompoint=Ontkoppel vanaf huidige punt
-function.removephoto=Verwyder foto
menu.view=Kyk
menu.view.browser=Kaart in werf blaaier
menu.view.browser.google=Google Kaarte
menu.map.connect=Connekteer baan punte
menu.map.autopan=Automatiese Skuif van Kyk Venster
menu.map.showmap=Wys Kaart
+menu.map.showscalebar=Wys SkalleerStaaf
# Alt keys for menus
altkey.menu.file=L
function.exportkml=KML Uitvoer
function.exportgpx=GPS Uitvoer
function.exportpov=POV Uitvoer
+function.exportsvg=SVG Uitvoer
function.editwaypointname=Redigeer Baken Naam
function.compress=Kompakteer spoor
function.addtimeoffset=Voeg tyd spruit by
+function.addaltitudeoffset=Voeg hoogte spruit by
+function.convertnamestotimes=Omskakel baken name na tye
function.findwaypoint=Vind Baken
+function.pastecoordinates=Enter nuwe koordinate
function.charts=Grafieke
function.show3d=3D Kyk
function.distances=Afstande
+function.fullrangedetails=Vol reeks besonderhede
function.setmapbg=Stel Kaart agtergrond
+function.setpaths=Stel program paaie
function.getgpsies=Kry GPS spore
+function.duplicatepoint=Dupliseer Punt
+function.setcolours=Stel kleure
+function.setlanguage=Stel tale
+function.connecttopoint=Las foto by huidige punt
+function.disconnectfrompoint=Ontkoppel vanaf huidige punt
+function.removephoto=Verwyder foto
function.correlatephotos=Korreleer Fotos
+function.rearrangephotos=Herrangskik fotos
+function.rotatephotoleft=Roteer foto links
+function.rotatephotoright=Roteer foto regs
+function.ignoreexifthumb=Ignoreer EXIF thumbnail
function.help=Hulp
function.showkeys=Wys Kortpad sleutels
function.about=Omtrent Prune
dialog.gpsload.format=Formaat
dialog.gpsload.getwaypoints=Laai Bakens
dialog.gpsload.gettracks=Laai spore
+dialog.gpsload.save=Stoor na l\u00eaer
dialog.gpssend.sendwaypoints=Stuur Bakens
dialog.gpssend.sendtracks=Stuur Spore
dialog.gpssend.trackname=Spoor name
dialog.save.timestampformat=Tyd Stempel Formaat
dialog.save.overwrite.title=L\u00eaer bestaan reeds
dialog.save.overwrite.text=Hierdie L\u00eaer bestaan reeds. Is jy seker jy wil die l\u00eaer oorskryf?
+dialog.save.notypesselected=Geen punt tipes is geselekteer nie
dialog.exportkml.text=Titel vir die data
dialog.exportkml.altitude=Absolute hoogte (vir aviasie)
dialog.exportkml.kmz=Kompakteer om kmz l\u00eaer te maak
dialog.exportkml.exportimages=Voer beeld duimnaelsketse uit na kmz
+dialog.exportkml.trackcolour=Spoor kleur
dialog.exportgpx.name=Naam
dialog.exportgpx.desc=Beskrywing
dialog.exportgpx.includetimestamps=Tydstempel Insluit
+dialog.exportgpx.copysource=Kopieer bron xml
dialog.exportpov.text=Steutel asseblief parameters in vir POV uitvoer
dialog.exportpov.font=Font
dialog.exportpov.camerax=Kamera X
dialog.clearundo.title=Maak Herroep lys uit skoon
dialog.clearundo.text=Is jy seker jy wil die herroep lys skoon maak?\nAlle herroep informasie sal verlore gaan!
dialog.pointedit.title=Redigeer punt
+dialog.pointedit.table.field=Veld
+dialog.pointedit.table.value=Waarde
+dialog.pointedit.table.changed=Verander
+dialog.pointnameedit.name=Baken naam
+dialog.pointnameedit.uppercase=Hoof letter
+dialog.pointnameedit.lowercase=Klein letter
+dialog.addtimeoffset.add=Voeg tyd by
+dialog.addtimeoffset.subtract=Vat tyd weg
+dialog.addtimeoffset.days=Dae
+dialog.addtimeoffset.hours=Ure
+dialog.addtimeoffset.minutes=Minute
+dialog.findwaypoint.search=Soek
+dialog.saveexif.title=Stoor Exif
+dialog.saveexif.table.status=Status
+dialog.saveexif.table.save=Stoor
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Czech entries thanks to prot_d
# Menu entries
menu.file=Soubor
menu.file.addphotos=P\u0159idat fotografie
+menu.file.recentfiles=Naposledy otev\u0159en\u00e9
menu.file.save=Ulo\u017eit jako text
menu.file.exit=Konec
menu.track=Trasa
menu.view.browser.bing=Mapy Bing
menu.settings=Nastaven\u00ed
menu.settings.onlinemode=Na\u010d\u00edtat mapy z internetu
+menu.settings.autosave=P\u0159i ukon\u010den\u00ed automaticky ukl\u00e1dat
menu.help=Pomoc
# Popup menu for map
menu.map.zoomin=P\u0159ibl\u00ed\u017eit
# Functions
function.open=Otev\u0159\u00edt soubor
+function.importwithgpsbabel=Importovat p\u0159es GPSBabel
function.loadfromgps=Na\u010d\u00edst data z GPS
function.sendtogps=Poslat data do GPS
function.exportkml=Export KML
function.checkversion=Zkontrolovat existenci nov\u00e9 verze
function.saveconfig=Ulo\u017eit nastaven\u00ed
function.diskcache=Ulo\u017eit mapy na disk
+function.managetilecache=Upravit cache map
# Dialogs
-dialog.exit.confirm.title=Ukon\u010dit Prune
+dialog.exit.confirm.title=Ukon\u010dit GpsPrune
dialog.exit.confirm.text=Data nejsou ulo\u017eena. Opravdu chcete ukon\u010dit program?
dialog.openappend.title=P\u0159ipojit k na\u010dten\u00fdm dat\u016fm
dialog.openappend.text=P\u0159ipojit tato data k ji\u017e na\u010dten\u00fdm dat\u016fm?
dialog.exportgpx.desc=Popis
dialog.exportgpx.includetimestamps=Ulo\u017eit \u010dasov\u00e9 zna\u010dky
dialog.exportgpx.copysource=Zkop\u00edrovat zdrojov\u00e9 xml
+dialog.exportgpx.encoding=K\u00f3dov\u00e1n\u00ed
+dialog.exportgpx.encoding.system=Implicitn\u00ed dle OS
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Pros\u00edm vlo\u017ete paramerty exportu do POV
dialog.exportpov.font=Font
dialog.exportpov.camerax=Kamera X
dialog.compress.singletons.title=Odstran\u011bn\u00ed osamocen\u00fdch bod\u016f
dialog.compress.singletons.paramdesc=Koeficient vzd\u00e1lenosti
dialog.compress.duplicates.title=Odstran\u011bn\u00ed zdvojen\u00fdch bod\u016f
+dialog.compress.douglaspeucker.title=Douglasova-Peuckerova komprese
+dialog.compress.douglaspeucker.paramdesc=Povolen\u00e1 odchylka
dialog.compress.summarylabel=Bod\u016f ke smaz\u00e1n\u00ed
dialog.pastecoordinates.desc=Zadejte sou\u0159adnice
dialog.pastecoordinates.coords=Sou\u0159adnice
dialog.pastecoordinates.nothingfound=Pros\u00edm ov\u011b\u0159te sou\u0159adnice a zkuste znovu
-dialog.help.help=V\u00edce informac\u00ed v\u010detn\u011b manu\u00e1l\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete na adrese:\n http://activityworkshop.net/software/prune/
+dialog.help.help=V\u00edce informac\u00ed v\u010detn\u011b manu\u00e1l\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete na adrese:\n http://activityworkshop.net/software/gpsprune/
dialog.about.version=Verze
dialog.about.build=Build
-dialog.about.summarytext1=Prune je program k na\u010d\u00edt\u00e1n\u00ed, zobrazov\u00e1n\u00ed a editaci dat z navigac\u00ed GPS.
+dialog.about.summarytext1=GpsPrune je program k na\u010d\u00edt\u00e1n\u00ed, zobrazov\u00e1n\u00ed a editaci dat z navigac\u00ed GPS.
dialog.about.summarytext2=Je vyd\u00e1n pod licenc\u00ed GNU GPL, tak\u017ee je zdarma a voln\u011b k u\u017e\u00edv\u00e1n\u00ed a vylep\u0161ov\u00e1n\u00ed.<br>Kop\u00edrov\u00e1n\u00ed, redistribuce a \u00fapravy jsou povoleny a podporov\u00e1ny<br>podle podm\u00ednek popsan\u00fdch v souboru <code>licence.txt</code>.
dialog.about.summarytext3=V\u00edce informac\u00ed v\u010detn\u011b manu\u00e1l\u016f (bohu\u017eel nikoli v \u010de\u0161tin\u011b) naleznete<br> na adrese: <code style="font-weight:bold">http://activityworkshop.net/</code>.
dialog.about.languages=Dostupn\u00e9 jazyky
dialog.about.credits.thanks=D\u011bkujeme
dialog.about.readme=Readme
dialog.checkversion.error=\u010c\u00edslo verze se nepoda\u0159ilo zkontrolovat.\nPros\u00edm ov\u011b\u0159te p\u0159ipojen\u00ed k internetu.
-dialog.checkversion.uptodate=Pou\u017e\u00edv\u00e1te posledn\u00ed verzi Prune.
-dialog.checkversion.newversion1=Nov\u00e1 verze Prune je u\u017e dostupn\u00e1! Posledn\u00ed verze m\u00e1 \u010d\u00edslo
+dialog.checkversion.uptodate=Pou\u017e\u00edv\u00e1te posledn\u00ed verzi GpsPrune.
+dialog.checkversion.newversion1=Nov\u00e1 verze GpsPrune je u\u017e dostupn\u00e1! Posledn\u00ed verze m\u00e1 \u010d\u00edslo
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Tato verze byla vyd\u00e1na
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Novou verzi m\u016f\u017eete st\u00e1hnout na adrese http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=M\u00edsto my\u0161i m\u016f\u017eete pou\u017e\u00edvat n\u00e1sleduj\u00edc\u00ed kl\u00e1vesov\u00e9 zkratky
dialog.keys.keylist=<table><tr><td>\u0160ipky</td><td>Posunout mapu vlevo, vpravo, nahoru, dol\u016f</td></tr><tr><td>Ctrl + \u0161ipka vlevo, vpravo</td><td>Vybrat p\u0159edchoz\u00ed nebo n\u00e1sleduj\u00edc\u00ed bod</td></tr><tr><td>Ctrl + \u0161ipka nahoru, dol\u016f</td><td>P\u0159ibl\u00ed\u017eit, odd\u00e1lit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Vybrat p\u0159edchoz\u00ed, n\u00e1sleduj\u00edc\u00ed \u010d\u00e1st trasy</td></tr><tr><td>Ctrl + Home, End</td><td>Vybrat prvn\u00ed, posledn\u00ed bod</td></tr><tr><td>Del</td><td>Smazat aktu\u00e1ln\u00ed bod</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Barevn\u00e9 sch\u00e9ma
dialog.saveconfig.prune.linewidth=Tlou\u0161\u0165ka \u010d\u00e1ry
dialog.saveconfig.prune.kmltrackcolour=Barva trasy v KML
+dialog.saveconfig.prune.autosavesettings=Mo\u017enosti ukl\u00e1d\u00e1n\u00ed
dialog.setpaths.intro=Je-li to t\u0159eba, m\u016f\u017eete nastavit cesty k extern\u00edm aplikac\u00edm:
dialog.setpaths.found=Cesta nalezena?
dialog.addaltitude.noaltitudes=Vybran\u00e9 rozmez\u00ed neobsahuje nadmo\u0159skou v\u00fd\u0161ku
dialog.colourchooser.green=Zelen\u00e1
dialog.colourchooser.blue=Modr\u00e1
dialog.setlanguage.firstintro=M\u016f\u017eete bu\u010f zvolit jeden z vypsan\u00fdch jazyk\u016f,<p>nebo vybrat textov\u00fd soubor
-dialog.setlanguage.secondintro=Aby do\u0161lo ke zm\u011bn\u011b jazyka, je t\u0159eba ulo\u017eit nastaven\u00ed<p> a potom restartovat program Prune.
+dialog.setlanguage.secondintro=Aby do\u0161lo ke zm\u011bn\u011b jazyka, je t\u0159eba ulo\u017eit nastaven\u00ed<p> a potom restartovat program GpsPrune.
dialog.setlanguage.language=Jazyk
dialog.setlanguage.languagefile=Jazykov\u00fd soubor
-dialog.setlanguage.endmessage=Aby do\u0161lo ke zm\u011bn\u011b jazyka, nyn\u00ed ulo\u017ete nastaven\u00ed\na spus\u0165te Prune nanovo.
+dialog.setlanguage.endmessage=Aby do\u0161lo ke zm\u011bn\u011b jazyka, nyn\u00ed ulo\u017ete nastaven\u00ed\na spus\u0165te GpsPrune nanovo.
+dialog.setlanguage.endmessagewithautosave=Aby byla dokon\u010dena zm\u011bna jazyka, pros\u00edm restartujte program.
dialog.diskcache.save=Ukl\u00e1dat mapov\u00e9 podklady na disk
dialog.diskcache.dir=Adres\u00e1\u0159 s cache
dialog.diskcache.createdir=Vytvo\u0159it adres\u00e1\u0159
dialog.diskcache.nocreate=Adres\u00e1\u0159 nebyl vytvo\u0159en
+dialog.diskcache.table.path=Cesta
+dialog.diskcache.table.usedby=Pou\u017e\u00edv\u00e1
+dialog.diskcache.table.zoom=Zv\u011bt\u0161en\u00ed
+dialog.diskcache.table.tiles=Dla\u017edic
+dialog.diskcache.table.megabytes=Megabyt\u016f
+dialog.diskcache.tileset=Mapov\u00fd podklad
+dialog.diskcache.tileset.multiple=v\u00edce sad
+dialog.diskcache.deleteold=Smazat star\u00e9 soubory
+dialog.diskcache.deleteall=Smazat v\u0161echny soubory
+dialog.diskcache.deleted1=Smaz\u00e1no
+dialog.diskcache.deleted2=soubor\u016f z cache
dialog.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit
dialog.setlinewidth.text=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed trasa (1-4)
dialog.downloadosm.desc=Potvr\u010fte, \u017ee se maj\u00ed k dan\u00e9 oblasti st\u00e1hnout data OSM:
dialog.searchwikipedianames.search=Vyhledat:
# 3d window
-dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed Prune
+dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed GpsPrune
dialog.3d.altitudefactor=Faktor zd\u016frazn\u011bn\u00ed v\u00fd\u0161ky
-dialog.3dlines.title=Linky m\u0159\u00ed\u017eky Prune
+dialog.3dlines.title=Linky m\u0159\u00ed\u017eky GpsPrune
dialog.3dlines.empty=Nejsou \u017e\u00e1dn\u00e9 linky k zobrazen\u00ed!
dialog.3dlines.intro=Toto jsou linky m\u0159\u00ed\u017eky trojrozm\u011brn\u00e9ho zobrazen\u00ed
button.browse=Proch\u00e1zet...
button.addnew=P\u0159idat nov\u00e9
button.delete=Smazat
+button.manage=Upravit
# File types
filetype.txt=soubory TXT
details.photodetails=Detaily fotografie
details.nophoto=Fotografie nevybr\u00e1na
details.photo.loading=Na\u010d\u00edt\u00e1m
+details.photo.bearing=Azimut
details.media.connected=P\u0159ipojeno
details.audiodetails=Detaily audionahr\u00e1vky
details.noaudio=Audionahr\u00e1vka nevybr\u00e1na
fieldname.duration=Celkov\u00fd \u010das
fieldname.speed=Rychlost
fieldname.verticalspeed=Vertik. rychlost
+fieldname.description=Popis
# Measurement units
units.original=P\u016fvodn\u00ed
error.readme.notfound=Nenalezen soubor readme
error.osmimage.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed mapov\u00fdch podklad\u016f
error.osmimage.failed=Selhalo na\u010dten\u00ed mapov\u00fdch podklad\u016f. Pros\u00edm zkontrolujte p\u0159ipojen\u00ed k internetu.
-error.language.wrongfile=Vybran\u00fd soubor nevypad\u00e1 jako jazykov\u00fd soubor pro Prune
+error.language.wrongfile=Vybran\u00fd soubor nevypad\u00e1 jako jazykov\u00fd soubor pro GpsPrune
error.convertnamestotimes.nonames=N\u00e1zvy nemohou b\u00fdt p\u0159evedeny na \u010dasov\u00e9 zna\u010dky
error.lookupsrtm.nonefound=Pro tyto body nen\u00ed k dispozici informace o nadmo\u0159sk\u00e9 v\u00fd\u0161ce
error.lookupsrtm.nonerequired=U v\u0161ech bod\u016f u\u017e je informaci o v\u00fd\u0161ce, tak\u017ee nen\u00ed co dohled\u00e1vat
error.gpsies.uploadnotok=Server gpsies vr\u00e1til hl\u00e1\u0161en\u00ed
error.gpsies.uploadfailed=Chyba - nepoda\u0159ilo se nahr\u00e1t data.
error.playaudiofailed=Nepoda\u0159ilo se p\u0159ehr\u00e1t zvukov\u00fd soubor.
+error.cache.notthere=Nepoda\u0159ilo se nal\u00e9zt adres\u00e1\u0159 s cache map.
+error.cache.empty=Adres\u00e1\u0159 s cache map je pr\u00e1zdn\u00fd.
+error.cache.cannotdelete=Nelze smazat soubory map.
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# German entries as extra
# Menu entries
menu.file=Datei
menu.file.addphotos=Fotos laden
+menu.file.recentfiles=Zuletzt verwendete Dateien
menu.file.save=Als Text Speichern
menu.file.exit=Beenden
menu.track=Track
menu.track.undo=R\u00fcckg\u00e4ngig
-menu.track.clearundo=Liste der letzten Änderungen l\u00f6schen
+menu.track.clearundo=Liste der letzten \u00c4nderungen l\u00f6schen
menu.track.deletemarked=Komprimierte Punkte l\u00f6schen
menu.track.rearrange=Wegpunkte reorganisieren
menu.track.rearrange.start=Alle Wegpunkte zum Anfang
menu.point.deletepoint=Punkt l\u00f6schen
menu.photo=Foto
menu.photo.saveexif=Exif Daten speichern
-function.connecttopoint=Mit Punkt verkn\u00fcpfen
-function.disconnectfrompoint=Vom Punkt trennen
-function.removephoto=Foto entfernen
menu.audio=Audio
menu.view=Ansicht
menu.view.showsidebars=Seitenleisten anzeigen
menu.view.browser=Karte in Browser
menu.settings=Einstellungen
menu.settings.onlinemode=Karten aus dem Internet laden
+menu.settings.autosave=Einstellungen automatisch speichern
menu.help=Hilfe
# Popup menu for map
menu.map.zoomin=Hineinzoomen
# Functions
function.open=Datei \u00f6ffnen
+function.importwithgpsbabel=Datei mit GPSBabel importieren
function.loadfromgps=Vom GPS laden
function.sendtogps=zum GPS schicken
function.exportkml=KML exportieren
function.addtimeoffset=Zeitverschiebung aufrechnen
function.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
function.convertnamestotimes=Wegpunktenamen in Zeitstempel umwandeln
-function.deletefieldvalues=Werte eines Feldes löschen
+function.deletefieldvalues=Werte eines Feldes l\u00f6schen
function.findwaypoint=Wegpunkt finden
function.pastecoordinates=Neue Koordinaten eingeben
function.charts=Diagramme
function.getgpsies=Gpsies Tracks holen
function.uploadgpsies=Daten zum Gpsies hochladen
function.lookupsrtm=H\u00f6hendaten von SRTM holen
-function.getwikipedia=Wikipediaartikeln in der N\u00e4he nachschlagen
+function.getwikipedia=Wikipediaartikel in der N\u00e4he nachschlagen
function.searchwikipedianames=Wikipedia mit Name durchsuchen
function.downloadosm=OSM Daten f\u00fcr dieses Gebiet herunterladen
function.duplicatepoint=Punkt verdoppeln
function.setcolours=Farben einstellen
-function.setlinewidth=Liniedicke einstellen
+function.setlinewidth=Liniendicke einstellen
function.setlanguage=Sprache einstellen
+function.connecttopoint=Mit Punkt verkn\u00fcpfen
+function.disconnectfrompoint=Vom Punkt trennen
+function.removephoto=Foto entfernen
function.correlatephotos=Fotos korrelieren
function.rearrangephotos=Fotos reorganisieren
function.rotatephotoleft=Foto nach Links drehen
function.stopaudio=Abspielen abbrechen
function.help=Hilfe
function.showkeys=Tastenkombinationen anzeigen
-function.about=\u00dcber Prune
+function.about=\u00dcber GpsPrune
function.checkversion=Nach neuen Versionen suchen
function.saveconfig=Einstellungen speichern
function.diskcache=Karten auf Festplatte speichern
+function.managetilecache=Kartenkacheln verwalten
# Dialogs
-dialog.exit.confirm.title=Prune beenden
+dialog.exit.confirm.title=GpsPrune beenden
dialog.exit.confirm.text=Ihre Daten wurden nicht gespeichert. Wollen Sie das Programm trotzdem beenden?
dialog.openappend.title=Daten anh\u00e4ngen oder ersetzen
dialog.openappend.text=Diese Daten an die aktuellen Daten anh\u00e4ngen?
dialog.delimiter.space=Leerzeichen
dialog.delimiter.semicolon=Strichpunkt ;
dialog.delimiter.other=Andere
-dialog.openoptions.deliminfo.records=Aufnahmen, mit
+dialog.openoptions.deliminfo.records=Datens\u00e4tze, mit
dialog.openoptions.deliminfo.fields=Feldern
-dialog.openoptions.deliminfo.norecords=Keine Rekords
+dialog.openoptions.deliminfo.norecords=Keine Datens\u00e4tze
dialog.openoptions.altitudeunits=H\u00f6he Ma\u00dfeinheiten
dialog.open.contentsdoubled=Diese Datei enth\u00e4lt zwei Kopien von jedem Punkt,\neinmal als Waypoint und einmal als Trackpunkt.
dialog.selecttracks.intro=W\u00e4hlen Sie den Track oder die Tracks aus, die Sie laden m\u00f6chten
dialog.exportgpx.desc=Beschreibung
dialog.exportgpx.includetimestamps=Zeitstempel mit exportieren
dialog.exportgpx.copysource=Xml von Quelle kopieren
+dialog.exportgpx.encoding=Enkodierung
+dialog.exportgpx.encoding.system=System
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Geben Sie die Parameter f\u00fcr den POV Export ein
dialog.exportpov.font=Font
dialog.exportpov.camerax=Kamera X
dialog.exportpov.ballsandsticks=B\u00e4lle und Stangen
dialog.exportpov.tubesandwalls=R\u00f6hren und W\u00e4nde
dialog.exportpov.warningtracksize=Dieser Track hat sehr viele Punkte, die Java3D vielleicht nicht bearbeiten kann.\nM\u00f6chten Sie den Vorgang trotzdem fortsetzen?
-dialog.exportsvg.text=Wählen Sie die Parameter für den SVG Export aus
-dialog.exportsvg.phi=Richtungswinkel \u03D5
-dialog.exportsvg.theta=Neigungswinkel \u03B8
-dialog.exportsvg.gradients=Farbverläufe verwenden
+dialog.exportsvg.text=W\u00e4hlen Sie die Parameter f\u00fcr den SVG Export aus
+dialog.exportsvg.phi=Richtungswinkel \u03d5
+dialog.exportsvg.theta=Neigungswinkel \u03b8
+dialog.exportsvg.gradients=Farbverl\u00e4ufe verwenden
dialog.pointtype.desc=Folgende Punkttypen speichern:
dialog.pointtype.track=Trackpunkte
dialog.pointtype.waypoint=Wegpunkte
dialog.gpsies.username=Gpsies Username
dialog.gpsies.password=Gpsies Passwort
dialog.gpsies.keepprivate=Track privat halten
-dialog.gpsies.confirmopenpage=Webseite für den hochgeladenen Track öffnen?
+dialog.gpsies.confirmopenpage=Webseite f\u00fcr den hochgeladenen Track \u00f6ffnen?
dialog.gpsies.activities=Aktivit\u00e4ten
dialog.gpsies.activity.trekking=Wandern
dialog.gpsies.activity.walking=Walking
dialog.compress.singletons.title=Singletons (isolierte Punkte) entfernen
dialog.compress.singletons.paramdesc=Distanzfaktor
dialog.compress.duplicates.title=Duplikate entfernen
+dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierung
+dialog.compress.douglaspeucker.paramdesc=Span Faktor
dialog.compress.summarylabel=Punkte zu entfernen
dialog.pastecoordinates.desc=Geben Sie die Koordinaten ein
dialog.pastecoordinates.coords=Koordinaten
dialog.pastecoordinates.nothingfound=Bitte pr\u00fcfen Sie die Koordinaten und versuchen Sie es nochmals
-dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/prune/
+dialog.help.help=Weitere Informationen und Benutzeranleitungen finden Sie unter\n http://activityworkshop.net/software/gpsprune/
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\u00e4ten.
+dialog.about.summarytext1=GpsPrune ist ein Programm zum Laden, Darstellen und Editieren von Daten von GPS Ger\u00e4ten.
dialog.about.summarytext2=Es wird unter der Gnu GPL zur Verf\u00fcgung gestellt, zum freien, kostenlosen und offenen Gebrauch und zur Weiterentwicklung.<br>Kopieren, Weiterverbreitung und Ver\u00e4nderungen sind erlaubt und willkommen<br>unter den in der <code>license.txt</code> Datei enthaltenen Bedingungen.
dialog.about.summarytext3=Auf der Seite <code style="font-weight:bold">http://activityworkshop.net/</code> finden Sie weitere Informationen und Bedienungsanleitungen.
dialog.about.languages=Verf\u00fcgbare Sprachen
dialog.about.yes=Ja
dialog.about.no=Nein
dialog.about.credits=Credits
-dialog.about.credits.code=Prune Code geschrieben von
+dialog.about.credits.code=GpsPrune Code geschrieben von
dialog.about.credits.exifcode=Exif Code von
dialog.about.credits.icons=Einige Bilder von
dialog.about.credits.translators=Dolmetscher
dialog.about.credits.thanks=Dank an
dialog.about.readme=Liesmich
dialog.checkversion.error=Die Versionnummer konnte nicht ermittelt werden.\nBitte pr\u00fcfen Sie die Internet Verbindung.
-dialog.checkversion.uptodate=Sie haben schon die neueste Version von Prune.
-dialog.checkversion.newversion1=Eine neue Version vom Prune ist jetzt verf\u00fcgbar! Die neue Version ist Version
+dialog.checkversion.uptodate=Sie haben schon die neueste Version von GpsPrune.
+dialog.checkversion.newversion1=Eine neue Version vom GpsPrune ist jetzt verf\u00fcgbar! Die neue Version ist Version
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Diese neue Version ist am
dialog.checkversion.releasedate2=ver\u00f6ffentlicht worden.
-dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Um die neue Version herunterzuladen, gehen Sie zu http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=Anstelle der Maus k\u00f6nnen Sie folgende Tastenkombinationen nutzen
dialog.keys.keylist=<table><tr><td>Pfeil Tasten</td><td>Karte verschieben</td></tr><tr><td>Strg + links, rechts Pfeil</td><td>Vorherigen oder n\u00e4chsten Punkt markieren</td></tr><tr><td>Strg + auf, abw\u00e4rts Pfeil</td><td>Ein- oder Auszoomen</td></tr><tr><td>Strg + Bild auf, ab</td><td>Vorherigen oder n\u00e4chsten Segment markieren</td></tr><tr><td>Strg + Pos1, Ende</td><td>Ersten oder letzten Punkt markieren</td></tr><tr><td>Entf</td><td>Aktuellen Punkt entfernen</td></tr></table>
dialog.keys.normalmodifier=Strg
dialog.saveconfig.prune.colourscheme=Farbschema
dialog.saveconfig.prune.linewidth=Liniedicke
dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe
+dialog.saveconfig.prune.autosavesettings=Einstellungen speichern
dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Applikationen setzen:
dialog.setpaths.found=Pfad gefunden?
dialog.addaltitude.noaltitudes=Der markierte Bereich enth\u00e4lt keine H\u00f6henangaben
dialog.colourchooser.green=Gr\u00fcn
dialog.colourchooser.blue=Blau
dialog.setlanguage.firstintro=Sie k\u00f6nnen entweder eine von den mitgelieferten Sprachen<p>oder eine Text-Datei ausw\u00e4hlen.
-dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>Prune neu starten um die Sprache zu \u00e4ndern.
+dialog.setlanguage.secondintro=Sie m\u00fcssen Ihre Einstellungen speichern und dann<p>GpsPrune neu starten um die Sprache zu \u00e4ndern.
dialog.setlanguage.language=Sprache
dialog.setlanguage.languagefile=Sprachdatei
-dialog.setlanguage.endmessage=Speichern Sie nun Ihre Einstellungen und starten Sie Prune neu\num die neue Sprache zu verwenden.
+dialog.setlanguage.endmessage=Speichern Sie nun Ihre Einstellungen und starten Sie GpsPrune neu\num die neue Sprache zu verwenden.
+dialog.setlanguage.endmessagewithautosave=Starten Sie GpsPrune neu um die neue Sprache zu verwenden.
dialog.diskcache.save=Karten auf Festplatte speichern
dialog.diskcache.dir=Kartenordner
dialog.diskcache.createdir=Ordner anlegen
dialog.diskcache.nocreate=Ordner wurde nicht angelegt
+dialog.diskcache.table.path=Pfad
+dialog.diskcache.table.usedby=Anwender
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Kacheln
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Ordner
+dialog.diskcache.tileset.multiple=mehrere
+dialog.diskcache.deleteold=Veraltete Kacheln l\u00f6schen
+dialog.diskcache.deleteall=Alle Kacheln l\u00f6schen
+dialog.diskcache.deleted1=Es wurden
+dialog.diskcache.deleted2=Dateien aus dem Ordner gel\u00f6scht
dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, die Sie l\u00f6schen m\u00f6chten
dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4)
dialog.downloadosm.desc=Best\u00e4tigen um rohe OSM Daten f\u00fcr den Gebiet herunterzuladen:
dialog.searchwikipedianames.search=Suche nach:
# 3d window
-dialog.3d.title=Prune 3D Ansicht
-dialog.3d.altitudefactor=Vervielfachungsfaktor für Höhen
-dialog.3dlines.title=Prune Gitterlinien
+dialog.3d.title=GpsPrune 3D Ansicht
+dialog.3d.altitudefactor=Vervielfachungsfaktor f\u00fcr H\u00f6hen
+dialog.3dlines.title=GpsPrune Gitterlinien
dialog.3dlines.empty=Keine Linien zum Anzeigen!
dialog.3dlines.intro=Hier sind die Linien f\u00fcr die 3D Ansicht
confirm.media.connect=Media verbunden
confirm.photo.disconnect=Foto getrennt
confirm.audio.disconnect=Audio getrennt
+confirm.media.removed=entfernt
confirm.correlatephotos.single=Foto wurde korreliert
confirm.correlatephotos.multi=Fotos wurden korreliert
confirm.createpoint=Punkt erzeugt
confirm.running=In Bearbeitung ...
confirm.lookupsrtm1=Es wurden
confirm.lookupsrtm2=H\u00f6henwerte gefunden
-confirm.deletefieldvalues=Feldwerte gelöscht
+confirm.deletefieldvalues=Feldwerte gel\u00f6scht
confirm.audioload=Audiodateien geladen
-confirm.media.removed=entfernt
confirm.correlateaudios.single=Audio wurde korreliert
confirm.correlateaudios.multi=Audios wurden korreliert
button.finish=Fertig
button.cancel=Abbrechen
button.overwrite=\u00dcberschreiben
-button.moveup=Nach oben verschieben
-button.movedown=Nach unten verschieben
+button.moveup=Nach oben
+button.movedown=Nach unten
button.showlines=Linien anzeigen
button.edit=Bearbeiten
button.exit=Beenden
button.browse=Durchsuchen...
button.addnew=Hinzuf\u00fcgen
button.delete=Entfernen
+button.manage=Verwalten
# File types
filetype.txt=TXT Dateien
display.range.time.hours=h
display.range.time.days=T
details.range.avespeed=Durchschnittsgeschwindigkeit
-details.range.avemovingspeed=Durchschnittsgeschwindigkeit unterwegs
+details.range.avemovingspeed=Durchschnittsgeschwindigkeit gleitend
details.range.maxspeed=H\u00f6chstgeschwindigkeit
details.range.numsegments=Anzahl Abschnitte
details.range.pace=Tempo
details.range.gradient=Gef\u00e4lle
details.lists.waypoints=Wegpunkte
details.lists.photos=Fotos
+details.lists.audio=Audio
details.photodetails=Fotodetails
details.nophoto=Kein Foto ausgew\u00e4hlt
details.photo.loading=Laden
+details.photo.bearing=Richtung
details.media.connected=Verbunden
-details.lists.audio=Audio
details.audiodetails=Audiodetails
details.noaudio=Keine Audiodatei ausgew\u00e4hlt
details.audio.file=Audiodatei
fieldname.duration=Zeitdauer
fieldname.speed=Geschwindigkeit
fieldname.verticalspeed=Vertikale Geschwindigkeit
+fieldname.description=Beschreibung
# Measurement units
units.original=Original
undo.rotatephoto=Foto umdrehen
undo.convertnamestotimes=Namen in Zeitstempel umwandeln
undo.lookupsrtm=H\u00f6hendaten von SRTM holen
-undo.deletefieldvalues=Feldwerte löschen
+undo.deletefieldvalues=Feldwerte l\u00f6schen
undo.correlateaudios=Audios korrelieren
# Error messages
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\u00fcfen Sie die Internetverbindung.
-error.language.wrongfile=Die ausgew\u00e4hlte Datei scheint keine Sprachdatei f\u00fcr Prune zu sein
+error.language.wrongfile=Die ausgew\u00e4hlte Datei scheint keine Sprachdatei f\u00fcr GpsPrune zu sein
error.convertnamestotimes.nonames=Es konnten keine Namen umgewandelt werden
-error.lookupsrtm.nonefound=Keine H\u00f6hendaten verfügbar für diese Punkte
-error.lookupsrtm.nonerequired=Alle Punkte haben schon Höhendaten
+error.lookupsrtm.nonefound=Keine H\u00f6hendaten verf\u00fcgbar f\u00fcr diese Punkte
+error.lookupsrtm.nonerequired=Alle Punkte haben schon H\u00f6hendaten
error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet
error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen
error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen
+error.cache.notthere=Der Ordner wurde nicht gefunden
+error.cache.empty=Der Ordner ist leer
+error.cache.cannotdelete=Es konnte keine Kacheln gel\u00f6scht werden
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Swiss-German entries as extra
# Menu entries
menu.file=File
menu.file.addphotos=Fötelis innätue
+menu.file.recentfiles=Letzschti aagluegte Files
menu.file.save=Als Text Speichere
menu.file.exit=Beände
menu.track=Track
menu.view.showsidebars=Seiteleischten aazeige
menu.view.browser=Karte inem Browser
menu.settings=Iistellige
-menu.settings.onlinemode=Karte uusem Internet lade
+menu.settings.onlinemode=Karten uusem Internet lade
+menu.settings.autosave=Iistellige automatisch speichere
menu.help=Hilfe
# Popup menu for map
menu.map.zoomin=Innezoome
# Functions
function.open=File öffne
+function.importwithgpsbabel=mit GPSBabel importiere
function.loadfromgps=uusem GPS lade
function.sendtogps=zum GPS schicke
function.exportkml=KML exportierä
function.setlanguage=Sproch setzä
function.help=Hilfe
function.showkeys=Tastekombinatione aazeige
-function.about=Ãœber Prune
+function.about=Ãœber GpsPrune
function.checkversion=Pruef nach ne noie Version
function.saveconfig=Iistellige speichere
-function.diskcache=Karte uufem Disk speichere
+function.diskcache=Karten uufem Disk speichere
+function.managetilecache=Kartebildli verwolte
# Dialogs
-dialog.exit.confirm.title=Prune beände
+dialog.exit.confirm.title=GpsPrune beände
dialog.exit.confirm.text=Ihri Date sind nonig gspeicheret worde. Wend Sie trotzdem s Programm beände?
dialog.openappend.title=Date aahänge oder ersätze
dialog.openappend.text=Häng diese Date zur aktuelli Daten aa?
dialog.exportgpx.desc=Beschriibig
dialog.exportgpx.includetimestamps=Au Ziitstämpel
dialog.exportgpx.copysource=Xml-Quälle kopierä
+dialog.exportgpx.encoding=Enkodierig
+dialog.exportgpx.encoding.system=System
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Gäbet Sie die Parameter ii fürs POV Export
dialog.exportpov.font=Font
dialog.exportpov.camerax=Kamera X
dialog.compress.wackypoints.paramdesc=Distanz Faktor
dialog.compress.singletons.title=Singletons entfärnä
dialog.compress.singletons.paramdesc=Distanz faktor
+dialog.compress.douglaspeucker.title=Douglas-Peucker Komprimierig
+dialog.compress.douglaspeucker.paramdesc=Span 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.help.help=Bitte lueg na\n http://activityworkshop.net/software/gpsprune/\nfür wiitere Information und Benutzeraaleitige.
dialog.about.version=Version
dialog.about.build=Build
-dialog.about.summarytext1=Prune isch s Programm fürs Lade, Darstelle und Editiere vo Date von GPS Geräte.
+dialog.about.summarytext1=GpsPrune isch s Programm fürs Lade, Darstelle und Editiere vo Date von GPS Geräte.
dialog.about.summarytext2=Es isch unter den Gnu GPL zur Verfüegig gstellt,für frei, gratis und offen Gebruuch und Wiiterentwicklig.<br>Kopiere, Wiiterverbreitig und Veränderige sin erlaubt und willkomme<br>unter die Bedingige im enthaltene <code>license.txt</code> File.
dialog.about.summarytext3=Bitte lueget Sie na <code style="font-weight:bold">http://activityworkshop.net/</code> für wiitere Informatione und Benutzeraaleitige.
dialog.about.languages=Verfüegbare Sproche
dialog.about.yes=Ja
dialog.about.no=Nei
dialog.about.credits=Credits
-dialog.about.credits.code=Prune Code gschriebä vo
+dialog.about.credits.code=GpsPrune Code gschriebä vo
dialog.about.credits.exifcode=Exif Code vo
dialog.about.credits.icons=Einigi Bilder vo
dialog.about.credits.translators=Dolmätscher
dialog.about.credits.thanks=Danke an
dialog.about.readme=Läsmi
dialog.checkversion.error=Die Versionnummer könne nöd gefprüft werdä.\nGits ne internet Verbindig?
-dialog.checkversion.uptodate=Sie han die noischti Version vonem Prune scho.
-dialog.checkversion.newversion1=Ne noii Version vonem Prune isch jetzt usse! Die heisst jetzt Version
+dialog.checkversion.uptodate=Sie han die noischti Version vonem GpsPrune scho.
+dialog.checkversion.newversion1=Ne noii Version vonem GpsPrune isch jetzt usse! Die heisst jetzt Version
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Die noii Version isch am
dialog.checkversion.releasedate2=ussecho.
-dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=Aastatt d'Muus könnet Sie diese Tastekombinationen nutze
dialog.keys.keylist=<table><tr><td>Pfiil Taste</td><td>Karte verschiebe</td></tr><tr><td>Strg + links, rächts Pfiil</td><td>Vorherigi oder nöchsti Punkt markiere</td></tr><tr><td>Strg + uuf, aba Pfiil</td><td>Ii- oder Uusezoome</td></tr><tr><td>Strg + Bild uuf, ab</td><td>Vorherigi oder nöchsti Segmänt markiere</td></tr><tr><td>Strg + Pos1, Ände</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt lösche</td></tr></table>
dialog.keys.normalmodifier=Strg
dialog.saveconfig.prune.colourscheme=Farbeschema
dialog.saveconfig.prune.linewidth=Liniedicke
dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb
+dialog.saveconfig.prune.autosavesettings=Iistellige speichere
dialog.setpaths.intro=Sie könnet dann die Pfade für dia Applikatione setzä:
dialog.setpaths.found=Pfad gfunde?
dialog.addaltitude.noaltitudes=Dr seläktierte Beriich hät keini Höchiinformation
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.secondintro=Sie münt Ihri Iistellige speichere und dann<p>GpsPrune 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.
+dialog.setlanguage.endmessage=Jetze speicheret Sie Ihri Iistellige und startet Sie GpsPrune neu\num t noii Sproch z' verwände.
+dialog.setlanguage.endmessagewithautosave=Startet Sie GpsPrune neu um t noii Sproch z' verwände.
dialog.diskcache.save=Karten uufem Disk speichere
dialog.diskcache.dir=Kartenordner
dialog.diskcache.createdir=Ordner kreiere
dialog.diskcache.nocreate=Ordner isch nöd kreiert worde
+dialog.diskcache.table.path=Pfad
+dialog.diskcache.table.usedby=Aawänder
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Kachle
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Ordner
+dialog.diskcache.tileset.multiple=mehreri
+dialog.diskcache.deleteold=Uualti Kachle l\u00f6sche
+dialog.diskcache.deleteall=Alli Kachle l\u00f6sche
+dialog.diskcache.deleted1=Es sin
+dialog.diskcache.deleted2=Files uusem Ordner gl\u00f6scht worde
dialog.deletefieldvalues.intro=Wählet Sie s Fäld uus zum lösche
dialog.setlinewidth.text=Gäbet Sie die Dicke vonen Linien ii (1-4)
dialog.downloadosm.desc=Best\ätige um rohi OSM Date fürn Gebiet aba zlade:
dialog.searchwikipedianames.search=Sueche na:
# 3d window
-dialog.3d.title=Prune Drüü-d Aasicht
+dialog.3d.title=GpsPrune Drüü-d Aasicht
dialog.3d.altitudefactor=Höchivervilfachigsfaktor
-dialog.3dlines.title=Prune Gitterlinie
+dialog.3dlines.title=GpsPrune Gitterlinie
dialog.3dlines.empty=Kei Linie zum aazeigä!
dialog.3dlines.intro=Hier sin die Linie für die drüü-D Aasicht
button.browse=Durasuechä...
button.addnew=Hinzuefügä
button.delete=Entfärnä
+button.manage=Verwoltä
# File types
filetype.txt=TXT Dateie
details.photodetails=Details vonem Föteli
details.nophoto=Kei föteli selektiert
details.photo.loading=Ladä
+details.photo.bearing=Richtig
details.media.connected=Verbundä
details.lists.audio=Audio
details.audiodetails=Audiodetails
fieldname.duration=Ziitlängi
fieldname.speed=Gschwindikeit
fieldname.verticalspeed=Uf/Ab Gschwindikeit
+fieldname.description=Bschriibig
# Measurement units
units.original=Original
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.language.wrongfile=Die uusgewählti Datei scheint kei Sproch-Datei für GpsPrune z'sii
error.convertnamestotimes.nonames=Kei Namen han könnet verwondlet werde
error.lookupsrtm.nonefound=Kei Höhendate verfüegbar für d'Punkte
error.lookupsrtm.nonerequired=Alle Punkte han die Höhendate scho. Nüüt z'tue.
error.gpsies.uploadnotok=Der Gpsies Server hät gseit gha
error.gpsies.uploadfailed=S Uufalade isch fehlgschlage
error.playaudiofailed=S Abschpiele vonem File isch fehlgschlage
+error.cache.notthere=D Ordner isch nöd gfunde worde
+error.cache.empty=D Ordner hät nüüt drinne
+error.cache.cannotdelete=Es sin kei Kachle gl\u00f6scht worde
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# English entries as default - others can be added
# Menu entries
menu.file=File
menu.file.addphotos=Add photos
+menu.file.recentfiles=Recent files
menu.file.save=Save as text
menu.file.exit=Exit
menu.track=Track
menu.view.browser.bing=Bing maps
menu.settings=Settings
menu.settings.onlinemode=Load maps from internet
+menu.settings.autosave=Autosave settings on exit
menu.help=Help
# Popup menu for map
menu.map.zoomin=Zoom in
# Functions
function.open=Open file
+function.importwithgpsbabel=Import file with GPSBabel
function.loadfromgps=Load data from GPS
function.sendtogps=Send data to GPS
function.exportkml=Export KML
function.rotatephotoright=Rotate photo right
function.photopopup=Show photo popup
function.ignoreexifthumb=Ignore exif thumbnail
-function.loadaudio=Add audio files
-function.removeaudio=Remove audio file
+function.loadaudio=Add audio clips
+function.removeaudio=Remove audio clip
function.correlateaudios=Correlate audios
-function.playaudio=Play audio file
-function.stopaudio=Stop audio file
+function.playaudio=Play audio clip
+function.stopaudio=Stop audio clip
function.setmapbg=Set map background
function.setkmzimagesize=Set KMZ image size
function.setpaths=Set program paths
function.setlanguage=Set language
function.help=Help
function.showkeys=Show shortcut keys
-function.about=About Prune
+function.about=About GpsPrune
function.checkversion=Check for new version
function.saveconfig=Save settings
function.diskcache=Save maps to disk
+function.managetilecache=Manage tile cache
# Dialogs
-dialog.exit.confirm.title=Exit Prune
+dialog.exit.confirm.title=Exit GpsPrune
dialog.exit.confirm.text=Your data is not saved. Are you sure you want to exit?
dialog.openappend.title=Append to existing data
dialog.openappend.text=Append this data to the data already loaded?
dialog.exportgpx.desc=Description
dialog.exportgpx.includetimestamps=Include timestamps
dialog.exportgpx.copysource=Copy source xml
+dialog.exportgpx.encoding=Encoding
+dialog.exportgpx.encoding.system=System
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Please enter the parameters for the POV export
dialog.exportpov.font=Font
dialog.exportpov.camerax=Camera X
dialog.compress.wackypoints.paramdesc=Distance factor
dialog.compress.singletons.title=Singleton removal
dialog.compress.singletons.paramdesc=Distance factor
+dialog.compress.douglaspeucker.title=Douglas-Peucker compression
+dialog.compress.douglaspeucker.paramdesc=Span 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.help.help=Please see\n http://activityworkshop.net/software/gpsprune/\nfor more information and user guides.
dialog.about.version=Version
dialog.about.build=Build
-dialog.about.summarytext1=Prune is a program for loading, displaying and editing data from GPS receivers.
+dialog.about.summarytext1=GpsPrune is a program for loading, displaying and editing data from GPS receivers.
dialog.about.summarytext2=It is released under the Gnu GPL for free, open, worldwide use and enhancement.<br>Copying, redistribution and modification are permitted and encouraged<br>according to the conditions in the included <code>license.txt</code> file.
dialog.about.summarytext3=Please see <code style="font-weight:bold">http://activityworkshop.net/</code> for more information and user guides.
dialog.about.languages=Available languages
dialog.about.yes=Yes
dialog.about.no=No
dialog.about.credits=Credits
-dialog.about.credits.code=Prune code written by
+dialog.about.credits.code=GpsPrune code written by
dialog.about.credits.exifcode=Exif code by
dialog.about.credits.icons=Some icons taken from
dialog.about.credits.translators=Translators
dialog.about.credits.thanks=Thanks to
dialog.about.readme=Readme
dialog.checkversion.error=The version number couldn't be checked.\nPlease check the internet connection.
-dialog.checkversion.uptodate=You are using the latest version of Prune.
-dialog.checkversion.newversion1=A new version of Prune is now available! The latest version is now version
+dialog.checkversion.uptodate=You are using the latest version of GpsPrune.
+dialog.checkversion.newversion1=A new version of GpsPrune is now available! The latest version is now version
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=This new version was released on
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=To download the new version, go to http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=To download the new version, go to http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=You can use the following shortcut keys instead of using the mouse
dialog.keys.keylist=<table><tr><td>Arrow keys</td><td>Pan map left right, up, down</td></tr><tr><td>Ctrl + left, right arrow</td><td>Select previous or next point</td></tr><tr><td>Ctrl + up, down arrow</td><td>Zoom in or out</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Select previous, next segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select first, last point</td></tr><tr><td>Del</td><td>Delete current point</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Colour scheme
dialog.saveconfig.prune.linewidth=Line width
dialog.saveconfig.prune.kmltrackcolour=KML track colour
+dialog.saveconfig.prune.autosavesettings=Autosave settings
dialog.setpaths.intro=If you need to, you can choose the paths to the external applications:
dialog.setpaths.found=Path found?
dialog.addaltitude.noaltitudes=The selected range does not contain altitudes
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.secondintro=You need to save your settings and then<p>restart GpsPrune 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.
+dialog.setlanguage.endmessage=Now save your settings and restart GpsPrune\nfor the language change to take effect.
+dialog.setlanguage.endmessagewithautosave=Please restart GpsPrune for the language change to take effect.
dialog.diskcache.save=Save map images to disk
dialog.diskcache.dir=Cache directory
dialog.diskcache.createdir=Create directory
dialog.diskcache.nocreate=Cache directory not created
+dialog.diskcache.table.path=Path
+dialog.diskcache.table.usedby=Used by
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Tiles
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Tileset
+dialog.diskcache.tileset.multiple=multiple
+dialog.diskcache.deleteold=Delete old tiles
+dialog.diskcache.deleteall=Delete all tiles
+dialog.diskcache.deleted1=Deleted
+dialog.diskcache.deleted2=files from the cache
dialog.deletefieldvalues.intro=Select the field to delete for the current range
dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4)
dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
dialog.searchwikipedianames.search=Search for:
# 3d window
-dialog.3d.title=Prune Three-d view
+dialog.3d.title=GpsPrune Three-d view
dialog.3d.altitudefactor=Altitude exaggeration factor
-dialog.3dlines.title=Prune gridlines
+dialog.3dlines.title=GpsPrune gridlines
dialog.3dlines.empty=No gridlines to display!
dialog.3dlines.intro=These are the gridlines for the three-d view
button.browse=Browse...
button.addnew=Add new
button.delete=Delete
+button.manage=Manage
# File types
filetype.txt=TXT files
details.photodetails=Photo details
details.nophoto=No photo selected
details.photo.loading=Loading
+details.photo.bearing=Bearing
details.media.connected=Connected
details.audiodetails=Audio details
-details.noaudio=No audio file selected
+details.noaudio=No audio clip selected
details.audio.file=Audio file
details.audio.playing=playing...
map.overzoom=No maps available at this zoom level
fieldname.duration=Duration
fieldname.speed=Speed
fieldname.verticalspeed=Vertical speed
+fieldname.description=Description
# Measurement units
units.original=Original
# Undo operations
undo.load=load data
undo.loadphotos=load photos
-undo.loadaudios=load audio files
+undo.loadaudios=load audio clips
undo.editpoint=edit point
undo.deletepoint=delete point
undo.removephoto=remove photo
-undo.removeaudio=remove audio file
+undo.removeaudio=remove audio clip
undo.deleterange=delete range
undo.compress=compress track
undo.insert=insert points
error.jpegload.nojpegsfound=No jpeg files found
error.jpegload.nogpsfound=No GPS information found
error.jpegload.exifreadfailed=Failed to read EXIF information. No EXIF information can be read\nwithout either an internal or external library.
-error.audioload.nofilesfound=No audio files found
+error.audioload.nofilesfound=No audio clips found
error.gpsload.unknown=Unknown error
error.undofailed.title=Undo failed
error.undofailed.text=Failed to undo operation
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.language.wrongfile=The selected file doesn't appear to be a language file for GpsPrune
error.convertnamestotimes.nonames=No names could be converted into times
error.lookupsrtm.nonefound=No altitude values available for these points
error.lookupsrtm.nonerequired=All points already have altitudes, so there's nothing to lookup
error.gpsies.uploadnotok=The gpsies server returned the message
error.gpsies.uploadfailed=The upload failed with the error
-error.playaudiofailed=Failed to play audio file
+error.playaudiofailed=Failed to play audio clip
+error.cache.notthere=The tile cache directory was not found
+error.cache.empty=The tile cache directory is empty
+error.cache.cannotdelete=No tiles could be deleted
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Spanish entries as extra
# Menu entries
menu.file=Archivo
menu.file.addphotos=Cargar fotos
+menu.file.recentfiles=Archivos recientes
menu.file.save=Guardar
menu.file.exit=Salir
menu.track=Track
menu.view.browser.bing=Mapas Bing
menu.settings=Preferencias
menu.settings.onlinemode=Cargar mapas de Internet
+menu.settings.autosave=Auto Guardar
menu.help=Ayuda
# Popup menu for map
menu.map.zoomin=Ampliar zoom
# Functions
function.open=Abrir archivo
+function.importwithgpsbabel=Importar archivo con GPSBabel
function.loadfromgps=Cargar datos del GPS
function.sendtogps=Enviar datos al GPS
function.exportkml=Exportar KML
function.stopaudio=Detener reproducci\u00f3n de audio
function.help=Ayuda
function.showkeys=Mostrar teclas o combinaciones de atajo
-function.about=Acerca de Prune
+function.about=Acerca de GpsPrune
function.checkversion=Buscar una nueva versi\u00f3n
function.saveconfig=Guardar preferencias
function.diskcache=Guardar mapas en disco
+function.managetilecache=Administrar cache de mapas
# Dialogs
-dialog.exit.confirm.title=Salir de Prune
-dialog.exit.confirm.text=\u00bfLos datos han sido modificados. Desea salir de Prune?
+dialog.exit.confirm.title=Salir de GpsPrune
+dialog.exit.confirm.text=\u00bfLos datos han sido modificados. Desea salir de GpsPrune?
dialog.openappend.title=\u00bfAgregar a datos existentes
dialog.openappend.text=\u00bfAgregar estos datos a los datos ya guardados?
dialog.deletepoint.title=Borrar punto
dialog.exportgpx.desc=Descripci\u00f3n
dialog.exportgpx.includetimestamps=Tiempo tambien
dialog.exportgpx.copysource=Copiar la fuente
+dialog.exportgpx.encoding=Codificaci\u00f3n
+dialog.exportgpx.encoding.system=Sistema
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Introdzca los Parametros para exportar
dialog.exportpov.font=Fuente
dialog.exportpov.camerax=C\u00e1mara X
dialog.compress.singletons.title=Eliminar puntos aislados
dialog.compress.singletons.paramdesc=Factor distancia
dialog.compress.duplicates.title=Eliminar duplicados
+dialog.compress.douglaspeucker.title=Compresion Douglas-Peucker
+dialog.compress.douglaspeucker.paramdesc=Factor de extensi\u00f3n
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.help.help=Por favor, ver\n http://activityworkshop.net/software/gpsprune/\npara m\u00e1s informaci\u00f3n y gu\u00edas del usuario.
dialog.about.version=Versi\u00f3n
dialog.about.build=Construcci\u00f3n
-dialog.about.summarytext1=Prune es un programa para cargar, mostrar y editar datos de receptores GPS.
+dialog.about.summarytext1=GpsPrune 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.
dialog.about.languages=Idiomas disponibles
dialog.about.yes=Si
dialog.about.no=No
dialog.about.credits=Cr\u00e9ditos
-dialog.about.credits.code=El c\u00f3digo de Prune fue escrito por
+dialog.about.credits.code=El c\u00f3digo de GpsPrune fue escrito por
dialog.about.credits.exifcode=El c\u00f3digo Exif por
dialog.about.credits.icons=Algunos iconos se tomaron de
dialog.about.credits.translators=Traductores
dialog.about.credits.thanks=Gracias a
dialog.about.readme=Readme
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 \u00faltima es ahora la versi\u00f3n
+dialog.checkversion.uptodate=Esta usted utilizando la \u00faltima versi\u00f3n de GpsPrune
+dialog.checkversion.newversion1=¡Una nueva versi\u00f3n de GpsPrune est\u00e1 disponible! La \u00faltima es ahora la versi\u00f3n
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=La nueva versi\u00f3n fue lanzada en
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Para descargar la nueva versi\u00f3n visite http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Para descargar la nueva versi\u00f3n visite http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=Usted puede usar el siguiente atajo en lugar de usar el rat\u00f3n
dialog.keys.keylist=<table><tr><td>Teclas de cursor</td><td>Desplazar a la izquierde, derecha, arriba, abajo</td></tr><tr><td>Ctrl + cursor izquierda, derecha</td><td>Seleccionar punto siguiente o anterior</td></tr><tr><td>Ctrl + cursor arriba, abajo</td><td>Ampliar o reducir zoom</td></tr><tr><td>Ctrl + Av Pag, Re Pag</td><td>Seleccionar segmento siguiente, anterior</td></tr><tr><td>Ctrl + Inicio, Fin</td><td>Seleccionar primer, \u00faltimo punto</td></tr><tr><td>Supr</td><td>Eliminar punto actual</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
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.colourscheme=Color de esquema
dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea
dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML
+dialog.saveconfig.prune.autosavesettings=Guardar preferencias al salir
dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas
dialog.setpaths.found=\u00bfRuta encontrada?
dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes
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 guardar sus preferencias y luego<p>reiniciar Prune para cambiar el lenguaje
+dialog.setlanguage.secondintro=Usted necesita guardar sus preferencias y luego<p>reiniciar GpsPrune para cambiar el lenguaje
dialog.setlanguage.language=Lenguaje
dialog.setlanguage.languagefile=Archivo de lenguaje
-dialog.setlanguage.endmessage=Ahora guarde sus preferencias y reinicie Prune\npara que los cambios tomen efecto.
+dialog.setlanguage.endmessage=Ahora guarde sus preferencias y reinicie GpsPrune\npara que los cambios tomen efecto.
+dialog.setlanguage.endmessagewithautosave=Ahora reinicie GpsPrune para que los cambios tomen efecto.
dialog.diskcache.save=Guardar im\u00e1genes de mapa a disco
dialog.diskcache.dir=Directorio de mapas
dialog.diskcache.createdir=Crear directorio
dialog.diskcache.nocreate=No se ha creado el directorio de mapas
+dialog.diskcache.table.path=Ruta
+dialog.diskcache.table.usedby=Utilizado por
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Recuadros
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Conjunto de recuadros
+dialog.diskcache.tileset.multiple=varios
+dialog.diskcache.deleteold=Borrar recuadros antiguos
+dialog.diskcache.deleteall=Borrar todos los recuadros
+dialog.diskcache.deleted1=Borrado
+dialog.diskcache.deleted2=Archivos del cache
dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual
dialog.setlinewidth.text=Introduzca la anchura de las l\u00edneas a dibujar para los recorridos (1-4)
dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada.
dialog.searchwikipedianames.search=Buscar:
# 3d window
-dialog.3d.title=Prune vista 3-D
+dialog.3d.title=GpsPrune vista 3-D
dialog.3d.altitudefactor=Factor de exageraci\u00f3n de altura
-dialog.3dlines.title=Cuadr\u00edcula Prune
+dialog.3dlines.title=Cuadr\u00edcula GpsPrune
dialog.3dlines.empty=¡No hay ninguna cuadr\u00edcula!
dialog.3dlines.intro=Informaci\u00f3n de la cuadr\u00edcula
button.browse=Navegar...
button.addnew=A\u00f1adir nuevo
button.delete=Eliminar
+button.manage=Administrar
# File types
filetype.txt=Archivos TXT
details.photodetails=Detalles de la foto
details.nophoto=Ninguna foto seleccionada
details.photo.loading=Cargando
+details.photo.bearing=Rumbo
details.media.connected=Conectada
details.audiodetails=Detalles de audio
details.noaudio=No se ha seleccionado ning\u00fan archivo de audio
fieldname.duration=Duraci\u00f3n
fieldname.speed=Velocidad
fieldname.verticalspeed=Velocidad vertical
+fieldname.description=Descripci\u00f3n
# Measurement units
units.original=Original
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.language.wrongfile=El archivo seleccionado no parece ser un archivo de lenguaje para GpsPrune
error.convertnamestotimes.nonames=Los nombres no pudieron ser convertidos en tiempos
error.lookupsrtm.nonefound=No se encontraron valores de altitud
error.lookupsrtm.nonerequired=Todos los puntos tienen altitudes, as\u00ed que no hay nada que buscar.
error.gpsies.uploadnotok=El servidor de gpsies ha devuelto el mensaje
error.gpsies.uploadfailed=La carga ha fallado con el error
error.playaudiofailed=Fallo reproduciendo archivo de audio
+error.cache.notthere=No se encontr\u00f3 la carpeta del cache de recuadros
+error.cache.empty=La carpeta del cache de recuadros esta vac\u00edo
+error.cache.cannotdelete=No se pudieron borrar recuadros
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# French entries as extra
# Menu entries
menu.file=Fichier
menu.file.addphotos=Ajouter photos
+menu.file.recentfiles=Derniers Fichiers utilis\u00e9s
menu.file.save=Enregistrer
menu.file.exit=Quitter
menu.track=Trace
menu.range.mergetracksegments=Fusionner les segments de trace
menu.range.cutandmove=Couper et bouger la s\u00e9lection
menu.point=Point
-menu.point.editpoint=Editer le point
+menu.point.editpoint=\u00c9diter le point
menu.point.deletepoint=Supprimer le point
menu.photo=Photo
menu.photo.saveexif=Enregistrer dans les Exif
-function.connecttopoint=Relier au point
-function.disconnectfrompoint=D\u00e9tacher du point
-function.removephoto=Retirer la photo
+menu.audio=Audio
menu.view=Affichage
+menu.view.showsidebars=Montrer les barres lat\u00e9rales
menu.view.browser=Ouvrir la carte dans le navigateur
menu.view.browser.google=Google maps
menu.view.browser.openstreetmap=Openstreetmap
menu.view.browser.yahoo=Yahoo maps
menu.view.browser.bing=Cartes dans Bing
menu.settings=Pr\u00e9f\u00e9rences
-menu.settings.onlinemode=Charger une carte depuis internet
+menu.settings.onlinemode=Charger cartes depuis internet
+menu.settings.autosave=Sauver automatiquement en quittant
menu.help=Aide
# Popup menu for map
menu.map.zoomin=Zoom avant
menu.map.zoomout=Zoom arri\u00e8re
menu.map.zoomfull=Adapter \u00e0 la vue
menu.map.newpoint=Ajouter un point
+menu.map.drawpoints=Ajouter une s\u00e9rie des points
menu.map.connect=Relier les points de trace
menu.map.autopan=D\u00e9placement automatique
menu.map.showmap=Montrer la carte
altkey.menu.point=P
altkey.menu.view=A
altkey.menu.photo=H
+altkey.menu.audio=U
altkey.menu.settings=R
altkey.menu.help=I
function.exportkml=Exporter en KML
function.exportgpx=Exporter en GPX
function.exportpov=Exporter en POV
-function.editwaypointname=Editer le nom du waypoint
+function.exportsvg=Exporter en SVG
+function.editwaypointname=\u00c9diter 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.deletefieldvalues=Effacer les valeurs du champ
function.findwaypoint=Trouver un waypoint
function.pastecoordinates=Coller les coordonn\u00e9es
function.charts=Graphiques
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.uploadgpsies=T\u00e9l\u00e9charger la trace sur Gpsies
function.lookupsrtm=R\u00e9cup\u00e9rer les altitudes depuis SRTM
-function.duplicatepoint=Duppliquer le point
-function.setcolours=R\u00e9gler les couleurs
-function.setlanguage=R\u00e9gler la langue
+function.getwikipedia=Obtenir les articles de Wikip\u00e9dia \u00e0 proxilit\u00e9
+function.searchwikipedianames=Rechercher dans Wikip\u00e9dia par nom
+function.downloadosm=T\u00e9l\u00e9charger les donn\u00e9es OSM de la zone
+function.duplicatepoint=Dupliquer le point
+function.setcolours=Choisir les couleurs
+function.setlinewidth=Choisir la largeur de ligne
+function.setlanguage=Choisir la langue
+function.connecttopoint=Relier au point
+function.disconnectfrompoint=D\u00e9tacher du point
+function.removephoto=Retirer la photo
function.correlatephotos=Corr\u00e9ler les photos
function.rearrangephotos=R\u00e9arranger les photos
function.rotatephotoleft=Tourner la photo vers la gauche
function.rotatephotoright=Tourner la photo vers la droite
function.photopopup=Montrer la photo
function.ignoreexifthumb=Ignorer l\u2019aper\u00e7u Exif
+function.loadaudio=Ajouter des fichiers audio
+function.removeaudio=Retirer des fichiers audio
+function.correlateaudios=Corr\u00e9ler les fichiers audio
+function.playaudio=Lire le fichier audio
+function.stopaudio=Arr\u00eater la lecture du fichier audio
function.help=Aide
function.showkeys=Montrer les raccourcis clavier
-function.about=À propos de Prune
+function.about=\u00c0 propos de GpsPrune
function.checkversion=Chercher une mise \u00e0 jour
function.saveconfig=Enregistrer les pr\u00e9f\u00e9rences
function.diskcache=Enregistrer les cartes sur le disque
# Dialogs
-dialog.exit.confirm.title=Quitter Prune
-dialog.exit.confirm.text=Les donn\u00e9es ont \u00e9t\u00e9 modifi\u00e9es. Souhaitez-vous quitter Prune sans les enregistrer ?
+dialog.exit.confirm.title=Quitter GpsPrune
+dialog.exit.confirm.text=Les donn\u00e9es ont \u00e9t\u00e9 modifi\u00e9es. Souhaitez-vous quitter GpsPrune sans les enregistrer ?
dialog.openappend.title=Ajouter aux donn\u00e9es existantes
dialog.openappend.text=Ajouter aux donn\u00e9es d\u00e9j\u00e0 charg\u00e9es ?
dialog.deletepoint.title=Effacer le point
dialog.openoptions.deliminfo.fields=champs
dialog.openoptions.deliminfo.norecords=Pas d'enregistrements
dialog.openoptions.altitudeunits=Unit\u00e9s d'altitude
+dialog.open.contentsdoubled=Ce fichier contient deux copies de chaque point,\nune fois comme waypoint, une autre comme point de trace.
+dialog.selecttracks.intro=S\u00e9lectionner la ou les traces \u00e0 charger
+dialog.selecttracks.noname=Sans titre
dialog.jpegload.subdirectories=Inclure les sous-dossiers
dialog.jpegload.loadjpegswithoutcoords=Inclure les photos sans coordonn\u00e9es
dialog.jpegload.loadjpegsoutsidearea=Inclure des photos en dehors de la zone actuel
dialog.exportgpx.desc=L\u00e9gende
dialog.exportgpx.includetimestamps=Inclure l'heure pour chaque point
dialog.exportgpx.copysource=Copier la source xml
+dialog.exportgpx.encoding.system=Syst\u00e8me
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Entrez les param\u00e8tres pour l'export POV
dialog.exportpov.font=Police
dialog.exportpov.camerax=Cam\u00e9ra X
dialog.exportpov.ballsandsticks=Points et b\u00e2tons
dialog.exportpov.tubesandwalls=Tubes et murs
dialog.exportpov.warningtracksize=Cette trace poss\u00e8de un grand nombre de points, Java3D peut ne pas pouvoir l'afficher.\n\u00cates-vous s\u00fbr de vouloir continuer ?
+dialog.exportsvg.text=S\u00e9lectionner les param\u00e8tres de l'export SVG
+dialog.exportsvg.phi=Angle d'azimuth \u03d5
+dialog.exportsvg.theta=Angle d'\u00e9l\u00e9vation \u03b8
+dialog.exportsvg.gradients=Utiliser des d\u00e9grad\u00e9s pour l'ombrage
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.audio=Points audio
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.undo.none.title=Annulation impossible
dialog.undo.none.text=Pas d'op\u00e9ration \u00e0 annuler !
dialog.clearundo.title=Purger la liste d'annulation
-dialog.clearundo.text=Etes-vous s\u00fbr de vouloir effacer la liste d'annulation ?\nToutes les informations d'annulation seront perdues !
-dialog.pointedit.title=Editer le point
-dialog.pointedit.text=S\u00e9lectionner chaque champ \u00e0 \u00e9diter et utiliser le bouton 'Editer' pour changer la valeur
+dialog.clearundo.text=\u00cates-vous s\u00fbr de vouloir effacer la liste d'annulation ?\nToutes les informations d'annulation seront perdues !
+dialog.pointedit.title=\u00c9diter le point
+dialog.pointedit.text=S\u00e9lectionner chaque champ \u00e0 \u00e9diter et utiliser le bouton '\u00c9diter' pour changer la valeur
dialog.pointedit.table.field=Champ
dialog.pointedit.table.value=Valeur
dialog.pointedit.table.changed=Chang\u00e9
dialog.pointedit.changevalue.text=Entrer la nouvelle valeur pour ce champ
-dialog.pointedit.changevalue.title=Editer le champ
+dialog.pointedit.changevalue.title=\u00c9diter le champ
dialog.pointnameedit.name=Nom de waypoint
dialog.pointnameedit.uppercase=CASSE MAJUSCULES
dialog.pointnameedit.lowercase=casse minuscules
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.overwrite=\u00c9craser les fichiers
dialog.saveexif.force=Ignorer les erreurs mineures
dialog.charts.xaxis=Axe des x
dialog.charts.yaxis=Axe des y
dialog.addmapsource.layer1url=URL de la premi\u00e8re couche
dialog.addmapsource.layer2url=URL optionnelle de la deuxi\u00e8me couche
dialog.addmapsource.maxzoom=Niveau de zoom maximum
-dialog.addmapsource.cloudstyle=Taille
-dialog.addmapsource.noname=Sans-titre
+dialog.addmapsource.cloudstyle=Num\u00e9ro du style
+dialog.addmapsource.noname=Sans titre
dialog.gpsies.column.name=Nom de trace
dialog.gpsies.column.length=Distance
dialog.gpsies.description=Description
dialog.gpsies.nodescription=Aucune description
-dialog.gpsies.nonefound=Aucun trace trouv\u00e9
+dialog.gpsies.nonefound=Aucune trace trouv\u00e9e
+dialog.gpsies.username=Nom d'utilisateur Gpsies
+dialog.gpsies.password=Mot de passe Gpsies
+dialog.gpsies.keepprivate=Trace priv\u00e9e
+dialog.gpsies.confirmopenpage=Ouvrir la page web de la trace t\u00e9l\u00e9charg\u00e9e ?
dialog.gpsies.activities=Activit\u00e9
dialog.gpsies.activity.trekking=Trekking
dialog.gpsies.activity.walking=Randonn\u00e9e
dialog.gpsies.activity.snowshoe=Raquette
dialog.gpsies.activity.sailing=Volle
dialog.gpsies.activity.skating=Skating
+dialog.wikipedia.column.name=Nom de l'article
+dialog.wikipedia.column.distance=Distance
dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler.
dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ?
dialog.correlate.photoselect.intro=S\u00e9lectionner une de ces photos corr\u00e9l\u00e9es pour d\u00e9finir le d\u00e9calage de temps
dialog.correlate.options.offset.seconds=secondes
dialog.correlate.options.photolater=Photo post\u00e9rieure au point
dialog.correlate.options.pointlaterphoto=Point post\u00e9rieur \u00e0 la photo
+dialog.correlate.options.audiolater=Audio post\u00e9rieur au point
+dialog.correlate.options.pointlateraudio=Point post\u00e9rieur \u00e0 l'audio
dialog.correlate.options.limitspanel=Limites de corr\u00e9lation
dialog.correlate.options.notimelimit=Pas de limite de temps
dialog.correlate.options.timelimit=Limite de temps
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.correlate.filetimes=La date du fichier indique :
+dialog.correlate.filetimes2=de l'extrait audio
+dialog.correlate.correltimes=Pour la corr\u00e9lation, utiliser :
+dialog.correlate.timestamp.beginning=Le d\u00e9but
+dialog.correlate.timestamp.middle=Le milieu
+dialog.correlate.timestamp.end=La fin
+dialog.correlate.audioselect.intro=Choisir un de ces fichiers audio corr\u00e9l\u00e9s comme d\u00e9calage de temps
+dialog.correlate.select.audioname=Nom du fichier audio
+dialog.correlate.select.audiolater=Audio apr\u00e8s
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.compress.singletons.title=Suppression des points isol\u00e9s
dialog.compress.singletons.paramdesc=Distance
dialog.compress.duplicates.title=Suppression des doublons
+dialog.compress.douglaspeucker.title=Compression Douglas-Peucker
+dialog.compress.douglaspeucker.paramdesc=Taille du voisinage
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.help.help=Consultez la page\n http://activityworkshop.net/software/gpsprune/\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.summarytext1=GpsPrune 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.languages=Langues disponibles
dialog.about.yes=Oui
dialog.about.no=Non
dialog.about.credits=Cr\u00e9dits
-dialog.about.credits.code=Code de Prune \u00e9crit par
+dialog.about.credits.code=Code de GpsPrune \u00e9crit par
dialog.about.credits.exifcode=Code Exif par
dialog.about.credits.icons=Quelques ic\u00f4nes provenant de
dialog.about.credits.translators=Interpr\u00e8tes
dialog.about.credits.thanks=Merci \u00e0
dialog.about.readme=Lisez-moi
dialog.checkversion.error=Ne peut pas v\u00e9rifier la version.\nVeuillez v\u00e9rifier votre connexion Internet.
-dialog.checkversion.uptodate=Vous utilisez d\u00e9j\u00e0 la version actuelle de Prune.
+dialog.checkversion.uptodate=Vous utilisez d\u00e9j\u00e0 la version actuelle de GpsPrune.
dialog.checkversion.newversion1=La version actuelle est maintenant la version
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=La nouvelle version est sortie le
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Pour t\u00e9l\u00e9charger la nouvelle version, aller \u00e0 http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Pour t\u00e9l\u00e9charger la nouvelle version, aller \u00e0 http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=Vous pouvez utiliser ces raccourcis clavier \u00e0 la place de la souris
-dialog.keys.keylist=<table><tr><td>Touches-fl\u00e8ches</td><td>Faire d\u00e9filer la carte horizontallement et verticallement</td></tr><tr><td>Ctrl + gauche, Ctrl + droite</td><td>Choisir le point pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + haut, Ctrl + bas</td><td>Zoomer, s'\u00e9loigner</td></tr><tr><td>Suppr</td><td>Effacer le point courant</td></tr></table>
+dialog.keys.keylist=<table><tr><td>Touches-fl\u00e8ches</td><td>Faire d\u00e9filer la carte horizontalement et verticalement</td></tr><tr><td>Ctrl + gauche, Ctrl + droite</td><td>Choisir le point pr\u00e9c\u00e9dent ou suivant</td></tr><tr><td>Ctrl + haut, Ctrl + bas</td><td>Zoomer, s'\u00e9loigner</td></tr><tr><td>Suppr</td><td>Effacer le point courant</td></tr></table>
+dialog.keys.normalmodifier=Ctrl
+dialog.keys.macmodifier=Command
dialog.saveconfig.desc=Les param\u00e8tres suivants peuvent \u00eatre sauvegard\u00e9s dans un fichier de configuration:
dialog.saveconfig.prune.trackdirectory=Dossier des traces
dialog.saveconfig.prune.photodirectory=Dossier des Photos
dialog.saveconfig.prune.gnuplotpath=Chemin gnuplot
dialog.saveconfig.prune.gpsbabelpath=Chemin gpsbabel
dialog.saveconfig.prune.exiftoolpath=Chemin exiftool
-dialog.saveconfig.prune.mapserverindex=Index du serveur de carte
-dialog.saveconfig.prune.mapserverurl=URL du serveur de carte
dialog.saveconfig.prune.mapsource=Carte source s\u00e9lectionn\u00e9e
dialog.saveconfig.prune.mapsourcelist=Sources de cartes
dialog.saveconfig.prune.diskcache=Cache de carte
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.linewidth=Largeur de ligne
dialog.saveconfig.prune.kmltrackcolour=Couleur de la trace KML
+dialog.saveconfig.prune.autosavesettings=R\u00e9glages de sauvegarde automatique
dialog.setpaths.intro=Si vous le souhaitez, vous pouvez d\u00e9finir les chemins des applications externes:
dialog.setpaths.found=Chemin trouv\u00e9 ?
dialog.addaltitude.noaltitudes=L'\u00e9tendue s\u00e9lectionn\u00e9e de contient pas d'altitudes
-dialog.addaltitude.desc=D\u00e9callage d'altitude \u00e0 ajouter
+dialog.addaltitude.desc=D\u00e9calage d'altitude \u00e0 ajouter
+dialog.lookupsrtm.overwritezeros=Ramener les valeurs d'altitude \u00e0 z\u00e9ro ?
dialog.setcolours.intro=Cliquez sur une couleur pour la changer
dialog.setcolours.background=Arri\u00e8re-plan
dialog.setcolours.borders=Bordures
dialog.colourchooser.green=Vert
dialog.colourchooser.blue=Bleu
dialog.setlanguage.firstintro=Vous pouvez s\u00e9lectionner l'une des langues disponibles,<p> ou bien un fichier de langue \u00e0 utiliser.
-dialog.setlanguage.secondintro=Vous devez sauvegarder vos param\u00e8tres puis<p>red\u00e9marrer Prune pour changer de langue.
+dialog.setlanguage.secondintro=Vous devez sauvegarder vos param\u00e8tres puis<p>red\u00e9marrer GpsPrune pour changer de langue.
dialog.setlanguage.language=Langue
dialog.setlanguage.languagefile=Fichier de langue
-dialog.setlanguage.endmessage=Enregistrez vos r\u00e9glages et red\u00e9marrez Prune\npour que le changement de langue soit effectif.
+dialog.setlanguage.endmessage=Enregistrez vos r\u00e9glages et red\u00e9marrez GpsPrune\npour que le changement de langue soit effectif.
+dialog.setlanguage.endmessagewithautosave=Red\u00e9marrez GpsPrune pour que le changement de langue soit effectif.
dialog.diskcache.save=Enregistrer les images de carte sur le disque
dialog.diskcache.dir=R\u00e9pertoire cache
dialog.diskcache.createdir=Cr\u00e9er r\u00e9pertoire
dialog.diskcache.nocreate=Le r\u00e9pertoire cache n'est pas cr\u00e9\u00e9
+dialog.deletefieldvalues.intro=Choisir le champ \u00e0 effacer pour l'\u00e9tendue actuelle
+dialog.setlinewidth.text=Entrer l'\u00e9paisseur des lignes des traces (1-4)
+dialog.downloadosm.desc=Confirmer le t\u00e9l\u00e9chargement des donn\u00e9es OSM brutes pour la zone indiqu\u00e9e :
+dialog.searchwikipedianames.search=Chercher :
# 3d window
-dialog.3d.title=Vue 3D de Prune
-dialog.3dlines.title=Grille de Prune
+dialog.3d.title=Vue 3D de GpsPrune
+dialog.3d.altitudefactor=Facteur d'exag\u00e9ration de l'altitude
+dialog.3dlines.title=Grille de GpsPrune
dialog.3dlines.empty=Pas de grille \u00e0 afficher !
dialog.3dlines.intro=Ceci est la grille pour la vue 3D
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
confirm.loadfile=Donn\u00e9es charg\u00e9es depuis le fichier
confirm.save.ok1=Enregistrement r\u00e9ussi de
confirm.save.ok2=points dans le fichier
confirm.deletepoint.multi=points ont \u00e9t\u00e9 effac\u00e9s
confirm.point.edit=point \u00e9dit\u00e9
confirm.mergetracksegments=Segments de trace ont \u00e9t\u00e9 fusionn\u00e9
-confirm.reverserange=Etendue invers\u00e9e
+confirm.reverserange=\u00c9tendue invers\u00e9e
confirm.addtimeoffset=D\u00e9calage ajout\u00e9
confirm.addaltitudeoffset=D\u00e9calage d'altitude ajout\u00e9
confirm.rearrangewaypoints=Waypoints r\u00e9arrang\u00e9s
confirm.jpegload.multi=les photos ont \u00e9t\u00e9 ajout\u00e9es
confirm.media.connect=m\u00e9dia reli\u00e9e
confirm.photo.disconnect=photo d\u00e9tach\u00e9e
+confirm.audio.disconnect=audio d\u00e9tach\u00e9
+confirm.media.removed=retir\u00e9
confirm.correlatephotos.single=photo a \u00e9t\u00e9 corr\u00e9l\u00e9e
confirm.correlatephotos.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es
confirm.createpoint=Point cr\u00e9\u00e9
confirm.running=En cours...
confirm.lookupsrtm1=Trouv\u00e9
confirm.lookupsrtm2=valeurs d'altitude
+confirm.deletefieldvalues=Valeurs effac\u00e9es
+confirm.audioload=Fichiers audio ajout\u00e9s
+confirm.correlateaudios.single=fichier audio a \u00e9t\u00e9 corr\u00e9l\u00e9
+confirm.correlateaudios.multi=fichiers audio ont \u00e9t\u00e9 corr\u00e9l\u00e9s
# Buttons
button.ok=OK
button.selectnone=Ne rien s\u00e9lectionner
button.preview=Aper\u00e7u
button.load=T\u00e9l\u00e9charger
+button.upload=Envoyer
button.guessfields=Deviner les champs
button.showwebpage=Montrer page web
button.check=V\u00e9rifier
filetype.gpx=Fichiers GPX
filetype.pov=Fichiers POV
filetype.svg=Fichiers SVG
+filetype.audio=Fichiers MP3, OGG, WAV
-# Display components || These are all for the side panels showing point/range details
+# Display components
display.nodata=Pas de donn\u00e9es charg\u00e9es
display.noaltitudes=La trace ne comporte pas d'information d'altitude
display.notimestamps=La trace ne comporte pas d'information de temps
details.range.gradient=Pente
details.lists.waypoints=Waypoints
details.lists.photos=Photos
+details.lists.audio=Audio
details.photodetails=D\u00e9tails de la photo
details.nophoto=Pas de photo
details.photo.loading=Chargement
details.media.connected=Reli\u00e9e
+details.audiodetails=D\u00e9tails de l'audio
+details.noaudio=Pas de fichier audio s\u00e9lectionner
+details.audio.file=Fichier audio
+details.audio.playing=Lecture en cours...
map.overzoom=Aucune carte disponible \u00e0 ce niveau de zoom
# Field names
fieldname.duration=Dur\u00e9e
fieldname.speed=Vitesse
fieldname.verticalspeed=Vitesse verticale
+fieldname.description=Description
# Measurement units
units.original=Original
cardinal.e=E
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 operations
undo.load=charger les donn\u00e9es
undo.loadphotos=charger les photos
+undo.loadaudios=charger les fichiers audio
undo.editpoint=\u00e9diter le point
undo.deletepoint=effacer le point
undo.removephoto=retirer la photo
+undo.removeaudio=retirer le fichier audio
undo.deleterange=effacer l'\u00e9tendue
undo.compress=compresser la trace
undo.insert=ins\u00e9rer les points
undo.connect=relier
undo.disconnect=d\u00e9tacher
undo.correlatephotos=corr\u00e9ler les photos
-undo.rearrangephotos=R\u00e9arranger 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
-undo.lookupsrtm=Rechercher les altitudes depuis SRTM
+undo.rotatephoto=tourner la photo
+undo.convertnamestotimes=convertir les noms en points
+undo.lookupsrtm=rechercher les altitudes depuis SRTM
+undo.deletefieldvalues=effacer les valeurs
+undo.correlateaudios=corr\u00e9ler les fichiers audio
# Error messages
error.save.dialogtitle=Erreur \u00e0 l'enregistrement des donn\u00e9es
error.save.nodata=Pas de donn\u00e9es \u00e0 enregistrer
-error.save.failed=Echec de l'enregistrement des donn\u00e9es dans le fichier
+error.save.failed=\u00c9chec 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.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9
error.jpegload.nogpsfound=Aucune information GPS trouv\u00e9e
error.jpegload.exifreadfailed=Information EXIF illisible. Aucune information EXIF ne peut \u00eatre lue\nsans une librairie interne ou externe.
+error.audioload.nofilesfound=Aucun fichier audio trouv\u00e9
error.gpsload.unknown=Erreur inconnue
-error.undofailed.title=Echec de l'annulation
-error.undofailed.text=Echec de l'op\u00e9ration d'annulation
+error.undofailed.title=\u00c9chec de l'annulation
+error.undofailed.text=\u00c9chec de l'op\u00e9ration d'annulation
error.function.noop.title=Fonction sans effet
error.rearrange.noop=R\u00e9arrangement des points sans effet
error.function.notavailable.title=Function non-disponible
error.readme.notfound=Fichier Lisez-moi introuvable
error.osmimage.dialogtitle=Erreur au chargement des portions de cartes
error.osmimage.failed=Erreur du chargement des portions de cartes. V\u00e9rifiez votre connexion internet.
-error.language.wrongfile=Le fichier s\u00e9lectionn\u00e9 n'est pas un fichier de langue pour Prune
+error.language.wrongfile=Le fichier s\u00e9lectionn\u00e9 n'est pas un fichier de langue pour GpsPrune
error.convertnamestotimes.nonames=Aucun nom n'a pu \u00eatre converti en horaire
error.lookupsrtm.nonefound=Aucune valeur d'altitude trouv\u00e9e pour les points
+error.lookupsrtm.nonerequired=Tous les points ont d\u00e9j\u00e0 une altitude, il n'y a rien \u00e0 r\u00e9cup\u00e9rer
+error.gpsies.uploadnotok=Le serveur de Gpsies \u00e0 renvoy\u00e9 le message
+error.gpsies.uploadfailed=L'envoi a \u00e9chou\u00e9 avec l'erreur
+error.playaudiofailed=\u00c9chec de la lecture du fichier audio
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3
# Menu entries
menu.file=F\u00e1jl
menu.file.addphotos=F\u00e9nyk\u00e9pek hozz\u00e1ad\u00e1sa
+menu.file.recentfiles=Legut\u00f3bbi f\u00e1jlok
menu.file.save=Ment\u00e9s sz\u00f6vegk\u00e9nt
menu.file.exit=Kil\u00e9p\u00e9s
menu.track=Nyomvonal
menu.view.browser.bing=Bing Maps
menu.settings=Be\u00e1ll\u00edt\u00e1sok
menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se az internetr\u0151l
+menu.settings.autosave=Be\u00e1ll\u00edt\u00e1sok automatikus ment\u00e9se a programb\u00f3l t\u00f6rt\u00e9n\u0151 kil\u00e9p\u00e9skor
menu.help=S\u00fag\u00f3
# Popup menu for map
menu.map.zoomin=Nagy\u00edt\u00e1s
# Functions
function.open=F\u00e1jl megnyit\u00e1sa
+function.importwithgpsbabel=F\u00e1jl import\u00e1l\u00e1sa GPSBabellel
function.loadfromgps=Adatok let\u00f6lt\u00e9se GPS-r\u0151l
function.sendtogps=Adatok felt\u00f6lt\u00e9se GPS-re
function.exportkml=Export\u00e1l\u00e1s KML-be
function.stopaudio=Hangf\u00e1jl meg\u00e1ll\u00edt\u00e1sa
function.help=S\u00fag\u00f3
function.showkeys=Gyorsbillenty\u0171k megjelen\u00edt\u00e9se
-function.about=A Prune n\u00e9vjegye
+function.about=A GpsPrune n\u00e9vjegye
function.checkversion=\u00daj verzi\u00f3 keres\u00e9se
function.saveconfig=Be\u00e1ll\u00edt\u00e1sok ment\u00e9se
function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre
+function.managetilecache=Csempegyors\u00edt\u00f3t\u00e1r kezel\u00e9se
# Dialogs
-dialog.exit.confirm.title=Kil\u00e9p\u00e9s a Prune-b\u00f3l
+dialog.exit.confirm.title=Kil\u00e9p\u00e9s a GpsPrune-b\u00f3l
dialog.exit.confirm.text=Az adatok nincsenek elmentve. Biztos benne, hogy kil\u00e9p?
dialog.openappend.title=Hozz\u00e1f\u0171z\u00e9s a megl\u00e9v\u0151 adatokhoz
dialog.openappend.text=Hozz\u00e1f\u0171zi ezeket az adatokat a m\u00e1r bet\u00f6lt\u00f6tt adatokhoz?
dialog.exportgpx.desc=Le\u00edr\u00e1s
dialog.exportgpx.includetimestamps=Id\u0151b\u00e9lyegek is
dialog.exportgpx.copysource=Forr\u00e1s xml m\u00e1sol\u00e1sa
+dialog.exportgpx.encoding=Karakterk\u00f3dol\u00e1s
+dialog.exportgpx.encoding.system=Rendszer
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Adja meg a param\u00e9tereket a POV exporthoz
dialog.exportpov.font=Bet\u0171t\u00edpus
dialog.exportpov.camerax=X kamera
dialog.compress.singletons.title=Egyke pontok elt\u00e1vol\u00edt\u00e1sa
dialog.compress.singletons.paramdesc=T\u00e1vols\u00e1gt\u00e9nyez\u0151
dialog.compress.duplicates.title=Kett\u0151z\u00f6tt pontok elt\u00e1vol\u00edt\u00e1sa
+dialog.compress.douglaspeucker.title=Douglas-Peucker t\u00f6m\u00f6r\u00edt\u00e9s
+dialog.compress.douglaspeucker.paramdesc=T\u00f6m\u00f6r\u00edt\u00e9si t\u00e9nyez\u0151
dialog.compress.summarylabel=T\u00f6rlend\u0151 pontok
dialog.pastecoordinates.desc=Adja meg vagy illessze be a koordin\u00e1t\u00e1kat ide
dialog.pastecoordinates.coords=Koordin\u00e1t\u00e1k
dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00f3jra
-dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/prune/\nwebhelyet.
+dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/gpsprune/\nwebhelyet.
dialog.about.version=Verzi\u00f3
dialog.about.build=Build
-dialog.about.summarytext1=A Prune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re.
+dialog.about.summarytext1=A GpsPrune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re.
dialog.about.summarytext2=Gnu GPL licenc alatt ker\u00fclt kiad\u00e1sra a szabad, ny\u00edlt, vil\u00e1gm\u00e9ret\u0171 haszn\u00e1lathoz \u00e9s fejleszt\u00e9shez.<br>M\u00e1sol\u00e1sa, terjeszt\u00e9se \u00e9s m\u00f3dos\u00edt\u00e1sa megengedett \u00e9s \u00f6szt\u00f6nz\u00f6tt<br>a mell\u00e9kelt <code>license.txt</code> f\u00e1jlban r\u00f6gz\u00edtett felt\u00e9telek szerint
dialog.about.summarytext3=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a <code style="font-weight:bold">http://activityworkshop.net/</code> webhelyet.
dialog.about.languages=El\u00e9rhet\u0151 nyelvek
dialog.about.yes=Igen
dialog.about.no=Nem
dialog.about.credits=K\u00e9sz\u00edt\u0151k
-dialog.about.credits.code=Prune k\u00f3dj\u00e1t \u00edrta:
+dialog.about.credits.code=GpsPrune k\u00f3dj\u00e1t \u00edrta:
dialog.about.credits.exifcode=Exif k\u00f3d:
dialog.about.credits.icons=N\u00e9h\u00e1ny ikon sz\u00e1rmazik:
dialog.about.credits.translators=Ford\u00edt\u00f3k
dialog.about.credits.thanks=K\u00f6sz\u00f6net:
dialog.about.readme=Olvassel
dialog.checkversion.error=A verzi\u00f3sz\u00e1m nem ellen\u0151rizhet\u0151.\nEllen\u0151rizze az internetkapcsolatot.
-dialog.checkversion.uptodate=A Prune leg\u00fajabb verzi\u00f3j\u00e1t haszn\u00e1lja.
-dialog.checkversion.newversion1=El\u00e9rhet\u0151 a Prune \u00faj verzi\u00f3ja! A leg\u00fajabb veri\u00f3 most:
+dialog.checkversion.uptodate=A GpsPrune leg\u00fajabb verzi\u00f3j\u00e1t haszn\u00e1lja.
+dialog.checkversion.newversion1=El\u00e9rhet\u0151 a GpsPrune \u00faj verzi\u00f3ja! A leg\u00fajabb verzi\u00f3 most:
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Ez az \u00faj verzi\u00f3 kiad\u00e1sra ker\u00fclt:
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/prune/download.html webhelyet.
+dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/gpsprune/download.html webhelyet.
dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00f3k az eg\u00e9r haszn\u00e1lata helyett
dialog.keys.keylist=<table><tr><td>Ny\u00edlbillenty\u0171k</td><td>T\u00e9rk\u00e9p mozgat\u00e1sa balra, jobbra, fel, le</td></tr><tr><td>Ctrl + bal, jobb ny\u00edl</td><td>El\u0151z\u0151 vagy k\u00f6vetkez\u0151 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + fel, le ny\u00edl</td><td>Nagy\u00edt\u00e1s vagy kicsiny\u00edt\u00e9s</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>El\u0151z\u0151, k\u00f6vetkez\u0151 szakasz kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Ctrl + Home, End</td><td>Els\u0151, utols\u00f3 pont kiv\u00e1laszt\u00e1sa</td></tr><tr><td>Del</td><td>Jelenlegi pont t\u00f6rl\u00e9se</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Sz\u00edns\u00e9ma
dialog.saveconfig.prune.linewidth=Vonalsz\u00e9less\u00e9g
dialog.saveconfig.prune.kmltrackcolour=KML nyomvonal sz\u00edne
+dialog.saveconfig.prune.autosavesettings=Automatikus ment\u00e9s be\u00e1ll\u00edt\u00e1sai
dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait:
dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3?
dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket
dialog.colourchooser.green=Z\u00f6ld
dialog.colourchooser.blue=K\u00e9k
dialog.setlanguage.firstintro=Kiv\u00e1laszthat egy be\u00e9p\u00edtett nyelvet,<p>vagy v\u00e1lasszon egy sz\u00f6vegf\u00e1jlt helyette.
-dialog.setlanguage.secondintro=Mentenie kell a be\u00e1ll\u00edt\u00e1sokat, majd<p>a nyelv v\u00e1lt\u00e1s\u00e1hoz ind\u00edtsa \u00fajra a Prune-t.
+dialog.setlanguage.secondintro=Mentenie kell a be\u00e1ll\u00edt\u00e1sokat, majd<p>a nyelv v\u00e1lt\u00e1s\u00e1hoz ind\u00edtsa \u00fajra a GpsPrune-t.
dialog.setlanguage.language=Nyelv
dialog.setlanguage.languagefile=Nyelvi f\u00e1jl
-dialog.setlanguage.endmessage=Most mentse a be\u00e1ll\u00edt\u00e1sokat, \u00e9s ind\u00edtsa \u00fajra a Prune-t,\nhogy a nyelv v\u00e1lt\u00e1sa \u00e9rv\u00e9nybe l\u00e9pjen
+dialog.setlanguage.endmessage=Most mentse a be\u00e1ll\u00edt\u00e1sokat, \u00e9s ind\u00edtsa \u00fajra a GpsPrune-t,\nhogy a nyelv v\u00e1lt\u00e1sa \u00e9rv\u00e9nybe l\u00e9pjen
+dialog.setlanguage.endmessagewithautosave=A nyelv megv\u00e1ltoztat\u00e1s\u00e1nak \u00e9rv\u00e9nyes\u00edt\u00e9s\u00e9hez ind\u00edtsa \u00fajra a GpsPrune-t.
dialog.diskcache.save=T\u00e9rk\u00e9pek ment\u00e9se a lemezre
dialog.diskcache.dir=Gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra
dialog.diskcache.createdir=K\u00f6nyvt\u00e1r l\u00e9trehoz\u00e1sa
dialog.diskcache.nocreate=A gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem ker\u00fclt l\u00e9trehoz\u00e1sra
+dialog.diskcache.table.path=El\u00e9r\u00e9si \u00fat
+dialog.diskcache.table.usedby=Haszn\u00e1lja
+dialog.diskcache.table.zoom=Nagy\u00edt\u00e1s
+dialog.diskcache.table.tiles=Csemp\u00e9k
+dialog.diskcache.table.megabytes=Megab\u00e1jt
+dialog.diskcache.tileset=Csempecsoport
+dialog.diskcache.tileset.multiple=t\u00f6bb
+dialog.diskcache.deleteold=R\u00e9gi csemp\u00e9k t\u00f6rl\u00e9se
+dialog.diskcache.deleteall=Az \u00f6sszes csempe t\u00f6rl\u00e9se
+dialog.diskcache.deleted1=
+dialog.diskcache.deleted2=f\u00e1jl t\u00f6r\u00f6lve a gyors\u00edt\u00f3t\u00e1rb\u00f3l
dialog.deletefieldvalues.intro=V\u00e1lassza ki a t\u00f6rlend\u0151 mez\u0151t a jelenlegi tartom\u00e1nyban
dialog.setlinewidth.text=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4)
dialog.downloadosm.desc=Nyers OSM adatok let\u00f6lt\u00e9s\u00e9nek meger\u0151s\u00edt\u00e9se a megadott ter\u00fcletre:
dialog.searchwikipedianames.search=Keres\u00e9s erre:
# 3d window
-dialog.3d.title=Prune 3D n\u00e9zet
+dialog.3d.title=GpsPrune 3D n\u00e9zet
dialog.3d.altitudefactor=Magass\u00e1gi ny\u00fajt\u00e1si t\u00e9nyez\u0151
-dialog.3dlines.title=Prune r\u00e1csvonalak
+dialog.3dlines.title=GpsPrune r\u00e1csvonalak
dialog.3dlines.empty=Nincsenek megjelen\u00edthet\u0151 r\u00e1csvonalak!
dialog.3dlines.intro=Ezek a r\u00e1csvonalak a 3D n\u00e9zethez
button.browse=B\u00f6ng\u00e9sz\u00e9s...
button.addnew=\u00daj hozz\u00e1ad\u00e1sa
button.delete=T\u00f6rl\u00e9s
+button.manage=Kezel\u00e9s
# File types
filetype.txt=TXT f\u00e1jlok
details.photodetails=F\u00e9nyk\u00e9p r\u00e9szletei
details.nophoto=Nincs f\u00e9nyk\u00e9p kiv\u00e1lasztva
details.photo.loading=Bet\u00f6lt\u00e9s
+details.photo.bearing=Ir\u00e1ny
details.media.connected=\u00d6sszekapcsolva
details.audiodetails=Hang r\u00e9szletei
-details.noaudio=Nincs hang kiv\u00e1lasztva
+details.noaudio=Nincs hangf\u00e1jl kiv\u00e1lasztva
details.audio.file=Hangf\u00e1jl
details.audio.playing=lej\u00e1tsz\u00e1s...
map.overzoom=Nem \u00e9rhet\u0151 el t\u00e9rk\u00e9p ezen a nagy\u00edt\u00e1si szinten
fieldname.duration=Id\u0151tartam
fieldname.speed=Sebess\u00e9g
fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g
+fieldname.description=Le\u00edr\u00e1s
# Measurement units
units.original=Eredeti
error.readme.notfound=Az olvassel f\u00e1jl nem tal\u00e1lhat\u00f3
error.osmimage.dialogtitle=Hiba a t\u00e9rk\u00e9p bet\u00f6lt\u00e9sekor
error.osmimage.failed=A t\u00e9rk\u00e9p bet\u00f6lt\u00e9se nem siker\u00fclt. Ellen\u0151rizze az internetkapcsolatot.
-error.language.wrongfile=\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott f\u00e1jl nem egy nyelvi f\u00e1jl a Prune-hoz
+error.language.wrongfile=\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott f\u00e1jl nem egy nyelvi f\u00e1jl a GpsPrune-hoz
error.convertnamestotimes.nonames=A nevek nem konvert\u00e1lhat\u00f3k id\u0151adatokk\u00e1
error.lookupsrtm.nonefound=Nem \u00e9rhet\u0151 el magass\u00e1gi \u00e9rt\u00e9k ezekhez a pontokhoz
error.lookupsrtm.nonerequired=Az \u00f6sszes pont m\u00e1r rendelkezik magass\u00e1gadatokkal, \u00edgy nincs mit keresni
error.gpsies.uploadnotok=A gpsies szerver a k\u00f6vetkez\u0151 \u00fczenetet adta vissza
error.gpsies.uploadfailed=A felt\u00f6lt\u00e9s nem siker\u00fclt a k\u00f6vetkez\u0151 hib\u00e1val
error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt
+error.cache.notthere=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem tal\u00e1lhat\u00f3
+error.cache.empty=A csempegyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra \u00fcres
+error.cache.cannotdelete=Nincs t\u00f6r\u00f6lhet\u0151 csempe
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Italian entries thanks to josatoc
# Menu entries
menu.file=File
menu.file.addphotos=Aggiungi foto
+menu.file.recentfiles=File recenti
menu.file.save=Salva
menu.file.exit=Esci
menu.track=Traccia
function.stopaudio=Ferma ripresa audio
function.help=Aiuto
function.showkeys=Visualizza tasti scelta rapida
-function.about=Informazioni su Prune
+function.about=Informazioni su GpsPrune
function.checkversion=Controlla gli aggiornamenti
function.saveconfig=Salva configurazione
function.diskcache=Salva mappe su disco
# Dialogs
-dialog.exit.confirm.title=Esci da Prune
+dialog.exit.confirm.title=Esci da GpsPrune
dialog.exit.confirm.text=Le modifiche non sono state salvate. Sei sicuro di voler uscire?
dialog.openappend.title=Aggiungi ai dati esistenti
dialog.openappend.text=Aggiungi questi dati a quelli gi\u00e0 caricati?
dialog.exportgpx.desc=Descrizione
dialog.exportgpx.includetimestamps=Includi dati temporali
dialog.exportgpx.copysource=Copia xml sorgente
+dialog.exportgpx.encoding.system=Sistema
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Per favore inserisci i parametri per l'esportazione in POV
dialog.exportpov.font=Font
dialog.exportpov.camerax=Camera X
dialog.pastecoordinates.desc=Inserisci o incolla qui le coordinate
dialog.pastecoordinates.coords=Coordinate
dialog.pastecoordinates.nothingfound=Per favore, controlla le coordinate e riprova
-dialog.help.help=Per favore vedi\n http://activityworkshop.net/software/prune/\nper maggiori informazioni e per la guida utente.
+dialog.help.help=Per favore vedi\n http://activityworkshop.net/software/gpsprune/\nper maggiori informazioni e per la guida utente.
dialog.about.version=Versione
dialog.about.build=Build
-dialog.about.summarytext1=Prune \u00e8 un programma per il caricamento, la visione e l'edit di dati provenienti da un GPS.
+dialog.about.summarytext1=GpsPrune \u00e8 un programma per il caricamento, la visione e l'edit di dati provenienti da un GPS.
dialog.about.summarytext2=\u00c8 rilasciato sotto la licenza Gnu GPL per l'uso gratuito e aperto ed il suo miglioramento, con validit\u00e0 mondiale.<br>La copia, la ridistribuzione sono permesse e incoraggiate<br>in accordo con i termini inclusi nel file <code>license.txt</code>.
dialog.about.summarytext3=Per favore vedi <code style="font-weight:bold">http://activityworkshop.net/</code> per maggiori informazioni e per la guida utente.
dialog.about.languages=Lingue disponibili
dialog.about.yes=S\u00ec
dialog.about.no=No
dialog.about.credits=Crediti
-dialog.about.credits.code=Il codice Prune code \u00e8 scritto da
+dialog.about.credits.code=Il codice GpsPrune code \u00e8 scritto da
dialog.about.credits.exifcode=Il codice Exif \u00e8 scritto da
dialog.about.credits.icons=Alcune icone prese da
dialog.about.credits.translators=Traduttori
dialog.about.credits.thanks=Grazie a
dialog.about.readme=Leggimi
dialog.checkversion.error=Non posso verificare l'aggiornamento.\nPer favore controlla la connessione internet.
-dialog.checkversion.uptodate=Stai usando l'ultima versione di Prune
-dialog.checkversion.newversion1=Una nuova versione di Prune \u00e8 disponibile! L'ultima versione \u00e8 ora la versione
+dialog.checkversion.uptodate=Stai usando l'ultima versione di GpsPrune
+dialog.checkversion.newversion1=Una nuova versione di GpsPrune \u00e8 disponibile! L'ultima versione \u00e8 ora la versione
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.checkversion.download=Per scaricare la nuova versione vai a http://activityworkshop.net/software/gpsprune/download.html.
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.keys.normalmodifier=Ctrl
dialog.colourchooser.green=Verde
dialog.colourchooser.blue=Blu
dialog.setlanguage.firstintro=Puoi selezionare una delle lingue incluse,<p>oppure selezionare un file di testo.
-dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare Prune per cambiare la lingua.
+dialog.setlanguage.secondintro=Devi salvare le impostazioni e<p> riavviare GpsPrune per cambiare la lingua.
dialog.setlanguage.language=Lingua
dialog.setlanguage.languagefile=File della lingua
-dialog.setlanguage.endmessage=Ora salva le tue preferenze e riavvia Prune\n per rendere effettivo il cambio di lingua
+dialog.setlanguage.endmessage=Ora salva le tue preferenze e riavvia GpsPrune\n per rendere effettivo il cambio di lingua
dialog.diskcache.save=Salva la mappa sul disco
dialog.diskcache.dir=Cartella della cache
dialog.diskcache.createdir=Crea cartella
dialog.searchwikipedianames.search=Cerca per:
# 3d window
-dialog.3d.title=Visione Prune in 3D
+dialog.3d.title=Visione GpsPrune in 3D
dialog.3d.altitudefactor=Fattore di moltiplicazione della quota
-dialog.3dlines.title=Griglia di Prune
+dialog.3dlines.title=Griglia di GpsPrune
dialog.3dlines.empty=Nessuna griglia mostrata!
dialog.3dlines.intro=Queste sono le linee della griglia per la visione 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.
-error.language.wrongfile=Il file selezionato non sembra essere un file di lingua per Prune
+error.language.wrongfile=Il file selezionato non sembra essere un file di lingua per GpsPrune
error.convertnamestotimes.nonames=Nomi non convertibili in orari
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
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Japanese entries as extra
# Menu entries
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.about=GpsPrune \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.title=GpsPrune \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.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.help.help=\u8a73\u3057\u3044\u60c5\u5831\u3084\u3001\u30e6\u30fc\u30b6\u30fc\u30ac\u30a4\u30c9\u306a\u3069\u306f\u3001\nhttp://activityworkshop.net/software/gpsprune/ \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.summarytext1=GpsPrune \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.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.code=GpsPrune \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.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.uptodate=\u3042\u306a\u305f\u306f\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306eGpsPrune\u3092\u4f7f\u3063\u3066\u3044\u307e\u3059\u3002
+dialog.checkversion.newversion1=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u306e GpsPrune \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.checkversion.download=\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3059\u308b\u306b\u306f\u3001 http://activityworkshop.net/software/gpsprune/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.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.secondintro=\u8a00\u8a9e\u3092\u5207\u308a\u66ff\u3048\u308b\u306b\u306f\u3001\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3066<p>GpsPrune \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
+dialog.setlanguage.endmessage=\u8a2d\u5b9a\u3092\u4fdd\u5b58\u3057\u3001\u8a00\u8a9e\u306e\u5909\u66f4\u3092\u6709\u52b9\u306b\u3059\u308b\u305f\u3081\u306b\nGpsPrune \u3092\u518d\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002
# 3d window
-dialog.3d.title=Prune 3D \u8868\u793a
-dialog.3dlines.title=Prune \u683c\u5b50\u7dda
+dialog.3d.title=GpsPrune 3D \u8868\u793a
+dialog.3dlines.title=GpsPrune \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
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.language.wrongfile=\u9078\u629e\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u306fGpsPrune \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
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Korean entries thanks to HooAU
# Menu entries
function.stopaudio=\uc18c\ub9ac\ud30c\uc77c \uba48\ucda4
function.help=\ub3c4\uc6c0\ub9d0
function.showkeys=\ub2e8\ucd95\ud0a4 \ubcf4\uae30
-function.about=Prune \uc815\ubcf4
+function.about=GpsPrune \uc815\ubcf4
function.checkversion=\uc0c8\ubc84\uc804\uc774 \uc788\ub294\uc9c0 \uac80\uc0ac
function.saveconfig=\uc124\uc815 \uc800\uc7a5\ud558\uae30
function.diskcache=\ub514\uc2a4\ud06c\uc5d0 \ub9f5 \uc800\uc7a5
# Dialogs
-dialog.exit.confirm.title=Prune \uc885\ub8cc
+dialog.exit.confirm.title=GpsPrune \uc885\ub8cc
dialog.exit.confirm.text=\ub370\uc774\ud0c0\uac00 \uc800\uc7a5\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uadf8\ub798\ub3c4 \ub098\uac00\uc2dc\uac8c\uc2b5\ub2c8\uae4c?
dialog.openappend.title=\ub370\uc774\ud130 \ucd94\uac00\ud558\uae30
dialog.openappend.text=\uc774\uc804\uc5d0 \ubd88\ub7ec\uc628 \ub370\uc774\ud0c0\ub85c \ucd94\uac00\ud558\uae30\uaca0\uc2b5\ub2c8\uae4c?
dialog.pastecoordinates.desc=\uc790\ud45c\ub97c \ub123\uc73c\uc138\uc694
dialog.pastecoordinates.coords=\uc88c\ud45c
dialog.pastecoordinates.nothingfound=\uc88c\ud45c\ub97c \ud655\uc778\ud558\uc2dc\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \ubcf4\uc138\uc694.
-dialog.help.help=http://activityworkshop.net/software/prun
+dialog.help.help=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.\n http://activityworkshop.net/software/gpsprune/
dialog.about.version=\ubc84\uc804
dialog.about.build=\ube4c\ub4dc
-dialog.about.summarytext1=Prune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4.
+dialog.about.summarytext1=GpsPrune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4.
dialog.about.summarytext2=\uc774 \ud504\ub85c\uadf8\ub7a8\uc740 \uc790\uc720\ub86d\uac8c, \uac1c\ubc29\uc801\uc73c\ub85c, \uadf8\ub9ac\uace0 \uc804\uc138\uacc4\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uace0 \uac1c\uc120\ud558\uae30 \uc704\ud574 Gnu GPL \uc5d0 \ub530\ub77c\uc11c \ubc30\ud3ec\ub429\ub2c8\ub2e4. <br> \uc774 \ud504\ub85c\uadf8\ub7a8\uc5d0 \ud3ec\ud568\ub41c <code>license.txt</code> \ud30c\uc77c\uc758 \uc870\uac74\uc5d0 \ub530\ub77c \ubcf5\uc81c, \uc7ac\ubc30\ud3ec, \uc218\uc815\uc774 \ud5c8\uac00\ub418\uace0, \uc7a5\ub824\ub429\ub2c8\ub2e4.
dialog.about.summarytext3=\ub354 \uc790\uc138\ud55c \uc815\ubcf4\ub098 \uc0ac\uc6a9\uc790\uc124\uba85\uc740 \uc774\uacf3\uc744 \ucc38\uace0\ud574\uc8fc\uc138\uc694.<code style="font-weight:bold">http://activityworkshop.net/</code></span>
dialog.about.languages=\uc0ac\uc6a9 \uac00\ub2a5\ud55c \uc5b8\uc5b4\ub4e4
dialog.about.yes=\uc608
dialog.about.no=\uc544\ub2c8\uc624
dialog.about.credits=\uc5d0\uac8c \uac10\uc0ac\ub97c
-dialog.about.credits.code=Prune \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900
+dialog.about.credits.code=GpsPrune \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900
dialog.about.credits.exifcode=Exif \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900
dialog.about.credits.icons=\uc77c\ubd80 \uc544\uc774\ucf58\uc744 \uac00\uc838\uc628
dialog.about.credits.translators=\ubc88\uc5ed\uc790\ub4e4
dialog.about.credits.thanks=\uac10\uc0ac\ud569\ub2c8\ub2e4.
dialog.about.readme=\uc77d\uc5b4\uc8fc\uc138\uc694
dialog.checkversion.error=\ubc84\uc804\uc774 \ud655\uc778\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694./n \uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.
-dialog.checkversion.uptodate=\ub2f9\uc2e0\uc740 Prune\uc758 \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud558\uace0 \uacc4\uc2ed\ub2c8\ub2e4.
-dialog.checkversion.newversion1=Prune\uc758 \uc0c8 \ubc84\uc804\uc744 \uc9c0\uae08 \uc0ac\uc6a9\ud560 \uc218 \uc788\uaca0\ub124\uc694. \ucd5c\uc2e0\ubc84\uc804\uc740
+dialog.checkversion.uptodate=\ub2f9\uc2e0\uc740 GpsPrune\uc758 \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud558\uace0 \uacc4\uc2ed\ub2c8\ub2e4.
+dialog.checkversion.newversion1=GpsPrune\uc758 \uc0c8 \ubc84\uc804\uc744 \uc9c0\uae08 \uc0ac\uc6a9\ud560 \uc218 \uc788\uaca0\ub124\uc694. \ucd5c\uc2e0\ubc84\uc804\uc740
dialog.checkversion.newversion2=\ubc84\uc804\uc785\ub2c8\ub2e4.
dialog.checkversion.releasedate1=\uc0c8 \ubc84\uc804\uc740
dialog.checkversion.releasedate2=\uc5d0 \ubc30\ud3ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
-dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc\uace0 \uc544\ub798 \ub2e8\ucd95\ud0a4\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc138\uc694.
dialog.keys.keylist=<table><tr><td>\ud654\uc0b4\ud45c \ud0a4</td><td>\uc88c,\uc6b0,\uc544\ub798,\uc704\ub85c \uc9c0\ub3c4 \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc67c\ucabd, \uc624\ub978\ucabd \ud654\uc0b4\ud45c</td><td>\uc9c0\uc810 \uc120\ud0dd \uc774\ub3d9</td></tr><tr><td>Ctrl + \uc704, \uc544\ub798 \ud654\uc0b4\ud45c</td><td>\uc9c0\ub3c4 \ud655\ub300 \ucd95\uc18c</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\uc774\uc804 \uc774\ud6c4 \ubd80\ubd84 \uc120\ud0dd</td></tr><tr><td>Ctrl + Home, End</td><td>\ucc98\uc74c \uc9c0\uc810, \ub9c8\uc9c0\ub9c9 \uc9c0\uc810 \uc120\ud0dd</td></tr><tr><td>Del</td><td>\ud604\uc7ac \uc9c0\uc810 \uc0ad\uc81c</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.colourchooser.green=\ub179\uc0c9
dialog.colourchooser.blue=\ud30c\ub791
dialog.setlanguage.firstintro=\ud3ec\ud568\ub41c \uc5b8\uc5b4\ub4e4 \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc2e4 \uc218 \uc788\uc5b4\uc694,<p> \ud639\uc740 \ub300\uc2e0 \uc0ac\uc6a9\ud560 \ud14d\uc2a4\ud2b8 \ud30c\uc77c\uc744 \uc120\ud0dd\ud558\uc138\uc694.
-dialog.setlanguage.secondintro=\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0, <p> \uc5b8\uc5b4\ub97c \ubc14\uafb8\uae30\uc704\ud574\uc11c\ub294 Prune\uc744 \uc7ac\uc2dc\uc791\ud558\uc154\uc57c \ud574\uc694.
+dialog.setlanguage.secondintro=\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0, <p> \uc5b8\uc5b4\ub97c \ubc14\uafb8\uae30\uc704\ud574\uc11c\ub294 GpsPrune\uc744 \uc7ac\uc2dc\uc791\ud558\uc154\uc57c \ud574\uc694.
dialog.setlanguage.language=\uc5b8\uc5b4
dialog.setlanguage.languagefile=\uc5b8\uc5b4 \ud30c\uc77c
-dialog.setlanguage.endmessage=\ubcc0\uacbd\ub41c \uc5b8\uc5b4\ub97c \uc801\uc6a9\ud558\uc2e4\ub824\uba74/n\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0 Prune\uc744 \uc7ac\uc2dc\uc791\ud558\uc138\uc694.
+dialog.setlanguage.endmessage=\ubcc0\uacbd\ub41c \uc5b8\uc5b4\ub97c \uc801\uc6a9\ud558\uc2e4\ub824\uba74/n\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0 GpsPrune\uc744 \uc7ac\uc2dc\uc791\ud558\uc138\uc694.
dialog.diskcache.save=\ub514\uc2a4\ud06c\ub85c \uc9c0\ub3c4 \uc774\ubbf8\uc9c0\ub97c \uc800\uc7a5
dialog.diskcache.dir=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac
dialog.diskcache.createdir=\ub514\ub809\ud1a0\ub9ac \ub9cc\ub4e4\uae30
dialog.searchwikipedianames.search=\ucc3e\uae30
# 3d window
-dialog.3d.title=Prune 3D \ubcf4\uae30
+dialog.3d.title=GpsPrune 3D \ubcf4\uae30
dialog.3d.altitudefactor=\uace0\ub3c4 \uacfc\uc7a5 \uacc4\uc218
-dialog.3dlines.title=Prune \uaca9\uc790\uc120
+dialog.3dlines.title=GpsPrune \uaca9\uc790\uc120
dialog.3dlines.empty=\uaca9\uc790\uc120 \uc5c6\uc774 \ud45c\uc2dc\ud558\uae30
dialog.3dlines.intro=3D \ubcf4\uae30\ub97c \uc704\ud55c \uaca9\uc790\uc120\uc785\ub2c8\ub2e4.
error.readme.notfound=Readme \ud30c\uc77c\uc744 \ubabb \ucc3e\uc74c
error.osmimage.dialogtitle=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30\uc911 \uc624\ub958
error.osmimage.failed=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30 \uc2e4\ud328./n\uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.
-error.language.wrongfile=\uc120\ud0dd\ud558\uc2e0 \ud30c\uc77c\uc774 Prune\uc744 \uc704\ud55c \uc5b8\uc5b4\ud30c\uc77c\uc774 \uc544\ub2cc\uac70 \uac19\uc544\uc694.
+error.language.wrongfile=\uc120\ud0dd\ud558\uc2e0 \ud30c\uc77c\uc774 GpsPrune\uc744 \uc704\ud55c \uc5b8\uc5b4\ud30c\uc77c\uc774 \uc544\ub2cc\uac70 \uac19\uc544\uc694.
error.convertnamestotimes.nonames=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 \ud560 \uc218 \uc5c6\uc5b4\uc694./n(\uacbd\uc720\uc9c0\uc774\ub984\uc744 \ucc3e\uc9c0 \ubabb\ud588\uac70\ub098 \ubcc0\ud658\ud560 \uc218 \uc5c6\ub294 \uacbd\uc6b0\uc5d0\uc694.)
error.lookupsrtm.nonefound=\uc774 \uc9c0\uc810\ub4e4\uc5d0\uc11c \uace0\ub3c4\uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5b4\uc694./n\uc774 \uc601\uc5ed\uc5d0\uc11c \ud0c0\uc77c\uc774 \uc5c6\uac70\ub098, \uc790\ub8cc\uac00 \uc54c\uc218 \uc5c6\ub294 \uac83\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \uacbd\uc720.
error.lookupsrtm.nonerequired=\ubaa8\ub4e0 \uc9c0\uc810\uc774 \uace0\ub3c4 \uc815\ubcf4\uac00 \uc788\uc5b4\uc11c, \ucc3e\uc744\uac8c \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694.
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Dutch entries as extra
# Menu entries
menu.file=Bestand
menu.file.addphotos=Foto's toevoegen
+menu.file.recentfiles=Onlangs geopend
menu.file.save=Opslaan als tekst
menu.file.exit=Afsluiten
menu.track=Route
menu.view.browser.bing=Bing maps
menu.settings=Instellingen
menu.settings.onlinemode=Kaarten van internet ophalen
+menu.settings.autosave=Automatisch opslaan bij afsluiten
menu.help=Help
# Popup menu for map
menu.map.zoomin=Zoom +
# Functions
function.open=Open bestand
+function.importwithgpsbabel=Bestand importeren met GPSBabel
function.loadfromgps=Gegevens lezen van GPS
function.sendtogps=Gegevens verzenden naar GPS
function.exportkml=Export KML
function.stopaudio=Stop audiobestand
function.help=Help
function.showkeys=Toon sneltoetsen
-function.about=Over Prune
+function.about=Over GpsPrune
function.checkversion=Controleer op nieuwe versie
function.saveconfig=Instellingen opslaan
function.diskcache=Kaart opslaan op schijf
+function.managetilecache=Beheer tegelcache
# Dialogs
-dialog.exit.confirm.title=Prune afsluiten
+dialog.exit.confirm.title=GpsPrune afsluiten
dialog.exit.confirm.text=Uw data is niet opgeslagen. Weet u zeker dat u wilt afsluiten?
dialog.openappend.title=Toevoegen aan bestaande data
dialog.openappend.text=Wilt u deze data toevoegen aan de reeds geladen data?
dialog.exportgpx.desc=Omschrijving
dialog.exportgpx.includetimestamps=Tijden meenemen
dialog.exportgpx.copysource=Kopieer bron xml
+dialog.exportgpx.encoding=Coderen
+dialog.exportgpx.encoding.system=Systeem
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Geef parameters voor POV export
dialog.exportpov.font=Lettertype
dialog.exportpov.camerax=Camera X
dialog.compress.singletons.title=Singletons verwijderen
dialog.compress.singletons.paramdesc=Afstandfactor
dialog.compress.duplicates.title=Verwijderen duplicaten
+dialog.compress.douglaspeucker.title=Douglas-Peucker compressie
+dialog.compress.douglaspeucker.paramdesc=Span factor
dialog.compress.summarylabel=Te verwijderen punten
dialog.pastecoordinates.desc=Geef co\u00f6rdinaten in
dialog.pastecoordinates.coords=Co\u00f6rdinaten
dialog.pastecoordinates.nothingfound=Controleer de co\u00f6rdinaten en probeer het nogmaals
-dialog.help.help=Ga naar\n http://activityworkshop.net/software/prune/\nvoor meer informatie en handleidingen.
+dialog.help.help=Ga naar\n http://activityworkshop.net/software/gpsprune/\nvoor meer informatie en handleidingen.
dialog.about.version=Versie
dialog.about.build=Build
-dialog.about.summarytext1=Prune is een programma voor het laden, tonen en wijzigen van data uit GPS ontvangers.
+dialog.about.summarytext1=GpsPrune is een programma voor het laden, tonen en wijzigen van data uit GPS ontvangers.
dialog.about.summarytext2=Uitgebracht onder de Gnu GPL voor gratis, open, wereldwijd gebruik en verbetering<br>Kopieren, verspreiding en aanpassing is toegestaan en aangemoedigd<br>volgens de voorwaarden in het bestand <code>license.txt</code>.
dialog.about.summarytext3=Ga naar <code style="font-weight:bold">http://activityworkshop.net/</code> voor meer informatie en handleidingen.
dialog.about.languages=Beschikbare talen
dialog.about.yes=Ja
dialog.about.no=Nee
dialog.about.credits=Credits
-dialog.about.credits.code=Prune code geschreven door
+dialog.about.credits.code=GpsPrune code geschreven door
dialog.about.credits.exifcode=Exif code door
dialog.about.credits.icons=Sommige iconen overgenomen van
dialog.about.credits.translators=Vertalers
dialog.about.credits.thanks=Dank aan
dialog.about.readme=Leesmij
dialog.checkversion.error=Versienummer kon niet worden gecontroleerd.\nControleer de internetverbinding.
-dialog.checkversion.uptodate=U gebruikt de laatste versie van Prune.
-dialog.checkversion.newversion1=Een nieuwe versie van Prune is beschikbaar. De nieuwste versie is nu versie
+dialog.checkversion.uptodate=U gebruikt de laatste versie van GpsPrune.
+dialog.checkversion.newversion1=Een nieuwe versie van GpsPrune is beschikbaar. De nieuwste versie is nu versie
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Deze nieuwe versie was vrijgegeven op
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Om de nieuwst versie te downloaden, ga naar http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=De volgende sneltoetsen vervangen muisacties
dialog.keys.keylist=<table><tr><td>Pijltjestoetsen</td><td>verschuif de kaart links, rechts, omhoog, omlaag</td></tr><tr><td>Ctrl + pijltje naar links, rechts</td><td>Selecteer volgende, vorige punt</td></tr><tr><td>Ctrl + pijltje omhoog, omlaag</td><td>Zoom in of uit</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>Selecteer vorig, volgend segment</td></tr><tr><td>Ctrl + Home, End</td><td>Select eerste, laatste punt</td></tr><tr><td>Del</td><td>Verwijder huidige punt</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Kleurenschema
dialog.saveconfig.prune.linewidth=Lijndikte
dialog.saveconfig.prune.kmltrackcolour=KML routekleur
+dialog.saveconfig.prune.autosavesettings=Instellingen automatisch opslaan
dialog.setpaths.intro=Indien nodig kan je de paden naar externe applicaties kiezen:
dialog.setpaths.found=Pad gevonden?
dialog.addaltitude.noaltitudes=De geselecteerde reeks bevat geen hoogtes
dialog.colourchooser.green=Groen
dialog.colourchooser.blue=Blauw
dialog.setlanguage.firstintro=U kunt kiezen uit \u00e9\u00e9n van de meegeleverde talen,<p>of selecteer een tekstbestand.
-dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>Prune te herstarten om de taal te kunnen wijzigen
+dialog.setlanguage.secondintro=U dient uw instellingen op te slaan en<p>GpsPrune te herstarten om de taal te kunnen wijzigen
dialog.setlanguage.language=Taal
dialog.setlanguage.languagefile=Taal bestand
-dialog.setlanguage.endmessage=Sla nu uw instellingen op en herstart Prune\nom de taalwijziging te activeren
+dialog.setlanguage.endmessage=Sla nu uw instellingen op en herstart GpsPrune\nom de taalwijziging te activeren
+dialog.setlanguage.endmessagewithautosave=Herstart GpsPrune om te taalwijziging actief te maken.
dialog.diskcache.save=Kaartafbeeldingen opslaan op schijf
dialog.diskcache.dir=Cache map
dialog.diskcache.createdir=Cre\u00eber map
dialog.diskcache.nocreate=Cache map niet aangemaakt
+dialog.diskcache.table.path=Pad
+dialog.diskcache.table.usedby=Gebruikt door
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Tegels
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Tegelset
+dialog.diskcache.tileset.multiple=meerdere
+dialog.diskcache.deleteold=Verwijder oude tegels
+dialog.diskcache.deleteall=Verwijder alle tegels
+dialog.diskcache.deleted1=
+dialog.diskcache.deleted2=bestanden uit de cache verwijderd
dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
dialog.setlinewidth.text=Geef lijndikte voor routes (1-4)
dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
dialog.searchwikipedianames.search=Zoeken naar:
# 3d window
-dialog.3d.title=Prune in 3D
+dialog.3d.title=GpsPrune in 3D
dialog.3d.altitudefactor=Hoogte overdrijvingsfactor
-dialog.3dlines.title=Prune raster
+dialog.3dlines.title=GpsPrune raster
dialog.3dlines.empty=Geen raster om af te beelden
dialog.3dlines.intro=Dit is het raster voor 3D
button.browse=Browse...
button.addnew=Nieuwe toevoegen
button.delete=Verwijderen
+button.manage=Beheer
# File types
filetype.txt=TXT bestand
details.photodetails=Foto details
details.nophoto=Geen foto geselecteerd
details.photo.loading=Bezig met laden
+details.photo.bearing=Richting
details.media.connected=Geconnecteerd
details.audiodetails=Audio details
details.noaudio=Geen audiobestand geselecteerd
fieldname.duration=Duur
fieldname.speed=Snelheid
fieldname.verticalspeed=Verticale snelheid
+fieldname.description=Omschrijving
# Measurement units
units.original=Oorspronkelijke
error.readme.notfound=Leesmij bestand niet gevonden
error.osmimage.dialogtitle=Fout bij inlezen kaart afbeeldingen
error.osmimage.failed=Fout bij inlezen kaart afbeeldingen. Controleer je internet verbinding
-error.language.wrongfile=Het geselecteerde bestand is vermoedelijk geen taalbestand voor Prune
+error.language.wrongfile=Het geselecteerde bestand is vermoedelijk geen taalbestand voor GpsPrune
error.convertnamestotimes.nonames=Namen konden niet naar tijden worden geconverteerd
error.lookupsrtm.nonefound=Geen hoogtewaarden beschikbaar voor deze punten
error.lookupsrtm.nonerequired=Alle punten hebben reeds hoogte, er hoeft niets te worden opgezocht.
error.gpsies.uploadnotok=Gpsies server antwoordde met
error.gpsies.uploadfailed=De upload is mislukt. Fout
error.playaudiofailed=Kon audiobestand niet afspelen
+error.cache.notthere=De tegelcache map niet gevonden
+error.cache.empty=De tegelcache map is leeg
+error.cache.cannotdelete=Er konden geen tegels verwijderd worden
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Polish entries as extra
# Menu entries
menu.file=Plik
menu.file.addphotos=Dodaj zdj\u0119cia
+menu.file.recentfiles=Ostatnio u\u017cywane
menu.file.save=Zapisz
menu.file.exit=Zako\u0144cz
menu.track=\u015acie\u017cka
menu.view.browser.bing=Mapy Bing
menu.settings=Ustawienia
menu.settings.onlinemode=\u0141aduj mapy z sieci
+menu.settings.autosave=Autozapis ustawie\u0144 przy wyj\u015bciu
menu.help=Pomoc
# Popup menu for map
menu.map.zoomin=Powi\u0119ksz
# Functions
function.open=Otw\u00f3rz
+function.importwithgpsbabel=Importuj plik z GPSBabel
function.loadfromgps=\u0141aduj z GPS
function.sendtogps=Wy\u015blij dane do urz\u0105dzenia GPS
function.exportkml=Eksportuj KML
function.stopaudio=Zatrzymaj plik audio
function.help=Pomoc
function.showkeys=Poka\u017c klawisze skr\u00f3tu
-function.about=O Prune
+function.about=O GpsPrune
function.checkversion=Sprawd\u017a czy jest nowa wersja
function.saveconfig=Zapisz ustawienia
function.diskcache=Zapisz mapy na dysk
+function.managetilecache=Zarz\u0105dzaj keszem p\u0142ytek
# Dialogs
-dialog.exit.confirm.title=Zako\u0144cz Prune
+dialog.exit.confirm.title=Zako\u0144cz GpsPrune
dialog.exit.confirm.text=Dane nie s\u0105 zapisane. Czy na pewno chcesz wyj\u015b\u0107?
dialog.openappend.title=Dodaj do istniej\u0105cych danych
dialog.openappend.text=Doda\u0107 dane do ju\u017c za\u0142adowanych?
dialog.exportgpx.desc=Opis
dialog.exportgpx.includetimestamps=Do\u0142\u0105cz znaczniki czasu
dialog.exportgpx.copysource=Kopiuj \u017ar\u00f3d\u0142owy xml
+dialog.exportgpx.encoding=Kodowanie
+dialog.exportgpx.encoding.system=Systemowe
+dialog.exportgpx.encoding.utf8=UTF-8
dialog.exportpov.text=Wprowad\u017a dodatkowe parametry eksportu do formatu POV
dialog.exportpov.font=Czcionka
dialog.exportpov.camerax=Kamera X
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.douglaspeucker.title=kompresja Douglasa-Peuckera
+dialog.compress.douglaspeucker.paramdesc=wsp\u00f3\u0142czynnik rozpi\u0119to\u015bci (szeroko\u015bci korytarza)
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.help.help=Na stronie\n http://activityworkshop.net/software/gpsprune/ \nznajdziesz wi\u0119cej informacji oraz podr\u0119cznik u\u017cytkownika
dialog.about.version=Wersja
dialog.about.build=Build
-dialog.about.summarytext1=Prune s\u0142u\u017cy do pobierania, wy\u015bwietlania i edycji danych z odbiornik\u00f3w GPS.
+dialog.about.summarytext1=GpsPrune s\u0142u\u017cy do pobierania, wy\u015bwietlania i edycji danych z odbiornik\u00f3w GPS.
dialog.about.summarytext2=Ten program zosta\u0142 udost\u0119pniony na podstawie licencji GNU pozwalaj\u0105cej<br>na jego wolne, nieograniczone i og\u00f3lno\u015bwiatowe u\u017cytkowanie i rozszerzanie. <br>Kopiowanie, rozprowadzanie i modyfikowanie s\u0105 dozwolone i zalecane<br>zgodnie z warunkami zawartymi w do\u0142\u0105czonym pliku<code>license.txt</code>
dialog.about.summarytext3=Na stronie <code style="font-weight:bold">http://activityworkshop.net/</code> znajdziesz wi\u0119cej informacji oraz podr\u0119cznik u\u017cytkownika.
dialog.about.languages=Dost\u0119pne j\u0119zyki
dialog.about.yes=Tak
dialog.about.no=Nie
dialog.about.credits=Podzi\u0119kowania
-dialog.about.credits.code=Kod Prune napisany przez
+dialog.about.credits.code=Kod GpsPrune napisany przez
dialog.about.credits.exifcode=Kod Exif przez
dialog.about.credits.icons=Niekt\u00f3re ikony z
dialog.about.credits.translators=T\u0142umacze
dialog.about.credits.thanks=Podzi\u0119kowania dla
dialog.about.readme=Czytaj to
dialog.checkversion.error=Nie mo\u017cna sprawdzi\u0107 numeru wersji.\nSprawd\u017a po\u0142\u0105czenie z internetem.
-dialog.checkversion.uptodate=U\u017cywasz najnowszej wersji Prune.
-dialog.checkversion.newversion1=Dost\u0119pna jest nowa wersja Prune! Najnowsz\u0105 wersj\u0105 jest wersja
+dialog.checkversion.uptodate=U\u017cywasz najnowszej wersji GpsPrune.
+dialog.checkversion.newversion1=Dost\u0119pna jest nowa wersja GpsPrune! Najnowsz\u0105 wersj\u0105 jest wersja
dialog.checkversion.newversion2=.
dialog.checkversion.releasedate1=Nowa wersja zosta\u0142a opublikowana
dialog.checkversion.releasedate2=.
-dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/prune/download.html.
+dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/gpsprune/download.html.
dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w zamiast myszki
dialog.keys.keylist=<table><tr><td>klawisze strza\u0142ek</td><td>Przesuwa map\u0119 w lewo, w prawo, w g\u00f3r\u0119, w d\u00f3\u0142</td></tr><tr><td>Ctrl + lewa, prawa strza\u0142ka</td><td>Wybierz punkt poprzedni lub nast\u0119pny</td></tr><tr><td>Ctrl + strza\u0142ka w g\u00f3r\u0119, w d\u00f3\u0142</td><td>Powi\u0119ksz, pomniejsz</td></tr><tr><td>Del</td><td>Usun bie\u017c\u0105cy punkt</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Schemat kolor\u00f3w
dialog.saveconfig.prune.linewidth=Szeroko\u015b\u0107 linii
dialog.saveconfig.prune.kmltrackcolour=Kolor \u015bcie\u017cki w pliku KML
+dialog.saveconfig.prune.autosavesettings=ustawienia autozapisu
dialog.setpaths.intro=Je\u015bli zachodzi tak potrzeba, mo\u017cesz wybra\u0107 \u015bcie\u017cki do aplikacji zewn\u0119trznych
dialog.setpaths.found=Znalezione \u015bcie\u017cki?
dialog.addaltitude.noaltitudes=Wybrany zakres nie zawiera danych o wysoko\u015bciach
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.secondintro=B\u0119dziesz musia\u0142 zapisa\u0107 ustawienia<p>i zrestartowa\u0107 GpsPrune 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.
+dialog.setlanguage.endmessage=Zapisz ustawienia i zrestartuj GpsPrune\n by zmiana j\u0119zyka odnios\u0142a skutek.
+dialog.setlanguage.endmessagewithautosave=Zrestartuj GpsPrune by zmiana j\u0119zyka odnios\u0142a skutek.
dialog.diskcache.save=Zapisz mapy na dysk
dialog.diskcache.dir=katalog pami\u0119ci podr\u0119cznej
dialog.diskcache.createdir=stw\u00f3rz katalog
dialog.diskcache.nocreate=Nie utworzono katalogu pami\u0119ci podr\u0119cznej
+dialog.diskcache.table.path=\u015acie\u017cka
+dialog.diskcache.table.usedby=u\u017cywane przez
+dialog.diskcache.table.zoom=Powi\u0119kszenie
+dialog.diskcache.table.tiles=p\u0142ytek
+dialog.diskcache.table.megabytes=megabajt\u00f3w
+dialog.diskcache.tileset=zestaw p\u0142ytek
+dialog.diskcache.tileset.multiple=wiele
+dialog.diskcache.deleteold=Usu\u0144 stare p\u0142ytki
+dialog.diskcache.deleteall=Usu\u0144 wszystkie p\u0142ytki
+dialog.diskcache.deleted1=Usuni\u0119to
+dialog.diskcache.deleted2=plik\u00f3w z kesza
dialog.deletefieldvalues.intro=Wybierz pola do skasowania z wybranego zakresu
dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
dialog.searchwikipedianames.search=Szukaj
# 3d window
-dialog.3d.title=Prune widok tr\u00f3jwymiarowy
+dialog.3d.title=GpsPrune widok tr\u00f3jwymiarowy
dialog.3d.altitudefactor=Wsp\u00f3\u0142czynnik skalowania wysoko\u015bci
dialog.3dlines.title=Linie siatki
dialog.3dlines.empty=Brak siatki do wy\u015bwietlenia!
button.browse=Przegl\u0105daj...
button.addnew=Dodaj nowy
button.delete=Usu\u0144
+button.manage=Zarz\u0105dzaj
# File types
filetype.txt=Pliki TXT
details.photodetails=Szczeg\u00f3\u0142y zdj\u0119cia
details.nophoto=Brak zaznaczonego zdj\u0119cia
details.photo.loading=Wczytywanie
+details.photo.bearing=Kierunek
details.media.connected=Pod\u0142\u0105czony
details.audiodetails=Szczeg\u00f3\u0142y audio
details.noaudio=Brak zaznaczonego audio
fieldname.duration=Czas trwania
fieldname.speed=Pr\u0119dko\u015b\u0107
fieldname.verticalspeed=Pr\u0119dko\u015b\u0107 pionowa
+fieldname.description=Opis
# Measurement units
units.original=Oryginalny
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.
-error.language.wrongfile=Wybrany plik nie jest plikiem z t\u0142umaczeniem dla Prune
+error.language.wrongfile=Wybrany plik nie jest plikiem z t\u0142umaczeniem dla GpsPrune
error.convertnamestotimes.nonames=\u017badne nazwy nie mog\u0142y zosta\u0107 zmienione na czas
error.lookupsrtm.nonefound=Nie znaleziono danych o wysoko\u015bci.
error.lookupsrtm.nonerequired=Wszystkie pola maj\u0105 informacj\u0119 o wysoko\u015bci, nie ma czego szuka\u0107
error.gpsies.uploadnotok=Serwer Gpsies zwr\u00f3ci\u0142 informacj\u0119
error.gpsies.uploadfailed=B\u0142\u0105d wysy\u0142ania
error.playaudiofailed=Nie powiod\u0142o si\u0119 odtwarzanie pliku audio
+error.cache.notthere=Nie znaleziono katalogu kesza
+error.cache.empty=Katalog kesza jest pusty
+error.cache.cannotdelete=\u017badne p\u0142ytki nie mog\u0142y zosta\u0107 usuni\u0119te
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# (Brazilian) Portuguese entries as extra
# Menu entries
menu.file=Arquivo
menu.file.addphotos=Adicionar fotos
+menu.file.recentfiles=Arquivos recentes
menu.file.save=Salvar
menu.file.exit=Sair
menu.track=Rota
menu.view.browser.bing=Mapas do Bing
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
# Functions
function.open=Abrir
+function.importwithgpsbabel=Importar arquivo com GPSBabel
function.loadfromgps=Carregar dados do GPS
function.sendtogps=Enviar dados para o GPS
function.exportkml=Exportar para KML
function.stopaudio=Parar arquivo de \u00e1udio
function.help=Ajuda
function.showkeys=Mostrar atalhos de teclado
-function.about=Sobre o Prune
+function.about=Sobre o GpsPrune
function.checkversion=Verificar novas vers\u00f5es
function.saveconfig=Salvar configura\u00e7\u00f5es
function.diskcache=Salvar mapas para o disco
+function.managetilecache=Gerenciar cache de fundos
# Dialogs
-dialog.exit.confirm.title=Sair do Prune
+dialog.exit.confirm.title=Sair do GpsPrune
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.exportgpx.desc=Descri\u00e7\u00e3o
dialog.exportgpx.includetimestamps=Incluir data-hora
dialog.exportgpx.copysource=Copiar fonte xml
+dialog.exportgpx.encoding=Codifica\u00e7\u00e3o
+dialog.exportgpx.encoding.system=Sistema
+dialog.exportgpx.encoding.utf8=UTF-8
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.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.closepoints.paramdesc=Fator de deslocamento
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.douglaspeucker.title=Compress\u00e3o Douglas-Peucker
+dialog.compress.douglaspeucker.paramdesc=Fator de deslocamento
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.help.help=Por favor, veja\n http://activityworkshop.net/software/gpsprune/\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.summarytext1=GpsPrune \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.yes=Sim
dialog.about.no=N\u00e3o
dialog.about.credits=Cr\u00e9ditos
-dialog.about.credits.code=C\u00f3digo do Prune escrito por
+dialog.about.credits.code=C\u00f3digo do GpsPrune 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.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.uptodate=Voc\u00ea est\u00e1 usando a \u00faltima vers\u00e3o do GpsPrune.
+dialog.checkversion.newversion1=Uma nova vers\u00e3o do GpsPrune 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.checkversion.download=Para baixar a nova vers\u00e3o, v\u00e1 para http://activityworkshop.net/software/gpsprune/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.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=Esquema de cores
dialog.saveconfig.prune.linewidth=Espessura da linha
dialog.saveconfig.prune.kmltrackcolour=Cor da rota KML
+dialog.saveconfig.prune.autosavesettings=Configura\u00e7\u00f5es para salvamento autom\u00e1tico
dialog.setpaths.intro=Se voc\u00ea precisar, voc\u00ea pode escolher os caminhos para as aplica\u00e7\u00f5es externas:
dialog.setpaths.found=Caminho encontrado?
dialog.addaltitude.noaltitudes=O intervalo selecionado n\u00e3o cont\u00e9m altitudes
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.secondintro=Voc\u00ea precisa salvar suas configura\u00e7\u00f5es e ent\u00e3o<p>reiniciar o GpsPrune 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.
+dialog.setlanguage.endmessage=Agora salve suas configura\u00e7\u00f5es e reinicie o GpsPrune\npara que a mudan\u00e7a de idioma ocorra.
+dialog.setlanguage.endmessagewithautosave=Por favor, reinicie o GpsPrune para que a mudan\u00e7a de idioma tenha efeito.
dialog.diskcache.save=Salvar imagens do mapa para o disco
dialog.diskcache.dir=Diret\u00f3rio da cache
dialog.diskcache.createdir=Criar diret\u00f3rio
dialog.diskcache.nocreate=Diret\u00f3rio da cache n\u00e3o foi criado
+dialog.diskcache.table.path=Caminho
+dialog.diskcache.table.usedby=Usado por
+dialog.diskcache.table.zoom=Zoom
+dialog.diskcache.table.tiles=Fundos
+dialog.diskcache.table.megabytes=Megabytes
+dialog.diskcache.tileset=Conjunto de fundos
+dialog.diskcache.tileset.multiple=m\u00faltiplos
+dialog.diskcache.deleteold=Apagar fundos antigos
+dialog.diskcache.deleteall=Apagar todos os fundos
+dialog.diskcache.deleted1=Removidos
+dialog.diskcache.deleted2=arquivos do cache
dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual
dialog.setlinewidth.text=Insira a espessura das linhas para desenhar as rotas (1-4)
dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada:
dialog.searchwikipedianames.search=Procurar por:
# 3d window
-dialog.3d.title=Vista 3D do Prune
+dialog.3d.title=Vista 3D do GpsPrune
dialog.3d.altitudefactor=Fator de exagera\u00e7\u00e3o de altitude
-dialog.3dlines.title=Linhas da grade do Prune
+dialog.3dlines.title=Linhas da grade do GpsPrune
dialog.3dlines.empty=Nenhuma linha de grade para exibir!
dialog.3dlines.intro=Estas s\u00e3o as linhas da grade para a vista 3D.
button.browse=Navegar...
button.addnew=Adicionar novo
button.delete=Remover
+button.manage=Gerenciar
# File types
filetype.txt=Arquivos TXT
details.photodetails=Detalhes da foto
details.nophoto=Nenhuma foto selecionada
details.photo.loading=Carregando
+details.photo.bearing=Apontando
details.media.connected=Conectada
details.audiodetails=Detalhes do \u00e1udio
details.noaudio=Nenhum arquivo de \u00e1udio selecionado
fieldname.duration=Dura\u00e7\u00e3o
fieldname.speed=Velocidade
fieldname.verticalspeed=Velocidade vertical
+fieldname.description=Descri\u00e7\u00e3o
# Measurement units
units.original=Original
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.language.wrongfile=O arquivo selecionado n\u00e3o parece ser um arquivo de idioma do GpsPrune
error.convertnamestotimes.nonames=Nenhum nome pode ser convertido para tempo
error.lookupsrtm.nonefound=Nenhum valor de altitude encontrado
error.lookupsrtm.nonerequired=Todos os pontos j\u00e1 possuem altitude, assim n\u00e3o h\u00e1 nada a procurar
error.gpsies.uploadnotok=O servidor Gpsies retornou a mensagem
error.gpsies.uploadfailed=O envio falhou com o erro
error.playaudiofailed=Falha ao reproduzir arquivo de \u00e1udio
+error.cache.notthere=A paste de cache de fundos n\u00e3o foi encontrada
+error.cache.empty=A pasta de cache de fundos est\u00e1 vazia
+error.cache.cannotdelete=Nenhum fundo pode ser removido
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Romanian entries as extra
# Menu entries
function.setlanguage=Selectare limba
function.help=Ajutor
function.showkeys=Arat\u0103 tastele scurt\u0103turi
-function.about=Despre Prune
+function.about=Despre GpsPrune
function.checkversion=Verific\u0103 pentru o versiune noua
# Dialogs
-dialog.exit.confirm.title=Ie\u015fire din programul Prune
+dialog.exit.confirm.title=Ie\u015fire din programul GpsPrune
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?
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Turkish entries as extra
# Menu entries
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.about=GpsPrune 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.title=GpsPrune'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.correlate.options.photolater=Foto noktadan sonra
dialog.correlate.options.pointlaterphoto=Nokta fotodan sonra
dialog.pastecoordinates.coords=Koordinatlar
-dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/prune/\n sitesinde bak.
+dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/gpsprune/\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.summarytext1=GpsPrune 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.languages=GpsPrune ile kullanabilir diller
dialog.about.translatedby=Turkish text by katpatuka
dialog.about.systeminfo=Sistem bilgisi
dialog.about.systeminfo.os=\u0130\u015fletim Sistemi
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.uptodate=GpsPrune'nin so s\u00fcr\u00fcm\u00fc kullan\u0131yorsun.
+dialog.checkversion.newversion1=GpsPrune'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.checkversion.download=Yeni s\u00fcr\u00fcm indirmek i\u00e7in http://activityworkshop.net/software/gpsprune/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:
-# Text entries for the Prune application
+# Text entries for the GpsPrune application
# Chinese entries as extra
# Menu entries
menu.file=\u6587\u4ef6
menu.file.addphotos=\u6dfb\u52a0\u76f8\u7247
+menu.file.recentfiles=\u6700\u8fd1\u6253\u5f00\u8fc7\u6587\u4ef6
menu.file.save=\u4fdd\u5b58
menu.file.exit=\u9000\u51fa
menu.track=\u8f68\u8ff9
menu.view.browser.bing=Bing(\u5fc5\u5e94\uff09\u5730\u56fe
menu.settings=\u8bbe\u7f6e
menu.settings.onlinemode=\u4ece\u7f51\u4e0a\u5bfc\u5165\u5730\u56fe
+menu.settings.autosave=\u9000\u51fa\u65f6\u81ea\u52a8\u4fdd\u5b58\u8bbe\u7f6e
menu.help=\u5e2e\u52a9
# Popup menu for map
menu.map.zoomin=\u653e\u5927
menu.map.showmap=\u663e\u793a\u5730\u56fe
menu.map.showscalebar=\u663e\u793a\u6bd4\u4f8b\u5c3a
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=T
+altkey.menu.range=R
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=O
+altkey.menu.audio=A
+altkey.menu.settings=S
+altkey.menu.help=H
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=L
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.range.all=A
+shortcut.menu.help.help=H
+
# Functions
function.open=\u6253\u5f00
+function.importwithgpsbabel=\u7528GPSBabel\u5bfc\u5165\u6587\u4ef6
function.loadfromgps=\u4eceGPS\u5bfc\u5165
function.sendtogps=\u53d1\u9001\u81f3GPS
function.exportkml=\u8f93\u51faKML\u6587\u4ef6
function.stopaudio=\u505c\u6b62\u64ad\u653e
function.help=\u5e2e\u52a9
function.showkeys=\u663e\u793a\u5feb\u6377\u952e
-function.about=\u5173\u4e8ePrune
+function.about=\u5173\u4e8eGpsPrune
function.checkversion=\u68c0\u67e5\u66f4\u65b0
function.saveconfig=\u4fdd\u5b58\u8bbe\u7f6e
function.diskcache=\u4fdd\u5b58\u5730\u56fe
+function.managetilecache=\u7ba1\u7406\u5730\u56fe\u533a\u57df\u6570\u636e\u7f13\u5b58
# Dialogs
dialog.exit.confirm.title=\u9000\u51fa
dialog.exportgpx.desc=\u63cf\u8ff0
dialog.exportgpx.includetimestamps=\u5305\u542b\u65f6\u95f4
dialog.exportgpx.copysource=\u4ece\u6e90XML\u6587\u4ef6\u590d\u5236
+dialog.exportgpx.encoding=\u7f16\u7801
+dialog.exportgpx.encoding.system=\u9ed8\u8ba4\u7cfb\u7edf\u7f16\u7801
+dialog.exportgpx.encoding.utf8=\u4f7f\u7528UTF-8
dialog.exportpov.text=\u8bf7\u8f93\u5165POV\u53c2\u6570
dialog.exportpov.font=\u5b57\u4f53
dialog.exportpov.camerax=X\u76f8\u673a
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.douglaspeucker.title=Douglas-Peucker \u538b\u7f29
+dialog.compress.douglaspeucker.paramdesc=\u95f4\u8ddd\u7cfb\u6570
dialog.compress.summarylabel=\u8981\u5220\u9664\u7684\u70b9
dialog.pastecoordinates.desc=\u5728\u6b64\u8f93\u5165\u6216\u7c98\u8d34\u5750\u6807\u70b9
dialog.pastecoordinates.coords=\u5750\u6807\u70b9
dialog.pastecoordinates.nothingfound=\u8bf7\u68c0\u67e5\u5750\u6807\u6570\u636e\u5e76\u91cd\u8bd5
-dialog.help.help=\u66f4\u591a\u4fe1\u606f\u548c\u7528\u6cd5\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\nhttp://activityworkshop.net/software/prune///
+dialog.help.help=\u66f4\u591a\u4fe1\u606f\u548c\u7528\u6cd5\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\nhttp://activityworkshop.net/software/gpsprune/
dialog.about.version=\u7248\u672c
dialog.about.build=Build
-dialog.about.summarytext1=Prune\u662f\u4e00\u4e2a\u4eceGPS\u4e2d\u5bfc\u5165\u6570\u636e\uff0c\u663e\u793a\u6570\u636e\u548c\u7f16\u8f91\u6570\u636e\u7684\u8f6f\u4ef6
+dialog.about.summarytext1=GpsPrune\u662f\u4e00\u4e2a\u4eceGPS\u4e2d\u5bfc\u5165\u6570\u636e\uff0c\u663e\u793a\u6570\u636e\u548c\u7f16\u8f91\u6570\u636e\u7684\u8f6f\u4ef6
dialog.about.summarytext2=\u5b83\u7684\u53d1\u884c\u662f\u57fa\u4e8eGnu GPL\u89c4\u5219\uff0c\u662f\u514d\u8d39\u7684\uff0c\u5f00\u653e\u5f0f\u7684\uff0c\u5168\u4e16\u754c\u5171\u7528\u5e76\u6539\u5584\u589e\u5f3a\u5176\u529f\u80fd\n\u5728\u7b26\u5408"license.txt"\u6761\u4ef6\u4e0b\uff0c\u5bb9\u8bb8\u5e76\u9f13\u52b1\u590d\u5236\uff0c\u5206\u53d1\u53ca\u4fee\u6539\u3002
dialog.about.summarytext3=\u66f4\u591a\u4fe1\u606f\u53ca\u7528\u6cd5\u6307\u5357\uff0c\u8bf7\u53c2\u8003\u7f51\u7ad9\uff1a\nhttp:activityworkshop.net/
dialog.about.languages=\u652f\u6301\u8bed\u8a00
dialog.about.yes=\u662f
dialog.about.no=\u5426
dialog.about.credits=\u81f4\u8c22
-dialog.about.credits.code=Prune \u539f\u7801\u7f16\u5199
+dialog.about.credits.code=GpsPrune \u539f\u7801\u7f16\u5199
dialog.about.credits.exifcode=Exif \u539f\u7801\u7f16\u5199
dialog.about.credits.icons=\u56fe\u6807\u6765\u81ea\u4e8e
dialog.about.credits.translators=\u8bd1\u8005
dialog.checkversion.newversion2=
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.checkversion.download=\u4e0b\u8f7d\u6700\u65b0\u7248\u672c\uff0c\u8bf7\u767b\u9646\u7f51\u7ad9\uff1a\nhttp:activityworkshop.net/software/gpsprune/download.html
dialog.keys.intro=\u53ef\u7528\u4e0b\u5217\u5feb\u6377\u952e\u66ff\u4ee3\u9f20\u6807
dialog.keys.keylist=<table><tr><td>\u7bad\u5934</td><td>\u4e0a\u4e0b\u5de6\u53f3\u79fb\u52a8\u5730\u56fe</td></tr><tr><td>Ctrl + \u5de6\u53f3\u7bad\u5934</td><td>\u9009\u53d6\u524d\uff0c\u540e\u70b9</td></tr><tr><td>Ctrl + \u4e0a\u4e0b\u7bad\u5934</td><td>\u653e\u5927\u7f29\u5c0f</td></tr><tr><td>Ctrl + PgUp, PgDown</td><td>\u9009\u62e9\u524d\u540e\u6bb5</td></tr><tr><td>Ctrl + Home, End</td><td>\u9009\u62e9\u9996\u672b\u70b9</td></tr><tr><td>Del</td><td>\u5220\u9664\u5f53\u524d\u70b9</td></tr></table>
dialog.keys.normalmodifier=Ctrl
dialog.saveconfig.prune.colourscheme=\u989c\u8272
dialog.saveconfig.prune.linewidth=\u7ebf\u4f53\u5bbd\u5ea6
dialog.saveconfig.prune.kmltrackcolour=KML\u8f68\u8ff9\u989c\u8272
+dialog.saveconfig.prune.autosavesettings=\u81ea\u52a8\u4fdd\u5b58\u8bbe\u7f6e
dialog.setpaths.intro=\u82e5\u9700\u8981\uff0c\u53ef\u8bbe\u5b9a\u5916\u6302\u7a0b\u5e8f\u8def\u5f84
dialog.setpaths.found=\u627e\u5230\u8def\u5f84\uff1f
dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
dialog.colourchooser.green=\u7eff
dialog.colourchooser.blue=\u84dd
dialog.setlanguage.firstintro=\u4f60\u53ef\u4ee5\u9009\u62e9\u5df2\u6709\u8bed\u8a00,<p>\u6216\u9009\u62e9\u5916\u6302\u8bed\u8a00\u5305
-dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fPrune\u4f7f\u8bbe\u7f6e\u751f\u6548
+dialog.setlanguage.secondintro=\u8bf7\u4fdd\u5b58\u8bbe\u7f6e<p>\u5e76\u91cd\u542fGpsPrune\u4f7f\u8bbe\u7f6e\u751f\u6548
dialog.setlanguage.language=\u8bed\u8a00
dialog.setlanguage.languagefile=\u8bed\u8a00\u5305
-dialog.setlanguage.endmessage=\u73b0\u5728\u8bf7\u4fdd\u5b58\u8bbe\u7f6e\u5e76\u91cd\u542fPrune\n\u4f7f\u8bbe\u7f6e\u751f\u6548
+dialog.setlanguage.endmessage=\u73b0\u5728\u8bf7\u4fdd\u5b58\u8bbe\u7f6e\u5e76\u91cd\u542fGpsPrune\n\u4f7f\u8bbe\u7f6e\u751f\u6548
+dialog.setlanguage.endmessagewithautosave=\u8981\u4f7f\u6240\u9009\u8bed\u8a00\u751f\u6548\uff0c\u8bf7\u91cd\u65b0\u542f\u52a8GosPrune
dialog.diskcache.save=\u5730\u56fe\u56fe\u7247\u4fdd\u5b58\u5230\u7535\u8111
dialog.diskcache.dir=\u4fdd\u5b58\u8def\u5f84
dialog.diskcache.createdir=\u65b0\u5efa\u8def\u5f84
dialog.diskcache.nocreate=\u672a\u65b0\u5efa\u8def\u5f84
+dialog.diskcache.table.path=\u8def\u5f84
+dialog.diskcache.table.usedby=\u4f7f\u7528\u8005
+dialog.diskcache.table.zoom=\u653e\u5927\u7f29\u5c0f
+dialog.diskcache.table.tiles=\u627e\u5230\u7684\u5730\u56fe\u533a\u57df\u6570\u636e
+dialog.diskcache.table.megabytes=MB
+dialog.diskcache.tileset=\u5730\u56fe\u533a\u57df\u6570\u5b58\u653e\u8def\u5f84
+dialog.diskcache.tileset.multiple=\u6570\u76ee
+dialog.diskcache.deleteold=\u5220\u9664\u65e7\u533a\u57df\u6570\u636e
+dialog.diskcache.deleteall=\u5220\u9664\u6240\u6709\u533a\u57df\u6570\u636e
+dialog.diskcache.deleted1=\u5df2\u5220\u9664
+dialog.diskcache.deleted2=\u7f13\u5b58\u5185\u6587\u4ef6
dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u533a\u57df
dialog.setlinewidth.text=\u8f93\u5165\u8f68\u8ff9\u7ebf\u5bbd\u50cf\u7d20\u503c\uff081-4\uff09
dialog.downloadosm.desc=\u786e\u8ba4\u4eceOSM\u4e0b\u8f7d\u8be5\u5730\u533a\u539f\u59cb\u6570\u636e
dialog.searchwikipedianames.search=\u67e5\u627e
# 3d window
-dialog.3d.title=Prune 3D \u663e\u793a
+dialog.3d.title=GpsPrune 3D \u663e\u793a
dialog.3d.altitudefactor=\u9ad8\u5ea6\u653e\u5927\u7cfb\u6570
-dialog.3dlines.title=Prune \u7f51\u683c\u7ebf
+dialog.3dlines.title=GpsPrune \u7f51\u683c\u7ebf
dialog.3dlines.empty=\u65e0\u6cd5\u663e\u793a\u7f51\u683c\u7ebf
dialog.3dlines.intro=3D \u7f51\u683c\u7ebf
button.browse=\u6d4f\u89c8...
button.addnew=\u6dfb\u52a0
button.delete=\u5220\u9664
+button.manage=\u7ba1\u7406
# File types
filetype.txt=TXT\u6587\u4ef6
details.photodetails=\u76f8\u7247\u4fe1\u606f
details.nophoto=\u65e0\u76f8\u7247\u88ab\u9009\u4e2d
details.photo.loading=\u6b63\u5bfc\u5165
+details.photo.bearing=\u65b9\u5411
details.media.connected=\u5df2\u94fe\u63a5
details.audiodetails=\u8be6\u7ec6\u4fe1\u606f
details.noaudio=\u672a\u9009\u62e9\u58f0\u97f3\u6587\u4ef6
fieldname.duration=\u65f6\u957f
fieldname.speed=\u901f\u5ea6
fieldname.verticalspeed=\u5782\u76f4\u901f\u5ea6
+fieldname.description=\u63cf\u8ff0
# Measurement units
units.original=\u539f\u59cb
error.gpsies.uploadnotok=gpsies\u670d\u52a1\u5668\u8fd4\u56de\u4fe1\u606f\uff1a
error.gpsies.uploadfailed=\u4e0a\u4f20\u51fa\u9519\u5931\u8d25
error.playaudiofailed=\u65e0\u6cd5\u64ad\u653e\u58f0\u97f3\u6587\u4ef6
+error.cache.notthere=\u672a\u627e\u5230\u533a\u57df\u6570\u636e\u7f13\u5b58\u6587\u4ef6\u5939
+error.cache.empty=\u533a\u57df\u6570\u636e\u6587\u4ef6\u5939\u7a7a
+error.cache.cannotdelete=\u65e0\u53ef\u5220\u9664\u533a\u57df\u6570\u636e
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.undo.UndoLoadAudios;
/**
- * Class to manage the loading of audio files
+ * Class to manage the loading of audio clips
*/
public class AudioLoader extends GenericFunction
{
private JFileChooser _fileChooser = null;
private GenericFileFilter _fileFilter = null;
- private TreeSet<AudioFile> _fileList = null;
+ private TreeSet<AudioClip> _fileList = null;
/**
// Show file dialog to choose file / directory(ies)
if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
{
- _fileList = new TreeSet<AudioFile>(new MediaSorter());
+ _fileList = new TreeSet<AudioClip>(new MediaSorter());
processFileList(_fileChooser.getSelectedFiles());
final int numFiles = _fileList.size();
if (numFiles == 0) {
{
if (file.isFile()) {
if (_fileFilter.accept(file)) {
- _fileList.add(new AudioFile(file));
+ _fileList.add(new AudioClip(file));
}
}
else if (file.isDirectory()) {
--- /dev/null
+package tim.prune.load;
+
+/**
+ * Class to manage the list of file formats supported by Gpsbabel
+ * (older versions of gpsbabel might not support all of these, of course).
+ * Certain supported formats such as txt, csv, gpx are not included here
+ * as GpsPrune can already load them directly.
+ */
+public abstract class BabelFileFormats
+{
+ /**
+ * @return an object array for the format descriptions
+ */
+ public static Object[] getDescriptions() {
+ return getColumn(0);
+ }
+
+ /**
+ * Find the (first) appropriate file format for the given file suffix
+ * @param inSuffix end of filename including .
+ * @return matching index or -1 if not found
+ */
+ public static int getIndexForFileSuffix(String inSuffix)
+ {
+ if (inSuffix != null && inSuffix.length() > 1)
+ {
+ final String[] suffixes = getColumn(2);
+ for (int i=0; i<suffixes.length; i++)
+ if (suffixes[i] != null && suffixes[i].equalsIgnoreCase(inSuffix))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Get the format name for the selected row
+ * @param inIndex index of selected format
+ * @return name of this format to give to gpsbabel
+ */
+ public static String getFormat(int inIndex)
+ {
+ String[] formats = getColumn(1);
+ if (inIndex >= 0 && inIndex < formats.length)
+ return formats[inIndex];
+ return null;
+ }
+
+ /**
+ * Extract the specified column of the data array
+ * @param inIndex column index from 0 to 2
+ * @return string array containing required data
+ */
+ private static String[] getColumn(int inIndex)
+ {
+ final String[] allFormats = {
+ "Alan Map500 tracklogs", "alantrl", ".trl",
+ "Alan Map500 waypoints and routes", "alanwpr", ".wpr",
+ "Brauniger IQ Series Barograph Download", "baroiq", null,
+ "Bushnell GPS Trail file", "bushnell_trl", null,
+ "Bushnell GPS Waypoint file", "bushnell", null,
+ "Cambridge/Winpilot glider software", "cambridge", null,
+ "CarteSurTable data file", "cst", null,
+ "Cetus for Palm/OS", "cetus", null,
+ "CoastalExplorer XML", "coastexp", null,
+ "Columbus/Visiontac V900 files", "v900", ".csv",
+ "CompeGPS data files", "compegps", ".wpt/.trk/.rte",
+ "CoPilot Flight Planner for Palm/OS", "copilot", null,
+ "cotoGPS for Palm/OS", "coto", null,
+ "Data Logger iBlue747 csv", "iblue747", null,
+ "Dell Axim Navigation System file format", "axim_gpb", ".gpb",
+ "DeLorme .an1 (drawing) file", "an1", null,
+ "DeLorme GPL", "gpl", null,
+ "DeLorme PN-20/PN-30/PN-40 USB protocol", "delbin", null,
+ "DeLorme Street Atlas Plus", "saplus", null,
+ "DeLorme Street Atlas Route", "saroute", null,
+ "DeLorme XMap HH Native .WPT", "xmap", null,
+ "DeLorme XMap/SAHH 2006 Native .TXT", "xmap2006", null,
+ "DeLorme XMat HH Street Atlas USA .WPT (PPC)", "xmapwpt", null,
+ "Destinator Itineraries", "destinator_itn", ".dat",
+ "Destinator Points of Interest", "destinator_poi", ".dat",
+ "Destinator TrackLogs", "destinator_trl", ".dat",
+ "EasyGPS binary format", "easygps", null,
+ "Enigma binary waypoint file", "enigma", ".ert",
+ "FAI/IGC Flight Recorder Data Format", "igc", null,
+ "Franson GPSGate Simulation", "gpssim", null,
+ "Fugawi", "fugawi", null,
+ "G7ToWin data files", "g7towin", ".g7t",
+ "Garmin 301 Custom position and heartrate", "garmin301", null,
+ "Garmin Logbook XML", "glogbook", null,
+ "Garmin MapSource - gdb", "gdb", null,
+ "Garmin MapSource - mps", "mapsource", null,
+ "Garmin PCX5", "pcx", null,
+ "Garmin POI database", "garmin_poi", null,
+ "Garmin Points of Interest", "garmin_gpi", ".gpi",
+ "Garmin Training Center", "gtrnctr", null,
+ "Garmin Training Center", "gtrnctr", ".tcx",
+ "Geocaching.com .loc", "geo", null,
+ "GeocachingDB for Palm/OS", "gcdb", null,
+ "Geogrid-Viewer ascii overlay file", "ggv_ovl", ".ovl",
+ "Geogrid-Viewer tracklogs", "ggv_log", ".log",
+ "GEOnet Names Server (GNS)", "geonet", null,
+ "GeoNiche .pdb", "geoniche", null,
+ "GlobalSat DG-100/BT-335 Download", "dg-100", null,
+ "Google Maps XML", "google", null,
+ "Google Navigator Tracklines", "gnav_trl", ".trl",
+ "GoPal GPS track log", "gopal", ".trk",
+ "GpilotS", "gpilots", null,
+ "GPS TrackMaker", "gtm", null,
+ "GpsDrive Format", "gpsdrive", null,
+ "GpsDrive Format for Tracks", "gpsdrivetrack", null,
+ "GPSman", "gpsman", null,
+ "GPSPilot Tracker for Palm/OS", "gpspilot", null,
+ "gpsutil", "gpsutil", null,
+ "HikeTech", "hiketech", null,
+ "Holux (gm-100) .wpo Format", "holux", null,
+ "Holux M-241 (MTK based) Binary File Format", "m241-bin", null,
+ "Holux M-241 (MTK based) download", "m241", null,
+ "Honda/Acura Navigation System VP Log File Format", "vpl", null,
+ "HSA Endeavour Navigator export File", "hsandv", null,
+ "Humminbird tracks", "humminbird_ht", ".ht",
+ "Humminbird waypoints and routes", "humminbird", ".hwr",
+ "IGN Rando track files", "ignrando", null,
+ "iGO2008 points of interest", "igo2008_poi", ".upoi",
+ "IGO8 .trk", "igo8", null,
+ "Jelbert GeoTagger data file", "jtr", null,
+ "Jogmap.de XML format", "jogmap", null,
+ "Kartex 5 Track File", "ktf2", null,
+ "Kartex 5 Waypoint File", "kwf2", null,
+ "Kompass (DAV) Track", "kompass_tk", ".tk",
+ "Kompass (DAV) Waypoints", "kompass_wp", ".wp",
+ "KuDaTa PsiTrex text", "psitrex", null,
+ "Lowrance USR", "lowranceusr", null,
+ "Magellan Explorist Geocaching", "maggeo", null,
+ "Magellan Mapsend", "mapsend", null,
+ "Magellan NAV Companion for Palm/OS", "magnav", null,
+ "Magellan SD files (as for eXplorist)", "magellanx", null,
+ "Magellan SD files (as for Meridian)", "magellan", null,
+ "Magellan serial protocol", "magellan", null,
+ "MagicMaps IK3D project file", "ik3d", ".ikt",
+ "Map&Guide 'TourExchangeFormat' XML", "tef", null,
+ "Map&Guide to Palm/OS exported files", "mag_pdb", ".pdb",
+ "MapAsia track file", "mapasia_tr7", ".tr7",
+ "Mapopolis.com Mapconverter CSV", "mapconverter", null,
+ "MapTech Exchange Format", "mxf", null,
+ "Memory-Map Navigator overlay files", "mmo", ".mmo",
+ "Microsoft AutoRoute 2002 (pin/route reader)", "msroute", null,
+ "Microsoft Streets and Trips (pin/route reader)", "msroute", null,
+ "Microsoft Streets and Trips 2002-2007", "s_and_t", null,
+ "Mobile Garmin XT Track files", "garmin_xt", null,
+ "Motorrad Routenplaner (Map&Guide) .bcr files", "bcr", null,
+ "MS PocketStreets 2002 Pushpin", "psp", null,
+ "MTK Logger (iBlue 747,...) Binary File Format", "mtk-bin", null,
+ "MTK Logger (iBlue 747,Qstarz BT-1000,...) download", "mtk", null,
+ "National Geographic Topo .tpg (waypoints)", "tpg", null,
+ "National Geographic Topo 2.x .tpo", "tpo2", null,
+ "National Geographic Topo 3.x/4.x .tpo", "tpo3", null,
+ "Navicache.com XML", "navicache", null,
+ "Navigon Mobile Navigator .rte files", "nmn4", null,
+ "Navigon Waypoints", "navigonwpt", null,
+ "NaviGPS GT-11/BGT-11 Download", "navilink", null,
+ "NaviGPS GT-31/BGT-31 datalogger", "sbp", ".sbp",
+ "NaviGPS GT-31/BGT-31 SiRF binary logfile", "sbn", ".sbn",
+ "Naviguide binary route file", "naviguide", ".twl",
+ "Navitel binary track", "navitel_trk", ".bin",
+ "Navitrak DNA marker format", "dna", null,
+ "NetStumbler Summary File", "netstumbler", "text",
+ "NIMA/GNIS Geographic Names File", "nima", null,
+ "Nokia Landmark Exchange", "lmx", null,
+ "OpenStreetMap data files", "osm", ".osm",
+ "OziExplorer", "ozi", null,
+ "PalmDoc Output", "palmdoc", null,
+ "PathAway Database for Palm/OS", "pathaway", null,
+ "PocketFMS breadcrumbs", "pocketfms_bc", null,
+ "PocketFMS flightplan", "pocketfms_fp", ".xml",
+ "PocketFMS waypoints", "pocketfms_wp", ".txt",
+ "Quovadis", "quovadis", null,
+ "Raymarine Waypoint File", "raymarine", ".rwf",
+ "Ricoh GPS Log File", "ricoh", null,
+ "See You flight analysis data", "cup", null,
+ "Skymap / KMD150 ascii files", "skyforce", null,
+ "SkyTraq Venus based loggers (download)", "skytraq", null,
+ "SkyTraq Venus based loggers Binary File Format", "skytraq-bin", null,
+ "Sportsim track files (part of zipped .ssz files)", "sportsim", null,
+ "SubRip subtitles for video mapping", "subrip", ".srt",
+ "Suunto Trek Manager (STM) .sdf files", "stmsdf", null,
+ "Suunto Trek Manager (STM) WaypointPlus files", "stmwpp", null,
+ "Swiss Map 25/50/100", "xol", ".xol",
+ "TomTom Itineraries", "tomtom_itn", ".itn",
+ "TomTom Places Itineraries", "tomtom_itn_places", ".itn",
+ "TomTom POI file", "tomtom_asc", ".asc",
+ "TomTom POI file", "tomtom", ".ov2",
+ "TopoMapPro Places File", "tmpro", null,
+ "TrackLogs digital mapping", "dmtlog", ".trl",
+ "U.S. Census Bureau Tiger Mapping Service", "tiger", null,
+ "Vcard Output (for iPod)", "vcard", null,
+ "VidaOne GPS for Pocket PC", "vidaone", ".gpb",
+ "Vito Navigator II tracks", "vitosmt", null,
+ "Vito SmartMap tracks", "vitovtt", ".vtt",
+ "WiFiFoFum 2.0 for PocketPC XML", "wfff", null,
+ "Wintec TES file", "wintec_tes", null,
+ "Wintec WBT-100/200 Binary File Format", "wbt-bin", null,
+ "Wintec WBT-100/200 GPS Download", "wbt", null,
+ "Wintec WBT-201/G-Rays 2 Binary File Format", "wbt-tk1", null,
+ "XAiOX iTrackU Logger", "itracku", null,
+ "XAiOX iTrackU Logger Binary File Format", "itracku-bin", null,
+ "Yahoo Geocode API data", "yahoo", null,
+ };
+ // Copy elements into new array
+ final int numRows = allFormats.length / 3;
+ String[] result = new String[numRows];
+ for (int i=0; i<numRows; i++) {
+ result[i] = allFormats[i*3 + inIndex];
+ }
+ return result;
+ }
+}
--- /dev/null
+package tim.prune.load;
+
+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.File;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.SourceInfo;
+import tim.prune.data.SourceInfo.FILE_TYPE;
+import tim.prune.gui.GuiGridLayout;
+
+
+/**
+ * Class to manage the loading of data from a file using GpsBabel.
+ * This allows the use of Gpsbabel's importing functions to convert to gpx.
+ */
+public class BabelLoadFromFile extends BabelLoader
+{
+ // file chooser
+ private JFileChooser _fileChooser = null;
+ // Input file
+ private File _inputFile = null;
+ // Label for filename
+ private JLabel _inputFileLabel = null;
+ // Dropdown for format of file
+ private JComboBox _formatDropdown = null;
+ // Last used file suffix
+ private String _lastSuffix = null;
+
+ /**
+ * Constructor
+ * @param inApp Application object to inform of data load
+ */
+ public BabelLoadFromFile(App inApp) {
+ super(inApp);
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.importwithgpsbabel";
+ }
+
+ /** @return complete input file path for gpsbabel call */
+ protected String getFilePath() {
+ return _inputFile.getAbsolutePath();
+ }
+
+ /** @return Source info */
+ protected SourceInfo getSourceInfo() {
+ return new SourceInfo(_inputFile, FILE_TYPE.GPSBABEL);
+ }
+
+ /** @return input format */
+ protected String getInputFormat() {
+ return BabelFileFormats.getFormat(_formatDropdown.getSelectedIndex());
+ }
+
+ /** @return true if function can be run */
+ protected boolean isInputOk() {
+ return _inputFile.exists() && _inputFile.canRead();
+ }
+
+ /**
+ * Override the begin method to specify input file first
+ */
+ public void begin()
+ {
+ // Construct file chooser if necessary
+ if (_fileChooser == null)
+ {
+ _fileChooser = new JFileChooser();
+ // start from directory in config if already set
+ String configDir = Config.getConfigString(Config.KEY_TRACK_DIR);
+ if (configDir == null) {configDir = Config.getConfigString(Config.KEY_PHOTO_DIR);}
+ if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
+ _fileChooser.setMultiSelectionEnabled(false); // Single files only
+ }
+ // Show the open dialog
+ if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
+ {
+ _inputFile = _fileChooser.getSelectedFile();
+ if (_inputFile != null && isInputOk()) {
+ super.begin();
+ }
+ }
+ }
+
+ /**
+ * Begin the load function with a previously-specified file
+ * @param inFile file to load
+ */
+ public void beginWithFile(File inFile)
+ {
+ _inputFile = inFile;
+ super.begin();
+ }
+
+ /**
+ * @return a panel containing the main dialog components
+ */
+ protected JPanel makeDialogComponents()
+ {
+ JPanel outerPanel = new JPanel();
+ outerPanel.setLayout(new BorderLayout());
+ // Main panel with options etc
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+
+ // text fields for options
+ JPanel gridPanel = new JPanel();
+ GuiGridLayout grid = new GuiGridLayout(gridPanel);
+ JLabel nameLabel = new JLabel(I18nManager.getText("details.track.file"));
+ grid.add(nameLabel);
+ _inputFileLabel = new JLabel("------------");
+ grid.add(_inputFileLabel);
+ JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
+ grid.add(formatLabel);
+ _formatDropdown = new JComboBox(BabelFileFormats.getDescriptions());
+ grid.add(_formatDropdown);
+ gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
+ mainPanel.add(gridPanel);
+
+ // checkboxes
+ ChangeListener checkboxListener = new ChangeListener() {
+ public void stateChanged(ChangeEvent e)
+ {
+ enableOkButton();
+ }
+ };
+ _waypointCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.getwaypoints"), true);
+ _waypointCheckbox.addChangeListener(checkboxListener);
+ _waypointCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_waypointCheckbox);
+ _trackCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.gettracks"), true);
+ _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);
+ outerPanel.add(mainPanel, BorderLayout.NORTH);
+
+ // Lower panel with ok and cancel buttons
+ 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)
+ {
+ // start thread to call gpsbabel
+ _cancelled = false;
+ new Thread(BabelLoadFromFile.this).start();
+ }
+ };
+ _okButton.addActionListener(okListener);
+ buttonPanel.add(_okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _cancelled = true;
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ outerPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return outerPanel;
+ }
+
+ /**
+ * Initialise dialog
+ */
+ protected void initDialog()
+ {
+ _inputFileLabel.setText(_inputFile.getName());
+ // Get suffix of filename and compare with previous one
+ String filename = _inputFile.getName();
+ int dotPos = filename.lastIndexOf('.');
+ String suffix = (dotPos > 0 ? filename.substring(dotPos) : null);
+ if (suffix != null && !suffix.equals(".") && (_lastSuffix == null || !suffix.equalsIgnoreCase(_lastSuffix)))
+ {
+ // New suffix chosen, so select first appropriate format (if any)
+ int selIndex = BabelFileFormats.getIndexForFileSuffix(suffix);
+ if (selIndex >= 0) {
+ _formatDropdown.setSelectedIndex(selIndex);
+ }
+ }
+ _lastSuffix = suffix;
+ }
+
+ /**
+ * Save settings in config
+ */
+ protected void saveConfigValues()
+ {
+ // nothing needed
+ }
+}
--- /dev/null
+package tim.prune.load;
+
+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.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.config.Config;
+import tim.prune.data.SourceInfo;
+import tim.prune.data.SourceInfo.FILE_TYPE;
+
+/**
+ * Class to manage the loading of data from a GPS device using GpsBabel
+ */
+public class BabelLoadFromGps extends BabelLoader
+{
+ // Text fields for entering device and format
+ private JTextField _deviceField = null, _formatField = null;
+
+
+ /**
+ * Constructor
+ * @param inApp Application object to inform of data load
+ */
+ public BabelLoadFromGps(App inApp) {
+ super(inApp);
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.loadfromgps";
+ }
+
+ /** @return device name as file path */
+ protected String getFilePath() {
+ return _deviceField.getText();
+ }
+
+ /** @return Source info */
+ protected SourceInfo getSourceInfo() {
+ return new SourceInfo(_deviceField.getText(), FILE_TYPE.GPSBABEL);
+ }
+
+ /** @return input format */
+ protected String getInputFormat() {
+ return _formatField.getText();
+ }
+
+ /** @return true if function can be run */
+ protected boolean isInputOk() {
+ return _waypointCheckbox.isSelected() || _trackCheckbox.isSelected();
+ }
+
+ /**
+ * @return a panel containing the main dialog components
+ */
+ protected JPanel makeDialogComponents()
+ {
+ JPanel outerPanel = new JPanel();
+ outerPanel.setLayout(new BorderLayout());
+ // Main panel with options etc
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+
+ // text fields for options
+ JPanel gridPanel = new JPanel();
+ gridPanel.setLayout(new GridLayout(0, 2, 10, 3));
+ JLabel deviceLabel = new JLabel(I18nManager.getText("dialog.gpsload.device"));
+ deviceLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+ gridPanel.add(deviceLabel);
+ _deviceField = new JTextField(Config.getConfigString(Config.KEY_GPS_DEVICE), 12);
+ KeyAdapter escapeListener = new KeyAdapter() {
+ public void keyReleased(KeyEvent e)
+ {
+ // close dialog if escape pressed
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ _dialog.dispose();
+ }
+ }
+ };
+ _deviceField.addKeyListener(escapeListener);
+ gridPanel.add(_deviceField);
+ JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
+ formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
+ gridPanel.add(formatLabel);
+ _formatField = new JTextField(Config.getConfigString(Config.KEY_GPS_FORMAT), 12);
+ _formatField.addKeyListener(escapeListener);
+ gridPanel.add(_formatField);
+ gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
+ mainPanel.add(gridPanel);
+
+ // checkboxes
+ ChangeListener checkboxListener = new ChangeListener() {
+ public void stateChanged(ChangeEvent e)
+ {
+ enableOkButton();
+ }
+ };
+ _waypointCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.getwaypoints"), true);
+ _waypointCheckbox.addChangeListener(checkboxListener);
+ _waypointCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
+ mainPanel.add(_waypointCheckbox);
+ _trackCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.gettracks"), true);
+ _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);
+ outerPanel.add(mainPanel, BorderLayout.NORTH);
+
+ // Lower panel with ok and cancel buttons
+ 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)
+ {
+ // start thread to call gpsbabel
+ _cancelled = false;
+ new Thread(BabelLoadFromGps.this).start();
+ }
+ };
+ _okButton.addActionListener(okListener);
+ _deviceField.addActionListener(okListener);
+ _formatField.addActionListener(okListener);
+ buttonPanel.add(_okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _cancelled = true;
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ outerPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return outerPanel;
+ }
+
+ /**
+ * Save GPS settings in config
+ */
+ protected void saveConfigValues()
+ {
+ final String device = _deviceField.getText().trim();
+ final String format = _formatField.getText().trim();
+ Config.setConfigString(Config.KEY_GPS_DEVICE, device);
+ Config.setConfigString(Config.KEY_GPS_FORMAT, format);
+ }
+}
package tim.prune.load;
-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.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;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
-import javax.swing.JLabel;
import javax.swing.JOptionPane;
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.save.GpxExporter;
/**
- * Class to manage the loading of GPS data using GpsBabel
+ * Superclass to manage the loading of data using GpsBabel
+ * Subclasses handle either from GPS or from file
*/
-public class GpsLoader extends GenericFunction implements Runnable
+public abstract class BabelLoader extends GenericFunction implements Runnable
{
private boolean _gpsBabelChecked = false;
- 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;
+ protected JDialog _dialog = null;
+ // Checkboxes for which kinds of points to load
+ protected JCheckBox _waypointCheckbox = null, _trackCheckbox = null;
+ // Checkbox to save to file or not
+ protected JCheckBox _saveCheckbox = null;
+ protected JButton _okButton = null;
+ protected JProgressBar _progressBar = null;
+ protected File _saveFile = null;
+ protected boolean _cancelled = false;
/**
* Constructor
* @param inApp Application object to inform of data load
*/
- public GpsLoader(App inApp)
+ public BabelLoader(App inApp)
{
super(inApp);
}
- /** Get the name key */
- public String getNameKey() {
- return "function.loadfromgps";
- }
-
/**
* Open the GUI to select options and start the load
*/
// Initialise progress bars, buttons
enableOkButton();
setupProgressBar(true);
+ initDialog(); // do any subclass-specific init here
_dialog.setVisible(true);
}
}
/**
* @return a panel containing the main dialog components
*/
- private JPanel makeDialogComponents()
- {
- JPanel outerPanel = new JPanel();
- outerPanel.setLayout(new BorderLayout());
- // Main panel with options etc
- JPanel mainPanel = new JPanel();
- mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
-
- // text fields for options
- JPanel gridPanel = new JPanel();
- gridPanel.setLayout(new GridLayout(0, 2, 10, 3));
- JLabel deviceLabel = new JLabel(I18nManager.getText("dialog.gpsload.device"));
- deviceLabel.setHorizontalAlignment(SwingConstants.RIGHT);
- gridPanel.add(deviceLabel);
- _deviceField = new JTextField(Config.getConfigString(Config.KEY_GPS_DEVICE), 12);
- _deviceField.addKeyListener(new KeyAdapter() {
- public void keyReleased(KeyEvent e)
- {
- // close dialog if escape pressed
- if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
- _dialog.dispose();
- }
- }
- });
- gridPanel.add(_deviceField);
- JLabel formatLabel = new JLabel(I18nManager.getText("dialog.gpsload.format"));
- formatLabel.setHorizontalAlignment(SwingConstants.RIGHT);
- gridPanel.add(formatLabel);
- _formatField = new JTextField(Config.getConfigString(Config.KEY_GPS_FORMAT), 12);
- gridPanel.add(_formatField);
- gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
- gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
- mainPanel.add(gridPanel);
-
- // checkboxes
- ChangeListener checkboxListener = new ChangeListener() {
- public void stateChanged(ChangeEvent e)
- {
- enableOkButton();
- }
- };
- _waypointCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.getwaypoints"), true);
- _waypointCheckbox.addChangeListener(checkboxListener);
- _waypointCheckbox.setAlignmentX(Component.CENTER_ALIGNMENT);
- mainPanel.add(_waypointCheckbox);
- _trackCheckbox = new JCheckBox(I18nManager.getText("dialog.gpsload.gettracks"), true);
- _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);
+ protected abstract JPanel makeDialogComponents();
- // progress bar (initially invisible)
- _progressBar = new JProgressBar(0, 10);
- mainPanel.add(_progressBar);
- outerPanel.add(mainPanel, BorderLayout.NORTH);
-
- // Lower panel with ok and cancel buttons
- JPanel buttonPanel = new JPanel();
- buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
- _okButton = new JButton(I18nManager.getText("button.ok"));
- _okButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- // start thread to call gpsbabel
- _cancelled = false;
- new Thread(GpsLoader.this).start();
- }
- });
- buttonPanel.add(_okButton);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _cancelled = true;
- _dialog.dispose();
- }
- });
- buttonPanel.add(cancelButton);
- outerPanel.add(buttonPanel, BorderLayout.SOUTH);
- return outerPanel;
- }
+ /** Do any subclass-specific dialog initialisation necessary */
+ protected void initDialog() {}
/**
* @param inStart true if the dialog is restarting
/**
* Enable or disable the ok button
*/
- private void enableOkButton()
+ protected void enableOkButton()
{
- _okButton.setEnabled(_waypointCheckbox.isSelected() || _trackCheckbox.isSelected());
+ _okButton.setEnabled(isInputOk());
}
+ /**
+ * @return true if input fields of dialog are valid
+ */
+ protected abstract boolean isInputOk();
/**
* Run method for performing tasks in separate thread
{
_okButton.setEnabled(false);
setupProgressBar(false);
- if (_waypointCheckbox.isSelected() || _trackCheckbox.isSelected())
+ if (isInputOk())
{
_progressBar.setIndeterminate(true);
_saveFile = null;
private void callGpsBabel() throws Exception
{
// Set up command to call gpsbabel
- final String device = _deviceField.getText().trim();
- final String format = _formatField.getText().trim();
- String[] commands = getCommandArray(device, format);
+ String[] commands = getCommandArray();
// Save GPS settings in config
- Config.setConfigString(Config.KEY_GPS_DEVICE, device);
- Config.setConfigString(Config.KEY_GPS_FORMAT, format);
+ saveConfigValues();
String errorMessage = "", errorMessage2 = "";
XmlHandler handler = null;
// Send data back to app
_app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(), Altitude.Format.METRES,
- new SourceInfo(_deviceField.getText(), SourceInfo.FILE_TYPE.GPSBABEL),
+ getSourceInfo(),
handler.getTrackNameList());
}
}
/**
* 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)
+ private String[] getCommandArray()
{
String[] commands = null;
final String command = Config.getConfigString(Config.KEY_GPSBABEL_PATH);
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", "-"};
+ commands = new String[] {command, "-w", "-t", "-i", getInputFormat(),
+ "-f", getFilePath(), "-o", "gpx", "-F", "-"};
}
else
{
// Only waypoints OR track points selected
- commands = new String[] {command, "-w", "-i", inFormat,
- "-f", inDevice, "-o", "gpx", "-F", "-"};
+ commands = new String[] {command, "-w", "-i", getInputFormat(),
+ "-f", getFilePath(), "-o", "gpx", "-F", "-"};
if (loadTrack) {
commands[1] = "-t";
}
}
return commands;
}
+
+ /**
+ * @return SourceInfo object corresponding to the load
+ */
+ protected abstract SourceInfo getSourceInfo();
+
+ /**
+ * @return complete file path or device path for gpsbabel call
+ */
+ protected abstract String getFilePath();
+
+ /**
+ * @return file name or device name
+ */
+ protected abstract String getInputFormat();
+
+ /**
+ * Save any config values necessary
+ */
+ protected abstract void saveConfigValues();
}
--- /dev/null
+package tim.prune.load;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class to scoop bytes from an input stream into an array.
+ * The size of the array doesn't have to be known in advance.
+ * This is used for getting images and sound files out of zip
+ * files or from remote URLs.
+ */
+public class ByteScooper
+{
+ /** Bucket size in bytes */
+ private static final int BUCKET_SIZE = 5000;
+ /** Amount by which barrel size is increased on demand */
+ private static final int BARREL_SIZE_INCREMENT = 100000;
+
+ /**
+ * Scoop bytes from the given input stream and return the result
+ * @param inIs input stream to scoop bytes from
+ * @return byte array
+ */
+ public static byte[] scoop(InputStream inIs) throws IOException
+ {
+ byte[] _barrel = new byte[BARREL_SIZE_INCREMENT];
+ byte[] _bucket = new byte[BUCKET_SIZE];
+ int numBytesInBarrel = 0;
+ // read from stream into the bucket
+ int numBytesRead = inIs.read(_bucket);
+ while (numBytesRead >= 0)
+ {
+ // expand barrel if necessary
+ if ((numBytesInBarrel + numBytesRead) > _barrel.length)
+ {
+ byte[] newBarrel = new byte[_barrel.length + BARREL_SIZE_INCREMENT];
+ System.arraycopy(_barrel, 0, newBarrel, 0, numBytesInBarrel);
+ _barrel = newBarrel;
+ }
+ // copy from bucket into barrel
+ System.arraycopy(_bucket, 0, _barrel, numBytesInBarrel, numBytesRead);
+ numBytesInBarrel += numBytesRead;
+ // read next lot from stream into the bucket
+ numBytesRead = inIs.read(_bucket);
+ }
+ // Now we know how many bytes there are, so crop to size
+ if (numBytesInBarrel == 0) return null;
+ byte[] result = new byte[numBytesInBarrel];
+ System.arraycopy(_barrel, 0, result, 0, numBytesInBarrel);
+ return result;
+ }
+}
package tim.prune.load;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
import java.io.File;
import java.util.TreeSet;
-import javax.swing.BorderFactory;
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.JPanel;
-import javax.swing.JProgressBar;
import tim.prune.App;
import tim.prune.I18nManager;
import tim.prune.data.Longitude;
import tim.prune.data.Photo;
import tim.prune.data.Timestamp;
+import tim.prune.function.Cancellable;
import tim.prune.jpeg.ExifGateway;
import tim.prune.jpeg.JpegData;
/**
* Class to manage the loading of Jpegs and dealing with the GPS data from them
*/
-public class JpegLoader implements Runnable
+public class JpegLoader implements Runnable, Cancellable
{
private App _app = null;
private JFrame _parentFrame = null;
private JCheckBox _subdirCheckbox = null;
private JCheckBox _noExifCheckbox = null;
private JCheckBox _outsideAreaCheckbox = null;
- private JDialog _progressDialog = null;
- private JProgressBar _progressBar = null;
+ private MediaLoadProgressDialog _progressDialog = null;
private int[] _fileCounts = null;
private boolean _cancelled = false;
private LatLonRectangle _trackRectangle = null;
if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
{
// Bring up dialog before starting
- if (_progressDialog == null) {
- createProgressDialog();
- }
- // reset dialog and show it
- _progressBar.setValue(0);
- _progressBar.setString("");
- _progressDialog.setVisible(true);
+ _progressDialog = new MediaLoadProgressDialog(_parentFrame, this);
+ _progressDialog.show();
// start thread for processing
new Thread(this).start();
}
}
-
- /**
- * Create the dialog to show the progress
- */
- private void createProgressDialog()
- {
- _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
- _progressDialog.setLocationRelativeTo(_parentFrame);
- _progressBar = new JProgressBar(0, 100);
- _progressBar.setValue(0);
- _progressBar.setStringPainted(true);
- _progressBar.setString("");
- JPanel panel = new JPanel();
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
- panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress")));
- panel.add(_progressBar);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _cancelled = true;
- }
- });
- panel.add(cancelButton);
- _progressDialog.getContentPane().add(panel);
- _progressDialog.pack();
+ /** Cancel */
+ public void cancel() {
+ _cancelled = true;
}
// Loop recursively over selected files/directories to count files
int numFiles = countFileList(files, true, _subdirCheckbox.isSelected());
// Set up the progress bar for this number of files
- _progressBar.setMaximum(numFiles);
- _progressBar.setValue(0);
+ _progressDialog.showProgress(0, numFiles);
_cancelled = false;
// 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
+ _progressDialog.close();
if (_cancelled) {return;}
if (_fileCounts[0] == 0)
*/
private void processFileList(File[] inFiles, boolean inFirstDir, boolean inDescend)
{
- if (inFiles != null)
+ if (inFiles == null) return;
+ // Loop over elements in array
+ for (int i=0; i<inFiles.length && !_cancelled; i++)
{
- // Loop over elements in array
- for (int i=0; i<inFiles.length && !_cancelled; i++)
+ File file = inFiles[i];
+ if (file.exists() && file.canRead())
{
- File file = inFiles[i];
- if (file.exists() && file.canRead())
+ // Check whether it's a file or a directory
+ if (file.isFile())
{
- // Check whether it's a file or a directory
- if (file.isFile())
- {
- processFile(file);
- }
- else if (file.isDirectory() && (inFirstDir || inDescend))
- {
- // Always process first directory,
- // only process subdirectories if checkbox selected
- File[] files = file.listFiles();
- processFileList(files, false, inDescend);
- }
+ processFile(file);
+ }
+ else if (file.isDirectory() && (inFirstDir || inDescend))
+ {
+ // Always process first directory,
+ // only process subdirectories if checkbox selected
+ File[] files = file.listFiles();
+ processFileList(files, false, inDescend);
}
- // if file doesn't exist or isn't readable - ignore
}
+ // if file doesn't exist or isn't readable - ignore
}
}
{
// Update progress bar
_fileCounts[0]++; // file found
- _progressBar.setValue(_fileCounts[0]);
- _progressBar.setString("" + _fileCounts[0] + " / " + _progressBar.getMaximum());
- _progressBar.repaint();
+ _progressDialog.showProgress(_fileCounts[0], -1);
// Check whether filename corresponds with accepted filenames
if (!_fileFilter.acceptFilename(inFile.getName())) {return;}
photo.setExifThumbnail(jpegData.getThumbnailImage());
// Also extract orientation tag for setting rotation state of photo
photo.setRotation(jpegData.getRequiredRotation());
+ // Set bearing, if any
+ photo.setBearing(jpegData.getBearing());
}
// Use file timestamp if exif timestamp isn't available
if (timestamp == null) {
package tim.prune.load;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
-import tim.prune.data.AudioFile;
-import tim.prune.data.MediaFile;
-import tim.prune.data.MediaList;
+import tim.prune.data.AudioClip;
+import tim.prune.data.MediaObject;
import tim.prune.data.Photo;
-import tim.prune.data.Track;
/**
* Class to provide helper functions for loading media
/** File filters */
private static GenericFileFilter _jpegFilter = null, _audioFilter = null;
+
+ /**
+ * Construct a MediaObject for the given path
+ * @param inZipFile path to archive file (if any)
+ * @param inPath path to media file
+ * @return either Photo or AudioClip object as appropriate, or null
+ */
+ public static MediaObject createMediaObject(File inZipFile, String inPath)
+ {
+ if (inPath == null || inPath.length() < 5) return null;
+ InputStream is = null;
+ ZipFile zf = null;
+ byte[] data = null;
+ String url = null;
+ try
+ {
+ // Check if path is a URL, in which case get an input stream from it
+ if (inPath.substring(0, 5).toLowerCase().equals("http:"))
+ {
+ url = inPath;
+ is = new URL(inPath).openStream();
+ data = ByteScooper.scoop(is);
+ }
+ }
+ catch (IOException ioe) {
+ System.err.println("Got ioe from url: " + ioe.getMessage());
+ } // is stays null
+
+ // Now see if file is in the zip file
+ if (is == null && inZipFile != null && inZipFile.exists() && inZipFile.canRead())
+ {
+ try
+ {
+ zf = new ZipFile(inZipFile);
+ ZipEntry entry = zf.getEntry(inPath);
+ if (entry != null && entry.getSize() > 0)
+ {
+ data = ByteScooper.scoop(zf.getInputStream(entry));
+ // System.out.println("Size of data " + (data.length == entry.getSize()?"matches":"DOESN'T match"));
+ }
+ }
+ catch (IOException ioe) {
+ System.err.println("Got ioe from zip file: " + ioe.getMessage());
+ }
+ }
+ // Clean up input streams
+ if (is != null) try {
+ is.close();
+ } catch (IOException ioe) {}
+ if (zf != null) try {
+ zf.close();
+ } catch (IOException ioe) {}
+
+ if (data != null)
+ {
+ // Create Photo or AudioClip using this entry
+ String filename = new File(inPath).getName();
+ initFilters();
+ if (_jpegFilter.acceptFilename(inPath)) {
+ return new Photo(data, filename, url);
+ }
+ else if (_audioFilter.acceptFilename(inPath)) {
+ return new AudioClip(data, filename, url);
+ }
+ return null;
+ }
+ else
+ // If we haven't got a result by now, try to just load plain file
+ return createMediaObject(inPath);
+ }
+
/**
- * Construct a MediaFile object for the given path
+ * Construct a MediaObject for the given path
* @param inPath path to file
- * @return either Photo or AudioFile object as appropriate, or null
+ * @return either Photo or AudioClip object as appropriate, or null
*/
- public static MediaFile createMediaFile(String inPath)
+ private static MediaObject createMediaObject(String inPath)
{
if (inPath == null) {return null;}
File file = new File(inPath);
if (!file.exists() || !file.canRead() || !file.isFile()) {return null;}
- // Initialise filters if necessary
- if (_jpegFilter == null) {
- _jpegFilter = new JpegFileFilter();
- _audioFilter = new AudioFileFilter();
- }
+ initFilters();
// Check if filename looks like a jpeg
if (_jpegFilter.acceptFilename(file.getName())) {
return JpegLoader.createPhoto(file);
}
- // Check if filename looks like an audio file
+ // Check if filename looks like an audio clip
if (_audioFilter.acceptFilename(file.getName())) {
- return new AudioFile(file);
+ return new AudioClip(file);
}
// Neither photo nor audio
return null;
}
/**
- * Add all the media from the given track into the specified list
- * @param inTrack track from which media to take
- * @param inMediaList list to which media should be added
- * @param inMediaClass class of media, either Photo or AudioFile
+ * Initialise filters if necessary
*/
- public static void addMediaFromTrack(Track inTrack, MediaList inMediaList,
- Class<?> inMediaClass)
+ private static void initFilters()
{
- final int numPoints = inTrack.getNumPoints();
- for (int i=0; i<numPoints; i++)
- {
- MediaFile media = null;
- if (inMediaClass == Photo.class) {
- media = inTrack.getPoint(i).getPhoto();
- }
- else if (inMediaClass == AudioFile.class) {
- media = inTrack.getPoint(i).getAudio();
- }
- if (media != null) {
- inMediaList.addMedia(media);
- }
+ if (_jpegFilter == null) {
+ _jpegFilter = new JpegFileFilter();
+ _audioFilter = new AudioFileFilter();
}
}
}
--- /dev/null
+package tim.prune.load;
+
+import java.io.File;
+
+/**
+ * Container class to hold media link information from a loaded file
+ * including whether the media files are actual files or inside a kmz / zip
+ */
+public class MediaLinkInfo
+{
+ /** zip file (or kmz file) containing media files */
+ private File _zipFile = null;
+ /** array of URLs */
+ private String[] _linkArray = null;
+
+
+ /**
+ * Constructor for regular files
+ * @param inLinkArray array of links to files
+ */
+ public MediaLinkInfo(String[] inLinkArray)
+ {
+ _zipFile = null;
+ _linkArray = inLinkArray;
+ }
+
+ /**
+ * Constructor for media files inside a zip / kmz file
+ * @param inZipFile archive file
+ * @param inLinkArray array of file links
+ */
+ public MediaLinkInfo(File inZipFile, String[] inLinkArray)
+ {
+ _zipFile = inZipFile;
+ _linkArray = inLinkArray;
+ }
+
+ /**
+ * @return true if these media files are inside a zip / kmz
+ */
+ public boolean insideArchive() {
+ return _zipFile != null && _zipFile.exists();
+ }
+
+ /**
+ * @return zip file
+ */
+ public File getZipFile() {
+ return _zipFile;
+ }
+
+ /**
+ * @return link array
+ */
+ public String[] getLinkArray() {
+ return _linkArray;
+ }
+}
--- /dev/null
+package tim.prune.load;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+
+import tim.prune.I18nManager;
+import tim.prune.function.Cancellable;
+
+/**
+ * Class to show a progress dialog for loading media.
+ * Used for regular photo / audio loads plus the async
+ * loading function.
+ */
+public class MediaLoadProgressDialog
+{
+ private JDialog _progressDialog = null;
+ private JProgressBar _progressBar = null;
+ private JFrame _parentFrame = null;
+ private Cancellable _function = null;
+
+ /**
+ * Constructor
+ * @param inParentFrame parent frame for creating dialog
+ * @param inFunction function which can be cancelled
+ */
+ public MediaLoadProgressDialog(JFrame inParentFrame, Cancellable inFunction)
+ {
+ _parentFrame = inParentFrame;
+ _function = inFunction;
+ }
+
+ /**
+ * Create the dialog to show the progress
+ */
+ private void createProgressDialog()
+ {
+ _progressDialog = new JDialog(_parentFrame, I18nManager.getText("dialog.jpegload.progress.title"));
+ _progressDialog.setLocationRelativeTo(_parentFrame);
+ _progressBar = new JProgressBar(0, 100);
+ _progressBar.setValue(0);
+ _progressBar.setStringPainted(true);
+ _progressBar.setString("");
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+ panel.add(new JLabel(I18nManager.getText("dialog.jpegload.progress")));
+ panel.add(_progressBar);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _function.cancel();
+ }
+ });
+ panel.add(cancelButton);
+ _progressDialog.getContentPane().add(panel);
+ _progressDialog.pack();
+ _progressDialog.setVisible(true);
+ }
+
+ /**
+ * Show the dialog in indeterminate mode, before limits are calculated
+ */
+ public void show()
+ {
+ if (_progressDialog == null)
+ {
+ createProgressDialog();
+ _progressBar.setIndeterminate(true);
+ }
+ }
+
+ /**
+ * Update the progress bar
+ * @param inCurrent current value
+ * @param inMax maximum value
+ */
+ public void showProgress(int inCurrent, int inMax)
+ {
+ if (_progressDialog == null)
+ createProgressDialog();
+ if (_progressBar.isIndeterminate())
+ _progressBar.setIndeterminate(false);
+ if (inMax > 0)
+ _progressBar.setMaximum(inMax);
+ _progressBar.setValue(inCurrent);
+ _progressBar.setString("" + inCurrent + " / " + _progressBar.getMaximum());
+ // TODO: Need to repaint?
+ }
+
+ /**
+ * Close the dialog
+ */
+ public void close()
+ {
+ if (_progressDialog != null)
+ _progressDialog.dispose();
+ }
+}
import java.io.File;
import java.util.Comparator;
-import tim.prune.data.MediaFile;
-
+import tim.prune.data.MediaObject;
/**
- * Class to sort photos by name
+ * Class to sort photos, audios by name
*/
-public class MediaSorter implements Comparator<MediaFile>
+public class MediaSorter implements Comparator<MediaObject>
{
-
/**
- * Compare two media files
+ * Compare two media objects
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
- public int compare(MediaFile o1, MediaFile o2)
+ public int compare(MediaObject o1, MediaObject o2)
{
+ int nameComp = o1.getName().compareTo(o2.getName());
+ if (nameComp != 0) {
+ // names different
+ return nameComp;
+ }
File file1 = o1.getFile();
File file2 = o2.getFile();
- int nameComp = file1.getName().compareTo(file2.getName());
- if (nameComp == 0)
+ if (file1 != null && file2 != null)
{
// names same, maybe in different directories
- return file1.getAbsolutePath().compareTo(file2.getAbsolutePath());
+ nameComp = file1.getAbsolutePath().compareTo(file2.getAbsolutePath());
+ }
+ else if (o1.getByteData() != null && o2.getByteData() != null) {
+ // compare data lengths instead
+ nameComp = o1.getByteData().length - o2.getByteData().length;
+ }
+ else {
+ // one's a file, one's from data
+ nameComp = 1;
}
- // names different
return nameComp;
}
-
}
private GpxTag _name = new GpxTag(), _trackName = new GpxTag();
private String _latitude = null, _longitude = null;
private GpxTag _elevation = new GpxTag(), _time = new GpxTag();
- private GpxTag _type = new GpxTag(), _link = new GpxTag();
+ private GpxTag _type = new GpxTag(), _description = new GpxTag();
+ private GpxTag _link = new GpxTag();
private GpxTag _currentTag = null;
private ArrayList<String[]> _pointList = new ArrayList<String[]>();
private ArrayList<String> _linkList = new ArrayList<String>();
_time.setValue(null);
_type.setValue(null);
_link.setValue(null);
+ _description.setValue(null);
}
else if (tag.equals("ele")) {
_currentTag = _elevation;
else if (tag.equals("type")) {
_currentTag = _type;
}
+ else if (tag.equals("description")) {
+ _currentTag = _description;
+ }
else if (tag.equals("link")) {
_link.setValue(attributes.getValue("href"));
}
private void processPoint()
{
// Put the values into a String array matching the order in getFieldArray()
- String[] values = new String[7];
+ String[] values = new String[8];
values[0] = _latitude;
values[1] = _longitude;
values[2] = _elevation.getValue();
if (_insideWaypoint) {values[3] = _name.getValue();}
values[4] = _time.getValue();
- if (_startSegment && !_insideWaypoint) {
+ if (_startSegment && !_insideWaypoint)
+ {
values[5] = "1";
_startSegment = false;
}
values[6] = _type.getValue();
+ values[7] = _description.getValue();
_pointList.add(values);
_trackNameList.addPoint(_trackNum, _trackName.getValue(), _isTrackPoint);
_linkList.add(_link.getValue());
public Field[] getFieldArray()
{
final Field[] fields = {Field.LATITUDE, Field.LONGITUDE, Field.ALTITUDE,
- Field.WAYPT_NAME, Field.TIMESTAMP, Field.NEW_SEGMENT, Field.WAYPT_TYPE};
+ Field.WAYPT_NAME, Field.TIMESTAMP, Field.NEW_SEGMENT,
+ Field.WAYPT_TYPE, Field.DESCRIPTION};
return fields;
}
import tim.prune.I18nManager;
import tim.prune.data.Altitude;
import tim.prune.data.SourceInfo;
+import tim.prune.load.MediaLinkInfo;
/**
* Class to handle the loading of gzipped xml files
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.noread");
}
- else {
+ 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, handler.getTrackNameList(), handler.getLinkArray());
+ Altitude.Format.METRES, sourceInfo, handler.getTrackNameList(),
+ new MediaLinkInfo(inFile, handler.getLinkArray()));
}
}
- catch (Exception e) {
+ 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());
*/
public class KmlHandler extends XmlHandler
{
- private boolean _insidePlacemark = false;
- private boolean _insideName = false;
private boolean _insideCoordinates = false;
- private String _name = null;
+ private String _value = null;
+ private String _name = null, _desc = null;
+ private String _imgLink = null;
private StringBuffer _coordinates = null;
private ArrayList<String[]> _pointList = new ArrayList<String[]>();
+ private ArrayList<String> _linkList = new ArrayList<String>();
+ // variables for gx extensions
+ private ArrayList<String> _whenList = new ArrayList<String>();
+ private ArrayList<String> _whereList = new ArrayList<String>();
/**
* @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException
+ Attributes attributes) throws SAXException
{
String tagName = localName;
if (tagName == null || tagName.equals("")) {tagName = qName;}
- if (tagName.equalsIgnoreCase("Placemark")) _insidePlacemark = true;
- else if (tagName.equalsIgnoreCase("coordinates")) {_insideCoordinates = true; _coordinates = null;}
- else if (tagName.equalsIgnoreCase("name")) {_insideName = true; _name = null;}
+ if (tagName.equalsIgnoreCase("coordinates")) {_insideCoordinates = true; _coordinates = null;}
+ _value = null;
super.startElement(uri, localName, qName, attributes);
}
if (tagName.equalsIgnoreCase("Placemark"))
{
processPlacemark();
- _insidePlacemark = false;
+ _name = _desc = _imgLink = null;
}
else if (tagName.equalsIgnoreCase("coordinates")) _insideCoordinates = false;
- else if (tagName.equalsIgnoreCase("name")) _insideName = false;
+ else if (tagName.equalsIgnoreCase("name")) _name = _value;
+ else if (tagName.equalsIgnoreCase("description")) {
+ _desc = _value;
+ _imgLink = getImgLink(_desc);
+ }
+ else if (tagName.equalsIgnoreCase("when")) {
+ _whenList.add(_value);
+ }
+ else if (tagName.equalsIgnoreCase("gx:coord")) {
+ _whereList.add(_value);
+ }
+ else if (tagName.equalsIgnoreCase("gx:Track")) {
+ processGxTrack();
+ }
super.endElement(uri, localName, qName);
}
public void characters(char[] ch, int start, int length)
throws SAXException
{
- if (_insidePlacemark && (_insideName || _insideCoordinates))
+ String val = new String(ch, start, length);
+ if (_insideCoordinates)
{
- String value = new String(ch, start, length);
- if (_insideName) {_name = value;}
- else if (_insideCoordinates)
- {
- if (_coordinates == null)
- {
- _coordinates = new StringBuffer();
- }
- _coordinates.append(value);
+ if (_coordinates == null) {
+ _coordinates = new StringBuffer();
}
+ _coordinates.append(val);
+ }
+ else
+ {
+ // Store string in _value
+ if (_value == null) _value = val;
+ else _value = _value + val;
}
super.characters(ch, start, length);
}
if (numPoints == 1)
{
// Add single waypoint to list
- _pointList.add(makeStringArray(allCoords, _name));
+ _pointList.add(makeStringArray(allCoords, _name, _desc));
+ _linkList.add(_imgLink);
}
else if (numPoints > 1)
{
{
if (coordArray[p] != null && coordArray[p].trim().length()>3)
{
- String[] pointArray = makeStringArray(coordArray[p], null);
- if (firstPoint) {pointArray[4] = "1";} // start of segment flag
+ String[] pointArray = makeStringArray(coordArray[p], null, null);
+ if (firstPoint) {pointArray[5] = "1";} // start of segment flag
firstPoint = false;
_pointList.add(pointArray);
}
+ _linkList.add(null);
}
}
}
+ /**
+ * Process a Gx track including timestamps
+ */
+ private void processGxTrack()
+ {
+ if (_whenList.size() > 0 && _whenList.size() == _whereList.size())
+ {
+ // Add each of the unnamed track points to list
+ boolean firstPoint = true;
+ final int numPoints = _whenList.size();
+ for (int p=0; p < numPoints; p++)
+ {
+ String when = _whenList.get(p);
+ String where = _whereList.get(p);
+ if (where != null)
+ {
+ String[] coords = where.split(" ");
+ if (coords.length == 3)
+ {
+ String[] pointArray = new String[7];
+ pointArray[0] = coords[0];
+ pointArray[1] = coords[1];
+ pointArray[2] = coords[2];
+ // leave name and description empty
+ if (firstPoint) {pointArray[5] = "1";} // start of segment flag
+ firstPoint = false;
+ pointArray[6] = when; // timestamp
+ _pointList.add(pointArray);
+ }
+ }
+ _linkList.add(null);
+ }
+ }
+ _whenList.clear();
+ _whereList.clear();
+ }
+
+ /**
+ * Extract an image link from the point description
+ * @param inDesc description tag contents
+ * @return image link if found or null
+ */
+ private static String getImgLink(String inDesc)
+ {
+ if (inDesc == null || inDesc.equals("")) {return null;}
+ // Pull out <img tag from description (if any)
+ int spos = inDesc.indexOf("<img");
+ int epos = inDesc.indexOf('>', spos + 10);
+ if (spos < 0 || epos < 0) return null;
+ String imgtag = inDesc.substring(spos + 4, epos);
+ // Find the src attribute from img tag
+ int quotepos = imgtag.toLowerCase().indexOf("src=");
+ if (quotepos < 0) return null;
+ // source may be quoted with single or double quotes
+ char quotechar = imgtag.charAt(quotepos + 4);
+ int equotepos = imgtag.indexOf(quotechar, quotepos + 7);
+ if (equotepos < 0) return null;
+ return imgtag.substring(quotepos + 5, equotepos);
+ }
/**
* Construct the String array for the given coordinates and name
* @param inCoordinates coordinate string in Kml format
* @param inName name of waypoint, or null if track point
+ * @param inDesc description of waypoint, if any
* @return String array for point
*/
- private static String[] makeStringArray(String inCoordinates, String inName)
+ private static String[] makeStringArray(String inCoordinates,
+ String inName, String inDesc)
{
- String[] result = new String[5];
+ String[] result = new String[7];
String[] values = inCoordinates.split(",");
- if (values.length == 3) {System.arraycopy(values, 0, result, 0, 3);}
+ final int numValues = values.length;
+ if (numValues == 3 || numValues == 2) {
+ System.arraycopy(values, 0, result, 0, numValues);
+ }
result[3] = inName;
+ result[4] = inDesc;
return result;
}
*/
public Field[] getFieldArray()
{
- final Field[] fields = {Field.LONGITUDE, Field.LATITUDE, Field.ALTITUDE, Field.WAYPT_NAME, Field.NEW_SEGMENT};
+ final Field[] fields = {Field.LONGITUDE, Field.LATITUDE, Field.ALTITUDE,
+ Field.WAYPT_NAME, Field.DESCRIPTION, Field.NEW_SEGMENT, Field.TIMESTAMP};
return fields;
}
int numPoints = _pointList.size();
// construct data array
String[][] result = new String[numPoints][];
- for (int i=0; i<numPoints; i++)
- {
+ for (int i=0; i<numPoints; i++) {
result[i] = _pointList.get(i);
}
return result;
}
+ /**
+ * @return array of links, or null if none
+ */
+ public String[] getLinkArray()
+ {
+ int numPoints = _linkList.size();
+ boolean hasLink = false;
+ String[] result = new String[numPoints];
+ for (int i=0; i<numPoints; i++)
+ {
+ result[i] = _linkList.get(i);
+ if (result[i] != null) {hasLink = true;}
+ }
+ if (!hasLink) {result = null;}
+ return result;
+ }
}
import tim.prune.I18nManager;
import tim.prune.data.Altitude;
import tim.prune.data.SourceInfo;
+import tim.prune.load.MediaLinkInfo;
/**
* Class for handling loading of Xml files, and passing the
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, sourceInfo, _handler.getTrackNameList(), _handler.getLinkArray());
+ Altitude.Format.METRES, sourceInfo, _handler.getTrackNameList(),
+ new MediaLinkInfo(_handler.getLinkArray()));
}
}
catch (Exception e)
import tim.prune.App;
import tim.prune.data.Altitude;
import tim.prune.data.SourceInfo;
+import tim.prune.load.MediaLinkInfo;
/**
* Class to handle the loading of zipped xml files
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.othererror");
}
- else {
+ 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, handler.getTrackNameList(), handler.getLinkArray());
+ Altitude.Format.METRES, sourceInfo, handler.getTrackNameList(),
+ new MediaLinkInfo(inFile, handler.getLinkArray()));
xmlFound = true;
}
}
if (handler == null) {
_app.showErrorMessage("error.load.dialogtitle", "error.load.othererror");
}
- else {
+ else
+ {
// Send back to app
_app.informDataLoaded(handler.getFieldArray(), handler.getDataArray(),
Altitude.Format.METRES, new SourceInfo("gpsies", SourceInfo.FILE_TYPE.GPSIES),
-Prune version 12
-================
+GpsPrune version 13
+===================
-Prune is an application for viewing, editing and managing coordinate data from GPS systems,
+GpsPrune 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/
+Full details can be found at http://activityworkshop.net/software/gpsprune/
-Prune is copyright 2006-2010 activityworkshop.net and distributed under the terms of the Gnu GPL version 2.
+GpsPrune is copyright 2006-2011 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.
-Prune comes without warranty and without guarantee - the authors cannot be held responsible for
+GpsPrune comes without warranty and without guarantee - the authors cannot be held responsible for
losses incurred through use of the program, however caused.
Running
=======
-To run Prune from the jar file, simply call it from a command prompt or shell:
- java -jar prune_12.jar
+To run GpsPrune from the jar file, simply call it from a command prompt or shell:
+ java -jar gpsprune_13.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
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_12.jar --lang=DE
+ java -jar gpsprune_13.jar --lang=DE
+
+
+New with version 13
+===================
+The following features were added since version 12:
+ - Name change from Prune to GpsPrune
+ - Handling of description field for waypoints
+ - Opening of images from within kmz files, zip files and from http links
+ - Compression using Douglas-Peucker algorithm
+ - Option to save settings automatically on exit
+ - Dialog to show the local tile cache, report sizes and allow deletion
+ of tiles, either deleting whole tilesets or individual tiles older than
+ a specified number of days
+ - Checkbox on GPX export to specify UTF-8 rather than default system encoding
+ - Importing of files through GPSBabel
+ - List of recently used files in the menu
+ - Display of bearing at which a photo was taken (display only)
New with version 12
===================
please visit the website: http://activityworkshop.net/
You will find there user guides, screenshots and demo videos illustrating the major features.
-As Prune is further developed, subsequent versions of the program will also be made freely
+As GpsPrune is further developed, subsequent versions of the program will also be made freely
available at this website.
-You can also provide feedback on Prune, and find out more about contributing to the development,
+You can also provide feedback on GpsPrune, and find out more about contributing to the development,
especially with regard to language translations.
*/
private boolean savePhoto(Photo inPhoto, boolean inOverwriteFlag, boolean inForceFlag)
{
+ // If photos don't have a file, then can't save them
+ if (inPhoto.getFile() == null) {
+ return false;
+ }
// Check whether photo file still exists
if (!inPhoto.getFile().exists())
{
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.FieldList;
+import tim.prune.data.RecentFile;
import tim.prune.data.Timestamp;
import tim.prune.data.Track;
import tim.prune.load.GenericFileFilter;
}
// Store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, saveFile.getParentFile().getAbsolutePath());
+ // Add to recent file list
+ Config.getRecentFileList().addFile(new RecentFile(inSaveFile, true));
// Save successful
+ UpdateMessageBroker.informSubscribers();
UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+ " " + numSaved + " " + I18nManager.getText("confirm.save.ok2")
+ " " + saveFile.getAbsolutePath());
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
+import javax.swing.JRadioButton;
import javax.swing.JTextField;
+import javax.swing.border.EtchedBorder;
import tim.prune.App;
import tim.prune.GenericFunction;
-import tim.prune.GpsPruner;
+import tim.prune.GpsPrune;
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.data.Altitude;
-import tim.prune.data.AudioFile;
+import tim.prune.data.AudioClip;
import tim.prune.data.Coordinate;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
-import tim.prune.data.MediaFile;
+import tim.prune.data.MediaObject;
import tim.prune.data.Photo;
+import tim.prune.data.RecentFile;
import tim.prune.data.Timestamp;
import tim.prune.data.TrackInfo;
import tim.prune.gui.DialogCloser;
import tim.prune.load.GenericFileFilter;
import tim.prune.save.xml.GpxCacherList;
+import tim.prune.save.xml.XmlUtils;
/**
private PointTypeSelector _pointTypeSelector = null;
private JCheckBox _timestampsCheckbox = null;
private JCheckBox _copySourceCheckbox = null;
+ private JPanel _encodingsPanel = null;
+ private JRadioButton _useSystemRadio = null, _forceUtf8Radio = null;
private File _exportFile = null;
+ private static String _systemEncoding = null;
/** this program name */
- private static final String GPX_CREATOR = "Prune v" + GpsPruner.VERSION_NUMBER + " activityworkshop.net";
+ private static final String GPX_CREATOR = "GpsPrune v" + GpsPrune.VERSION_NUMBER + " activityworkshop.net";
/**
_dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
_dialog.setLocationRelativeTo(_parentFrame);
_dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _systemEncoding = getSystemEncoding();
_dialog.getContentPane().add(makeDialogComponents());
_dialog.pack();
}
_pointTypeSelector.init(_app.getTrackInfo());
+ _encodingsPanel.setVisible(!isSystemUtf8());
+ if (!isSystemUtf8()) {
+ _useSystemRadio.setText(I18nManager.getText("dialog.exportgpx.encoding.system")
+ + " (" + (_systemEncoding == null ? "unknown" : _systemEncoding) + ")");
+ }
_dialog.setVisible(true);
}
dialogPanel.setLayout(new BorderLayout());
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
- // Make a central panel with the text boxes
+ // Make a panel for the name/desc text boxes
JPanel descPanel = new JPanel();
descPanel.setLayout(new GridLayout(2, 2));
descPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.name")));
_copySourceCheckbox.setSelected(true);
checkPanel.add(_copySourceCheckbox);
mainPanel.add(checkPanel);
+ // panel for selecting character encoding
+ _encodingsPanel = new JPanel();
+ if (!isSystemUtf8())
+ {
+ // only add this panel if system isn't utf8 (or can't be identified yet)
+ _encodingsPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4)));
+ _encodingsPanel.setLayout(new BorderLayout());
+ _encodingsPanel.add(new JLabel(I18nManager.getText("dialog.exportgpx.encoding")), BorderLayout.NORTH);
+ JPanel radioPanel = new JPanel();
+ radioPanel.setLayout(new FlowLayout());
+ ButtonGroup radioGroup = new ButtonGroup();
+ _useSystemRadio = new JRadioButton(I18nManager.getText("dialog.exportgpx.encoding.system"));
+ _forceUtf8Radio = new JRadioButton(I18nManager.getText("dialog.exportgpx.encoding.utf8"));
+ radioGroup.add(_useSystemRadio);
+ radioGroup.add(_forceUtf8Radio);
+ radioPanel.add(_useSystemRadio);
+ radioPanel.add(_forceUtf8Radio);
+ _useSystemRadio.setSelected(true);
+ _encodingsPanel.add(radioPanel, BorderLayout.CENTER);
+ mainPanel.add(_encodingsPanel);
+ }
dialogPanel.add(mainPanel, BorderLayout.CENTER);
// close dialog if escape pressed
private void startExport()
{
// OK pressed, so check selections
- if (!_pointTypeSelector.getAnythingSelected()) {
+ if (!_pointTypeSelector.getAnythingSelected())
+ {
JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.save.notypesselected"),
I18nManager.getText("dialog.saveoptions.title"), JOptionPane.WARNING_MESSAGE);
return;
}
}
+
/**
* Select a GPX file to save to
* @param inParentFrame parent frame for file chooser dialog
return saveFile;
}
+
/**
* Run method for controlling separate thread for exporting
*/
OutputStreamWriter writer = null;
try
{
- // normal writing to file
- writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
+ // normal writing to file - firstly specify UTF8 encoding if requested
+ if (_forceUtf8Radio != null && _forceUtf8Radio.isSelected())
+ writer = new OutputStreamWriter(new FileOutputStream(_exportFile), "UTF-8");
+ else
+ writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
final boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
_pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getAudiopointsSelected(),
_pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()};
writer.close();
// Store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
+ // Add to recent file list
+ Config.getRecentFileList().addFile(new RecentFile(_exportFile, true));
// Show confirmation
+ UpdateMessageBroker.informSubscribers();
UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+ " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
+ " " + _exportFile.getAbsolutePath());
inWriter.write(getXmlHeaderString(inWriter));
inWriter.write(getGpxHeaderString(gpxCachers));
// Name field
- String trackName = "PruneTrack";
+ String trackName = "GpsPruneTrack";
if (inName != null && !inName.equals(""))
{
trackName = inName;
}
// Description field
inWriter.write("\t<desc>");
- inWriter.write((inDesc != null && !inDesc.equals(""))?inDesc:"Export from Prune");
+ inWriter.write((inDesc != null && !inDesc.equals(""))?inDesc:"Export from GpsPrune");
inWriter.write("</desc>\n");
int i = 0;
for (i=0; i<numPoints; i++)
{
point = inInfo.getTrack().getPoint(i);
- if (!exportSelection || (i>=selStart && i<=selEnd)) {
+ if (!exportSelection || (i>=selStart && i<=selEnd))
+ {
// Make a wpt element for each waypoint
- if (point.isWaypoint()) {
- if (exportWaypoints)
- {
- String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null);
- if (pointSource != null) {
- inWriter.write(pointSource);
- inWriter.write('\n');
- }
- else {
- exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios);
- }
- numSaved++;
+ if (point.isWaypoint() && exportWaypoints)
+ {
+ String pointSource = (inUseCopy?getPointSource(gpxCachers, point):null);
+ if (pointSource != null) {
+ inWriter.write(pointSource);
+ inWriter.write('\n');
+ }
+ else {
+ exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios);
}
+ numSaved++;
}
}
}
return numSaved;
}
+
/**
* Loop through the track outputting the relevant track points
* @param inWriter writer object for output
source = replaceGpxTags(source, "lon=\"", "\"", inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
source = replaceGpxTags(source, "<ele>", "</ele>", inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
source = replaceGpxTags(source, "<time>", "</time>", inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
- if (inPoint.isWaypoint()) {source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());} // only for waypoints
+ if (inPoint.isWaypoint())
+ {
+ source = replaceGpxTags(source, "<name>", "</name>", inPoint.getWaypointName());
+ source = replaceGpxTags(source, "<description>", "</description>",
+ XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION)));
+ }
// photo / audio links
if (source != null && (inPoint.hasMedia() || source.indexOf("</link>") > 0)) {
source = replaceMediaLinks(source, makeMediaLink(inPoint));
return null;
}
+
/**
* Replace the media tags in the given XML string
* @param inSource source XML for point
return null;
}
+
/**
* Get the header string for the xml document including encoding
* @param inWriter writer object
* @return header string defining encoding
*/
private static String getXmlHeaderString(OutputStreamWriter inWriter)
+ {
+ return "<?xml version=\"1.0\" encoding=\"" + getEncoding(inWriter) + "\"?>\n";
+ }
+
+
+ /**
+ * Get the default system encoding using a writer
+ * @param inWriter writer object
+ * @return string defining encoding
+ */
+ private static String getEncoding(OutputStreamWriter inWriter)
{
String encoding = inWriter.getEncoding();
try {
encoding = Charset.forName(encoding).name();
}
catch (Exception e) {} // ignore failure to find encoding
- return "<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>\n";
+ // Hack to fix bugs with Mac OSX (which reports MacRoman but is actually UTF-8)
+ if (encoding == null || encoding.toLowerCase().startsWith("macroman")) {
+ encoding = "UTF-8";
+ }
+ return encoding;
+ }
+
+
+ /**
+ * Use a temporary file to obtain the name of the default system encoding
+ * @return name of default system encoding, or null if write failed
+ */
+ private static String getSystemEncoding()
+ {
+ File tempFile = null;
+ String encoding = null;
+ try
+ {
+ tempFile = File.createTempFile("prune", null);
+ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile));
+ encoding = getEncoding(writer);
+ writer.close();
+ }
+ catch (IOException e) {} // value stays null
+ // Delete temp file
+ if (tempFile != null && tempFile.exists()) {
+ if (!tempFile.delete()) {
+ System.err.println("Cannot delete temp file: " + tempFile.getAbsolutePath());
+ }
+ }
+ // If writing failed (eg permissions) then just ask system for default
+ if (encoding == null) encoding = Charset.defaultCharset().name();
+ return encoding;
+ }
+
+ /**
+ * Creates temp file if necessary to check system encoding
+ * @return true if system uses UTF-8 by default
+ */
+ private static boolean isSystemUtf8()
+ {
+ if (_systemEncoding == null) _systemEncoding = getSystemEncoding();
+ return (_systemEncoding != null && _systemEncoding.toUpperCase().equals("UTF-8"));
}
/**
return gpxHeader + "\n";
}
+
/**
* Export the specified waypoint into the file
* @param inPoint waypoint to export
inWriter.write("\t\t<name>");
inWriter.write(inPoint.getWaypointName().trim());
inWriter.write("</name>\n");
+ // description, if any
+ String desc = XmlUtils.fixCdata(inPoint.getFieldValue(Field.DESCRIPTION));
+ if (desc != null && !desc.equals(""))
+ {
+ inWriter.write("\t\t<description>");
+ inWriter.write(desc);
+ inWriter.write("</description>\n");
+ }
// Media links, if any
if (inPhoto && inPoint.getPhoto() != null)
{
inWriter.write("</trkpt>\n");
}
+
/**
* Make the xml for the media link(s)
* @param inPoint point to generate text for
private static String makeMediaLink(DataPoint inPoint)
{
Photo photo = inPoint.getPhoto();
- AudioFile audio = inPoint.getAudio();
+ AudioClip audio = inPoint.getAudio();
if (photo == null && audio == null) {
return null;
}
* @param inMedia media item, either photo or audio
* @return link for this media
*/
- private static String makeMediaLink(MediaFile inMedia)
+ private static String makeMediaLink(MediaObject inMedia)
{
- return "<link href=\"" + inMedia.getFile().getAbsolutePath() + "\"><text>" + inMedia.getFile().getName() + "</text></link>";
+ if (inMedia.getFile() != null)
+ // file link
+ return "<link href=\"" + inMedia.getFile().getAbsolutePath() + "\"><text>" + inMedia.getName() + "</text></link>";
+ if (inMedia.getUrl() != null)
+ // url link
+ return "<link href=\"" + inMedia.getUrl() + "\"><text>" + inMedia.getName() + "</text></link>";
+ // No link available, must have been loaded from zip file - no link possible
+ return "";
}
}
import tim.prune.data.Coordinate;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
+import tim.prune.data.RecentFile;
import tim.prune.data.Track;
import tim.prune.data.TrackInfo;
import tim.prune.gui.ColourChooser;
import tim.prune.gui.DialogCloser;
import tim.prune.gui.ImageUtils;
import tim.prune.load.GenericFileFilter;
+import tim.prune.save.xml.XmlUtils;
/**
* Class to export track information
_imageDimensions = null;
// Store directory in config for later
Config.setConfigString(Config.KEY_TRACK_DIR, _exportFile.getParentFile().getAbsolutePath());
+ // Add to recent file list
+ Config.getRecentFileList().addFile(new RecentFile(_exportFile, true));
// show confirmation
+ UpdateMessageBroker.informSubscribers();
UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.save.ok1")
+ " " + numPoints + " " + I18nManager.getText("confirm.save.ok2")
+ " " + _exportFile.getAbsolutePath());
inWriter.write(_descriptionField.getText());
}
else {
- inWriter.write("Export from Prune");
+ inWriter.write("Export from GpsPrune");
}
inWriter.write("</name>\n");
exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes);
numSaved++;
}
- // Make a blob with description for each audio file
+ // Make a blob with description for each audio clip
if (point.getAudio() != null && writeAudios && writeCurrentPoint)
{
if (!writtenAudioHeader)
private void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException
{
String name = inPoint.getWaypointName().trim();
- exportNamedPoint(inPoint, inWriter, name, null, null, inAbsoluteAltitude);
+ exportNamedPoint(inPoint, inWriter, name, inPoint.getFieldValue(Field.DESCRIPTION), null, inAbsoluteAltitude);
}
*/
private void exportAudioPoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException
{
- String name = inPoint.getAudio().getFile().getName();
- String desc = inPoint.getAudio().getFile().getAbsolutePath();
+ String name = inPoint.getAudio().getName();
+ String desc = null;
+ if (inPoint.getAudio().getFile() != null) {
+ desc = inPoint.getAudio().getFile().getAbsolutePath();
+ }
exportNamedPoint(inPoint, inWriter, name, desc, "audio_icon", inAbsoluteAltitude);
}
int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude)
throws IOException
{
- String name = inPoint.getPhoto().getFile().getName();
+ String name = inPoint.getPhoto().getName();
String desc = null;
if (inImageLink)
{
// Create html for the thumbnail images
desc = "<![CDATA[<br/><table border='0'><tr><td><center><img src='images/image"
+ inImageNumber + ".jpg' width='" + imageSize.width + "' height='" + imageSize.height + "'></center></td></tr>"
- + "<tr><td><center>" + inPoint.getPhoto().getFile().getName() + "</center></td></tr></table>]]>";
+ + "<tr><td><center>" + name + "</center></td></tr></table>]]>";
}
// Export point
exportNamedPoint(inPoint, inWriter, name, desc, "camera_icon", inAbsoluteAltitude);
if (inDesc != null)
{
// Write out description
- inWriter.write("<description>");
- inWriter.write(inDesc);
- inWriter.write("</description>");
+ inWriter.write("\t\t<description>");
+ inWriter.write(XmlUtils.fixCdata(inDesc));
+ inWriter.write("</description>\n");
}
if (inStyle != null)
{
ZipEntry entry = new ZipEntry("images/image" + photoNum + ".jpg");
inZipStream.putNextEntry(entry);
// Load image and write to outstream
- ImageIcon icon = new ImageIcon(point.getPhoto().getFile().getAbsolutePath());
+ ImageIcon icon = point.getPhoto().createImageIcon();
- // Scale and smooth image to required size
+ // Scale image to required size TODO: should it also be smoothed, or only if it's smaller than a certain size?
BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(),
inThumbWidth, inThumbHeight, point.getPhoto().getRotationDegrees());
// Store image dimensions so that it doesn't have to be calculated again for the points
_photo = inPhoto;
if (inPhoto != null)
{
- _photoName = inPhoto.getFile().getName();
+ _photoName = inPhoto.getName();
_status = getStatusString(inPhoto.getOriginalStatus(), inPhoto.getCurrentStatus());
}
}
private void writeStartOfFile(FileWriter inWriter, double inModelSize, String inLineSeparator)
throws IOException
{
- inWriter.write("// Pov file produced by Prune - see http://activityworkshop.net/");
+ inWriter.write("// Pov file produced by GpsPrune - see http://activityworkshop.net/");
inWriter.write(inLineSeparator);
inWriter.write(inLineSeparator);
// Select font based on user input
{
inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
inWriter.write(inLineSeparator);
- inWriter.write("<!-- Svg file produced by Prune - see http://activityworkshop.net/ -->");
+ inWriter.write("<!-- Svg file produced by GpsPrune - see http://activityworkshop.net/ -->");
inWriter.write(inLineSeparator);
inWriter.write("<svg width=\"800\" height=\"700\">");
inWriter.write(inLineSeparator);
--- /dev/null
+package tim.prune.save.xml;
+
+/**
+ * Collection of utility functions for handling XML
+ */
+public abstract class XmlUtils
+{
+ /** Start of Cdata sequence */
+ private static final String CDATA_START = "<![CDATA[";
+ /** End of Cdata sequence */
+ private static final String CDATA_END = "]]>";
+
+ /**
+ * Fix the CDATA blocks in the given String to give valid xml
+ * @param inString String to modify
+ * @return fixed String
+ */
+ public static String fixCdata(String inString)
+ {
+ if (inString == null) return "";
+ if (inString.indexOf('<') < 0 && inString.indexOf('>') < 0) {
+ return inString;
+ }
+ String result = inString;
+ // Remove cdata block at start if present
+ if (result.startsWith(CDATA_START)) {
+ result = result.substring(CDATA_START.length());
+ }
+ // Remove all instances of end block
+ result = result.replaceAll(CDATA_END, "");
+ // Now check whether cdata block is required
+ if (result.indexOf('<') < 0 && result.indexOf('>') < 0) {
+ return result;
+ }
+ return CDATA_START + result + CDATA_END;
+ }
+}
\r
import tim.prune.I18nManager;\r
import tim.prune.UpdateMessageBroker;\r
-import tim.prune.data.AudioFile;\r
+import tim.prune.data.AudioClip;\r
import tim.prune.data.DataPoint;\r
import tim.prune.data.Photo;\r
import tim.prune.data.TrackInfo;\r
if (_audioFilename != null)\r
{\r
// Disconnect audio\r
- AudioFile audio = _point.getAudio();\r
+ AudioClip audio = _point.getAudio();\r
if (audio != null)\r
{\r
_point.setAudio(null);\r
package tim.prune.undo;\r
\r
import tim.prune.I18nManager;\r
-import tim.prune.data.AudioFile;\r
+import tim.prune.data.AudioClip;\r
import tim.prune.data.DataPoint;\r
import tim.prune.data.TrackInfo;\r
\r
// restore audio association\r
for (int i=0; i<_audioPoints.length; i++)\r
{\r
- AudioFile audio = inTrackInfo.getAudioList().getAudio(i);\r
+ AudioClip audio = inTrackInfo.getAudioList().getAudio(i);\r
// Only need to look at connected ones, since correlation wouldn't disconnect\r
- if (audio.getCurrentStatus() == AudioFile.Status.CONNECTED)\r
+ if (audio.getCurrentStatus() == AudioClip.Status.CONNECTED)\r
{\r
DataPoint prevPoint = _audioPoints[i];\r
DataPoint currPoint = audio.getDataPoint();\r
\r
import tim.prune.I18nManager;\r
import tim.prune.UpdateMessageBroker;\r
-import tim.prune.data.AudioFile;\r
+import tim.prune.data.AudioClip;\r
import tim.prune.data.DataPoint;\r
import tim.prune.data.TrackInfo;\r
\r
public class UndoDeleteAudio implements UndoOperation\r
{\r
private int _audioIndex = -1;\r
- private AudioFile _audio = null;\r
+ private AudioClip _audio = null;\r
private int _pointIndex = -1;\r
private DataPoint _point = null;\r
\r
* @param inPoint data point\r
* @param inPointIndex index number of point within track\r
*/\r
- public UndoDeleteAudio(AudioFile inAudio, int inAudioIndex, DataPoint inPoint, int inPointIndex)\r
+ public UndoDeleteAudio(AudioClip inAudio, int inAudioIndex, DataPoint inPoint, int inPointIndex)\r
{\r
_audio = inAudio;\r
_audioIndex = inAudioIndex;\r
* @return description of operation including filename\r
*/\r
public String getDescription() {\r
- return I18nManager.getText("undo.removeaudio") + " " + _audio.getFile().getName();\r
+ return I18nManager.getText("undo.removeaudio") + " " + _audio.getName();\r
}\r
\r
\r
*/\r
public String getDescription()\r
{\r
- String desc = I18nManager.getText("undo.removephoto") + " " + _photo.getFile().getName();\r
+ String desc = I18nManager.getText("undo.removephoto") + " " + _photo.getName();\r
return desc;\r
}\r
\r
inTrackInfo.getPhotoList().addPhoto(_point.getPhoto(), _photoIndex);\r
}\r
// Ensure that photo is associated with point\r
- _point.getPhoto().setDataPoint(_point);\r
+ if (_point.getPhoto().getDataPoint() != _point) {\r
+ _point.getPhoto().setDataPoint(_point);\r
+ }\r
}\r
// Restore previous status of following track point if necessary\r
if (!_segmentStart)\r
package tim.prune.undo;\r
\r
import tim.prune.I18nManager;\r
-import tim.prune.data.AudioFile;\r
+import tim.prune.data.AudioClip;\r
import tim.prune.data.DataPoint;\r
import tim.prune.data.Photo;\r
import tim.prune.data.TrackInfo;\r
{\r
private DataPoint _point = null;\r
private Photo _photo = null;\r
- private AudioFile _audio = null;\r
+ private AudioClip _audio = null;\r
private String _filename = null;\r
\r
\r
*/\r
public class UndoLoadAudios implements UndoOperation\r
{\r
- /** Number of audio files added */\r
+ /** Number of audio clips added */\r
private int _numAudios = -1;\r
\r
\r
*/\r
public String getDescription()\r
{\r
- return I18nManager.getText("undo.rotatephoto") + " " + _photo.getFile().getName();\r
+ return I18nManager.getText("undo.rotatephoto") + " " + _photo.getName();\r
}\r
\r
\r