import javax.swing.JOptionPane;
import tim.prune.data.Altitude;
+import tim.prune.data.AudioFile;
import tim.prune.data.Checker;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
import tim.prune.data.LatLonRectangle;
+import tim.prune.data.MediaFile;
import tim.prune.data.NumberUtils;
import tim.prune.data.Photo;
import tim.prune.data.PhotoList;
import tim.prune.gui.Viewport;
import tim.prune.load.FileLoader;
import tim.prune.load.JpegLoader;
+import tim.prune.load.MediaHelper;
import tim.prune.load.TrackNameList;
import tim.prune.save.ExifSaver;
import tim.prune.save.FileSaver;
private TrackInfo _trackInfo = null;
private int _lastSavePosition = 0;
private MenuManager _menuManager = null;
- private SidebarController __sidebarController = null;
+ private SidebarController _sidebarController = null;
private FileLoader _fileLoader = null;
private JpegLoader _jpegLoader = null;
private FileSaver _fileSaver = null;
public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
Altitude.Format inAltFormat, SourceInfo inSourceInfo)
{
- informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null);
+ informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo, null, null);
}
/**
*/
public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
Altitude.Format inAltFormat, SourceInfo inSourceInfo, TrackNameList inTrackNameList)
+ {
+ // no link array given
+ informDataLoaded(inFieldArray, inDataArray, inAltFormat, inSourceInfo,
+ inTrackNameList, null);
+ }
+
+ /**
+ * Receive loaded data and determine whether to filter on tracks or not
+ * @param inFieldArray array of fields
+ * @param inDataArray array of data
+ * @param inAltFormat altitude format
+ * @param inSourceInfo information about the source of the data
+ * @param inTrackNameList information about the track names
+ * @param inLinkArray array of links to photo/audio files
+ */
+ public void informDataLoaded(Field[] inFieldArray, Object[][] inDataArray,
+ Altitude.Format inAltFormat, SourceInfo inSourceInfo,
+ TrackNameList inTrackNameList, String[] inLinkArray)
{
// Check whether loaded array can be properly parsed into a Track
Track loadedTrack = new Track();
JOptionPane.showMessageDialog(_frame, I18nManager.getText("dialog.open.contentsdoubled"),
I18nManager.getText("function.open"), JOptionPane.WARNING_MESSAGE);
}
+ // Attach photos and/or audio files to points
+ if (inLinkArray != null)
+ {
+ for (int i=0; i<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);
+ }
+ }
+ }
+ }
// Look at TrackNameList, decide whether to filter or not
if (inTrackNameList != null && inTrackNameList.getNumTracks() > 1)
{
}
}
+
/**
* Receive loaded data and optionally merge with current Track
* @param inLoadedTrack loaded track
if (answer == JOptionPane.YES_OPTION)
{
// append data to current Track
- _undoStack.add(new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints()));
+ UndoLoad undo = new UndoLoad(_track.getNumPoints(), inLoadedTrack.getNumPoints());
+ undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
+ _undoStack.add(undo);
_track.combine(inLoadedTrack);
+ // Add photos and audios (if any in loaded track) to list(s)
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
// set source information
inSourceInfo.populatePointObjects(_track, inLoadedTrack.getNumPoints());
_trackInfo.getFileInfo().addSource(inSourceInfo);
if (_trackInfo.getPhotoList().hasCorrelatedPhotos()) {
photos = _trackInfo.getPhotoList().cloneList();
}
- _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos));
+ UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), photos);
+ undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
+ _undoStack.add(undo);
_lastSavePosition = _undoStack.size();
_trackInfo.getSelection().clearAll();
_track.load(inLoadedTrack);
inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
_trackInfo.getFileInfo().replaceSource(inSourceInfo);
- if (photos != null) {
- _trackInfo.getPhotoList().removeCorrelatedPhotos();
- }
+ _trackInfo.getPhotoList().removeCorrelatedPhotos();
+ _trackInfo.getAudioList().removeCorrelatedAudios();
+ // Add photos and audios (if any in loaded track) to list(s)
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
}
}
else
{
// Currently no data held, so transfer received data
- _undoStack.add(new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null));
+ UndoLoad undo = new UndoLoad(_trackInfo, inLoadedTrack.getNumPoints(), null);
+ undo.setNumPhotosAudios(_trackInfo.getPhotoList().getNumPhotos(), _trackInfo.getAudioList().getNumAudios());
+ _undoStack.add(undo);
_lastSavePosition = _undoStack.size();
_trackInfo.getSelection().clearAll();
_track.load(inLoadedTrack);
inSourceInfo.populatePointObjects(_track, _track.getNumPoints());
_trackInfo.getFileInfo().addSource(inSourceInfo);
+ // Add photos and audios (if any in loaded track) to list(s)
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getPhotoList(), Photo.class);
+ MediaHelper.addMediaFromTrack(_track, _trackInfo.getAudioList(), AudioFile.class);
}
UpdateMessageBroker.informSubscribers();
// Update status bar
// MAYBE: Improve message when photo(s) fail to load (eg already added)
UpdateMessageBroker.informSubscribers();
// update menu
- _menuManager.informFileLoaded();
- }
- }
-
-
- /**
- * Connect the current photo to the current point
- */
- public void connectPhotoToPoint()
- {
- Photo photo = _trackInfo.getCurrentPhoto();
- DataPoint point = _trackInfo.getCurrentPoint();
- if (photo != null && point != null)
- {
- if (point.getPhoto() == null)
- {
- // point doesn't currently have a photo, so just connect it
- _undoStack.add(new UndoConnectPhoto(point, photo.getFile().getName()));
- photo.setDataPoint(point);
- point.setPhoto(photo);
- UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
- UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.connect"));
- }
- }
- }
-
-
- /**
- * Disconnect the current photo from its point
- */
- public void disconnectPhotoFromPoint()
- {
- Photo photo = _trackInfo.getCurrentPhoto();
- if (photo != null && photo.getDataPoint() != null)
- {
- DataPoint point = photo.getDataPoint();
- _undoStack.add(new UndoDisconnectPhoto(point, photo.getFile().getName()));
- // disconnect
- photo.setDataPoint(null);
- point.setPhoto(null);
- UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
- UpdateMessageBroker.informSubscribers(I18nManager.getText("confirm.photo.disconnect"));
- }
- }
-
-
- /**
- * Remove the current photo, if any
- */
- public void deleteCurrentPhoto()
- {
- // Delete the current photo, and optionally its point too, keeping undo information
- Photo currentPhoto = _trackInfo.getCurrentPhoto();
- if (currentPhoto != null)
- {
- // Photo is selected, see if it has a point or not
- boolean photoDeleted = false;
- UndoDeletePhoto undoAction = null;
- if (currentPhoto.getDataPoint() == null)
- {
- // no point attached, so just delete photo
- undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(),
- null, -1);
- photoDeleted = _trackInfo.deleteCurrentPhoto(false);
- }
- else
- {
- // point is attached, so need to confirm point deletion
- undoAction = new UndoDeletePhoto(currentPhoto, _trackInfo.getSelection().getCurrentPhotoIndex(),
- currentPhoto.getDataPoint(), _trackInfo.getTrack().getPointIndex(currentPhoto.getDataPoint()));
- int response = JOptionPane.showConfirmDialog(_frame,
- I18nManager.getText("dialog.deletephoto.deletepoint"),
- I18nManager.getText("dialog.deletephoto.title"),
- JOptionPane.YES_NO_CANCEL_OPTION);
- boolean deletePointToo = (response == JOptionPane.YES_OPTION);
- // Cancel delete if cancel pressed or dialog closed
- if (response == JOptionPane.YES_OPTION || response == JOptionPane.NO_OPTION)
- {
- photoDeleted = _trackInfo.deleteCurrentPhoto(deletePointToo);
- }
- }
- // Add undo information to stack if necessary
- if (photoDeleted)
- {
- _undoStack.add(undoAction);
- }
+ if (numPointsAdded > 0) _menuManager.informFileLoaded();
}
}
*/
public void setSidebarController(SidebarController inController)
{
- __sidebarController = inController;
+ _sidebarController = inController;
}
/**
*/
public void toggleSidebars()
{
- __sidebarController.toggle();
+ _sidebarController.toggle();
}
}
package tim.prune;
+import tim.prune.correlate.AudioCorrelator;
import tim.prune.correlate.PhotoCorrelator;
import tim.prune.function.*;
import tim.prune.function.charts.Charter;
import tim.prune.function.gpsies.GetGpsiesFunction;
import tim.prune.function.gpsies.UploadGpsiesFunction;
import tim.prune.function.srtm.LookupSrtmFunction;
+import tim.prune.load.AudioLoader;
import tim.prune.load.GpsLoader;
import tim.prune.save.GpsSaver;
import tim.prune.save.GpxExporter;
public static GenericFunction FUNCTION_REARRANGE_PHOTOS = null;
public static GenericFunction FUNCTION_COMPRESS = null;
public static GenericFunction FUNCTION_LOOKUP_SRTM = null;
+ public static GenericFunction FUNCTION_LOOKUP_WIKIPEDIA = null;
+ public static GenericFunction FUNCTION_SEARCH_WIKIPEDIA = null;
+ public static GenericFunction FUNCTION_DOWNLOAD_OSM = null;
public static GenericFunction FUNCTION_ADD_TIME_OFFSET = null;
public static GenericFunction FUNCTION_ADD_ALTITUDE_OFFSET = null;
public static GenericFunction FUNCTION_CONVERT_NAMES_TO_TIMES = null;
public static GenericFunction FUNCTION_PASTE_COORDINATES = null;
public static GenericFunction FUNCTION_FIND_WAYPOINT = null;
public static GenericFunction FUNCTION_DUPLICATE_POINT = null;
+ public static GenericFunction FUNCTION_CONNECT_TO_POINT = null;
+ public static GenericFunction FUNCTION_DISCONNECT_PHOTO = null;
+ public static GenericFunction FUNCTION_REMOVE_PHOTO = null;
+ public static GenericFunction FUNCTION_DISCONNECT_AUDIO = null;
public static GenericFunction FUNCTION_CORRELATE_PHOTOS = null;
public static GenericFunction FUNCTION_ROTATE_PHOTO_LEFT = null;
public static GenericFunction FUNCTION_ROTATE_PHOTO_RIGHT = null;
+ public static GenericFunction FUNCTION_PHOTO_POPUP = null;
public static GenericFunction FUNCTION_IGNORE_EXIF_THUMB = null;
public static GenericFunction FUNCTION_CHARTS = null;
public static GenericFunction FUNCTION_3D = null;
public static GenericFunction FUNCTION_FULL_RANGE_DETAILS = null;
public static GenericFunction FUNCTION_GET_GPSIES = null;
public static GenericFunction FUNCTION_UPLOAD_GPSIES = null;
+ public static GenericFunction FUNCTION_LOAD_AUDIO = null;
+ public static GenericFunction FUNCTION_REMOVE_AUDIO = null;
+ public static GenericFunction FUNCTION_CORRELATE_AUDIOS = null;
+ public static GenericFunction FUNCTION_PLAY_AUDIO = null;
+ public static GenericFunction FUNCTION_STOP_AUDIO = null;
public static GenericFunction FUNCTION_SET_MAP_BG = null;
public static GenericFunction FUNCTION_SET_DISK_CACHE = null;
public static GenericFunction FUNCTION_SET_PATHS = null;
public static GenericFunction FUNCTION_SET_KMZ_IMAGE_SIZE = null;
public static GenericFunction FUNCTION_SET_COLOURS = null;
+ public static GenericFunction FUNCTION_SET_LINE_WIDTH = null;
public static GenericFunction FUNCTION_SET_LANGUAGE = null;
public static GenericFunction FUNCTION_HELP = null;
public static GenericFunction FUNCTION_SHOW_KEYS = null;
FUNCTION_REARRANGE_PHOTOS = new RearrangePhotosFunction(inApp);
FUNCTION_COMPRESS = new CompressTrackFunction(inApp);
FUNCTION_LOOKUP_SRTM = new LookupSrtmFunction(inApp);
+ FUNCTION_LOOKUP_WIKIPEDIA = new GetWikipediaFunction(inApp);
+ FUNCTION_SEARCH_WIKIPEDIA = new SearchWikipediaNames(inApp);
+ FUNCTION_DOWNLOAD_OSM = new DownloadOsmFunction(inApp);
FUNCTION_ADD_TIME_OFFSET = new AddTimeOffset(inApp);
FUNCTION_ADD_ALTITUDE_OFFSET = new AddAltitudeOffset(inApp);
FUNCTION_CONVERT_NAMES_TO_TIMES = new ConvertNamesToTimes(inApp);
FUNCTION_PASTE_COORDINATES = new PasteCoordinates(inApp);
FUNCTION_FIND_WAYPOINT = new FindWaypoint(inApp);
FUNCTION_DUPLICATE_POINT = new DuplicatePoint(inApp);
+ FUNCTION_CONNECT_TO_POINT = new ConnectToPointFunction(inApp);
+ FUNCTION_DISCONNECT_PHOTO = new DisconnectPhotoFunction(inApp);
+ FUNCTION_REMOVE_PHOTO = new RemovePhotoFunction(inApp);
FUNCTION_CORRELATE_PHOTOS = new PhotoCorrelator(inApp);
FUNCTION_ROTATE_PHOTO_LEFT = new RotatePhoto(inApp, false);
FUNCTION_ROTATE_PHOTO_RIGHT = new RotatePhoto(inApp, true);
+ FUNCTION_PHOTO_POPUP = new PhotoPopupFunction(inApp);
FUNCTION_IGNORE_EXIF_THUMB = new IgnoreExifThumb(inApp);
FUNCTION_CHARTS = new Charter(inApp);
FUNCTION_3D = new ShowThreeDFunction(inApp);
FUNCTION_FULL_RANGE_DETAILS = new FullRangeDetails(inApp);
FUNCTION_GET_GPSIES = new GetGpsiesFunction(inApp);
FUNCTION_UPLOAD_GPSIES = new UploadGpsiesFunction(inApp);
+ FUNCTION_LOAD_AUDIO = new AudioLoader(inApp);
+ FUNCTION_REMOVE_AUDIO = new RemoveAudioFunction(inApp);
+ FUNCTION_CORRELATE_AUDIOS = new AudioCorrelator(inApp);
+ FUNCTION_PLAY_AUDIO = new PlayAudioFunction(inApp);
+ FUNCTION_STOP_AUDIO = new StopAudioFunction(inApp);
+ FUNCTION_DISCONNECT_AUDIO = new DisconnectAudioFunction(inApp);
FUNCTION_SET_MAP_BG = new SetMapBgFunction(inApp);
FUNCTION_SET_DISK_CACHE = new DiskCacheConfig(inApp);
FUNCTION_SET_PATHS = new SetPathsFunction(inApp);
FUNCTION_SET_KMZ_IMAGE_SIZE = new SetKmzImageSize(inApp);
FUNCTION_SET_COLOURS = new SetColours(inApp);
+ FUNCTION_SET_LINE_WIDTH = new SetLineWidth(inApp);
FUNCTION_SET_LANGUAGE = new SetLanguage(inApp);
FUNCTION_HELP = new HelpScreen(inApp);
FUNCTION_SHOW_KEYS = new ShowKeysScreen(inApp);
public class GpsPruner
{
/** Version number of application, used in about screen and for version check */
- public static final String VERSION_NUMBER = "11.2";
+ public static final String VERSION_NUMBER = "12";
/** Build number, just used for about screen */
- public static final String BUILD_NUMBER = "205a";
+ public static final String BUILD_NUMBER = "223";
/** Static reference to App object */
private static App APP = null;
for (int i=0; i<args.length; i++)
{
String arg = args[i];
- if (arg.startsWith("--locale="))
- {
- localeCode = arg.substring(9);
- locale = getLanguage(localeCode);
- }
- else if (arg.startsWith("--lang="))
+ if (arg.startsWith("--lang="))
{
localeCode = arg.substring(7);
locale = getLanguage(localeCode);
if (showUsage) {
System.out.println("Possible parameters:"
+ "\n --configfile=<file> used to specify a configuration file"
- + "\n --lang=<code> or --locale=<code> used to specify language"
+ + "\n --lang=<code> used to specify language code such as DE"
+ "\n --langfile=<file> used to specify an alternative language file\n");
}
// Initialise configuration if selected
{
return new Locale(inString);
}
- else if (inString.length() == 5)
+ else if (inString.length() == 5 && inString.charAt(2) == '_')
{
return new Locale(inString.substring(0, 2), inString.substring(3));
}
public static final String KEY_EXIFTOOL_PATH = "prune.exiftoolpath";
/** Key for colour scheme */
public static final String KEY_COLOUR_SCHEME = "prune.colourscheme";
+ /** Key for line width used for drawing */
+ public static final String KEY_LINE_WIDTH = "prune.linewidth";
/** Key for kml track colour */
public static final String KEY_KML_TRACK_COLOUR = "prune.kmltrackcolour";
The source code of Prune is copyright 2006-2010 activityworkshop.net
-and distributed under the terms of the Gnu GPL version 2.
+and is distributed under the terms of the Gnu GPL version 2.
Portions of the package jpeg.drew (if included in this package) were taken
from Drew Noakes' "Metadata extractor" v2.3.1 which is
--- /dev/null
+package tim.prune.correlate;
+
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTable;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.AudioFile;
+import tim.prune.data.AudioList;
+import tim.prune.data.DataPoint;
+import tim.prune.data.MediaFile;
+import tim.prune.data.MediaList;
+import tim.prune.data.TimeDifference;
+import tim.prune.data.Timestamp;
+import tim.prune.undo.UndoCorrelateAudios;
+
+/**
+ * Class to manage the automatic correlation of audio files to points
+ * which is very similar to the PhotoCorrelator apart from the clip lengths
+ */
+public class AudioCorrelator extends Correlator
+{
+ private AudioTimestampSelector _fileTimesSelector = null, _correlTimesSelector = null;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public AudioCorrelator(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.correlateaudios";
+ }
+
+ /** @return type key */
+ protected String getMediaTypeKey() {
+ return "audio";
+ }
+
+ /** @return photo list*/
+ protected MediaList getMediaList() {
+ return _app.getTrackInfo().getAudioList();
+ }
+
+
+ /**
+ * @return first gui panel including timestamp specification (beginning, middle, end)
+ */
+ protected JPanel makeFirstPanel()
+ {
+ // First panel for timestamp stuff
+ JPanel card1 = new JPanel();
+ card1.setLayout(new FlowLayout(FlowLayout.CENTER));
+ JPanel grid1 = new JPanel();
+ grid1.setLayout(new GridLayout(0, 1));
+ _fileTimesSelector = new AudioTimestampSelector("dialog.correlate.filetimes", "dialog.correlate.filetimes2");
+ grid1.add(_fileTimesSelector);
+ _correlTimesSelector = new AudioTimestampSelector("dialog.correlate.correltimes", null);
+ grid1.add(_correlTimesSelector);
+ card1.add(grid1);
+ return card1;
+ }
+
+
+ /**
+ * @return array of boolean flags denoting availability of cards
+ */
+ protected boolean[] getCardEnabledFlags()
+ {
+ boolean[] cards = super.getCardEnabledFlags();
+ cards[0] = getAudioLengthAvailability(_app.getTrackInfo().getAudioList());
+ return cards;
+ }
+
+ /**
+ * @param inAudios AudioList object
+ * @return true if there are any audio lengths available
+ */
+ private static boolean getAudioLengthAvailability(AudioList inAudios)
+ {
+ for (int i=0; i<inAudios.getNumMedia(); i++)
+ {
+ AudioFile a = inAudios.getAudio(i);
+ if (a.getLengthInSeconds() > 0) {return true;}
+ }
+ return false;
+ }
+
+ /**
+ * Create a preview of the correlate action using the selected time difference
+ * @param inTimeDiff TimeDifference to use for preview
+ * @param inShowWarning true to show warning if all points out of range
+ */
+ protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
+ {
+ TimeDifference timeLimit = parseTimeLimit();
+ double angDistLimit = parseDistanceLimit();
+ MediaPreviewTableModel model = new MediaPreviewTableModel("dialog.correlate.select.audioname");
+ AudioList audios = _app.getTrackInfo().getAudioList();
+ // Loop through audios deciding whether to set correlate flag or not
+ int numAudios = audios.getNumAudios();
+ for (int i=0; i<numAudios; i++)
+ {
+ AudioFile 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;}
+ // Check time limits, distance limits
+ if (timeLimit != null && correlateAudio) {
+ long numSecs = pair.getMinSeconds();
+ correlateAudio = (numSecs <= timeLimit.getTotalSeconds());
+ }
+ if (angDistLimit > 0.0 && correlateAudio)
+ {
+ final double angDistPair = DataPoint.calculateRadiansBetween(pair.getPointBefore(), pair.getPointAfter());
+ double frac = pair.getFraction();
+ if (frac > 0.5) {frac = 1 - frac;}
+ final double angDistPhoto = angDistPair * frac;
+ correlateAudio = (angDistPhoto < angDistLimit);
+ }
+ // Don't select audios which are already correlated to the same point
+ if (pair.getSecondsBefore() == 0L && pair.getPointBefore().isDuplicate(audio.getDataPoint())) {
+ correlateAudio = false;
+ }
+ row.setCorrelateFlag(correlateAudio);
+ model.addRow(row);
+ }
+ _previewTable.setModel(model);
+ // Set distance units
+ model.setDistanceUnits(getSelectedDistanceUnits());
+ // Set column widths
+ _previewTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ final int[] colWidths = {150, 160, 100, 100, 50};
+ for (int i=0; i<model.getColumnCount(); i++) {
+ _previewTable.getColumnModel().getColumn(i).setPreferredWidth(colWidths[i]);
+ }
+ // check if any audios found
+ _okButton.setEnabled(model.hasAnySelected());
+ if (inShowWarning && !model.hasAnySelected())
+ {
+ JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
+ I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ /**
+ * @return modified timestamp of specified media object
+ */
+ protected Timestamp getMediaTimestamp(MediaFile inMedia)
+ {
+ Timestamp tstamp = super.getMediaTimestamp(inMedia);
+ try {
+ AudioFile audio = (AudioFile) 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 *
+ (_correlTimesSelector.getSelectedOption() - _fileTimesSelector.getSelectedOption()) / 2;
+ if (audioLength > 0 && secsToAdd != 0) {
+ tstamp = tstamp.createPlusOffset(secsToAdd);
+ }
+ }
+ catch (ClassCastException cce) {}
+ return tstamp;
+ }
+
+ /**
+ * Finish the correlation by modifying the track
+ * and passing the Undo information back to the App
+ */
+ protected void finishCorrelation()
+ {
+ // TODO: Probably should be able to combine this into the Correlator?
+ PointMediaPair[] pointPairs = getPointPairs();
+ if (pointPairs == null || pointPairs.length <= 0) {return;}
+
+ // begin to construct undo information
+ UndoCorrelateAudios undo = new UndoCorrelateAudios(_app.getTrackInfo());
+ // loop over Audios
+ int arraySize = pointPairs.length;
+ int i = 0, numAudios = 0;
+ int numPointsToCreate = 0;
+ PointMediaPair pair = null;
+ for (i=0; i<arraySize; i++)
+ {
+ pair = pointPairs[i];
+ if (pair != null && pair.isValid())
+ {
+ if (pair.getMinSeconds() == 0L)
+ {
+ // exact match
+ AudioFile pointAudio = pair.getPointBefore().getAudio();
+ if (pointAudio == null)
+ {
+ // photo coincides with audioless point so connect the two
+ pair.getPointBefore().setAudio((AudioFile) pair.getMedia());
+ pair.getMedia().setDataPoint(pair.getPointBefore());
+ }
+ else if (pointAudio.equals(pair.getMedia())) {
+ // photo is already connected, nothing to do
+ }
+ else {
+ // point is already connected to a different audio, so need to clone point
+ numPointsToCreate++;
+ }
+ }
+ else
+ {
+ // audio time falls between two points, so need to interpolate new one
+ numPointsToCreate++;
+ }
+ numAudios++;
+ }
+ }
+ // Second loop, to create points if necessary
+ if (numPointsToCreate > 0)
+ {
+ // make new array for added points
+ DataPoint[] addedPoints = new DataPoint[numPointsToCreate];
+ int pointNum = 0;
+ DataPoint pointToAdd = null;
+ for (i=0; i<arraySize; i++)
+ {
+ pair = pointPairs[i];
+ if (pair != null && pair.isValid())
+ {
+ pointToAdd = null;
+ if (pair.getMinSeconds() == 0L && pair.getPointBefore().getAudio() != null
+ && !pair.getPointBefore().getAudio().equals(pair.getMedia()))
+ {
+ // clone point
+ pointToAdd = pair.getPointBefore().clonePoint();
+ }
+ else if (pair.getMinSeconds() > 0L)
+ {
+ // interpolate point
+ pointToAdd = DataPoint.interpolate(pair.getPointBefore(), pair.getPointAfter(), pair.getFraction());
+ }
+ if (pointToAdd != null)
+ {
+ // link audio to point
+ pointToAdd.setAudio((AudioFile) pair.getMedia());
+ pair.getMedia().setDataPoint(pointToAdd);
+ // set to start of segment so not joined in track
+ pointToAdd.setSegmentStart(true);
+ // add to point array
+ addedPoints[pointNum] = pointToAdd;
+ pointNum++;
+ }
+ }
+ }
+ // expand track
+ _app.getTrackInfo().getTrack().appendPoints(addedPoints);
+ }
+
+ // send undo information back to controlling app
+ undo.setNumAudiosCorrelated(numAudios);
+ _app.completeFunction(undo, ("" + numAudios + " "
+ + (numAudios==1?I18nManager.getText("confirm.correlateaudios.single"):I18nManager.getText("confirm.correlateaudios.multi"))));
+ // observers already informed by track update if new points created
+ if (numPointsToCreate == 0) {
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ }
+ }
+}
--- /dev/null
+package tim.prune.correlate;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+
+import javax.swing.BorderFactory;
+import javax.swing.ButtonGroup;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.border.EtchedBorder;
+
+import tim.prune.I18nManager;
+
+/**
+ * GUI element to allow the selection of timestamp options
+ * for audio file correlation
+ */
+public class AudioTimestampSelector extends JPanel
+{
+ /** Array of radio buttons */
+ private JRadioButton[] _radios = new JRadioButton[3];
+
+
+ /**
+ * Constructor
+ * @param inTopLabelKey key for description label at top
+ * @param inLowerLabelKey key for description label at bottom, if any
+ */
+ public AudioTimestampSelector(String inTopLabelKey, String inLowerLabelKey)
+ {
+ createComponents(inTopLabelKey, inLowerLabelKey);
+ setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(4, 4, 4, 4))
+ );
+ }
+
+ /**
+ * Create the GUI components
+ * @param inTopLabelKey key for description label at top
+ * @param inLowerLabelKey key for description label at bottom, if any
+ */
+ private void createComponents(String inTopLabelKey, String inLowerLabelKey)
+ {
+ setLayout(new BorderLayout());
+ add(new JLabel(I18nManager.getText(inTopLabelKey)), BorderLayout.NORTH);
+ // panel for the radio buttons
+ JPanel gridPanel = new JPanel();
+ gridPanel.setLayout(new GridLayout(0, 3, 15, 3));
+ final String[] keys = {"beginning", "middle", "end"};
+ ButtonGroup group = new ButtonGroup();
+ for (int i=0; i<3; i++)
+ {
+ _radios[i] = new JRadioButton(I18nManager.getText("dialog.correlate.timestamp." + keys[i]));
+ group.add(_radios[i]);
+ gridPanel.add(_radios[i]);
+ }
+ _radios[0].setSelected(true);
+ add(gridPanel, BorderLayout.CENTER);
+ if (inLowerLabelKey != null) {
+ add(new JLabel(I18nManager.getText(inLowerLabelKey)), BorderLayout.SOUTH);
+ }
+ }
+
+ /**
+ * Get the option selected by the user
+ * @return 0 for beginning, 1 for middle or 2 for end
+ */
+ public int getSelectedOption()
+ {
+ for (int i=0; i<_radios.length; i++)
+ if (_radios[i].isSelected()) {return i;}
+ return 0;
+ }
+}
--- /dev/null
+package tim.prune.correlate;
+
+import java.awt.CardLayout;
+import java.awt.Component;
+
+import javax.swing.JPanel;
+
+/**
+ * Panel to act as a card stack
+ */
+public class CardStack extends JPanel
+{
+ private int _numCards = 0;
+ private int _currCard = 0;
+ private CardLayout _layout = null;
+ private static final String cardName = "card";
+
+ /**
+ * Constructor
+ */
+ public CardStack()
+ {
+ _layout = new CardLayout();
+ setLayout(_layout);
+ }
+
+ /**
+ * Add a card to the stack
+ * @param inComponent component to add
+ */
+ public void addCard(Component inComponent)
+ {
+ super.add(inComponent, cardName + _numCards);
+ _numCards++;
+ }
+
+ /**
+ * @return current card index, starting from 0
+ */
+ public int getCurrentCardIndex()
+ {
+ return _currCard;
+ }
+
+ /**
+ * @return number of cards in the stack
+ */
+ public int getNumCards()
+ {
+ return _numCards;
+ }
+
+ /**
+ * Show the specified card
+ * @param inIndex index of card, starting from 0
+ */
+ public void showCard(int inIndex)
+ {
+ if (inIndex >= 0 && inIndex < _numCards) {
+ _currCard = inIndex;
+ _layout.show(this, cardName + inIndex);
+ }
+ }
+}
--- /dev/null
+package tim.prune.correlate;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Distance;
+import tim.prune.data.Field;
+import tim.prune.data.MediaFile;
+import tim.prune.data.MediaList;
+import tim.prune.data.TimeDifference;
+import tim.prune.data.Timestamp;
+import tim.prune.data.Track;
+
+/**
+ * Abstract superclass of the two correlator functions
+ */
+public abstract class Correlator extends GenericFunction
+{
+ protected JDialog _dialog;
+ private CardStack _cards = null;
+ private JLabel _tipLabel = null;
+ private JTable _selectionTable = null;
+ protected JTable _previewTable = null;
+ private boolean _previewEnabled = false; // flag required to enable preview function on final panel
+ private boolean[] _cardEnabled = null; // flag for each card
+ private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null;
+ private JRadioButton _mediaLaterOption = null, _pointLaterOption = null;
+ private JRadioButton _timeLimitRadio = null, _distLimitRadio = null;
+ private JTextField _limitMinBox = null, _limitSecBox = null;
+ private JTextField _limitDistBox = null;
+ private JComboBox _distUnitsDropdown = null;
+ private JButton _nextButton = null, _backButton = null;
+ protected JButton _okButton = null;
+
+ /**
+ * Constructor
+ * @param inApp App object to report actions to
+ */
+ public Correlator(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return type key eg photo, audio
+ */
+ protected abstract String getMediaTypeKey();
+
+ /**
+ * @return media list
+ */
+ protected abstract MediaList getMediaList();
+
+ /**
+ * Begin the function by initialising and showing the dialog
+ */
+ public void begin()
+ {
+ // Check whether track has timestamps, exit if not
+ if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP))
+ {
+ JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"),
+ I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
+ return;
+ }
+ // Show warning if no uncorrelated audios
+ if (!getMediaList().hasUncorrelatedMedia())
+ {
+ Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")};
+ if (JOptionPane.showOptionDialog(_parentFrame,
+ I18nManager.getText("dialog.correlate.nouncorrelated" + getMediaTypeKey() + "s"),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
+ == JOptionPane.NO_OPTION)
+ {
+ return;
+ }
+ }
+ // Create dialog if necessary
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.getContentPane().add(makeDialogContents());
+ _dialog.pack();
+ }
+ // Go to first available card
+ int card = 0;
+ _cardEnabled = null;
+ while (!isCardEnabled(card)) {card++;}
+ _cards.showCard(card);
+ showCard(0); // does set up and next/prev enabling
+ _okButton.setEnabled(false);
+ _dialog.setVisible(true);
+ }
+
+ /**
+ * Make contents of correlate dialog
+ * @return JPanel containing gui elements
+ */
+ private JPanel makeDialogContents()
+ {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout());
+
+ // Card panel in the middle
+ _cards = new CardStack();
+
+ // First panel (not required by photo correlator)
+ JPanel card1 = makeFirstPanel();
+ if (card1 == null) {card1 = new JPanel();}
+ _cards.addCard(card1);
+
+ // Second panel for selection of linked media
+ _cards.addCard(makeSecondPanel());
+
+ // Third panel for options and preview
+ _cards.addCard(makeThirdPanel());
+ mainPanel.add(_cards, BorderLayout.CENTER);
+
+ // Button panel at the bottom
+ JPanel buttonPanel = new JPanel();
+ _backButton = new JButton(I18nManager.getText("button.back"));
+ _backButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showCard(-1);
+ }
+ });
+ _backButton.setEnabled(false);
+ buttonPanel.add(_backButton);
+ _nextButton = new JButton(I18nManager.getText("button.next"));
+ _nextButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showCard(1);
+ }
+ });
+ buttonPanel.add(_nextButton);
+ _okButton = new JButton(I18nManager.getText("button.ok"));
+ _okButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ finishCorrelation();
+ _dialog.dispose();
+ }
+ });
+ _okButton.setEnabled(false);
+ buttonPanel.add(_okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ mainPanel.add(buttonPanel, BorderLayout.SOUTH);
+ return mainPanel;
+ }
+
+ /**
+ * Construct a table model for the photo / audio selection table
+ * @return table model
+ */
+ protected MediaSelectionTableModel makeSelectionTableModel()
+ {
+ MediaList mediaList = getMediaList();
+ MediaSelectionTableModel model = new MediaSelectionTableModel(
+ "dialog.correlate.select." + getMediaTypeKey() + "name",
+ "dialog.correlate.select." + getMediaTypeKey() + "later");
+ int numMedia = mediaList.getNumMedia();
+ for (int i=0; i<numMedia; i++)
+ {
+ MediaFile 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)
+ {
+ // Calculate time difference, add to table model
+ long timeDiff = getMediaTimestamp(media).getSecondsSince(media.getDataPoint().getTimestamp());
+ model.addMedia(media, timeDiff);
+ }
+ }
+ return model;
+ }
+
+ /**
+ * Group the two radio buttons together with a ButtonGroup
+ * @param inButton1 first radio button
+ * @param inButton2 second radio button
+ */
+ protected static void groupRadioButtons(JRadioButton inButton1, JRadioButton inButton2)
+ {
+ ButtonGroup buttonGroup = new ButtonGroup();
+ buttonGroup.add(inButton1);
+ buttonGroup.add(inButton2);
+ inButton1.setSelected(true);
+ }
+
+
+ /**
+ * Try to parse the given string
+ * @param inText String to parse
+ * @return value if parseable, 0 otherwise
+ */
+ protected static int getValue(String inText)
+ {
+ int value = 0;
+ try {
+ value = Integer.parseInt(inText);
+ }
+ catch (NumberFormatException nfe) {}
+ return value;
+ }
+
+
+ /**
+ * @param inFirstTimestamp timestamp of first photo / audio object, or null if not available
+ * @return time difference of local time zone from UTC when the first photo was taken
+ */
+ private static TimeDifference getTimezoneOffset(Timestamp inFirstTimestamp)
+ {
+ Calendar cal = null;
+ // Use first timestamp if available
+ if (inFirstTimestamp != null) {
+ cal = inFirstTimestamp.getCalendar();
+ }
+ else {
+ // No photo or no timestamp, just use current time
+ cal = Calendar.getInstance();
+ }
+ // Both time zone offset and dst offset are based on milliseconds, so convert to seconds
+ TimeDifference timeDiff = new TimeDifference((cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 1000);
+ return timeDiff;
+ }
+
+
+ /**
+ * Calculate the median index to select from the table
+ * @param inModel table model
+ * @return index of entry to select from table
+ */
+ protected static int getMedianIndex(MediaSelectionTableModel inModel)
+ {
+ // make sortable list
+ TreeSet<TimeIndexPair> set = new TreeSet<TimeIndexPair>();
+ // loop through rows of table adding to list
+ int numRows = inModel.getRowCount();
+ int i;
+ for (i=0; i<numRows; i++)
+ {
+ MediaSelectionTableRow row = inModel.getRow(i);
+ set.add(new TimeIndexPair(row.getTimeDiff().getTotalSeconds(), i));
+ }
+ // pull out middle entry and return index
+ TimeIndexPair pair = null;
+ Iterator<TimeIndexPair> iterator = set.iterator();
+ for (i=0; i<(numRows+1)/2; i++)
+ {
+ pair = iterator.next();
+ }
+ return pair.getIndex();
+ }
+
+
+ /**
+ * Disable the ok button
+ */
+ public void disableOkButton()
+ {
+ if (_okButton != null) {
+ _okButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * @return gui components for first panel, or null if empty
+ */
+ protected JPanel makeFirstPanel() {
+ return null;
+ }
+
+ /**
+ * Make the second panel for the selection screen
+ * @return JPanel object containing gui elements
+ */
+ private JPanel makeSecondPanel()
+ {
+ JPanel card = new JPanel();
+ card.setLayout(new BorderLayout(10, 10));
+ card.add(new JLabel(I18nManager.getText(
+ "dialog.correlate." + getMediaTypeKey() + "select.intro")), BorderLayout.NORTH);
+ // table doesn't have model yet - that will be attached later
+ _selectionTable = new JTable();
+ JScrollPane photoScrollPane = new JScrollPane(_selectionTable);
+ photoScrollPane.setPreferredSize(new Dimension(400, 100));
+ card.add(photoScrollPane, BorderLayout.CENTER);
+ return card;
+ }
+
+
+ /**
+ * Make contents of third panel including options and preview
+ * @return JPanel containing gui elements
+ */
+ private JPanel makeThirdPanel()
+ {
+ OptionsChangedListener optionsChangedListener = new OptionsChangedListener(this);
+ // Second panel for options
+ JPanel card2 = new JPanel();
+ card2.setLayout(new BorderLayout());
+ JPanel card2Top = new JPanel();
+ card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS));
+ _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip"));
+ _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+ card2Top.add(_tipLabel);
+ JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
+ introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
+ card2Top.add(introLabel);
+ // time offset section
+ JPanel offsetPanel = new JPanel();
+ offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel")));
+ offsetPanel.setLayout(new BoxLayout(offsetPanel, BoxLayout.Y_AXIS));
+ JPanel offsetPanelTop = new JPanel();
+ offsetPanelTop.setLayout(new FlowLayout());
+ offsetPanelTop.setBorder(null);
+ offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset") + ": "));
+ _offsetHourBox = new JTextField(3);
+ _offsetHourBox.addKeyListener(optionsChangedListener);
+ offsetPanelTop.add(_offsetHourBox);
+ offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
+ _offsetMinBox = new JTextField(3);
+ _offsetMinBox.addKeyListener(optionsChangedListener);
+ offsetPanelTop.add(_offsetMinBox);
+ offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes")));
+ _offsetSecBox = new JTextField(3);
+ _offsetSecBox.addKeyListener(optionsChangedListener);
+ offsetPanelTop.add(_offsetSecBox);
+ offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds")));
+ offsetPanel.add(offsetPanelTop);
+
+ // radio buttons for photo / point later
+ JPanel offsetPanelBot = new JPanel();
+ offsetPanelBot.setLayout(new FlowLayout());
+ offsetPanelBot.setBorder(null);
+ _mediaLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options." + getMediaTypeKey() + "later"));
+ _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlaterphoto"));
+ _mediaLaterOption.addItemListener(optionsChangedListener);
+ _pointLaterOption.addItemListener(optionsChangedListener);
+ ButtonGroup laterGroup = new ButtonGroup();
+ laterGroup.add(_mediaLaterOption);
+ laterGroup.add(_pointLaterOption);
+ offsetPanelBot.add(_mediaLaterOption);
+ offsetPanelBot.add(_pointLaterOption);
+ offsetPanel.add(offsetPanelBot);
+ offsetPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ card2Top.add(offsetPanel);
+
+ // time limits section
+ JPanel limitsPanel = new JPanel();
+ limitsPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.limitspanel")));
+ limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
+ JPanel timeLimitPanel = new JPanel();
+ timeLimitPanel.setLayout(new FlowLayout());
+ JRadioButton noTimeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.notimelimit"));
+ noTimeLimitRadio.addItemListener(optionsChangedListener);
+ timeLimitPanel.add(noTimeLimitRadio);
+ _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : ");
+ _timeLimitRadio.addItemListener(optionsChangedListener);
+ timeLimitPanel.add(_timeLimitRadio);
+ groupRadioButtons(noTimeLimitRadio, _timeLimitRadio);
+ _limitMinBox = new JTextField(3);
+ _limitMinBox.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitMinBox);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes")));
+ _limitSecBox = new JTextField(3);
+ _limitSecBox.addKeyListener(optionsChangedListener);
+ timeLimitPanel.add(_limitSecBox);
+ timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds")));
+ limitsPanel.add(timeLimitPanel);
+ // distance limits
+ JPanel distLimitPanel = new JPanel();
+ distLimitPanel.setLayout(new FlowLayout());
+ JRadioButton noDistLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.nodistancelimit"));
+ noDistLimitRadio.addItemListener(optionsChangedListener);
+ distLimitPanel.add(noDistLimitRadio);
+ _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit"));
+ _distLimitRadio.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distLimitRadio);
+ groupRadioButtons(noDistLimitRadio, _distLimitRadio);
+ _limitDistBox = new JTextField(4);
+ _limitDistBox.addKeyListener(optionsChangedListener);
+ distLimitPanel.add(_limitDistBox);
+ String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
+ I18nManager.getText("units.miles")};
+ _distUnitsDropdown = new JComboBox(distUnitsOptions);
+ _distUnitsDropdown.addItemListener(optionsChangedListener);
+ distLimitPanel.add(_distUnitsDropdown);
+ limitsPanel.add(distLimitPanel);
+ limitsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ card2Top.add(limitsPanel);
+
+ // preview button
+ JButton previewButton = new JButton(I18nManager.getText("button.preview"));
+ previewButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ createPreview(true);
+ }
+ });
+ card2Top.add(previewButton);
+ card2.add(card2Top, BorderLayout.NORTH);
+ // preview
+ _previewTable = new JTable(new MediaPreviewTableModel("dialog.correlate.select." + getMediaTypeKey() + "name"));
+ JScrollPane previewScrollPane = new JScrollPane(_previewTable);
+ previewScrollPane.setPreferredSize(new Dimension(300, 100));
+ card2.add(previewScrollPane, BorderLayout.CENTER);
+ return card2;
+ }
+
+
+ /**
+ * Go to the next or previous card in the stack
+ * @param increment 1 for next, -1 for previous card
+ */
+ private void showCard(int increment)
+ {
+ int currCard = _cards.getCurrentCardIndex();
+ int next = currCard + increment;
+ if (!isCardEnabled(next)) {
+ next += increment;
+ }
+ setupCard(next);
+ _backButton.setEnabled(next > 0 && (isCardEnabled(next-1) || isCardEnabled(next-2)));
+ _nextButton.setEnabled(next < (_cards.getNumCards()-1));
+ _cards.showCard(next);
+ }
+
+ /**
+ * @param inCardNum index of card
+ * @return true if specified card is enabled
+ */
+ private boolean isCardEnabled(int inCardNum)
+ {
+ if (_cardEnabled == null) {_cardEnabled = getCardEnabledFlags();}
+ return (inCardNum >= 0 && inCardNum < _cardEnabled.length && _cardEnabled[inCardNum]);
+ }
+
+ /**
+ * @return array of boolean flags denoting availability of cards
+ */
+ protected boolean[] getCardEnabledFlags()
+ {
+ // by default first is off and third is always on; second depends on selection table
+ return new boolean[] {false, makeSelectionTableModel().getRowCount() > 0, true};
+ }
+
+ /**
+ * Set up the specified card
+ * @param inCardNum index of card
+ */
+ protected void setupCard(int inCardNum)
+ {
+ _previewEnabled = false;
+ if (inCardNum == 1)
+ {
+ // set up photo selection card
+ MediaSelectionTableModel model = makeSelectionTableModel();
+ _selectionTable.setModel(model);
+ for (int i=0; i<model.getColumnCount(); i++) {
+ _selectionTable.getColumnModel().getColumn(i).setPreferredWidth(i==3?50:150);
+ }
+ // Calculate median time difference, select corresponding row of table
+ int preselectedIndex = model.getRowCount() < 3 ? 0 : getMedianIndex(model);
+ _selectionTable.getSelectionModel().setSelectionInterval(preselectedIndex, preselectedIndex);
+ _nextButton.requestFocus();
+ }
+ else if (inCardNum == 2)
+ {
+ // set up the options/preview card - first check for given time difference
+ TimeDifference timeDiff = null;
+ if (isCardEnabled(1))
+ {
+ int rowNum = _selectionTable.getSelectedRow();
+ if (rowNum < 0) {rowNum = 0;}
+ MediaSelectionTableRow selectedRow =
+ ((MediaSelectionTableModel) _selectionTable.getModel()).getRow(rowNum);
+ timeDiff = selectedRow.getTimeDiff();
+ }
+ setupPreviewCard(timeDiff, getMediaList().getMedia(0));
+ }
+ // enable ok button if any photos have been selected
+ _okButton.setEnabled(inCardNum == 2 && ((MediaPreviewTableModel) _previewTable.getModel()).hasAnySelected());
+ }
+
+ /**
+ * Parse the time limit values entered and validate them
+ * @return TimeDifference object describing limit
+ */
+ protected TimeDifference parseTimeLimit()
+ {
+ if (!_timeLimitRadio.isSelected()) {return null;}
+ int mins = getValue(_limitMinBox.getText());
+ _limitMinBox.setText("" + mins);
+ int secs = getValue(_limitSecBox.getText());
+ _limitSecBox.setText("" + secs);
+ if (mins <= 0 && secs <= 0) {return null;}
+ return new TimeDifference(0, mins, secs, true);
+ }
+
+ /**
+ * Parse the distance limit value entered and validate
+ * @return angular distance in radians
+ */
+ protected double parseDistanceLimit()
+ {
+ double value = -1.0;
+ if (_distLimitRadio.isSelected())
+ {
+ try {
+ value = Double.parseDouble(_limitDistBox.getText());
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ if (value <= 0.0) {
+ _limitDistBox.setText("0");
+ return -1.0;
+ }
+ _limitDistBox.setText("" + value);
+ return Distance.convertDistanceToRadians(value, getSelectedDistanceUnits());
+ }
+
+
+ /**
+ * @return the selected distance units from the dropdown
+ */
+ protected Distance.Units getSelectedDistanceUnits()
+ {
+ final Distance.Units[] distUnits = {Distance.Units.KILOMETRES, Distance.Units.METRES, Distance.Units.MILES};
+ return distUnits[_distUnitsDropdown.getSelectedIndex()];
+ }
+
+ /**
+ * Create a preview of the correlate action using the selected time difference
+ * @param inFromButton true if triggered from button press, false if automatic
+ */
+ public void createPreview(boolean inFromButton)
+ {
+ // Exit if still on first panel
+ if (!_previewEnabled) {return;}
+ // Create a TimeDifference based on the edit boxes
+ int numHours = getValue(_offsetHourBox.getText());
+ int numMins = getValue(_offsetMinBox.getText());
+ int numSecs = getValue(_offsetSecBox.getText());
+ boolean isPos = _mediaLaterOption.isSelected();
+ createPreview(new TimeDifference(numHours, numMins, numSecs, isPos), inFromButton);
+ }
+
+ /**
+ * Set up the final card using the given time difference and show it
+ * @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)
+ {
+ _previewEnabled = false;
+ TimeDifference timeDiff = inTimeDiff;
+ if (timeDiff == null)
+ {
+ // No time difference available, so calculate based on computer's time zone
+ Timestamp tstamp = null;
+ if (inFirstMedia != null) {
+ tstamp = inFirstMedia.getTimestamp();
+ }
+ timeDiff = getTimezoneOffset(tstamp);
+ }
+ // Use time difference to set edit boxes
+ _offsetHourBox.setText("" + timeDiff.getNumHours());
+ _offsetMinBox.setText("" + timeDiff.getNumMinutes());
+ _offsetSecBox.setText("" + timeDiff.getNumSeconds());
+ _mediaLaterOption.setSelected(timeDiff.getIsPositive());
+ _pointLaterOption.setSelected(!timeDiff.getIsPositive());
+ _previewEnabled = true;
+ createPreview(timeDiff, true);
+ }
+
+ /**
+ * Create a preview of the correlate action using the selected time difference
+ * @param inTimeDiff TimeDifference to use for preview
+ * @param inShowWarning true to show warning if all points out of range
+ */
+ protected abstract void createPreview(TimeDifference inTimeDiff, boolean inShowWarning);
+
+
+ /**
+ * Get the timestamp of the given media
+ * @param inMedia media object
+ * @return normally just returns the media timestamp, overridden by audio correlator
+ */
+ protected Timestamp getMediaTimestamp(MediaFile inMedia)
+ {
+ return inMedia.getTimestamp();
+ }
+
+ /**
+ * Get the point pair surrounding the given media item
+ * @param inTrack track object
+ * @param inMedia media object
+ * @param inOffset time offset to apply
+ * @return point pair resulting from correlation
+ */
+ protected PointMediaPair getPointPairForMedia(Track inTrack, MediaFile inMedia, TimeDifference inOffset)
+ {
+ PointMediaPair pair = new PointMediaPair(inMedia);
+ // Add/subtract offset to media timestamp
+ Timestamp mediaStamp = getMediaTimestamp(inMedia).createMinusOffset(inOffset);
+ int numPoints = inTrack.getNumPoints();
+ for (int i=0; i<numPoints; i++)
+ {
+ DataPoint point = inTrack.getPoint(i);
+ if (point.getPhoto() == null && point.getAudio() == null)
+ {
+ Timestamp pointStamp = point.getTimestamp();
+ if (pointStamp != null && pointStamp.isValid())
+ {
+ long numSeconds = pointStamp.getSecondsSince(mediaStamp);
+ pair.addPoint(point, numSeconds);
+ }
+ }
+ }
+ return pair;
+ }
+
+
+ /**
+ * Finish the correlation
+ */
+ protected abstract void finishCorrelation();
+
+ /**
+ * Construct an array of the point pairs to use for correlation
+ * @return array of PointMediaPair objects
+ */
+ protected PointMediaPair[] getPointPairs()
+ {
+ MediaPreviewTableModel model = (MediaPreviewTableModel) _previewTable.getModel();
+ int numMedia = model.getRowCount();
+ PointMediaPair[] pairs = new PointMediaPair[numMedia];
+ // Loop over items in preview table model
+ for (int i=0; i<numMedia; i++)
+ {
+ MediaPreviewTableRow row = model.getRow(i);
+ // add all selected pairs to array (other elements remain null)
+ if (row.getCorrelateFlag().booleanValue()) {
+ pairs[i] = row.getPointPair();
+ }
+ }
+ return pairs;
+ }
+}
import tim.prune.data.Distance;
/**
- * Class to act as table model for the photo preview table
+ * Class to act as the table model for the correlation preview table
*/
-public class PhotoPreviewTableModel extends AbstractTableModel
+public class MediaPreviewTableModel extends AbstractTableModel
{
+ /** Text for first column heading */
+ private String _firstColumnHeading = null;
/** ArrayList containing TableRow objects */
- private ArrayList<PhotoPreviewTableRow> _list = new ArrayList<PhotoPreviewTableRow>();
+ private ArrayList<MediaPreviewTableRow> _list = new ArrayList<MediaPreviewTableRow>();
/** Distance units */
private Distance.Units _distanceUnits = Distance.Units.KILOMETRES;
/** Number formatter */
FORMAT_ONE_DP.setMinimumFractionDigits(1);
}
+ /**
+ * Constructor
+ * @param inFirstColumnKey key for first column heading
+ */
+ public MediaPreviewTableModel(String inFirstColumnKey) {
+ _firstColumnHeading = I18nManager.getText(inFirstColumnKey);
+ }
/**
* @return the column count, always 5
*/
- public int getColumnCount()
- {
+ public int getColumnCount() {
return 5;
}
-
/**
* Get the name of the column
* @param inColNum column number
*/
public String getColumnName(int inColNum)
{
- if (inColNum == 0) return I18nManager.getText("dialog.correlate.photoselect.photoname");
+ if (inColNum == 0) return _firstColumnHeading;
else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp");
- else if (inColNum == 2) return I18nManager.getText("dialog.correlate.photoselect.timediff");
+ else if (inColNum == 2) return I18nManager.getText("dialog.correlate.select.timediff");
else if (inColNum == 3) return I18nManager.getText("fieldname.distance");
return I18nManager.getText("dialog.correlate.options.correlate");
}
* @param inRowIndex row index
* @return table row object
*/
- public PhotoPreviewTableRow getRow(int inRowIndex)
+ public MediaPreviewTableRow getRow(int inRowIndex)
{
return _list.get(inRowIndex);
}
*/
public Object getValueAt(int inRowIndex, int inColumnIndex)
{
- PhotoPreviewTableRow row = _list.get(inRowIndex);
- if (inColumnIndex == 0) return row.getPhoto().getFile().getName();
+ MediaPreviewTableRow row = _list.get(inRowIndex);
+ if (inColumnIndex == 0) return row.getMedia().getFile().getName();
else if (inColumnIndex == 1) {
- return row.getPhoto().getTimestamp().getText();
+ return row.getMedia().getTimestamp().getText();
}
else if (inColumnIndex == 2) {
if (row.getPointPair().isValid()) {
/**
- * Add a photo to the list
+ * Add a row to the list
* @param inRow row to add
*/
- public void addPhotoRow(PhotoPreviewTableRow inRow)
+ public void addRow(MediaPreviewTableRow inRow)
{
_list.add(inRow);
}
/**
* @return true if any of the correlate flags are on
*/
- public boolean hasPhotosSelected()
+ public boolean hasAnySelected()
{
for (int i=0; i<getRowCount(); i++)
{
- if (getRow(i).getCorrelateFlag().booleanValue())
- {
+ if (getRow(i).getCorrelateFlag().booleanValue()) {
return true;
}
}
// can only edit the correlate column
if (inColumnIndex == 4)
{
- PhotoPreviewTableRow row = getRow(inRowIndex);
- // Don't allow setting of photos which can't be correlated
- if (row.getPointPair().isValid())
- {
+ MediaPreviewTableRow row = getRow(inRowIndex);
+ // Don't allow setting of items which can't be correlated
+ if (row.getPointPair().isValid()) {
row.setCorrelateFlag(((Boolean) inValue).booleanValue());
}
}
import tim.prune.data.Distance;
/**
- * Class to hold contents of a single row
- * in the photo preview table
+ * Class to hold the contents of a single row in the correlation preview table
*/
-public class PhotoPreviewTableRow extends PhotoSelectionTableRow
+public class MediaPreviewTableRow extends MediaSelectionTableRow
{
- private PointPair _pointPair = null;
+ private PointMediaPair _pointPair = null;
private double _distance = 0.0;
private int _status = 0;
private boolean _correlate = false;
* Constructor
* @param inPointPair point pair object
*/
- public PhotoPreviewTableRow(PointPair inPointPair)
+ public MediaPreviewTableRow(PointMediaPair inPointPair)
{
- super(inPointPair.getPhoto(), inPointPair.getMinSeconds());
+ super(inPointPair.getMedia(), inPointPair.getMinSeconds());
_pointPair = inPointPair;
_distance = inPointPair.getMinRadians();
_status = 0;
- _correlate = (inPointPair.getPhoto().getDataPoint() == null);
+ _correlate = (inPointPair.getMedia().getDataPoint() == null);
}
/**
/**
* @return point pair object
*/
- public PointPair getPointPair()
+ public PointMediaPair getPointPair()
{
return _pointPair;
}
--- /dev/null
+package tim.prune.correlate;
+
+import java.util.ArrayList;
+import javax.swing.table.AbstractTableModel;
+import tim.prune.I18nManager;
+import tim.prune.data.MediaFile;
+
+
+/**
+ * Class to act as the table model for the selection table in the correlation functions.
+ * Can be used by both photo correlation and audio correlation
+ */
+public class MediaSelectionTableModel extends AbstractTableModel
+{
+ /** Text for first column heading */
+ private String _firstColumnHeading = null;
+ /** Text for last column heading */
+ private String _lastColumnHeading = null;
+ /** List of rows */
+ private ArrayList<MediaSelectionTableRow> _list = new ArrayList<MediaSelectionTableRow>();
+
+
+ /**
+ * Constructor
+ * @param inFirstColumnKey key for first column heading
+ * @param inLastColumnKey key for last column heading
+ */
+ public MediaSelectionTableModel(String inFirstColumnKey, String inLastColumnKey)
+ {
+ _firstColumnHeading = I18nManager.getText(inFirstColumnKey);
+ _lastColumnHeading = I18nManager.getText(inLastColumnKey);
+ }
+
+ /**
+ * @return the column count, always 4
+ */
+ public int getColumnCount() {
+ return 4;
+ }
+
+ /**
+ * Get the name of the column
+ * @param inColNum column number
+ * @return column name
+ */
+ public String getColumnName(int inColNum)
+ {
+ if (inColNum == 0) return _firstColumnHeading;
+ else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp");
+ else if (inColNum == 2) return I18nManager.getText("dialog.correlate.select.timediff");
+ return _lastColumnHeading;
+ }
+
+
+ /**
+ * @return the row count
+ */
+ public int getRowCount()
+ {
+ return _list.size();
+ }
+
+
+ /**
+ * Get the selected row from the table
+ * @param inRowIndex row index
+ * @return table row object
+ */
+ public MediaSelectionTableRow getRow(int inRowIndex)
+ {
+ return _list.get(inRowIndex);
+ }
+
+
+ /**
+ * Get the value of the specified cell
+ * @param inRowIndex row index
+ * @param inColumnIndex column index
+ * @return value of specified cell
+ */
+ public Object getValueAt(int inRowIndex, int inColumnIndex)
+ {
+ // MAYBE: only show time of photos (not date) if dates all identical
+ MediaSelectionTableRow row = _list.get(inRowIndex);
+ if (inColumnIndex == 0) return row.getMedia().getFile().getName();
+ else if (inColumnIndex == 1) return row.getMedia().getTimestamp().getText();
+ else if (inColumnIndex == 2) return row.getTimeDiff().getDescription();
+ return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") :
+ I18nManager.getText("dialog.about.no"));
+ }
+
+
+ /**
+ * Clear the list
+ */
+ public void reset() {
+ _list.clear();
+ }
+
+ /**
+ * Add a media object to the list
+ * @param inMedia item to add
+ * @param inTimeDiff time difference
+ */
+ public void addMedia(MediaFile inMedia, long inTimeDiff)
+ {
+ _list.add(new MediaSelectionTableRow(inMedia, inTimeDiff));
+ }
+}
--- /dev/null
+package tim.prune.correlate;
+
+import tim.prune.data.MediaFile;
+import tim.prune.data.TimeDifference;
+
+/**
+ * Class to hold the contents of a single row
+ * in the selection table for correlation
+ */
+public class MediaSelectionTableRow
+{
+ private MediaFile _media = null;
+ private TimeDifference _timeDiff = null;
+
+ /**
+ * Constructor
+ * @param inMedia media item
+ * @param inNumSecs number of seconds time difference as long
+ */
+ public MediaSelectionTableRow(MediaFile inMedia, long inNumSecs)
+ {
+ _media = inMedia;
+ _timeDiff = new TimeDifference(inNumSecs);
+ }
+
+ /**
+ * @return Media object
+ */
+ public MediaFile getMedia() {
+ return _media;
+ }
+
+ /**
+ * @return time difference
+ */
+ public TimeDifference getTimeDiff() {
+ return _timeDiff;
+ }
+}
import java.awt.event.KeyListener;
/**
- * Helper class to listen for changed options on the PhotoCorrelator
+ * Helper class to listen for changed options on the Correlators
* Tightly coupled but only to ok button and preview function
*/
public class OptionsChangedListener implements KeyListener, ActionListener, ItemListener, Runnable
{
/** Correlator object for callbacks */
- private PhotoCorrelator _correlator;
+ private Correlator _correlator;
/** Thread counter */
private int _threadCount = 0;
* Constructor
* @param inCorrelator correlator object for callbacks
*/
- public OptionsChangedListener(PhotoCorrelator inCorrelator)
+ public OptionsChangedListener(Correlator inCorrelator)
{
_correlator = inCorrelator;
}
package tim.prune.correlate;
-import java.awt.BorderLayout;
-import java.awt.CardLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.util.Calendar;
-import java.util.Iterator;
-import java.util.TreeSet;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JComboBox;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JScrollPane;
import javax.swing.JTable;
-import javax.swing.JTextField;
import tim.prune.App;
-import tim.prune.GenericFunction;
+import tim.prune.DataSubscriber;
import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
import tim.prune.data.DataPoint;
-import tim.prune.data.Distance;
-import tim.prune.data.Field;
+import tim.prune.data.MediaList;
import tim.prune.data.Photo;
import tim.prune.data.PhotoList;
import tim.prune.data.TimeDifference;
-import tim.prune.data.Timestamp;
-import tim.prune.data.Track;
-import tim.prune.data.TrackInfo;
import tim.prune.undo.UndoCorrelatePhotos;
/**
* Class to manage the automatic correlation of photos to points
* including the GUI stuff to control the correlation options
*/
-public class PhotoCorrelator extends GenericFunction
+public class PhotoCorrelator extends Correlator
{
- private JDialog _dialog;
- private JButton _nextButton = null, _backButton = null;
- private JButton _okButton = null;
- private JPanel _cards = null;
- private JTable _photoSelectionTable = null;
- private JLabel _tipLabel = null;
- private JTextField _offsetHourBox = null, _offsetMinBox = null, _offsetSecBox = null;
- private JRadioButton _photoLaterOption = null, _pointLaterOption = null;
- private JRadioButton _timeLimitRadio = null, _distLimitRadio = null;
- private JTextField _limitMinBox = null, _limitSecBox = null;
- private JTextField _limitDistBox = null;
- private JComboBox _distUnitsDropdown = null;
- private JTable _previewTable = null;
- private boolean _firstTabAvailable = false;
- private boolean _previewEnabled = false; // flag required to enable preview function on second panel
-
-
/**
* Constructor
* @param inApp App object to report actions to
*/
- public PhotoCorrelator(App inApp)
- {
+ public PhotoCorrelator(App inApp) {
super(inApp);
}
return "function.correlatephotos";
}
- /**
- * Reset dialog and show it
- */
- public void begin()
- {
- // First create dialog if necessary
- if (_dialog == null)
- {
- _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
- _dialog.setLocationRelativeTo(_parentFrame);
- _dialog.getContentPane().add(makeDialogContents());
- _dialog.pack();
- }
- // Check whether track has timestamps, exit if not
- if (!_app.getTrackInfo().getTrack().hasData(Field.TIMESTAMP))
- {
- JOptionPane.showMessageDialog(_parentFrame, I18nManager.getText("dialog.correlate.notimestamps"),
- I18nManager.getText(getNameKey()), JOptionPane.INFORMATION_MESSAGE);
- return;
- }
- // Check for any non-correlated photos, show warning continue/cancel
- if (!trackHasUncorrelatedPhotos())
- {
- Object[] buttonTexts = {I18nManager.getText("button.continue"), I18nManager.getText("button.cancel")};
- if (JOptionPane.showOptionDialog(_parentFrame, I18nManager.getText("dialog.correlate.nouncorrelatedphotos"),
- I18nManager.getText(getNameKey()), JOptionPane.YES_NO_OPTION,
- JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
- == JOptionPane.NO_OPTION)
- {
- return;
- }
- }
- PhotoSelectionTableModel model = makePhotoSelectionTableModel(_app.getTrackInfo());
- _firstTabAvailable = model != null && model.getRowCount() > 0;
- CardLayout cl = (CardLayout) _cards.getLayout();
- if (_firstTabAvailable)
- {
- cl.first(_cards);
- _nextButton.setEnabled(true);
- _backButton.setEnabled(false);
- _tipLabel.setVisible(false);
- _photoSelectionTable.setModel(model);
- _previewEnabled = false;
- for (int i=0; i<model.getColumnCount(); i++) {
- _photoSelectionTable.getColumnModel().getColumn(i).setPreferredWidth(i==3?50:150);
- }
- // Calculate median time difference, select corresponding row of table
- int preselectedIndex = model.getRowCount() < 3 ? 0 : getMedianIndex(model);
- _photoSelectionTable.getSelectionModel().setSelectionInterval(preselectedIndex, preselectedIndex);
- _nextButton.requestFocus();
- }
- else
- {
- _tipLabel.setVisible(true);
- setupSecondCard(null);
- }
- _dialog.setVisible(true);
+ /** @return type key */
+ protected String getMediaTypeKey() {
+ return "photo";
}
-
- /**
- * Make contents of correlate dialog
- * @return JPanel containing gui elements
- */
- private JPanel makeDialogContents()
- {
- JPanel mainPanel = new JPanel();
- mainPanel.setLayout(new BorderLayout());
- // Card panel in the middle
- _cards = new JPanel();
- _cards.setLayout(new CardLayout());
-
- // First panel for photo selection table
- JPanel card1 = new JPanel();
- card1.setLayout(new BorderLayout(10, 10));
- card1.add(new JLabel(I18nManager.getText("dialog.correlate.photoselect.intro")), BorderLayout.NORTH);
- _photoSelectionTable = new JTable();
- JScrollPane photoScrollPane = new JScrollPane(_photoSelectionTable);
- photoScrollPane.setPreferredSize(new Dimension(400, 100));
- card1.add(photoScrollPane, BorderLayout.CENTER);
- _cards.add(card1, "card1");
-
- OptionsChangedListener optionsChangedListener = new OptionsChangedListener(this);
- // Second panel for options
- JPanel card2 = new JPanel();
- card2.setLayout(new BorderLayout());
- JPanel card2Top = new JPanel();
- card2Top.setLayout(new BoxLayout(card2Top, BoxLayout.Y_AXIS));
- _tipLabel = new JLabel(I18nManager.getText("dialog.correlate.options.tip"));
- _tipLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
- card2Top.add(_tipLabel);
- JLabel introLabel = new JLabel(I18nManager.getText("dialog.correlate.options.intro"));
- introLabel.setBorder(BorderFactory.createEmptyBorder(8, 6, 5, 6));
- card2Top.add(introLabel);
- // time offset section
- JPanel offsetPanel = new JPanel();
- offsetPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.offsetpanel")));
- offsetPanel.setLayout(new BoxLayout(offsetPanel, BoxLayout.Y_AXIS));
- JPanel offsetPanelTop = new JPanel();
- offsetPanelTop.setLayout(new FlowLayout());
- offsetPanelTop.setBorder(null);
- offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset") + ": "));
- _offsetHourBox = new JTextField(3);
- _offsetHourBox.addKeyListener(optionsChangedListener);
- offsetPanelTop.add(_offsetHourBox);
- offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.hours")));
- _offsetMinBox = new JTextField(3);
- _offsetMinBox.addKeyListener(optionsChangedListener);
- offsetPanelTop.add(_offsetMinBox);
- offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes")));
- _offsetSecBox = new JTextField(3);
- _offsetSecBox.addKeyListener(optionsChangedListener);
- offsetPanelTop.add(_offsetSecBox);
- offsetPanelTop.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds")));
- offsetPanel.add(offsetPanelTop);
-
- // radio buttons for photo / point later
- JPanel offsetPanelBot = new JPanel();
- offsetPanelBot.setLayout(new FlowLayout());
- offsetPanelBot.setBorder(null);
- _photoLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.photolater"));
- _pointLaterOption = new JRadioButton(I18nManager.getText("dialog.correlate.options.pointlater"));
- _photoLaterOption.addItemListener(optionsChangedListener);
- _pointLaterOption.addItemListener(optionsChangedListener);
- ButtonGroup laterGroup = new ButtonGroup();
- laterGroup.add(_photoLaterOption);
- laterGroup.add(_pointLaterOption);
- offsetPanelBot.add(_photoLaterOption);
- offsetPanelBot.add(_pointLaterOption);
- offsetPanel.add(offsetPanelBot);
- offsetPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
- card2Top.add(offsetPanel);
-
- // time limits section
- JPanel limitsPanel = new JPanel();
- limitsPanel.setBorder(BorderFactory.createTitledBorder(I18nManager.getText("dialog.correlate.options.limitspanel")));
- limitsPanel.setLayout(new BoxLayout(limitsPanel, BoxLayout.Y_AXIS));
- JPanel timeLimitPanel = new JPanel();
- timeLimitPanel.setLayout(new FlowLayout());
- JRadioButton noTimeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.notimelimit"));
- noTimeLimitRadio.addItemListener(optionsChangedListener);
- timeLimitPanel.add(noTimeLimitRadio);
- _timeLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.timelimit") + " : ");
- _timeLimitRadio.addItemListener(optionsChangedListener);
- timeLimitPanel.add(_timeLimitRadio);
- groupRadioButtons(noTimeLimitRadio, _timeLimitRadio);
- _limitMinBox = new JTextField(3);
- _limitMinBox.addKeyListener(optionsChangedListener);
- timeLimitPanel.add(_limitMinBox);
- timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.minutes")));
- _limitSecBox = new JTextField(3);
- _limitSecBox.addKeyListener(optionsChangedListener);
- timeLimitPanel.add(_limitSecBox);
- timeLimitPanel.add(new JLabel(I18nManager.getText("dialog.correlate.options.offset.seconds")));
- limitsPanel.add(timeLimitPanel);
- // distance limits
- JPanel distLimitPanel = new JPanel();
- distLimitPanel.setLayout(new FlowLayout());
- JRadioButton noDistLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.nodistancelimit"));
- noDistLimitRadio.addItemListener(optionsChangedListener);
- distLimitPanel.add(noDistLimitRadio);
- _distLimitRadio = new JRadioButton(I18nManager.getText("dialog.correlate.options.distancelimit"));
- _distLimitRadio.addItemListener(optionsChangedListener);
- distLimitPanel.add(_distLimitRadio);
- groupRadioButtons(noDistLimitRadio, _distLimitRadio);
- _limitDistBox = new JTextField(4);
- _limitDistBox.addKeyListener(optionsChangedListener);
- distLimitPanel.add(_limitDistBox);
- String[] distUnitsOptions = {I18nManager.getText("units.kilometres"), I18nManager.getText("units.metres"),
- I18nManager.getText("units.miles")};
- _distUnitsDropdown = new JComboBox(distUnitsOptions);
- _distUnitsDropdown.addItemListener(optionsChangedListener);
- distLimitPanel.add(_distUnitsDropdown);
- limitsPanel.add(distLimitPanel);
- limitsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
- card2Top.add(limitsPanel);
-
- // preview button
- JButton previewButton = new JButton(I18nManager.getText("button.preview"));
- previewButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- createPreview(true);
- }
- });
- card2Top.add(previewButton);
- card2.add(card2Top, BorderLayout.NORTH);
- // preview
- _previewTable = new JTable(new PhotoPreviewTableModel());
- JScrollPane previewScrollPane = new JScrollPane(_previewTable);
- previewScrollPane.setPreferredSize(new Dimension(300, 100));
- card2.add(previewScrollPane, BorderLayout.CENTER);
- _cards.add(card2, "card2");
- mainPanel.add(_cards, BorderLayout.CENTER);
-
- // Button panel at the bottom
- JPanel buttonPanel = new JPanel();
- _backButton = new JButton(I18nManager.getText("button.back"));
- _backButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- CardLayout cl = (CardLayout) _cards.getLayout();
- cl.previous(_cards);
- _backButton.setEnabled(false);
- _nextButton.setEnabled(true);
- _okButton.setEnabled(false);
- _previewEnabled = false;
- }
- });
- _backButton.setEnabled(false);
- buttonPanel.add(_backButton);
- _nextButton = new JButton(I18nManager.getText("button.next"));
- _nextButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- int rowNum = _photoSelectionTable.getSelectedRow();
- if (rowNum < 0) {rowNum = 0;}
- PhotoSelectionTableRow selectedRow = ((PhotoSelectionTableModel) _photoSelectionTable.getModel())
- .getRow(rowNum);
- setupSecondCard(selectedRow.getTimeDiff());
- }
- });
- buttonPanel.add(_nextButton);
- _okButton = new JButton(I18nManager.getText("button.ok"));
- _okButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- finishCorrelation();
- _dialog.dispose();
- }
- });
- _okButton.setEnabled(false);
- buttonPanel.add(_okButton);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- _dialog.dispose();
- }
- });
- buttonPanel.add(cancelButton);
- mainPanel.add(buttonPanel, BorderLayout.SOUTH);
- return mainPanel;
- }
-
-
- /**
- * Construct a table model for the photo selection table
- * @param inTrackInfo track info object
- * @return table model
- */
- private static PhotoSelectionTableModel makePhotoSelectionTableModel(TrackInfo inTrackInfo)
- {
- PhotoSelectionTableModel model = new PhotoSelectionTableModel();
- int numPhotos = inTrackInfo.getPhotoList().getNumPhotos();
- for (int i=0; i<numPhotos; i++)
- {
- Photo photo = inTrackInfo.getPhotoList().getPhoto(i);
- // For working out time differences, can't use photos which already had point information
- if (photo.getDataPoint() != null && photo.getDataPoint().hasTimestamp()
- && photo.getOriginalStatus() == Photo.Status.NOT_CONNECTED)
- {
- // Calculate time difference, add to table model
- long timeDiff = photo.getTimestamp().getSecondsSince(photo.getDataPoint().getTimestamp());
- model.addPhoto(photo, timeDiff);
- }
- }
- return model;
- }
-
-
- /**
- * Group the two radio buttons together with a ButtonGroup
- * @param inButton1 first radio button
- * @param inButton2 second radio button
- */
- private static void groupRadioButtons(JRadioButton inButton1, JRadioButton inButton2)
- {
- ButtonGroup buttonGroup = new ButtonGroup();
- buttonGroup.add(inButton1);
- buttonGroup.add(inButton2);
- inButton1.setSelected(true);
+ /** @return photo list*/
+ protected MediaList getMediaList() {
+ return _app.getTrackInfo().getPhotoList();
}
-
- /**
- * Set up the second card using the given time difference and show it
- * @param inTimeDiff time difference to use for photo time offsets
- */
- private void setupSecondCard(TimeDifference inTimeDiff)
- {
- _previewEnabled = false;
- boolean hasTimeDiff = inTimeDiff != null;
- if (!hasTimeDiff)
- {
- // No time difference available, so calculate based on computer's time zone
- inTimeDiff = getTimezoneOffset();
- }
- // Use time difference to set edit boxes
- _offsetHourBox.setText("" + inTimeDiff.getNumHours());
- _offsetMinBox.setText("" + inTimeDiff.getNumMinutes());
- _offsetSecBox.setText("" + inTimeDiff.getNumSeconds());
- _photoLaterOption.setSelected(inTimeDiff.getIsPositive());
- _pointLaterOption.setSelected(!inTimeDiff.getIsPositive());
- createPreview(inTimeDiff, true);
- CardLayout cl = (CardLayout) _cards.getLayout();
- cl.last(_cards);
- _backButton.setEnabled(hasTimeDiff);
- _nextButton.setEnabled(false);
- // enable ok button if any photos have been selected
- _okButton.setEnabled(((PhotoPreviewTableModel) _previewTable.getModel()).hasPhotosSelected());
- _previewEnabled = true;
- }
-
-
- /**
- * Create a preview of the correlate action using the selected time difference
- * @param inFromButton true if triggered from button press, false if automatic
- */
- public void createPreview(boolean inFromButton)
- {
- // Exit if still on first panel
- if (!_previewEnabled) {return;}
- // Create a TimeDifference based on the edit boxes
- int numHours = getValue(_offsetHourBox.getText());
- int numMins = getValue(_offsetMinBox.getText());
- int numSecs = getValue(_offsetSecBox.getText());
- boolean isPos = _photoLaterOption.isSelected();
- createPreview(new TimeDifference(numHours, numMins, numSecs, isPos), inFromButton);
- }
-
-
/**
* Create a preview of the correlate action using the selected time difference
* @param inTimeDiff TimeDifference to use for preview
* @param inShowWarning true to show warning if all points out of range
*/
- private void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
+ protected void createPreview(TimeDifference inTimeDiff, boolean inShowWarning)
{
TimeDifference timeLimit = parseTimeLimit();
double angDistLimit = parseDistanceLimit();
- PhotoPreviewTableModel model = new PhotoPreviewTableModel();
+ MediaPreviewTableModel model = new MediaPreviewTableModel("dialog.correlate.select.photoname");
PhotoList photos = _app.getTrackInfo().getPhotoList();
// Loop through photos deciding whether to set correlate flag or not
int numPhotos = photos.getNumPhotos();
for (int i=0; i<numPhotos; i++)
{
Photo photo = photos.getPhoto(i);
- PointPair pair = getPointPairForPhoto(_app.getTrackInfo().getTrack(), photo, inTimeDiff);
- PhotoPreviewTableRow row = new PhotoPreviewTableRow(pair);
+ PointMediaPair pair = getPointPairForMedia(_app.getTrackInfo().getTrack(), photo, inTimeDiff);
+ MediaPreviewTableRow row = new MediaPreviewTableRow(pair);
// Don't try to correlate photos which don't have points either side
boolean correlatePhoto = pair.isValid();
// Don't select photos which already have a point
correlatePhoto = false;
}
row.setCorrelateFlag(correlatePhoto);
- model.addPhotoRow(row);
+ model.addRow(row);
}
_previewTable.setModel(model);
// Set distance units
_previewTable.getColumnModel().getColumn(i).setPreferredWidth(colWidths[i]);
}
// check if any photos found
- _okButton.setEnabled(model.hasPhotosSelected());
- if (inShowWarning && !model.hasPhotosSelected())
+ _okButton.setEnabled(model.hasAnySelected());
+ if (inShowWarning && !model.hasAnySelected())
{
JOptionPane.showMessageDialog(_dialog, I18nManager.getText("dialog.correlate.alloutsiderange"),
I18nManager.getText(getNameKey()), JOptionPane.ERROR_MESSAGE);
}
}
- /**
- * Parse the time limit values entered and validate them
- * @return TimeDifference object describing limit
- */
- private TimeDifference parseTimeLimit()
- {
- if (!_timeLimitRadio.isSelected()) {return null;}
- int mins = getValue(_limitMinBox.getText());
- _limitMinBox.setText("" + mins);
- int secs = getValue(_limitSecBox.getText());
- _limitSecBox.setText("" + secs);
- if (mins <= 0 && secs <= 0) {return null;}
- return new TimeDifference(0, mins, secs, true);
- }
-
- /**
- * Parse the distance limit value entered and validate
- * @return angular distance in radians
- */
- private double parseDistanceLimit()
- {
- double value = -1.0;
- if (_distLimitRadio.isSelected())
- {
- try
- {
- value = Double.parseDouble(_limitDistBox.getText());
- }
- catch (NumberFormatException nfe) {}
- }
- if (value <= 0.0) {
- _limitDistBox.setText("0");
- return -1.0;
- }
- _limitDistBox.setText("" + value);
- return Distance.convertDistanceToRadians(value, getSelectedDistanceUnits());
- }
-
-
- /**
- * @return the selected distance units from the dropdown
- */
- private Distance.Units getSelectedDistanceUnits()
- {
- final Distance.Units[] distUnits = {Distance.Units.KILOMETRES, Distance.Units.METRES, Distance.Units.MILES};
- return distUnits[_distUnitsDropdown.getSelectedIndex()];
- }
-
-
- /**
- * Try to parse the given string
- * @param inText String to parse
- * @return value if parseable, 0 otherwise
- */
- private static int getValue(String inText)
- {
- int value = 0;
- try {
- value = Integer.parseInt(inText);
- }
- catch (NumberFormatException nfe) {}
- return value;
- }
-
-
- /**
- * Get the point pair surrounding the given photo
- * @param inTrack track object
- * @param inPhoto photo object
- * @param inOffset time offset to apply to photos
- * @return point pair resulting from correlation
- */
- private static PointPair getPointPairForPhoto(Track inTrack, Photo inPhoto, TimeDifference inOffset)
- {
- PointPair pair = new PointPair(inPhoto);
- // Add/subtract offet to photo timestamp
- Timestamp photoStamp = inPhoto.getTimestamp().createMinusOffset(inOffset);
- int numPoints = inTrack.getNumPoints();
- for (int i=0; i<numPoints; i++)
- {
- DataPoint point = inTrack.getPoint(i);
- if (point.getPhoto() == null || point.getPhoto().getCurrentStatus() != Photo.Status.TAGGED)
- {
- Timestamp pointStamp = point.getTimestamp();
- if (pointStamp != null && pointStamp.isValid())
- {
- long numSeconds = pointStamp.getSecondsSince(photoStamp);
- pair.addPoint(point, numSeconds);
- }
- }
- }
- return pair;
- }
-
-
- /**
- * Construct an array of the point pairs to use for correlation
- * @return array of PointPair objects
- */
- private PointPair[] getPointPairs()
- {
- PhotoPreviewTableModel model = (PhotoPreviewTableModel) _previewTable.getModel();
- int numPhotos = model.getRowCount();
- PointPair[] pairs = new PointPair[numPhotos];
- // Loop over photos in preview table model
- for (int i=0; i<numPhotos; i++)
- {
- PhotoPreviewTableRow row = model.getRow(i);
- // add all selected pairs to array (other elements remain null)
- if (row.getCorrelateFlag().booleanValue())
- {
- pairs[i] = row.getPointPair();
- }
- }
- return pairs;
- }
-
- /**
- * @return time difference of local time zone from UTC when the first photo was taken
- */
- private TimeDifference getTimezoneOffset()
- {
- Calendar cal = null;
- // Base time difference on DST when first photo was taken
- Photo firstPhoto = _app.getTrackInfo().getPhotoList().getPhoto(0);
- if (firstPhoto != null && firstPhoto.getTimestamp() != null) {
- cal = firstPhoto.getTimestamp().getCalendar();
- }
- else {
- // No photo or no timestamp, just use current time
- cal = Calendar.getInstance();
- }
- // Both time zone offset and dst offset are based on milliseconds, so convert to seconds
- TimeDifference timeDiff = new TimeDifference((cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / 1000);
- return timeDiff;
- }
-
-
- /**
- * Calculate the median index to select from the table
- * @param inModel table model
- * @return index of entry to select from table
- */
- private static int getMedianIndex(PhotoSelectionTableModel inModel)
- {
- // make sortable list
- TreeSet<TimeIndexPair> set = new TreeSet<TimeIndexPair>();
- // loop through rows of table adding to list
- int numRows = inModel.getRowCount();
- int i;
- for (i=0; i<numRows; i++)
- {
- PhotoSelectionTableRow row = inModel.getRow(i);
- set.add(new TimeIndexPair(row.getTimeDiff().getTotalSeconds(), i));
- }
- // pull out middle entry and return index
- TimeIndexPair pair = null;
- Iterator<TimeIndexPair> iterator = set.iterator();
- for (i=0; i<(numRows+1)/2; i++)
- {
- pair = iterator.next();
- }
- return pair.getIndex();
- }
-
-
- /**
- * Disable the ok button
- */
- public void disableOkButton()
- {
- if (_okButton != null) {
- _okButton.setEnabled(false);
- }
- }
-
-
- /**
- * Check if the track has any uncorrelated photos
- * @return true if there are any photos which are not connected to points
- */
- private boolean trackHasUncorrelatedPhotos()
- {
- PhotoList photoList = _app.getTrackInfo().getPhotoList();
- int numPhotos = photoList.getNumPhotos();
- // loop over photos
- for (int i=0; i<numPhotos; i++)
- {
- Photo photo = photoList.getPhoto(i);
- if (photo != null && photo.getDataPoint() == null) {
- return true;
- }
- }
- // no uncorrelated photos found
- return false;
- }
/**
* Finish the correlation by modifying the track
* and passing the Undo information back to the App
*/
- private void finishCorrelation()
+ protected void finishCorrelation()
{
- PointPair[] pointPairs = getPointPairs();
+ PointMediaPair[] pointPairs = getPointPairs();
if (pointPairs == null || pointPairs.length <= 0) {return;}
// begin to construct undo information
int arraySize = pointPairs.length;
int i = 0, numPhotos = 0;
int numPointsToCreate = 0;
- PointPair pair = null;
+ PointMediaPair pair = null;
for (i=0; i<arraySize; i++)
{
pair = pointPairs[i];
if (pointPhoto == null)
{
// photo coincides with photoless point so connect the two
- pair.getPointBefore().setPhoto(pair.getPhoto());
- pair.getPhoto().setDataPoint(pair.getPointBefore());
+ pair.getPointBefore().setPhoto((Photo) pair.getMedia());
+ pair.getMedia().setDataPoint(pair.getPointBefore());
}
- else if (pointPhoto.equals(pair.getPhoto())) {
+ else if (pointPhoto.equals(pair.getMedia())) {
// photo is already connected, nothing to do
}
else {
{
pointToAdd = null;
if (pair.getMinSeconds() == 0L && pair.getPointBefore().getPhoto() != null
- && !pair.getPointBefore().getPhoto().equals(pair.getPhoto()))
+ && !pair.getPointBefore().getPhoto().equals(pair.getMedia()))
{
// clone point
pointToAdd = pair.getPointBefore().clonePoint();
if (pointToAdd != null)
{
// link photo to point
- pointToAdd.setPhoto(pair.getPhoto());
- pair.getPhoto().setDataPoint(pointToAdd);
+ pointToAdd.setPhoto((Photo) pair.getMedia());
+ pair.getMedia().setDataPoint(pointToAdd);
// set to start of segment so not joined in track
pointToAdd.setSegmentStart(true);
// add to point array
// send undo information back to controlling app
undo.setNumPhotosCorrelated(numPhotos);
_app.completeFunction(undo, ("" + numPhotos + " "
- + (numPhotos==1?I18nManager.getText("confirm.correlate.single"):I18nManager.getText("confirm.correlate.multi"))));
- // observers already informed by track update
+ + (numPhotos==1?I18nManager.getText("confirm.correlatephotos.single"):I18nManager.getText("confirm.correlatephotos.multi"))));
+ // observers already informed by track update if new points created
+ if (numPointsToCreate == 0) {
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ }
}
}
+++ /dev/null
-package tim.prune.correlate;
-
-import java.util.ArrayList;
-import javax.swing.table.AbstractTableModel;
-
-import tim.prune.I18nManager;
-import tim.prune.data.Photo;
-
-/**
- * Class to act as table model for the photo selection table
- */
-public class PhotoSelectionTableModel extends AbstractTableModel
-{
- private ArrayList<PhotoSelectionTableRow> _list = new ArrayList<PhotoSelectionTableRow>();
-
-
- /**
- * @return the column count, always 4
- */
- public int getColumnCount()
- {
- return 4;
- }
-
-
- /**
- * Get the name of the column
- * @param inColNum column number
- * @return column name
- */
- public String getColumnName(int inColNum)
- {
- if (inColNum == 0) return I18nManager.getText("dialog.correlate.photoselect.photoname");
- else if (inColNum == 1) return I18nManager.getText("fieldname.timestamp");
- else if (inColNum == 2) return I18nManager.getText("dialog.correlate.photoselect.timediff");
- return I18nManager.getText("dialog.correlate.photoselect.photolater");
- }
-
-
- /**
- * @return the row count
- */
- public int getRowCount()
- {
- return _list.size();
- }
-
-
- /**
- * Get the selected row from the table
- * @param inRowIndex row index
- * @return table row object
- */
- public PhotoSelectionTableRow getRow(int inRowIndex)
- {
- return _list.get(inRowIndex);
- }
-
-
- /**
- * Get the value of the specified cell
- * @param inRowIndex row index
- * @param inColumnIndex column index
- * @return value of specified cell
- */
- public Object getValueAt(int inRowIndex, int inColumnIndex)
- {
- // MAYBE: only show time of photos (not date) if dates all identical
- PhotoSelectionTableRow row = _list.get(inRowIndex);
- if (inColumnIndex == 0) return row.getPhoto().getFile().getName();
- else if (inColumnIndex == 1) return row.getPhoto().getTimestamp().getText();
- else if (inColumnIndex == 2) return row.getTimeDiff().getDescription();
- return (row.getTimeDiff().getIsPositive() ? I18nManager.getText("dialog.about.yes") :
- I18nManager.getText("dialog.about.no"));
- }
-
-
- /**
- * Clear the list
- */
- public void reset()
- {
- _list.clear();
- }
-
- /**
- * Add a photo to the list
- * @param inPhoto photo to add
- * @param inTimeDiff time difference
- */
- public void addPhoto(Photo inPhoto, long inTimeDiff)
- {
- _list.add(new PhotoSelectionTableRow(inPhoto, inTimeDiff));
- }
-}
+++ /dev/null
-package tim.prune.correlate;
-
-import tim.prune.data.Photo;
-import tim.prune.data.TimeDifference;
-
-/**
- * Class to hold contents of a single row
- * in the photo selection table
- */
-public class PhotoSelectionTableRow
-{
- private Photo _photo = null;
- private TimeDifference _timeDiff = null;
-
- /**
- * Constructor
- * @param inPhoto Photo object
- * @param inNumSecs number of seconds time difference as long
- */
- public PhotoSelectionTableRow(Photo inPhoto, long inNumSecs)
- {
- _photo = inPhoto;
- _timeDiff = new TimeDifference(inNumSecs);
- }
-
- /**
- * @return Photo object
- */
- public Photo getPhoto()
- {
- return _photo;
- }
-
- /**
- * @return time difference
- */
- public TimeDifference getTimeDiff()
- {
- return _timeDiff;
- }
-}
package tim.prune.correlate;
import tim.prune.data.DataPoint;
-import tim.prune.data.Photo;
+import tim.prune.data.MediaFile;
/**
- * Class to hold a pair of points
- * used to hold the result of correlation of a photo
+ * Class to hold a pair of points used to hold the result of correlation
*/
-public class PointPair
+public class PointMediaPair
{
- private Photo _photo = null;
+ private MediaFile _media = null;
private DataPoint _pointBefore = null;
private DataPoint _pointAfter = null;
private long _secondsBefore = 1L;
/**
* Constructor
- * @param inPhoto Photo object
+ * @param inMedia media object
*/
- public PointPair(Photo inPhoto)
- {
- _photo = inPhoto;
+ public PointMediaPair(MediaFile inMedia) {
+ _media = inMedia;
}
-
/**
* Add a point to the pair
* @param inPoint data point
// Check if point is closest point before
if (inSeconds <= 0)
{
- // point stamp is before photo stamp
+ // point stamp is before media stamp
if (inSeconds > _secondsBefore || _secondsBefore > 0L)
{
- // point stamp is nearer to photo
+ // point stamp is nearer to media
_pointBefore = inPoint;
_secondsBefore = inSeconds;
}
// Check if point is closest point after
if (inSeconds >= 0)
{
- // point stamp is after photo stamp
+ // point stamp is after media stamp
if (inSeconds < _secondsAfter || _secondsAfter < 0L)
{
- // point stamp is nearer to photo
+ // point stamp is nearer to media
_pointAfter = inPoint;
_secondsAfter = inSeconds;
}
/**
- * @return Photo object
+ * @return Media object
*/
- public Photo getPhoto()
- {
- return _photo;
+ public MediaFile getMedia() {
+ return _media;
}
/**
- * @return the closest point before the photo
+ * @return the closest point before the media
*/
- public DataPoint getPointBefore()
- {
+ public DataPoint getPointBefore() {
return _pointBefore;
}
/**
- * @return number of seconds between photo and subsequent point
+ * @return number of seconds between media and subsequent point
*/
- public long getSecondsBefore()
- {
+ public long getSecondsBefore() {
return _secondsBefore;
}
/**
- * @return the closest point after the photo
+ * @return the closest point after the media
*/
- public DataPoint getPointAfter()
- {
+ public DataPoint getPointAfter() {
return _pointAfter;
}
/**
- * @return number of seconds between previous point and photo
+ * @return number of seconds between previous point and media
*/
- public long getSecondsAfter()
- {
+ public long getSecondsAfter() {
return _secondsAfter;
}
/**
* @return true if both points found
*/
- public boolean isValid()
- {
+ public boolean isValid() {
return getPointBefore() != null && getPointAfter() != null;
}
/**
* @return the number of seconds to the nearest point
*/
- public long getMinSeconds()
- {
+ public long getMinSeconds() {
return Math.min(_secondsAfter, -_secondsBefore);
}
/**
- * @return angle from photo to nearest point in radians
+ * @return angle from media to nearest point in radians
*/
public double getMinRadians()
{
--- /dev/null
+package tim.prune.data;
+
+import java.io.File;
+import javax.sound.sampled.AudioFileFormat;
+import javax.sound.sampled.AudioSystem;
+
+/**
+ * Class to represent an audio file for correlation
+ */
+public class AudioFile extends MediaFile
+{
+ /** length of current audio file in seconds */
+ private int _lengthInSeconds = LENGTH_UNKNOWN;
+
+ private static final int LENGTH_UNKNOWN = -1;
+ private static final int LENGTH_NOT_AVAILABLE = -2;
+
+ /**
+ * Constructor
+ * @param inFile file object
+ */
+ public AudioFile(File inFile)
+ {
+ // Timestamp is always just taken from the file modification stamp
+ super(inFile, new Timestamp(inFile.lastModified()));
+ }
+
+ /**
+ * @return length of this audio file in seconds
+ */
+ public int getLengthInSeconds()
+ {
+ if (_lengthInSeconds == LENGTH_UNKNOWN)
+ {
+ try {
+ AudioFileFormat format = AudioSystem.getAudioFileFormat(getFile());
+ _lengthInSeconds = (int) (format.getFrameLength() / format.getFormat().getFrameRate());
+ }
+ catch (Exception e) {
+ _lengthInSeconds = LENGTH_NOT_AVAILABLE;
+ }
+ }
+ return _lengthInSeconds;
+ }
+}
--- /dev/null
+package tim.prune.data;
+
+import java.util.ArrayList;
+
+/**
+ * Class to hold a list of audio files, using the MediaList superclass
+ */
+public class AudioList extends MediaList
+{
+ /**
+ * Empty constructor
+ */
+ public AudioList() {
+ this(null);
+ }
+
+ /**
+ * Constructor
+ * @param inList ArrayList containing audio file objects
+ */
+ private AudioList(ArrayList<MediaFile> inList) {
+ super(inList);
+ }
+
+ /**
+ * @return clone of list contents
+ */
+ public AudioList cloneList()
+ {
+ if (getNumMedia() == 0) return this;
+ ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ listCopy.addAll(_media);
+ return new AudioList(listCopy);
+ }
+
+ /**
+ * @return the number of audio files in the list
+ */
+ public int getNumAudios() {
+ return getNumMedia();
+ }
+
+ /**
+ * Add an audio file to the list
+ * @param inAudio object to add
+ */
+ public void addAudio(AudioFile inAudio) {
+ addMedia(inAudio);
+ }
+
+ /**
+ * Add an audio file to the list
+ * @param inAudio object to add
+ * @param inIndex index at which to add
+ */
+ public void addAudio(AudioFile inAudio, int inIndex) {
+ addMedia(inAudio, inIndex);
+ }
+
+ /**
+ * Remove the selected audio file from the list
+ * @param inIndex index number to remove
+ */
+ public void deleteAudio(int inIndex) {
+ deleteMedia(inIndex);
+ }
+
+ /**
+ * Get the index of the given audio file
+ * @param inAudio object to check
+ * @return index of this object in the list, or -1 if not found
+ */
+ public int getAudioIndex(AudioFile inAudio) {
+ return getMediaIndex(inAudio);
+ }
+
+ /**
+ * Get the Audio object at the given index
+ * @param inIndex index number, starting at 0
+ * @return specified object
+ */
+ public AudioFile getAudio(int inIndex) {
+ return (AudioFile) getMedia(inIndex);
+ }
+
+ /**
+ * @return true if list contains correlated objects
+ */
+ public boolean hasCorrelatedAudios() {
+ return hasCorrelatedMedia();
+ }
+
+ /**
+ * Remove all correlated media from the list
+ */
+ public void removeCorrelatedAudios() {
+ removeCorrelatedMedia();
+ }
+}
private Coordinate _latitude = null, _longitude = null;
private Altitude _altitude;
private Timestamp _timestamp = null;
+ /** Attached photo */
private Photo _photo = null;
+ /** Attached audio file */
+ private AudioFile _audio = null;
private String _waypointName = null;
private boolean _startOfSegment = false;
private boolean _markedForDeletion = false;
* Set the photo for this data point
* @param inPhoto Photo object
*/
- public void setPhoto(Photo inPhoto)
- {
+ public void setPhoto(Photo inPhoto) {
_photo = inPhoto;
+ _modifyCount++;
}
-
/**
* @return associated Photo object
*/
- public Photo getPhoto()
- {
+ public Photo getPhoto() {
return _photo;
}
+ /**
+ * Set the audio file for this point
+ * @param inAudio audio object
+ */
+ public void setAudio(AudioFile inAudio) {
+ _audio = inAudio;
+ _modifyCount++;
+ }
+
+ /**
+ * @return associated audio object
+ */
+ public AudioFile getAudio() {
+ return _audio;
+ }
+
+ /**
+ * Attach the given media object according to type
+ * @param inMedia either a photo or an audio file
+ */
+ public void attachMedia(MediaFile inMedia)
+ {
+ if (inMedia != null) {
+ if (inMedia instanceof Photo) {
+ setPhoto((Photo) inMedia);
+ inMedia.setDataPoint(this);
+ }
+ else if (inMedia instanceof AudioFile) {
+ setAudio((AudioFile) inMedia);
+ inMedia.setDataPoint(this);
+ }
+ }
+ }
/**
* @return true if the point is valid
return _latitude.isValid() && _longitude.isValid();
}
+ /**
+ * @return true if the point has either a photo or audio attached
+ */
+ public boolean hasMedia() {
+ return _photo != null || _audio != null;
+ }
+
/**
* Interpolate a set of points between this one and the given one
* @param inEndPoint end point of interpolation
/**
* Class to represent a field of a data point
- * including its type
*/
public class Field
{
private String _labelKey = null;
private String _customLabel = null;
- private FieldType _type = null;
private boolean _builtin = false;
- public static final Field LATITUDE = new Field("fieldname.latitude", FieldType.COORD);
- public static final Field LONGITUDE = new Field("fieldname.longitude", FieldType.COORD);
- public static final Field ALTITUDE = new Field("fieldname.altitude", FieldType.INT);
- public static final Field TIMESTAMP = new Field("fieldname.timestamp", FieldType.TIME);
- public static final Field WAYPT_NAME = new Field("fieldname.waypointname", FieldType.NONE);
- public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", FieldType.NONE);
- public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", FieldType.BOOL);
+ public static final Field LATITUDE = new Field("fieldname.latitude", true);
+ public static final Field LONGITUDE = new Field("fieldname.longitude", true);
+ public static final Field ALTITUDE = new Field("fieldname.altitude", true);
+ public static final Field TIMESTAMP = new Field("fieldname.timestamp", true);
+ public static final Field WAYPT_NAME = new Field("fieldname.waypointname", true);
+ public static final Field WAYPT_TYPE = new Field("fieldname.waypointtype", true);
+ public static final Field NEW_SEGMENT = new Field("fieldname.newsegment", true);
// TODO: Field for photo filename, ability to load (from text) and save (to text)
private static final Field[] ALL_AVAILABLE_FIELDS = {
LATITUDE, LONGITUDE, ALTITUDE, TIMESTAMP, WAYPT_NAME, WAYPT_TYPE, NEW_SEGMENT,
- new Field("fieldname.custom", FieldType.NONE)
+ new Field(I18nManager.getText("fieldname.custom"))
};
/**
* Private constructor
* @param inLabelKey Key for label texts
- * @param inType type of field
+ * @param inBuiltin true for built-in types, false for custom
*/
- private Field(String inLabelKey, FieldType inType)
+ private Field(String inLabelKey, boolean inBuiltin)
{
- _labelKey = inLabelKey;
- _customLabel = null;
- _type = inType;
- _builtin = true;
+ if (inBuiltin) {
+ _labelKey = inLabelKey;
+ _customLabel = null;
+ }
+ else {
+ _labelKey = null;
+ _customLabel = inLabelKey;
+ }
+ _builtin = inBuiltin;
}
*/
public Field(String inLabel)
{
- _labelKey = null;
- _customLabel = inLabel;
- _type = FieldType.NONE;
+ this(inLabel, false);
}
/**
return _builtin;
}
- /**
- * @return field type
- */
- public FieldType getType()
- {
- return _type;
- }
-
/**
* Checks if the two fields are equal
* @param inOther other Field object
*/
public static Field getField(String inFieldName)
{
- for (int i=0; i<ALL_AVAILABLE_FIELDS.length; i++) {
+ for (int i=0; i<ALL_AVAILABLE_FIELDS.length; i++)
+ {
Field field = ALL_AVAILABLE_FIELDS[i];
if (field.getName().equals(inFieldName)) {
return field;
public String toString()
{
StringBuffer buffer = new StringBuffer();
- buffer.append("(");
+ buffer.append('(');
for (int i=0; i<_fieldArray.length; i++)
{
buffer.append(_fieldArray[i].getName()).append(',');
}
- buffer.append(")");
+ buffer.append(')');
return buffer.toString();
}
}
+++ /dev/null
-package tim.prune.data;
-
-/**
- * Class to represent a type of field,
- * for example coordinate or integer
- */
-public class FieldType
-{
- private char _id = 0;
-
- public static final FieldType NONE = new FieldType('0');
- public static final FieldType INT = new FieldType('1');
- public static final FieldType BOOL = new FieldType('2');
- public static final FieldType COORD = new FieldType('3');
- public static final FieldType TIME = new FieldType('4');
-
-
- /**
- * Private constructor
- * @param inId identifier
- */
- private FieldType(char inId)
- {
- _id = inId;
- }
-
- /**
- * Method only needed to avoid compiler warnings
- * @return id
- */
- protected char getId() {
- return _id;
- }
-}
--- /dev/null
+package tim.prune.data;
+
+import java.io.File;
+
+/**
+ * Class to represent a general media file for correlation.
+ * Subclasses are currently Photo and AudioFile
+ */
+public abstract class MediaFile
+{
+ /** File where media is stored */
+ protected File _file = null;
+ /** Timestamp, if any */
+ protected Timestamp _timestamp = null;
+ /** Associated DataPoint if correlated */
+ protected DataPoint _dataPoint = null;
+ /** Status when loaded */
+ private Status _originalStatus = Status.NOT_CONNECTED;
+ /** Current status */
+ private Status _currentStatus = Status.NOT_CONNECTED;
+
+ /** Connection status */
+ public enum Status {
+ /** Media is not connected to any point */
+ NOT_CONNECTED,
+ /** Media has been connected to a point since it was loaded */
+ TAGGED,
+ /** Media is connected to a point */
+ CONNECTED
+ };
+
+
+ /**
+ * Constructor
+ * @param inFile file object
+ * @param inStamp timestamp object
+ */
+ public MediaFile(File inFile, Timestamp inStamp)
+ {
+ _file = inFile;
+ _timestamp = inStamp;
+ }
+
+ /**
+ * @return the file object
+ */
+ public File getFile() {
+ return _file;
+ }
+
+ /**
+ * @return the timestamp object
+ */
+ public Timestamp getTimestamp() {
+ return _timestamp;
+ }
+
+ /**
+ * @param inTimestamp Timestamp object
+ */
+ public void setTimestamp(Timestamp inTimestamp) {
+ _timestamp = inTimestamp;
+ }
+
+ /**
+ * @return true if details are valid (might not have timestamp)
+ */
+ public boolean isValid() {
+ return _file != null && _file.exists() && _file.canRead();
+ }
+
+ /**
+ * @return true if file has timestamp
+ */
+ public boolean hasTimestamp() {
+ return _timestamp != null && _timestamp.isValid();
+ }
+
+ /**
+ * Check if this object refers to the same File as another
+ * @param inOther other MediaFile object
+ * @return true if the Files are the same
+ */
+ public boolean equals(MediaFile inOther)
+ {
+ return (inOther != null && inOther.getFile() != null && getFile() != null
+ && inOther.getFile().equals(getFile()));
+ }
+
+ /**
+ * Set the data point associated with the photo
+ * @param inPoint DataPoint with coordinates etc
+ */
+ public void setDataPoint(DataPoint inPoint)
+ {
+ _dataPoint = inPoint;
+ // set status according to point
+ if (inPoint == null) {
+ setCurrentStatus(Status.NOT_CONNECTED);
+ }
+ else {
+ setCurrentStatus(Status.CONNECTED);
+ }
+ }
+
+ /**
+ * @return the DataPoint object
+ */
+ public DataPoint getDataPoint() {
+ return _dataPoint;
+ }
+
+ /**
+ * @param inStatus status of file when loaded
+ */
+ public void setOriginalStatus(Status inStatus)
+ {
+ _originalStatus = inStatus;
+ _currentStatus = inStatus;
+ }
+
+ /**
+ * @return status of file when it was loaded
+ */
+ public Status getOriginalStatus()
+ {
+ return _originalStatus;
+ }
+
+ /**
+ * @return current status
+ */
+ public Status getCurrentStatus()
+ {
+ return _currentStatus;
+ }
+ /**
+ * @param inStatus current status
+ */
+ public void setCurrentStatus(Status inStatus)
+ {
+ _currentStatus = inStatus;
+ }
+
+ /**
+ * @return true if file is connected to a point
+ */
+ public boolean isConnected()
+ {
+ return _currentStatus != Status.NOT_CONNECTED;
+ }
+
+ /**
+ * Reset any cached data held by the media file (eg thumbnail)
+ */
+ public void resetCachedData() {}
+}
--- /dev/null
+package tim.prune.data;
+
+import java.util.ArrayList;
+
+/**
+ * Class to hold a list of Media, either Photos or Audio files
+ */
+public abstract class MediaList
+{
+ /** list of media file objects */
+ protected ArrayList<MediaFile> _media = null;
+
+
+ /**
+ * Empty constructor
+ */
+ public MediaList() {
+ this(null);
+ }
+
+ /**
+ * Constructor
+ * @param inList ArrayList containing media objects
+ */
+ protected MediaList(ArrayList<MediaFile> inList)
+ {
+ _media = inList;
+ if (_media == null) {
+ _media = new ArrayList<MediaFile>();
+ }
+ }
+
+ /**
+ * @return the number of media in the list
+ */
+ public int getNumMedia() {
+ return _media.size();
+ }
+
+ /**
+ * Add an object to the list
+ * @param inObject object to add
+ */
+ public void addMedia(MediaFile inObject)
+ {
+ if (inObject != null) {
+ _media.add(inObject);
+ }
+ }
+
+ /**
+ * Add an object to the list at a specified index (used for undo)
+ * @param inObject object to add
+ * @param inIndex index at which to add
+ */
+ public void addMedia(MediaFile inObject, int inIndex)
+ {
+ if (inObject != null) {
+ _media.add(inIndex, inObject);
+ }
+ }
+
+
+ /**
+ * Remove the selected media from the list
+ * @param inIndex index number to remove
+ */
+ public void deleteMedia(int inIndex)
+ {
+ // Maybe throw exception if this fails?
+ _media.remove(inIndex);
+ }
+
+
+ /**
+ * Checks if the specified object is already in the list
+ * @param inMedia media object to check
+ * @return true if it's already in the list
+ */
+ public boolean contains(MediaFile inMedia) {
+ return (getMediaIndex(inMedia) > -1);
+ }
+
+
+ /**
+ * Get the index of the given media
+ * @param inMedia object to check
+ * @return index of this object in the list, or -1 if not found
+ */
+ public int getMediaIndex(MediaFile inMedia)
+ {
+ // Check if we need to check
+ final int num = getNumMedia();
+ if (num <= 0 || inMedia == null || inMedia.getFile() == null)
+ return -1;
+ // Loop over list
+ for (int i=0; i<num; i++)
+ {
+ MediaFile m = _media.get(i);
+ if (m != null && m.equals(inMedia)) {
+ return i;
+ }
+ }
+ // not found
+ return -1;
+ }
+
+
+ /**
+ * Get the media at the given index
+ * @param inIndex index number, starting at 0
+ * @return specified object
+ */
+ public MediaFile getMedia(int inIndex)
+ {
+ if (inIndex < 0 || inIndex >= getNumMedia()) return null;
+ return _media.get(inIndex);
+ }
+
+
+ /**
+ * Crop the list to the specified size
+ * @param inIndex previous size
+ */
+ public void cropTo(int inIndex)
+ {
+ if (inIndex <= 0)
+ {
+ // delete whole list
+ _media.clear();
+ }
+ else
+ {
+ // delete to previous size
+ while (_media.size() > inIndex) {
+ _media.remove(_media.size()-1);
+ }
+ }
+ }
+
+
+ /**
+ * @return array of file names
+ */
+ public String[] getNameList()
+ {
+ final int num = getNumMedia();
+ String[] names = new String[num];
+ for (int i=0; i<num; i++) {
+ names[i] = getMedia(i).getFile().getName();
+ }
+ return names;
+ }
+
+
+ /**
+ * @return true if list contains correlated media
+ */
+ public boolean hasCorrelatedMedia()
+ {
+ for (MediaFile m : _media) {
+ if (m.getDataPoint() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true if list contains uncorrelated media
+ */
+ public boolean hasUncorrelatedMedia()
+ {
+ for (MediaFile m : _media) {
+ if (m.getDataPoint() == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Remove all correlated media from the list
+ */
+ public void removeCorrelatedMedia()
+ {
+ if (getNumMedia() > 0)
+ {
+ // Construct new list to copy into
+ ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ // Loop over list
+ for (MediaFile m : _media)
+ {
+ // Copy media if it has no point
+ if (m != null)
+ {
+ if (m.getDataPoint() == null)
+ listCopy.add(m);
+ else
+ m.resetCachedData();
+ }
+ }
+ // Switch reference to new list
+ _media = listCopy;
+ }
+ }
+
+ /**
+ * @return clone of list contents
+ */
+ public abstract MediaList cloneList();
+
+ /**
+ * Restore contents from other MediaList
+ * @param inOther MediaList with cloned contents
+ */
+ public void restore(MediaList inOther)
+ {
+ _media.clear();
+ if (inOther != null && inOther.getNumMedia() > 0)
+ {
+ // Copy contents from other list
+ _media.addAll(inOther._media);
+ }
+ }
+}
/**
* Class to represent a photo and link to DataPoint
*/
-public class Photo
+public class Photo extends MediaFile
{
- /** File where photo is stored */
- private File _file = null;
- /** Timestamp, if any */
- private Timestamp _timestamp = null;
- /** Associated DataPoint if correlated */
- private DataPoint _dataPoint = null;
/** Size of original image */
private Dimension _size = null;
- /** Status of photo when loaded */
- private Status _originalStatus = Status.NOT_CONNECTED;
- /** Current photo status */
- private Status _currentStatus = Status.NOT_CONNECTED;
/** rotation flag (clockwise from 0 to 3) */
private int _rotation = 0;
// TODO: Need to store caption for image?
// thumbnail for image (from exif)
private byte[] _exifThumbnail = null;
- /** Photo status */
- public enum Status {
- /** Photo is not connected to any point */
- NOT_CONNECTED,
- /** Photo has been connected to a point since it was loaded */
- TAGGED,
- /** Photo is connected to a point */
- CONNECTED
- };
-
/**
* Constructor
* @param inFile File object for photo
*/
public Photo(File inFile)
{
- _file = inFile;
- }
-
-
- /**
- * @return File object where photo resides
- */
- public File getFile()
- {
- return _file;
- }
-
-
- /**
- * Set the data point associated with the photo
- * @param inPoint DataPoint with coordinates etc
- */
- public void setDataPoint(DataPoint inPoint)
- {
- _dataPoint = inPoint;
- // set status according to point
- if (inPoint == null) {
- setCurrentStatus(Status.NOT_CONNECTED);
- }
- else {
- setCurrentStatus(Status.CONNECTED);
- }
- }
-
- /**
- * @return the DataPoint object
- */
- public DataPoint getDataPoint()
- {
- return _dataPoint;
- }
-
- /**
- * @param inTimestamp Timestamp of photo
- */
- public void setTimestamp(Timestamp inTimestamp)
- {
- _timestamp = inTimestamp;
- }
-
- /**
- * @return timestamp of photo
- */
- public Timestamp getTimestamp()
- {
- return _timestamp;
+ super(inFile, null);
}
/**
*/
public Dimension getSize()
{
- if (_size == null)
- {
+ if (_size == null) {
calculateSize();
}
return _size;
return _size.height;
}
- /**
- * @param inStatus status of photo when loaded
- */
- public void setOriginalStatus(Status inStatus)
- {
- _originalStatus = inStatus;
- _currentStatus = inStatus;
- }
-
- /**
- * @return status of photo when it was loaded
- */
- public Status getOriginalStatus()
- {
- return _originalStatus;
- }
-
- /**
- * @return current status of photo
- */
- public Status getCurrentStatus()
- {
- return _currentStatus;
- }
- /**
- * @param inStatus current status of photo
- */
- public void setCurrentStatus(Status inStatus)
- {
- _currentStatus = inStatus;
- }
-
- /**
- * @return true if photo is connected to a point
- */
- public boolean isConnected()
- {
- return _currentStatus != Status.NOT_CONNECTED;
- }
-
/**
* @return byte array of thumbnail data
*/
// remove thumbnail too
}
- /**
- * Check if a Photo object refers to the same File as another
- * @param inOther other Photo object
- * @return true if the Files are the same
- */
- public boolean equals(Photo inOther)
- {
- return (inOther != null && inOther.getFile() != null && getFile() != null
- && inOther.getFile().equals(getFile()));
- }
-
/**
* @param inRotation initial rotation value (from exif)
*/
import java.util.ArrayList;
/**
- * Class to hold a list of Photos
+ * Class to hold a list of Photos, using the MediaList superclass
*/
-public class PhotoList
+public class PhotoList extends MediaList
{
- private ArrayList<Photo> _photos = null;
-
/**
* Empty constructor
*/
- public PhotoList()
- {
+ public PhotoList() {
this(null);
}
* Constructor
* @param inList ArrayList containing Photo objects
*/
- private PhotoList(ArrayList<Photo> inList)
- {
- _photos = inList;
+ private PhotoList(ArrayList<MediaFile> inList) {
+ super(inList);
}
-
/**
- * @return the number of photos in the list
+ * @return clone of list contents
*/
- public int getNumPhotos()
+ public PhotoList cloneList()
{
- if (_photos == null) return 0;
- return _photos.size();
+ if (getNumMedia() == 0) return this;
+ ArrayList<MediaFile> listCopy = new ArrayList<MediaFile>();
+ listCopy.addAll(_media);
+ return new PhotoList(listCopy);
}
+ /**
+ * @return the number of photos in the list
+ */
+ public int getNumPhotos() {
+ return getNumMedia();
+ }
/**
* Add a Photo to the list
* @param inPhoto Photo object to add
*/
- public void addPhoto(Photo inPhoto)
- {
- if (inPhoto != null)
- {
- // Make sure array is initialised
- if (_photos == null)
- {
- _photos = new ArrayList<Photo>();
- }
- // Add the photo
- _photos.add(inPhoto);
- }
+ public void addPhoto(Photo inPhoto) {
+ addMedia(inPhoto);
}
-
/**
* Add a Photo to the list
* @param inPhoto Photo object to add
* @param inIndex index at which to add photo
*/
- public void addPhoto(Photo inPhoto, int inIndex)
- {
- if (inPhoto != null)
- {
- // Make sure array is initialised
- if (_photos == null)
- {
- _photos = new ArrayList<Photo>();
- }
- // Add the photo
- _photos.add(inIndex, inPhoto);
- }
+ public void addPhoto(Photo inPhoto, int inIndex) {
+ addMedia(inPhoto, inIndex);
}
-
/**
* Remove the selected photo from the list
* @param inIndex index number to remove
*/
- public void deletePhoto(int inIndex)
- {
- // Maybe throw exception if this fails?
- if (_photos != null)
- {
- _photos.remove(inIndex);
- }
- }
-
-
- /**
- * Checks if the specified Photo is already in the list
- * @param inPhoto Photo object to check
- * @return true if it's already in the list
- */
- public boolean contains(Photo inPhoto)
- {
- return (getPhotoIndex(inPhoto) > -1);
+ public void deletePhoto(int inIndex) {
+ deleteMedia(inIndex);
}
-
/**
* Get the index of the given Photo
* @param inPhoto Photo object to check
* @return index of this Photo in the list, or -1 if not found
*/
- public int getPhotoIndex(Photo inPhoto)
- {
- // Check if we need to check
- int numPhotos = getNumPhotos();
- if (numPhotos <= 0 || inPhoto == null || inPhoto.getFile() == null)
- return -1;
- // Loop around photos in list
- Photo foundPhoto = null;
- for (int i=0; i<numPhotos; i++)
- {
- foundPhoto = getPhoto(i);
- if (foundPhoto != null && foundPhoto.equals(inPhoto))
- {
- return i;
- }
- }
- // not found
- return -1;
+ public int getPhotoIndex(Photo inPhoto) {
+ return getMediaIndex(inPhoto);
}
-
/**
* Get the Photo at the given index
* @param inIndex index number, starting at 0
* @return specified Photo object
*/
- public Photo getPhoto(int inIndex)
- {
- if (inIndex < 0 || inIndex >= getNumPhotos()) return null;
- return _photos.get(inIndex);
+ public Photo getPhoto(int inIndex) {
+ return (Photo) getMedia(inIndex);
}
-
- /**
- * Crop the photo list to the specified size
- * @param inIndex previous size
- */
- public void cropTo(int inIndex)
- {
- if (inIndex <= 0)
- {
- // delete whole list
- if (_photos != null) {_photos.clear();}
- }
- else
- {
- // delete photos to previous size
- while (_photos.size() > inIndex)
- {
- _photos.remove(_photos.size()-1);
- }
- }
- }
-
-
- /**
- * @return array of file names
- */
- public String[] getNameList()
- {
- String[] names = new String[getNumPhotos()];
- for (int i=0; i<getNumPhotos(); i++)
- {
- names[i] = getPhoto(i).getFile().getName();
- }
- return names;
- }
-
-
/**
* @return true if photo list contains correlated photos
*/
- public boolean hasCorrelatedPhotos()
- {
- int numPhotos = getNumPhotos();
- boolean hasCorrelated = false;
- // Loop over photos in list
- for (int i=0; i<numPhotos && !hasCorrelated; i++)
- {
- if (getPhoto(i).getDataPoint() != null)
- hasCorrelated = true;
- }
- return hasCorrelated;
+ public boolean hasCorrelatedPhotos() {
+ return hasCorrelatedMedia();
}
-
/**
* Remove all correlated photos from the list
*/
- public void removeCorrelatedPhotos()
- {
- int numPhotos = getNumPhotos();
- if (numPhotos > 0)
- {
- // Construct new list to copy into
- ArrayList<Photo> listCopy = new ArrayList<Photo>();
- // Loop over photos in list
- for (int i=0; i<numPhotos; i++)
- {
- // Copy photo if it has no point
- Photo photo = getPhoto(i);
- if (photo != null)
- {
- if (photo.getDataPoint() == null)
- listCopy.add(photo);
- else
- photo.resetCachedData();
- }
- }
- // Switch reference to new list
- _photos = listCopy;
- }
- }
-
-
- /**
- * @return clone of photo list contents
- */
- public PhotoList cloneList()
- {
- if (_photos == null) return this;
- ArrayList<Photo> listCopy = new ArrayList<Photo>();
- for (int i=0; i<_photos.size(); i++) {
- listCopy.add(_photos.get(i));
- }
- return new PhotoList(listCopy);
- }
-
-
- /**
- * Restore contents from other PhotoList
- * @param inOther PhotoList with cloned contents
- */
- public void restore(PhotoList inOther)
- {
- if (inOther.getNumPhotos() == 0)
- {
- // List is empty
- _photos = null;
- }
- else
- {
- // Clear array and copy over from other one
- _photos.clear();
- _photos.addAll(inOther._photos);
- }
+ public void removeCorrelatedPhotos() {
+ removeCorrelatedMedia();
}
}
private Track _track = null;
private int _currentPoint = -1;
private boolean _valid = false;
+ private int _prevNumPoints = 0;
private int _startIndex = -1, _endIndex = -1;
private int _currentPhotoIndex = -1;
+ private int _currentAudioIndex = -1;
private IntegerRange _altitudeRange = null;
private int _climb = -1, _descent = -1;
private Altitude.Format _altitudeFormat = Altitude.Format.NO_FORMAT;
{
_altitudeFormat = Altitude.Format.NO_FORMAT;
_numSegments = 0;
- if (_track.getNumPoints() > 0 && hasRangeSelected())
+ final int numPoints = _track.getNumPoints();
+ // Recheck if the number of points has changed
+ if (numPoints != _prevNumPoints) {
+ _prevNumPoints = numPoints;
+ check();
+ }
+ if (numPoints > 0 && hasRangeSelected())
{
_altitudeRange = new IntegerRange();
_climb = 0;
}
/**
- * Clear selected point, range and photo
+ * Clear selected point, range, photo and audio
*/
public void clearAll()
{
_currentPoint = -1;
selectRange(-1, -1);
_currentPhotoIndex = -1;
+ _currentAudioIndex = -1;
check();
}
/**
* Select the specified photo and point
- * @param inPhotoIndex index of selected photo in PhotoList
* @param inPointIndex index of selected point
+ * @param inPhotoIndex index of selected photo in PhotoList
+ * @param inAudioIndex index of selected audio item
*/
- public void selectPhotoAndPoint(int inPhotoIndex, int inPointIndex)
+ public void selectPointPhotoAudio(int inPointIndex, int inPhotoIndex, int inAudioIndex)
{
- _currentPhotoIndex = inPhotoIndex;
_currentPoint = inPointIndex;
+ _currentPhotoIndex = inPhotoIndex;
+ _currentAudioIndex = inAudioIndex;
check();
}
return _currentPhotoIndex;
}
+ /**
+ * @return currently selected audio index
+ */
+ public int getCurrentAudioIndex()
+ {
+ return _currentAudioIndex;
+ }
+
/**
* Check that the selection still makes sense
* and fire update message to listeners
*/
private void check()
{
- if (_track != null)
+ if (_track != null && _track.getNumPoints() > 0)
{
- if (_track.getNumPoints() > 0)
+ int maxIndex = _track.getNumPoints() - 1;
+ if (_currentPoint > maxIndex)
{
- int maxIndex = _track.getNumPoints() - 1;
- if (_currentPoint > maxIndex)
- {
- _currentPoint = maxIndex;
- }
- if (_endIndex > maxIndex)
- {
- _endIndex = maxIndex;
- }
- if (_startIndex > maxIndex)
- {
- _startIndex = maxIndex;
- }
+ _currentPoint = maxIndex;
}
- else
+ if (_endIndex > maxIndex)
{
- // track is empty, clear selections
- _currentPoint = _startIndex = _endIndex = -1;
+ _endIndex = maxIndex;
}
+ if (_startIndex > maxIndex)
+ {
+ _startIndex = maxIndex;
+ }
+ }
+ else
+ {
+ // track is empty, clear selections
+ _currentPoint = _startIndex = _endIndex = -1;
}
UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
}
private static Calendar CALENDAR = null;
private static final Pattern GENERAL_TIMESTAMP_PATTERN
= Pattern.compile("(\\d{4})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})\\D(\\d{2})");
- private static Matcher GENERAL_TIMESTAMP_MATCHER = null;
private static long SECS_SINCE_1970 = 0L;
private static long SECS_SINCE_GARTRIP = 0L;
private static long MSECS_SINCE_1970 = 0L;
}
catch (ParseException e) {}
}
- if (!_valid && inString.length() == 19) {
- GENERAL_TIMESTAMP_MATCHER = GENERAL_TIMESTAMP_PATTERN.matcher(inString);
- if (GENERAL_TIMESTAMP_MATCHER.matches()) {
+ if (!_valid && inString.length() == 19)
+ {
+ final Matcher matcher = GENERAL_TIMESTAMP_PATTERN.matcher(inString);
+ if (matcher.matches())
+ {
try {
- _seconds = getSeconds(Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(1)),
- Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(2)),
- Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(3)),
- Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(4)),
- Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(5)),
- Integer.parseInt(GENERAL_TIMESTAMP_MATCHER.group(6)));
+ _seconds = getSeconds(Integer.parseInt(matcher.group(1)),
+ Integer.parseInt(matcher.group(2)),
+ Integer.parseInt(matcher.group(3)),
+ Integer.parseInt(matcher.group(4)),
+ Integer.parseInt(matcher.group(5)),
+ Integer.parseInt(matcher.group(6)));
_valid = true;
}
catch (NumberFormatException nfe2) {} // parse shouldn't fail if matcher matched
*/
public Timestamp createPlusOffset(TimeDifference inOffset)
{
- return new Timestamp((_seconds + inOffset.getTotalSeconds()) * 1000L);
+ return createPlusOffset(inOffset.getTotalSeconds());
+ }
+
+ /**
+ * Add the given number of seconds to this Timestamp
+ * @param inSeconds number of seconds to add
+ * @return new Timestamp object
+ */
+ public Timestamp createPlusOffset(long inSeconds)
+ {
+ return new Timestamp((_seconds + inSeconds) * 1000L);
}
{
DataPoint point = _dataPoints[i];
// Don't delete photo points
- if (point.getPhoto() != null || !point.getDeleteFlag())
+ if (point.hasMedia() || !point.getDeleteFlag())
{
newPointArray[numCopied] = point;
numCopied++;
package tim.prune.data;
-import java.util.Iterator;
import java.util.Set;
import tim.prune.UpdateMessageBroker;
private Selection _selection = null;
private FileInfo _fileInfo = null;
private PhotoList _photoList = null;
+ private AudioList _audioList = null;
/**
_selection = new Selection(_track);
_fileInfo = new FileInfo();
_photoList = new PhotoList();
+ _audioList = new AudioList();
}
/**
* @return the Track object
*/
- public Track getTrack()
- {
+ public Track getTrack() {
return _track;
}
/**
* @return the Selection object
*/
- public Selection getSelection()
- {
+ public Selection getSelection() {
return _selection;
}
/**
* @return the FileInfo object
*/
- public FileInfo getFileInfo()
- {
+ public FileInfo getFileInfo() {
return _fileInfo;
}
/**
* @return the PhotoList object
*/
- public PhotoList getPhotoList()
- {
+ public PhotoList getPhotoList() {
return _photoList;
}
+ /**
+ * @return the AudioList object
+ */
+ public AudioList getAudioList() {
+ return _audioList;
+ }
+
/**
* Get the currently selected point, if any
* @return DataPoint if single point selected, otherwise null
*/
- public DataPoint getCurrentPoint()
- {
+ public DataPoint getCurrentPoint() {
return _track.getPoint(_selection.getCurrentPointIndex());
}
* Get the currently selected photo, if any
* @return Photo if selected, otherwise null
*/
- public Photo getCurrentPhoto()
- {
+ public Photo getCurrentPhoto() {
return _photoList.getPhoto(_selection.getCurrentPhotoIndex());
}
+ /**
+ * Get the currently selected audio file, if any
+ * @return AudioFile if selected, otherwise null
+ */
+ public AudioFile getCurrentAudio() {
+ return _audioList.getAudio(_selection.getCurrentAudioIndex());
+ }
+
/**
* Add a Set of Photos
// Firstly count number of points and photos to add
int numPhotosToAdd = 0;
int numPointsToAdd = 0;
- Iterator<Photo> iterator = null;
if (inSet != null && !inSet.isEmpty())
{
- iterator = inSet.iterator();
- while (iterator.hasNext())
+ for (Photo photo : inSet)
{
- try
+ if (photo != null && !_photoList.contains(photo))
{
- Photo photo = iterator.next();
- if (photo != null && !_photoList.contains(photo))
- {
- numPhotosToAdd++;
- if (photo.getDataPoint() != null)
- {
- numPointsToAdd++;
- }
+ numPhotosToAdd++;
+ if (photo.getDataPoint() != null) {
+ numPointsToAdd++;
}
}
- catch (ClassCastException ce) {}
}
}
// If there are any photos to add, add them
int pointNum = 0;
boolean hasAltitude = false;
// Add each Photo in turn
- iterator = inSet.iterator();
- while (iterator.hasNext())
+ for (Photo photo : inSet)
{
- try
+ if (photo != null && !_photoList.contains(photo))
{
- Photo photo = iterator.next();
- if (photo != null && !_photoList.contains(photo))
+ // Add photo
+ _photoList.addPhoto(photo);
+ // Add point if there is one
+ if (photo.getDataPoint() != null)
{
- // Add photo
- _photoList.addPhoto(photo);
- // Add point if there is one
- if (photo.getDataPoint() != null)
- {
- dataPoints[pointNum] = photo.getDataPoint();
- // Check if any points have altitudes
- hasAltitude |= (photo.getDataPoint().getAltitude() != null);
- pointNum++;
- }
+ dataPoints[pointNum] = photo.getDataPoint();
+ // Check if any points have altitudes
+ hasAltitude |= (photo.getDataPoint().getAltitude() != null);
+ pointNum++;
}
}
- catch (ClassCastException ce) {}
}
if (numPointsToAdd > 0)
{
return result;
}
+ /**
+ * Add a Set of Audio objects
+ * @param inSet Set containing Audio objects
+ * @return number of audio objects added
+ */
+ public int addAudios(Set<AudioFile> inSet)
+ {
+ int numAudiosAdded = 0;
+ if (inSet != null && !inSet.isEmpty())
+ {
+ for (AudioFile audio : inSet)
+ {
+ if (audio != null && !_audioList.contains(audio))
+ {
+ // Add audio object
+ _audioList.addAudio(audio);
+ numAudiosAdded++;
+ // audio objects never have points when they're loaded
+ }
+ }
+ }
+ return numAudiosAdded;
+ }
/**
* Delete the currently selected range of points
*/
public boolean deleteCurrentPhoto(boolean inPointToo)
{
- // delete currently selected photo
int photoIndex = _selection.getCurrentPhotoIndex();
if (photoIndex >= 0)
{
return true;
}
+ /**
+ * Delete the currently selected audio item and optionally its point too
+ * @param inPointToo true to also delete associated point
+ * @return true if delete successful
+ */
+ public boolean deleteCurrentAudio(boolean inPointToo)
+ {
+ int audioIndex = _selection.getCurrentAudioIndex();
+ if (audioIndex >= 0)
+ {
+ AudioFile audio = _audioList.getAudio(audioIndex);
+ _audioList.deleteAudio(audioIndex);
+ // has it got a point?
+ if (audio.getDataPoint() != null)
+ {
+ if (inPointToo)
+ {
+ // delete point
+ int pointIndex = _track.getPointIndex(audio.getDataPoint());
+ _track.deletePoint(pointIndex);
+ }
+ else
+ {
+ // disconnect point from audio
+ audio.getDataPoint().setAudio(null);
+ audio.setDataPoint(null);
+ }
+ }
+ // update subscribers
+ _selection.modifyPointDeleted();
+ UpdateMessageBroker.informSubscribers();
+ }
+ return true;
+ }
+
/**
* Delete all the points which have been marked for deletion
public void selectPoint(int inPointIndex)
{
if (_selection.getCurrentPointIndex() == inPointIndex || inPointIndex >= _track.getNumPoints()) {return;}
+ DataPoint selectedPoint = _track.getPoint(inPointIndex);
// get the index of the current photo
int photoIndex = _selection.getCurrentPhotoIndex();
// Check if point has photo or not
- boolean pointHasPhoto = false;
- if (inPointIndex >= 0)
- {
- Photo pointPhoto = _track.getPoint(inPointIndex).getPhoto();
- pointHasPhoto = (pointPhoto != null);
- if (pointHasPhoto) {
- photoIndex = _photoList.getPhotoIndex(pointPhoto);
- }
+ boolean pointHasPhoto = inPointIndex >= 0 && selectedPoint.getPhoto() != null;
+ if (pointHasPhoto) {
+ photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto());
}
- // Might need to deselect photo
- if (!pointHasPhoto)
- {
+ else if (photoIndex < 0 || _photoList.getPhoto(photoIndex).isConnected()) {
// selected point hasn't got a photo - deselect photo if necessary
- if (photoIndex < 0 || _photoList.getPhoto(photoIndex).isConnected()) {
- photoIndex = -1;
- }
+ photoIndex = -1;
+ }
+ // Check if point has an audio item or not
+ int audioIndex = _selection.getCurrentAudioIndex();
+ boolean pointHasAudio = inPointIndex >= 0 && selectedPoint.getAudio() != null;
+ if (pointHasAudio) {
+ audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio());
+ }
+ else if (audioIndex < 0 || _audioList.getAudio(audioIndex).isConnected()) {
+ // deselect current audio file
+ audioIndex = -1;
}
// give to selection
- _selection.selectPhotoAndPoint(photoIndex, inPointIndex);
+ _selection.selectPointPhotoAudio(inPointIndex, photoIndex, audioIndex);
}
/**
// Therefore the photo selection takes priority, deselecting point if necessary
// Find Photo object
Photo photo = _photoList.getPhoto(inPhotoIndex);
+ int pointIndex = _selection.getCurrentPointIndex();
+ DataPoint currPoint = getCurrentPoint();
if (photo != null)
{
- // Find point object and its index
- int pointIndex = _track.getPointIndex(photo.getDataPoint());
- // Check whether to deselect current point or not if photo not correlated
- if (pointIndex < 0)
- {
- int currPointIndex = _selection.getCurrentPointIndex();
- if (currPointIndex >= 0 && _track.getPoint(currPointIndex).getPhoto() == null)
- {
- pointIndex = currPointIndex; // Keep currently selected point
+ // Has the photo got a point?
+ if (photo.isConnected()) {
+ pointIndex = _track.getPointIndex(photo.getDataPoint());
+ }
+ else {
+ // Check whether to deselect current point or not if photo not correlated
+ if (pointIndex >= 0 && _track.getPoint(pointIndex).getPhoto() != null) {
+ pointIndex = -1;
}
}
- // give to selection object
- _selection.selectPhotoAndPoint(inPhotoIndex, pointIndex);
}
else {
- // no photo, just reset selection
- DataPoint currPoint = getCurrentPoint();
- if (currPoint != null && currPoint.getPhoto() == null) {
- _selection.selectPhotoAndPoint(-1, _selection.getCurrentPointIndex()); // keep point
+ // no photo, but maybe need to deselect point
+ if (currPoint != null && currPoint.getPhoto() != null) {
+ pointIndex = -1;
+ }
+ }
+ // Has the new point got an audio file?
+ DataPoint selectedPoint = _track.getPoint(pointIndex);
+ int audioIndex = _selection.getCurrentAudioIndex();
+ if (selectedPoint != null) {
+ if (selectedPoint.getAudio() != null) audioIndex = _audioList.getAudioIndex(selectedPoint.getAudio());
+ }
+ else {
+ if (selectedPoint != currPoint && currPoint.getAudio() != null) {audioIndex = -1;}
+ }
+ // give to selection object
+ _selection.selectPointPhotoAudio(pointIndex, inPhotoIndex, audioIndex);
+ }
+
+ /**
+ * Select the given audio object and its point if any
+ * @param inAudioIndex index of audio item to select
+ */
+ public void selectAudio(int inAudioIndex)
+ {
+ if (_selection.getCurrentAudioIndex() == inAudioIndex) {return;}
+ // Audio selection takes priority, deselecting point if necessary
+ AudioFile audio = _audioList.getAudio(inAudioIndex);
+ int pointIndex = _selection.getCurrentPointIndex();
+ DataPoint currPoint = getCurrentPoint();
+ if (audio != null)
+ {
+ // Find point object and its index
+ if (audio.isConnected()) {
+ pointIndex = _track.getPointIndex(audio.getDataPoint());
}
else {
- _selection.selectPhotoAndPoint(-1, -1); // deselect point too
+ // Check whether to deselect current point or not if audio not correlated
+ if (pointIndex >= 0 && _track.getPoint(pointIndex).getAudio() != null) {
+ pointIndex = -1;
+ }
+ }
+ }
+ else {
+ // check if current point has audio or not
+ if (currPoint != null && currPoint.getAudio() != null) {
+ pointIndex = -1;
}
}
+ // Has the new point got a photo?
+ DataPoint selectedPoint = _track.getPoint(pointIndex);
+ int photoIndex = _selection.getCurrentPhotoIndex();
+ if (selectedPoint != null) {
+ if (selectedPoint.getPhoto() != null) photoIndex = _photoList.getPhotoIndex(selectedPoint.getPhoto());
+ }
+ else {
+ if (selectedPoint != currPoint && currPoint.getPhoto() != null) {photoIndex = -1;}
+ }
+ // give to selection object
+ _selection.selectPointPhotoAudio(pointIndex, photoIndex, inAudioIndex);
}
+
/**
* Extend the current selection to end at the given point, eg by shift-clicking
* @param inPointNum index of end point
descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext2")).append("</p>");
descBuffer.append("<p>").append(I18nManager.getText("dialog.about.summarytext3")).append("</p>");
descBuffer.append("<p>").append(I18nManager.getText("dialog.about.languages")).append(" : ")
- .append("deutsch, english, espa\u00F1ol, fran\u00E7ais, italiano, nederlands,<br>" +
- " polski, portugu\u00EAs, \u4e2d\u6587 (chinese), \u65E5\u672C\u8A9E (japanese), schwiizerd\u00FC\u00FCtsch, t\u00FCrk\u00E7e, " +
- "<br> \u010de\u0161tina, rom\u00E2n\u0103, afrikaans, bahasa indonesia, farsi").append("</p>");
+ .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>");
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));
new JLabel(" theYinYeti, Rothermographer, Sam, Rudolph, nazotoko,"),
1, 4);
addToGridBagPanel(creditsPanel, gridBag, constraints,
- new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d"),
+ new JLabel(" katpatuka, R\u00E9mi, Marcus, Ali, Javier, Jeroen, prot_d, Gy\u00F6rgy, HooAU"),
1, 5);
addToGridBagPanel(creditsPanel, gridBag, constraints,
new JLabel(I18nManager.getText("dialog.about.credits.translations") + " : "),
// First, try locally-held readme.txt if available (as it normally should be)
// Readme file can either be in file system or packed in the same jar as code
String errorMessage = null;
+ String readme = null;
+ InputStream in = null;
try
{
// For some reason using ../readme.txt doesn't work, so need absolute path
- InputStream in = AboutScreen.class.getResourceAsStream("/tim/prune/readme.txt");
+ in = AboutScreen.class.getResourceAsStream("/tim/prune/readme.txt");
if (in != null) {
byte[] buffer = new byte[in.available()];
in.read(buffer);
in.close();
- return new String(buffer);
+ readme = new String(buffer);
}
}
catch (IOException e) {
errorMessage = e.getMessage();
}
+ finally {
+ try {in.close();} catch (Exception e) {}
+ }
+ if (readme != null) {return readme;}
+
// Locally-held file failed, so try to find gz file installed on system (eg Debian)
try
{
if (gzFile.exists())
{
// Copy decompressed bytes from gz file into out
- InputStream in = new GZIPInputStream(new FileInputStream(gzFile));
+ in = new GZIPInputStream(new FileInputStream(gzFile));
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[8 * 1024];
int count = 0;
} while (count != -1);
out.close();
in.close();
- return out.toString();
+ readme = out.toString();
}
}
catch (IOException e) {
System.err.println("Exception trying to get readme.gz : " + e.getMessage());
}
+ finally {
+ try {in.close();} catch (Exception e) {}
+ }
+ if (readme != null) {return readme;}
// Only show first error message if couldn't get readme from gz either
if (errorMessage != null) {
System.err.println("Exception trying to get readme: " + errorMessage);
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
-import java.net.MalformedURLException;
-import java.net.URL;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
private boolean isOsmPanelOk()
{
boolean ok = _oNameField.getText().trim().length() > 1;
- URL baseUrl = null, topUrl = null;
- try {
- // Try to parse base url if given
- String baseText = _baseUrlField.getText().trim();
- if (baseText.length() > 10) {
- baseUrl = new URL(baseText);
- }
- else if (baseText.length() > 0) {ok = false;}
- // Same again for top url if given
- String topText = _topUrlField.getText().trim();
- if (topText.length() > 10) {
- topUrl = new URL(topText);
- }
- else if (topText.length() > 0) {ok = false;}
- } catch (MalformedURLException e) {
- ok = false;
- }
+ String baseUrl = null, topUrl = null;
+ // Try to parse base url if given
+ String baseText = _baseUrlField.getText().trim();
+ baseUrl = MapSource.fixBaseUrl(baseText);
+ if (baseText.length() > 0 && baseUrl == null) {ok = false;}
+ // Same again for top url if given
+ String topText = _topUrlField.getText().trim();
+ topUrl = MapSource.fixBaseUrl(topText);
+ if (topText.length() > 0 && topUrl == null) {ok = false;}
// looks ok if at least one url given
return (ok && (baseUrl != null || topUrl != null));
}
--- /dev/null
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.AudioFile;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Photo;
+import tim.prune.undo.UndoConnectMedia;
+import tim.prune.undo.UndoOperation;
+
+/**
+ * Function to connect either a photo or an audio file to the current point
+ */
+public class ConnectToPointFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public ConnectToPointFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.connecttopoint";
+ }
+
+ /**
+ * Perform function
+ */
+ public void begin()
+ {
+ Photo photo = _app.getTrackInfo().getCurrentPhoto();
+ DataPoint point = _app.getTrackInfo().getCurrentPoint();
+ AudioFile audio = _app.getTrackInfo().getCurrentAudio();
+ boolean connectPhoto = (point != null && photo != null && point.getPhoto() == null);
+ boolean connectAudio = (point != null && audio != null && point.getAudio() == null);
+
+ if (connectPhoto && connectAudio) {
+ // TODO: Let user choose whether to connect photo/audio or both
+ }
+ // Make undo object
+ UndoOperation undo = new UndoConnectMedia(point, connectPhoto?photo.getFile().getName():null,
+ connectAudio?audio.getFile().getName():null);
+ // Connect the media
+ if (connectPhoto) {
+ photo.setDataPoint(point);
+ point.setPhoto(photo);
+ }
+ if (connectAudio) {
+ audio.setDataPoint(point);
+ point.setAudio(audio);
+ }
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ _app.completeFunction(undo, I18nManager.getText("confirm.media.connect"));
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.AudioFile;
+import tim.prune.data.DataPoint;
+import tim.prune.undo.UndoDisconnectMedia;
+import tim.prune.undo.UndoOperation;
+
+/**
+ * Function to disconnect the current audio object from the current point (like DisconnectPhotoFunction)
+ */
+public class DisconnectAudioFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public DisconnectAudioFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.disconnectfrompoint";
+ }
+
+ /**
+ * Perform the operation
+ */
+ public void begin()
+ {
+ AudioFile audio = _app.getTrackInfo().getCurrentAudio();
+ if (audio != null && audio.getDataPoint() != null)
+ {
+ DataPoint point = audio.getDataPoint();
+ UndoOperation undo = new UndoDisconnectMedia(point, false, true, audio.getFile().getName());
+ // disconnect
+ audio.setDataPoint(null);
+ point.setAudio(null);
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ _app.completeFunction(undo, I18nManager.getText("confirm.audio.disconnect"));
+ }
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Photo;
+import tim.prune.undo.UndoDisconnectMedia;
+
+/**
+ * Function to disconnect the current photo from the current point
+ */
+public class DisconnectPhotoFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public DisconnectPhotoFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.disconnectfrompoint";
+ }
+
+ /**
+ * Perform the operation
+ */
+ public void begin()
+ {
+ Photo photo = _app.getTrackInfo().getCurrentPhoto();
+ if (photo != null && photo.getDataPoint() != null)
+ {
+ DataPoint point = photo.getDataPoint();
+ UndoDisconnectMedia undo = new UndoDisconnectMedia(point, true, false, photo.getFile().getName());
+ // disconnect
+ photo.setDataPoint(null);
+ point.setPhoto(null);
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ _app.completeFunction(undo, I18nManager.getText("confirm.photo.disconnect"));
+ }
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.NumberFormat;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.DoubleRange;
+
+/**
+ * Class to allow the download of OSM data (using the XAPI)
+ * for the area covered by the data
+ */
+public class DownloadOsmFunction extends GenericFunction implements Runnable
+{
+ private JDialog _dialog = null;
+ private JLabel[] _latLonLabels = null;
+ private JProgressBar _progressBar = null;
+ private JButton _okButton = null;
+ private JFileChooser _fileChooser = null;
+ private File _selectedFile = null;
+ private boolean _cancelled = false;
+ /** Number formatter */
+ private final NumberFormat FORMAT_TWO_DP = NumberFormat.getNumberInstance();
+
+
+ /**
+ * Constructor
+ * @param inApp application object for callback
+ */
+ public DownloadOsmFunction(App inApp)
+ {
+ super(inApp);
+ FORMAT_TWO_DP.setMaximumFractionDigits(2);
+ FORMAT_TWO_DP.setMinimumFractionDigits(2);
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.downloadosm";
+ }
+
+ /**
+ * Begin the function
+ */
+ public void begin()
+ {
+ // Make dialog window
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ _fileChooser = new JFileChooser();
+ _fileChooser.setSelectedFile(new File("data.osm"));
+ }
+ initDialog();
+ _dialog.setVisible(true);
+ }
+
+
+ /**
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
+ */
+ private Component makeDialogComponents()
+ {
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout(0, 10));
+ dialogPanel.add(new JLabel(I18nManager.getText("dialog.downloadosm.desc")), BorderLayout.NORTH);
+ // grid of labels to show lat/long extent
+ JPanel gridPanel = new JPanel();
+ gridPanel.setLayout(new GridLayout(3, 3));
+ _latLonLabels = new JLabel[4];
+ for (int i=0; i<4; i++) {
+ _latLonLabels[i] = new JLabel("0");
+ }
+ int lNum = 0;
+ for (int i=0; i<4; i++) {
+ gridPanel.add(new JLabel(" "));
+ gridPanel.add(_latLonLabels[lNum++]);
+ }
+ // layout main panel
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
+ mainPanel.add(gridPanel);
+ _progressBar = new JProgressBar();
+ _progressBar.setIndeterminate(true);
+ mainPanel.add(_progressBar);
+ dialogPanel.add(mainPanel, BorderLayout.CENTER);
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ _okButton = new JButton(I18nManager.getText("button.ok"));
+ ActionListener okListener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ finish();
+ }
+ };
+ _okButton.addActionListener(okListener);
+ buttonPanel.add(_okButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _cancelled = true;
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+ dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+ return dialogPanel;
+ }
+
+ /**
+ * Initialise the values of the labels in the dialog
+ */
+ private void initDialog()
+ {
+ // Get range of data
+ String[] lats = expandRange(_app.getTrackInfo().getTrack().getLatRange());
+ String[] lons = expandRange(_app.getTrackInfo().getTrack().getLonRange());
+ _latLonLabels[0].setText(lats[1]); // max lat
+ _latLonLabels[1].setText(lons[0]); // min lon
+ _latLonLabels[2].setText(lons[1]); // max lon
+ _latLonLabels[3].setText(lats[0]); // min lat
+ _okButton.setEnabled(true);
+ _progressBar.setVisible(false);
+ _cancelled = false;
+ }
+
+ /**
+ * Expand the given range to reasonable limits
+ * @param inRange range of lat/long values
+ * @return expanded range as pair of Strings
+ */
+ private String[] expandRange(DoubleRange inRange)
+ {
+ double mid = (inRange.getMaximum() + inRange.getMinimum()) / 2.0;
+ double range = inRange.getRange();
+ double max = 0.0, min = 0.0;
+ // Expand range to at least 0.02 degree
+ if (range < 0.02)
+ {
+ min = mid - 0.01;
+ max = mid + 0.01;
+ }
+ else {
+ // expand by 10% in both directions
+ min = mid - range * 0.55;
+ max = mid + range * 0.55;
+ }
+ // Round min down to 0.01 degree
+ int minCents = (int) (100 * min);
+ // Round max upwards likewise
+ int maxCents = (int) (100 * max + 1);
+ final String[] answer = new String[] {FORMAT_TWO_DP.format(minCents/100.0),
+ FORMAT_TWO_DP.format(maxCents/100.0)};
+ return answer;
+ }
+
+ /**
+ * Finish the dialog when OK pressed
+ */
+ private void finish()
+ {
+ if (!_okButton.isEnabled()) return;
+ _selectedFile = selectOsmFile();
+ if (_selectedFile != null)
+ {
+ // Show progress bar
+ _okButton.setEnabled(false);
+ _progressBar.setVisible(true);
+ new Thread(this).start();
+ }
+ else
+ _dialog.dispose();
+ }
+
+ /**
+ * Select a file to save the OSM data to
+ * @return selected file or null if cancelled
+ */
+ private File selectOsmFile()
+ {
+ File saveFile = null;
+ boolean chooseAgain = false;
+ do
+ {
+ chooseAgain = false;
+ if (_fileChooser.showSaveDialog(_dialog) == JFileChooser.APPROVE_OPTION)
+ {
+ // OK pressed and file chosen
+ File file = _fileChooser.getSelectedFile();
+ // Check if file exists and if necessary prompt for overwrite
+ Object[] buttonTexts = {I18nManager.getText("button.overwrite"), I18nManager.getText("button.cancel")};
+ if (!file.exists() || JOptionPane.showOptionDialog(_dialog,
+ I18nManager.getText("dialog.save.overwrite.text"),
+ I18nManager.getText("dialog.save.overwrite.title"), JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE, null, buttonTexts, buttonTexts[1])
+ == JOptionPane.YES_OPTION)
+ {
+ // new file or overwrite confirmed
+ saveFile = file;
+ }
+ else
+ {
+ // file exists and overwrite cancelled - select again
+ chooseAgain = true;
+ }
+ }
+ } while (chooseAgain);
+ return saveFile;
+ }
+
+
+ /**
+ * Do the actual download - launched to run in another thread
+ */
+ public void run()
+ {
+ final String url = "http://www.informationfreeway.org/api/0.6/map?bbox=" +
+ _latLonLabels[1].getText() + "," + _latLonLabels[3].getText() + "," +
+ _latLonLabels[2].getText() + "," + _latLonLabels[0].getText();
+
+ byte[] buffer = new byte[1024];
+ InputStream inStream = null;
+ FileOutputStream outStream = null;
+ int numBytesRead = 0;
+ try
+ {
+ inStream = new URL(url).openStream();
+ outStream = new FileOutputStream(_selectedFile);
+ // Loop and copy bytes to file
+ while ((numBytesRead = inStream.read(buffer)) > -1 && !_cancelled)
+ {
+ outStream.write(buffer, 0, numBytesRead);
+ }
+ }
+ catch (MalformedURLException mue) {}
+ catch (IOException ioe) {
+ // TODO: throw exception or show dialog
+ System.out.println("Exception: " + ioe.getClass().getName());
+ }
+ // clean up streams
+ finally {
+ try {inStream.close();} catch (Exception e) {}
+ try {outStream.close();} catch (Exception e) {}
+ }
+ // close dialog
+ _dialog.dispose();
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Field;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
+import tim.prune.function.gpsies.GenericDownloaderFunction;
+import tim.prune.function.gpsies.GpsiesTrack;
+
+/**
+ * Function to load nearby point information from Wikipedia
+ * according to the currently viewed area
+ */
+public class GetWikipediaFunction extends GenericDownloaderFunction
+{
+ /** Maximum number of results to get */
+ private static final int MAX_RESULTS = 20;
+ /** Maximum distance from point in km */
+ private static final int MAX_DISTANCE = 15;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public GetWikipediaFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.getwikipedia";
+ }
+
+ /**
+ * @param inColNum index of column, 0 or 1
+ * @return key for this column
+ */
+ protected String getColumnKey(int inColNum)
+ {
+ if (inColNum == 0) return "dialog.wikipedia.column.name";
+ return "dialog.wikipedia.column.distance";
+ }
+
+
+ /**
+ * Run method to call geonames in separate thread
+ */
+ public void run()
+ {
+ _statusLabel.setText(I18nManager.getText("confirm.running"));
+ // Get coordinates from current point (if any) or from centre of screen
+ double lat = 0.0, lon = 0.0;
+ DataPoint point = _app.getTrackInfo().getCurrentPoint();
+ if (point == null)
+ {
+ double[] coords = _app.getViewport().getBounds();
+ lat = (coords[0] + coords[2]) / 2.0;
+ lon = (coords[1] + coords[3]) / 2.0;
+ }
+ else {
+ lat = point.getLatitude().getDouble();
+ lon = point.getLongitude().getDouble();
+ }
+
+ String descMessage = "";
+ InputStream inStream = null;
+
+ // Example http://ws.geonames.org/findNearbyWikipedia?lat=47&lng=9
+ String urlString = "http://ws.geonames.org/findNearbyWikipedia?lat=" +
+ lat + "&lng=" + lon + "&maxRows=" + MAX_RESULTS
+ + "&radius=" + MAX_DISTANCE + "&lang=" + I18nManager.getText("wikipedia.lang");
+ // Parse the returned XML with a special handler
+ GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ inStream = url.openStream();
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e) {
+ descMessage = e.getClass().getName() + " - " + e.getMessage();
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+ // Add track list to model
+ ArrayList<GpsiesTrack> trackList = xmlHandler.getTrackList();
+ _trackListModel.addTracks(trackList);
+
+ // Set status label according to error or "none found", leave blank if ok
+ if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) {
+ descMessage = I18nManager.getText("dialog.gpsies.nonefound");
+ }
+ _statusLabel.setText(descMessage);
+ }
+
+ /**
+ * Load the selected track or point
+ */
+ protected void loadSelected()
+ {
+ // Find the row selected in the table and get the corresponding track
+ int rowNum = _trackTable.getSelectedRow();
+ if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+ {
+ String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
+ String[] latlon = coords.split(",");
+ if (latlon.length == 2)
+ {
+ DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
+ point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
+ _app.createPoint(point);
+ }
+ }
+ // Close the dialog
+ _cancelled = true;
+ _dialog.dispose();
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import java.util.ArrayList;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import tim.prune.function.gpsies.GpsiesTrack;
+
+/**
+ * XML handler for dealing with XML returned from gpsies.com
+ */
+public class GetWikipediaXmlHandler extends DefaultHandler
+{
+ private String _value = null;
+ private ArrayList<GpsiesTrack> _trackList = null;
+ private GpsiesTrack _track = null;
+ private String _lat = null, _lon = null;
+
+
+ /**
+ * React to the start of an XML tag
+ */
+ public void startElement(String inUri, String inLocalName, String inTagName,
+ Attributes inAttributes) throws SAXException
+ {
+ if (inTagName.equals("geonames")) {
+ _trackList = new ArrayList<GpsiesTrack>();
+ }
+ else if (inTagName.equals("entry")) {
+ _track = new GpsiesTrack();
+ _lat = null;
+ _lon = null;
+ }
+ else _value = null;
+ super.startElement(inUri, inLocalName, inTagName, inAttributes);
+ }
+
+ /**
+ * React to the end of an XML tag
+ */
+ public void endElement(String inUri, String inLocalName, String inTagName)
+ throws SAXException
+ {
+ if (inTagName.equals("entry")) {
+ // end of the entry
+ _track.setDownloadLink(_lat + "," + _lon);
+ _trackList.add(_track);
+ }
+ else if (inTagName.equals("title")) {
+ _track.setTrackName(_value);
+ }
+ else if (inTagName.equals("summary")) {
+ _track.setDescription(_value);
+ }
+ else if (inTagName.equals("lat")) {
+ _lat = _value;
+ }
+ else if (inTagName.equals("lng")) {
+ _lon = _value;
+ }
+ else if (inTagName.equals("distance")) {
+ try {
+ _track.setLength(Double.parseDouble(_value) * 1000.0); // convert from km to m
+ }
+ catch (NumberFormatException nfe) {}
+ }
+ else if (inTagName.equals("wikipediaUrl")) {
+ _track.setWebUrl(_value);
+ }
+ super.endElement(inUri, inLocalName, inTagName);
+ }
+
+ /**
+ * React to characters received inside tags
+ */
+ public void characters(char[] inCh, int inStart, int inLength)
+ throws SAXException
+ {
+ String value = new String(inCh, inStart, inLength);
+ _value = (_value==null?value:_value+value);
+ super.characters(inCh, inStart, inLength);
+ }
+
+ /**
+ * @return the list of tracks
+ */
+ public ArrayList<GpsiesTrack> getTrackList()
+ {
+ return _trackList;
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Photo;
+import tim.prune.gui.PhotoThumbnail;
+
+/**
+ * Class to show a popup window for a photo
+ */
+public class PhotoPopupFunction extends GenericFunction
+{
+ /** popup window */
+ private JFrame _frame = null; // would be a JDialog but that doesn't allow max button
+ /** label for filename */
+ private JLabel _label = null;
+ /** Photo thumbnail */
+ private PhotoThumbnail _photoThumb = null;
+
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public PhotoPopupFunction(App inApp)
+ {
+ super(inApp);
+ }
+
+ /**
+ * Get the name key
+ */
+ public String getNameKey() {
+ return "function.photopopup";
+ }
+
+ /**
+ * Show the screen
+ */
+ public void begin()
+ {
+ if (_frame == null)
+ {
+ _frame = new JFrame(I18nManager.getText(getNameKey()));
+ _frame.setIconImage(_parentFrame.getIconImage());
+ _frame.getContentPane().add(makeContents());
+ _frame.pack();
+ _frame.setLocationRelativeTo(_parentFrame);
+ }
+ initFrame();
+ _frame.setVisible(true);
+ }
+
+ /**
+ * Initialise the frame to show the current photo
+ */
+ private void initFrame()
+ {
+ _frame.setVisible(false);
+ Photo photo = _app.getTrackInfo().getCurrentPhoto();
+ _frame.setTitle(photo.getFile().getName());
+ _label.setText("'" + photo.getFile().getName() + "' ("
+ + photo.getWidth() + " x " + photo.getHeight() + ")");
+ _photoThumb.setPhoto(photo);
+ }
+
+ /**
+ * @return the contents of the window as a Component
+ */
+ private Component makeContents()
+ {
+ JPanel mainPanel = new JPanel();
+ mainPanel.setLayout(new BorderLayout());
+ _label = new JLabel("Photo popup");
+ mainPanel.add(_label, BorderLayout.NORTH);
+ _photoThumb = new PhotoThumbnail(false); // specify not in details panel
+ _photoThumb.setPreferredSize(new Dimension(300, 300));
+ mainPanel.add(_photoThumb, BorderLayout.CENTER);
+ // Close button at bottom
+ JPanel okPanel = new JPanel();
+ okPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton okButton = new JButton(I18nManager.getText("button.ok"));
+ okButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e) {
+ _frame.dispose();
+ }
+ });
+ okButton.addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {_frame.dispose();}
+ }
+ public void keyTyped(KeyEvent e) {}
+ public void keyReleased(KeyEvent e) {}
+ });
+ okPanel.add(okButton);
+ mainPanel.add(okPanel, BorderLayout.SOUTH);
+ return mainPanel;
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import java.io.File;
+import java.io.IOException;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+
+/**
+ * Class to play the current audio file
+ */
+public class PlayAudioFunction extends GenericFunction implements Runnable
+{
+ /** Audio clip used for playing within java */
+ private Clip _clip = null;
+
+
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public PlayAudioFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.playaudio";
+ }
+
+ /**
+ * Perform function
+ */
+ public void begin()
+ {
+ // Launch new thread if clip isn't currently playing
+ if (_clip == null) {
+ new Thread(this).start();
+ }
+ }
+
+ /**
+ * Play the audio in a new thread
+ */
+ public void run()
+ {
+ File audioFile = _app.getTrackInfo().getCurrentAudio().getFile();
+ boolean played = false;
+ if (audioFile.exists() && audioFile.isFile() && audioFile.canRead())
+ {
+ // First choice is to play using java
+ played = playClip(audioFile);
+ // Second choice is to try the Desktop library from java 6, if available
+ if (!played) {
+ try {
+ Class<?> d = Class.forName("java.awt.Desktop");
+ d.getDeclaredMethod("open", new Class[] {File.class}).invoke(
+ d.getDeclaredMethod("getDesktop").invoke(null), new Object[] {audioFile});
+ //above code mimics: Desktop.getDesktop().open(audioFile);
+ played = true;
+ }
+ catch (Exception ignore) {
+ played = false;
+ }
+ }
+ // If the Desktop call failed, need to try backup methods
+ if (!played)
+ {
+ // If system looks like a Mac, try open command
+ String osName = System.getProperty("os.name").toLowerCase();
+ boolean isMacOsx = osName.indexOf("mac os") >= 0 || osName.indexOf("darwin") >= 0;
+ if (isMacOsx) {
+ String[] command = new String[] {"open", audioFile.getAbsolutePath()};
+ try {
+ Runtime.getRuntime().exec(command);
+ played = true;
+ }
+ catch (IOException ioe) {}
+ }
+ }
+ }
+ if (!played)
+ {
+ // If still not worked, show error message
+ _app.showErrorMessage(getNameKey(), "error.playaudiofailed");
+ }
+ }
+
+ /**
+ * Try to play the sound file using built-in java libraries
+ * @return true if play was successful
+ */
+ private boolean playClip(File inFile)
+ {
+ boolean success = false;
+ AudioInputStream audioInputStream = null;
+ _clip = null;
+ try
+ {
+ audioInputStream = AudioSystem.getAudioInputStream(inFile);
+ _clip = AudioSystem.getClip();
+ _clip.open(audioInputStream);
+ // play the clip
+ _clip.start();
+ _clip.drain();
+ success = true;
+ } catch (Exception e) {
+ } finally {
+ // close the stream to clean up
+ try {
+ _clip.close();
+ audioInputStream.close();
+ } catch (Exception e) {}
+ _clip = null;
+ }
+ return success;
+ }
+
+ /**
+ * Try to stop a currently playing clip
+ */
+ public void stopClip()
+ {
+ if (_clip != null && _clip.isActive()) {
+ try {
+ _clip.stop();
+ _clip.flush();
+ }
+ catch (Exception e) {}
+ }
+ }
+
+ /**
+ * @return percentage of clip currently played, or -1 if not playing
+ */
+ public int getPercentage()
+ {
+ int percent = -1;
+ if (_clip != null && _clip.isActive())
+ {
+ long clipLen = _clip.getMicrosecondLength();
+ if (clipLen > 0) {
+ percent = (int) (_clip.getMicrosecondPosition() * 100.0 / clipLen);
+ }
+ }
+ return percent;
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.AudioFile;
+import tim.prune.undo.UndoDeleteAudio;
+
+/**
+ * Function to remove the currently selected audio file
+ */
+public class RemoveAudioFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public RemoveAudioFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.removeaudio";
+ }
+
+ /**
+ * Perform the function
+ */
+ public void begin()
+ {
+ // Delete the current audio, and optionally its point too, keeping undo information
+ AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio();
+ if (currentAudio != null)
+ {
+ // Audio is selected, see if it has a point or not
+ boolean deleted = false;
+ UndoDeleteAudio undoAction = null;
+ if (currentAudio.getDataPoint() == null)
+ {
+ // no point attached, so just delete
+ undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(),
+ null, -1);
+ deleted = _app.getTrackInfo().deleteCurrentAudio(false);
+ }
+ else
+ {
+ // point is attached, so need to confirm point deletion
+ undoAction = new UndoDeleteAudio(currentAudio, _app.getTrackInfo().getSelection().getCurrentAudioIndex(),
+ currentAudio.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentAudio.getDataPoint()));
+ int response = JOptionPane.showConfirmDialog(_app.getFrame(),
+ I18nManager.getText("dialog.deleteaudio.deletepoint"),
+ I18nManager.getText(getNameKey()), JOptionPane.YES_NO_CANCEL_OPTION);
+ boolean deletePointToo = (response == JOptionPane.YES_OPTION);
+ // Cancel delete if cancel pressed or dialog closed
+ if (response == JOptionPane.YES_OPTION || response == JOptionPane.NO_OPTION) {
+ deleted = _app.getTrackInfo().deleteCurrentAudio(deletePointToo);
+ }
+ }
+ // Add undo information to stack if necessary
+ if (deleted) {
+ _app.completeFunction(undoAction, currentAudio.getFile().getName() + " " + I18nManager.getText("confirm.media.removed"));
+ }
+ }
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.data.Photo;
+import tim.prune.undo.UndoDeletePhoto;
+
+/**
+ * Function to remove the currently selected photo
+ */
+public class RemovePhotoFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public RemovePhotoFunction(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.removephoto";
+ }
+
+ /**
+ * Perform the function
+ */
+ public void begin()
+ {
+ // Delete the current photo, and optionally its point too, keeping undo information
+ Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto();
+ if (currentPhoto != null)
+ {
+ // Photo is selected, see if it has a point or not
+ boolean photoDeleted = false;
+ UndoDeletePhoto undoAction = null;
+ if (currentPhoto.getDataPoint() == null)
+ {
+ // no point attached, so just delete photo
+ undoAction = new UndoDeletePhoto(currentPhoto, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(),
+ null, -1);
+ photoDeleted = _app.getTrackInfo().deleteCurrentPhoto(false);
+ }
+ else
+ {
+ // point is attached, so need to confirm point deletion
+ undoAction = new UndoDeletePhoto(currentPhoto, _app.getTrackInfo().getSelection().getCurrentPhotoIndex(),
+ currentPhoto.getDataPoint(), _app.getTrackInfo().getTrack().getPointIndex(currentPhoto.getDataPoint()));
+ int response = JOptionPane.showConfirmDialog(_app.getFrame(),
+ I18nManager.getText("dialog.deletephoto.deletepoint"),
+ I18nManager.getText("dialog.deletephoto.title"),
+ JOptionPane.YES_NO_CANCEL_OPTION);
+ boolean deletePointToo = (response == JOptionPane.YES_OPTION);
+ // Cancel delete if cancel pressed or dialog closed
+ if (response == JOptionPane.YES_OPTION || response == JOptionPane.NO_OPTION) {
+ photoDeleted = _app.getTrackInfo().deleteCurrentPhoto(deletePointToo);
+ }
+ }
+ // Add undo information to stack if necessary
+ if (photoDeleted) {
+ _app.completeFunction(undoAction, currentPhoto.getFile().getName() + " " + I18nManager.getText("confirm.media.removed"));
+ }
+ }
+ }
+}
if (response == JFileChooser.APPROVE_OPTION)
{
File saveFile = chooser.getSelectedFile();
+ FileOutputStream outStream = null;
try
{
- Config.getAllConfig().store(new FileOutputStream(saveFile), "Prune config file");
+ outStream = new FileOutputStream(saveFile);
+ Config.getAllConfig().store(outStream, "Prune config file");
}
catch (IOException ioe) {
_app.showErrorMessageNoLookup(getNameKey(),
I18nManager.getText("error.save.failed") + " : " + ioe.getMessage());
}
+ finally {
+ try {outStream.close();} catch (Exception e) {}
+ }
}
_dialog.dispose();
_dialog = null;
--- /dev/null
+package tim.prune.function;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.swing.JOptionPane;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import tim.prune.App;
+import tim.prune.I18nManager;
+import tim.prune.data.DataPoint;
+import tim.prune.data.Field;
+import tim.prune.data.Latitude;
+import tim.prune.data.Longitude;
+import tim.prune.function.gpsies.GenericDownloaderFunction;
+import tim.prune.function.gpsies.GpsiesTrack;
+
+/**
+ * Function to search Wikipedia for place names
+ */
+public class SearchWikipediaNames extends GenericDownloaderFunction
+{
+ /** search term */
+ private String _searchTerm = null;
+ /** Maximum number of results to get */
+ private static final int MAX_RESULTS = 20;
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public SearchWikipediaNames(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.searchwikipedianames";
+ }
+
+ /**
+ * @param inColNum index of column, 0 or 1
+ * @return key for this column
+ */
+ protected String getColumnKey(int inColNum)
+ {
+ if (inColNum == 0) return "dialog.wikipedia.column.name";
+ return null;
+ }
+
+ /**
+ * Before dialog is shown, need to get search term
+ */
+ public void begin()
+ {
+ Object search = JOptionPane.showInputDialog(_app.getFrame(),
+ I18nManager.getText("dialog.searchwikipedianames.search"),
+ I18nManager.getText(getNameKey()),
+ JOptionPane.QUESTION_MESSAGE, null, null, "");
+ if (search != null)
+ {
+ _searchTerm = search.toString();
+ if (!_searchTerm.equals("")) {
+ super.begin();
+ }
+ }
+ }
+
+ /**
+ * Run method to call geonames in separate thread
+ */
+ public void run()
+ {
+ _statusLabel.setText(I18nManager.getText("confirm.running"));
+
+ String descMessage = "";
+ InputStream inStream = null;
+
+ // language (only de and en available)
+ String lang = I18nManager.getText("wikipedia.lang");
+ if (lang.equals("de") || lang.equals("als")) {
+ lang = "de";
+ }
+ else {
+ lang = "en";
+ }
+ // Example http://ws.geonames.org/wikipediaSearch?q=london&maxRows=10
+ String urlString = "http://ws.geonames.org/wikipediaSearch?title=" + _searchTerm + "&maxRows=" + MAX_RESULTS
+ + "&lang=" + lang;
+ // Parse the returned XML with a special handler
+ GetWikipediaXmlHandler xmlHandler = new GetWikipediaXmlHandler();
+ try
+ {
+ URL url = new URL(urlString);
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ inStream = url.openStream();
+ saxParser.parse(inStream, xmlHandler);
+ }
+ catch (Exception e) {
+ descMessage = e.getClass().getName() + " - " + e.getMessage();
+ }
+ // Close stream and ignore errors
+ try {
+ inStream.close();
+ } catch (Exception e) {}
+ // Add track list to model
+ ArrayList<GpsiesTrack> trackList = xmlHandler.getTrackList();
+ // TODO: Do a better job of sorting replies by relevance - use three different lists
+ _trackListModel.addTracks(trackList);
+
+ // Set status label according to error or "none found", leave blank if ok
+ if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) {
+ descMessage = I18nManager.getText("dialog.gpsies.nonefound");
+ }
+ _statusLabel.setText(descMessage);
+ }
+
+ /**
+ * Load the selected track or point
+ */
+ protected void loadSelected()
+ {
+ // Find the row selected in the table and get the corresponding track
+ int rowNum = _trackTable.getSelectedRow();
+ if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+ {
+ String coords = _trackListModel.getTrack(rowNum).getDownloadLink();
+ String[] latlon = coords.split(",");
+ if (latlon.length == 2)
+ {
+ DataPoint point = new DataPoint(new Latitude(latlon[0]), new Longitude(latlon[1]), null);
+ point.setFieldValue(Field.WAYPT_NAME, _trackListModel.getTrack(rowNum).getTrackName(), false);
+ _app.createPoint(point);
+ }
+ }
+ // Close the dialog
+ _cancelled = true;
+ _dialog.dispose();
+ }
+}
private int _startIndex = 0;
/** Names of languages for display in dropdown (not translated) */
- private static final String[] LANGUAGE_NAMES = {"\u010de\u0161tina", "deutsch", "english", "espa\u00F1ol",
- "fran\u00E7ais", "italiano", "nederlands", "polski", "portugu\u00EAs", "\u4e2d\u6587 (chinese)",
- "\u65E5\u672C\u8A9E (japanese)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e", "rom\u00E2n\u0103",
- "afrikaans", "bahasa indonesia", "farsi"
+ private static final String[] LANGUAGE_NAMES = {"\u010de\u0161tina", "deutsch", "english",
+ "espa\u00F1ol", "fran\u00E7ais", "italiano", "magyar", "nederlands", "polski",
+ "portugu\u00EAs", "\u4e2d\u6587 (chinese)", "\u65E5\u672C\u8A9E (japanese)",
+ "\uD55C\uAD6D\uC5B4/\uC870\uC120\uB9D0 (korean)", "schwiizerd\u00FC\u00FCtsch", "t\u00FCrk\u00E7e",
+ "rom\u00E2n\u0103", "afrikaans", "bahasa indonesia", "farsi"
};
/** Associated language codes (must be in same order as names!) */
- private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "nl", "pl", "pt", "zh",
- "ja", "de_ch", "tr", "ro", "af", "in", "fa"
+ private static final String[] LANGUAGE_CODES = {"cz", "de", "en", "es", "fr", "it", "hu",
+ "nl", "pl", "pt", "zh", "ja", "ko", "de_ch", "tr", "ro", "af", "in", "fa"
};
--- /dev/null
+package tim.prune.function;
+
+import javax.swing.JOptionPane;
+
+import tim.prune.App;
+import tim.prune.DataSubscriber;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+
+public class SetLineWidth extends GenericFunction
+{
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public SetLineWidth(App inApp) {
+ super(inApp);
+ }
+
+ /** @return name key */
+ public String getNameKey() {
+ return "function.setlinewidth";
+ }
+
+
+ /**
+ * Run function
+ */
+ public void begin()
+ {
+ int currLineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
+ if (currLineWidth < 1 || currLineWidth > 4) {
+ currLineWidth = 2;
+ }
+ Object lineWidthStr = JOptionPane.showInputDialog(_app.getFrame(),
+ I18nManager.getText("dialog.setlinewidth.text"),
+ I18nManager.getText(getNameKey()),
+ JOptionPane.QUESTION_MESSAGE, null, null, "" + currLineWidth);
+ if (lineWidthStr != null)
+ {
+ int lineWidth = 2;
+ try {
+ lineWidth = Integer.parseInt(lineWidthStr.toString());
+ if (lineWidth >= 1 && lineWidth <= 4 && lineWidth != currLineWidth)
+ {
+ Config.setConfigInt(Config.KEY_LINE_WIDTH, lineWidth);
+ UpdateMessageBroker.informSubscribers(DataSubscriber.SELECTION_CHANGED);
+ }
+ }
+ catch (NumberFormatException nfe) {};
+ }
+ }
+}
--- /dev/null
+package tim.prune.function;
+
+import tim.prune.App;
+import tim.prune.FunctionLibrary;
+import tim.prune.GenericFunction;
+
+/**
+ * Class to stop playing the current audio file
+ */
+public class StopAudioFunction extends GenericFunction
+{
+ /**
+ * Constructor
+ * @param inApp app object
+ */
+ public StopAudioFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * @return name key
+ */
+ public String getNameKey() {
+ return "function.stopaudio";
+ }
+
+ /**
+ * Perform function
+ */
+ public void begin()
+ {
+ PlayAudioFunction playFn = (PlayAudioFunction) FunctionLibrary.FUNCTION_PLAY_AUDIO;
+ playFn.stopClip();
+ }
+}
if (!currPoint.isWaypoint())
{
// Don't delete any photo points or start/end of segments
- if (currPoint.getPhoto() == null
+ if (!currPoint.hasMedia()
&& !_trackDetails.isSegmentStart(i) && !_trackDetails.isSegmentEnd(i))
{
// Check current point against prevPoint
for (int i=0; i<deleteFlags.length; i++)
{
DataPoint point = _track.getPoint(i);
- point.setMarkedForDeletion(deleteFlags[i] && point.getPhoto() == null);
+ point.setMarkedForDeletion(deleteFlags[i] && !point.hasMedia());
}
// Close dialog and inform listeners
if (!inFlags[i])
{
DataPoint currPoint = _track.getPoint(i);
- // Don't delete any photo points
- if (currPoint.getPhoto() == null)
+ // Don't delete any photo points or audio points
+ if (!currPoint.hasMedia())
{
// loop over last few points before this one
for (int j=i-NUM_POINTS_TO_BACKTRACK; j<i; j++)
{
// Don't delete any waypoints or photo points
// Only interested in start and end of segments
- if (!currPoint.isWaypoint() && currPoint.getPhoto() == null
+ if (!currPoint.isWaypoint() && !currPoint.hasMedia()
&& _trackDetails.isSegmentStart(i) && _trackDetails.isSegmentEnd(i))
{
// Measure distance from previous track point
if (!inFlags[i])
{
// Don't delete any waypoints or photo points, or start/end of segments
- if (!currPoint.isWaypoint() && currPoint.getPhoto() == null
+ if (!currPoint.isWaypoint() && !currPoint.hasMedia()
&& !_trackDetails.isSegmentStart(i) && !_trackDetails.isSegmentEnd(i))
{
// Measure distance from previous track point
// second table for distances
_distModel = new DistanceTableModel();
JTable distTable = new JTable(_distModel);
- distTable.setAutoCreateRowSorter(true);
+ // Use reflection to call distTable.setAutoCreateRowSorter(true) which is new with Java 1.6
+ try {
+ Class<?> d = Class.forName("javax.swing.JTable");
+ d.getDeclaredMethod("setAutoCreateRowSorter", new Class[]{Boolean.TYPE}).invoke(distTable, Boolean.TRUE);
+ }
+ catch (Exception e) {}
scrollPane = new JScrollPane(distTable);
scrollPane.setPreferredSize(new Dimension(200, 250));
mainPanel.add(scrollPane);
--- /dev/null
+package tim.prune.function.gpsies;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.function.browser.BrowserLauncher;
+
+/**
+ * Function to load track information from any source,
+ * subclassed for special cases gpsies or wikipedia
+ */
+public abstract class GenericDownloaderFunction extends GenericFunction implements Runnable
+{
+ /** Dialog object */
+ protected JDialog _dialog = null;
+ /** list model */
+ protected TrackListModel _trackListModel = null;
+ /** track table */
+ protected JTable _trackTable = null;
+ /** Cancelled flag */
+ protected boolean _cancelled = false;
+ /** Status label */
+ protected JLabel _statusLabel = null;
+ /** Description box */
+ private JTextArea _descriptionBox = null;
+ /** Load button */
+ private JButton _loadButton = null;
+ /** Show button */
+ private JButton _showButton = null;
+
+
+ /**
+ * Constructor
+ * @param inApp App object
+ */
+ public GenericDownloaderFunction(App inApp) {
+ super(inApp);
+ }
+
+ /**
+ * Begin the function
+ */
+ public void begin()
+ {
+ // Initialise dialog, show empty list
+ if (_dialog == null)
+ {
+ _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
+ _dialog.setLocationRelativeTo(_parentFrame);
+ _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ // add closing listener
+ _dialog.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ _cancelled = true;
+ }
+ });
+ _dialog.getContentPane().add(makeDialogComponents());
+ _dialog.pack();
+ }
+ // Clear list
+ _trackListModel.clear();
+ _loadButton.setEnabled(false);
+ _showButton.setEnabled(false);
+ _cancelled = false;
+ _descriptionBox.setText("");
+ // Start new thread to load list asynchronously
+ new Thread(this).start();
+
+ // Show dialog
+ _dialog.setVisible(true);
+ }
+
+
+ /**
+ * Create dialog components
+ * @return Panel containing all gui elements in dialog
+ */
+ private Component makeDialogComponents()
+ {
+ JPanel dialogPanel = new JPanel();
+ dialogPanel.setLayout(new BorderLayout());
+
+ // Status label
+ _statusLabel = new JLabel(I18nManager.getText("confirm.running"));
+ dialogPanel.add(_statusLabel, BorderLayout.NORTH);
+ // Main panel with track list
+ _trackListModel = new TrackListModel(getColumnKey(0), getColumnKey(1));
+ _trackTable = new JTable(_trackListModel);
+ _trackTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting())
+ {
+ if (_trackTable.getSelectedRow() >= 0
+ && _trackTable.getSelectedRow() < _trackListModel.getRowCount())
+ {
+ _loadButton.setEnabled(true);
+ _showButton.setEnabled(true);
+ setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription());
+ _descriptionBox.setCaretPosition(0);
+ }
+ else {
+ _descriptionBox.setText("");
+ }
+ }
+ }
+ });
+ _trackTable.getColumnModel().getColumn(0).setPreferredWidth(300);
+ if (_trackListModel.getColumnCount() > 1) {
+ _trackTable.getColumnModel().getColumn(1).setPreferredWidth(70);
+ }
+ JScrollPane tablePane = new JScrollPane(_trackTable);
+ tablePane.setPreferredSize(new Dimension(450, 200));
+ // Panel to hold description label and box
+ JPanel descPanel = new JPanel();
+ descPanel.setLayout(new BorderLayout());
+ JLabel descLabel = new JLabel(I18nManager.getText("dialog.gpsies.description") + " :");
+ descPanel.add(descLabel, BorderLayout.NORTH);
+ _descriptionBox = new JTextArea(5, 20);
+ _descriptionBox.setEditable(false);
+ _descriptionBox.setLineWrap(true);
+ _descriptionBox.setWrapStyleWord(true);
+ JScrollPane descPane = new JScrollPane(_descriptionBox);
+ descPane.setPreferredSize(new Dimension(400, 80));
+ descPanel.add(descPane, BorderLayout.CENTER);
+ // Use split pane to split table from description
+ JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tablePane, descPanel);
+ splitPane.setResizeWeight(1.0);
+ dialogPanel.add(splitPane, BorderLayout.CENTER);
+
+ // button panel at bottom
+ JPanel buttonPanel = new JPanel();
+ buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ _loadButton = new JButton(I18nManager.getText("button.load"));
+ _loadButton.setEnabled(false);
+ _loadButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ loadSelected();
+ }
+ });
+ buttonPanel.add(_loadButton);
+ _showButton = new JButton(I18nManager.getText("button.showwebpage"));
+ _showButton.setEnabled(false);
+ _showButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ showSelectedWebpage();
+ }
+ });
+ buttonPanel.add(_showButton);
+ JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
+ cancelButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _cancelled = true;
+ _dialog.dispose();
+ }
+ });
+ buttonPanel.add(cancelButton);
+ dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
+ dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
+ return dialogPanel;
+ }
+
+ /**
+ * @param inColNum index of column, 0 or 1
+ * @return key for this column
+ */
+ protected abstract String getColumnKey(int inColNum);
+
+ /**
+ * Set the description in the box
+ * @param inDesc description to set, or null for no description
+ */
+ private void setDescription(String inDesc)
+ {
+ String text = inDesc;
+ if (inDesc == null || inDesc.length() < 2) {
+ text = I18nManager.getText("dialog.gpsies.nodescription");
+ }
+ _descriptionBox.setText(text);
+ }
+
+
+ /**
+ * Load the selected track or point
+ */
+ protected abstract void loadSelected();
+
+
+ /**
+ * Show the webpage for the selected item
+ */
+ private void showSelectedWebpage()
+ {
+ // Find the row selected in the table and show the corresponding url
+ int rowNum = _trackTable.getSelectedRow();
+ if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
+ {
+ String url = _trackListModel.getTrack(rowNum).getWebUrl();
+ BrowserLauncher.launchBrowser(url);
+ }
+ // Don't close the dialog
+ }
+}
package tim.prune.function.gpsies;
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
-import javax.swing.BorderFactory;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JSplitPane;
-import javax.swing.JTable;
-import javax.swing.JTextArea;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import tim.prune.App;
-import tim.prune.GenericFunction;
import tim.prune.I18nManager;
-import tim.prune.function.browser.BrowserLauncher;
import tim.prune.load.xml.XmlFileLoader;
import tim.prune.load.xml.ZipFileLoader;
* Function to load track information from Gpsies.com
* according to the currently viewed area
*/
-public class GetGpsiesFunction extends GenericFunction implements Runnable
+public class GetGpsiesFunction extends GenericDownloaderFunction
{
- /** Dialog object */
- private JDialog _dialog = null;
- /** list model */
- private TrackListModel _trackListModel = null;
- /** track table */
- private JTable _trackTable = null;
- /** Cancelled flag */
- private boolean _cancelled = false;
- /** Status label */
- private JLabel _statusLabel = null;
- /** Description box */
- private JTextArea _descriptionBox = null;
- /** Load button */
- private JButton _loadButton = null;
- /** Show button */
- private JButton _showButton = null;
/** Number of results per page */
private static final int RESULTS_PER_PAGE = 20;
/** Maximum number of results to get */
* Constructor
* @param inApp App object
*/
- public GetGpsiesFunction(App inApp)
- {
+ public GetGpsiesFunction(App inApp) {
super(inApp);
}
}
/**
- * Begin the function
- */
- public void begin()
- {
- // Initialise dialog, show empty list
- if (_dialog == null)
- {
- _dialog = new JDialog(_parentFrame, I18nManager.getText(getNameKey()), true);
- _dialog.setLocationRelativeTo(_parentFrame);
- _dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
- // add closing listener
- _dialog.addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- _cancelled = true;
- }
- });
- _dialog.getContentPane().add(makeDialogComponents());
- _dialog.pack();
- }
- // Clear list
- _trackListModel.clear();
- _loadButton.setEnabled(false);
- _showButton.setEnabled(false);
- _cancelled = false;
- _descriptionBox.setText("");
- // Start new thread to load list asynchronously
- new Thread(this).start();
-
- // Show dialog
- _dialog.setVisible(true);
- }
-
-
- /**
- * Create dialog components
- * @return Panel containing all gui elements in dialog
+ * @param inColNum index of column, 0 or 1
+ * @return key for this column
*/
- private Component makeDialogComponents()
+ protected String getColumnKey(int inColNum)
{
- JPanel dialogPanel = new JPanel();
- dialogPanel.setLayout(new BorderLayout());
-
- // Status label
- _statusLabel = new JLabel(I18nManager.getText("confirm.running"));
- dialogPanel.add(_statusLabel, BorderLayout.NORTH);
- // Main panel with track list
- _trackListModel = new TrackListModel();
- _trackTable = new JTable(_trackListModel);
- _trackTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
- public void valueChanged(ListSelectionEvent e) {
- if (!e.getValueIsAdjusting())
- {
- if (_trackTable.getSelectedRow() >= 0
- && _trackTable.getSelectedRow() < _trackListModel.getRowCount())
- {
- _loadButton.setEnabled(true);
- _showButton.setEnabled(true);
- setDescription(_trackListModel.getTrack(_trackTable.getSelectedRow()).getDescription());
- _descriptionBox.setCaretPosition(0);
- }
- else {
- _descriptionBox.setText("");
- }
- }
- }
- });
- _trackTable.getColumnModel().getColumn(0).setPreferredWidth(300);
- _trackTable.getColumnModel().getColumn(1).setPreferredWidth(70);
- JScrollPane tablePane = new JScrollPane(_trackTable);
- tablePane.setPreferredSize(new Dimension(450, 200));
- // Panel to hold description label and box
- JPanel descPanel = new JPanel();
- descPanel.setLayout(new BorderLayout());
- JLabel descLabel = new JLabel(I18nManager.getText("dialog.gpsies.description") + " :");
- descPanel.add(descLabel, BorderLayout.NORTH);
- _descriptionBox = new JTextArea(5, 20);
- _descriptionBox.setEditable(false);
- _descriptionBox.setLineWrap(true);
- _descriptionBox.setWrapStyleWord(true);
- JScrollPane descPane = new JScrollPane(_descriptionBox);
- descPane.setPreferredSize(new Dimension(400, 80));
- descPanel.add(descPane, BorderLayout.CENTER);
- // Use split pane to split table from description
- JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tablePane, descPanel);
- splitPane.setResizeWeight(1.0);
- dialogPanel.add(splitPane, BorderLayout.CENTER);
-
- // button panel at bottom
- JPanel buttonPanel = new JPanel();
- buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
- _loadButton = new JButton(I18nManager.getText("button.load"));
- _loadButton.setEnabled(false);
- _loadButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- loadSelectedTrack();
- }
- });
- buttonPanel.add(_loadButton);
- _showButton = new JButton(I18nManager.getText("button.showwebpage"));
- _showButton.setEnabled(false);
- _showButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- showSelectedTrack();
- }
- });
- buttonPanel.add(_showButton);
- JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
- cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _cancelled = true;
- _dialog.dispose();
- }
- });
- buttonPanel.add(cancelButton);
- dialogPanel.add(buttonPanel, BorderLayout.SOUTH);
- dialogPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 15));
- return dialogPanel;
+ if (inColNum == 0) return "dialog.gpsies.column.name";
+ return "dialog.gpsies.column.length";
}
- /**
- * Set the description in the box
- * @param inDesc description to set, or null for no description
- */
- private void setDescription(String inDesc)
- {
- String text = inDesc;
- if (inDesc == null || inDesc.length() < 2) {
- text = I18nManager.getText("dialog.gpsies.nodescription");
- }
- _descriptionBox.setText(text);
- }
/**
* Run method to call gpsies.com in separate thread
String urlString = "http://www.gpsies.com/api.do?BBOX=" +
coords[1] + "," + coords[0] + "," + coords[3] + "," + coords[2] +
"&limit=" + RESULTS_PER_PAGE + "&resultPage=" + currPage;
- // System.out.println(urlString);
// Parse the returned XML with a special handler
GpsiesXmlHandler xmlHandler = new GpsiesXmlHandler();
try
currPage++;
}
while (trackList != null && trackList.size() == RESULTS_PER_PAGE
- && _trackListModel.getRowCount() < MAX_RESULTS && !_cancelled);
+ && _trackListModel.getRowCount() < MAX_RESULTS && !_cancelled);
// Set status label according to error or "none found", leave blank if ok
if (descMessage.equals("") && (trackList == null || trackList.size() == 0)) {
descMessage = I18nManager.getText("dialog.gpsies.nonefound");
_statusLabel.setText(descMessage);
}
-
/**
- * Load the selected track
+ * Load the selected track or point
*/
- private void loadSelectedTrack()
+ protected void loadSelected()
{
// Find the row selected in the table and get the corresponding track
int rowNum = _trackTable.getSelectedRow();
_cancelled = true;
_dialog.dispose();
}
-
-
- /**
- * Show the webpage for the selected track
- */
- private void showSelectedTrack()
- {
- // Find the row selected in the table and show the corresponding url
- int rowNum = _trackTable.getSelectedRow();
- if (rowNum >= 0 && rowNum < _trackListModel.getRowCount())
- {
- String id = _trackListModel.getTrack(rowNum).getFileId();
- BrowserLauncher.launchBrowser("http://gpsies.com/map.do?fileId=" + id);
- }
- // Close the dialog
- _cancelled = true;
- _dialog.dispose();
- }
}
private String _trackName = null;
/** Description */
private String _description = null;
- /** File id for more details */
- private String _fileId = null;
+ /** Web page for more details */
+ private String _webUrl = null;
/** Track length in metres */
private double _trackLength = 0.0;
/** Download link */
}
/**
- * @param inId id of track
+ * @param inUrl web page url
*/
- public void setFileId(String inId)
+ public void setWebUrl(String inUrl)
{
- _fileId = inId;
+ _webUrl = inUrl;
}
/**
- * @return file id
+ * @return web url
*/
- public String getFileId()
+ public String getWebUrl()
{
- return _fileId;
+ return _webUrl;
}
/**
*/
public class GpsiesXmlHandler extends DefaultHandler
{
- private boolean _inTracks = false;
- private boolean _inTrack = false;
- private boolean _inTrackName = false;
- private boolean _inDescription = false;
- private boolean _inFileId = false;
- private boolean _inTrackLength = false;
- private boolean _inLink = false;
private String _value = null;
private ArrayList<GpsiesTrack> _trackList = null;
private GpsiesTrack _track = null;
Attributes inAttributes) throws SAXException
{
if (inTagName.equals("tracks")) {
- _inTracks = true;
_trackList = new ArrayList<GpsiesTrack>();
}
- else if (_inTracks && inTagName.equals("track")) {
- _inTrack = true;
+ else if (inTagName.equals("track")) {
_track = new GpsiesTrack();
}
- else if (_inTrack && inTagName.equals("title")) {_inTrackName = true;}
- else if (_inTrack && inTagName.equals("description")) {_inDescription = true;}
- else if (_inTrack && inTagName.equals("fileId")) {_inFileId = true;}
- else if (_inTrack && inTagName.equals("trackLengthM")) {_inTrackLength = true;}
- else if (_inTrack && inTagName.equals("downloadLink")) {_inLink = true;}
+ _value = null;
super.startElement(inUri, inLocalName, inTagName, inAttributes);
}
public void endElement(String inUri, String inLocalName, String inTagName)
throws SAXException
{
- if (inTagName.equals("tracks")) {_inTracks = false;}
- else if (_inTrack && inTagName.equals("track")) {
+ if (inTagName.equals("track")) {
_trackList.add(_track);
- _inTrack = false;
}
- else if (_inTrackName && inTagName.equals("title")) {
+ else if (inTagName.equals("title")) {
_track.setTrackName(_value);
- _inTrackName = false;
}
- else if (_inDescription && inTagName.equals("description")) {
+ else if (inTagName.equals("description")) {
_track.setDescription(_value);
- _inDescription = false;
}
- else if (_inFileId && inTagName.equals("fileId")) {
- _track.setFileId(_value);
- _inFileId = false;
+ else if (inTagName.equals("fileId")) {
+ _track.setWebUrl("http://gpsies.com/map.do?fileId=" + _value);
}
- else if (_inTrackLength && inTagName.equals("trackLengthM")) {
+ else if (inTagName.equals("trackLengthM")) {
try {
_track.setLength(Double.parseDouble(_value));
}
catch (NumberFormatException nfe) {}
- _inTrackLength = false;
}
- else if (_inLink && inTagName.equals("downloadLink")) {
+ else if (inTagName.equals("downloadLink")) {
_track.setDownloadLink(_value);
- _inLink = false;
}
super.endElement(inUri, inLocalName, inTagName);
}
public void characters(char[] inCh, int inStart, int inLength)
throws SAXException
{
- _value = new String(inCh, inStart, inLength);
- // System.out.println("Value: '" + value + "'");
- // TODO: Note, this doesn't cope well with split characters for really long descriptions etc
+ String value = new String(inCh, inStart, inLength);
+ _value = (_value==null?value:_value+value);
super.characters(inCh, inStart, inLength);
}
/** List of tracks */
private ArrayList<GpsiesTrack> _trackList = null;
/** Column heading for track name */
- private static final String _nameColLabel = I18nManager.getText("dialog.gpsies.column.name");
+ private String _nameColLabel = null;
/** Column heading for length */
- private static final String _lengthColLabel = I18nManager.getText("dialog.gpsies.column.length");
+ private String _lengthColLabel = null;
+ /** Number of columns */
+ private int _numColumns = 2;
/** Formatter for distances */
private NumberFormat _distanceFormatter = NumberFormat.getInstance();
/**
* Constructor
+ * @param inColumn1Key key for first column
+ * @param inColumn2Key key for second column
*/
- public TrackListModel()
+ public TrackListModel(String inColumn1Key, String inColumn2Key)
{
+ _nameColLabel = I18nManager.getText(inColumn1Key);
+ if (inColumn2Key != null) {
+ _lengthColLabel = I18nManager.getText(inColumn2Key);
+ }
+ _numColumns = (_lengthColLabel != null?2:1);
_distanceFormatter.setMaximumFractionDigits(1);
}
*/
public int getColumnCount()
{
- return 2;
+ return _numColumns;
}
/**
*/
private void startUpload()
{
+ BufferedReader reader = null;
try
{
FormPoster poster = new FormPoster(new URL(GPSIES_URL));
_writer = new OutputStreamWriter(oStream);
new Thread(new Runnable() {
public void run() {
- boolean[] saveFlags = {true, true, true, false, true}; // export everything
+ boolean[] saveFlags = {true, true, true, true, false, true}; // export everything
try {
GpxExporter.exportData(_writer, _app.getTrackInfo(), _nameField.getText(), null, saveFlags, false);
- _writer.close();
- } catch (IOException e) {
- e.printStackTrace();
+ } catch (IOException e) {}
+ finally {
+ try {_writer.close();} catch (IOException e) {}
}
}
}).start();
BufferedInputStream answer = new BufferedInputStream(poster.post());
int response = poster.getResponseCode();
- BufferedReader reader = new BufferedReader(new InputStreamReader(answer));
+ reader = new BufferedReader(new InputStreamReader(answer));
String line = reader.readLine();
// Try to extract gpsies page url from the returned message
String pageUrl = null;
_app.showErrorMessageNoLookup(getNameKey(), I18nManager.getText("error.gpsies.uploadfailed") + ": "
+ ioe.getClass().getName() + " : " + ioe.getMessage());
}
+ finally {
+ try {if (reader != null) reader.close();} catch (IOException e) {}
+ }
_dialog.dispose();
}
}
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JProgressBar;
+
+import tim.prune.FunctionLibrary;
+import tim.prune.function.PlayAudioFunction;
+
+/**
+ * Class to update the supplied progress bar on the basis of
+ * the currently playing audio file (if any)
+ */
+public class AudioListener implements Runnable, ActionListener
+{
+ /** progress bar */
+ private JProgressBar _progressBar = null;
+
+ /**
+ * Constructor
+ * @param inBar progress bar object to update
+ */
+ public AudioListener(JProgressBar inBar) {
+ _progressBar = inBar;
+ }
+
+ /**
+ * React to button press
+ */
+ public void actionPerformed(ActionEvent inEvent) {
+ new Thread(this).start();
+ }
+
+ /**
+ * Loop and update progress bar
+ */
+ public void run()
+ {
+ int progress = 0;
+ while (progress >= 0)
+ {
+ try {
+ Thread.sleep(400);
+ }
+ catch (InterruptedException e) {}
+ progress = ((PlayAudioFunction) FunctionLibrary.FUNCTION_PLAY_AUDIO).getPercentage();
+ _progressBar.setVisible(progress >= 0);
+ _progressBar.setValue(progress);
+ }
+ }
+}
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
+import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
+import javax.swing.JProgressBar;
import javax.swing.border.EtchedBorder;
import tim.prune.DataSubscriber;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.data.Altitude;
+import tim.prune.data.AudioFile;
import tim.prune.data.Coordinate;
import tim.prune.data.DataPoint;
import tim.prune.data.Distance;
private JLabel _aveSpeedLabel = null;
// Photo details
+ private JPanel _photoDetailsPanel = null;
private JLabel _photoLabel = null;
private PhotoThumbnail _photoThumbnail = null;
private JLabel _photoTimestampLabel = null;
private JLabel _photoConnectedLabel = null;
private JPanel _rotationButtons = null;
+ // Audio details
+ private JPanel _audioDetailsPanel = null;
+ private JLabel _audioLabel = null;
+ private JLabel _audioConnectedLabel = null;
+ private JLabel _audioTimestampLabel = null;
+ private JLabel _audioLengthLabel = null;
+ private JProgressBar _audioProgress = null;
+ private JPanel _playAudioPanel = null;
+
// Units
private JComboBox _coordFormatDropdown = null;
private JComboBox _distUnitsDropdown = null;
private static final String LABEL_RANGE_ALTITUDE = I18nManager.getText("fieldname.altitude") + ": ";
private static final String LABEL_RANGE_CLIMB = I18nManager.getText("details.range.climb") + ": ";
private static final String LABEL_RANGE_DESCENT = ", " + I18nManager.getText("details.range.descent") + ": ";
+ private static final String LABEL_AUDIO_FILE = I18nManager.getText("details.audio.file") + ": ";
private static String LABEL_POINT_ALTITUDE_UNITS = null;
private static Altitude.Format LABEL_POINT_ALTITUDE_FORMAT = Altitude.Format.NO_FORMAT;
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
+ Font biggerFont = new JLabel().getFont();
+ biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f);
// Point details panel
- JPanel pointDetailsPanel = new JPanel();
- pointDetailsPanel.setLayout(new BoxLayout(pointDetailsPanel, BoxLayout.Y_AXIS));
- pointDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
- );
- JLabel pointDetailsLabel = new JLabel(I18nManager.getText("details.pointdetails"));
- Font biggerFont = pointDetailsLabel.getFont();
- biggerFont = biggerFont.deriveFont(Font.BOLD, biggerFont.getSize2D() + 2.0f);
- pointDetailsLabel.setFont(biggerFont);
- pointDetailsPanel.add(pointDetailsLabel);
+ JPanel pointDetailsPanel = makeDetailsPanel("details.pointdetails", biggerFont);
_indexLabel = new JLabel(I18nManager.getText("details.nopointselection"));
pointDetailsPanel.add(_indexLabel);
_latLabel = new JLabel("");
_altLabel = new JLabel("");
pointDetailsPanel.add(_altLabel);
_timeLabel = new JLabel("");
+ _timeLabel.setMinimumSize(new Dimension(120, 10));
pointDetailsPanel.add(_timeLabel);
_speedLabel = new JLabel("");
pointDetailsPanel.add(_speedLabel);
pointDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
// range details panel
- JPanel rangeDetailsPanel = new JPanel();
- rangeDetailsPanel.setLayout(new BoxLayout(rangeDetailsPanel, BoxLayout.Y_AXIS));
- rangeDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
- );
- JLabel rangeDetailsLabel = new JLabel(I18nManager.getText("details.rangedetails"));
- rangeDetailsLabel.setFont(biggerFont);
- rangeDetailsPanel.add(rangeDetailsLabel);
+ JPanel rangeDetailsPanel = makeDetailsPanel("details.rangedetails", biggerFont);
_rangeLabel = new JLabel(I18nManager.getText("details.norangeselection"));
rangeDetailsPanel.add(_rangeLabel);
_distanceLabel = new JLabel("");
rangeDetailsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
// photo details panel
- JPanel photoDetailsPanel = new JPanel();
- photoDetailsPanel.setLayout(new BoxLayout(photoDetailsPanel, BoxLayout.Y_AXIS));
- photoDetailsPanel.setBorder(BorderFactory.createCompoundBorder(
- BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
- );
- JLabel photoDetailsLabel = new JLabel(I18nManager.getText("details.photodetails"));
- photoDetailsLabel.setFont(biggerFont);
- photoDetailsPanel.add(photoDetailsLabel);
+ _photoDetailsPanel = makeDetailsPanel("details.photodetails", biggerFont);
_photoLabel = new JLabel(I18nManager.getText("details.nophoto"));
- photoDetailsPanel.add(_photoLabel);
+ _photoDetailsPanel.add(_photoLabel);
_photoTimestampLabel = new JLabel("");
- photoDetailsPanel.add(_photoTimestampLabel);
+ _photoTimestampLabel.setMinimumSize(new Dimension(120, 10));
+ _photoDetailsPanel.add(_photoTimestampLabel);
_photoConnectedLabel = new JLabel("");
- photoDetailsPanel.add(_photoConnectedLabel);
+ _photoDetailsPanel.add(_photoConnectedLabel);
_photoThumbnail = new PhotoThumbnail();
_photoThumbnail.setVisible(false);
_photoThumbnail.setPreferredSize(new Dimension(100, 100));
- photoDetailsPanel.add(_photoThumbnail);
+ _photoDetailsPanel.add(_photoThumbnail);
// Rotate buttons
JButton rotLeft = makeRotateButton(IconManager.ROTATE_LEFT, FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT);
JButton rotRight = makeRotateButton(IconManager.ROTATE_RIGHT, FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT);
+ JButton popup = makeRotateButton(IconManager.SHOW_DETAILS, FunctionLibrary.FUNCTION_PHOTO_POPUP);
_rotationButtons = new JPanel();
_rotationButtons.add(rotLeft);
_rotationButtons.add(rotRight);
+ _rotationButtons.add(Box.createHorizontalStrut(10));
+ _rotationButtons.add(popup);
_rotationButtons.setAlignmentX(Component.LEFT_ALIGNMENT);
_rotationButtons.setVisible(false);
- photoDetailsPanel.add(_rotationButtons);
+ _photoDetailsPanel.add(_rotationButtons);
+ _photoDetailsPanel.setVisible(false);
+
+ // audio details panel
+ _audioDetailsPanel = makeDetailsPanel("details.audiodetails", biggerFont);
+ _audioLabel = new JLabel(I18nManager.getText("details.noaudio"));
+ _audioDetailsPanel.add(_audioLabel);
+ _audioTimestampLabel = new JLabel("");
+ _audioTimestampLabel.setMinimumSize(new Dimension(120, 10));
+ _audioDetailsPanel.add(_audioTimestampLabel);
+ _audioLengthLabel = new JLabel("");
+ _audioDetailsPanel.add(_audioLengthLabel);
+ _audioConnectedLabel = new JLabel("");
+ _audioDetailsPanel.add(_audioConnectedLabel);
+ _audioProgress = new JProgressBar(0, 100);
+ _audioProgress.setString(I18nManager.getText("details.audio.playing"));
+ _audioProgress.setStringPainted(true);
+ _audioProgress.setVisible(false);
+ _audioDetailsPanel.add(_audioProgress);
+ _playAudioPanel = new JPanel();
+ _playAudioPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
+ JButton playAudio = makeRotateButton(IconManager.PLAY_AUDIO, FunctionLibrary.FUNCTION_PLAY_AUDIO);
+ playAudio.addActionListener(new AudioListener(_audioProgress));
+ _playAudioPanel.add(playAudio);
+ JButton stopAudio = makeRotateButton(IconManager.STOP_AUDIO, FunctionLibrary.FUNCTION_STOP_AUDIO);
+ _playAudioPanel.add(stopAudio);
+ _playAudioPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ _playAudioPanel.setVisible(false);
+ _audioDetailsPanel.add(_playAudioPanel);
+ _audioDetailsPanel.setVisible(false);
// add the details panels to the main panel
mainPanel.add(pointDetailsPanel);
mainPanel.add(Box.createVerticalStrut(5));
mainPanel.add(rangeDetailsPanel);
mainPanel.add(Box.createVerticalStrut(5));
- mainPanel.add(photoDetailsPanel);
+ mainPanel.add(_photoDetailsPanel);
+ mainPanel.add(Box.createVerticalStrut(5));
+ mainPanel.add(_audioDetailsPanel);
mainPanel.add(Box.createVerticalStrut(5));
// add the main panel at the top
add(mainPanel, BorderLayout.NORTH);
// Update current point data, if any
DataPoint currentPoint = _trackInfo.getCurrentPoint();
Selection selection = _trackInfo.getSelection();
+ if ((inUpdateType | DATA_ADDED_OR_REMOVED) > 0) selection.markInvalid();
int currentPointIndex = selection.getCurrentPointIndex();
_speedLabel.setText("");
Distance.Units distUnits = _distUnitsDropdown.getSelectedIndex()==0?Distance.Units.KILOMETRES:Distance.Units.MILES;
}
}
// show photo details and thumbnail
+ _photoDetailsPanel.setVisible(_trackInfo.getPhotoList().getNumPhotos() > 0);
Photo currentPhoto = _trackInfo.getPhotoList().getPhoto(_trackInfo.getSelection().getCurrentPhotoIndex());
if ((currentPoint == null || currentPoint.getPhoto() == null) && currentPhoto == null)
{
if (currentPhoto == null) {currentPhoto = currentPoint.getPhoto();}
_photoLabel.setText(I18nManager.getText("details.photofile") + ": " + currentPhoto.getFile().getName());
_photoTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentPhoto.getTimestamp().getText());
- _photoConnectedLabel.setText(I18nManager.getText("details.photo.connected") + ": "
+ _photoConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
+ (currentPhoto.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
_photoThumbnail.setVisible(true);
if ((inUpdateType & DataSubscriber.PHOTOS_MODIFIED) > 0) {_photoThumbnail.refresh();}
}
_photoThumbnail.repaint();
+
+ // audio details
+ _audioDetailsPanel.setVisible(_trackInfo.getAudioList().getNumAudios() > 0);
+ AudioFile currentAudio = _trackInfo.getAudioList().getAudio(_trackInfo.getSelection().getCurrentAudioIndex());
+ if (currentAudio == null) {
+ _audioLabel.setText(I18nManager.getText("details.noaudio"));
+ _audioTimestampLabel.setText("");
+ _audioLengthLabel.setText("");
+ _audioConnectedLabel.setText("");
+ }
+ else
+ {
+ _audioLabel.setText(LABEL_AUDIO_FILE + currentAudio.getFile().getName());
+ _audioTimestampLabel.setText(LABEL_POINT_TIMESTAMP + currentAudio.getTimestamp().getText());
+ int audioLength = currentAudio.getLengthInSeconds();
+ _audioLengthLabel.setText(audioLength < 0?"":LABEL_RANGE_DURATION + DisplayUtils.buildDurationString(audioLength));
+ _audioConnectedLabel.setText(I18nManager.getText("details.media.connected") + ": "
+ + (currentAudio.getCurrentStatus() == Photo.Status.NOT_CONNECTED ?
+ I18nManager.getText("dialog.about.no"):I18nManager.getText("dialog.about.yes")));
+ }
+ _playAudioPanel.setVisible(currentAudio != null);
}
return inCoord;
}
+ /**
+ * Make a details subpanel
+ * @param inNameKey key to use for top label
+ * @param inFont font for top label
+ * @return panel with correct layout, label
+ */
+ private static JPanel makeDetailsPanel(String inNameKey, Font inFont)
+ {
+ JPanel detailsPanel = new JPanel();
+ detailsPanel.setLayout(new BoxLayout(detailsPanel, BoxLayout.Y_AXIS));
+ detailsPanel.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
+ );
+ JLabel detailsLabel = new JLabel(I18nManager.getText(inNameKey));
+ detailsLabel.setFont(inFont);
+ detailsPanel.add(detailsLabel);
+ return detailsPanel;
+ }
+
/**
* Create a little button for rotating the current photo
* @param inIcon icon to use (from IconManager)
--- /dev/null
+package tim.prune.gui;
+
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+import javax.swing.JDialog;
+
+/**
+ * Convenience class to close a dialog when the escape key is pressed
+ */
+public class DialogCloser extends KeyAdapter
+{
+ /** dialog to close */
+ private JDialog _dialog = null;
+
+ /**
+ * Constructor
+ * @param inDialog dialog to close
+ */
+ public DialogCloser(JDialog inDialog) {
+ _dialog = inDialog;
+ }
+
+ /**
+ * React to the release of the escape key
+ */
+ public void keyReleased(KeyEvent e)
+ {
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ _dialog.dispose();
+ }
+ }
+}
public static final String ROTATE_LEFT = "rotate_left_icon.png";
/** Icon for rotating photos rightwards */
public static final String ROTATE_RIGHT = "rotate_right_icon.png";
+ /** Icon for showing photo popup */
+ public static final String SHOW_DETAILS = "show_details_icon.gif";
+ /** Icon for playing audio file */
+ public static final String PLAY_AUDIO = "play_audio.gif";
+ /** Icon for stopping the current audio file */
+ public static final String STOP_AUDIO = "stop_audio.gif";
/**
* Get the specified image
--- /dev/null
+package tim.prune.gui;
+
+import javax.swing.AbstractListModel;
+
+import tim.prune.data.MediaFile;
+import tim.prune.data.MediaList;
+
+/**
+ * Class to act as list model for the photo list and audio list
+ */
+public class MediaListModel extends AbstractListModel
+{
+ /** media list */
+ MediaList _media = null;
+
+ /**
+ * Constructor giving MediaList object
+ * @param inList MediaList
+ */
+ public MediaListModel(MediaList inList) {
+ _media = inList;
+ }
+
+ /**
+ * @see javax.swing.ListModel#getSize()
+ */
+ public int getSize() {
+ return _media.getNumMedia();
+ }
+
+ /**
+ * @see javax.swing.ListModel#getElementAt(int)
+ */
+ public Object getElementAt(int inIndex)
+ {
+ MediaFile m = _media.getMedia(inIndex);
+ // * means modified since loading
+ return (m.getCurrentStatus() == m.getOriginalStatus()?"":"* ") + m.getFile().getName();
+ }
+
+ /**
+ * Fire event to notify that contents have changed
+ */
+ public void fireChanged() {
+ this.fireContentsChanged(this, 0, getSize()-1);
+ }
+}
import tim.prune.I18nManager;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
+import tim.prune.data.AudioFile;
import tim.prune.data.Photo;
-import tim.prune.data.PhotoList;
import tim.prune.data.Selection;
import tim.prune.data.Track;
import tim.prune.data.TrackInfo;
private App _app = null;
private Track _track = null;
private Selection _selection = null;
- private PhotoList _photos = null;
// Menu items which need enabling/disabling
private JMenuItem _sendGpsItem = null;
private JMenuItem _getGpsiesItem = null;
private JMenuItem _uploadGpsiesItem = null;
private JMenuItem _lookupSrtmItem = null;
+ private JMenuItem _lookupWikipediaItem = null;
+ private JMenuItem _downloadOsmItem = null;
private JMenuItem _distanceItem = null;
private JMenuItem _fullRangeDetailsItem = null;
private JMenuItem _saveExifItem = null;
+ private JMenuItem _photoPopupItem = null;
private JMenuItem _selectNoPhotoItem = null;
private JMenuItem _connectPhotoItem = null;
- private JMenuItem _deletePhotoItem = null;
+ private JMenuItem _removePhotoItem = null;
private JMenuItem _disconnectPhotoItem = null;
private JMenuItem _correlatePhotosItem = null;
private JMenuItem _rearrangePhotosItem = null;
private JMenuItem _rotatePhotoLeft = null;
private JMenuItem _rotatePhotoRight = null;
private JMenuItem _ignoreExifThumb = null;
+ private JMenuItem _connectAudioItem = null;
+ private JMenuItem _disconnectAudioItem = null;
+ private JMenuItem _removeAudioItem = null;
+ private JMenuItem _correlateAudiosItem = null;
+ private JMenuItem _selectNoAudioItem = null;
private JCheckBoxMenuItem _onlineCheckbox = null;
// ActionListeners for reuse by menu and toolbar
private ActionListener _deleteRangeAction = null;
private ActionListener _selectStartAction = null;
private ActionListener _selectEndAction = null;
- private ActionListener _connectPhotoAction = null;
// Toolbar buttons which need enabling/disabling
private JButton _saveButton = null;
private JButton _deleteRangeButton = null;
private JButton _selectStartButton = null;
private JButton _selectEndButton = null;
- private JButton _connectPhotoButton = null;
+ private JButton _connectButton = null;
/** Array of key events */
private static final int[] KEY_EVENTS = {
_app = inApp;
_track = inTrackInfo.getTrack();
_selection = inTrackInfo.getSelection();
- _photos = inTrackInfo.getPhotoList();
}
JMenuItem openMenuItem = new JMenuItem(I18nManager.getText("function.open"));
setShortcut(openMenuItem, "shortcut.menu.file.open");
_openFileAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.openFile();
}
};
// Add photos
JMenuItem addPhotosMenuItem = new JMenuItem(I18nManager.getText("menu.file.addphotos"));
_addPhotoAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.addPhotos();
}
};
addPhotosMenuItem.addActionListener(_addPhotoAction);
fileMenu.add(addPhotosMenuItem);
+ // Add audio files
+ JMenuItem addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
+ fileMenu.add(addAudioMenuItem);
fileMenu.addSeparator();
// Load from GPS
JMenuItem loadFromGpsMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSLOAD);
setShortcut(loadFromGpsMenuItem, "shortcut.menu.file.load");
fileMenu.add(loadFromGpsMenuItem);
// Send to GPS
- _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE);
- _sendGpsItem.setEnabled(false);
+ _sendGpsItem = makeMenuItem(FunctionLibrary.FUNCTION_GPSSAVE, false);
fileMenu.add(_sendGpsItem);
fileMenu.addSeparator();
// Save
_saveItem = new JMenuItem(I18nManager.getText("menu.file.save"), KeyEvent.VK_S);
setShortcut(_saveItem, "shortcut.menu.file.save");
_saveAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.saveFile();
}
};
_saveItem.setEnabled(false);
fileMenu.add(_saveItem);
// Export - Kml
- _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT);
- _exportKmlItem.setEnabled(false);
+ _exportKmlItem = makeMenuItem(FunctionLibrary.FUNCTION_KMLEXPORT, false);
fileMenu.add(_exportKmlItem);
// Gpx
- _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT);
- _exportGpxItem.setEnabled(false);
+ _exportGpxItem = makeMenuItem(FunctionLibrary.FUNCTION_GPXEXPORT, false);
fileMenu.add(_exportGpxItem);
// Pov
- _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT);
- _exportPovItem.setEnabled(false);
+ _exportPovItem = makeMenuItem(FunctionLibrary.FUNCTION_POVEXPORT, false);
fileMenu.add(_exportPovItem);
// Svg
- _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT);
- _exportSvgItem.setEnabled(false);
+ _exportSvgItem = makeMenuItem(FunctionLibrary.FUNCTION_SVGEXPORT, false);
fileMenu.add(_exportSvgItem);
fileMenu.addSeparator();
JMenuItem exitMenuItem = new JMenuItem(I18nManager.getText("menu.file.exit"));
exitMenuItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.exit();
}
});
_undoItem = new JMenuItem(I18nManager.getText("menu.track.undo"));
setShortcut(_undoItem, "shortcut.menu.track.undo");
_undoAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.beginUndo();
}
};
trackMenu.add(_undoItem);
_clearUndoItem = new JMenuItem(I18nManager.getText("menu.track.clearundo"));
_clearUndoItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.clearUndo();
}
});
_clearUndoItem.setEnabled(false);
trackMenu.add(_clearUndoItem);
trackMenu.addSeparator();
- _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS);
+ _compressItem = makeMenuItem(FunctionLibrary.FUNCTION_COMPRESS, false);
setShortcut(_compressItem, "shortcut.menu.edit.compress");
- _compressItem.setEnabled(false);
trackMenu.add(_compressItem);
_deleteMarkedPointsItem = new JMenuItem(I18nManager.getText("menu.track.deletemarked"));
_deleteMarkedPointsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.finishCompressTrack();
}
});
_rearrangeMenu.setEnabled(false);
JMenuItem rearrangeStartItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.start"));
rearrangeStartItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_START);
}
});
_rearrangeMenu.add(rearrangeStartItem);
JMenuItem rearrangeEndItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.end"));
rearrangeEndItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_END);
}
});
_rearrangeMenu.add(rearrangeEndItem);
JMenuItem rearrangeNearestItem = new JMenuItem(I18nManager.getText("menu.track.rearrange.nearest"));
rearrangeNearestItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
FunctionLibrary.FUNCTION_REARRANGE_WAYPOINTS.rearrangeWaypoints(Rearrange.TO_NEAREST);
}
});
_rearrangeMenu.add(rearrangeNearestItem);
trackMenu.add(_rearrangeMenu);
// Get gpsies tracks
- _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES);
- _getGpsiesItem.setEnabled(false);
+ _getGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_GET_GPSIES, false);
trackMenu.add(_getGpsiesItem);
// Upload to gpsies
- _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES);
- _uploadGpsiesItem.setEnabled(false);
+ _uploadGpsiesItem = makeMenuItem(FunctionLibrary.FUNCTION_UPLOAD_GPSIES, false);
trackMenu.add(_uploadGpsiesItem);
- _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM);
- _lookupSrtmItem.setEnabled(false);
+ _lookupSrtmItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_SRTM, false);
trackMenu.add(_lookupSrtmItem);
+ _lookupWikipediaItem = makeMenuItem(FunctionLibrary.FUNCTION_LOOKUP_WIKIPEDIA, false);
+ trackMenu.add(_lookupWikipediaItem);
+ JMenuItem searchWikipediaNamesItem = makeMenuItem(FunctionLibrary.FUNCTION_SEARCH_WIKIPEDIA);
+ trackMenu.add(searchWikipediaNamesItem);
+ _downloadOsmItem = makeMenuItem(FunctionLibrary.FUNCTION_DOWNLOAD_OSM, false);
+ trackMenu.add(_downloadOsmItem);
menubar.add(trackMenu);
// Range menu
setShortcut(_selectAllItem, "shortcut.menu.range.all");
_selectAllItem.setEnabled(false);
_selectAllItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_selection.selectRange(0, _track.getNumPoints()-1);
}
});
_selectNoneItem = new JMenuItem(I18nManager.getText("menu.range.none"));
_selectNoneItem.setEnabled(false);
_selectNoneItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.selectNone();
}
});
_selectStartItem = new JMenuItem(I18nManager.getText("menu.range.start"));
_selectStartItem.setEnabled(false);
_selectStartAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_selection.selectRangeStart();
}
};
_selectEndItem = new JMenuItem(I18nManager.getText("menu.range.end"));
_selectEndItem.setEnabled(false);
_selectEndAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_selection.selectRangeEnd();
}
};
rangeMenu.addSeparator();
_deleteRangeItem = new JMenuItem(I18nManager.getText("menu.range.deleterange"));
_deleteRangeAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.deleteSelectedRange();
}
};
rangeMenu.add(_deleteRangeItem);
_reverseItem = new JMenuItem(I18nManager.getText("menu.range.reverse"));
_reverseItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.reverseRange();
}
});
_reverseItem.setEnabled(false);
rangeMenu.add(_reverseItem);
- _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET);
- _addTimeOffsetItem.setEnabled(false);
+ _addTimeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_TIME_OFFSET, false);
rangeMenu.add(_addTimeOffsetItem);
- _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET);
- _addAltitudeOffsetItem.setEnabled(false);
+ _addAltitudeOffsetItem = makeMenuItem(FunctionLibrary.FUNCTION_ADD_ALTITUDE_OFFSET, false);
rangeMenu.add(_addAltitudeOffsetItem);
_mergeSegmentsItem = new JMenuItem(I18nManager.getText("menu.range.mergetracksegments"));
_mergeSegmentsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.mergeTrackSegments();
}
});
_mergeSegmentsItem.setEnabled(false);
rangeMenu.add(_mergeSegmentsItem);
- _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES);
- _deleteFieldValuesItem.setEnabled(false);
+ _deleteFieldValuesItem = makeMenuItem(FunctionLibrary.FUNCTION_DELETE_FIELD_VALUES, false);
rangeMenu.add(_deleteFieldValuesItem);
rangeMenu.addSeparator();
_interpolateItem = new JMenuItem(I18nManager.getText("menu.range.interpolate"));
_interpolateItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.interpolateSelection();
}
});
rangeMenu.add(_interpolateItem);
_averageItem = new JMenuItem(I18nManager.getText("menu.range.average"));
_averageItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.averageSelection();
}
});
});
_cutAndMoveItem.setEnabled(false);
rangeMenu.add(_cutAndMoveItem);
- _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES);
- _convertNamesToTimesItem.setEnabled(false);
+ _convertNamesToTimesItem = makeMenuItem(FunctionLibrary.FUNCTION_CONVERT_NAMES_TO_TIMES, false);
rangeMenu.add(_convertNamesToTimesItem);
menubar.add(rangeMenu);
setAltKey(pointMenu, "altkey.menu.point");
_editPointItem = new JMenuItem(I18nManager.getText("menu.point.editpoint"));
_editPointAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.editCurrentPoint();
}
};
_editPointItem.addActionListener(_editPointAction);
_editPointItem.setEnabled(false);
pointMenu.add(_editPointItem);
- _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME);
- _editWaypointNameItem.setEnabled(false);
+ _editWaypointNameItem = makeMenuItem(FunctionLibrary.FUNCTION_EDIT_WAYPOINT_NAME, false);
pointMenu.add(_editWaypointNameItem);
_deletePointItem = new JMenuItem(I18nManager.getText("menu.point.deletepoint"));
_deletePointAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.deleteCurrentPoint();
}
};
pointMenu.add(_deletePointItem);
pointMenu.addSeparator();
// find a waypoint
- _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT);
- _findWaypointItem.setEnabled(false);
+ _findWaypointItem = makeMenuItem(FunctionLibrary.FUNCTION_FIND_WAYPOINT, false);
pointMenu.add(_findWaypointItem);
// duplicate current point
- _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT);
- _duplicatePointItem.setEnabled(false);
+ _duplicatePointItem = makeMenuItem(FunctionLibrary.FUNCTION_DUPLICATE_POINT, false);
pointMenu.add(_duplicatePointItem);
// paste coordinates function
JMenuItem pasteCoordsItem = makeMenuItem(FunctionLibrary.FUNCTION_PASTE_COORDINATES);
});
viewMenu.add(sidebarsCheckbox);
// 3d
- _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D);
- _show3dItem.setEnabled(false);
+ _show3dItem = makeMenuItem(FunctionLibrary.FUNCTION_3D, false);
viewMenu.add(_show3dItem);
// browser submenu
_browserMapMenu = new JMenu(I18nManager.getText("menu.view.browser"));
_browserMapMenu.setEnabled(false);
JMenuItem googleMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.google"));
googleMapsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.showExternalMap(UrlGenerator.MAP_SOURCE_GOOGLE);
}
});
_browserMapMenu.add(googleMapsItem);
JMenuItem openMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.openstreetmap"));
openMapsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.showExternalMap(UrlGenerator.MAP_SOURCE_OSM);
}
});
_browserMapMenu.add(openMapsItem);
JMenuItem mapquestMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.mapquest"));
mapquestMapsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.showExternalMap(UrlGenerator.MAP_SOURCE_MAPQUEST);
}
});
_browserMapMenu.add(mapquestMapsItem);
JMenuItem yahooMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.yahoo"));
yahooMapsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.showExternalMap(UrlGenerator.MAP_SOURCE_YAHOO);
}
});
_browserMapMenu.add(yahooMapsItem);
JMenuItem bingMapsItem = new JMenuItem(I18nManager.getText("menu.view.browser.bing"));
bingMapsItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.showExternalMap(UrlGenerator.MAP_SOURCE_BING);
}
});
_browserMapMenu.add(bingMapsItem);
viewMenu.add(_browserMapMenu);
// Charts
- _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS);
- _chartItem.setEnabled(false);
+ _chartItem = makeMenuItem(FunctionLibrary.FUNCTION_CHARTS, false);
viewMenu.add(_chartItem);
// Distances
- _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES);
- _distanceItem.setEnabled(false);
+ _distanceItem = makeMenuItem(FunctionLibrary.FUNCTION_DISTANCES, false);
viewMenu.add(_distanceItem);
// full range details
- _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS);
- _fullRangeDetailsItem.setEnabled(false);
+ _fullRangeDetailsItem = makeMenuItem(FunctionLibrary.FUNCTION_FULL_RANGE_DETAILS, false);
viewMenu.add(_fullRangeDetailsItem);
menubar.add(viewMenu);
photoMenu.add(addPhotosMenuItem);
_saveExifItem = new JMenuItem(I18nManager.getText("menu.photo.saveexif"));
_saveExifItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_app.saveExif();
}
});
_saveExifItem.setEnabled(false);
photoMenu.add(_saveExifItem);
- _connectPhotoItem = new JMenuItem(I18nManager.getText("menu.photo.connect"));
- _connectPhotoAction = new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _app.connectPhotoToPoint();
+ // Deselect current photo
+ _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none"));
+ _selectNoPhotoItem.setEnabled(false);
+ _selectNoPhotoItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _app.getTrackInfo().selectPhoto(-1);
}
- };
- _connectPhotoItem.addActionListener(_connectPhotoAction);
- _connectPhotoItem.setEnabled(false);
+ });
+ photoMenu.add(_selectNoPhotoItem);
photoMenu.addSeparator();
+ _connectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
photoMenu.add(_connectPhotoItem);
// disconnect photo
- _disconnectPhotoItem = new JMenuItem(I18nManager.getText("menu.photo.disconnect"));
- _disconnectPhotoItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _app.disconnectPhotoFromPoint();
- }
- });
- _disconnectPhotoItem.setEnabled(false);
+ _disconnectPhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_PHOTO, false);
photoMenu.add(_disconnectPhotoItem);
- _deletePhotoItem = new JMenuItem(I18nManager.getText("menu.photo.delete"));
- _deletePhotoItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _app.deleteCurrentPhoto();
- }
- });
- _deletePhotoItem.setEnabled(false);
- photoMenu.add(_deletePhotoItem);
+ _removePhotoItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_PHOTO, false);
+ photoMenu.add(_removePhotoItem);
// Rotate current photo
- _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT);
- _rotatePhotoLeft.setEnabled(false);
+ _rotatePhotoLeft = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_LEFT, false);
photoMenu.add(_rotatePhotoLeft);
- _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT);
- _rotatePhotoRight.setEnabled(false);
+ _rotatePhotoRight = makeMenuItem(FunctionLibrary.FUNCTION_ROTATE_PHOTO_RIGHT, false);
photoMenu.add(_rotatePhotoRight);
- _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB);
- _ignoreExifThumb.setEnabled(false);
+ // Show photo popup
+ _photoPopupItem = makeMenuItem(FunctionLibrary.FUNCTION_PHOTO_POPUP, false);
+ photoMenu.add(_photoPopupItem);
+ _ignoreExifThumb = makeMenuItem(FunctionLibrary.FUNCTION_IGNORE_EXIF_THUMB, false);
photoMenu.add(_ignoreExifThumb);
- _selectNoPhotoItem = new JMenuItem(I18nManager.getText("menu.range.none"));
- _selectNoPhotoItem.setEnabled(false);
- _selectNoPhotoItem.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
- _app.getTrackInfo().selectPhoto(-1);
- }
- });
- photoMenu.add(_selectNoPhotoItem);
photoMenu.addSeparator();
// correlate all photos
- _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS);
- _correlatePhotosItem.setEnabled(false);
+ _correlatePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_PHOTOS, false);
photoMenu.add(_correlatePhotosItem);
// rearrange photo points
- _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS);
- _rearrangePhotosItem.setEnabled(false);
+ _rearrangePhotosItem = makeMenuItem(FunctionLibrary.FUNCTION_REARRANGE_PHOTOS, false);
photoMenu.add(_rearrangePhotosItem);
menubar.add(photoMenu);
+ // Audio menu
+ JMenu audioMenu = new JMenu(I18nManager.getText("menu.audio"));
+ setAltKey(audioMenu, "altkey.menu.audio");
+ addAudioMenuItem = makeMenuItem(FunctionLibrary.FUNCTION_LOAD_AUDIO);
+ audioMenu.add(addAudioMenuItem);
+ _selectNoAudioItem = new JMenuItem(I18nManager.getText("menu.range.none"));
+ _selectNoAudioItem.setEnabled(false);
+ _selectNoAudioItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _app.getTrackInfo().selectAudio(-1);
+ }
+ });
+ audioMenu.add(_selectNoAudioItem);
+ audioMenu.addSeparator();
+ // connect audio
+ _connectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_CONNECT_TO_POINT, false);
+ audioMenu.add(_connectAudioItem);
+ // Disconnect current audio file
+ _disconnectAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_DISCONNECT_AUDIO, false);
+ audioMenu.add(_disconnectAudioItem);
+ // Remove current audio file
+ _removeAudioItem = makeMenuItem(FunctionLibrary.FUNCTION_REMOVE_AUDIO, false);
+ audioMenu.add(_removeAudioItem);
+ audioMenu.addSeparator();
+ // Correlate audio files
+ _correlateAudiosItem = makeMenuItem(FunctionLibrary.FUNCTION_CORRELATE_AUDIOS, false);
+ audioMenu.add(_correlateAudiosItem);
+ menubar.add(audioMenu);
+
// Settings menu
JMenu settingsMenu = new JMenu(I18nManager.getText("menu.settings"));
setAltKey(settingsMenu, "altkey.menu.settings");
settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_PATHS));
// Set colours
settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_COLOURS));
+ // Set line width used for drawing
+ settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LINE_WIDTH));
// Set language
settingsMenu.add(makeMenuItem(FunctionLibrary.FUNCTION_SET_LANGUAGE));
settingsMenu.addSeparator();
return menubar;
}
+ /**
+ * Convenience method for making a menu item using a function
+ * @param inFunction function
+ * @param inEnabled flag to specify initial enabled state
+ * @return menu item using localized name of function
+ */
+ private static JMenuItem makeMenuItem(GenericFunction inFunction, boolean inEnabled)
+ {
+ JMenuItem item = makeMenuItem(inFunction);
+ item.setEnabled(inEnabled);
+ return item;
+ }
+
/**
* Convenience method for making a menu item using a function
* @param inFunction function
_selectEndButton.addActionListener(_selectEndAction);
_selectEndButton.setEnabled(false);
toolbar.add(_selectEndButton);
- _connectPhotoButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO));
- _connectPhotoButton.setToolTipText(I18nManager.getText("menu.photo.connect"));
- _connectPhotoButton.addActionListener(_connectPhotoAction);
- _connectPhotoButton.setEnabled(false);
- toolbar.add(_connectPhotoButton);
+ // Connect to point
+ _connectButton = new JButton(IconManager.getImageIcon(IconManager.CONNECT_PHOTO));
+ _connectButton.setToolTipText(I18nManager.getText(FunctionLibrary.FUNCTION_CONNECT_TO_POINT.getNameKey()));
+ _connectButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ FunctionLibrary.FUNCTION_CONNECT_TO_POINT.begin();
+ }
+ });
+ _connectButton.setEnabled(false);
+ toolbar.add(_connectButton);
// finish off
toolbar.setFloatable(false);
return toolbar;
_getGpsiesItem.setEnabled(hasData);
_uploadGpsiesItem.setEnabled(hasData && _track.hasTrackPoints());
_lookupSrtmItem.setEnabled(hasData);
+ _lookupWikipediaItem.setEnabled(hasData);
+ _downloadOsmItem.setEnabled(hasData);
_findWaypointItem.setEnabled(hasData && _track.hasWaypoints());
// is undo available?
boolean hasUndo = !_app.getUndoStack().isEmpty();
_selectEndButton.setEnabled(hasPoint);
_duplicatePointItem.setEnabled(hasPoint);
// are there any photos?
- boolean anyPhotos = _photos != null && _photos.getNumPhotos() > 0;
+ boolean anyPhotos = _app.getTrackInfo().getPhotoList().getNumPhotos() > 0;
_saveExifItem.setEnabled(anyPhotos);
- // is there a current photo?
- boolean hasPhoto = anyPhotos && _selection.getCurrentPhotoIndex() >= 0;
- // connect is available if photo and point selected, and photo has no point
- Photo currentPhoto = _photos.getPhoto(_selection.getCurrentPhotoIndex());
- boolean connectAvailable = hasPhoto && hasPoint && currentPhoto != null
- && currentPhoto.getDataPoint() == null;
- _connectPhotoItem.setEnabled(connectAvailable);
- _connectPhotoButton.setEnabled(connectAvailable);
- _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getDataPoint() != null);
+ // is there a current photo, audio?
+ Photo currentPhoto = _app.getTrackInfo().getCurrentPhoto();
+ boolean hasPhoto = currentPhoto != null;
+ AudioFile currentAudio = _app.getTrackInfo().getCurrentAudio();
+ boolean hasAudio = currentAudio != null;
+ // connect is available if (photo/audio) and point selected, and media has no point
+ boolean connectAvailable = (hasPhoto && hasPoint && currentPhoto.getDataPoint() == null)
+ || (hasAudio && hasPoint && currentAudio.getDataPoint() == null);
+ _connectPhotoItem.setEnabled(hasPhoto && hasPoint && currentPhoto.getDataPoint() == null);
+ _connectButton.setEnabled(connectAvailable);
+ _disconnectPhotoItem.setEnabled(hasPhoto && currentPhoto.getDataPoint() != null);
_correlatePhotosItem.setEnabled(anyPhotos && hasData);
_rearrangePhotosItem.setEnabled(anyPhotos && hasData && _track.getNumPoints() > 1);
- _deletePhotoItem.setEnabled(hasPhoto);
+ _removePhotoItem.setEnabled(hasPhoto);
_rotatePhotoLeft.setEnabled(hasPhoto);
_rotatePhotoRight.setEnabled(hasPhoto);
- _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto != null && currentPhoto.getExifThumbnail() != null);
+ _photoPopupItem.setEnabled(hasPhoto);
+ _ignoreExifThumb.setEnabled(hasPhoto && currentPhoto.getExifThumbnail() != null);
_selectNoPhotoItem.setEnabled(hasPhoto);
+ boolean anyAudios = _app.getTrackInfo().getAudioList().getNumAudios() > 0;
+ _selectNoAudioItem.setEnabled(hasAudio);
+ _removeAudioItem.setEnabled(hasAudio);
+ _connectAudioItem.setEnabled(hasAudio && hasPoint && currentAudio.getDataPoint() == null);
+ _disconnectAudioItem.setEnabled(hasAudio && _app.getTrackInfo().getCurrentAudio().getDataPoint() != null);
+ _correlateAudiosItem.setEnabled(anyAudios && hasData);
// is there a current range?
boolean hasRange = (hasData && _selection.hasRangeSelected());
_deleteRangeItem.setEnabled(hasRange);
+++ /dev/null
-package tim.prune.gui;
-
-import javax.swing.AbstractListModel;
-
-import tim.prune.data.Photo;
-import tim.prune.data.PhotoList;
-
-/**
- * Class to act as list model for the photo list
- */
-public class PhotoListModel extends AbstractListModel
-{
- PhotoList _photos = null;
-
- /**
- * Constructor giving PhotoList object
- * @param inList PhotoList
- */
- public PhotoListModel(PhotoList inList)
- {
- _photos = inList;
- }
-
- /**
- * @see javax.swing.ListModel#getSize()
- */
- public int getSize()
- {
- return _photos.getNumPhotos();
- }
-
- /**
- * @see javax.swing.ListModel#getElementAt(int)
- */
- public Object getElementAt(int inIndex)
- {
- return _photos.getPhoto(inIndex).getFile().getName();
- }
-
- /**
- * Get the Photo at the given index
- * @param inIndex index number, starting at 0
- * @return Photo object
- */
- public Photo getPhoto(int inIndex)
- {
- return _photos.getPhoto(inIndex);
- }
-
- /**
- * Fire event to notify that contents have changed
- */
- public void fireChanged()
- {
- this.fireContentsChanged(this, 0, getSize()-1);
- }
-}
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
-import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PhotoThumbnail extends JPanel implements Runnable
{
private Photo _photo = null;
- private BufferedImage _thumbnail = null;
+ private Image _thumbnail = null;
private boolean _loadingImage = false;
private boolean _loadFailed = false;
+ private boolean _inPanel = false;
/** String to show before photo is loaded */
private static final String LOADING_STRING = I18nManager.getText("details.photo.loading") + " ...";
*/
public PhotoThumbnail()
{
- setOpaque(true);
+ this(true);
}
+ /**
+ * Constructor
+ * @param inPanel true if thumbnail is inside panel
+ */
+ public PhotoThumbnail(boolean inPanel)
+ {
+ setOpaque(true);
+ _inPanel = inPanel;
+ }
/**
* Set the Photo
public void setPhoto(Photo inPhoto)
{
// Check whether the photo has changed
- if (_photo != inPhoto) {
+ if (_photo != inPhoto)
+ {
_photo = inPhoto;
_thumbnail = null;
_loadFailed = false;
/**
* Force a refresh / reload
*/
- public void refresh() {
+ public void refresh()
+ {
_thumbnail = null;
_loadFailed = false;
}
{
// Copy scaled, smoothed (and rotated) image into scaled
int usableWidth = getParent().getWidth()-10;
- Image scaled = ImageUtils.rotateImage(_thumbnail, usableWidth, usableWidth, _photo.getRotationDegrees());
+ int usableHeight = (_inPanel?usableWidth:getHeight()-10);
+ Image scaled = ImageUtils.rotateImage(_thumbnail, usableWidth, usableHeight, _photo.getRotationDegrees());
int scaleWidth = scaled.getWidth(null);
int scaleHeight = scaled.getHeight(null);
// Draw scaled / rotated image to component
int horizOffset = (getWidth() - scaleWidth) / 2;
int vertOffset = (getHeight() - scaleHeight) / 2;
inG.drawImage(scaled, horizOffset, vertOffset, scaleWidth, scaleHeight, null);
- if (getHeight() < getWidth() || getHeight() > usableWidth)
+ // Special resize behaviour when locked inside details panel
+ if (_inPanel && (getHeight() < getWidth() || getHeight() > usableWidth))
{
Dimension newsize = new Dimension(usableWidth, usableWidth);
setPreferredSize(newsize);
*/
public void run()
{
- // Use exif thumbnail?
- if (_photo.getExifThumbnail() != null) {
- Image image = new ImageIcon(_photo.getExifThumbnail()).getImage();
- _thumbnail = ImageUtils.createScaledImage(image, image.getWidth(null), image.getHeight(null));
- image = null;
- }
- else
+ if (_inPanel)
{
- // no exif thumbnail available, going to have to read whole thing
- int picWidth = _photo.getWidth();
- int picHeight = _photo.getHeight();
- if (picWidth > -1 && picHeight > -1)
- {
- // Just set a "reasonable" thumbnail size for now
- final int DEFAULT_THUMB_SIZE = 400;
- // calculate maximum thumbnail size
- Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, DEFAULT_THUMB_SIZE, DEFAULT_THUMB_SIZE);
- // Make icon to load image into
- Image image = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage();
- // save scaled, smoothed thumbnail for reuse
- _thumbnail = ImageUtils.createScaledImage(image, thumbSize.width, thumbSize.height);
+ // use either exif thumbnail or photo scaled down to sensible size
+ if (_photo.getExifThumbnail() != null) {
+ // Use exif thumbnail
+ Image image = new ImageIcon(_photo.getExifThumbnail()).getImage();
+ _thumbnail = ImageUtils.createScaledImage(image, image.getWidth(null), image.getHeight(null));
image = null;
}
- else _loadFailed = true;
+ else
+ {
+ // no exif thumbnail available, going to have to read whole thing
+ int picWidth = _photo.getWidth();
+ int picHeight = _photo.getHeight();
+ if (picWidth > -1 && picHeight > -1)
+ {
+ // Just set a "reasonable" thumbnail size for now
+ final int DEFAULT_THUMB_SIZE = 400;
+ // calculate maximum thumbnail size
+ Dimension thumbSize = ImageUtils.getThumbnailSize(picWidth, picHeight, DEFAULT_THUMB_SIZE, DEFAULT_THUMB_SIZE);
+ // Make icon to load image into
+ Image image = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage();
+ // save scaled, smoothed thumbnail for reuse
+ _thumbnail = ImageUtils.createScaledImage(image, thumbSize.width, thumbSize.height);
+ image = null;
+ }
+ else _loadFailed = true;
+ }
+ }
+ else {
+ _thumbnail = new ImageIcon(_photo.getFile().getAbsolutePath()).getImage();
}
_loadingImage = false;
repaint();
private JScrollBar _scroller = null;
private boolean _ignoreScrollEvents = false;
- // Photos
- private JList _photoList = null;
- private PhotoListModel _photoListModel = null;
+ // Panel containing lists
+ private JPanel _listsPanel = null;
+ private int _visiblePanels = 1;
// Waypoints
+ private JPanel _waypointListPanel = null;
private JList _waypointList = null;
private WaypointListModel _waypointListModel = null;
+ // Photos
+ private JPanel _photoListPanel = null;
+ private JList _photoList = null;
+ private MediaListModel _photoListModel = null;
+ // Audio files
+ private JPanel _audioListPanel = null;
+ private JList _audioList = null;
+ private MediaListModel _audioListModel = null;
// scrollbar interval
private static final int SCROLLBAR_INTERVAL = 50;
_trackpointsLabel = new JLabel(I18nManager.getText("details.notrack"));
trackDetailsPanel.add(_trackpointsLabel);
_filenameLabel = new JLabel("");
+ _filenameLabel.setMinimumSize(new Dimension(120, 10));
trackDetailsPanel.add(_filenameLabel);
// Scroll bar
_scroller = new JScrollBar(JScrollBar.HORIZONTAL, 0, SCROLLBAR_INTERVAL, 0, 100);
_scroller.addAdjustmentListener(new AdjustmentListener() {
- public void adjustmentValueChanged(AdjustmentEvent e)
- {
+ public void adjustmentValueChanged(AdjustmentEvent e) {
selectPoint(e.getValue());
}
});
_scroller.setEnabled(false);
// Add panel for waypoints / photos
- JPanel listsPanel = new JPanel();
- listsPanel.setLayout(new GridLayout(0, 1));
- listsPanel.setBorder(BorderFactory.createCompoundBorder(
+ _listsPanel = new JPanel();
+ _listsPanel.setLayout(new GridLayout(0, 1));
+ _listsPanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), BorderFactory.createEmptyBorder(3, 3, 3, 3))
);
_waypointListModel = new WaypointListModel(_trackInfo.getTrack());
_waypointList = new JList(_waypointListModel);
_waypointList.setVisibleRowCount(NUM_LIST_ENTRIES);
- _waypointList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_waypointList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting()) selectWaypoint(_waypointList.getSelectedIndex());
- }});
- JPanel waypointListPanel = new JPanel();
- waypointListPanel.setLayout(new BorderLayout());
- waypointListPanel.add(new JLabel(I18nManager.getText("details.waypointsphotos.waypoints")), BorderLayout.NORTH);
- waypointListPanel.add(new JScrollPane(_waypointList), BorderLayout.CENTER);
- listsPanel.add(waypointListPanel);
+ }
+ });
+ _waypointListPanel = makeListPanel("details.lists.waypoints", _waypointList);
+ _listsPanel.add(_waypointListPanel);
// photo list
- _photoListModel = new PhotoListModel(_trackInfo.getPhotoList());
+ _photoListModel = new MediaListModel(_trackInfo.getPhotoList());
_photoList = new JList(_photoListModel);
_photoList.setVisibleRowCount(NUM_LIST_ENTRIES);
- _photoList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
_photoList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e)
{
selectPhoto(_photoList.getSelectedIndex());
}
}});
- JPanel photoListPanel = new JPanel();
- photoListPanel.setLayout(new BorderLayout());
- photoListPanel.add(new JLabel(I18nManager.getText("details.waypointsphotos.photos")), BorderLayout.NORTH);
- photoListPanel.add(new JScrollPane(_photoList), BorderLayout.CENTER);
- listsPanel.add(photoListPanel);
- listsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ _photoListPanel = makeListPanel("details.lists.photos", _photoList);
+ // don't add photo list (because there aren't any photos yet)
+
+ // List for audio files
+ _audioListModel = new MediaListModel(_trackInfo.getAudioList());
+ _audioList = new JList(_audioListModel);
+ _audioList.addListSelectionListener(new ListSelectionListener() {
+ public void valueChanged(ListSelectionEvent e)
+ {
+ if (!e.getValueIsAdjusting()) {
+ selectAudio(_audioList.getSelectedIndex());
+ }
+ }});
+ _audioListPanel = makeListPanel("details.lists.audio", _audioList);
+ // don't add audio list either
+ _listsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
// add the controls to the main panel
mainPanel.add(trackDetailsPanel);
// add the main panel at the top
add(mainPanel, BorderLayout.NORTH);
// and lists in the centre
- add(listsPanel, BorderLayout.CENTER);
+ add(_listsPanel, BorderLayout.CENTER);
// set preferred width to be small
setPreferredSize(new Dimension(100, 100));
}
*/
private void selectPoint(int inValue)
{
- if (_track != null && !_ignoreScrollEvents)
- {
+ if (_track != null && !_ignoreScrollEvents) {
_trackInfo.selectPoint(inValue);
}
}
_trackInfo.selectPhoto(inPhotoIndex);
}
+ /**
+ * Select the specified audio file
+ * @param inIndex index of selected audio file
+ */
+ private void selectAudio(int inIndex)
+ {
+ _trackInfo.selectAudio(inIndex);
+ }
/**
* Select the specified waypoint
*/
private void selectWaypoint(int inWaypointIndex)
{
- if (inWaypointIndex >= 0)
- {
+ if (inWaypointIndex >= 0) {
_trackInfo.selectPoint(_waypointListModel.getWaypoint(inWaypointIndex));
}
}
}
else if (numFiles > 1)
{
- _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": "
- + numFiles);
+ _filenameLabel.setText(I18nManager.getText("details.track.numfiles") + ": " + numFiles);
}
else _filenameLabel.setText("");
}
(DataSubscriber.DATA_ADDED_OR_REMOVED | DataSubscriber.DATA_EDITED | DataSubscriber.PHOTOS_MODIFIED)) > 0)
{
_photoListModel.fireChanged();
+ _audioListModel.fireChanged();
}
// Deselect selected waypoint if selected point has since changed
if (_waypointList.getSelectedIndex() >= 0)
_waypointList.clearSelection();
}
}
+ // Hide photo list if no photos loaded, same for audio
+ redrawLists(_photoListModel.getSize() > 0, _audioListModel.getSize() > 0);
+
// Make sure correct photo is selected
if (_photoListModel.getSize() > 0)
{
}
}
}
+ // Same for audio files
+ if (_audioListModel.getSize() > 0)
+ {
+ int audioIndex = _trackInfo.getSelection().getCurrentAudioIndex();
+ int listSelection = _audioList.getSelectedIndex();
+ // Change listbox selection if indexes not equal
+ if (listSelection != audioIndex)
+ {
+ if (audioIndex < 0) {
+ _audioList.clearSelection();
+ }
+ else {
+ _audioList.setSelectedIndex(audioIndex);
+ }
+ }
+ }
+ }
+
+ /**
+ * Make one of the three list panels
+ * @param inNameKey key for heading text
+ * @param inList list object
+ * @return panel object
+ */
+ private static JPanel makeListPanel(String inNameKey, JList inList)
+ {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.add(new JLabel(I18nManager.getText(inNameKey)), BorderLayout.NORTH);
+ inList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ panel.add(new JScrollPane(inList), BorderLayout.CENTER);
+ return panel;
+ }
+
+ /**
+ * Redraw the list panels in the display according to which ones should be shown
+ * @param inShowPhotos true to show photo list
+ * @param inShowAudio true to show audio list
+ */
+ private void redrawLists(boolean inShowPhotos, boolean inShowAudio)
+ {
+ // exit if same as last time
+ int panels = 1 + (inShowPhotos?2:0) + (inShowAudio?4:0);
+ if (panels == _visiblePanels) return;
+ _visiblePanels = panels;
+ // remove all panels and re-add them
+ _listsPanel.removeAll();
+ _listsPanel.setLayout(new GridLayout(0, 1));
+ _listsPanel.add(_waypointListPanel);
+ if (inShowPhotos) {
+ _listsPanel.add(_photoListPanel);
+ }
+ if (inShowAudio) {
+ _listsPanel.add(_audioListPanel);
+ }
+ _listsPanel.invalidate();
+ _listsPanel.getParent().validate();
}
}
}
catch (Exception e) {return;}
}
- try {
+ try
+ {
// Open streams from URL and to file
out = new FileOutputStream(tempFile);
in = _url.openStream();
}
finished = true;
} catch (IOException e) {}
- finally {
- try {
- in.close();
- out.close();
- if (!finished) {tempFile.delete();}
+ finally
+ {
+ // clean up files
+ try {in.close();} catch (Exception e) {} // ignore
+ try {out.close();} catch (Exception e) {} // ignore
+ if (!finished) {
+ tempFile.delete();
}
- catch (Exception e) {} // ignore
}
// Move temp file to desired file location
- if (!tempFile.renameTo(_file)) {
+ if (!tempFile.renameTo(_file))
+ {
// File couldn't be moved - delete both to be sure
tempFile.delete();
_file.delete();
private int _dragFromX = -1;
/** y coordinate of drag from point */
private int _dragFromY = -1;
- /** Flag set to true for right-click dragging */
- private boolean _zoomDragging = false;
/** x coordinate of drag to point */
private int _dragToX = -1;
/** y coordinate of drag to point */
private int _popupMenuY = -1;
/** Flag to prevent showing too often the error message about loading maps */
private boolean _shownOsmErrorAlready = false;
+ /** Current drawing mode */
+ private int _drawMode = MODE_DEFAULT;
/** Constant for click sensitivity when selecting nearest point */
private static final int CLICK_SENSITIVITY = 10;
// Colours
private static final Color COLOR_MESSAGES = Color.GRAY;
+ // Drawing modes
+ private static final int MODE_DEFAULT = 0;
+ private static final int MODE_ZOOM_RECT = 1;
+ private static final int MODE_DRAW_POINTS_START = 2;
+ private static final int MODE_DRAW_POINTS_CONT = 3;
/**
* Constructor
{
zoomIn();
}});
- zoomInItem.setEnabled(true);
_popup.add(zoomInItem);
JMenuItem zoomOutItem = new JMenuItem(I18nManager.getText("menu.map.zoomout"));
zoomOutItem.addActionListener(new ActionListener() {
{
zoomOut();
}});
- zoomOutItem.setEnabled(true);
_popup.add(zoomOutItem);
JMenuItem zoomFullItem = new JMenuItem(I18nManager.getText("menu.map.zoomfull"));
zoomFullItem.addActionListener(new ActionListener() {
_recalculate = true;
repaint();
}});
- zoomFullItem.setEnabled(true);
_popup.add(zoomFullItem);
_popup.addSeparator();
// Set background
newPointItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
- double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(_popupMenuY, getHeight()));
- double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(_popupMenuX, getWidth()));
- _app.createPoint(new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE),
- new Longitude(lon, Coordinate.FORMAT_NONE), null));
+ _app.createPoint(createPointFromClick(_popupMenuX, _popupMenuY));
}});
- newPointItem.setEnabled(true);
_popup.add(newPointItem);
+ // draw point series
+ JMenuItem drawPointsItem = new JMenuItem(I18nManager.getText("menu.map.drawpoints"));
+ drawPointsItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e)
+ {
+ _drawMode = MODE_DRAW_POINTS_START;
+ }
+ });
+ _popup.add(drawPointsItem);
}
inG.drawImage(_mapImage, 0, 0, getWidth(), getHeight(), null);
}
// Draw the zoom rectangle if necessary
- if (_zoomDragging)
+ if (_drawMode == MODE_ZOOM_RECT)
{
inG.setColor(Color.RED);
inG.drawLine(_dragFromX, _dragFromY, _dragFromX, _dragToY);
inG.drawLine(_dragToX, _dragFromY, _dragToX, _dragToY);
inG.drawLine(_dragFromX, _dragToY, _dragToX, _dragToY);
}
+ else if (_drawMode == MODE_DRAW_POINTS_CONT)
+ {
+ // draw line to mouse position to show drawing mode
+ inG.setColor(Config.getColourScheme().getColour(ColourScheme.IDX_POINT));
+ int prevIndex = _track.getNumPoints()-1;
+ int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(prevIndex));
+ int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(prevIndex));
+ inG.drawLine(px, py, _dragToX, _dragToY);
+ }
}
else
{
final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY);
final Color textColour = Config.getColourScheme().getColour(ColourScheme.IDX_TEXT);
- // try to set double line width for painting
- if (inG instanceof Graphics2D) {
- ((Graphics2D) inG).setStroke(new BasicStroke(2.0f));
+ // try to set line width for painting
+ if (inG instanceof Graphics2D)
+ {
+ int lineWidth = Config.getConfigInt(Config.KEY_LINE_WIDTH);
+ if (lineWidth < 1 || lineWidth > 4) {lineWidth = 2;}
+ ((Graphics2D) inG).setStroke(new BasicStroke(lineWidth));
}
int pointsPainted = 0;
// draw track points
}
}
}
- // Loop over points, drawing blobs for photo points
+ // Loop over points, drawing blobs for photo / audio points
inG.setColor(secondColour);
for (int i=0; i<_track.getNumPoints(); i++)
{
- if (_track.getPoint(i).getPhoto() != null)
+ if (_track.getPoint(i).hasMedia())
{
int px = getWidth() / 2 + _mapPosition.getXFromCentre(_track.getX(i));
int py = getHeight() / 2 + _mapPosition.getYFromCentre(_track.getY(i));
repaint();
}
+ /**
+ * Create a DataPoint object from the given click coordinates
+ * @param inX x coordinate of click
+ * @param inY y coordinate of click
+ * @return DataPoint with given coordinates and no altitude
+ */
+ private DataPoint createPointFromClick(int inX, int inY)
+ {
+ double lat = MapUtils.getLatitudeFromY(_mapPosition.getYFromPixels(inY, getHeight()));
+ double lon = MapUtils.getLongitudeFromX(_mapPosition.getXFromPixels(inX, getWidth()));
+ return new DataPoint(new Latitude(lat, Coordinate.FORMAT_NONE),
+ new Longitude(lon, Coordinate.FORMAT_NONE), null);
+ }
+
/**
* @see javax.swing.JComponent#getMinimumSize()
*/
if (inE.getClickCount() == 1)
{
// single click
- int pointIndex = _track.getNearestPointIndex(
- _mapPosition.getXFromPixels(inE.getX(), getWidth()),
- _mapPosition.getYFromPixels(inE.getY(), getHeight()),
- _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false);
- // Extend selection for shift-click
- if (inE.isShiftDown()) {
- _trackInfo.extendSelection(pointIndex);
+ if (_drawMode == MODE_DEFAULT)
+ {
+ int pointIndex = _track.getNearestPointIndex(
+ _mapPosition.getXFromPixels(inE.getX(), getWidth()),
+ _mapPosition.getYFromPixels(inE.getY(), getHeight()),
+ _mapPosition.getBoundsFromPixels(CLICK_SENSITIVITY), false);
+ // Extend selection for shift-click
+ if (inE.isShiftDown()) {
+ _trackInfo.extendSelection(pointIndex);
+ }
+ else {
+ _trackInfo.selectPoint(pointIndex);
+ }
}
- else {
- _trackInfo.selectPoint(pointIndex);
+ else if (_drawMode == MODE_DRAW_POINTS_START)
+ {
+ _app.createPoint(createPointFromClick(inE.getX(), inE.getY()));
+ _dragToX = inE.getX();
+ _dragToY = inE.getY();
+ _drawMode = MODE_DRAW_POINTS_CONT;
+ }
+ else if (_drawMode == MODE_DRAW_POINTS_CONT)
+ {
+ DataPoint point = createPointFromClick(inE.getX(), inE.getY());
+ _app.createPoint(point);
+ point.setSegmentStart(false);
}
}
else if (inE.getClickCount() == 2) {
// double click
- panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2);
- zoomIn();
+ if (_drawMode == MODE_DEFAULT) {
+ panMap(inE.getX() - getWidth()/2, inE.getY() - getHeight()/2);
+ zoomIn();
+ }
+ else if (_drawMode == MODE_DRAW_POINTS_START || _drawMode == MODE_DRAW_POINTS_CONT) {
+ _drawMode = MODE_DEFAULT;
+ }
}
}
else
public void mouseReleased(MouseEvent inE)
{
_recalculate = true;
- if (_zoomDragging && Math.abs(_dragToX - _dragFromX) > 20 && Math.abs(_dragToY - _dragFromY) > 20)
+ if (_drawMode == MODE_ZOOM_RECT && Math.abs(_dragToX - _dragFromX) > 20
+ && Math.abs(_dragToY - _dragFromY) > 20)
{
//System.out.println("Finished zoom: " + _dragFromX + ", " + _dragFromY + " to " + _dragToX + ", " + _dragToY);
_mapPosition.zoomToPixels(_dragFromX, _dragToX, _dragFromY, _dragToY, getWidth(), getHeight());
+ _drawMode = MODE_DEFAULT;
}
_dragFromX = _dragFromY = -1;
- _zoomDragging = false;
repaint();
}
if (!inE.isMetaDown())
{
// Left mouse drag - pan map by appropriate amount
- _zoomDragging = false;
if (_dragFromX != -1)
{
panMap(_dragFromX - inE.getX(), _dragFromY - inE.getY());
_recalculate = true;
repaint();
}
- _dragFromX = inE.getX();
- _dragFromY = inE.getY();
+ _dragFromX = _dragToX = inE.getX();
+ _dragFromY = _dragToY = inE.getY();
}
else
{
// Right-click and drag - draw rectangle and control zoom
- _zoomDragging = true;
+ _drawMode = MODE_ZOOM_RECT;
if (_dragFromX == -1) {
_dragFromX = inE.getX();
_dragFromY = inE.getY();
*/
public void mouseMoved(MouseEvent inEvent)
{
- // ignore
+ // Ignore unless we're drawing points
+ if (_drawMode == MODE_DRAW_POINTS_CONT)
+ {
+ _dragToX = inEvent.getX();
+ _dragToY = inEvent.getY();
+ repaint();
+ }
}
/**
else if (code == KeyEvent.VK_LEFT)
rightwardsPan = -PAN_DISTANCE;
panMap(rightwardsPan, upwardsPan);
+ // Check for escape
+ if (code == KeyEvent.VK_ESCAPE)
+ _drawMode = MODE_DEFAULT;
// Check for backspace key to delete current point (delete key already handled by menu)
- if (code == KeyEvent.VK_BACK_SPACE && currPointIndex >= 0) {
+ else if (code == KeyEvent.VK_BACK_SPACE && currPointIndex >= 0) {
_app.deleteCurrentPoint();
}
}
* @param inUrl url to check
* @return validated url with correct prefix and trailing slash, or null
*/
- protected static String fixBaseUrl(String inUrl)
+ public static String fixBaseUrl(String inUrl)
{
if (inUrl == null || inUrl.equals("")) {return null;}
- String url = inUrl;
+ String urlstr = inUrl;
// check prefix
try {
- new URL(url);
+ new URL(urlstr);
}
catch (MalformedURLException e) {
+ // fail if protocol specified
+ if (urlstr.indexOf("://") >= 0) {return null;}
// add the http protocol
- url = "http://" + url;
+ urlstr = "http://" + urlstr;
}
// check trailing /
- if (!url.endsWith("/")) {
- url = url + "/";
+ if (!urlstr.endsWith("/")) {
+ urlstr = urlstr + "/";
}
- return url;
+ // Validate current url, return null if not ok
+ try {
+ URL url = new URL(urlstr);
+ // url host must contain a dot
+ if (url.getHost().indexOf('.') < 0) {return null;}
+ }
+ catch (MalformedURLException e) {
+ urlstr = null;
+ }
+ return urlstr;
}
/**
*/
public String getSiteStrings()
{
- String s = "";
+ StringBuilder sb = new StringBuilder();
for (int i=0; i<getNumLayers(); i++) {
String url = getBaseUrl(i);
- if (url != null) {s = s + url + ";";}
+ if (url != null) {
+ sb.append(url);
+ sb.append(';');
+ }
}
- return s;
+ return sb.toString();
}
}
package tim.prune.jpeg;
import java.io.File;
-
import javax.swing.JOptionPane;
-
import tim.prune.I18nManager;
/**
* to the external libmetadata-extractor-java library
* instead of the included modified routines.
*
- * To use the internal routines, set the USE_INTERNAL_LIBRARY flag to true
- * and include the internal classes in the compiled jar.
- * To use the external library, set the USE_INTERNAL_LIBRARY flag to false
- * and do not export the internal classes.
+ * Switching between internal and external libraries is
+ * handled by the ExifLibrarySwitch
*/
public abstract class ExifGateway
{
if (_exifLibrary == null || !_exifLibrary.looksOK()) {key = key + ".failed";}
return key;
}
+
+
+
+ /**
+ * @param inNumerator numerator from Rational
+ * @param inDenominator denominator from Rational
+ * @return the value of the specified number as a positive <code>double</code>.
+ * Prevents interpretation of 32 bit numbers as negative, and forces a positive answer
+ */
+ public static final double convertToPositiveValue(int inNumerator, int inDenominator)
+ {
+ if (inDenominator == 0) return 0.0;
+ double numeratorDbl = inNumerator;
+ double denomDbl = inDenominator;
+ if (inNumerator >= 0)
+ return numeratorDbl / denomDbl;
+ final double correction = Math.pow(2.0, 32);
+ numeratorDbl += correction;
+ if (inDenominator < 0) denomDbl += correction;
+ return numeratorDbl / denomDbl;
+ }
}
{
data.setLatitudeRef(gpsdir.getString(GpsDirectory.TAG_GPS_LATITUDE_REF));
Rational[] latRats = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE);
+ double seconds = ExifGateway.convertToPositiveValue(latRats[2].getNumerator(), latRats[2].getDenominator());
data.setLatitude(new double[] {latRats[0].doubleValue(),
- latRats[1].doubleValue(), latRats[2].doubleValue()});
+ latRats[1].doubleValue(), seconds});
data.setLongitudeRef(gpsdir.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF));
Rational[] lonRats = gpsdir.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE);
+ seconds = ExifGateway.convertToPositiveValue(lonRats[2].getNumerator(), lonRats[2].getDenominator());
data.setLongitude(new double[] {lonRats[0].doubleValue(),
- lonRats[1].doubleValue(), lonRats[2].doubleValue()});
+ lonRats[1].doubleValue(), seconds});
}
// Altitude (if present)
return data;
}
+
/**
* Check whether the exifreader class can be correctly resolved
* @return true if it looks ok
import java.io.File;\r
import java.util.HashMap;\r
\r
+import tim.prune.jpeg.ExifGateway;\r
import tim.prune.jpeg.JpegData;\r
\r
/**\r
*/\r
public ExifReader(File inFile) throws JpegException\r
{\r
- JpegSegmentData segments = JpegSegmentReader.readSegments(inFile);\r
- _data = segments.getSegment(JpegSegmentReader.SEGMENT_APP1);\r
+ _data = JpegSegmentReader.readExifSegment(inFile);\r
}\r
\r
/**\r
break;\r
case TAG_GPS_LATITUDE:\r
Rational[] latitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
- inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(), latitudes[2].doubleValue()});\r
+ inMetadata.setLatitude(new double[] {latitudes[0].doubleValue(), latitudes[1].doubleValue(),\r
+ ExifGateway.convertToPositiveValue(latitudes[2].getNumerator(), latitudes[2].getDenominator())});\r
break;\r
case TAG_GPS_LONGITUDE_REF:\r
inMetadata.setLongitudeRef(readString(inTagValueOffset, inFormatCode, inComponentCount));\r
break;\r
case TAG_GPS_LONGITUDE:\r
Rational[] longitudes = readRationalArray(inTagValueOffset, inFormatCode, inComponentCount);\r
- inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(), longitudes[2].doubleValue()});\r
+ inMetadata.setLongitude(new double[] {longitudes[0].doubleValue(), longitudes[1].doubleValue(),\r
+ ExifGateway.convertToPositiveValue(longitudes[2].getNumerator(), longitudes[2].getDenominator())});\r
break;\r
case TAG_GPS_ALTITUDE_REF:\r
inMetadata.setAltitudeRef(_data[inTagValueOffset]);\r
/** End of image marker */\r
private static final byte MARKER_EOI = (byte)0xD9;\r
\r
- /** APP0 Jpeg segment identifier -- Jfif data. */\r
- public static final byte SEGMENT_APP0 = (byte)0xE0;\r
/** APP1 Jpeg segment identifier -- where Exif data is kept. */\r
- public static final byte SEGMENT_APP1 = (byte)0xE1;\r
- /** APP2 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP2 = (byte)0xE2;\r
- /** APP3 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP3 = (byte)0xE3;\r
- /** APP4 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP4 = (byte)0xE4;\r
- /** APP5 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP5 = (byte)0xE5;\r
- /** APP6 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP6 = (byte)0xE6;\r
- /** APP7 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP7 = (byte)0xE7;\r
- /** APP8 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP8 = (byte)0xE8;\r
- /** APP9 Jpeg segment identifier. */\r
- public static final byte SEGMENT_APP9 = (byte)0xE9;\r
- /** APPA Jpeg segment identifier -- can hold Unicode comments. */\r
- public static final byte SEGMENT_APPA = (byte)0xEA;\r
- /** APPB Jpeg segment identifier. */\r
- public static final byte SEGMENT_APPB = (byte)0xEB;\r
- /** APPC Jpeg segment identifier. */\r
- public static final byte SEGMENT_APPC = (byte)0xEC;\r
- /** APPD Jpeg segment identifier -- IPTC data in here. */\r
- public static final byte SEGMENT_APPD = (byte)0xED;\r
- /** APPE Jpeg segment identifier. */\r
- public static final byte SEGMENT_APPE = (byte)0xEE;\r
- /** APPF Jpeg segment identifier. */\r
- public static final byte SEGMENT_APPF = (byte)0xEF;\r
- /** Start Of Image segment identifier. */\r
- public static final byte SEGMENT_SOI = (byte)0xD8;\r
- /** Define Quantization Table segment identifier. */\r
- public static final byte SEGMENT_DQT = (byte)0xDB;\r
- /** Define Huffman Table segment identifier. */\r
- public static final byte SEGMENT_DHT = (byte)0xC4;\r
- /** Start-of-Frame Zero segment identifier. */\r
- public static final byte SEGMENT_SOF0 = (byte)0xC0;\r
- /** Jpeg comment segment identifier. */\r
- public static final byte SEGMENT_COM = (byte)0xFE;\r
+ private static final byte SEGMENT_APP1 = (byte)0xE1;\r
\r
/** Magic numbers to mark the beginning of all Jpegs */\r
private static final int MAGIC_JPEG_BYTE_1 = 0xFF;\r
private static final int MAGIC_JPEG_BYTE_2 = 0xD8;\r
\r
\r
+ /**\r
+ * Get the Exif data segment for the specified file\r
+ * @param inFile File to read\r
+ * @return Exif data segment as byte array\r
+ * @throws JpegException on file read errors or exif data errors\r
+ */\r
+ public static byte[] readExifSegment(File inFile) throws JpegException\r
+ {\r
+ JpegSegmentData data = readSegments(inFile);\r
+ return data.getSegment(SEGMENT_APP1);\r
+ }\r
+\r
+\r
/**\r
* Obtain the Jpeg segment data from the specified file\r
* @param inFile File to read\r
* @return Jpeg segment data from file\r
* @throws JpegException on file read errors or exif data errors\r
*/\r
- public static JpegSegmentData readSegments(File inFile) throws JpegException\r
+ private static JpegSegmentData readSegments(File inFile) throws JpegException\r
{\r
JpegSegmentData segmentData = new JpegSegmentData();\r
-\r
BufferedInputStream bStream = null;\r
\r
try\r
{\r
bStream = new BufferedInputStream(new FileInputStream(inFile));\r
- int offset = 0;\r
// first two bytes should be jpeg magic number\r
- int magic1 = bStream.read() & 0xFF;\r
- int magic2 = bStream.read() & 0xFF;\r
- checkMagicNumbers(magic1, magic2);\r
+ final int magic1 = bStream.read() & 0xFF;\r
+ final int magic2 = bStream.read() & 0xFF;\r
+ if (magic1 != MAGIC_JPEG_BYTE_1 || magic2 != MAGIC_JPEG_BYTE_2) {\r
+ throw new JpegException("not a jpeg file");\r
+ }\r
\r
- offset += 2;\r
// Loop around segments found\r
+ boolean foundExif = false;\r
do\r
{\r
// next byte is 0xFF\r
byte segmentIdentifier = (byte) (bStream.read() & 0xFF);\r
if ((segmentIdentifier & 0xFF) != 0xFF)\r
{\r
- throw new JpegException("expected jpeg segment start identifier 0xFF at offset "\r
- + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF));\r
+ throw new JpegException("expected jpeg segment start 0xFF, not 0x"\r
+ + Integer.toHexString(segmentIdentifier & 0xFF));\r
}\r
- offset++;\r
// next byte is <segment-marker>\r
byte thisSegmentMarker = (byte) (bStream.read() & 0xFF);\r
- offset++;\r
// next 2-bytes are <segment-size>: [high-byte] [low-byte]\r
byte[] segmentLengthBytes = new byte[2];\r
bStream.read(segmentLengthBytes, 0, 2);\r
- offset += 2;\r
int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF);\r
// segment length includes size bytes, so subtract two\r
segmentLength -= 2;\r
else if (segmentLength < 0)\r
throw new JpegException("segment size would be less than zero");\r
byte[] segmentBytes = new byte[segmentLength];\r
- bStream.read(segmentBytes, 0, segmentLength);\r
- offset += segmentLength;\r
+ int bytesRead = bStream.read(segmentBytes, 0, segmentLength);\r
+ // Bail if not all bytes read in one go - otherwise following sections will be out of step\r
+ if (bytesRead != segmentLength) {\r
+ throw new JpegException("Tried to read " + segmentLength + " bytes but only got " + bytesRead);\r
+ }\r
if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF))\r
{\r
// The 'Start-Of-Scan' segment comes last so break out of loop\r
{\r
segmentData.addSegment(thisSegmentMarker, segmentBytes);\r
}\r
- // loop through to the next segment\r
+ // loop through to the next segment if exif hasn't already been found\r
+ foundExif = (thisSegmentMarker == SEGMENT_APP1);\r
}\r
- while (true);\r
+ while (!foundExif);\r
}\r
catch (FileNotFoundException fnfe)\r
{\r
// Return the result\r
return segmentData;\r
}\r
-\r
-\r
- /**\r
- * Helper method that validates the Jpeg file's magic number.\r
- * @param inMagic1 first half of magic number\r
- * @param inMagic2 second half of magic number\r
- * @throws JpegException if numbers do not match magic numbers expected\r
- */\r
- private static void checkMagicNumbers(int inMagic1, int inMagic2) throws JpegException\r
- {\r
- if (inMagic1 != MAGIC_JPEG_BYTE_1 || inMagic2 != MAGIC_JPEG_BYTE_2)\r
- {\r
- throw new JpegException("not a jpeg file");\r
- }\r
- }\r
-}
\ No newline at end of file
+}\r
menu.range.end=Stel Reeks Einde
menu.photo=Foto
menu.photo.saveexif=Stoor na EXIF
-menu.photo.connect=Las foto by huidige punt
-menu.photo.disconnect=Ontkoppel foto vanaf huidige punt
-menu.photo.delete=Verwyder foto
+function.connecttopoint=Las foto by huidige punt
+function.disconnectfrompoint=Ontkoppel vanaf huidige punt
+function.removephoto=Verwyder foto
menu.view=Kyk
menu.view.browser=Kaart in werf blaaier
menu.view.browser.google=Google Kaarte
# Text entries for the Prune application
-# Czech entries as extra
+# Czech entries thanks to prot_d
# Menu entries
menu.file=Soubor
menu.track.undo=Undo
menu.track.clearundo=Vypr\u00e1zdnit pam\u011b\u0165 undo
menu.track.deletemarked=Smazat ozna\u010den\u00e9 body
-menu.track.rearrange=P\u0159euspo\u0159\u00e1dat body
+menu.track.rearrange=P\u0159euspo\u0159\u00e1dat z\u00e1jmov\u00e9 body
menu.track.rearrange.start=V\u0161e na po\u010d\u00e1tek
menu.track.rearrange.end=V\u0161e na konec
menu.track.rearrange.nearest=Zarovnat body na trasu
menu.point.deletepoint=Smazat bod
menu.photo=Fotografie
menu.photo.saveexif=Ulo\u017eit do Exif
-menu.photo.connect=P\u0159ipojit do bodu
-menu.photo.disconnect=Odpojit od bodu
-menu.photo.delete=Odebrat fotografii
+menu.audio=Audionahr\u00e1vka
menu.view=Zobrazen\u00ed
menu.view.showsidebars=Zobrazit panely
menu.view.browser=Mapa v internetov\u00e9m prohl\u00ed\u017ee\u010di
menu.map.zoomout=Odd\u00e1lit
menu.map.zoomfull=\u00dapln\u011b odd\u00e1lit
menu.map.newpoint=Vytvo\u0159it nov\u00fd bod
-menu.map.connect=Propojit body
+menu.map.drawpoints=Vytvo\u0159it n\u011bkolik bod\u016f
+menu.map.connect=Propojit body trasy
menu.map.autopan=Automatika zorn\u00e9ho pole
menu.map.showmap=Zobrazit mapu
menu.map.showscalebar=Zobrazit m\u011b\u0159\u00edtko
altkey.menu.point=B
altkey.menu.view=Z
altkey.menu.photo=F
+altkey.menu.audio=A
altkey.menu.settings=N
altkey.menu.help=P
function.exportgpx=Export GPX
function.exportpov=Export POV
function.exportsvg=Export SVG
-function.editwaypointname=Nastavit n\u00e1zev bodu
+function.editwaypointname=Nastavit n\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu
function.compress=Komprimovat trasu
function.addtimeoffset=P\u0159idat \u010dasov\u00fd posun
function.addaltitudeoffset=P\u0159idat v\u00fd\u0161kov\u00fd posun
-function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy bod\u016f na \u010dasov\u00e9 zna\u010dky
+function.convertnamestotimes=P\u0159ev\u00e9st n\u00e1zvy v\u00fdzna\u010dn\u00fdch bod\u016f na \u010dasy
function.deletefieldvalues=Smazat hodnoty pole
function.findwaypoint=Hledat bod
function.pastecoordinates=Zadat sou\u0159adnice
function.getgpsies=St\u00e1hnout trasy z Gpsies
function.uploadgpsies=Nahr\u00e1t trasu na Gpsies
function.lookupsrtm=Na\u010d\u00edst nadm. v\u00fd\u0161ku ze SRTM
+function.getwikipedia=Hledat na Wikipedii podle vzd\u00e1lenosti
+function.searchwikipedianames=Hledat na Wikipedii podle jm\u00e9na
+function.downloadosm=St\u00e1hnout data OSM pro oblast
function.duplicatepoint=Zdvojit bod
function.setcolours=Nastavit barvy
+function.setlinewidth=Nastavit tlou\u0161\u0165ku \u010d\u00e1ry
function.setlanguage=Nastavit jazyk
+function.connecttopoint=P\u0159ipojit do bodu
+function.disconnectfrompoint=Odpojit od bodu
+function.removephoto=Odebrat fotografii
function.correlatephotos=Sladit fotografie podle \u010dasu
function.rearrangephotos=Uspo\u0159\u00e1dat fotografie
function.rotatephotoleft=Oto\u010dit fotografii doleva
function.rotatephotoright=Oto\u010dit fotografii doprava
+function.photopopup=Zobrazit celou fotografii
function.ignoreexifthumb=Ignorovat n\u00e1hled v Exif
+function.loadaudio=P\u0159idat audionahr\u00e1vky
+function.removeaudio=Odebrat audionahr\u00e1vku
+function.correlateaudios=Sladit audionahr\u00e1vky podle \u010dasu
+function.playaudio=P\u0159ehr\u00e1t audionahr\u00e1vku
+function.stopaudio=Zastavit p\u0159ehr\u00e1v\u00e1n\u00ed
function.help=Pomoc
function.showkeys=Zobrazit kl\u00e1vesov\u00e9 zkratky
function.about=O programu
dialog.gpsload.nogpsbabel=Nenalezen program gpsbabel. Pokra\u010dovat?
dialog.gpsload.device=Ozna\u010den\u00ed za\u0159\u00edzen\u00ed
dialog.gpsload.format=Form\u00e1t
-dialog.gpsload.getwaypoints=Na\u010d\u00edst body
+dialog.gpsload.getwaypoints=Na\u010d\u00edst v\u00fdzna\u010dn\u00e9 body
dialog.gpsload.gettracks=Na\u010d\u00edst trasy
dialog.gpsload.save=Ulo\u017eit do souboru
dialog.gpssend.sendwaypoints=Poslat bod
dialog.save.overwrite.text=Tento soubor u\u017e existuje. Opravdu chcete soubor p\u0159epsat?
dialog.save.notypesselected=Nebyl vybr\u00e1n ani jeden typ bod\u016f
dialog.exportkml.text=Nadpis dat
-dialog.exportkml.altitude=V\u00fd\u0161ka nad hladinou mo\u0159e (pro l\u00e9t\u00e1n\u00ed)
+dialog.exportkml.altitude=V\u00fd\u0161ka nad hladinou mo\u0159e (pro letectv\u00ed)
dialog.exportkml.kmz=Komprimovat do souboru kmz
dialog.exportkml.exportimages=Vlo\u017eit n\u00e1hledy fotografi\u00ed
dialog.exportkml.trackcolour=Barva trasy
dialog.pointtype.track=Body trasy
dialog.pointtype.waypoint=V\u00fdzna\u010dn\u00e9 body
dialog.pointtype.photo=M\u00edsta s fotografiemi
+dialog.pointtype.audio=M\u00edsta s audionahr\u00e1vkami
dialog.pointtype.selection=Jen v\u00fdb\u011br
dialog.confirmreversetrack.title=Potvr\u010fte obr\u00e1cen\u00ed
dialog.confirmreversetrack.text=Tato trasa obsahuje \u010dasov\u00e9 zna\u010dky, jejich\u017e po\u0159ad\u00ed se obr\u00e1cen\u00edm zm\u011bn\u00ed.\nOpravdu chcete obr\u00e1tit v\u00fdb\u011br?
dialog.pointedit.changevalue.text=Zadejte novou hodnotu pole
dialog.pointedit.changevalue.title=Upravit pole
dialog.pointnameedit.name=N\u00e1zev v\u00fdzna\u010dn\u00e9ho bodu
-dialog.pointnameedit.uppercase=VELKÁ p\u00edsmena
+dialog.pointnameedit.uppercase=VELK\u00c1 p\u00edsmena
dialog.pointnameedit.lowercase=mal\u00e1 p\u00edsmena
dialog.pointnameedit.sentencecase=Po\u010d\u00e1te\u010dn\u00ed P\u00edsmena Velk\u00e1
dialog.addtimeoffset.add=P\u0159idat \u010das
dialog.gpsies.activity.snowshoe=Sn\u011b\u017enice
dialog.gpsies.activity.sailing=Lo\u010f
dialog.gpsies.activity.skating=Bruslen\u00ed
+dialog.wikipedia.column.name=N\u00e1zev \u010dl\u00e1nku
+dialog.wikipedia.column.distance=Vzd\u00e1lenost
dialog.correlate.notimestamps=U bod\u016f nejsou \u010dasov\u00e9 zna\u010dky, tak\u017ee nen\u00ed s \u010d\u00edm fotografie sladit.
dialog.correlate.nouncorrelatedphotos=V\u0161echny fotografie jsou slad\u011bn\u00e9.\nOpravdu chcete pokra\u010dovat?
dialog.correlate.photoselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch fotografi\u00ed pro ur\u010den\u00ed \u010dasov\u00e9ho posunu
-dialog.correlate.photoselect.photoname=N\u00e1zev fotografie
-dialog.correlate.photoselect.timediff=\u010casov\u00fd rozd\u00edl
-dialog.correlate.photoselect.photolater=Vyfoceno pozd\u011bji
+dialog.correlate.select.photoname=N\u00e1zev fotografie
+dialog.correlate.select.timediff=\u010casov\u00fd rozd\u00edl
+dialog.correlate.select.photolater=Vyfoceno pozd\u011bji
dialog.correlate.options.tip=Tip: kdy\u017e ru\u010dn\u011b slad\u00edte aspo\u0148 jednu fotografii, \u010dasov\u00fd posun bude vypo\u010d\u00edtat za v\u00e1s.
dialog.correlate.options.intro=Upravte mo\u017enosti automatick\u00e9ho slad\u011bn\u00ed
dialog.correlate.options.offsetpanel=\u010casov\u00fd posun
dialog.correlate.options.offset.minutes=minut a
dialog.correlate.options.offset.seconds=sekund
dialog.correlate.options.photolater=Fotografie pozd\u011bj\u0161\u00ed ne\u017e bod
-dialog.correlate.options.pointlater=Bod pozd\u011bj\u0161\u00ed ne\u017e fotografie
+dialog.correlate.options.pointlaterphoto=Bod pozd\u011bj\u0161\u00ed ne\u017e fotografie
+dialog.correlate.options.audiolater=Audio pozd\u011bj\u0161\u00ed ne\u017e bod
+dialog.correlate.options.pointlateraudio=Bod pozd\u011bj\u0161\u00ed ne\u017e audio
dialog.correlate.options.limitspanel=Limity slad\u011bn\u00ed
dialog.correlate.options.notimelimit=Bez \u010dasov\u00e9ho limitu
dialog.correlate.options.timelimit=\u010casov\u00fd limit
dialog.correlate.options.distancelimit=D\u00e9lkov\u00fd limit
dialog.correlate.options.correlate=Sladit
dialog.correlate.alloutsiderange=V\u0161echny fotografie le\u017e\u00ed mimo \u010dasov\u00e9 rozmez\u00ed trasy, tak\u017ee nemohou b\u00fdt slad\u011bny.\nPokuste se zm\u011bnit \u010dasov\u00fd posun nebo ru\u010dn\u011b sla\u010fte aspo\u0148 jednu fotografii.
+dialog.correlate.filetimes=\u010cas z\u00e1znamu souboru znamen\u00e1:
+dialog.correlate.filetimes2=audionahr\u00e1vky
+dialog.correlate.correltimes=Sladit tento okam\u017eik nahr\u00e1vky:
+dialog.correlate.timestamp.beginning=Za\u010d\u00e1tek
+dialog.correlate.timestamp.middle=St\u0159ed
+dialog.correlate.timestamp.end=Konec
+dialog.correlate.audioselect.intro=Vyberte jednu z t\u011bchto slad\u011bn\u00fdch nahr\u00e1vek pro ur\u010den\u00ed \u010dasov\u00e9ho posunu
+dialog.correlate.select.audioname=N\u00e1zev audionahr\u00e1vky
+dialog.correlate.select.audiolater=Audio pozd\u011bj\u0161\u00ed
dialog.rearrangephotos.desc=Vyberte um\u00edst\u011bn\u00ed a uspo\u0159\u00e1d\u00e1n\u00ed bod\u016f fotografi\u00ed
dialog.rearrangephotos.tostart=P\u0159en\u00e9st na za\u010d\u00e1tek
dialog.rearrangephotos.toend=P\u0159en\u00e9st na konec
dialog.saveconfig.prune.kmzimagewidth=\u0160\u00ed\u0159ka bitmapy KMZ
dialog.saveconfig.prune.kmzimageheight=V\u00fd\u0161ka bitmapy KMZ
dialog.saveconfig.prune.colourscheme=Barevn\u00e9 sch\u00e9ma
+dialog.saveconfig.prune.linewidth=Tlou\u0161\u0165ka \u010d\u00e1ry
dialog.saveconfig.prune.kmltrackcolour=Barva trasy v KML
dialog.setpaths.intro=Je-li to t\u0159eba, m\u016f\u017eete nastavit cesty k extern\u00edm aplikac\u00edm:
dialog.setpaths.found=Cesta nalezena?
dialog.diskcache.createdir=Vytvo\u0159it adres\u00e1\u0159
dialog.diskcache.nocreate=Adres\u00e1\u0159 nebyl vytvo\u0159en
dialog.deletefieldvalues.intro=Vyberte pole, kter\u00e9 se m\u00e1 z aktu\u00e1ln\u00edho rozmez\u00ed odstranit
+dialog.setlinewidth.text=Zvolte tlou\u0161\u0165ku \u010d\u00e1ry, kterou se nakresl\u00ed trasa (1-4)
+dialog.downloadosm.desc=Potvr\u010fte, \u017ee se maj\u00ed k dan\u00e9 oblasti st\u00e1hnout data OSM:
+dialog.searchwikipedianames.search=Vyhledat:
# 3d window
dialog.3d.title=Trojrozm\u011brn\u00e9 zobrazen\u00ed Prune
confirm.undo.multi=operac\u00ed vr\u00e1ceno
confirm.jpegload.single=fotografie p\u0159id\u00e1na
confirm.jpegload.multi=fotografie p\u0159id\u00e1ny
-confirm.photo.connect=fotografie propojena
+confirm.media.connect=soubor p\u0159ipojen
confirm.photo.disconnect=fotografie odpojena
-confirm.correlate.single=fotografie slad\u011bna
-confirm.correlate.multi=fotografie slad\u011bny
+confirm.audio.disconnect=audionahr\u00e1vka odpojena
+confirm.media.removed=odstran\u011bno
+confirm.correlatephotos.single=fotografie slad\u011bna
+confirm.correlatephotos.multi=fotografie slad\u011bny
confirm.createpoint=bod vytvo\u0159en
confirm.rotatephoto=fotografie oto\u010dena
confirm.running=Prob\u00edh\u00e1 ...
confirm.lookupsrtm1=Nalezeno
confirm.lookupsrtm2=v\u00fd\u0161kov\u00fdch hodnot
confirm.deletefieldvalues=Hodnoty pole smaz\u00e1ny
+confirm.audioload=Audionahr\u00e1vky p\u0159id\u00e1ny
+confirm.correlateaudios.single=Audionahr\u00e1vka slad\u011bna
+confirm.correlateaudios.multi=Audionahr\u00e1vky slad\u011bny
# Buttons
button.ok=OK
filetype.gpx=soubory GPX
filetype.pov=soubory POV
filetype.svg=soubory SVG
+filetype.audio=soubory MP3, OGG, WAV
# Display components
display.nodata=\u017d\u00e1dn\u00e1 data
details.range.numsegments=Po\u010det segment\u016f
details.range.pace=Tempo
details.range.gradient=Sp\u00e1d
-details.waypointsphotos.waypoints=V\u00fdzna\u010dn\u00e9 body
-details.waypointsphotos.photos=Fotografie
+details.lists.waypoints=V\u00fdzna\u010dn\u00e9 body
+details.lists.photos=Fotografie
+details.lists.audio=Audionahr\u00e1vky
details.photodetails=Detaily fotografie
details.nophoto=Fotografie nevybr\u00e1na
details.photo.loading=Na\u010d\u00edt\u00e1m
-details.photo.connected=P\u0159ipojeno
+details.media.connected=P\u0159ipojeno
+details.audiodetails=Detaily audionahr\u00e1vky
+details.noaudio=Audionahr\u00e1vka nevybr\u00e1na
+details.audio.file=Zvukov\u00fd soubor
+details.audio.playing=p\u0159ehr\u00e1v\u00e1n...
map.overzoom=P\u0159i tomto p\u0159ibl\u00ed\u017een\u00ed mapa nen\u00ed k dispozici
# Field names
# External urls
url.googlemaps=maps.google.cz
+wikipedia.lang=cs
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=na\u010d\u00edst data
undo.loadphotos=na\u010d\u00edst fotografie
+undo.loadaudios=na\u010d\u00edst audionahr\u00e1vky
undo.editpoint=upravit bod
undo.deletepoint=smazat bod
-undo.deletephoto=odebrat fotografii
+undo.removephoto=odebrat fotografii
+undo.removeaudio=odebrat audionahr\u00e1vku
undo.deleterange=smazat rozmez\u00ed
undo.compress=zkomprimovat trasu
undo.insert=vlo\u017eit body
undo.addaltitudeoffset=p\u0159idat v\u00fd\u0161kov\u00fd posun
undo.rearrangewaypoints=p\u0159euspo\u0159\u00e1dat body
undo.cutandmove=p\u0159esunout v\u00fdb\u011br
-undo.connectphoto=p\u0159ipojit fotografii
-undo.disconnectphoto=odpojit fotografii
-undo.correlate=sladit fotografie
+undo.connect=p\u0159ipojit
+undo.disconnect=odpojit
+undo.correlatephotos=sladit fotografie
undo.rearrangephotos=uspo\u0159\u00e1dat fotografie
undo.createpoint=vytvo\u0159it bod
undo.rotatephoto=oto\u010dit fotografii
undo.convertnamestotimes=p\u0159ev\u00e9st n\u00e1zvy na \u010dasy
undo.lookupsrtm=na\u010d\u00edst nadm. v\u00fd\u0161ky ze SRTM
undo.deletefieldvalues=smazat hodnoty pol\u00ed
+undo.correlateaudios=sladit audionahr\u00e1vky
# Error messages
error.save.dialogtitle=Chyba p\u0159i ukl\u00e1d\u00e1n\u00ed
error.jpegload.dialogtitle=Chyba p\u0159i na\u010d\u00edt\u00e1n\u00ed fotografi\u00ed
error.jpegload.nofilesfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory
error.jpegload.nojpegsfound=Nenalezeny \u017e\u00e1dn\u00e9 soubory jpeg
-error.jpegload.noexiffound=Nenalezena informace EXIF
error.jpegload.nogpsfound=Nenalezena informace GPS
error.jpegload.exifreadfailed=Nepoda\u0159ilo se na\u010d\u00edst informaci EXIF. Tu nelze na\u010d\u00edst\nbez intern\u00ed nebo extern\u00ed knihovny.
+error.audioload.nofilesfound=Nebyly nalezeny \u017e\u00e1dn\u00e9 zvukov\u00e9 soubory.
error.gpsload.unknown=Nezn\u00e1m\u00e1 chyba
error.undofailed.title=Selhalo undo
error.undofailed.text=Nepoda\u0159ilo se vr\u00e1tit operaci
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.
menu.point.deletepoint=Punkt l\u00f6schen
menu.photo=Foto
menu.photo.saveexif=Exif Daten speichern
-menu.photo.connect=Mit Punkt verkn\u00fcpfen
-menu.photo.disconnect=Vom Punkt trennen
-menu.photo.delete=Foto entfernen
+function.connecttopoint=Mit Punkt verkn\u00fcpfen
+function.disconnectfrompoint=Vom Punkt trennen
+function.removephoto=Foto entfernen
+menu.audio=Audio
menu.view=Ansicht
menu.view.showsidebars=Seitenleisten anzeigen
menu.view.browser=Karte in Browser
menu.map.zoomout=Herauszoomen
menu.map.zoomfull=Auf Bildschirmgr\u00f6\u00dfe zoomen
menu.map.newpoint=Neuen Punkt erzeugen
+menu.map.drawpoints=Punktereihe aufzeichnen
menu.map.connect=Trackpunkte mit Linie anzeigen
menu.map.autopan=Autozentrierung
menu.map.showmap=Karte zeigen
altkey.menu.point=P
altkey.menu.view=A
altkey.menu.photo=F
+altkey.menu.audio=U
altkey.menu.settings=E
altkey.menu.help=H
function.getgpsies=Gpsies Tracks holen
function.uploadgpsies=Daten zum Gpsies hochladen
function.lookupsrtm=H\u00f6hendaten von SRTM holen
+function.getwikipedia=Wikipediaartikeln in der N\u00e4he nachschlagen
+function.searchwikipedianames=Wikipedia mit Name durchsuchen
+function.downloadosm=OSM Daten f\u00fcr dieses Gebiet herunterladen
function.duplicatepoint=Punkt verdoppeln
function.setcolours=Farben einstellen
+function.setlinewidth=Liniedicke einstellen
function.setlanguage=Sprache einstellen
function.correlatephotos=Fotos korrelieren
function.rearrangephotos=Fotos reorganisieren
function.rotatephotoleft=Foto nach Links drehen
function.rotatephotoright=Foto nach Rechts drehen
+function.photopopup=Fotofenster anzeigen
function.ignoreexifthumb=Exif Vorschaubild ignorieren
+function.loadaudio=Audiodateien laden
+function.removeaudio=Audiodatei entfernen
+function.correlateaudios=Audios korrelieren
+function.playaudio=Audiodatei abspielen
+function.stopaudio=Abspielen abbrechen
function.help=Hilfe
function.showkeys=Tastenkombinationen anzeigen
function.about=\u00dcber Prune
dialog.pointtype.track=Trackpunkte
dialog.pointtype.waypoint=Wegpunkte
dialog.pointtype.photo=Fotopunkte
+dialog.pointtype.audio=Audiopunkte
dialog.pointtype.selection=Nur aktuellen Bereich
dialog.confirmreversetrack.title=Umkehrung best\u00e4tigen
dialog.confirmreversetrack.text=Diese Daten enthalten Zeitangaben, die bei einer Umkehrung in falscher Reihenfolge erscheinen w\u00fcrden.\nSind Sie sicher, dass Sie diesen Bereich umkehren wollen?
dialog.gpsies.activity.snowshoe=Schneeschuh
dialog.gpsies.activity.sailing=Segeln
dialog.gpsies.activity.skating=Inline-Skating
+dialog.wikipedia.column.name=Artikelname
+dialog.wikipedia.column.distance=Entfernung
dialog.correlate.notimestamps=Die Punkte enthalten keine Zeitangaben, deshalb k\u00f6nnen die Fotos nicht zugeordnet werden.
dialog.correlate.nouncorrelatedphotos=Alle Fotos sind schon zugeordnet.\nWollen Sie trotzdem fortfahren?
dialog.correlate.photoselect.intro=W\u00e4hlen Sie eines dieser Fotos aus, um die Zeitdifferenz zu berechnen
-dialog.correlate.photoselect.photoname=Bezeichnung des Fotos
-dialog.correlate.photoselect.timediff=Zeitdifferenz
-dialog.correlate.photoselect.photolater=Foto sp\u00e4ter
-dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell korrelierten Foto kann die Zeitdifferenz automatisch berechnet werden.
+dialog.correlate.select.photoname=Bezeichnung des Fotos
+dialog.correlate.select.timediff=Zeitdifferenz
+dialog.correlate.select.photolater=Foto sp\u00e4ter
+dialog.correlate.options.tip=Tipp: Mit mindestens einem manuell verbundenen Element kann die Zeitdifferenz automatisch berechnet werden.
dialog.correlate.options.intro=W\u00e4hlen Sie die Optionen f\u00fcr die Korrelation aus
dialog.correlate.options.offsetpanel=Zeitunterschied
dialog.correlate.options.offset=Unterschied
dialog.correlate.options.offset.minutes=Minuten und
dialog.correlate.options.offset.seconds=Sekunden
dialog.correlate.options.photolater=Foto sp\u00e4ter als Punkt
-dialog.correlate.options.pointlater=Punkt sp\u00e4ter als Foto
+dialog.correlate.options.pointlaterphoto=Punkt sp\u00e4ter als Foto
+dialog.correlate.options.audiolater=Audio sp\u00e4ter als Punkt
+dialog.correlate.options.pointlateraudio=Punkt sp\u00e4ter als Audio
dialog.correlate.options.limitspanel=Korrelation Grenzen
dialog.correlate.options.notimelimit=Keine Zeitgrenzen
dialog.correlate.options.timelimit=Zeitgrenzen
dialog.correlate.options.distancelimit=Distanzgrenzen
dialog.correlate.options.correlate=Korrelieren
dialog.correlate.alloutsiderange=Alle Fotos sind au\u00dferhalb des Track Zeitraums. Sie k\u00f6nnen nicht korreliert werden.\nVersuchen Sie es mit einem anderen Offset oder binden Sie manuell mindestens ein Foto ein.
+dialog.correlate.filetimes=Die Datei Zeitstempel zeigen:
+dialog.correlate.filetimes2=der Tonspuren
+dialog.correlate.correltimes=F\u00fcr das Korrelieren, folgendes verwenden:
+dialog.correlate.timestamp.beginning=Anfang
+dialog.correlate.timestamp.middle=Mitte
+dialog.correlate.timestamp.end=Ende
+dialog.correlate.audioselect.intro=W\u00e4hlen Sie eines dieser Audios aus, um die Zeitdifferenz zu berechnen
+dialog.correlate.select.audioname=Audio Name
+dialog.correlate.select.audiolater=Audio sp\u00e4ter
dialog.rearrangephotos.desc=Setzen Sie das Ziel und die Reihenfolge der Fotopunkte
dialog.rearrangephotos.tostart=Am Anfang
dialog.rearrangephotos.toend=Am Ende
dialog.saveconfig.prune.kmzimagewidth=Bildbreite in KMZ
dialog.saveconfig.prune.kmzimageheight=Bildh\u00f6he in KMZ
dialog.saveconfig.prune.colourscheme=Farbschema
+dialog.saveconfig.prune.linewidth=Liniedicke
dialog.saveconfig.prune.kmltrackcolour=KML Trackfarbe
dialog.setpaths.intro=Sie k\u00f6nnen hier die Pfade f\u00fcr externe Applikationen setzen:
dialog.setpaths.found=Pfad gefunden?
dialog.diskcache.createdir=Ordner anlegen
dialog.diskcache.nocreate=Ordner wurde nicht angelegt
dialog.deletefieldvalues.intro=W\u00e4hlen Sie das Feld aus, die Sie l\u00f6schen m\u00f6chten
+dialog.setlinewidth.text=Geben Sie die Dicke der Linien ein (1-4)
+dialog.downloadosm.desc=Best\u00e4tigen um rohe OSM Daten f\u00fcr den Gebiet herunterzuladen:
+dialog.searchwikipedianames.search=Suche nach:
# 3d window
dialog.3d.title=Prune 3D Ansicht
-dialog.3d.altitudecap=Minimum H\u00f6henskala
dialog.3d.altitudefactor=Vervielfachungsfaktor für Höhen
dialog.3dlines.title=Prune Gitterlinien
dialog.3dlines.empty=Keine Linien zum Anzeigen!
dialog.3dlines.intro=Hier sind die Linien f\u00fcr die 3D Ansicht
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
confirm.loadfile=Daten aus Datei geladen
confirm.save.ok1=Es wurden
confirm.save.ok2=Punkte gespeichert nach
confirm.undo.multi=Operationen r\u00fcckg\u00e4ngig gemacht
confirm.jpegload.single=Foto wurde geladen
confirm.jpegload.multi=Fotos wurden geladen
-confirm.photo.connect=Foto verbunden
+confirm.media.connect=Media verbunden
confirm.photo.disconnect=Foto getrennt
-confirm.correlate.single=Foto wurde korreliert
-confirm.correlate.multi=Fotos wurden korreliert
+confirm.audio.disconnect=Audio getrennt
+confirm.correlatephotos.single=Foto wurde korreliert
+confirm.correlatephotos.multi=Fotos wurden korreliert
confirm.createpoint=Punkt erzeugt
confirm.rotatephoto=Foto gedreht
confirm.running=In Bearbeitung ...
confirm.lookupsrtm1=Es wurden
confirm.lookupsrtm2=H\u00f6henwerte gefunden
confirm.deletefieldvalues=Feldwerte gelöscht
+confirm.audioload=Audiodateien geladen
+confirm.media.removed=entfernt
+confirm.correlateaudios.single=Audio wurde korreliert
+confirm.correlateaudios.multi=Audios wurden korreliert
-# Buttons || These are all the texts for buttons
+# Buttons
button.ok=OK
button.back=Zur\u00fcck
button.next=Vorw\u00e4rts
filetype.gpx=GPX Dateien
filetype.pov=POV Dateien
filetype.svg=SVG Dateien
+filetype.audio=MP3, OGG, WAV Dateien
# Display components
display.nodata=Keine Daten geladen
details.range.numsegments=Anzahl Abschnitte
details.range.pace=Tempo
details.range.gradient=Gef\u00e4lle
-details.waypointsphotos.waypoints=Wegpunkte
-details.waypointsphotos.photos=Fotos
+details.lists.waypoints=Wegpunkte
+details.lists.photos=Fotos
details.photodetails=Fotodetails
details.nophoto=Kein Foto ausgew\u00e4hlt
details.photo.loading=Laden
-details.photo.connected=Verbunden
+details.media.connected=Verbunden
+details.lists.audio=Audio
+details.audiodetails=Audiodetails
+details.noaudio=Keine Audiodatei ausgew\u00e4hlt
+details.audio.file=Audiodatei
+details.audio.playing=wird abgespielt...
map.overzoom=Keine Karten f\u00fcr diesen Zoomfaktor verf\u00fcgbar
# Field names
# External urls
url.googlemaps=maps.google.de
+wikipedia.lang=de
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=Daten laden
undo.loadphotos=Fotos laden
+undo.loadaudios=Audiodateien laden
undo.editpoint=Punkt bearbeiten
undo.deletepoint=Punkt l\u00f6schen
-undo.deletephoto=Foto entfernen
+undo.removephoto=Foto entfernen
+undo.removeaudio=Audiodatei entfernen
undo.deleterange=Bereich l\u00f6schen
undo.compress=Track komprimieren
undo.insert=Punkte hinzuf\u00fcgen
undo.addaltitudeoffset=H\u00f6henverschiebung aufrechnen
undo.rearrangewaypoints=Wegpunkte reorganisieren
undo.cutandmove=Bereich verschieben
-undo.connectphoto=Foto verbinden
-undo.disconnectphoto=Foto trennen
-undo.correlate=Fotos korrelieren
+undo.connect=verbinden
+undo.disconnect=trennen
+undo.correlatephotos=Fotos korrelieren
undo.rearrangephotos=Fotos reorganisieren
undo.createpoint=Punkt erzeugen
undo.rotatephoto=Foto umdrehen
undo.convertnamestotimes=Namen in Zeitstempel umwandeln
undo.lookupsrtm=H\u00f6hendaten von SRTM holen
undo.deletefieldvalues=Feldwerte löschen
+undo.correlateaudios=Audios korrelieren
# Error messages
error.save.dialogtitle=Fehler beim Speichern
error.jpegload.dialogtitle=Fehler beim Laden von Fotos
error.jpegload.nofilesfound=Keine Dateien gefunden
error.jpegload.nojpegsfound=Keine Jpeg Dateien gefunden
-error.jpegload.noexiffound=Keine EXIF Information gefunden
error.jpegload.nogpsfound=Keine GPS Information gefunden
error.jpegload.exifreadfailed=EXIF Aufruf fehlgeschlagen. Keine EXIF Information k\u00f6nnen gelesen werden\nohne einen internen oder externen Bibliothek.
+error.audioload.nofilesfound=Keine Audiodateien gefunden
error.gpsload.unknown=Unbekannter Fehler
error.undofailed.title=Undo fehlgeschlagen
error.undofailed.text=Operation konnte nicht r\u00fcckg\u00e4ngig gemacht werden
error.lookupsrtm.nonerequired=Alle Punkte haben schon Höhendaten
error.gpsies.uploadnotok=Der Gpsies Server hat geantwortet
error.gpsies.uploadfailed=Das Hochladen ist fehlgeschlagen
+error.playaudiofailed=Das Abspielen der Audiodatei ist fehlgeschlagen
menu.point.deletepoint=Punkt lösche
menu.photo=Föteli
menu.photo.saveexif=Exif Date speicherä
-menu.photo.connect=Mitem Punkt verbindä
-menu.photo.disconnect=Vonem Punkt trännä
-menu.photo.delete=Föteli entfernä
+function.connecttopoint=Mitem Punkt verbindä
+function.disconnectfrompoint=Vonem Punkt trännä
+function.removephoto=Föteli entfernä
+menu.audio=Audio
menu.view=Aasicht
menu.view.showsidebars=Seiteleischten aazeige
menu.view.browser=Karte inem Browser
menu.map.zoomout=Uusezoome
menu.map.zoomfull=Zoome zum ganzes Bild
menu.map.newpoint=Noii Punkt
+menu.map.drawpoints=Noii Punkte uufzeichnä
menu.map.connect=Trackpünktli verbindä
menu.map.autopan=Autopan
menu.map.showmap=Karte zeigä
altkey.menu.point=P
altkey.menu.view=A
altkey.menu.photo=F
+altkey.menu.audio=U
altkey.menu.settings=I
altkey.menu.help=H
function.pastecoordinates=Noii Koordinaten iigebe
function.charts=Diagramme
function.show3d=Drüü-D Aasicht
-function.distances=Distanze
+function.distances=Entfärnige
function.fullrangedetails=Zuesätzlichi Beriichinfos
function.setmapbg=Karte Hintegrund setzä
function.getgpsies=Gpsies Tracks holä
function.uploadgpsies=Date zum Gpsies uufaladä
function.lookupsrtm=Höhendate vonem SRTM hole
+function.getwikipedia=Im Wikipedia in dr Nöchi naaluege
+function.searchwikipedianames=Wikipedia mit Name durasueche
+function.downloadosm=OSM-Date für dere Gebiet abaladä
function.duplicatepoint=Punkt verdopplä
function.correlatephotos=Fötelis korrelierä
function.rearrangephotos=Fötelis reorganisierä
function.rotatephotoleft=Föteli nach Links dräyä
function.rotatephotoright=Föteli nach Rächts dräyä
+function.photopopup=Fötelifänschter aazeigä
function.ignoreexifthumb=Exif Vorschaubildli ignorierä
-function.setkmzimagesize=Bildligrösse inem KMZ setze
-function.setpaths=Programmepfade setze
-function.setcolours=Farben setze
-function.setlanguage=Sproch setze
+function.loadaudio=Audiofiles lade
+function.removeaudio=Audiodatei entfernä
+function.correlateaudios=Audios korrelierä
+function.playaudio=Audiofile abspielä
+function.stopaudio=Abspielen abbrächä
+function.setkmzimagesize=Bildligrösse inem KMZ setzä
+function.setpaths=Programmepfade setzä
+function.setcolours=Farben setzä
+function.setlinewidth=Liniedicke setzä
+function.setlanguage=Sproch setzä
function.help=Hilfe
function.showkeys=Tastekombinatione aazeige
function.about=Über Prune
dialog.pointtype.track=Trackpunkte
dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Fötelipunkte
+dialog.pointtype.audio=Audiopunkte
dialog.pointtype.selection=Nur aktuelli Beriich
dialog.confirmreversetrack.title=Umdrehig bestätige
dialog.confirmreversetrack.text=Diese Daten enthalte Ziitstämpel Informatione, die bei dr Umkehrig usser Reihefolge erschiene würdi.\nSind Sie sicher, Sie wend dn Beriich umkehre?
dialog.charts.svgheight=SVG Höhi
dialog.charts.needaltitudeortimes=Ohni Höhi Date und au ohne Ziit, isch es nöd möglech, Diagramme z zeigä.
dialog.charts.gnuplotnotfound=Gnuplot isch mit dem Pfad nöd gfunde worde
-dialog.distances.intro=Dischtanze per Luftlinie zwüschet Punkte
+dialog.distances.intro=Entfärnige per Luftlinie zwüschet Punkte
dialog.distances.column.from=Vom Punkt
dialog.distances.column.to=Zum Punkt
dialog.distances.currentpoint=Aktuelli Punkt
dialog.gpsies.activity.snowshoe=Schneeschuh
dialog.gpsies.activity.sailing=Segle
dialog.gpsies.activity.skating=Inline-Skate
+dialog.wikipedia.column.name=Artikelname
+dialog.wikipedia.column.distance=Entfärnig
dialog.correlate.notimestamps=Es hät kei Ziitstämpel inem Track innä, so s'isch nöd möglech die Fötelis zu korrelierä.
dialog.correlate.nouncorrelatedphotos=Alle Fötelis sin scho korreliert.\nWend Sie trotzdem fortsetzä?
dialog.correlate.photoselect.intro=Wählet Sie eini vo deren Föteli uus, um die Ziitdifferänz zu berächnä
-dialog.correlate.photoselect.photoname=Föteli Name
-dialog.correlate.photoselect.timediff=Ziitdifferänz
-dialog.correlate.photoselect.photolater=Föteli spöter
-dialog.correlate.options.tip=Tipp: Mit mindeschtens einem korrelierten Föteli, die Ziitdifferänz kann automatisch berächnet werdä.
+dialog.correlate.select.photoname=Föteli Name
+dialog.correlate.select.timediff=Ziitdifferänz
+dialog.correlate.select.photolater=Föteli spöter
+dialog.correlate.options.tip=Tipp: Mit mindeschtens einem verbundenen Elemänt kann die Ziitdifferänz automatisch berächnet werdä.
dialog.correlate.options.intro=Wählet Sie die Optione uus für die Korrelierig
dialog.correlate.options.offsetpanel=Ziitunterschied
dialog.correlate.options.offset=Unterschied
dialog.correlate.options.offset.minutes=Minutä und
dialog.correlate.options.offset.seconds=Sekundä
dialog.correlate.options.photolater=Föteli spöter alsem Punkt
-dialog.correlate.options.pointlater=Punkt spöter alsem Föteli
+dialog.correlate.options.pointlaterphoto=Punkt spöter alsem Föteli
+dialog.correlate.options.audiolater=Audio spöter alsem Punkt
+dialog.correlate.options.pointlateraudio=Punkt spöter alsem Audio
dialog.correlate.options.limitspanel=Korrelation Gränzä
dialog.correlate.options.notimelimit=Kei Ziitgränzä
dialog.correlate.options.timelimit=Ziitgränzä
dialog.correlate.options.distancelimit=Distanzgränzä
dialog.correlate.options.correlate=Korrelierä
dialog.correlate.alloutsiderange=Alli Fötelis sin uusserhalb vonem Track Ziitruum, so chönne nöd korreliert werdä.\nVersuechet Sie mitenem anderen Offset oder verbindet Sie manuell mindeschtens eis Föteli.
+dialog.correlate.filetimes=Die Datei Zeitstempel zeigen:
+dialog.correlate.filetimes2=der Tonspuren
+dialog.correlate.correltimes=Fürs Korreliere, folgendes verwände:
+dialog.correlate.timestamp.beginning=Aafang
+dialog.correlate.timestamp.middle=Mitti
+dialog.correlate.timestamp.end=Ände
+dialog.correlate.audioselect.intro=Wählet Sie eini vo deren Audios uus, um die Ziitdifferänz zu berächnä
+dialog.correlate.select.audioname=Audio Name
+dialog.correlate.select.audiolater=Audio spöter
dialog.rearrangephotos.desc=Bitte Ziel und Reihefolge von den Punkten setze
dialog.rearrangephotos.tostart=zum Aafang
-dialog.rearrangephotos.toend=zum Ende
+dialog.rearrangephotos.toend=zum Ände
dialog.rearrangephotos.nosort=Nöd sortiere
dialog.rearrangephotos.sortbyfilename=per Filename sortiere
dialog.rearrangephotos.sortbytime=per Ziit sortiere
dialog.checkversion.releasedate2=ussecho.
dialog.checkversion.download=Um die noii Version runterzlade, schauet Sie na http://activityworkshop.net/software/prune/download.html.
dialog.keys.intro=Aastatt d'Muus könnet Sie diese Tastekombinationen nutze
-dialog.keys.keylist=<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, Ende</td><td>Erschti oder letschti Punkt markiere</td></tr><tr><td>Entf</td><td>Aktuelli Punkt lösche</td></tr></table>
+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.keys.macmodifier=Kommando
dialog.saveconfig.desc=Die folgendi Iinstellige könne gspeicheret werde :
dialog.saveconfig.prune.kmzimagewidth=Bildbreiti im KMZ
dialog.saveconfig.prune.kmzimageheight=Bildhöchi im KMZ
dialog.saveconfig.prune.colourscheme=Farbeschema
+dialog.saveconfig.prune.linewidth=Liniedicke
dialog.saveconfig.prune.kmltrackcolour=KML Trackfarb
dialog.setpaths.intro=Sie könnet dann die Pfade für dia Applikatione setzä:
dialog.setpaths.found=Pfad gfunde?
dialog.diskcache.createdir=Ordner kreiere
dialog.diskcache.nocreate=Ordner isch nöd kreiert worde
dialog.deletefieldvalues.intro=Wählet Sie s Fäld uus zum lösche
+dialog.setlinewidth.text=Gäbet Sie die Dicke vonen Linien ii (1-4)
+dialog.downloadosm.desc=Best\ätige um rohi OSM Date fürn Gebiet aba zlade:
+dialog.searchwikipedianames.search=Sueche na:
# 3d window
dialog.3d.title=Prune Drüü-d Aasicht
-dialog.3d.altitudecap=Minimum Höhenskala
dialog.3d.altitudefactor=Höchivervilfachigsfaktor
dialog.3dlines.title=Prune Gitterlinie
dialog.3dlines.empty=Kei Linie zum aazeigä!
confirm.undo.multi=Operatione rückgängig gmacht worde.
confirm.jpegload.single=Föteli isch glade worde
confirm.jpegload.multi=Fötelis sin glade worde
-confirm.photo.connect=Föteli verbundä
+confirm.media.connect=Media verbundä
confirm.photo.disconnect=Föteli gtrännt
-confirm.correlate.single=Föteli isch korreliert worde
-confirm.correlate.multi=Fötelis sin korreliert worde
+confirm.audio.disconnect=Audio gtrännt
+confirm.correlatephotos.single=Föteli isch korreliert worde
+confirm.correlatephotos.multi=Fötelis sin korreliert worde
confirm.createpoint=Punkt kreiert worde
confirm.rotatephoto=Föteli umgedräit worde
confirm.running=Am Laufe ...
confirm.lookupsrtm1=Es sin
confirm.lookupsrtm2=Höhenwerte gfunde
-confirm.deletefieldvalues=Feldwärte glöscht
+confirm.deletefieldvalues=Feldwärte glöscht worde
+confirm.audioload=Audiofiles glade worde
+confirm.media.removed=entfärnt
+confirm.correlateaudios.single=Audiofile isch korreliert worde
+confirm.correlateaudios.multi=Audiofiles sin korreliert worde
# Buttons
button.ok=OK
filetype.gpx=GPX Dateie
filetype.pov=POV Dateie
filetype.svg=SVG Dateie
+filetype.audio=MP3, OGG, WAV Dateie
# Display components
display.nodata=Kei Date glade worde
details.range.numsegments=Aazahl Segmänte
details.range.pace=Tempo
details.range.gradient=Gefälle
-details.waypointsphotos.waypoints=Waypoints
-details.waypointsphotos.photos=Fötelis
+details.lists.waypoints=Waypoints
+details.lists.photos=Fötelis
details.photodetails=Details vonem Föteli
details.nophoto=Kei föteli selektiert
details.photo.loading=Ladä
-details.photo.connected=Verbundä
+details.media.connected=Verbundä
+details.lists.audio=Audio
+details.audiodetails=Audiodetails
+details.noaudio=Kei Audiofile selektiert
+details.audio.file=Audiofile
+details.audio.playing=am abschpielä...
map.overzoom=Kei Karte mit diesem Zoom
# Field names
# External urls
url.googlemaps=maps.google.ch
+wikipedia.lang=als
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=Date ladä
undo.loadphotos=Fötelis ladä
+undo.loadaudios=Audiofiles ladä
undo.editpoint=Punkt editierä
undo.deletepoint=Punkt löschä
-undo.deletephoto=Föteli entfärnä
+undo.removephoto=Föteli entfärnä
+undo.removeaudio=Audiofile entfärnä
undo.deleterange=Beriich löschä
undo.compress=Track komprimierä
undo.insert=Punkte innätuä
undo.addaltitudeoffset=höchiverschiebig zutue
undo.rearrangewaypoints=Waypoints reorganisierä
undo.cutandmove=Selektion movä
-undo.connectphoto=Föteli verbindä
-undo.disconnectphoto=Föteli trännä
-undo.correlate=Fötelis korrelierä
+undo.connect=verbindä
+undo.disconnect=trännä
+undo.correlatephotos=Fötelis korrelierä
undo.rearrangephotos=Fötelis reorganisierä
undo.createpoint=Punkt kreierä
undo.rotatephoto=Föteli umadräya
undo.convertnamestotimes=Name ins Ziitstämple verwondlä
undo.lookupsrtm=Höhendate vonem SRTM holä
undo.deletefieldvalues=Feldwärte löschä
+undo.correlateaudios=Audios korrelierä
# Error messages
error.save.dialogtitle=Fähle bim Speichere
error.load.noxmlinzip=Kei xml im Zip File gfunde
error.load.othererror=Fähle bim Läse:
error.jpegload.dialogtitle=Fähle bim Lade von Fötelis
-error.jpegload.nofilesfound=Kei Dateie gfunde
+error.jpegload.nofilesfound=Kei Files gfunde
error.jpegload.nojpegsfound=Kei Jpegs gfunde
-error.jpegload.noexiffound=Kei EXIF Information gfunde
error.jpegload.nogpsfound=Kei GPS Information gfunde
error.jpegload.exifreadfailed=EXIF Uufruef isch fehlgschlage. Kei EXIF Infos könnet gläse werde\nohni nen interni oder extärni Bibliothek.
+error.audioload.nofilesfound=Kei Audiofiles gfunde
error.gpsload.unknown=Unbekannts Fähler
error.undofailed.title=Undo isch fehlgschlage worde
error.undofailed.text=Operation kann nöd rückgängig gmacht werde
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
menu.point.deletepoint=Delete point
menu.photo=Photo
menu.photo.saveexif=Save to Exif
-menu.photo.connect=Connect to point
-menu.photo.disconnect=Disconnect from point
-menu.photo.delete=Remove photo
+menu.audio=Audio
menu.view=View
menu.view.showsidebars=Show sidebars
menu.view.browser=Map in a browser window
menu.map.zoomout=Zoom out
menu.map.zoomfull=Zoom to full scale
menu.map.newpoint=Create new point
+menu.map.drawpoints=Create series of points
menu.map.connect=Connect track points
menu.map.autopan=Autopan
menu.map.showmap=Show map
altkey.menu.point=P
altkey.menu.view=V
altkey.menu.photo=O
+altkey.menu.audio=A
altkey.menu.settings=S
altkey.menu.help=H
function.getgpsies=Get Gpsies tracks
function.uploadgpsies=Upload track to Gpsies
function.lookupsrtm=Get altitudes from SRTM
+function.getwikipedia=Get nearby Wikipedia articles
+function.searchwikipedianames=Search Wikipedia by name
+function.downloadosm=Download OSM data for area
function.duplicatepoint=Duplicate point
+function.connecttopoint=Connect to point
+function.disconnectfrompoint=Disconnect from point
+function.removephoto=Remove photo
function.correlatephotos=Correlate photos
function.rearrangephotos=Rearrange photos
function.rotatephotoleft=Rotate photo left
function.rotatephotoright=Rotate photo right
+function.photopopup=Show photo popup
function.ignoreexifthumb=Ignore exif thumbnail
+function.loadaudio=Add audio files
+function.removeaudio=Remove audio file
+function.correlateaudios=Correlate audios
+function.playaudio=Play audio file
+function.stopaudio=Stop audio file
function.setmapbg=Set map background
function.setkmzimagesize=Set KMZ image size
function.setpaths=Set program paths
function.setcolours=Set colours
+function.setlinewidth=Set line width
function.setlanguage=Set language
function.help=Help
function.showkeys=Show shortcut keys
dialog.pointtype.track=Track points
dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Photo points
+dialog.pointtype.audio=Audio points
dialog.pointtype.selection=Just selection
dialog.confirmreversetrack.title=Confirm reversal
dialog.confirmreversetrack.text=This track contains timestamp information, which will be out of sequence after a reversal.\nAre you sure you want to reverse this section?
dialog.gpsies.activity.snowshoe=Snowshoeing
dialog.gpsies.activity.sailing=Sailing
dialog.gpsies.activity.skating=Skating
+dialog.wikipedia.column.name=Article name
+dialog.wikipedia.column.distance=Distance
dialog.correlate.notimestamps=There are no timestamps in the data points, so there is nothing to correlate with the photos.
dialog.correlate.nouncorrelatedphotos=There are no uncorrelated photos.\nAre you sure you want to continue?
dialog.correlate.photoselect.intro=Select one of these correlated photos to use as the time offset
-dialog.correlate.photoselect.photoname=Photo name
-dialog.correlate.photoselect.timediff=Time difference
-dialog.correlate.photoselect.photolater=Photo later
-dialog.correlate.options.tip=Tip: By manually correlating at least one photo, the time offset can be calculated for you.
+dialog.correlate.select.photoname=Photo name
+dialog.correlate.select.timediff=Time difference
+dialog.correlate.select.photolater=Photo later
+dialog.correlate.options.tip=Tip: By manually connecting at least one item, the time offset can be calculated for you.
dialog.correlate.options.intro=Select the options for automatic correlation
dialog.correlate.options.offsetpanel=Time offset
dialog.correlate.options.offset=Offset
dialog.correlate.options.offset.minutes=minutes and
dialog.correlate.options.offset.seconds=seconds
dialog.correlate.options.photolater=Photo later than point
-dialog.correlate.options.pointlater=Point later than photo
+dialog.correlate.options.pointlaterphoto=Point later than photo
+dialog.correlate.options.audiolater=Audio later than point
+dialog.correlate.options.pointlateraudio=Point later than audio
dialog.correlate.options.limitspanel=Correlation limits
dialog.correlate.options.notimelimit=No time limit
dialog.correlate.options.timelimit=Time limit
dialog.correlate.options.distancelimit=Distance limit
dialog.correlate.options.correlate=Correlate
dialog.correlate.alloutsiderange=All photos are outside the time range of the track, so none can be correlated.\nTry changing the offset or manually correlating at least one photo.
+dialog.correlate.filetimes=File timestamps denote:
+dialog.correlate.filetimes2=of audio clip
+dialog.correlate.correltimes=For correlation, use:
+dialog.correlate.timestamp.beginning=Beginning
+dialog.correlate.timestamp.middle=Middle
+dialog.correlate.timestamp.end=End
+dialog.correlate.audioselect.intro=Select one of these correlated audios to use as the time offset
+dialog.correlate.select.audioname=Audio name
+dialog.correlate.select.audiolater=Audio later
dialog.rearrangephotos.desc=Select the destination and sort order of the photo points
dialog.rearrangephotos.tostart=Move to start
dialog.rearrangephotos.toend=Move to end
dialog.saveconfig.prune.kmzimagewidth=KMZ image width
dialog.saveconfig.prune.kmzimageheight=KMZ image height
dialog.saveconfig.prune.colourscheme=Colour scheme
+dialog.saveconfig.prune.linewidth=Line width
dialog.saveconfig.prune.kmltrackcolour=KML track colour
dialog.setpaths.intro=If you need to, you can choose the paths to the external applications:
dialog.setpaths.found=Path found?
dialog.diskcache.createdir=Create directory
dialog.diskcache.nocreate=Cache directory not created
dialog.deletefieldvalues.intro=Select the field to delete for the current range
+dialog.setlinewidth.text=Enter the thickness of lines to draw for the tracks (1-4)
+dialog.downloadosm.desc=Confirm to download the raw OSM data for the specified area:
+dialog.searchwikipedianames.search=Search for:
# 3d window
dialog.3d.title=Prune Three-d view
-dialog.3d.altitudecap=
dialog.3d.altitudefactor=Altitude exaggeration factor
dialog.3dlines.title=Prune gridlines
dialog.3dlines.empty=No gridlines to display!
dialog.3dlines.intro=These are the gridlines for the three-d view
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
confirm.loadfile=Data loaded from file
confirm.save.ok1=Successfully saved
confirm.save.ok2=points to file
confirm.undo.multi=operations undone
confirm.jpegload.single=photo was added
confirm.jpegload.multi=photos were added
-confirm.photo.connect=photo connected
+confirm.media.connect=media connected
confirm.photo.disconnect=photo disconnected
-confirm.correlate.single=photo was correlated
-confirm.correlate.multi=photos were correlated
+confirm.audio.disconnect=audio disconnected
+confirm.media.removed=removed
+confirm.correlatephotos.single=photo was correlated
+confirm.correlatephotos.multi=photos were correlated
confirm.rotatephoto=photo rotated
confirm.createpoint=point created
confirm.running=Running ...
confirm.lookupsrtm1=Found
confirm.lookupsrtm2=altitude values
confirm.deletefieldvalues=Field values deleted
+confirm.audioload=Audio files added
+confirm.correlateaudios.single=audio was correlated
+confirm.correlateaudios.multi=audios were correlated
# Buttons
button.ok=OK
filetype.gpx=GPX files
filetype.pov=POV files
filetype.svg=SVG files
+filetype.audio=MP3, OGG, WAV files
# Display components
display.nodata=No data loaded
details.range.numsegments=Number of segments
details.range.pace=Pace
details.range.gradient=Gradient
-details.waypointsphotos.waypoints=Waypoints
-details.waypointsphotos.photos=Photos
+details.lists.waypoints=Waypoints
+details.lists.photos=Photos
+details.lists.audio=Audio
details.photodetails=Photo details
details.nophoto=No photo selected
details.photo.loading=Loading
-details.photo.connected=Connected
+details.media.connected=Connected
+details.audiodetails=Audio details
+details.noaudio=No audio file selected
+details.audio.file=Audio file
+details.audio.playing=playing...
map.overzoom=No maps available at this zoom level
# Field names
# External urls
url.googlemaps=maps.google.co.uk
+wikipedia.lang=en
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=load data
undo.loadphotos=load photos
+undo.loadaudios=load audio files
undo.editpoint=edit point
undo.deletepoint=delete point
-undo.deletephoto=remove photo
+undo.removephoto=remove photo
+undo.removeaudio=remove audio file
undo.deleterange=delete range
undo.compress=compress track
undo.insert=insert points
undo.addaltitudeoffset=add altitude offset
undo.rearrangewaypoints=rearrange waypoints
undo.cutandmove=move section
-undo.connectphoto=connect photo
-undo.disconnectphoto=disconnect photo
-undo.correlate=correlate photos
+undo.connect=connect
+undo.disconnect=disconnect
+undo.correlatephotos=correlate photos
undo.rearrangephotos=rearrange photos
undo.rotatephoto=rotate photo
undo.createpoint=create point
undo.convertnamestotimes=convert names to times
undo.lookupsrtm=lookup altitudes from SRTM
undo.deletefieldvalues=delete field values
+undo.correlateaudios=correlate audios
# Error messages
error.save.dialogtitle=Error saving data
error.jpegload.dialogtitle=Error loading photos
error.jpegload.nofilesfound=No files found
error.jpegload.nojpegsfound=No jpeg files found
-error.jpegload.noexiffound=No EXIF information found
error.jpegload.nogpsfound=No GPS information found
error.jpegload.exifreadfailed=Failed to read EXIF information. No EXIF information can be read\nwithout either an internal or external library.
+error.audioload.nofilesfound=No audio files found
error.gpsload.unknown=Unknown error
error.undofailed.title=Undo failed
error.undofailed.text=Failed to undo operation
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
menu.point.deletepoint=Eliminar punto
menu.photo=Foto
menu.photo.saveexif=Guardar Exif
-menu.photo.connect=Conectar con punto
-menu.photo.disconnect=Desconectar de punto
-menu.photo.delete=Eliminar foto
+menu.audio=Audio
menu.view=Ver
menu.view.showsidebars=Mostrar barras laterales
menu.view.browser=Mapa en una ventana del navegador
menu.map.zoomout=Reducir zoom
menu.map.zoomfull=Mostrar todo
menu.map.newpoint=Crear un punto nuevo
+menu.map.drawpoints=Crear series de puntos
menu.map.connect=Conectar puntos de track
menu.map.autopan=Posicionar autom\u00e1ticamente
menu.map.showmap=Mostrar el mapa
altkey.menu.point=U
altkey.menu.view=V
altkey.menu.photo=F
+altkey.menu.audio=D
altkey.menu.settings=P
altkey.menu.help=Y
function.getgpsies=Bajar ruta de Gpsies
function.uploadgpsies=Subir recorrido a Gpsies
function.lookupsrtm=Obtener altitudes de SRTM
+function.getwikipedia=Obtener art\u00edculos de Wikipedia cercanos
+function.searchwikipedianames=Buscar en Wikipedia por nombre
+function.downloadosm=Descargar datos OSM del \u00e1rea
function.duplicatepoint=Duplicar punto
function.setcolours=Establecer color
+function.setlinewidth=Establecer ancho de l\u00ednea
function.setlanguage=Establecer lenguaje
+function.connecttopoint=Conectar con punto
+function.disconnectfrompoint=Desconectar de punto
+function.removephoto=Eliminar foto
function.correlatephotos=Correlacionar fotos
function.rearrangephotos=Reacomodar fotos
function.rotatephotoleft=Girar a la izquierda
function.rotatephotoright=Girar a la derecha
+function.photopopup=Mostrar foto en ventana emergente
function.ignoreexifthumb=Ignorar miniatura exif
+function.loadaudio=A\u00f1adir archivos de audio
+function.removeaudio=Eliminar archivo de audio
+function.correlateaudios=Correlacionar audios
+function.playaudio=Reproducir archivo de audio
+function.stopaudio=Detener reproducci\u00f3n de audio
function.help=Ayuda
function.showkeys=Mostrar teclas o combinaciones de atajo
function.about=Acerca de Prune
# Dialogs
dialog.exit.confirm.title=Salir de Prune
-dialog.exit.confirm.text=¿Los datos han sido modificados. Desea salir de Prune?
-dialog.openappend.title=¿Agregar a datos existentes
-dialog.openappend.text=¿Agregar estos datos a los datos ya guardados?
+dialog.exit.confirm.text=\u00bfLos datos han sido modificados. Desea salir de Prune?
+dialog.openappend.title=\u00bfAgregar a datos existentes
+dialog.openappend.text=\u00bfAgregar estos datos a los datos ya guardados?
dialog.deletepoint.title=Borrar punto
-dialog.deletepoint.deletephoto=¿Borrar la foto tambien?
+dialog.deletepoint.deletephoto=\u00bfBorrar la foto tambien?
dialog.deletephoto.title=Borrar foto
-dialog.deletephoto.deletepoint=¿Borrar el punto tambien?
+dialog.deletephoto.deletepoint=\u00bfBorrar el punto tambien?
dialog.openoptions.title=Opciones de abrir
dialog.openoptions.filesnippet=Extraer archivo
dialog.load.table.field=Campo
dialog.jpegload.loadjpegsoutsidearea=Incluir fotos fuera del \u00e1rea
dialog.jpegload.progress.title=Cargando fotos
dialog.jpegload.progress=Por favor espere mientras se buscan las fotos
-dialog.gpsload.nogpsbabel=No se ha encontrado el programa gpsbabel. ¿Desea continuar?
+dialog.gpsload.nogpsbabel=No se ha encontrado el programa gpsbabel. \u00bfDesea continuar?
dialog.gpsload.device=Dispositivo
dialog.gpsload.format=Formato
dialog.gpsload.getwaypoints=Cargar waypoints
dialog.save.altitudeunits=Unidades de las altitudes
dialog.save.timestampformat=Formato del tiempo
dialog.save.overwrite.title=El archivo ya existe
-dialog.save.overwrite.text=El archivo ya existe, ¿desea sobreescribirlo?
+dialog.save.overwrite.text=El archivo ya existe, \u00bfdesea sobreescribirlo?
dialog.save.notypesselected=No se han seleccionado tipos de puntos
dialog.exportkml.text=Descripci\u00f3n para los datos
dialog.exportkml.altitude=Absoluta altitudes (para aviaci\u00f3n)
dialog.exportpov.tubesandwalls=Tubos y paredes
dialog.exportpov.warningtracksize=Este track contiene un gran numero de puntos. Puede ser que Java3D no los pueda visualizar. Est\u00e1 seguro de que desea continuar?
dialog.exportsvg.text=Seleccione los par\u00e1metros para exportar a SVG
-dialog.exportsvg.phi=Ángulo de azimuth \u03d5
-dialog.exportsvg.theta=Ángulo de elevaci\u00f3n
+dialog.exportsvg.phi=\u00c1ngulo de azimuth \u03d5
+dialog.exportsvg.theta=\u00c1ngulo de elevaci\u00f3n
dialog.exportsvg.gradients=Usar degradado para sombras
dialog.pointtype.desc=Salvar los siguientes tipos de puntos:
dialog.pointtype.track=Puntos de track
+dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Puntos de foto
+dialog.pointtype.audio=Puntos de audio
dialog.pointtype.selection=Solo selecci\u00f3n
dialog.confirmreversetrack.title=Confirmar inversi\u00f3n
-dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. ¿Est\u00e1 seguro que desea invertir esta secci\u00f3n?
+dialog.confirmreversetrack.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de la inversi\u00f3n. \u00bfEst\u00e1 seguro que desea invertir esta secci\u00f3n?
dialog.confirmcutandmove.title=Confirmar accion cortar/pegar
-dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n¿Esta seguro que desea mover esta secci\u00f3n?
+dialog.confirmcutandmove.text=Este track contiene informaci\u00f3n sobre la fecha, que estar\u00e1 fuera de secuencia despu\u00e9s de mover.\n\u00bfEsta seguro que desea mover esta secci\u00f3n?
dialog.interpolate.title=Interpolar puntos
dialog.interpolate.parameter.text=N\u00famero de los puntos a insertar entre los puntos elegidos
dialog.undo.title=Deshacer
dialog.undo.none.title=No se puede deshacer
dialog.undo.none.text=Ninguna operaci\u00f3n a deshacer
dialog.clearundo.title=Despejar la lista de deshacer
-dialog.clearundo.text=¿Esta seguro que desea despejar la lista de deshacer?, ¡se perder\u00e1 toda la informaci\u00f3n!
+dialog.clearundo.text=\u00bfEsta seguro que desea despejar la lista de deshacer?, ¡se perder\u00e1 toda la informaci\u00f3n!
dialog.pointedit.title=Editar punto
dialog.pointedit.text=Seleccione cada campo a editar y use el bot\u00f3n 'Editar' para modificar el valor
dialog.pointedit.table.field=Campo
dialog.saveexif.title=Guardar Exif
dialog.saveexif.intro=Seleccione fotos a guardar
dialog.saveexif.nothingtosave=Coordenadas no modificadas, nada que guardar
-dialog.saveexif.noexiftool=No se encuentra el programa exiftool. ¿Desea continuar?
+dialog.saveexif.noexiftool=No se encuentra el programa exiftool. \u00bfDesea continuar?
dialog.saveexif.table.photoname=Nombre de la foto
dialog.saveexif.table.status=Estado
dialog.saveexif.table.save=Guardar
dialog.gpsies.activity.snowshoe=Raquetas de nieve
dialog.gpsies.activity.sailing=Vela
dialog.gpsies.activity.skating=Patinaje
+dialog.wikipedia.column.name=Nombre del art\u00edculo
+dialog.wikipedia.column.distance=Distancia
dialog.correlate.notimestamps=No hay informaci\u00f3n de tiempo para los puntos, as\u00ed que no hay nada que correlacionar con las fotos.
-dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n¿Est\u00e1 seguro de que desea continuar?
+dialog.correlate.nouncorrelatedphotos=No hay fotos no correlacionadas.\n\u00bfEst\u00e1 seguro de que desea continuar?
dialog.correlate.photoselect.intro=Seleccione una de estas fotos correlacionadas para usar como margen de tiempo
-dialog.correlate.photoselect.photoname=Nombre de la foto
-dialog.correlate.photoselect.timediff=Diferencia de tiempo
-dialog.correlate.photoselect.photolater=Foto m\u00e1s adelante
+dialog.correlate.select.photoname=Nombre de la foto
+dialog.correlate.select.timediff=Diferencia de tiempo
+dialog.correlate.select.photolater=Foto m\u00e1s adelante
dialog.correlate.options.tip=Sugerencia: Correlacionando al menos una foto manualmente, el margen de tiempo se calcula autom\u00e1ticamente.
dialog.correlate.options.intro=Seleccionar las opciones para correlaci\u00f3n autom\u00e1tica
dialog.correlate.options.offsetpanel=Margen de tiempo
dialog.correlate.options.offset.minutes=minutos y
dialog.correlate.options.offset.seconds=segundos
dialog.correlate.options.photolater=Foto despu\u00e9s de punto
-dialog.correlate.options.pointlater=Punto despu\u00e9s de foto
+dialog.correlate.options.pointlaterphoto=Punto despu\u00e9s de foto
+dialog.correlate.options.audiolater=Audio despu\u00e9s de punto
+dialog.correlate.options.pointlateraudio=Punto despu\u00e9s de audio
dialog.correlate.options.limitspanel=L\u00edmites de correlaci\u00f3n
dialog.correlate.options.notimelimit=Sin l\u00edmite de tiempo
dialog.correlate.options.timelimit=L\u00edmite de tiempo
dialog.correlate.options.distancelimit=L\u00edmite de distancia
dialog.correlate.options.correlate=Correlacionar
dialog.correlate.alloutsiderange=Todas las fotos est\u00e1n fuera del margen horario del track, por lo que ninguna puede ser correlada.\nIntente cambiar el margen o correle manualmente al menos una foto.
+dialog.correlate.filetimes=Las marcas del archivo denotan:
+dialog.correlate.filetimes2=de sonido
+dialog.correlate.correltimes=Para correlacionar use:
+dialog.correlate.timestamp.beginning=Comienzo
+dialog.correlate.timestamp.middle=Mitad
+dialog.correlate.timestamp.end=Final
+dialog.correlate.audioselect.intro=Seleccione uno de estos audios correlacionados para usarlo como margen temporal.
+dialog.correlate.select.audioname=Nombre del audio
+dialog.correlate.select.audiolater=Audio m\u00e1s adelante
dialog.rearrangephotos.desc=Seleccionar el destino y sortear el orden de los puntos de las fotos
dialog.rearrangephotos.tostart=Mover al comienzo
dialog.rearrangephotos.toend=Mover al final
dialog.saveconfig.prune.gpsdevice=Dispositivo GPS
dialog.saveconfig.prune.gpsformat=Formato GPS
dialog.saveconfig.prune.povrayfont=Fuente povray
-dialog.saveconfig.prune.metricunits=¿Usar unidades m\u00e9tricas?
+dialog.saveconfig.prune.metricunits=\u00bfUsar unidades m\u00e9tricas?
dialog.saveconfig.prune.gnuplotpath=Ruta a gnuplot
dialog.saveconfig.prune.gpsbabelpath=Ruta a gpsbabel
dialog.saveconfig.prune.exiftoolpath=Ruta a exiftool
dialog.saveconfig.prune.kmzimagewidth=Ancho de im\u00e1genes en KMZ
dialog.saveconfig.prune.kmzimageheight=Alto de im\u00e1genes en KMZ
dialog.saveconfig.prune.colourscheme=Color de esquema
+dialog.saveconfig.prune.linewidth=Ancho de l\u00ednea
dialog.saveconfig.prune.kmltrackcolour=Color de pista de KML
dialog.setpaths.intro=Si usted necesita, puede escoger las rutas a aplicaciones externas
-dialog.setpaths.found=¿Ruta encontrada?
+dialog.setpaths.found=\u00bfRuta encontrada?
dialog.addaltitude.noaltitudes=Los rangos seleccionados no contienen altitudes
dialog.addaltitude.desc=Desplazamiento de altitud a a\u00f1adir
-dialog.lookupsrtm.overwritezeros=¿Sobrescribir valores de altitud nulos?
+dialog.lookupsrtm.overwritezeros=\u00bfSobrescribir valores de altitud nulos?
dialog.setcolours.intro=Haga clic sobre una placa de color para cambiar el color
dialog.setcolours.background=Fondo
dialog.setcolours.borders=Bordes
dialog.diskcache.createdir=Crear directorio
dialog.diskcache.nocreate=No se ha creado el directorio de mapas
dialog.deletefieldvalues.intro=Seleccionar el campo a eliminar para el rango actual
+dialog.setlinewidth.text=Introduzca la anchura de las l\u00edneas a dibujar para los recorridos (1-4)
+dialog.downloadosm.desc=Confirmar la descarga de datos en bruto de OSM para el \u00e1rea especificada.
+dialog.searchwikipedianames.search=Buscar:
# 3d window
dialog.3d.title=Prune vista 3-D
-dialog.3d.altitudecap=Escala de las altitudes
dialog.3d.altitudefactor=Factor de exageraci\u00f3n de altura
dialog.3dlines.title=Cuadr\u00edcula Prune
dialog.3dlines.empty=¡No hay ninguna cuadr\u00edcula!
confirm.undo.multi=operaci\u00f3n(es) deshechas(s)
confirm.jpegload.single=Foto incluida
confirm.jpegload.multi=Fotos incluidas
-confirm.photo.connect=Foto conectada
+confirm.media.connect=Medio conectada
confirm.photo.disconnect=Foto desconectada
-confirm.correlate.single=foto fue correlacionada
-confirm.correlate.multi=fotos fueron correlacionadas
+confirm.audio.disconnect=Audio desconectado
+confirm.media.removed=Eliminado
+confirm.correlatephotos.single=foto fue correlacionada
+confirm.correlatephotos.multi=fotos fueron correlacionadas
confirm.createpoint=punto creado
confirm.rotatephoto=foto rotada
confirm.running=Trabajando ...
confirm.lookupsrtm1=Encontrados
confirm.lookupsrtm2=valor de altitud para la funci\u00f3n de b\u00fasqueda SRTM
confirm.deletefieldvalues=Valores del campo eliminados
+confirm.audioload=A\u00f1adidos archivos de audio
+confirm.correlateaudios.single=El audio fue correlacionado
+confirm.correlateaudios.multi=Los audios fueron correlacionados
# Buttons
button.ok=Aceptar
filetype.gpx=Archivos GPX
filetype.pov=Archivos POV
filetype.svg=Archivos SVG
+filetype.audio=Archivos MP3, OGG, WAV
# Display components
display.nodata=Ning\u00fan dato cargado
details.range.numsegments=N\u00famero de segmentos
details.range.pace=Ritmo
details.range.gradient=Gradiente
-details.waypointsphotos.waypoints=Waypoints
-details.waypointsphotos.photos=Fotos
+details.lists.waypoints=Waypoints
+details.lists.photos=Fotos
+details.lists.audio=Audio
details.photodetails=Detalles de la foto
details.nophoto=Ninguna foto seleccionada
details.photo.loading=Cargando
-details.photo.connected=Conectada
+details.media.connected=Conectada
+details.audiodetails=Detalles de audio
+details.noaudio=No se ha seleccionado ning\u00fan archivo de audio
+details.audio.file=Archivo de audio
+details.audio.playing=Reproduciendo...
map.overzoom=No existen mapas disponibles con este nivel de enfoque
# Field names
# External urls
url.googlemaps=maps.google.es
+wikipedia.lang=es
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=cargar datos
undo.loadphotos=cargar fotos
+undo.loadaudios=Cargar archivos de audio
undo.editpoint=editar punto
undo.deletepoint=eliminar punto
-undo.deletephoto=eliminar foto
+undo.removephoto=eliminar foto
+undo.removeaudio=Eliminar archivos de audio
undo.deleterange=eliminar rango
undo.compress=comprimir track
undo.insert=insertar puntos
undo.addaltitudeoffset=a\u00f1adir margen de altitud
undo.rearrangewaypoints=reordenar waypoints
undo.cutandmove=mover secci\u00f3n
-undo.connectphoto=conectar foto
-undo.disconnectphoto=desconectar foto
-undo.correlate=correlacionar fotos
+undo.connect=Conectar
+undo.disconnect=Desconectar
+undo.correlatephotos=correlacionar fotos
undo.rearrangephotos=reordenar fotos
undo.createpoint=crear punto
undo.rotatephoto=girar foto
undo.convertnamestotimes=convertir nombres a tiempo
undo.lookupsrtm=obtener altitudes de SRTM
undo.deletefieldvalues=Eliminar valores de campo
+undo.correlateaudios=Correlacionar audios
# Error messages
error.save.dialogtitle=Fallo al guardar datos
error.jpegload.dialogtitle=Error cargando fotos
error.jpegload.nofilesfound=No se encuentra ning\u00fan archivo
error.jpegload.nojpegsfound=No se encuentra ning\u00fan archivo jpeg
-error.jpegload.noexiffound=No se encuentra informaci\u00f3n EXIF
error.jpegload.nogpsfound=No se encuentra informaci\u00f3n GPS
error.jpegload.exifreadfailed=Fallo al leer la informaci\u00f3n EXIF. No se puede leer ninguna informaci\u00f3n EXIF\ncon las librer\u00edas internas ni externas.
+error.audioload.nofilesfound=No se encontraron archivos de audio
error.gpsload.unknown=Error desconocido
error.undofailed.title=Fallo al deshacer
error.undofailed.text=No ha sido posible deshacer la operaci\u00f3n
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
menu.point.deletepoint=\u067e\u0627\u06a9 \u06a9\u0631\u062f\u0646 \u0646\u0642\u0637\u0647
menu.photo=\u0639\u06a9\u0633
menu.photo.saveexif=\u0630\u062e\u064a\u0631\u0647 \u062f\u0631 \u0641\u0627\u064a\u0644 \u0636\u0645\u064a\u0645\u0647 \u0639\u06a9\u0633
-menu.photo.connect=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647
-menu.photo.disconnect=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647
-menu.photo.delete=\u0628\u0631\u062f\u0627\u0634\u062a\u0646 \u0639\u06a9\u0633
+function.connecttopoint=\u0627\u062a\u0635\u0627\u0644 \u0628\u0647 \u0646\u0642\u0637\u0647
+function.disconnectfrompoint=\u0642\u0637\u0639 \u0627\u062a\u0635\u0627\u0644 \u0627\u0632 \u0646\u0642\u0637\u0647
+function.removephoto=\u0628\u0631\u062f\u0627\u0634\u062a\u0646 \u0639\u06a9\u0633
menu.view=\u062f\u064a\u062f
menu.view.browser=\u0646\u0642\u0634\u0647 \u062f\u0631\u062c\u0633\u062a\u062c\u0648\u06af\u0631
menu.view.browser.google=Google Maps
menu.point.deletepoint=Supprimer le point
menu.photo=Photo
menu.photo.saveexif=Enregistrer dans les Exif
-menu.photo.connect=Relier au point
-menu.photo.disconnect=D\u00e9tacher du point
-menu.photo.delete=Retirer la photo
+function.connecttopoint=Relier au point
+function.disconnectfrompoint=D\u00e9tacher du point
+function.removephoto=Retirer la photo
menu.view=Affichage
menu.view.browser=Ouvrir la carte dans le navigateur
menu.view.browser.google=Google maps
function.rearrangephotos=R\u00e9arranger les photos
function.rotatephotoleft=Tourner la photo vers la gauche
function.rotatephotoright=Tourner la photo vers la droite
+function.photopopup=Montrer la photo
function.ignoreexifthumb=Ignorer l\u2019aper\u00e7u Exif
function.help=Aide
function.showkeys=Montrer les raccourcis clavier
dialog.correlate.notimestamps=Les points n'ont pas d'indication de temps, il n'est pas possible de les corr\u00e9ler.
dialog.correlate.nouncorrelatedphotos=Il n'y a pas de photos non-corr\u00e9l\u00e9es.\nVoulez-vous continuer ?
dialog.correlate.photoselect.intro=S\u00e9lectionner une de ces photos corr\u00e9l\u00e9es pour d\u00e9finir le d\u00e9calage de temps
-dialog.correlate.photoselect.photoname=Nom de la photo
-dialog.correlate.photoselect.timediff=Diff\u00e9rence de temps
-dialog.correlate.photoselect.photolater=Photo prise plus tard
+dialog.correlate.select.photoname=Nom de la photo
+dialog.correlate.select.timediff=Diff\u00e9rence de temps
+dialog.correlate.select.photolater=Photo prise plus tard
dialog.correlate.options.tip=Astuce : En corr\u00e9lant manuellement au moins une photo, le d\u00e9calage de temps peut \u00eatre calcul\u00e9 pour vous.
dialog.correlate.options.intro=S\u00e9lectionner les options pour la corr\u00e9lation automatique
dialog.correlate.options.offsetpanel=D\u00e9calage de temps
dialog.correlate.options.offset.minutes=minutes et
dialog.correlate.options.offset.seconds=secondes
dialog.correlate.options.photolater=Photo post\u00e9rieure au point
-dialog.correlate.options.pointlater=Point post\u00e9rieur \u00e0 la photo
+dialog.correlate.options.pointlaterphoto=Point post\u00e9rieur \u00e0 la photo
dialog.correlate.options.limitspanel=Limites de corr\u00e9lation
dialog.correlate.options.notimelimit=Pas de limite de temps
dialog.correlate.options.timelimit=Limite de temps
# 3d window
dialog.3d.title=Vue 3D de Prune
-dialog.3d.altitudecap=Etendue d'altitude minimale
dialog.3dlines.title=Grille de Prune
dialog.3dlines.empty=Pas de grille \u00e0 afficher !
dialog.3dlines.intro=Ceci est la grille pour la vue 3D
confirm.undo.multi=op\u00e9rations annul\u00e9es
confirm.jpegload.single=la photo a \u00e9t\u00e9 ajout\u00e9e
confirm.jpegload.multi=les photos ont \u00e9t\u00e9 ajout\u00e9es
-confirm.photo.connect=photo reli\u00e9e
+confirm.media.connect=m\u00e9dia reli\u00e9e
confirm.photo.disconnect=photo d\u00e9tach\u00e9e
-confirm.correlate.single=photo a \u00e9t\u00e9 corr\u00e9l\u00e9e
-confirm.correlate.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es
+confirm.correlatephotos.single=photo a \u00e9t\u00e9 corr\u00e9l\u00e9e
+confirm.correlatephotos.multi=photos ont \u00e9t\u00e9 corr\u00e9l\u00e9es
confirm.createpoint=Point cr\u00e9\u00e9
confirm.rotatephoto=Photo tourn\u00e9e
confirm.running=En cours...
confirm.lookupsrtm1=Trouv\u00e9
confirm.lookupsrtm2=valeurs d'altitude
-# Buttons || These are all the texts for buttons
+# Buttons
button.ok=OK
button.back=Retour
button.next=Prochain
details.range.numsegments=Nombre de segments
details.range.pace=Allure
details.range.gradient=Pente
-details.waypointsphotos.waypoints=Waypoints
-details.waypointsphotos.photos=Photos
+details.lists.waypoints=Waypoints
+details.lists.photos=Photos
details.photodetails=D\u00e9tails de la photo
details.nophoto=Pas de photo
details.photo.loading=Chargement
-details.photo.connected=Reli\u00e9e
+details.media.connected=Reli\u00e9e
map.overzoom=Aucune carte disponible \u00e0 ce niveau de zoom
# Field names
# External urls
url.googlemaps=maps.google.fr
+wikipedia.lang=fr
# Cardinals for 3d plots
cardinal.n=N
undo.loadphotos=charger les photos
undo.editpoint=\u00e9diter le point
undo.deletepoint=effacer le point
-undo.deletephoto=retirer la photo
+undo.removephoto=retirer la photo
undo.deleterange=effacer l'\u00e9tendue
undo.compress=compresser la trace
undo.insert=ins\u00e9rer les points
undo.addaltitudeoffset=ajouter d\u00e9calage d'altitude
undo.rearrangewaypoints=r\u00e9arranger les waypoints
undo.cutandmove=d\u00e9placer la s\u00e9lection
-undo.connectphoto=relier la photo
-undo.disconnectphoto=d\u00e9tacher la photo
-undo.correlate=corr\u00e9ler les photos
+undo.connect=relier
+undo.disconnect=d\u00e9tacher
+undo.correlatephotos=corr\u00e9ler les photos
undo.rearrangephotos=R\u00e9arranger les photos
undo.createpoint=ajouter un point
undo.rotatephoto=Tourner la photo
error.jpegload.dialogtitle=Erreur au chargement des photos
error.jpegload.nofilesfound=Aucun fichier trouv\u00e9
error.jpegload.nojpegsfound=Aucun fichier jpeg trouv\u00e9
-error.jpegload.noexiffound=Aucune information EXIF trouv\u00e9e
error.jpegload.nogpsfound=Aucune information GPS trouv\u00e9e
error.jpegload.exifreadfailed=Information EXIF illisible. Aucune information EXIF ne peut \u00eatre lue\nsans une librairie interne ou externe.
error.gpsload.unknown=Erreur inconnue
--- /dev/null
+# Text entries for the Prune application
+# Hungarian entries thanks to Gy\u00f6rgy Ball\u00f3
+
+# Menu entries
+menu.file=F\u00e1jl
+menu.file.addphotos=F\u00e9nyk\u00e9pek hozz\u00e1ad\u00e1sa
+menu.file.save=Ment\u00e9s sz\u00f6vegk\u00e9nt
+menu.file.exit=Kil\u00e9p\u00e9s
+menu.track=Nyomvonal
+menu.track.undo=Visszavon\u00e1s
+menu.track.clearundo=Visszavon\u00e1si lista t\u00f6rl\u00e9se
+menu.track.deletemarked=Jel\u00f6lt pontok t\u00f6rl\u00e9se
+menu.track.rearrange=\u00datpontok \u00fajrarendez\u00e9se
+menu.track.rearrange.start=\u00d6sszes a f\u00e1jl elej\u00e9re
+menu.track.rearrange.end=\u00d6sszes a f\u00e1jl v\u00e9g\u00e9re
+menu.track.rearrange.nearest=Egyenk\u00e9nt a legk\u00f6zelebbi nyomponthoz
+menu.range=Tartom\u00e1ny
+menu.range.all=Mindet kijel\u00f6l
+menu.range.none=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
+menu.range.start=Tartom\u00e1ny kezdet\u00e9nek be\u00e1ll\u00edt\u00e1sa
+menu.range.end=Tartom\u00e1ny v\u00e9g\u00e9nek be\u00e1ll\u00edt\u00e1sa
+menu.range.deleterange=Tartom\u00e1ny t\u00f6rl\u00e9se
+menu.range.interpolate=Interpol\u00e1ci\u00f3
+menu.range.average=Kijel\u00f6l\u00e9s \u00e1tlaga
+menu.range.reverse=Tartom\u00e1ny megford\u00edt\u00e1sa
+menu.range.mergetracksegments=Nyomvonalszakaszok egyes\u00edt\u00e9se
+menu.range.cutandmove=Kijel\u00f6l\u00e9s kiv\u00e1g\u00e1sa \u00e9s mozgat\u00e1sa
+menu.point=Pont
+menu.point.editpoint=Pont szerkeszt\u00e9se
+menu.point.deletepoint=Pont t\u00f6rl\u00e9se
+menu.photo=F\u00e9nyk\u00e9p
+menu.photo.saveexif=Ment\u00e9s Exifbe
+menu.audio=Hang
+menu.view=N\u00e9zet
+menu.view.showsidebars=Oldals\u00e1vok megjelen\u00edt\u00e9se
+menu.view.browser=T\u00e9rk\u00e9p b\u00f6ng\u00e9sz\u0151ablakban
+menu.view.browser.google=Google T\u00e9rk\u00e9p
+menu.view.browser.openstreetmap=OpenStreetMap
+menu.view.browser.mapquest=Mapquest
+menu.view.browser.yahoo=Yahoo! Maps
+menu.view.browser.bing=Bing Maps
+menu.settings=Be\u00e1ll\u00edt\u00e1sok
+menu.settings.onlinemode=T\u00e9rk\u00e9pek bet\u00f6lt\u00e9se az internetr\u0151l
+menu.help=S\u00fag\u00f3
+# Popup menu for map
+menu.map.zoomin=Nagy\u00edt\u00e1s
+menu.map.zoomout=Kicsiny\u00edt\u00e9s
+menu.map.zoomfull=Nagy\u00edt\u00e1s a teljes m\u00e9retre
+menu.map.newpoint=\u00daj pont l\u00e9trehoz\u00e1sa
+menu.map.drawpoints=Pontsorozat l\u00e9trehoz\u00e1sa
+menu.map.connect=Nyompontok \u00f6sszek\u00f6t\u00e9se
+menu.map.autopan=Automatikus mozgat\u00e1s
+menu.map.showmap=T\u00e9rk\u00e9p megjelen\u00edt\u00e9se
+menu.map.showscalebar=M\u00e9retar\u00e1ny megjelen\u00edt\u00e9se
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=V
+altkey.menu.range=T
+altkey.menu.point=P
+altkey.menu.view=N
+altkey.menu.photo=K
+altkey.menu.audio=H
+altkey.menu.settings=B
+altkey.menu.help=S
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=L
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.range.all=A
+shortcut.menu.help.help=H
+
+# Functions
+function.open=F\u00e1jl megnyit\u00e1sa
+function.loadfromgps=Adatok let\u00f6lt\u00e9se GPS-r\u0151l
+function.sendtogps=Adatok felt\u00f6lt\u00e9se GPS-re
+function.exportkml=Export\u00e1l\u00e1s KML-be
+function.exportgpx=Export\u00e1l\u00e1s GPX-be
+function.exportpov=Export\u00e1l\u00e1s POV-ba
+function.exportsvg=Export\u00e1l\u00e1s SVG-be
+function.editwaypointname=\u00datpont nev\u00e9nek szerkeszt\u00e9se
+function.compress=Nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
+function.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa
+function.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa
+function.convertnamestotimes=\u00datpontok neveinek konvert\u00e1l\u00e1sa id\u0151pontokk\u00e1
+function.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9keinek t\u00f6rl\u00e9se
+function.findwaypoint=\u00datpont keres\u00e9se
+function.pastecoordinates=\u00daj koordin\u00e1t\u00e1k megad\u00e1sa
+function.charts=Diagramok
+function.show3d=3D n\u00e9zet
+function.distances=T\u00e1vols\u00e1gok
+function.fullrangedetails=Teljes tartom\u00e1ny r\u00e9szletei
+function.setmapbg=H\u00e1tt\u00e9rk\u00e9p be\u00e1ll\u00edt\u00e1sa
+function.setkmzimagesize=KMZ k\u00e9pm\u00e9ret be\u00e1ll\u00edt\u00e1sa
+function.setpaths=Program\u00fatvonalak be\u00e1ll\u00edt\u00e1sa
+function.getgpsies=Gpsies nyomvonalak let\u00f6lt\u00e9se
+function.uploadgpsies=Nyomvonal felt\u00f6lt\u00e9se Gpsiesra
+function.lookupsrtm=Magass\u00e1gok let\u00f6lt\u00e9se SRTM-r\u0151l
+function.getwikipedia=K\u00f6zeli Wikip\u00e9dia sz\u00f3cikkek let\u00f6lt\u00e9se
+function.searchwikipedianames=Keres\u00e9s a Wikip\u00e9di\u00e1ban n\u00e9v szerint
+function.downloadosm=OSM adatok let\u00f6lt\u00e9se a ter\u00fcletr\u0151l
+function.duplicatepoint=Pont kett\u0151z\u00e9se
+function.setcolours=Sz\u00ednek be\u00e1ll\u00edt\u00e1sa
+function.setlinewidth=Vonalsz\u00e9less\u00e9g be\u00e1ll\u00edt\u00e1sa
+function.setlanguage=Nyelv be\u00e1ll\u00edt\u00e1sa
+function.connecttopoint=Kapcsol\u00e1s ponthoz
+function.disconnectfrompoint=Lev\u00e1laszt\u00e1s pontr\u00f3l
+function.removephoto=F\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa
+function.correlatephotos=F\u00e9nyk\u00e9pek megfeleltet\u00e9se
+function.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendez\u00e9se
+function.rotatephotoleft=F\u00e9nyk\u00e9p forgat\u00e1sa balra
+function.rotatephotoright=F\u00e9nyk\u00e9p forgat\u00e1sa jobbra
+function.photopopup=F\u00e9nyk\u00e9p felugr\u00f3 ablak megjelen\u00edt\u00e9se
+function.ignoreexifthumb=Exif miniat\u0171r figyelmen k\u00edv\u00fcl hagy\u00e1sa
+function.loadaudio=Hangf\u00e1jlok hozz\u00e1ad\u00e1sa
+function.removeaudio=Hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa
+function.correlateaudios=Hangok megfeleltet\u00e9se
+function.playaudio=Hangf\u00e1jl lej\u00e1tsz\u00e1sa
+function.stopaudio=Hangf\u00e1jl meg\u00e1ll\u00edt\u00e1sa
+function.help=S\u00fag\u00f3
+function.showkeys=Gyorsbillenty\u0171k megjelen\u00edt\u00e9se
+function.about=A Prune n\u00e9vjegye
+function.checkversion=\u00daj verzi\u00f3 keres\u00e9se
+function.saveconfig=Be\u00e1ll\u00edt\u00e1sok ment\u00e9se
+function.diskcache=T\u00e9rk\u00e9pek ment\u00e9se lemezre
+
+# Dialogs
+dialog.exit.confirm.title=Kil\u00e9p\u00e9s a Prune-b\u00f3l
+dialog.exit.confirm.text=Az adatok nincsenek elmentve. Biztos benne, hogy kil\u00e9p?
+dialog.openappend.title=Hozz\u00e1f\u0171z\u00e9s a megl\u00e9v\u0151 adatokhoz
+dialog.openappend.text=Hozz\u00e1f\u0171zi ezeket az adatokat a m\u00e1r bet\u00f6lt\u00f6tt adatokhoz?
+dialog.deletepoint.title=Pont t\u00f6rl\u00e9se
+dialog.deletepoint.deletephoto=T\u00f6rli a f\u00e9nyk\u00e9pet, amely ehhez a ponthoz tartozik?
+dialog.deletephoto.title=F\u00e9nyk\u00e9p t\u00f6rl\u00e9se
+dialog.deletephoto.deletepoint=T\u00f6rli a pontot, amely ehhez a f\u00e9nyk\u00e9phez tartozik?
+dialog.openoptions.title=Be\u00e1ll\u00edt\u00e1sok megnyit\u00e1sa
+dialog.openoptions.filesnippet=F\u00e1jl kivonata
+dialog.load.table.field=Mez\u0151
+dialog.load.table.datatype=Adatt\u00edpus
+dialog.load.table.description=Le\u00edr\u00e1s
+dialog.delimiter.label=Mez\u0151elv\u00e1laszt\u00f3
+dialog.delimiter.comma=Vessz\u0151 ,
+dialog.delimiter.tab=Tabul\u00e1tor
+dialog.delimiter.space=Sz\u00f3k\u00f6z
+dialog.delimiter.semicolon=Pontosvessz\u0151 ;
+dialog.delimiter.other=Egy\u00e9b
+dialog.openoptions.deliminfo.records=rekord
+dialog.openoptions.deliminfo.fields=mez\u0151vel
+dialog.openoptions.deliminfo.norecords=Nincsenek rekordok
+dialog.openoptions.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.open.contentsdoubled=Ez a f\u00e1jl minden egyes pont k\u00e9t p\u00e9ld\u00e1ny\u00e1t tartalmazza,\negyszer mint \u00fatpont, m\u00e1sodszor mint nyompont.
+dialog.selecttracks.intro=Nyomvonal vagy nyomvonalak kiv\u00e1laszt\u00e1sa bet\u00f6lt\u00e9shez
+dialog.selecttracks.noname=N\u00e9vtelen
+dialog.jpegload.subdirectories=Alk\u00f6nyvt\u00e1rakban is
+dialog.jpegload.loadjpegswithoutcoords=Koordin\u00e1t\u00e1k n\u00e9lk\u00fcli k\u00e9peket is
+dialog.jpegload.loadjpegsoutsidearea=A jelenlegi ter\u00fcleten k\u00edv\u00fcli k\u00e9peket is
+dialog.jpegload.progress.title=F\u00e9nyk\u00e9pek bet\u00f6lt\u00e9se
+dialog.jpegload.progress=K\u00e9rem, v\u00e1rjon, am\u00edg a f\u00e9nyk\u00e9pek keres\u00e9se tart
+dialog.gpsload.nogpsbabel=A gpsbabel program nem tal\u00e1lhat\u00f3. Folytatja?
+dialog.gpsload.device=Eszk\u00f6z neve
+dialog.gpsload.format=Form\u00e1tum
+dialog.gpsload.getwaypoints=\u00datpontok bet\u00f6lt\u00e9se
+dialog.gpsload.gettracks=Nyomvonalak bet\u00f6lt\u00e9se
+dialog.gpsload.save=Ment\u00e9s f\u00e1jlba
+dialog.gpssend.sendwaypoints=\u00datpontok k\u00fcld\u00e9se
+dialog.gpssend.sendtracks=Nyomvonalak k\u00fcld\u00e9se
+dialog.gpssend.trackname=Nyomvonal neve
+dialog.saveoptions.title=F\u00e1jl ment\u00e9se
+dialog.save.fieldstosave=Mentend\u0151 mez\u0151k
+dialog.save.table.field=Mez\u0151
+dialog.save.table.hasdata=Tartalmaz adatot
+dialog.save.table.save=Ment\u00e9s
+dialog.save.headerrow=Fejl\u00e9csor a kimenetbe
+dialog.save.coordinateunits=Koordin\u00e1ta egys\u00e9ge
+dialog.save.altitudeunits=Magass\u00e1g egys\u00e9ge
+dialog.save.timestampformat=Id\u0151b\u00e9lyeg form\u00e1tuma
+dialog.save.overwrite.title=A f\u00e1jl m\u00e1r l\u00e9tezik
+dialog.save.overwrite.text=Ez a f\u00e1jl m\u00e1r l\u00e9tezik. Biztos benne, hogy fel\u00fcl\u00edrja a f\u00e1jlt?
+dialog.save.notypesselected=Nincs pontt\u00edpus kiv\u00e1lasztva
+dialog.exportkml.text=C\u00edm az adatokhoz
+dialog.exportkml.altitude=Abszol\u00fat magass\u00e1gok (rep\u00fcl\u00e9shez)
+dialog.exportkml.kmz=T\u00f6m\u00f6r\u00edt\u00e9s kmz f\u00e1jl k\u00e9sz\u00edt\u00e9s\u00e9hez
+dialog.exportkml.exportimages=K\u00e9pminiat\u0171r\u00f6k export\u00e1l\u00e1sa kmz-be
+dialog.exportkml.trackcolour=Nyomvonal sz\u00edne
+dialog.exportgpx.name=N\u00e9v
+dialog.exportgpx.desc=Le\u00edr\u00e1s
+dialog.exportgpx.includetimestamps=Id\u0151b\u00e9lyegek is
+dialog.exportgpx.copysource=Forr\u00e1s xml m\u00e1sol\u00e1sa
+dialog.exportpov.text=Adja meg a param\u00e9tereket a POV exporthoz
+dialog.exportpov.font=Bet\u0171t\u00edpus
+dialog.exportpov.camerax=X kamera
+dialog.exportpov.cameray=Y kamera
+dialog.exportpov.cameraz=Z kamera
+dialog.exportpov.modelstyle=Modell st\u00edlusa
+dialog.exportpov.ballsandsticks=Goly\u00f3k \u00e9s botok
+dialog.exportpov.tubesandwalls=Cs\u00f6vek \u00e9s falak
+dialog.exportpov.warningtracksize=Ez a nyomvonal nagy sz\u00e1m\u00fa pontot tartalmaz, amelyet a Java3D nem biztos, hogy meg tud jelen\u00edteni.\nBiztos benne, hogy folytatni szeretn\u00e9?
+dialog.exportsvg.text=Param\u00e9terek kiv\u00e1laszt\u00e1sa az SVG exporthoz
+dialog.exportsvg.phi=Ir\u00e1nysz\u00f6g \u03d5
+dialog.exportsvg.theta=Emel\u00e9s sz\u00f6ge \u03b8
+dialog.exportsvg.gradients=\u00c1tmenetek haszn\u00e1lata az \u00e1rny\u00e9kol\u00e1shoz
+dialog.pointtype.desc=A k\u00f6vetkez\u0151 pontt\u00edpusok ment\u00e9se:
+dialog.pointtype.track=Nyompontok
+dialog.pointtype.waypoint=\u00datpontok
+dialog.pointtype.photo=F\u00e9nyk\u00e9ppontok
+dialog.pointtype.audio=Hangpontok
+dialog.pointtype.selection=Csak a kijel\u00f6lt
+dialog.confirmreversetrack.title=Megford\u00edt\u00e1s meger\u0151s\u00edt\u00e9se
+dialog.confirmreversetrack.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje megford\u00edt\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy megford\u00edtja a kijel\u00f6l\u00e9st?
+dialog.confirmcutandmove.title=Kiv\u00e1g\u00e1s \u00e9s mozgat\u00e1s meger\u0151s\u00edt\u00e9se
+dialog.confirmcutandmove.text=Ez a nyomvonal id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t tartalmaz, amely sorrendje mozgat\u00e1s ut\u00e1n megv\u00e1ltozik.\n Biztos benne, hogy mozgatja a kijel\u00f6l\u00e9st?
+dialog.interpolate.title=Pontok interpol\u00e1l\u00e1sa
+dialog.interpolate.parameter.text=Pontok sz\u00e1ma, amely a k\u00e9t kiv\u00e1lasztott pont k\u00f6z\u00e9 besz\u00farand\u00f3
+dialog.undo.title=M\u0171velet(ek) visszavon\u00e1sa
+dialog.undo.pretext=V\u00e1lassza ki a visszavonand\u00f3 m\u0171velet(ek)et
+dialog.undo.none.title=Nem vonhat\u00f3 vissza
+dialog.undo.none.text=Nincs visszavonhat\u00f3 m\u0171velet!
+dialog.clearundo.title=Visszavon\u00e1si lista t\u00f6rl\u00e9se
+dialog.clearundo.text=Biztos benne, hogy t\u00f6r\u00f6lni szeretn\u00e9 a visszavon\u00e1si list\u00e1t?\nMinden visszavon\u00e1si inform\u00e1ci\u00f3 el fog veszni!
+dialog.pointedit.title=Pont szerkeszt\u00e9se
+dialog.pointedit.text=V\u00e1lassza ki egyenk\u00e9nt a mez\u0151ket, amelyeket szerkeszteni szeretne, majd az \u00e9rt\u00e9k m\u00f3dos\u00edt\u00e1s\u00e1hoz haszn\u00e1lja a "Szerkeszt\u00e9s" gombot
+dialog.pointedit.table.field=Mez\u0151
+dialog.pointedit.table.value=\u00c9rt\u00e9k
+dialog.pointedit.table.changed=M\u00f3dosult
+dialog.pointedit.changevalue.text=Adjon meg egy \u00faj \u00e9rt\u00e9ket a mez\u0151h\u00f6z
+dialog.pointedit.changevalue.title=Mez\u0151 szerkeszt\u00e9se
+dialog.pointnameedit.name=\u00datpont neve
+dialog.pointnameedit.uppercase=NAGYBET\u0170S
+dialog.pointnameedit.lowercase=kisbet\u0171s
+dialog.pointnameedit.sentencecase=Nagy Kezd\u0151bet\u0171s
+dialog.addtimeoffset.add=Id\u0151 hozz\u00e1ad\u00e1sa
+dialog.addtimeoffset.subtract=Id\u0151 kivon\u00e1sa
+dialog.addtimeoffset.days=Nap
+dialog.addtimeoffset.hours=\u00d3ra
+dialog.addtimeoffset.minutes=Perc
+dialog.addtimeoffset.notimestamps=Nem adhat\u00f3 hozz\u00e1 id\u0151eltol\u00e1s, mivel a kijel\u00f6l\u00e9s nem tartalmaz id\u0151b\u00e9lyeg-inform\u00e1ci\u00f3t
+dialog.findwaypoint.intro=Adja meg az \u00fatpont nev\u00e9nek egy r\u00e9sz\u00e9t
+dialog.findwaypoint.search=Keres\u00e9s
+dialog.saveexif.title=Exif ment\u00e9se
+dialog.saveexif.intro=V\u00e1lassza ki a mentend\u0151 f\u00e9nyk\u00e9peket a jel\u00f6l\u0151n\u00e9gyzetek haszn\u00e1lat\u00e1val
+dialog.saveexif.nothingtosave=A koordin\u00e1taadatok nem m\u00f3dosultak, nincs mit menteni
+dialog.saveexif.noexiftool=Az exiftool program nem tal\u00e1lhat\u00f3. Folytatja?
+dialog.saveexif.table.photoname=F\u00e9nyk\u00e9p neve
+dialog.saveexif.table.status=\u00c1llapot
+dialog.saveexif.table.save=Ment\u00e9s
+dialog.saveexif.photostatus.connected=\u00d6sszekapcsolva
+dialog.saveexif.photostatus.disconnected=Lev\u00e1lasztva
+dialog.saveexif.photostatus.modified=M\u00f3dos\u00edtva
+dialog.saveexif.overwrite=F\u00e1jlok fel\u00fcl\u00edr\u00e1sa
+dialog.saveexif.force=K\u00e9nyszer\u00edt\u00e9s kisebb hib\u00e1k ellen\u00e9re
+dialog.charts.xaxis=X tengely
+dialog.charts.yaxis=Y tengely
+dialog.charts.output=Kimenet
+dialog.charts.screen=Kimenet k\u00e9perny\u0151re
+dialog.charts.svg=Kimenet SVG f\u00e1jlba
+dialog.charts.svgwidth=SVG sz\u00e9less\u00e9ge
+dialog.charts.svgheight=SVG magass\u00e1ga
+dialog.charts.needaltitudeortimes=Diagramok k\u00e9sz\u00edt\u00e9s\u00e9hez a nyomvonalnak tartalmaznia kell magass\u00e1gi vagy id\u0151inform\u00e1ci\u00f3kat
+dialog.charts.gnuplotnotfound=A gnuplot a megadott \u00fatvonalon nem tal\u00e1lhat\u00f3
+dialog.distances.intro=T\u00e1vols\u00e1gok l\u00e9gvonalban a pontok k\u00f6z\u00f6tt
+dialog.distances.column.from=Indul\u00f3 pont
+dialog.distances.column.to=V\u00e9gpont
+dialog.distances.currentpoint=Jelenlegi pont
+dialog.distances.toofewpoints=Ehhez a funkci\u00f3hoz \u00fatpontok kellenek, amelyek k\u00f6z\u00f6tt a t\u00e1vols\u00e1g sz\u00e1m\u00edt\u00e1sra ker\u00fcl
+dialog.fullrangedetails.intro=Itt vannak a r\u00e9szletei a kiv\u00e1lasztott tartom\u00e1nynak
+dialog.setmapbg.intro=V\u00e1lassza ki az egyik t\u00e9rk\u00e9pforr\u00e1st, vagy adjon hozz\u00e1 egy \u00fajat
+dialog.addmapsource.title=\u00daj t\u00e9rk\u00e9pforr\u00e1s hozz\u00e1ad\u00e1sa
+dialog.addmapsource.sourcename=Forr\u00e1s neve
+dialog.addmapsource.layer1url=Els\u0151 r\u00e9teg URL-je
+dialog.addmapsource.layer2url=Opcion\u00e1lis m\u00e1sodik r\u00e9teg URL-je
+dialog.addmapsource.maxzoom=Maxim\u00e1lis nagy\u00edt\u00e1si szint
+dialog.addmapsource.cloudstyle=St\u00edlus sz\u00e1ma
+dialog.addmapsource.noname=N\u00e9vtelen
+dialog.gpsies.column.name=Nyomvonal neve
+dialog.gpsies.column.length=Hossz
+dialog.gpsies.description=Le\u00edr\u00e1s
+dialog.gpsies.nodescription=Nincs le\u00edr\u00e1s
+dialog.gpsies.nonefound=Nem tal\u00e1lhat\u00f3 nyomvonal
+dialog.gpsies.username=Gpsies felhaszn\u00e1l\u00f3n\u00e9v
+dialog.gpsies.password=Gpsies jelsz\u00f3
+dialog.gpsies.keepprivate=A nyomvonal maradjon priv\u00e1t
+dialog.gpsies.confirmopenpage=Megnyitja a weboldalt a felt\u00f6lt\u00f6tt nyomvonal sz\u00e1m\u00e1ra?
+dialog.gpsies.activities=Tev\u00e9kenys\u00e9gt\u00edpusok
+dialog.gpsies.activity.trekking=T\u00far\u00e1z\u00e1s
+dialog.gpsies.activity.walking=S\u00e9ta
+dialog.gpsies.activity.jogging=Fut\u00e1s
+dialog.gpsies.activity.biking=Ker\u00e9kp\u00e1roz\u00e1s
+dialog.gpsies.activity.motorbiking=Motorker\u00e9kp\u00e1roz\u00e1s
+dialog.gpsies.activity.snowshoe=H\u00f3talpas s\u00e9ta
+dialog.gpsies.activity.sailing=Vitorl\u00e1z\u00e1s
+dialog.gpsies.activity.skating=Korcsoly\u00e1z\u00e1s
+dialog.wikipedia.column.name=Sz\u00f3cikk neve
+dialog.wikipedia.column.distance=T\u00e1vols\u00e1g
+dialog.correlate.notimestamps=Nincsenek id\u0151b\u00e9lyegek az adatpontokon, \u00edgy nem feleltethet\u0151 meg semmi a f\u00e9nyk\u00e9pekkel.
+dialog.correlate.nouncorrelatedphotos=Nincsenek megfeleltetlen f\u00e9nyk\u00e9pek.\nBiztos benne, hogy folytatja?
+dialog.correlate.photoselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a megfeleltetett f\u00e9nyk\u00e9pek k\u00f6z\u00fcl az id\u0151eltol\u00e1s haszn\u00e1lat\u00e1hoz
+dialog.correlate.select.photoname=F\u00e9nyk\u00e9p neve
+dialog.correlate.select.timediff=Id\u0151k\u00fcl\u00f6nbs\u00e9g
+dialog.correlate.select.photolater=K\u00e9s\u0151bbi f\u00e9nyk\u00e9p
+dialog.correlate.options.tip=Tipp: legal\u00e1bb egy elem k\u00e9zzel t\u00f6rt\u00e9n\u0151 \u00f6sszekapcsol\u00e1s\u00e1val az id\u0151eltol\u00e1s kisz\u00e1m\u00edthat\u00f3.
+dialog.correlate.options.intro=V\u00e1lassza ki az opci\u00f3kat az automatikus megfeleltet\u00e9shez
+dialog.correlate.options.offsetpanel=Id\u0151eltol\u00e1s
+dialog.correlate.options.offset=Eltol\u00e1s
+dialog.correlate.options.offset.hours=\u00f3ra,
+dialog.correlate.options.offset.minutes=perc \u00e9s
+dialog.correlate.options.offset.seconds=m\u00e1sodperc
+dialog.correlate.options.photolater=A f\u00e9nyk\u00e9p k\u00e9s\u0151bbi, mint a pont
+dialog.correlate.options.pointlaterphoto=A pont k\u00e9s\u0151bbi, mint a f\u00e9nyk\u00e9p
+dialog.correlate.options.audiolater=A hang k\u00e9s\u0151bbi, mint a pont
+dialog.correlate.options.pointlateraudio=A pont k\u00e9s\u0151bbi, mint a hang
+dialog.correlate.options.limitspanel=Megfeleltet\u00e9s korl\u00e1tai
+dialog.correlate.options.notimelimit=Nincs id\u0151korl\u00e1t
+dialog.correlate.options.timelimit=Id\u0151korl\u00e1t
+dialog.correlate.options.nodistancelimit=Nincs t\u00e1vols\u00e1gkorl\u00e1t
+dialog.correlate.options.distancelimit=T\u00e1vols\u00e1gkorl\u00e1t
+dialog.correlate.options.correlate=Megfeleltet\u00e9s
+dialog.correlate.alloutsiderange=Az \u00f6sszes f\u00e9nyk\u00e9p a nyomvonal id\u0151tartom\u00e1ny\u00e1n k\u00edv\u00fcl esik, \u00edgy egyik sem feleltethet\u0151 meg.\nPr\u00f3b\u00e1lja m\u00f3dos\u00edtani az eltol\u00e1st, vagy k\u00e9zzel megfeleltetni legal\u00e1bb egy f\u00e9nyk\u00e9pet.
+dialog.correlate.filetimes=F\u00e1jl-id\u0151b\u00e9lyegek jelzik:
+dialog.correlate.filetimes2=a hangf\u00e1jlnak
+dialog.correlate.correltimes=A megfeleltet\u00e9shez haszn\u00e1lja:
+dialog.correlate.timestamp.beginning=Elej\u00e9t
+dialog.correlate.timestamp.middle=K\u00f6zep\u00e9t
+dialog.correlate.timestamp.end=V\u00e9g\u00e9t
+dialog.correlate.audioselect.intro=V\u00e1lasszon egyet ezek k\u00f6z\u00fcl a megfeleltetett f\u00e9nyk\u00e9pek k\u00f6z\u00fcl az id\u0151eltol\u00e1s haszn\u00e1lat\u00e1hoz
+dialog.correlate.select.audioname=Hang neve
+dialog.correlate.select.audiolater=Hang k\u00e9s\u0151bb
+dialog.rearrangephotos.desc=V\u00e1lassza ki a f\u00e9nyk\u00e9ppontok c\u00e9lj\u00e1t \u00e9s rendez\u00e9si sorrendj\u00e9t
+dialog.rearrangephotos.tostart=Mozgat\u00e1s a kezdet\u00e9hez
+dialog.rearrangephotos.toend=Mozgat\u00e1s a v\u00e9g\u00e9hez
+dialog.rearrangephotos.nosort=Ne rendezze
+dialog.rearrangephotos.sortbyfilename=Rendez\u00e9s f\u00e1jln\u00e9v szerint
+dialog.rearrangephotos.sortbytime=Rendez\u00e9s id\u0151 szerint
+dialog.compress.nonefound=Nem t\u00e1vol\u00edthat\u00f3 el adatpont
+dialog.compress.closepoints.title=K\u00f6zeli pontok elt\u00e1vol\u00edt\u00e1sa
+dialog.compress.closepoints.paramdesc=Hat\u00f3t\u00e1vols\u00e1g
+dialog.compress.wackypoints.title=Kisz\u00e1m\u00edthatatlan pontok elt\u00e1vol\u00edt\u00e1sa
+dialog.compress.wackypoints.paramdesc=T\u00e1vols\u00e1gt\u00e9nyez\u0151
+dialog.compress.singletons.title=Egyke pontok elt\u00e1vol\u00edt\u00e1sa
+dialog.compress.singletons.paramdesc=T\u00e1vols\u00e1gt\u00e9nyez\u0151
+dialog.compress.duplicates.title=Kett\u0151z\u00f6tt pontok elt\u00e1vol\u00edt\u00e1sa
+dialog.compress.summarylabel=T\u00f6rlend\u0151 pontok
+dialog.pastecoordinates.desc=Adja meg vagy illessze be a koordin\u00e1t\u00e1kat ide
+dialog.pastecoordinates.coords=Koordin\u00e1t\u00e1k
+dialog.pastecoordinates.nothingfound=Ellen\u0151rizze a koordin\u00e1t\u00e1kat, \u00e9s pr\u00f3b\u00e1lja \u00f3jra
+dialog.help.help=Tov\u00e1bbi inform\u00e1ci\u00f3k\u00e9rt \u00e9s kezel\u00e9si \u00fatmutat\u00f3\u00e9rt l\u00e1sd a \n http://activityworkshop.net/software/prune/\nwebhelyet.
+dialog.about.version=Verzi\u00f3
+dialog.about.build=Build
+dialog.about.summarytext1=A Prune egy program GPS vev\u0151kr\u0151l sz\u00e1rmaz\u00f3 adatok bet\u00f6lt\u00e9s\u00e9re, megjelen\u00edt\u00e9s\u00e9re \u00e9s szerkeszt\u00e9s\u00e9re.
+dialog.about.summarytext2=Gnu GPL licenc alatt ker\u00fclt kiad\u00e1sra a szabad, ny\u00edlt, vil\u00e1gm\u00e9ret\u0171 haszn\u00e1lathoz \u00e9s fejleszt\u00e9shez.<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.translatedby=Magyar sz\u00f6veg: Ball\u00f3 Gy\u00f6rgy
+dialog.about.systeminfo=Rendszerinform\u00e1ci\u00f3
+dialog.about.systeminfo.os=Oper\u00e1ci\u00f3s rendszer
+dialog.about.systeminfo.java=Java futtat\u00f3k\u00f6rnyezet
+dialog.about.systeminfo.java3d=Java3d telep\u00edtve
+dialog.about.systeminfo.povray=Povray telep\u00edtve
+dialog.about.systeminfo.exiftool=Exiftool telep\u00edtve
+dialog.about.systeminfo.gpsbabel=Gpsbabel telep\u00edtve
+dialog.about.systeminfo.gnuplot=Gnuplot telep\u00edtve
+dialog.about.systeminfo.exiflib=Exif f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r
+dialog.about.systeminfo.exiflib.internal=Be\u00e9p\u00edtett
+dialog.about.systeminfo.exiflib.internal.failed=Be\u00e9p\u00edtett (nem tal\u00e1lhat\u00f3)
+dialog.about.systeminfo.exiflib.external=K\u00fcls\u0151
+dialog.about.systeminfo.exiflib.external.failed=K\u00fcls\u0151 (nem tal\u00e1lhat\u00f3)
+dialog.about.yes=Igen
+dialog.about.no=Nem
+dialog.about.credits=K\u00e9sz\u00edt\u0151k
+dialog.about.credits.code=Prune k\u00f3dj\u00e1t \u00edrta:
+dialog.about.credits.exifcode=Exif k\u00f3d:
+dialog.about.credits.icons=N\u00e9h\u00e1ny ikon sz\u00e1rmazik:
+dialog.about.credits.translators=Ford\u00edt\u00f3k
+dialog.about.credits.translations=Ford\u00edt\u00e1st seg\u00edtette
+dialog.about.credits.devtools=Fejleszt\u0151eszk\u00f6z\u00f6k
+dialog.about.credits.othertools=Egy\u00e9b eszk\u00f6z\u00f6k
+dialog.about.credits.thanks=K\u00f6sz\u00f6net:
+dialog.about.readme=Olvassel
+dialog.checkversion.error=A verzi\u00f3sz\u00e1m nem ellen\u0151rizhet\u0151.\nEllen\u0151rizze az internetkapcsolatot.
+dialog.checkversion.uptodate=A Prune leg\u00fajabb verzi\u00f3j\u00e1t haszn\u00e1lja.
+dialog.checkversion.newversion1=El\u00e9rhet\u0151 a Prune \u00faj verzi\u00f3ja! A leg\u00fajabb veri\u00f3 most:
+dialog.checkversion.newversion2=.
+dialog.checkversion.releasedate1=Ez az \u00faj verzi\u00f3 kiad\u00e1sra ker\u00fclt:
+dialog.checkversion.releasedate2=.
+dialog.checkversion.download=Az \u00faj verzi\u00f3 let\u00f6lt\u00e9s\u00e9hez keresse fel a http://activityworkshop.net/software/prune/download.html webhelyet.
+dialog.keys.intro=A k\u00f6vetkez\u0151 gyorsbillenty\u0171k haszn\u00e1lhat\u00f3k az eg\u00e9r haszn\u00e1lata helyett
+dialog.keys.keylist=<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.keys.macmodifier=Command
+dialog.saveconfig.desc=A k\u00f6vetkez\u0151 be\u00e1ll\u00edt\u00e1sok menthet\u0151k egy konfigur\u00e1ci\u00f3s f\u00e1jlba
+dialog.saveconfig.prune.trackdirectory=Nyomvonalak k\u00f6nyvt\u00e1ra
+dialog.saveconfig.prune.photodirectory=F\u00e9nyk\u00e9pek k\u00f6nyvt\u00e1ra
+dialog.saveconfig.prune.languagecode=Nyelv k\u00f3dja (HU)
+dialog.saveconfig.prune.languagefile=Nyelvi f\u00e1jl
+dialog.saveconfig.prune.gpsdevice=GPS eszk\u00f6z
+dialog.saveconfig.prune.gpsformat=GPS form\u00e1tum
+dialog.saveconfig.prune.povrayfont=Povray bet\u0171t\u00edpus
+dialog.saveconfig.prune.metricunits=Metrikus m\u00e9rt\u00e9krendszer haszn\u00e1lata?
+dialog.saveconfig.prune.gnuplotpath=\u00datvonal a gnuplothoz
+dialog.saveconfig.prune.gpsbabelpath=\u00datvonal a gpsbabelhez
+dialog.saveconfig.prune.exiftoolpath=\u00datvonal az exiftoolhoz
+dialog.saveconfig.prune.mapsource=Kiv\u00e1lasztott t\u00e9rk\u00e9pforr\u00e1s
+dialog.saveconfig.prune.mapsourcelist=T\u00e9rk\u00e9pforr\u00e1sok
+dialog.saveconfig.prune.diskcache=T\u00e9rk\u00e9p-gyors\u00edt\u00f3t\u00e1r
+dialog.saveconfig.prune.kmzimagewidth=KMZ k\u00e9psz\u00e9less\u00e9g
+dialog.saveconfig.prune.kmzimageheight=KMZ k\u00e9pmagass\u00e1g
+dialog.saveconfig.prune.colourscheme=Sz\u00edns\u00e9ma
+dialog.saveconfig.prune.linewidth=Vonalsz\u00e9less\u00e9g
+dialog.saveconfig.prune.kmltrackcolour=KML nyomvonal sz\u00edne
+dialog.setpaths.intro=Ha sz\u00fcks\u00e9ges, kiv\u00e1laszthatja a k\u00fcls\u0151 alkalmaz\u00e1sok \u00fatvonalait:
+dialog.setpaths.found=\u00datvonal megtal\u00e1lhat\u00f3?
+dialog.addaltitude.noaltitudes=A kiv\u00e1lasztott tartom\u00e1ny nem tartalmaz magass\u00e1gi \u00e9rt\u00e9keket
+dialog.addaltitude.desc=Magass\u00e1ki eltol\u00e1s, amely hozz\u00e1adand\u00f3
+dialog.lookupsrtm.overwritezeros=Fel\u00fcl\u00edrja a nulla magass\u00e1g \u00e9rt\u00e9ket?
+dialog.setcolours.intro=A sz\u00edn m\u00f3dos\u00edt\u00e1s\u00e1hoz kattintson egy sz\u00ednfoltra
+dialog.setcolours.background=H\u00e1tt\u00e9r
+dialog.setcolours.borders=Szeg\u00e9lyek
+dialog.setcolours.lines=Vonalak
+dialog.setcolours.primary=Els\u0151dleges
+dialog.setcolours.secondary=M\u00e1sodlagos
+dialog.setcolours.point=Pontok
+dialog.setcolours.selection=Kijel\u00f6l\u00e9s
+dialog.setcolours.text=Sz\u00f6veg
+dialog.colourchooser.title=Sz\u00edn kiv\u00e1laszt\u00e1sa
+dialog.colourchooser.red=Piros
+dialog.colourchooser.green=Z\u00f6ld
+dialog.colourchooser.blue=K\u00e9k
+dialog.setlanguage.firstintro=Kiv\u00e1laszthat egy be\u00e9p\u00edtett nyelvet,<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.language=Nyelv
+dialog.setlanguage.languagefile=Nyelvi f\u00e1jl
+dialog.setlanguage.endmessage=Most mentse a be\u00e1ll\u00edt\u00e1sokat, \u00e9s ind\u00edtsa \u00fajra a Prune-t,\nhogy a nyelv v\u00e1lt\u00e1sa \u00e9rv\u00e9nybe l\u00e9pjen
+dialog.diskcache.save=T\u00e9rk\u00e9pek ment\u00e9se a lemezre
+dialog.diskcache.dir=Gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra
+dialog.diskcache.createdir=K\u00f6nyvt\u00e1r l\u00e9trehoz\u00e1sa
+dialog.diskcache.nocreate=A gyors\u00edt\u00f3t\u00e1r k\u00f6nyvt\u00e1ra nem ker\u00fclt l\u00e9trehoz\u00e1sra
+dialog.deletefieldvalues.intro=V\u00e1lassza ki a t\u00f6rlend\u0151 mez\u0151t a jelenlegi tartom\u00e1nyban
+dialog.setlinewidth.text=Adja meg a rajzoland\u00f3 vonalak vastags\u00e1g\u00e1t a nyomvonalak sz\u00e1m\u00e1ra (1-4)
+dialog.downloadosm.desc=Nyers OSM adatok let\u00f6lt\u00e9s\u00e9nek meger\u0151s\u00edt\u00e9se a megadott ter\u00fcletre:
+dialog.searchwikipedianames.search=Keres\u00e9s erre:
+
+# 3d window
+dialog.3d.title=Prune 3D n\u00e9zet
+dialog.3d.altitudefactor=Magass\u00e1gi ny\u00fajt\u00e1si t\u00e9nyez\u0151
+dialog.3dlines.title=Prune r\u00e1csvonalak
+dialog.3dlines.empty=Nincsenek megjelen\u00edthet\u0151 r\u00e1csvonalak!
+dialog.3dlines.intro=Ezek a r\u00e1csvonalak a 3D n\u00e9zethez
+
+# Confirm messages
+confirm.loadfile=Adatok f\u00e1jlb\u00f3l bet\u00f6ltve
+confirm.save.ok1=
+confirm.save.ok2=pont f\u00e1jlba ment\u00e9se sikeres
+confirm.deletepoint.single=adatpont t\u00f6r\u00f6lve
+confirm.deletepoint.multi=adatpont t\u00f6r\u00f6lve
+confirm.point.edit=pont szerkesztve
+confirm.mergetracksegments=Nyomvonalszakaszok egyes\u00edtve
+confirm.reverserange=Tartom\u00e1ny megford\u00edtva
+confirm.addtimeoffset=Id\u0151eltol\u00e1s hozz\u00e1adva
+confirm.addaltitudeoffset=Magass\u00e1geltol\u00e1s hozz\u00e1adva
+confirm.rearrangewaypoints=\u00datpontok \u00fajrarendezve
+confirm.rearrangephotos=F\u00e9nyk\u00e9pek \u00fajrarendezve
+confirm.cutandmove=Kijel\u00f6l\u00e9s \u00e1thelyezve
+confirm.convertnamestotimes=\u00datpont nevei konvert\u00e1lva
+confirm.saveexif.ok1=Mentve
+confirm.saveexif.ok2=k\u00e9pf\u00e1jl
+confirm.undo.single=m\u0171velet visszavonva
+confirm.undo.multi=m\u0171velet visszavonva
+confirm.jpegload.single=f\u00e9nyk\u00e9p hozz\u00e1adva
+confirm.jpegload.multi=f\u00e9nyk\u00e9p hozz\u00e1adva
+confirm.media.connect=m\u00e9dium \u00f6sszekapcsolva
+confirm.photo.disconnect=f\u00e9nyk\u00e9p lev\u00e1lasztva
+confirm.audio.disconnect=hang lev\u00e1lasztva
+confirm.media.removed=elt\u00e1vol\u00edtva
+confirm.correlatephotos.single=f\u00e9nyk\u00e9p megfeleltetve
+confirm.correlatephotos.multi=f\u00e9nyk\u00e9p megfeleltetve
+confirm.createpoint=pont l\u00e9trehozva
+confirm.rotatephoto=f\u00e9nyk\u00e9p elforgatva
+confirm.running=Futtat\u00e1s...
+confirm.lookupsrtm1=
+confirm.lookupsrtm2=magass\u00e1gi \u00e9rt\u00e9k tal\u00e1lhat\u00f3
+confirm.deletefieldvalues=Mez\u0151 \u00e9rt\u00e9kei t\u00f6r\u00f6lve
+confirm.audioload=Hangf\u00e1jl hozz\u00e1adva
+confirm.correlateaudios.single=hangf\u00e1jl megfeleltetve
+confirm.correlateaudios.multi=hangf\u00e1jl megfeleltetve
+
+# Buttons
+button.ok=OK
+button.back=El\u0151z\u0151
+button.next=K\u00f6vetkez\u0151
+button.finish=K\u00e9sz
+button.cancel=M\u00e9gse
+button.overwrite=Fel\u00fcl\u00edr\u00e1s
+button.moveup=Mozgat\u00e1s feljebb
+button.movedown=Mozgat\u00e1s lejjebb
+button.showlines=Sorok megjelen\u00edt\u00e9se
+button.edit=Szerkeszt\u00e9s
+button.exit=Kil\u00e9p\u00e9s
+button.close=Bez\u00e1r\u00e1s
+button.continue=Folytat\u00e1s
+button.yes=Igen
+button.no=Nem
+button.yestoall=Igen, mindet
+button.notoall=Nem, egyiket se
+button.select=Kijel\u00f6l\u00e9s
+button.selectall=Mindent kijel\u00f6l
+button.selectnone=Kijel\u00f6l\u00e9s megsz\u00fcntet\u00e9se
+button.preview=El\u0151n\u00e9zet
+button.load=Bet\u00f6lt\u00e9s
+button.upload=Felt\u00f6lt\u00e9s
+button.guessfields=Mez\u0151k kital\u00e1l\u00e1sa
+button.showwebpage=Weboldal megjelen\u00edt\u00e9se
+button.check=Ellen\u0151rz\u00e9s
+button.resettodefaults=Vissza\u00e1ll\u00edt\u00e1s az alap\u00e9rtelmezettre
+button.browse=B\u00f6ng\u00e9sz\u00e9s...
+button.addnew=\u00daj hozz\u00e1ad\u00e1sa
+button.delete=T\u00f6rl\u00e9s
+
+# File types
+filetype.txt=TXT f\u00e1jlok
+filetype.jpeg=JPG f\u00e1jlok
+filetype.kmlkmz=KML, KMZ f\u00e1jlok
+filetype.kml=KML f\u00e1jlok
+filetype.kmz=KMZ f\u00e1jlok
+filetype.gpx=GPX f\u00e1jlok
+filetype.pov=POV f\u00e1jlok
+filetype.svg=SVG f\u00e1jlok
+filetype.audio=MP3, OGG, WAV f\u00e1jlok
+
+# Display components
+display.nodata=Nincs adat bet\u00f6ltve
+display.noaltitudes=A nyomvonal nem tartalmaz magass\u00e1gi adatot
+display.notimestamps=A nyomvonal nem tartalmaz id\u0151b\u00e9lyegeket
+details.trackdetails=Nyomvonal r\u00e9szletei
+details.notrack=Nincs adat bet\u00f6ltve
+details.track.points=Pontok
+details.track.file=F\u00e1jl
+details.track.numfiles=F\u00e1jlok sz\u00e1ma
+details.pointdetails=Pont r\u00e9szletei
+details.index.selected=Jelenlegi:
+details.index.of=\u00f6sszesen:
+details.nopointselection=Nincs pont kijel\u00f6lve
+details.photofile=F\u00e9nyk\u00e9pf\u00e1jl
+details.norangeselection=Nincs tartom\u00e1ny kijel\u00f6lve
+details.rangedetails=Tartom\u00e1ny r\u00e9szletei
+details.range.selected=Kiv\u00e1lasztva:
+details.range.to=\u00f6sszesen:
+details.altitude.to=\u00f6sszesen:
+details.range.climb=Emelked\u00e9s
+details.range.descent=Lejt\u00e9s
+details.coordformat=Koordin\u00e1ta form\u00e1tuma
+details.distanceunits=T\u00e1vols\u00e1g egys\u00e9ge
+display.range.time.secs=mp
+display.range.time.mins=p
+display.range.time.hours=\u00f3
+display.range.time.days=n
+details.range.avespeed=\u00c1tlagsebess\u00e9g
+details.range.avemovingspeed=Mozg\u00e1si \u00e1tlag
+details.range.maxspeed=Maxim\u00e1lis sebess\u00e9g
+details.range.numsegments=Szakaszok sz\u00e1ma
+details.range.pace=Iram
+details.range.gradient=Szintk\u00fcl\u00f6nbs\u00e9g
+details.lists.waypoints=\u00datpontok
+details.lists.photos=F\u00e9nyk\u00e9pek
+details.lists.audio=Hang
+details.photodetails=F\u00e9nyk\u00e9p r\u00e9szletei
+details.nophoto=Nincs f\u00e9nyk\u00e9p kiv\u00e1lasztva
+details.photo.loading=Bet\u00f6lt\u00e9s
+details.media.connected=\u00d6sszekapcsolva
+details.audiodetails=Hang r\u00e9szletei
+details.noaudio=Nincs hang kiv\u00e1lasztva
+details.audio.file=Hangf\u00e1jl
+details.audio.playing=lej\u00e1tsz\u00e1s...
+map.overzoom=Nem \u00e9rhet\u0151 el t\u00e9rk\u00e9p ezen a nagy\u00edt\u00e1si szinten
+
+# Field names
+fieldname.latitude=Sz\u00e9less\u00e9g
+fieldname.longitude=Hossz\u00fas\u00e1g
+fieldname.altitude=Magass\u00e1g
+fieldname.timestamp=Id\u00f5
+fieldname.time=Id\u0151
+fieldname.waypointname=N\u00e9v
+fieldname.waypointtype=T\u00edpus
+fieldname.newsegment=Szakasz
+fieldname.custom=Egy\u00e9ni
+fieldname.prefix=Mez\u0151
+fieldname.distance=T\u00e1vols\u00e1g
+fieldname.movingdistance=Mozg\u00e1si t\u00e1vols\u00e1g
+fieldname.duration=Id\u0151tartam
+fieldname.speed=Sebess\u00e9g
+fieldname.verticalspeed=F\u00fcgg\u0151leges sebess\u00e9g
+
+# Measurement units
+units.original=Eredeti
+units.default=Alap\u00e9rtelmezett
+units.metres=m\u00e9ter
+units.metres.short=m
+units.feet=l\u00e1b
+units.feet.short=ft
+units.kilometres=kilom\u00e9ter
+units.kilometres.short=km
+units.kmh=km/h
+units.miles=m\u00e9rf\u00f6ld
+units.miles.short=mi
+units.mph=mph
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=\u00f3ra
+units.degminsec=Sz\u00f6g-sz\u00f6gperc-sz\u00f6gm\u00e1sodperc
+units.degmin=Sz\u00f6g-sz\u00f6gperc
+units.deg=Sz\u00f6g
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.hu
+wikipedia.lang=hu
+
+# Cardinals for 3d plots
+cardinal.n=\u00c9
+cardinal.s=D
+cardinal.e=K
+cardinal.w=Ny
+
+# Undo operations
+undo.load=adatok bet\u00f6lt\u00e9se
+undo.loadphotos=f\u00e9nyk\u00e9pek bet\u00f6lt\u00e9se
+undo.loadaudios=hangf\u00e1jlok bet\u00f6lt\u00e9se
+undo.editpoint=pont szerkeszt\u00e9se
+undo.deletepoint=pont t\u00f6rl\u00e9se
+undo.removephoto=f\u00e9nyk\u00e9p elt\u00e1vol\u00edt\u00e1sa
+undo.removeaudio=hangf\u00e1jl elt\u00e1vol\u00edt\u00e1sa
+undo.deleterange=tartom\u00e1ny t\u00f6rl\u00e9se
+undo.compress=nyomvonal t\u00f6m\u00f6r\u00edt\u00e9se
+undo.insert=pontok besz\u00far\u00e1sa
+undo.reverse=tartom\u00e1ny megford\u00edt\u00e1sa
+undo.mergetracksegments=nyomvonalszakaszok egyes\u00edt\u00e9se
+undo.addtimeoffset=id\u0151eltol\u00e1s hozz\u00e1ad\u00e1sa
+undo.addaltitudeoffset=magass\u00e1geltol\u00e1s hozz\u00e1ad\u00e1sa
+undo.rearrangewaypoints=\u00fatpontok \u00fajrarendez\u00e9se
+undo.cutandmove=kijel\u00f6l\u00e9s mozgat\u00e1sa
+undo.connect=\u00f6sszekapcsol\u00e1s
+undo.disconnect=lev\u00e1laszt\u00e1s
+undo.correlatephotos=f\u00e9nyk\u00e9pek megfeleltet\u00e9se
+undo.rearrangephotos=f\u00e9nyk\u00e9pek \u00fajrarendez\u00e9se
+undo.createpoint=pont l\u00e9trehoz\u00e1sa
+undo.rotatephoto=f\u00e9nyk\u00e9p forgat\u00e1sa
+undo.convertnamestotimes=nevek konvert\u00e1l\u00e1sa id\u0151v\u00e9
+undo.lookupsrtm=magass\u00e1gi adatok let\u00f6lt\u00e9se SRTM-r\u0151l
+undo.deletefieldvalues=mez\u0151 \u00e9rt\u00e9keinek t\u00f6rl\u00e9se
+undo.correlateaudios=hang megfeleltet\u00e9se
+
+# Error messages
+error.save.dialogtitle=Hiba az adatok ment\u00e9sekor
+error.save.nodata=Nincs mentend\u0151 adat
+error.save.failed=Az adatok f\u00e1jlba ment\u00e9se nem siker\u00fclt
+error.saveexif.filenotfound=F\u00e9nyk\u00e9pf\u00e1jl keres\u00e9se nem siker\u00fclt
+error.saveexif.cannotoverwrite1=A(z)
+error.saveexif.cannotoverwrite2=f\u00e9nyk\u00e9pf\u00e1jl csak olvashat\u00f3, \u00e9s nem \u00edrhat\u00f3 fel\u00fcl. \u00cdrjuk m\u00e1solatba?
+error.saveexif.failed1=
+error.saveexif.failed2=k\u00e9p ment\u00e9se nem siker\u00fclt
+error.saveexif.forced1=
+error.saveexif.forced2=k\u00e9p er\u00f6ltet\u00e9st ig\u00e9nyelt
+error.load.dialogtitle=Hiba az adatok bet\u00f6lt\u00e9sekor
+error.load.noread=A f\u00e1jl nem olvashat\u00f3
+error.load.nopoints=Nem tal\u00e1lhat\u00f3 koordin\u00e1tainform\u00e1ci\u00f3 a f\u00e1jlban
+error.load.unknownxml=Ismeretlen xml form\u00e1tum:
+error.load.noxmlinzip=Nem tal\u00e1lhat\u00f3 xml f\u00e1jl a zip f\u00e1jlon bel\u00fcl
+error.load.othererror=Hiba a f\u00e1jl olvas\u00e1sa sor\u00e1n:
+error.jpegload.dialogtitle=Hiba a k\u00e9pek bet\u00f6lt\u00e9sekor
+error.jpegload.nofilesfound=Nem tal\u00e1lhat\u00f3 f\u00e1jl
+error.jpegload.nojpegsfound=Nem tal\u00e1lhat\u00f3 jpeg f\u00e1jl
+error.jpegload.nogpsfound=Nem tal\u00e1lhat\u00f3 GPS inform\u00e1ci\u00f3
+error.jpegload.exifreadfailed=Az EXIF inform\u00e1ci\u00f3 olvas\u00e1sa nem siker\u00fclt. Nem olvasat\u00f3 EXIF inform\u00e1ci\u00f3\nbe\u00e9p\u00edtett vagy k\u00fcls\u0151 f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r n\u00e9lk\u00fcl.
+error.audioload.nofilesfound=Nem tal\u00e1lhat\u00f3 hangf\u00e1jl
+error.gpsload.unknown=Ismeretlen hiba
+error.undofailed.title=A visszavon\u00e1s nem siker\u00fclt
+error.undofailed.text=A m\u0171velet visszavon\u00e1sa nem siker\u00fclt
+error.function.noop.title=A funkci\u00f3 nem eredm\u00e9nyezett v\u00e1ltoz\u00e1st
+error.rearrange.noop=A pontok \u00fajrarendez\u00e9se nem eredm\u00e9nyezett v\u00e1ltoz\u00e1st
+error.function.notavailable.title=A funkci\u00f3 nem \u00e9rhet\u0151 el
+error.function.nojava3d=Ehhez a funkci\u00f3hoz a Java3d f\u00fcggv\u00e9nyk\u00f6nyvt\u00e1r sz\u00fcks\u00e9ges,\n amely a Sun.com webhelyr\u0151l \u00e9rhet\u0151 el.
+error.3d=Hiba t\u00f6rt\u00e9nt a 3d megjelen\u00edt\u00e9ssel
+error.readme.notfound=Az olvassel f\u00e1jl nem tal\u00e1lhat\u00f3
+error.osmimage.dialogtitle=Hiba a t\u00e9rk\u00e9p bet\u00f6lt\u00e9sekor
+error.osmimage.failed=A t\u00e9rk\u00e9p bet\u00f6lt\u00e9se nem siker\u00fclt. Ellen\u0151rizze az internetkapcsolatot.
+error.language.wrongfile=\u00dagy t\u0171nik, hogy a kiv\u00e1lasztott f\u00e1jl nem egy nyelvi f\u00e1jl a Prune-hoz
+error.convertnamestotimes.nonames=A nevek nem konvert\u00e1lhat\u00f3k id\u0151adatokk\u00e1
+error.lookupsrtm.nonefound=Nem \u00e9rhet\u0151 el magass\u00e1gi \u00e9rt\u00e9k ezekhez a pontokhoz
+error.lookupsrtm.nonerequired=Az \u00f6sszes pont m\u00e1r rendelkezik magass\u00e1gadatokkal, \u00edgy nincs mit keresni
+error.gpsies.uploadnotok=A gpsies szerver a k\u00f6vetkez\u0151 \u00fczenetet adta vissza
+error.gpsies.uploadfailed=A felt\u00f6lt\u00e9s nem siker\u00fclt a k\u00f6vetkez\u0151 hib\u00e1val
+error.playaudiofailed=A hangf\u00e1jl lej\u00e1tsz\u00e1sa nem siker\u00fclt
menu.range.none=Tidak memilih
menu.photo=Foto
menu.photo.saveexif=Simpan ke Exif
-menu.photo.connect=Hubungkan ke titik
-menu.photo.disconnect=Putuskan dari titik
-menu.photo.delete=Menghapus foto
+function.connecttopoint=Hubungkan ke titik
+function.disconnectfrompoint=Putuskan dari titik
+function.removephoto=Menghapus foto
menu.view=Lihat
menu.settings=Pengaturan
menu.view.browser=Peta di browser
details.nopointselection=Tidak ada titik
details.norangeselection=Tidak ada jangkauan
details.rangedetails=Rincian jangkauan
-details.waypointsphotos.photos=Foto
+details.lists.photos=Foto
details.photodetails=Rincian foto
details.nophoto=Tidak ada foto
details.photo.loading=Membuka
# Text entries for the Prune application
-# Italian entries as extra
+# Italian entries thanks to josatoc
# Menu entries
menu.file=File
menu.point.deletepoint=Cancella Punto
menu.photo=Foto
menu.photo.saveexif=Salva su Exif
-menu.photo.connect=Collega al punto
-menu.photo.disconnect=Scollega dal punto
-menu.photo.delete=Rimuovi foto
-menu.view=Visualizza
+menu.audio=Audio
+menu.view=Vista
menu.view.showsidebars=Mostra barre laterali
menu.view.browser=Mappa sul browser
menu.view.browser.google=Google maps
menu.map.zoomout=Zoom -
menu.map.zoomfull=Zoom tutto
menu.map.newpoint=Crea nuovo punto
+menu.map.drawpoints=Crea serie di punti
menu.map.connect=Aggancia ai punti
menu.map.autopan=Autopan
menu.map.showmap=Mostra sulla mappa
altkey.menu.point=P
altkey.menu.view=V
altkey.menu.photo=O
+altkey.menu.audio=U
altkey.menu.settings=R
altkey.menu.help=A
function.getgpsies=Ottieni traccie da Gpsies
function.uploadgpsies=Carica traccia su Gpsies
function.lookupsrtm=Ottieni quote da SRTM
+function.getwikipedia=Ottieni informazioni da Wikipedia
+function.searchwikipedianames=Cerca il nome in Wikipedia
+function.downloadosm=Scarica dati OSM dell'area
function.duplicatepoint=Duplica punto corrente in coda
function.setcolours=Scegli colori
+function.setlinewidth=Scegli la lo spessore
function.setlanguage=Scegli la lingua
+function.connecttopoint=Collega al punto
+function.disconnectfrompoint=Scollega dal punto
+function.removephoto=Rimuovi foto
function.correlatephotos=Correla le foto
function.rearrangephotos=Riordina foto
function.rotatephotoleft=Ruota foto a sinistra
function.rotatephotoright=Ruota foto a destra
+function.photopopup=Mostra la foto in un riquadro
function.ignoreexifthumb=Ignora anteprima foto exif
+function.loadaudio=Aggiungi ripresa audio
+function.removeaudio=Rimuovi ripresa audio
+function.correlateaudios=Correla ripresa audio
+function.playaudio=Riproduci ripresa audio
+function.stopaudio=Ferma ripresa audio
function.help=Aiuto
function.showkeys=Visualizza tasti scelta rapida
function.about=Informazioni su Prune
dialog.pointtype.track=Punti traccia
dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Punti foto
+dialog.pointtype.audio=Punti audio
dialog.pointtype.selection=Solo la selezione
dialog.confirmreversetrack.title=Conferma l'inversione
dialog.confirmreversetrack.text=Questa traccia contiene informazioni sull'orario di scatto che possono essere messe fuori sequenza dopo l'inversione.\nSei sicuro di voler invertire questa sezione?
dialog.gpsies.activity.snowshoe=Trekking sulla neve
dialog.gpsies.activity.sailing=Navigazione
dialog.gpsies.activity.skating=Pattinaggio
+dialog.wikipedia.column.name=Titolo articolo
+dialog.wikipedia.column.distance=Distanza
dialog.correlate.notimestamps=Non ci sono informazioni temporali tra i dati dei punti, non c'\u00e8 niente per collegarli con le foto.
dialog.correlate.nouncorrelatedphotos=Non ci sono foto non correlate.\nSei sicuro di voler continuare?
dialog.correlate.photoselect.intro=Selezione una delle foto correlate da usare come scarto dell'orario
-dialog.correlate.photoselect.photoname=Nome della foto
-dialog.correlate.photoselect.timediff=Differenza di orario
-dialog.correlate.photoselect.photolater=Foto scattata dopo il punto
+dialog.correlate.select.photoname=Nome della foto
+dialog.correlate.select.timediff=Differenza di orario
+dialog.correlate.select.photolater=Foto scattata dopo il punto
dialog.correlate.options.tip=Consiglio: Con il collegamento manuale di almeno una foto, lo scarto di orario viene calcolato per te
dialog.correlate.options.intro=Selezione le opzioni per la correlazione automatica
dialog.correlate.options.offsetpanel=Scarto di orario
dialog.correlate.options.offset.minutes=minuti e
dialog.correlate.options.offset.seconds=secondi
dialog.correlate.options.photolater=Foto scattata dopo il punto
-dialog.correlate.options.pointlater=Punto successivo allo scatto della foto
+dialog.correlate.options.pointlaterphoto=Punto successivo allo scatto della foto
+dialog.correlate.options.audiolater=Ripresa audio dopo il punto
+dialog.correlate.options.pointlateraudio=Punto successivo alla ripresa audio
dialog.correlate.options.limitspanel=Limiti di correlamento
dialog.correlate.options.notimelimit=Nessun limite di tempo
dialog.correlate.options.timelimit=Limite di tempo
dialog.correlate.options.distancelimit=Distanza limite
dialog.correlate.options.correlate=Correlate
dialog.correlate.alloutsiderange=Tutte le foto sono fuori dall'orario della traccia, e nessuna pu\u00f2 essere correlata.\nProva a cambiare lo scarto o correla manualmente almeno una foto.
+dialog.correlate.filetimes=I dati temporali mostrano:
+dialog.correlate.filetimes2=della ripresa audio
+dialog.correlate.correltimes=Per la correlazione usa:
+dialog.correlate.timestamp.beginning=Inizio
+dialog.correlate.timestamp.middle=Met\u00e0
+dialog.correlate.timestamp.end=Fine
+dialog.correlate.audioselect.intro=Seleziona una di queste riprese audio correlate come scarto temporale
+dialog.correlate.select.audioname=Nome ripresa audio
+dialog.correlate.select.audiolater=Ripresa audio successiva
dialog.rearrangephotos.desc=Seleziona la destinazione e l'ordine dei punti foto
dialog.rearrangephotos.tostart=Sposta all'inizio
dialog.rearrangephotos.toend=Sposta alla fine
dialog.saveconfig.prune.kmzimagewidth=larghezza immagine KMZ
dialog.saveconfig.prune.kmzimageheight=altezza immagine KMZ
dialog.saveconfig.prune.colourscheme=Schema colori
+dialog.saveconfig.prune.linewidth=Spessore linea
dialog.saveconfig.prune.kmltrackcolour=Colore della traccia KML
dialog.setpaths.intro=Se necessario, puoi indicare il percorso delle applicazioni esterne:
dialog.setpaths.found=trovato?
dialog.diskcache.createdir=Crea cartella
dialog.diskcache.nocreate=Cartella della cache non creata
dialog.deletefieldvalues.intro=Selezione il campo da cancellare dall'intervallo corrente
+dialog.setlinewidth.text=Specifica il tratteggio delle linee per disegnare la traccia (1-4)
+dialog.downloadosm.desc=Conferma lo scarico dei dati raw OSM per l'area specificata:
+dialog.searchwikipedianames.search=Cerca per:
# 3d window
dialog.3d.title=Visione Prune in 3D
-dialog.3d.altitudecap=Intervallo altitudine minimo
dialog.3d.altitudefactor=Fattore di moltiplicazione della quota
dialog.3dlines.title=Griglia di Prune
dialog.3dlines.empty=Nessuna griglia mostrata!
confirm.undo.multi=operazioni annullate
confirm.jpegload.single=foto \u00e8 stata aggiunta
confirm.jpegload.multi=foto sono state aggiunte
-confirm.photo.connect=foto collegata
+confirm.media.connect=media collegata
confirm.photo.disconnect=foto scollegata
-confirm.correlate.single=foto era correlata
-confirm.correlate.multi=foto erano correlate
+confirm.audio.disconnect=ripresa audio scollegata
+confirm.media.removed=rimosso
+confirm.correlatephotos.single=foto era correlata
+confirm.correlatephotos.multi=foto erano correlate
confirm.createpoint=punto creato
confirm.rotatephoto=foto ruotata
confirm.running=Operazione in corso...
confirm.lookupsrtm1=Trovato
confirm.lookupsrtm2=valori di quota
confirm.deletefieldvalues=Valori del campo cancellati
+confirm.audioload=Ripresa audio aggiunta
+confirm.correlateaudios.single=la ripresa audio era correlata
+confirm.correlateaudios.multi=le riprese audio erano correlate
# Buttons
button.ok=OK
filetype.gpx=File GPX
filetype.pov=File POV
filetype.svg=File SVG
+filetype.audio=File MP3, OGG, WAV
# Display components
display.nodata=Nessun dato caricato
details.range.numsegments=Numero di segmenti
details.range.pace=Passo
details.range.gradient=Gradiente
-details.waypointsphotos.waypoints=Waypoint
-details.waypointsphotos.photos=Foto
+details.lists.waypoints=Waypoint
+details.lists.photos=Foto
+details.lists.audio=Ripresa audio
details.photodetails=Dettagli foto
details.nophoto=Nessuna foto selezionata
details.photo.loading=Caricamento
-details.photo.connected=Collegata
+details.media.connected=Collegata
+details.audiodetails=Dettagli ripresa audio
+details.noaudio=Nessuna ripresa audio selezionata
+details.audio.file=Ripresa audio
+details.audio.playing=Riproduzione...
map.overzoom=Mappa non disponibile a questo livello di zoom
# Field names
# External urls
url.googlemaps=maps.google.it
+wikipedia.lang=it
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=carica dati
undo.loadphotos=carica foto
+undo.loadaudios=carica riprese audio
undo.editpoint=edita punto
undo.deletepoint=cancella punto
-undo.deletephoto=rimuovi foto
+undo.removephoto=rimuovi foto
+undo.removeaudio=rimuovi riprese audio
undo.deleterange=cancella l'intervallo
undo.compress=comprimi traccia
undo.insert=inserisci punti
undo.addaltitudeoffset=aggiungi scarto altitudine
undo.rearrangewaypoints=riorganizza waypoint
undo.cutandmove=muovi selezione
-undo.connectphoto=collega foto
-undo.disconnectphoto=scollega foto
-undo.correlate=correla foto
+undo.connect=collega
+undo.disconnect=scollega
+undo.correlatephotos=correla foto
undo.rearrangephotos=riorganizza foto
undo.createpoint=crea punto
undo.rotatephoto=ruota foto
undo.convertnamestotimes=converti nomi in orari
undo.lookupsrtm=cerca quote in SRTM
undo.deletefieldvalues=cancellare i valori del campo
+undo.correlateaudios=correla riprese audio
# Error messages
error.save.dialogtitle=Errore nel salvataggio dati
error.jpegload.dialogtitle=Errore nel caricamento delle foto
error.jpegload.nofilesfound=File non trovato
error.jpegload.nojpegsfound=File jpeg non trovato
-error.jpegload.noexiffound=Informazioni EXIF non trovate
error.jpegload.nogpsfound=Informazioni GPS non trovate
error.jpegload.exifreadfailed=Lettera dei dati EXIF fallita. I dati EXIF non possono\n essere letti senza una libreria interna o esterna.
+error.audioload.nofilesfound=Riprese audio non trovate
error.gpsload.unknown=Errore sconosciuto
error.undofailed.title=Impossibile annullare
error.undofailed.text=Impossibile annullare l'operazione
error.lookupsrtm.nonerequired=Tutti i punti hanno gi\u00e0 una quota, non c'\u00e8 niente da cercare
error.gpsies.uploadnotok=Il server Gpsies ha riportato il messaggio
error.gpsies.uploadfailed=Il caricamento \u00e8 fallito con l'errore
+error.playaudiofailed=Ripresa audio non riprodotta
menu.range.end=\u7d42\u4e86\u70b9\u3092\u7f6e\u304f
menu.photo=\u5199\u771f
menu.photo.saveexif=Exif\u306b\u4fdd\u5b58
-menu.photo.connect=\u70b9\u306b\u63a5\u7d9a
-menu.photo.disconnect=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664
-menu.photo.delete=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f
+function.connecttopoint=\u70b9\u306b\u63a5\u7d9a
+function.disconnectfrompoint=\u70b9\u304b\u3089\u63a5\u7d9a\u89e3\u9664
+function.removephoto=\u5199\u771f\u3092\u53d6\u308a\u9664\u304f
menu.view=\u30d3\u30e5\u30fc
menu.view.browser=\u5730\u56f3\u3092\u30d6\u30e9\u30a6\u30b6\u30fc\u3067\u898b\u308b
menu.view.browser.google=Google \u30de\u30c3\u30d7
dialog.correlate.notimestamps=\u30c7\u30fc\u30bf\u30dd\u30a4\u30f3\u30c8\u306b\u306f\u30bf\u30a4\u30e0\u30b9\u30bf\u30f3\u30d7\u304c\u306a\u3044\u306e\u3067\u3001\u5199\u771f\u3092\u95a2\u9023\u4ed8\u3051\u3089\u308c\u308b\u7269\u304c\u3042\u308a\u307e\u305b\u3093\u3002
dialog.correlate.nouncorrelatedphotos=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u306a\u304b\u3063\u305f\u5199\u771f\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u7d9a\u3051\u307e\u3059\u304b\uff1f
dialog.correlate.photoselect.intro=\u6642\u9593\u504f\u4f4d\u3092\u4f5c\u308b\u305f\u3081\u306e\u5199\u771f\u3092\u4e00\u3064\u9078\u3093\u3067\u304f\u3060\u3055\u3044\u3002
-dialog.correlate.photoselect.photoname=\u5199\u771f\u540d
-dialog.correlate.photoselect.timediff=\u6642\u9593\u5dee
-dialog.correlate.photoselect.photolater=\u5199\u771f\u304c\u5f8c
+dialog.correlate.select.photoname=\u5199\u771f\u540d
+dialog.correlate.select.timediff=\u6642\u9593\u5dee
+dialog.correlate.select.photolater=\u5199\u771f\u304c\u5f8c
dialog.correlate.options.tip=\u63d0\u793a\uff1a\u5c11\u306a\u304f\u3068\u3082\u4e00\u3064\u306e\u5199\u771f\u3092\u624b\u52d5\u3067\u95a2\u9023\u4ed8\u3051\u308b\u3068\u3001\u6642\u9593\u504f\u4f4d\u3092\u8a08\u7b97\u3059\u308b\u4e8b\u304c\u3067\u304d\u307e\u3059\u3002
dialog.correlate.options.intro=\u81ea\u52d5\u95a2\u9023\u4ed8\u3051\u30aa\u30d7\u30b7\u30e7\u30f3\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
dialog.correlate.options.offsetpanel=\u6642\u9593\u504f\u4f4d
dialog.correlate.options.offset.minutes=\u5206
dialog.correlate.options.offset.seconds=\u79d2
dialog.correlate.options.photolater=\u3053\u306e\u70b9\u3088\u308a\u5f8c\u308d\u306e\u5199\u771f
-dialog.correlate.options.pointlater=\u5199\u771f\u3088\u308a\u5f8c\u308d\u306e\u70b9
+dialog.correlate.options.pointlaterphoto=\u5199\u771f\u3088\u308a\u5f8c\u308d\u306e\u70b9
dialog.correlate.options.limitspanel=\u95a2\u9023\u4ed8\u3051\u5236\u9650
dialog.correlate.options.notimelimit=\u6642\u9593\u5236\u9650\u306a\u3057
dialog.correlate.options.timelimit=\u6642\u9593\u5236\u9650
# 3d window
dialog.3d.title=Prune 3D \u8868\u793a
-dialog.3d.altitudecap=\u6700\u4f4e\u9ad8\u5ea6\u7bc4\u56f2
dialog.3dlines.title=Prune \u683c\u5b50\u7dda
dialog.3dlines.empty=\u683c\u5b50\u7dda\u304c\u8868\u793a\u3055\u308c\u307e\u305b\u3093
dialog.3dlines.intro=\u3053\u308c\u3089\u304c 3D \u8868\u793a\u7528\u306e\u683c\u5b50\u7dda\u3067\u3059\u3002
confirm.undo.multi=\u64cd\u4f5c\u306f\u30a2\u30f3\u30c9\u30a5\u3055\u308c\u305f
confirm.jpegload.single=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
confirm.jpegload.multi=\u5199\u771f\u304c\u52a0\u3048\u3089\u308c\u305f
-confirm.photo.connect=\u5199\u771f\u304c\u63a5\u7d9a\u3055\u308c\u305f
confirm.photo.disconnect=\u5199\u771f\u304c\u63a5\u7d9a\u3055\u308c\u305f
-confirm.correlate.single=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
-confirm.correlate.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
+confirm.correlatephotos.single=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
+confirm.correlatephotos.multi=\u5199\u771f\u304c\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f
confirm.createpoint=\u70b9\u304c\u4f5c\u3089\u308c\u305f
confirm.rotatephoto=\u5199\u771f\u3092\u56de\u8ee2\u3057\u305f
confirm.running=\u5b9f\u884c\u4e2d...
details.range.numsegments=\u30bb\u30b0\u30e1\u30f3\u30c8\u6570
details.range.pace=\u30da\u30fc\u30b9
details.range.gradient=\u52fe\u914d
-details.waypointsphotos.waypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8
-details.waypointsphotos.photos=\u5199\u771f
+details.lists.waypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8
+details.lists.photos=\u5199\u771f
details.photodetails=\u5199\u771f\u8a73\u7d30
details.nophoto=\u5199\u771f\u304c\u9078\u629e\u3055\u308c\u3066\u3044\u307e\u305b\u3093
details.photo.loading=\u8aad\u307f\u8fbc\u307f\u4e2d
-details.photo.connected=\u63a5\u7d9a\u6e08
+details.media.connected=\u63a5\u7d9a\u6e08
map.overzoom=\u3053\u306e\u30ba\u30fc\u30e0\u30ec\u30d9\u30eb\u3067\u306f\u5730\u56f3\u304c\u5165\u624b\u3067\u304d\u307e\u305b\u3093\u3002
# Field names
# External urls
url.googlemaps=maps.google.co.jp
+wikipedia.lang=ja
# Cardinals for 3d plots
cardinal.n=N
undo.loadphotos=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f
undo.editpoint=\u70b9\u306e\u7de8\u96c6
undo.deletepoint=\u70b9\u306e\u524a\u9664
-undo.deletephoto=\u5199\u771f\u306e\u53d6\u308a\u9664\u304d
+undo.removephoto=\u5199\u771f\u306e\u53d6\u308a\u9664\u304d
undo.deleterange=\u7bc4\u56f2\u306e\u524a\u9664
undo.compress=\u30c8\u30e9\u30c3\u30af\u306e\u5727\u7e2e
undo.insert=\u70b9\u306e\u633f\u5165
undo.addaltitudeoffset=\u9ad8\u5ea6\u504f\u4f4d\u3092\u52a0\u3048\u308b
undo.rearrangewaypoints=\u30a6\u30a7\u30a4\u30dd\u30a4\u30f3\u30c8\u3092\u633f\u5165
undo.cutandmove=\u30bb\u30af\u30b7\u30e7\u30f3\u306e\u79fb\u52d5
-undo.connectphoto=\u5199\u771f\u306e\u63a5\u7d9a
-undo.disconnectphoto=\u5199\u771f\u306e\u63a5\u7d9a\u89e3\u9664
-undo.correlate=\u5199\u771f\u306e\u95a2\u9023\u4ed8\u3051
+undo.connect=\u5199\u771f\u306e\u63a5\u7d9a
+undo.disconnect=\u5199\u771f\u306e\u63a5\u7d9a\u89e3\u9664
+undo.correlatephotos=\u5199\u771f\u306e\u95a2\u9023\u4ed8\u3051
undo.rearrangephotos=\u5199\u771f\u306e\u4e26\u3079\u66ff\u3048
undo.createpoint=\u70b9\u306e\u4f5c\u6210
undo.rotatephoto=\u5199\u771f\u306e\u56de\u8ee2
error.jpegload.dialogtitle=\u5199\u771f\u306e\u8aad\u307f\u8fbc\u307f\u30a8\u30e9\u30fc:
error.jpegload.nofilesfound=\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
error.jpegload.nojpegsfound=Jpeg\u30d5\u30a1\u30a4\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
-error.jpegload.noexiffound=EXIF\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
error.jpegload.nogpsfound=GPS\u60c5\u5831\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093
error.gpsload.unknown=\u4e0d\u660e\u306a\u30a8\u30e9\u30fc
error.undofailed.title=\u30a2\u30f3\u30c9\u30a5\u5931\u6557
--- /dev/null
+# Text entries for the Prune application
+# Korean entries thanks to HooAU
+
+# Menu entries
+menu.file=\ud30c\uc77c
+menu.file.addphotos=\uc0ac\uc9c4 \ucd94\uac00\ud558\uae30
+menu.file.save=\ud0dd\uc2a4\ud2b8\ub85c \uc800\uc7a5\ud558\uae30
+menu.file.exit=\uc885\ub8cc
+menu.track=\ud2b8\ub799
+menu.track.undo=\uc2e4\ud589\ucde8\uc18c
+menu.track.clearundo=\uc2e4\ud589\ucde8\uc18c \ubaa9\ub85d \uc0ad\uc81c
+menu.track.deletemarked=\ud45c\uc2dc\ub41c \uc9c0\uc810 \uc9c0\uc6b0\uae30
+menu.track.rearrange=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
+menu.track.rearrange.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
+menu.track.rearrange.end=\ub05d \uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
+menu.track.rearrange.nearest=\uac00\uae4c\uc6b4 \ud2b8\ub799\uc9c0\uc810\uc73c\ub85c \uc774\ub3d9
+menu.range=\uc5f0\uacb0\uc120
+menu.range.all=\ubaa8\ub450 \uc120\ud0dd
+menu.range.none=\uc120\ud0dd\ud558\uc9c0 \uc54a\uae30
+menu.range.start=\uc2dc\uc791 \uc9c0\uc810\uc73c\ub85c \uc9c0\uc815
+menu.range.end=\ub05d\uc9c0\uc810\uc73c\ub85c \uc9c0\uc815
+menu.range.deleterange=\uc5f0\uacb0\uc120 \uc0ad\uc81c
+menu.range.interpolate=\uc911\uac04\uc5d0 \ub123\uae30
+menu.range.average=\ud3c9\uade0\uc9c0\uc810\uc120\ud0dd
+menu.range.reverse=\ucc98\uc74c\uacfc \ub05d \ubc14\uafb8\uae30
+menu.range.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569
+menu.range.cutandmove=\uc790\ub974\uace0 \uc62e\uae30\uae30
+menu.point=\uc9c0\uc810
+menu.point.editpoint=\uc9c0\uc810 \uc218\uc815
+menu.point.deletepoint=\uc9c0\uc810 \uc0ad\uc81c
+menu.photo=\uc0ac\uc9c4
+menu.photo.saveexif=Exif\ub85c \uc800\uc7a5
+menu.audio=\uc18c\ub9ac
+menu.view=\ubcf4\uae30
+menu.view.showsidebars=\uc0ac\uc774\ub4dc\ubc14 \ubcf4\uae30
+menu.view.browser=\uc6f9\ube0c\ub77c\uc6b0\uc800\ub85c \uc9c0\ub3c4\ubcf4\uae30
+menu.view.browser.google=\uad6c\uae00 \uc9c0\ub3c4
+menu.view.browser.openstreetmap=\uc624\ud508\uc2a4\ud2b8\ub9ac\ud2b8\ub9f5
+menu.view.browser.mapquest=\ub9f5\ud018\uc2a4\ud2b8
+menu.view.browser.yahoo=\uc57c\ud6c4 \uc9c0\ub3c4
+menu.view.browser.bing=\ube59 \uc9c0\ub3c4
+menu.settings=\uc124\uc815
+menu.settings.onlinemode=\uc778\ud130\ub137\uc5d0\uc11c \uc9c0\ub3c4 \ubd88\ub7ec\uc624\uae30
+menu.help=\ub3c4\uc6c0\ub9d0
+# Popup menu for map
+menu.map.zoomin=\ud655\ub300
+menu.map.zoomout=\ucd95\uc18c
+menu.map.zoomfull=\uc804\uccb4\ud06c\uae30 \ubcf4\uae30
+menu.map.newpoint=\uc0c8 \uc9c0\uc810 \uc0dd\uc131
+menu.map.drawpoints=\uc0c8 \uc9c0\uc810\ub4e4 \uc0dd\uc131
+menu.map.connect=\ud2b8\ub809 \uc9c0\uc810 \uc5f0\uacb0\ud558\uae30
+menu.map.autopan=\uc790\ub3d9 \uc2dc\uc810 \ucd94\uc801
+menu.map.showmap=\uc9c0\ub3c4\ubcf4\uae30
+menu.map.showscalebar=\ucd95\uc801 \uc870\uc808\ubc14 \ubcf4\uae30
+
+# Alt keys for menus
+altkey.menu.file=F
+altkey.menu.track=F
+altkey.menu.range=R
+altkey.menu.point=P
+altkey.menu.view=V
+altkey.menu.photo=O
+altkey.menu.audio=A
+altkey.menu.settings=S
+altkey.menu.help=H
+
+# Ctrl shortcuts for menu items
+shortcut.menu.file.open=O
+shortcut.menu.file.load=L
+shortcut.menu.file.save=S
+shortcut.menu.track.undo=Z
+shortcut.menu.edit.compress=C
+shortcut.menu.range.all=A
+shortcut.menu.help.help=H
+
+# Functions
+function.open=\ud30c\uc77c \uc5f4\uae30
+function.loadfromgps=GPS\uc5d0\uc11c \uc790\ub8cc \uac00\uc838\uc624\uae30
+function.sendtogps=GPS\ub85c \uc790\ub8cc \ubcf4\ub0b4\uae30
+function.exportkml=KML \ub0b4\ubcf4\ub0b4\uae30
+function.exportgpx=GPX \ub0b4\ubcf4\ub0b4\uae30
+function.exportpov=POV \ub0b4\ubcf4\ub0b4\uae30
+function.exportsvg=SVG \ub0b4\ubcf4\ub0b4\uae30
+function.editwaypointname=\uacbd\uc720\uc9c0 \uc774\ub984 \uc218\uc815
+function.compress=\uacbd\ub85c \uc555\ucd95\ud558\uae30
+function.addtimeoffset=\uc624\ud504\uc14b \uc2dc\uac04 \ucd94\uac00
+function.addaltitudeoffset=\uc624\ud504\uc14b \uace0\ub3c4 \ucd94\uac00
+function.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658
+function.deletefieldvalues=\ud544\ub4dc\uac12 \uc0ad\uc81c
+function.findwaypoint=\uacbd\uc720\uc9c0 \ucc3e\uae30
+function.pastecoordinates=\uc0c8 \uc88c\ud45c \ub123\uae30
+function.charts=\ucc28\ud2b8
+function.show3d=3\ucc28\uc6d0 \ubcf4\uae30
+function.distances=\uac70\ub9ac
+function.fullrangedetails=\uc5f0\uacb0\uc120 \uc0c1\uc138 \uc815\ubcf4 \ubcf4\uae30
+function.setmapbg=\ubc30\uacbd \uc9c0\ub3c4 \uc9c0\uc815
+function.setkmzimagesize=KMZ \uadf8\ub9bc \ud06c\uae30 \uc9c0\uc815
+function.setpaths=\uc678\ubd80\ud504\ub85c\uadf8\ub7a8 \uc9c0\uc815
+function.getgpsies=gpsies\uc5d0\uc11c \ud2b8\ub799\ubaa9\ub85d \uc5bb\uae30
+function.uploadgpsies=gpsies\ub85c \ud2b8\ub799 \uc62c\ub9ac\uae30
+function.lookupsrtm=SRTM\uc5d0\uc11c \uace0\ub3c4 \ucc3e\uae30
+function.getwikipedia=\uc704\ud0a4\ud53c\ub514\uc544\uc5d0\uc11c \uadfc\ucc98 \uc815\ubcf4 \ucc3e\uae30
+function.searchwikipedianames=\uc774\ub984\uc73c\ub85c \uc704\ud0a4\ud53c\ub514\uc544 \uac80\uc0c9
+function.downloadosm=OSM \ub370\uc774\ud0c0 \ub2e4\uc6b4\ub85c\ub4dc\ud558\uae30
+function.duplicatepoint=\uc9c0\uc810 \ubcf5\uc0ac\ud558\uae30
+function.setcolours=\uc0c9\uc0c1 \uc9c0\uc815
+function.setlinewidth=\uc120 \ub113\uc774 \uc9c0\uc815
+function.setlanguage=\uc5b8\uc5b4 \uc9c0\uc815
+function.connecttopoint=\uc9c0\uc810\uc73c\ub85c \uc5f0\uacb0
+function.disconnectfrompoint=\uc9c0\uc810\uc5d0\uc11c \uc5f0\uacb0 \ub04a\uae30
+function.removephoto=\ub9ac\uc2a4\ud2b8\uc5d0\uc11c \uc0ac\uc9c4 \uc9c0\uc6b0\uae30
+function.correlatephotos=\uc2dc\uac04\uc815\ubcf4\ub85c \uc0ac\uc9c4 \uc5f0\uacb0\ud558\uae30
+function.rearrangephotos=\uc0ac\uc9c4 \uc7ac\ubc30\uc5f4\ud558\uae30
+function.rotatephotoleft=\uc67c\ucabd\uc73c\ub85c \uc0ac\uc9c4 \ud68c\uc804
+function.rotatephotoright=\uc624\ub978\ucabd\uc73c\ub85c \uc0ac\uc9c4 \ud68c\uc804
+function.photopopup=\ud31d\uc5c5 \uc0ac\uc9c4 \ubcf4\uae30
+function.ignoreexifthumb=exif \uc378\ub124\uc77c \ubb34\uc2dc\ud558\uae30
+function.loadaudio=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ud558\uae30
+function.removeaudio=\ub9ac\uc2a4\ud2b8\uc5d0\uc11c \uc18c\ub9ac\ud30c\uc77c \uc9c0\uc6b0\uae30
+function.correlateaudios=\uc2dc\uac04\uc815\ubcf4\ub85c \uc18c\ub9ac\ud30c\uc77c \uc5f0\uacb0\ud558\uae30
+function.playaudio=\uc18c\ub9ac\ud30c\uc77c \uc7ac\uc0dd
+function.stopaudio=\uc18c\ub9ac\ud30c\uc77c \uba48\ucda4
+function.help=\ub3c4\uc6c0\ub9d0
+function.showkeys=\ub2e8\ucd95\ud0a4 \ubcf4\uae30
+function.about=Prune \uc815\ubcf4
+function.checkversion=\uc0c8\ubc84\uc804\uc774 \uc788\ub294\uc9c0 \uac80\uc0ac
+function.saveconfig=\uc124\uc815 \uc800\uc7a5\ud558\uae30
+function.diskcache=\ub514\uc2a4\ud06c\uc5d0 \ub9f5 \uc800\uc7a5
+
+# Dialogs
+dialog.exit.confirm.title=Prune \uc885\ub8cc
+dialog.exit.confirm.text=\ub370\uc774\ud0c0\uac00 \uc800\uc7a5\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4. \uadf8\ub798\ub3c4 \ub098\uac00\uc2dc\uac8c\uc2b5\ub2c8\uae4c?
+dialog.openappend.title=\ub370\uc774\ud130 \ucd94\uac00\ud558\uae30
+dialog.openappend.text=\uc774\uc804\uc5d0 \ubd88\ub7ec\uc628 \ub370\uc774\ud0c0\ub85c \ucd94\uac00\ud558\uae30\uaca0\uc2b5\ub2c8\uae4c?
+dialog.deletepoint.title=\uc9c0\uc810 \uc0ad\uc81c
+dialog.deletepoint.deletephoto=\uc9c0\uc810\uc73c\ub85c \ucca8\ubd80\ub41c \uc0ac\uc9c4\uc744 \uc9c0\uc6b0\uae30\uaca0\uc2b5\ub2c8\uae4c?
+dialog.deletephoto.title=\uc0ac\uc9c4 \uc0ad\uc81c
+dialog.deletephoto.deletepoint=\uc0ac\uc9c4\uc5d0 \ucca8\ubd80\ub41c \uc9c0\uc810\uc744 \uc9c0\uc6b0\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
+dialog.openoptions.title=\uc124\uc815
+dialog.openoptions.filesnippet=\ud30c\uc77c\uc5d0\uc11c \ucd94\ucd9c\ud558\uae30
+dialog.load.table.field=\ud544\ub4dc
+dialog.load.table.datatype=\ub370\uc774\ud0c0 \ud0c0\uc785
+dialog.load.table.description=\uc124\uba85
+dialog.delimiter.label=\ud544\ub4dc \uad6c\ubd84 \uae30\ud638
+dialog.delimiter.comma=\ucf64\ub9c8 ,
+dialog.delimiter.tab=\ud0ed
+dialog.delimiter.space=\ube48\uce78
+dialog.delimiter.semicolon=\uc138\ubbf8\ucf5c\ub860 ;
+dialog.delimiter.other=\uae30\ud0c0
+dialog.openoptions.deliminfo.records=\uac1c\uc758 \ub808\ucf54\ub4dc
+dialog.openoptions.deliminfo.fields=\uac1c\uc758 \ud544\ub4dc\uac00 \uc788\ub294
+dialog.openoptions.deliminfo.norecords=\ub808\ucf54\ub4dc\uac00 \uc5c6\uc74c
+dialog.openoptions.altitudeunits=\uace0\ub3c4 \ub2e8\uc704
+dialog.open.contentsdoubled=\uc774 \ud30c\uc77c\uc740 \uac01 \uc9c0\uc810\uc774 2\ubc88\uc529 \uae30\ub85d\ub418\uc5b4 \uc788\uc2b5\ub2c8\ub2e4. /n\uac01 \uc9c0\uc810\uacfc \uac01 \uacbd\uc720\uc9c0\ub294 \ud55c\ubc88\uc529\ub9cc \uae30\ub85d\ub418\uc5b4\uc57c \ud569\ub2c8\ub2e4.
+dialog.selecttracks.intro=\ubd88\ub7ec\uc62c \uc9c0\uc810\uc774\ub098 \uc9c0\uc810\ub4e4\uc744 \uc120\ud0dd\ud558\uc138\uc694
+dialog.selecttracks.noname=\uc774\ub984 \uc5c6\uc74c
+dialog.jpegload.subdirectories=\ud558\uc704 \ud3f4\ub354\ub3c4 \ud3ec\ud568
+dialog.jpegload.loadjpegswithoutcoords=\uc88c\ud45c \uc815\ubcf4\uc5c6\ub294 \uc0ac\uc9c4\ub4e4\ub3c4 \ud3ec\ud568
+dialog.jpegload.loadjpegsoutsidearea=\uc9c0\uae08 \uc9c0\uc5ed\uc678\ubd80 \uc0ac\uc9c4\ub4e4\ub3c4 \ud3ec\ud568
+dialog.jpegload.progress.title=\uc0ac\uc9c4\uc744 \ubd88\ub7ec\uc624\uace0 \uc788\uc2b5\ub2c8\ub2e4.
+dialog.jpegload.progress=\uc0ac\uc9c4\uc774 \uac80\uc0c9\ub418\ub294 \ub3d9\uc548 \uae30\ub2e4\ub824 \uc8fc\uc138\uc694.
+dialog.gpsload.nogpsbabel=gpsbabel \ud504\ub85c\uadf8\ub7a8\uc744 \ucc3e\uc9c0 \ubabb\ud558\uc600\uc2b5\ub2c8\ub2e4. \uacc4\uc18d \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
+dialog.gpsload.device=\uc7a5\uce58 \uc774\ub984
+dialog.gpsload.format=\ud615\ud0dc
+dialog.gpsload.getwaypoints=\uacbd\uc720\uc9c0 \ubd88\ub7ec\uc624\uae30
+dialog.gpsload.gettracks=\ud2b8\ub809 \ubd88\ub7ec\uc624\uae30
+dialog.gpsload.save=\ud30c\uc77c\ub85c \uc800\uc7a5
+dialog.gpssend.sendwaypoints=\uacbd\uc720\uc9c0 \ubcf4\ub0b4\uae30
+dialog.gpssend.sendtracks=\ud2b8\ub809 \ubcf4\ub0b4\uae30
+dialog.gpssend.trackname=\ud2b8\ub809 \uc774\ub984
+dialog.saveoptions.title=\ud30c\uc77c \uc800\uc7a5
+dialog.save.fieldstosave=\ud544\ub4dc\uba85\uc73c\ub85c \uc800\uc7a5
+dialog.save.table.field=\ud544\ub4dc
+dialog.save.table.hasdata=\uc790\ub8cc\uac00 \uc788\uc74c
+dialog.save.table.save=\uc800\uc7a5
+dialog.save.headerrow=\uccab\ubc88\uc9f8 \uc904\ub3c4 \uc800\uc7a5
+dialog.save.coordinateunits=\uc88c\ud45c \ub2e8\uc704
+dialog.save.altitudeunits=\uace0\ub3c4 \ub2e8\uc704
+dialog.save.timestampformat=\uc2dc\uac04 \ud45c\ud604\ud615\uc2dd
+dialog.save.overwrite.title=\ud30c\uc77c\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4.
+dialog.save.overwrite.text=\ud30c\uc77c\uc774 \uc774\ubbf8 \uc788\uc2b5\ub2c8\ub2e4. \uadf8\ub798\ub3c4 \uc774 \ud30c\uc77c\uc5d0 \ub36e\uc5b4\uc50c\uc6b0\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
+dialog.save.notypesselected=\uc9c0\uc810\uc758 \ud615\uc2dd\uc774 \uc9c0\uc815\ub418\uc9c0 \uc54a\uc558\uc2b5\ub2c8\ub2e4.
+dialog.exportkml.text=\uc81c\ubaa9\uc73c\ub85c \uc800\uc7a5
+dialog.exportkml.altitude=\uace0\ub3c4 \uace0\uc815(\ube44\ud589)
+dialog.exportkml.kmz=kmz \ud30c\uc77c \uc0dd\uc131\uc2dc \uc555\ucd95\ud558\uae30
+dialog.exportkml.exportimages=kmz\ub85c \uc791\uc740 \uc0ac\uc9c4 \ub0b4\ubcf4\ub0b4\uae30
+dialog.exportkml.trackcolour=\ud2b8\ub809 \uc0c9
+dialog.exportgpx.name=\uc774\ub984
+dialog.exportgpx.desc=\uc124\uba85
+dialog.exportgpx.includetimestamps=\uc2dc\uac04 \ud3ec\ud568\ud558\uae30
+dialog.exportgpx.copysource=xml \ud30c\uc77c \ubcf5\uc0ac\ud558\uae30
+dialog.exportpov.text=POV\ub85c \ub0b4\ubcf4\ub0bc \ubcc0\uc218\ub97c \uc801\uc5b4\uc8fc\uc138\uc694.
+dialog.exportpov.font=\uae00\uaf34
+dialog.exportpov.camerax=\uce74\uba54\ub77c\uc758 X \uc88c\ud45c
+dialog.exportpov.cameray=\uce74\uba54\ub77c\uc758 Y \uc88c\ud45c
+dialog.exportpov.cameraz=\uce74\uba54\ub77c\uc758 Z \uc88c\ud45c
+dialog.exportpov.modelstyle=\ubaa8\ub378 \uc2a4\ud0c0\uc77c
+dialog.exportpov.ballsandsticks=\ub9c9\ub300\uae30\uc640 \uacf5
+dialog.exportpov.tubesandwalls=\ubcbd\uacfc \ud29c\ube0c
+dialog.exportpov.warningtracksize=\uc774 \ud2b8\ub799\uc740 \uc9c0\uc810\uc774 \ub108\ubb34 \ub9ce\uc544 Java3D\uac00 \ud45c\ud604 \ubabb\ud560 \uc218\ub3c4 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uacc4\uc18d \ud558\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
+dialog.exportsvg.text=SVG\ub85c \ub0b4\ubcf4\ub0bc \ud30c\ub77c\ubbf8\ud130\ub97c \uc120\ud0dd\ud558\uc138\uc694
+dialog.exportsvg.phi=\ubc29\uc704\uac01(\u03d5)
+dialog.exportsvg.theta=\uace0\ub3c4(\u03b8)
+dialog.exportsvg.gradients=\uc250\uc774\ub529\uc5d0 \uadf8\ub798\ub514\uc5b8\ud2b8 \uc0ac\uc6a9\ud558\uae30
+dialog.pointtype.desc=\uc9c0\uc810 \ud615\uc2dd \uc800\uc7a5\ud558\uae30
+dialog.pointtype.track=\ud2b8\ub799 \uc9c0\uc810
+dialog.pointtype.waypoint=\uacbd\uc720 \uc9c0\uc810
+dialog.pointtype.photo=\uc0ac\uc9c4 \uc9c0\uc810
+dialog.pointtype.audio=\uc18c\ub9ac \uc9c0\uc810
+dialog.pointtype.selection=\uc120\ud0dd \uc601\uc5ed\ub9cc
+dialog.confirmreversetrack.title=\ubc18\uc804\uc778\uc9c0 \ud655\uc778
+dialog.confirmreversetrack.text=\uc774 \ud2b8\ub799\uc740 \ubc18\uc804\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe8\uc744 \uc218\ub3c4 \uc788\ub294\uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774\uc601\uc5ed\uc744 \ubc18\uc804\uc2dc\ud0a4\uc2dc\ub824\ub098\uc694?
+dialog.confirmcutandmove.title=\uc790\ub974\uace0 \uc62e\uae30\uae30 \ud655\uc778
+dialog.confirmcutandmove.text=\uc774 \ud2b8\ub799\uc740 \uc774\ub3d9\ud6c4 \uc21c\uc11c\uac00 \ubc14\uafe7\uc744 \uc218\ub3c4 \uc788\ub294 \uc2dc\uac04\uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc2b5\ub2c8\ub2e4. /n \uadf8\ub798\ub3c4 \uc774 \uc601\uc5ed\uc744 \uc62e\uae30\uc2dc\ub824\ub098\uc694?
+dialog.interpolate.title=\uc0bd\uc785\ud55c \uc9c0\uc810
+dialog.interpolate.parameter.text=\uc120\ud0dd\ud55c \uc9c0\uc810 \uc0ac\uc774\uc5d0 \ub123\uc744 \uc9c0\uc810\uc758 \uc218
+dialog.undo.title=\uc791\uc5c5 \ub418\ub3cc\ub9ac\uae30
+dialog.undo.pretext=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694
+dialog.undo.none.title=\ub418\ub3cc\ub9b4 \uc218 \uc5c6\uc5b4\uc694
+dialog.undo.none.text=\ub418\ub3cc\ub9b4 \uc791\uc5c5\uc774 \uc5c6\uc5b4\uc694!
+dialog.clearundo.title=\ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uae30
+dialog.clearundo.text=\uc815\ub9d0\ub85c \ub418\ub3cc\ub9ac\uae30 \ubaa9\ub85d \uc9c0\uc6b0\uc2e4\uac74\uac00\uc694? /n \ubaa8\ub4e0 \ub418\ub3cc\ub9ac\uae30 \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc9c4\ub2e4\uad6c\uc694!
+dialog.pointedit.title=\uc9c0\uc810 \uc218\uc815\ud558\uae30
+dialog.pointedit.text=\uc218\uc815\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd\ud558\uc2dc\uace0, \uc218\uc815\ud558\uae30 \ubc84\ud2bc\uc744 \uc0ac\uc6a9\ud558\uc138\uc694.
+dialog.pointedit.table.field=\ud544\ub4dc
+dialog.pointedit.table.value=\uac12
+dialog.pointedit.table.changed=\uc218\uc815\ub428
+dialog.pointedit.changevalue.text=\uc774 \ud544\ub4dc\uc5d0 \uc0c8 \uac12\uc744 \uc785\ub825\ud574\uc8fc\uc138\uc694
+dialog.pointedit.changevalue.title=\ud544\ub4dc \uc218\uc815
+dialog.pointnameedit.name=\uacbd\uc720\uc9c0 \uc774\ub984
+dialog.pointnameedit.uppercase=\ub300\ubb38\uc790\ub85c
+dialog.pointnameedit.lowercase=\uc18c\ubb38\uc790\ub85c
+dialog.pointnameedit.sentencecase=\uccab\uae00\uc790\ub9cc \ub300\ubb38\uc790\ub85c
+dialog.addtimeoffset.add=\uc2dc\uac04 \ucd94\uac00
+dialog.addtimeoffset.subtract=\uc2dc\uac04 \ube7c\uae30
+dialog.addtimeoffset.days=\uc77c
+dialog.addtimeoffset.hours=\uc2dc
+dialog.addtimeoffset.minutes=\ubd84
+dialog.addtimeoffset.notimestamps=\uc2dc\uac04 \uc815\ubcf4\ub97c \ud3ec\ud568\ud558\uace0 \uc788\uc9c0\uc54a\uc544\uc11c \uc2dc\uac04\uc624\ud504\uc14b\uc744 \ucd94\uac00\ud560 \uc218 \uc5c6\ub124\uc694.
+dialog.findwaypoint.intro=\uacbd\uc720\uc9c0 \uc774\ub984\uc744 \uc801\uc73c\uc138\uc694.
+dialog.findwaypoint.search=\ucc3e\uae30
+dialog.saveexif.title=Exif \uc800\uc7a5
+dialog.saveexif.intro=\uccb4\ud06c\ubc15\uc2a4\ub97c \uc774\uc6a9\ud574\uc11c \uc800\uc7a5\ud560 \uc0ac\uc9c4\uc744 \uc120\ud0dd\ud558\uc138\uc694.
+dialog.saveexif.nothingtosave=\uc88c\ud45c \ub370\uc774\ud0c0\uac00 \ubc14\ub00c\uc9c0 \uc54a\uc558\uc5b4\uc694. \uc800\uc7a5\ud560 \uac83\uc774 \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694.
+dialog.saveexif.noexiftool=exiftool \ud504\ub85c\uadf8\ub7a8\uc744 \ucc3e\uc9c0 \ubabb\ud588\uc5b4\uc694. \uacc4\uc18d \ud558\uc2dc\uaca0\uc5b4\uc694?
+dialog.saveexif.table.photoname=\uc0ac\uc9c4 \uc774\ub984
+dialog.saveexif.table.status=\uc0c1\ud0dc
+dialog.saveexif.table.save=\uc800\uc7a5
+dialog.saveexif.photostatus.connected=\uc9c0\uc810\uacfc \uc5f0\uacb0\ub428
+dialog.saveexif.photostatus.disconnected=\uc9c0\uac80\uacfc\uc758 \uc5f0\uacb0 \ub04a\uae40
+dialog.saveexif.photostatus.modified=\uc218\uc815\ub428
+dialog.saveexif.overwrite=\ud30c\uc77c \ub36e\uc5b4\uc4f0\uae30
+dialog.saveexif.force=\uc624\ub958\ubb34\uc2dc\ud558\uae30
+dialog.charts.xaxis=x \ucd95
+dialog.charts.yaxis=y \ucd95
+dialog.charts.output=\ucd9c\ub825
+dialog.charts.screen=\ud654\uba74\uc73c\ub85c \ucd9c\ub825
+dialog.charts.svg=SVG\ud30c\uc77c\ub85c \ucd9c\ub825
+dialog.charts.svgwidth=SVG \ud3ed
+dialog.charts.svgheight=SVG \ub192\uc774
+dialog.charts.needaltitudeortimes=\ucc28\ud2b8\ub9cc\ub4e4\ub54c \ud2b8\ub809\uc5d0\ub294 \uace0\ub3c4\uc640 \uc2dc\uac04 \uc815\ubcf4\uac00 \ud544\uc694\ud574\uc694.
+dialog.charts.gnuplotnotfound=\uc54c\ub824\uc900\uacbd\ub85c\uc5d0\uc11c gnuplot\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc5b4\uc694.
+dialog.distances.intro=\uc9c0\uc810\uc0ac\uc774\uc758 \uc9c1\uc120 \uac70\ub9ac
+dialog.distances.column.from=\uc9c0\uac80\uc5d0\uc11c
+dialog.distances.column.to=\uc9c0\uc810\uae4c\uc9c0
+dialog.distances.currentpoint=\uc9c0\uae08 \uc9c0\uc810
+dialog.distances.toofewpoints=\uacbd\uc720\uc9c0 \uc0ac\uc774\uc758 \uac70\ub9ac\ub97c \uc54c\ub824\uba74 \uacbd\uc720\uc9c0\uac00 2\uac1c\uc774\uc0c1 \ud544\uc694\ud574\uc694.
+dialog.fullrangedetails.intro=\uc120\ud0dd\ub41c \ubc94\uc704\uc758 \uc790\uc138\ud55c \uc815\ubcf4
+dialog.setmapbg.intro=\ub9f5 \uc18c\uc2a4 \ud558\ub098\ub97c \uace0\ub974\uac70\ub098 \uc0c8\ub85c \ucd94\uac00\ud558\uc138\uc694.
+dialog.addmapsource.title=\uc0c8 \ub9f5 \uc18c\uc2a4 \ucd94\uac00\ud558\uae30
+dialog.addmapsource.sourcename=\uc18c\uc2a4 \uc774\ub984
+dialog.addmapsource.layer1url=\uccab \ub808\uc774\uc5b4\uc758 URL
+dialog.addmapsource.layer2url=\ub450\ubc88\uc9f8 \ub808\uc774\uc5b4\uc758 URL
+dialog.addmapsource.maxzoom=\ucd5c\uace0 \ud655\ub300
+dialog.addmapsource.cloudstyle=\uc2a4\ud0c0\uc77c \uc218
+dialog.addmapsource.noname=\uc774\ub984 \uc5c6\uc74c
+dialog.gpsies.column.name=\ud2b8\ub809 \uc774\ub984
+dialog.gpsies.column.length=\uae38\uc774
+dialog.gpsies.description=\uc124\uba85
+dialog.gpsies.nodescription=\uc124\uba85 \uc5c6\uc74c
+dialog.gpsies.nonefound=\ud2b8\ub799\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc74c
+dialog.gpsies.username=Gpsies username
+dialog.gpsies.password=Gpsies \ube44\ubc00\ubc88\ud638
+dialog.gpsies.keepprivate=\ud2b8\ub799 \uac1c\uc778\uc815\ubcf4\ubcf4\ud638
+dialog.gpsies.confirmopenpage=\uc5c5\ub85c\ub4dc\ud55c \ud2b8\ub799\uc744 \uc6f9\ud398\uc774\uc9c0\uc5d0\uc11c \ubcf4\uc2dc\uaca0\uc5b4\uc694?
+dialog.gpsies.activities=\ud65c\ub3d9 \ud615\ud0dc
+dialog.gpsies.activity.trekking=\ub3c4\ubcf4\uc5ec\ud589
+dialog.gpsies.activity.walking=\uac77\uae30
+dialog.gpsies.activity.jogging=\ub2ec\ub9ac\uae30
+dialog.gpsies.activity.biking=\uc790\uc804\uac70\ud0c0\uae30
+dialog.gpsies.activity.motorbiking=\uc624\ud1a0\ubc14\uc774\ud0c0\uae30
+dialog.gpsies.activity.snowshoe=\ub208\uae38\uac77\uae30
+dialog.gpsies.activity.sailing=\ubc30\ud0c0\uae30
+dialog.gpsies.activity.skating=\uc2a4\ucf00\uc774\ud2b8\ud0c0\uae30
+dialog.wikipedia.column.name=\uac8c\uc2dc\ubb3c \uc774\ub984
+dialog.wikipedia.column.distance=\uac70\ub9ac
+dialog.correlate.notimestamps=\uc9c0\uc810 \ub370\uc774\ud130\uc5d0 \uc2dc\uac04\uc815\ubcf4\uac00 \uc5c6\uc5b4\uc11c \uc0ac\uc9c4\uc744 \uc5f0\uacb0 \ud560 \uc218 \uc5c6\uc5b4\uc694.
+dialog.correlate.nouncorrelatedphotos=\uc5f0\uacb0\uc774 \uc548\ub41c \uc0ac\uc9c4\uc774 \uc5c6\ub124\uc694. /n \uacc4\uc18d \ud558\uc2e4\uac70\uc8e0?
+dialog.correlate.photoselect.intro=\ub300\ud45c\uc0ac\uc9c4\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc0ac\uc9c4\uc744 \ud558\ub098 \uc120\ud0dd\ud558\uc138\uc694.
+dialog.correlate.select.photoname=\uc0ac\uc9c4\uc774\ub984
+dialog.correlate.select.timediff=\uc0ac\uae34 \ucc28\uc774
+dialog.correlate.select.photolater=\uc0ac\uc9c4 \uc2dc\uac04\uc774 \uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \ub290\ub9bc
+dialog.correlate.options.tip=\ub3c4\uc6c0: \ucd5c\uc18c\ud55c \ud55c\uac1c\uc758 \uc544\uc774\ud0ec(\uc0ac\uc9c4,\uc18c\ub9ac)\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud558\uba74, \ud0c0\uc784 \uc624\ud504\uc14b\uc744 \uacc4\uc0b0\ud560 \uc218 \uc788\uc5b4\uc694.
+dialog.correlate.options.intro=\uc790\ub3d9 \uc5f0\uacb0\uc744 \uc704\ud574 \uc635\uc158\uc744 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
+dialog.correlate.options.offsetpanel=\uc2dc\uac04 \uc624\ud504\uc14b
+dialog.correlate.options.offset=\uc624\ud504\uc14b
+dialog.correlate.options.offset.hours=\uc2dc\uac04
+dialog.correlate.options.offset.minutes=\ubd84
+dialog.correlate.options.offset.seconds=\ucd08
+dialog.correlate.options.photolater=\uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \uc0ac\uc9c4\uc2dc\uac04\uc774 \ub4a4\uc784
+dialog.correlate.options.pointlaterphoto=\uc0ac\uc9c4\uc2dc\uac04\ubcf4\ub2e4 \uc9c0\uc810\uc2dc\uac04\uc774 \ub4a4\uc784
+dialog.correlate.options.audiolater=\uc9c0\uc810\uc2dc\uac04\ubcf4\ub2e4 \uc18c\ub9ac\uc2dc\uac04\uc774 \ub4a4\uc784
+dialog.correlate.options.pointlateraudio=\uc18c\ub9ac\uc2dc\uac04\ubcf4\ub2e4 \uc9c0\uc810\uc2dc\uac04\uc774 \ub4a4\uc784
+dialog.correlate.options.limitspanel=\uc5f0\uacb0 \uc81c\ud55c
+dialog.correlate.options.notimelimit=\uc2dc\uac04 \uc81c\ud55c \uc5c6\uc74c
+dialog.correlate.options.timelimit=\uc2dc\uac04 \uc81c\ud55c
+dialog.correlate.options.nodistancelimit=\uac70\ub9ac\uc81c\ud55c \uc5c6\uc74c
+dialog.correlate.options.distancelimit=\uac70\ub9ac\uc81c\ud55c
+dialog.correlate.options.correlate=\uc5f0\uacb0
+dialog.correlate.alloutsiderange=\ubaa8\ub4e0 \uc0ac\uc9c4\ub4e4\uc774 \ud2b8\ub809\uc758 \uc2dc\uac04 \ubc94\uc704\uc548\uc5d0 \uc788\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4./n\uc624\ud504\uc14b\uc744 \uc218\uc815\ud574\ubcf4\uc2dc\uac70\ub098 \ucd5c\uc18c\ud55c \ud55c \uc7a5\uc758 \uc0ac\uc9c4\uc744 \uc9c1\uc811 \uc5f0\uacb0\ud574\ubcf4\uc138\uc694.
+dialog.correlate.filetimes=\ud30c\uc77c\uc758 \uc2dc\uac04\uc774 \ub098\ud0c0\ub0b4\ub294 :
+dialog.correlate.filetimes2=\uc18c\ub9ac\ud30c\uc77c\uc758
+dialog.correlate.correltimes=\uc5f0\uacb0\uc744 \uc0ac\uc6a9\ud558\uc138\uc694
+dialog.correlate.timestamp.beginning=\uc2dc\uc791
+dialog.correlate.timestamp.middle=\uc911\uac04
+dialog.correlate.timestamp.end=\ub05d
+dialog.correlate.audioselect.intro=\ud0c0\uc784\uc624\ud504\uc14b\uc73c\ub85c \uc0ac\uc6a9\ud560 \uc5f0\uacb0\ub41c \uc18c\ub9ac\ub4e4\uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc138\uc694
+dialog.correlate.select.audioname=\uc18c\ub9ac \uc774\ub984
+dialog.correlate.select.audiolater=\uc18c\ub9ac \ud6c4\uc5d0
+dialog.rearrangephotos.desc=\uc0ac\uc9c4 \uc9c0\uc810\ub4e4\uc744 \uc5b4\ub5bb\uac8c \uc815\ub82c\ud560 \uac74\uc9c0 \uc120\ud0dd\ud574\uc8fc\uc138\uc694.
+dialog.rearrangephotos.tostart=\uc2dc\uc791\uc73c\ub85c
+dialog.rearrangephotos.toend=\ub05d\uc73c\ub85c
+dialog.rearrangephotos.nosort=\uc815\ub82c\ud558\uc9c0 \uc54a\uae30
+dialog.rearrangephotos.sortbyfilename=\ud30c\uc77c \uc774\ub984\uc73c\ub85c \uc815\ub82c
+dialog.rearrangephotos.sortbytime=\uc2dc\uac04\uc73c\ub85c \uc815\ub82c
+dialog.compress.nonefound=\uc9c0\uc810 \ub370\uc774\ud130\uac00 \uc81c\uac70\ub420 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.
+dialog.compress.closepoints.title=\uc8fc\ubcc0 \ud3ec\uc778\ud2b8 \uc81c\uac70
+dialog.compress.closepoints.paramdesc=\ud655\uc7a5 \uacc4\uc218
+dialog.compress.wackypoints.title=\uc5c9\ub6b1\ud558\uac70\ub098 \uc0ac\uc6a9\ud558\uc9c0 \uc54a\ub294 \uc9c0\uc810 \uc81c\uac70
+dialog.compress.wackypoints.paramdesc=\uac70\ub9ac \uacc4\uc218
+dialog.compress.singletons.title=\ud558\ub098\uc529 \uc81c\uac70
+dialog.compress.singletons.paramdesc=\uac70\ub9ac \uacc4\uc218
+dialog.compress.duplicates.title=\ubcf5\uc0ac\ub41c\uac70 \uc81c\uac70
+dialog.compress.summarylabel=\uc0ad\uc81c\ud560 \uc9c0\uc810
+dialog.pastecoordinates.desc=\uc790\ud45c\ub97c \ub123\uc73c\uc138\uc694
+dialog.pastecoordinates.coords=\uc88c\ud45c
+dialog.pastecoordinates.nothingfound=\uc88c\ud45c\ub97c \ud655\uc778\ud558\uc2dc\uace0 \ub2e4\uc2dc \uc2dc\ub3c4\ud574 \ubcf4\uc138\uc694.
+dialog.help.help=http://activityworkshop.net/software/prun
+dialog.about.version=\ubc84\uc804
+dialog.about.build=\ube4c\ub4dc
+dialog.about.summarytext1=Prune\uc740 GPS\uc218\uc2e0\uae30\uc5d0\uc11c \uc704\uce58 \uc815\ubcf4\ub97c \ubc1b\uace0, \ud654\uba74\uc5d0 \ubcf4\uc5ec\uc8fc\uace0, \uc218\uc815\ud558\uac8c \ud574\uc8fc\ub294 \ud504\ub85c\uadf8\ub7a8\uc785\ub2c8\ub2e4.
+dialog.about.summarytext2=\uc774 \ud504\ub85c\uadf8\ub7a8\uc740 \uc790\uc720\ub86d\uac8c, \uac1c\ubc29\uc801\uc73c\ub85c, \uadf8\ub9ac\uace0 \uc804\uc138\uacc4\uc801\uc73c\ub85c \uc0ac\uc6a9\ud558\uace0 \uac1c\uc120\ud558\uae30 \uc704\ud574 Gnu GPL \uc5d0 \ub530\ub77c\uc11c \ubc30\ud3ec\ub429\ub2c8\ub2e4. <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.translatedby=\ud55c\uad6d\uc5b4 Hooau
+dialog.about.systeminfo=\uc2dc\uc2a4\ud15c \uc815\ubcf4
+dialog.about.systeminfo.os=\uc6b4\uc601\uccb4\uc81c
+dialog.about.systeminfo.java=\uc790\ubc14 \ub7f0\ud0c0\uc784
+dialog.about.systeminfo.java3d=Java3D\uac00 \uc124\uce58\ub418\uc5c8\uc74c
+dialog.about.systeminfo.povray=Povray\uac00 \uc124\uce58\ub418\uc5c8\uc74c
+dialog.about.systeminfo.exiftool=Exiftool\uc774 \uc124\uce58\ub418\uc5c8\uc74c
+dialog.about.systeminfo.gpsbabel=Gpsbabel\uc774 \uc124\uce58\ub418\uc5c8\uc74c
+dialog.about.systeminfo.gnuplot=Gnuplot\uc774 \uc124\uce58\ub418\uc5c8\uc74c
+dialog.about.systeminfo.exiflib=Exif \ub77c\uc774\ube0c\ub7ec\ub9ac
+dialog.about.systeminfo.exiflib.internal=\ub0b4\uc7a5
+dialog.about.systeminfo.exiflib.internal.failed=\ub0b4\uc7a5(\ucc3e\uc9c0\ubabb\ud568)
+dialog.about.systeminfo.exiflib.external=\uc678\uc7a5
+dialog.about.systeminfo.exiflib.external.failed=\uc678\uc7a5(\ucc3e\uc9c0\ubabb\ud568)
+dialog.about.yes=\uc608
+dialog.about.no=\uc544\ub2c8\uc624
+dialog.about.credits=\uc5d0\uac8c \uac10\uc0ac\ub97c
+dialog.about.credits.code=Prune \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900
+dialog.about.credits.exifcode=Exif \ucf54\ub4dc\ub97c \uc791\uc131\ud574\uc900
+dialog.about.credits.icons=\uc77c\ubd80 \uc544\uc774\ucf58\uc744 \uac00\uc838\uc628
+dialog.about.credits.translators=\ubc88\uc5ed\uc790\ub4e4
+dialog.about.credits.translations=\ubc88\uc5ed\uc5d0 \ub3c4\uc6c0\uc744 \uc900
+dialog.about.credits.devtools=\uac1c\ubc1c \ub3c4\uad6c\ub4e4
+dialog.about.credits.othertools=\ub2e4\ub978 \ub3c4\uad6c\ub4e4
+dialog.about.credits.thanks=\uac10\uc0ac\ud569\ub2c8\ub2e4.
+dialog.about.readme=\uc77d\uc5b4\uc8fc\uc138\uc694
+dialog.checkversion.error=\ubc84\uc804\uc774 \ud655\uc778\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694./n \uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.
+dialog.checkversion.uptodate=\ub2f9\uc2e0\uc740 Prune\uc758 \ucd5c\uc2e0 \ubc84\uc804\uc744 \uc0ac\uc6a9\ud558\uace0 \uacc4\uc2ed\ub2c8\ub2e4.
+dialog.checkversion.newversion1=Prune\uc758 \uc0c8 \ubc84\uc804\uc744 \uc9c0\uae08 \uc0ac\uc6a9\ud560 \uc218 \uc788\uaca0\ub124\uc694. \ucd5c\uc2e0\ubc84\uc804\uc740
+dialog.checkversion.newversion2=\ubc84\uc804\uc785\ub2c8\ub2e4.
+dialog.checkversion.releasedate1=\uc0c8 \ubc84\uc804\uc740
+dialog.checkversion.releasedate2=\uc5d0 \ubc30\ud3ec\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
+dialog.checkversion.download=\uc0c8 \ubc84\uc804\uc744 \ub2e4\uc6b4\ubc1b\uace0 \uc2f6\uc73c\uc138\uc694? \uadf8\ub7fc \uc544\ub798 URL\ub85c \uc640\uc8fc\uc138\uc694. /n http://activityworkshop.net/software/prune/download.html.
+dialog.keys.intro=\ub9c8\uc6b0\uc2a4\ub97c \uc0ac\uc6a9\ud558\uc9c0 \ub9c8\uc2dc\uace0 \uc544\ub798 \ub2e8\ucd95\ud0a4\ub97c \uc0ac\uc6a9\ud574\ubcf4\uc138\uc694.
+dialog.keys.keylist=<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.keys.macmodifier=Command
+dialog.saveconfig.desc=\uc544\ub798 \uc124\uc815\ub4e4\uc740 \uc124\uc815\ud30c\uc77c\uc5d0 \uc800\uc7a5\ud560 \uc218 \uc788\uc5b4\uc694:
+dialog.saveconfig.prune.trackdirectory=\ud2b8\ub809 \ud3f4\ub354
+dialog.saveconfig.prune.photodirectory=\uc0ac\uc9c4 \ud3f4\ub354
+dialog.saveconfig.prune.languagecode=\uc5b8\uc5b4\ucf54\ub4dc (KO)
+dialog.saveconfig.prune.languagefile=\uc5b8\uc5b4\ud30c\uc77c
+dialog.saveconfig.prune.gpsdevice=GPS\uc7a5\uce58
+dialog.saveconfig.prune.gpsformat=GPS \ud615\uc2dd
+dialog.saveconfig.prune.povrayfont=Povray \uae00\uaf34
+dialog.saveconfig.prune.metricunits=\ubbf8\ud130\ubc95 \ub2e8\uc704\ub97c \uc0ac\uc6a9\ud558\uc2dc\ub098\uc694?
+dialog.saveconfig.prune.gnuplotpath=gnjuplot \uacbd\ub85c
+dialog.saveconfig.prune.gpsbabelpath=gpsbabel \uacbd\ub85c
+dialog.saveconfig.prune.exiftoolpath=exiftool \uacbd\ub85c
+dialog.saveconfig.prune.mapsource=\uc120\ud0dd\ub41c \uc9c0\ub3c4 \uc704\uce58
+dialog.saveconfig.prune.mapsourcelist=\uc9c0\ub3c4 \uc18c\uc2a4
+dialog.saveconfig.prune.diskcache=\uc9c0\ub3c4 \uce90\uc2dc
+dialog.saveconfig.prune.kmzimagewidth=KMZ \uc774\ubbf8\uc9c0 \ub113\uc774
+dialog.saveconfig.prune.kmzimageheight=KMZ \uc774\ubbf8\uc9c0 \ub192\uc774
+dialog.saveconfig.prune.colourscheme=\uc0c9 \uad6c\uc131
+dialog.saveconfig.prune.linewidth=\ud2b8\ub799\uc120 \ub450\uaed8
+dialog.saveconfig.prune.kmltrackcolour=KML \ud2b8\ub799 \uc0c9
+dialog.setpaths.intro=\uc678\ubd80 \ud504\ub85c\uadf8\ub7a8\uc758 \uacbd\ub85c\ub97c \uc120\ud0dd\ud560 \uc218 \uc788\uc5b4\uc694.
+dialog.setpaths.found=\uacbd\ub85c\ub97c \ucc3e\uc73c\uc168\ub098\uc694?
+dialog.addaltitude.noaltitudes=\uc120\ud0dd\ub41c \ubc94\uc704\uc5d0 \uace0\ub3c4\uac00 \ud3ec\ud568\ub418\uc5b4\uc788\uc9c0 \uc54a\ub124\uc694.
+dialog.addaltitude.desc=\ucd94\uac00\ud560 \uace0\ub3c4 \uc624\ud504\uc14b
+dialog.lookupsrtm.overwritezeros=\uace0\ub3c4 \uac12\uc744 0\uc73c\ub85c \ub36e\uc5b4\uc4f0\uc2dc\uaca0\uc2b5\ub2c8\uae4c?
+dialog.setcolours.intro=\ubc14\uafc0 \uc0c9\uc73c\ub85c \uc0c9 \ud328\uce58\uc5d0\uc11c \ud074\ub9ad\ud558\uc138\uc694.
+dialog.setcolours.background=\ubc30\uacbd
+dialog.setcolours.borders=\ud14c\ub450\ub9ac
+dialog.setcolours.lines=\uc120
+dialog.setcolours.primary=1\ucc28
+dialog.setcolours.secondary=2\ucc28
+dialog.setcolours.point=\uc9c0\uc810
+dialog.setcolours.selection=\uc120\ud0dd
+dialog.setcolours.text=\uae00\uc790
+dialog.colourchooser.title=\uc0c9\uc0c1 \uc120\ud0dd
+dialog.colourchooser.red=\ube68\uac15
+dialog.colourchooser.green=\ub179\uc0c9
+dialog.colourchooser.blue=\ud30c\ub791
+dialog.setlanguage.firstintro=\ud3ec\ud568\ub41c \uc5b8\uc5b4\ub4e4 \uc911 \ud558\ub098\ub97c \uc120\ud0dd\ud558\uc2e4 \uc218 \uc788\uc5b4\uc694,<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.language=\uc5b8\uc5b4
+dialog.setlanguage.languagefile=\uc5b8\uc5b4 \ud30c\uc77c
+dialog.setlanguage.endmessage=\ubcc0\uacbd\ub41c \uc5b8\uc5b4\ub97c \uc801\uc6a9\ud558\uc2e4\ub824\uba74/n\uc124\uc815\uc744 \uc800\uc7a5\ud558\uace0 Prune\uc744 \uc7ac\uc2dc\uc791\ud558\uc138\uc694.
+dialog.diskcache.save=\ub514\uc2a4\ud06c\ub85c \uc9c0\ub3c4 \uc774\ubbf8\uc9c0\ub97c \uc800\uc7a5
+dialog.diskcache.dir=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac
+dialog.diskcache.createdir=\ub514\ub809\ud1a0\ub9ac \ub9cc\ub4e4\uae30
+dialog.diskcache.nocreate=\uce90\uc2dc \ub514\ub809\ud1a0\ub9ac\uac00 \ub9cc\ub4e4\uc5b4\uc9c0\uc9c0 \uc54a\uc558\uc5b4\uc694.
+dialog.deletefieldvalues.intro=\ud604\uc7ac \ubc94\uc704\uc5d0\uc11c \uc0ad\uc81c\ud560 \ud544\ub4dc\ub97c \uc120\ud0dd
+dialog.setlinewidth.text=\ud2b8\ub799\uc744 \uadf8\ub9b4 \uc120\uc758 \ub450\uaed8\ub97c \uc801\uc73c\uc138\uc694(1-4)
+dialog.downloadosm.desc=\ud2b9\uc815\uc9c0\uc5ed\uc758 raw OSM \ub370\uc774\ud130\ub97c \ub2e4\uc6b4\ub85c\ub4dc \ud558\uc2dc\uaca0\uc5b4\uc694?
+dialog.searchwikipedianames.search=\ucc3e\uae30
+
+# 3d window
+dialog.3d.title=Prune 3D \ubcf4\uae30
+dialog.3d.altitudefactor=\uace0\ub3c4 \uacfc\uc7a5 \uacc4\uc218
+dialog.3dlines.title=Prune \uaca9\uc790\uc120
+dialog.3dlines.empty=\uaca9\uc790\uc120 \uc5c6\uc774 \ud45c\uc2dc\ud558\uae30
+dialog.3dlines.intro=3D \ubcf4\uae30\ub97c \uc704\ud55c \uaca9\uc790\uc120\uc785\ub2c8\ub2e4.
+
+# Confirm messages
+confirm.loadfile=\ud30c\uc77c\uc5d0\uc11c \uc790\ub8cc\ub97c \ubd88\ub7ec\uc654\uc5b4\uc694.
+confirm.save.ok1=
+confirm.save.ok2=\uc9c0\uc810\uc774 \uc798 \uc800\uc7a5\ub418\uc5c8\uc5b4\uc694.
+confirm.deletepoint.single=\uc9c0\uc810\uc774 \uc81c\uac70\ub418\uc5c8\uc5b4\uc694.
+confirm.deletepoint.multi=\uc9c0\uc810\ub4e4\uc774 \uc81c\uc5b4\ub418\uc5c8\uc5b4\uc694.
+confirm.point.edit=\uc9c0\uc810\uc774 \uc218\uc815\ub418\uc5c8\uc5b4\uc694.
+confirm.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84\ub4e4\uc774 \ubcd1\ud569\ub418\uc5c8\uc5b4\uc694.
+confirm.reverserange=\ubc94\uc704\uac00 \ubc18\uc804\ub418\uc5c8\uc5b4\uc694.
+confirm.addtimeoffset=\ud0c0\uc784 \uc624\ud504\uc14b\uc774 \ucd94\uac00\ub418\uc5c8\uc5b4\uc694.
+confirm.addaltitudeoffset=\uace0\ub3c4 \uc624\ud504\uc14b\uc774 \ucd94\uac00\ub418\uc5c8\uc5b4\uc694.
+confirm.rearrangewaypoints=\uacbd\uc720\uc9c0\uac00 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694
+confirm.rearrangephotos=\uc0ac\uc9c4\ub4e4\uc774 \uc7ac\uc815\ub82c\ub418\uc5c8\uc5b4\uc694.
+confirm.cutandmove=\uc62e\uaca8\uc84c\uc5b4\uc694.
+confirm.convertnamestotimes=\uacbd\uc720\uc9c0 \uc774\ub984\ub4e4\uc774 \ubcc0\ud658\ub418\uc5c8\uc5b4\uc694.
+confirm.saveexif.ok1=\uc800\uc7a5\ub428
+confirm.saveexif.ok2=\uc0ac\uc9c4 \ud30c\uc77c\ub4e4
+confirm.undo.single=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
+confirm.undo.multi=\uac1c \uc2e4\ud589\ucde8\uc18c \ub3d9\uc791
+confirm.jpegload.single=\uc0ac\uc9c4\uc774 \ucd94\uac00\ub428
+confirm.jpegload.multi=\uc0ac\uc9c4\ub4e4\uc774 \ucd94\uac00\ub428
+confirm.media.connect=\ubbf8\ub514\uc544 \ud30c\uc77c \uc5f0\uacb0\ub428
+confirm.photo.disconnect=\uc0ac\uc9c4 \uc5f0\uacb0 \ub04a\uae40
+confirm.audio.disconnect=\uc18c\ub9ac \uc5f0\uacb0 \ub04a\uae40
+confirm.media.removed=\uc0ad\uc81c\ub428
+confirm.correlatephotos.single=\uc0ac\uc9c4 \uc5f0\uacb0\ub428
+confirm.correlatephotos.multi=\uc0ac\uc9c4\ub4e4 \uc5f0\uacb0\ub428
+confirm.createpoint=\uc9c0\uc810 \uc0dd\uc131\ub428
+confirm.rotatephoto=\uc0ac\uc9c4 \ub3cc\ub824\uc9d0
+confirm.running=\uc2e4\ud589\uc911
+confirm.lookupsrtm1=
+confirm.lookupsrtm2=\uace0\ub3c4\uac12 \ubc1c\uacac
+confirm.deletefieldvalues=\ud544\ub4dc\uac12 \uc9c0\uc6cc\uc9d0
+confirm.audioload=\uc18c\ub9ac\ud30c\uc77c \ucd94\uac00\ub428
+confirm.correlateaudios.single=\uc18c\ub9ac \uc5f0\uacb0\ub428
+confirm.correlateaudios.multi=\uc18c\ub9ac\ub4e4 \uc5f0\uacb0\ub428
+
+# Buttons
+button.ok=\ud655\uc778
+button.back=\ub4a4\ub85c
+button.next=\ub2e4\uc74c
+button.finish=\ub9c8\uce68
+button.cancel=\ucde8\uc18c
+button.overwrite=\ub36e\uc5b4\uc4f0\uae30
+button.moveup=\uc704\ub85c
+button.movedown=\uc544\ub798\ub85c
+button.showlines=\uc120 \ubcf4\uae30
+button.edit=\uc218\uc815
+button.exit=\ub098\uac00\uae30
+button.close=\ub2eb\uae30
+button.continue=\uacc4\uc18d
+button.yes=\uc608
+button.no=\uc544\ub2c8\uc624
+button.yestoall=\uc804\ubd80 \uc608
+button.notoall=\uc804\ubd80 \uc544\ub2c8\uc624
+button.select=\uc120\ud0dd
+button.selectall=\uc804\ubd80\uc120\ud0dd
+button.selectnone=\uc544\ubb34\uac83\ub3c4 \uc120\ud0dd\ud558\uc9c0\uc54a\uae30
+button.preview=\ubbf8\ub9ac\ubcf4\uae30
+button.load=\ubd88\ub7ec\uc624\uae30
+button.upload=\uc62c\ub9ac\uae30
+button.guessfields=\ucd94\uce21 \ud544\ub4dc
+button.showwebpage=\uc6f9\ud398\uc774\uc9c0 \ubcf4\uae30
+button.check=\uccb4\ud06c
+button.resettodefaults=\uae30\ubcf8\uc124\uc815\uc73c\ub85c
+button.browse=\ucc3e\uae30
+button.addnew=\uc0c8\ub85c \ucd94\uac00
+button.delete=\uc9c0\uc6b0\uae30
+
+# File types
+filetype.txt=TXT \ud30c\uc77c
+filetype.jpeg=JPG \ud30c\uc77c
+filetype.kmlkmz=KML, KMZ \ud30c\uc77c
+filetype.kml=KML \ud30c\uc77c
+filetype.kmz=KMZ \ud30c\uc77c
+filetype.gpx=GPX \ud30c\uc77c
+filetype.pov=POV \ud30c\uc77c
+filetype.svg=SVG \ud30c\uc77c
+filetype.audio=MP3, OGG, WAV \ud30c\uc77c
+
+# Display components
+display.nodata=\ubd88\ub7ec\uc9c4 \ub370\uc774\ud0c0 \uc5c6\uc74c
+display.noaltitudes=\ud2b8\ub799\ub370\uc774\ud0c0\uc5d0 \uace0\ub3c4\uac00 \ud3ec\ud568\uc548\ub418\uc788\uc5b4\uc694.
+display.notimestamps=\ud2b8\ub799\ub370\uc774\ud0c0\uc5d0 \uc2dc\uac04\uc774 \ud3ec\ud568\uc548\ub418\uc5b4\uc788\uc5b4\uc694.
+details.trackdetails=\ud2b8\ub809 \uc0c1\uc138\uc815\ubcf4
+details.notrack=\ubd88\ub7ec\uc628 \ud2b8\ub799\uc774 \uc5c6\uc5b4\uc694
+details.track.points=\uc9c0\uc810
+details.track.file=\ud30c\uc77c
+details.track.numfiles=\ud30c\uc77c\uc218
+details.pointdetails=\uc9c0\uc810 \uc0c1\uc138\uc815\ubcf4
+details.index.selected=\uac1c \uc120\ud0dd\ub428
+details.index.of=\uac1c \uc911\uc5d0
+details.nopointselection=\uc120\ud0dd\ub41c \uc9c0\uc810 \uc5c6\uc74c
+details.photofile=\uc0ac\uc9c4 \ud30c\uc77c
+details.norangeselection=\ubc94\uc704\uac00 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc558\uc5b4\uc694.
+details.rangedetails=\ubc94\uc704 \uc0c1\uc138\uc815\ubcf4
+details.range.selected=\uc120\ud0dd\ub428
+details.range.to=\ud604\uc7ac \ubc94\uc704 \uc9c0\uc810\uc5d0\uc11c
+details.altitude.to=\uc120\ud0dd\ub41c \ubc94\uc704\uc5d0\uc11c \uc774\uc9c0\uc810\uc740
+details.range.climb=\uc624\ub984
+details.range.descent=\ud558\uac15
+details.coordformat=\uc88c\ud45c\ud615\uc2dd
+details.distanceunits=\uac70\ub9ac \ub2e8\uc704
+display.range.time.secs=\ucd08
+display.range.time.mins=\ubd84
+display.range.time.hours=\uc2dc\uac04
+display.range.time.days=\uc77c
+details.range.avespeed=\ud3c9\uade0 \uc18d\ub3c4
+details.range.avemovingspeed=\ud3c9\uade0 \uc774\ub3d9
+details.range.maxspeed=\ucd5c\uace0 \uc18d\ub3c4
+details.range.numsegments=\ubd80\ubd84\ub4e4\uc758 \uc218
+details.range.pace=\ud398\uc774\uc2a4(1km\ub098 1mile\uc774\ub3d9 \uc2dc\uac04)
+details.range.gradient=\uacbd\uc0ac
+details.lists.waypoints=\uacbd\uc720\uc9c0
+details.lists.photos=\uc0ac\uc9c4\ub4e4
+details.lists.audio=\uc18c\ub9ac
+details.photodetails=\uc0ac\uc9c4 \uc0c1\uc138\uc815\ubcf4
+details.nophoto=\uc0ac\uc9c4\uc774 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc74c
+details.photo.loading=\ubd88\ub7ec\uc624\uae30
+details.media.connected=\uc5f0\uacb0\ub428
+details.audiodetails=\uc18c\ub9ac \uc0c1\uc138\uc815\ubcf4
+details.noaudio=\uc18c\ub9ac\ud30c\uc77c\uc774 \uc120\ud0dd\ub418\uc9c0 \uc54a\uc74c
+details.audio.file=\uc18c\ub9ac\ud30c\uc77c
+details.audio.playing=\uc7ac\uc0dd\uc911...
+map.overzoom=\uc774 \ub2e8\uacc4\uc5d0\uc11c\ub294 \uc9c0\ub3c4\ub97c \ud45c\uc2dc\ud560 \uc218 \uc5c6\uc5b4\uc694.
+
+# Field names
+fieldname.latitude=\uc704\ub3c4
+fieldname.longitude=\uacbd\ub3c4
+fieldname.altitude=\uace0\ub3c4
+fieldname.timestamp=\uc2dc\uac04
+fieldname.time=\uc2dc\uac04
+fieldname.waypointname=\uc774\ub984
+fieldname.waypointtype=\ud615\uc2dd
+fieldname.newsegment=\ubd80\ubd84
+fieldname.custom=\ucee4\uc2a4\ud140
+fieldname.prefix=\ud544\ub4dc
+fieldname.distance=\uac70\ub9ac
+fieldname.movingdistance=\uc6c0\uc9c0\uc778\uac70\ub9ac
+fieldname.duration=\uae30\uac04
+fieldname.speed=\uc18d\ub3c4
+fieldname.verticalspeed=\uc218\uc9c1\uc18d\ub3c4
+
+# Measurement units
+units.original=\uc6d0\ubcf8
+units.default=\uae30\ubcf8
+units.metres=\ubbf8\ud130
+units.metres.short=\ubbf8\ud130
+units.feet=\ud53c\ud2b8
+units.feet.short=\ud53c\ud2b8
+units.kilometres=\ud0ac\ub85c\ubbf8\ud130
+units.kilometres.short=\ud0ac\ub85c\ubbf8\ud130
+units.kmh=\ud0ac\ub85c\ubbf8\ud130-\uc2dc\uc18d
+units.miles=\ub9c8\uc77c
+units.miles.short=\ub9c8\uc77c
+units.mph=\ub9c8\uc77c-\uc2dc\uc18d
+units.metrespersec=m/s
+units.feetpersec=ft/s
+units.hours=\uc2dc\uac04
+units.degminsec=\ub3c4-\ubd84-\ucd08
+units.degmin=\ub3c4-\ubd84
+units.deg=\ub3c4
+units.iso8601=ISO 8601
+
+# External urls
+url.googlemaps=maps.google.co.kr
+wikipedia.lang=ko
+
+# Cardinals for 3d plots
+cardinal.n=N
+cardinal.s=S
+cardinal.e=E
+cardinal.w=W
+
+# Undo operations
+undo.load=\uc790\ub8cc \ubd88\ub7ec\uc624\uae30
+undo.loadphotos=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\uae30
+undo.loadaudios=\uc18c\ub9ac\ud30c\uc77c \ubd88\ub7ec\uc624\uae30
+undo.editpoint=\uc9c0\uc810 \uc218\uc815
+undo.deletepoint=\uc9c0\uc810 \uc0ad\uc81c
+undo.removephoto=\uc0ac\uc9c4 \uc81c\uac70
+undo.removeaudio=\uc18c\ub9ac\ud30c\uc77c \uc81c\uac70
+undo.deleterange=\ubc94\uc704 \uc0ad\uc81c
+undo.compress=\ud2b8\ub799 \uc555\ucd95
+undo.insert=\uc9c0\uc810\ub4e4 \uc0bd\uc785
+undo.reverse=\ubc94\uc704 \ubc18\uc804
+undo.mergetracksegments=\ud2b8\ub799 \ubd80\ubd84 \ubcd1\ud569
+undo.addtimeoffset=\ud0c0\uc784 \uc624\ud504\uc14b \ucd94\uac00
+undo.addaltitudeoffset=\uace0\ub3c4 \uc624\ud504\uc14b \ucd94\uac00
+undo.rearrangewaypoints=\uacbd\uc720\uc9c0 \uc7ac\uc815\ub82c
+undo.cutandmove=\ubd80\ubd84 \uc774\ub3d9
+undo.connect=\uc5f0\uacb0
+undo.disconnect=\uc5f0\uacb0 \ub04a\uae30
+undo.correlatephotos=\uc0ac\uc9c4 \uc5f0\uacb0
+undo.rearrangephotos=\uc0ac\uc9c4 \uc7ac\uc815\ub82c
+undo.createpoint=\uc9c0\uc810 \uc0dd\uc131
+undo.rotatephoto=\uc0ac\uc9c4 \ud68c\uc804
+undo.convertnamestotimes=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658
+undo.lookupsrtm=SRTM\uc5d0\uc11c \uace0\ub3c4 \ucc3e\uae30
+undo.deletefieldvalues=\ud544\ub4dc \uac11 \uc218\uc815
+undo.correlateaudios=\uc18c\ub9ac \uc5f0\uacb0
+
+# Error messages
+error.save.dialogtitle=\uc790\ub8cc \uc800\uc7a5\uc911 \uc624\ub958
+error.save.nodata=\uc800\uc7a5\ud560 \uc790\ub8cc\uac00 \uc5c6\uc5b4\uc694
+error.save.failed=\ud30c\uc77c\uc5d0 \uc790\ub8cc\uc800\uc7a5 \uc2e4\ud328
+error.saveexif.filenotfound=\uc0ac\uc9c4\ud30c\uc77c \ucc3e\uae30 \uc2e4\ud328
+error.saveexif.cannotoverwrite1=\uc0ac\uc9c4\ud30c\uc77c
+error.saveexif.cannotoverwrite2=\uc774 \uc77d\uae30\uc804\uc6a9\uc774\ub124\uc694, \ub36e\uc5b4\uc4f8\uc218 \uc5c6\uc5b4\uc694 \ubcf5\uc0ac\ud574\uc11c \uc4f8\uae4c\uc694?
+error.saveexif.failed1=
+error.saveexif.failed2=\uc774\ubbf8\uc9c0 \uc800\uc7a5 \uc2e4\ud328
+error.saveexif.forced1=
+error.saveexif.forced2=\uc774\ubbf8\uc9c0\uac00 \uac15\uc81c\ub97c \uc694\uad6c\ud569\ub2c8\ub2e4.
+error.load.dialogtitle=\uc790\ub8cc \ubd88\ub7ec\uc624\ub2e4\uac00 \uc5d0\ub7ec
+error.load.noread=\ud30c\uc77c\uc744 \uc77d\uc744 \uc218 \uc5c6\ub124\uc694.
+error.load.nopoints=\ud30c\uc77c\uc5d0 \uc88c\ud45c \uc815\ubcf4\uac00 \uc5c6\uc5b4\uc694.
+error.load.unknownxml=\uc54c\uc218\uc5c6\ub294 xml \ud3ec\uba67\uc774\uc5d0\uc694.(kml\uc774\ub098 gpx\uac00 \uc544\ub2cc\uac70 \uac19\uc544\uc694)
+error.load.noxmlinzip=zip\ud30c\uc77c\uc548\uc5d0 xml\ud30c\uc77c\uc744 \ucc3e\uc744 \uc218 \uc5c6\uc5b4\uc694.
+error.load.othererror=\ud30c\uc77c \uc77d\ub294 \uc911 \uc5d0\ub7ec :
+error.jpegload.dialogtitle=\uc0ac\uc9c4 \ubd88\ub7ec\uc624\ub294 \uc911 \uc5d0\ub7ec
+error.jpegload.nofilesfound=\ucc3e\uc740 \ud30c\uc77c \uc5c6\uc74c.
+error.jpegload.nojpegsfound=\ucc3e\uc740 jpeg\ud30c\uc77c \uc5c6\uc74c.
+error.jpegload.nogpsfound=GPS \uc815\ubcf4\ub97c \ucc3e\uc9c0 \ubabb\ud568.
+error.jpegload.exifreadfailed=EXIF\uc815\ubcf4 \uc77d\uae30 \uc2e4\ud328./n\ub0b4\uc7a5\uc774\ub098 \uc678\uc7a5 \ub77c\uc774\ube0c\ub7ec\uc774\uac00 \uc5c6\uc73c\uba74/nEXIF\uc815\ubcf4\ub97c \uc77d\uc744 \uc218 \uc5c6\uc5b4\uc694.
+error.audioload.nofilesfound=\ucc3e\uc740 \uc18c\ub9ac\ud30c\uc77c \uc5c6\uc74c.
+error.gpsload.unknown=\uc54c\ub824\uc9c0\uc9c0 \uc54a\uc740 \uc624\ub958.
+error.undofailed.title=\ub418\ub3cc\ub9ac\uae30 \uc2e4\ud328.
+error.undofailed.text=\ub418\ub3cc\ub9ac\uae30 \ub3d9\uc791\uc774 \uc2e4\ud328\ud558\uc600\uc5b4\uc694.
+error.function.noop.title=\uae30\ub2a5\uc774 \uc544\ubb34 \ud6a8\uacfc \uc5c6\ub124\uc694.
+error.rearrange.noop=\uc7ac\uc815\ub82c\ud55c \uc9c0\uc810\ub4e4\uc774 \ud6a8\uacfc \uc5c6\uc5b4\uc694.(\uacbd\uc720\uc9c0\uac00 \uc544\ub2cc\uac70 \uac19\uc544\uc694.)
+error.function.notavailable.title=\uae30\ub2a5\uc744 \uc791\ub3d9\ud560 \uc218 \uc5c6\uc74c.
+error.function.nojava3d=\uc774 \uae30\ub2a5\uc740 Java3D \ub77c\uc774\ube0c\ub7ec\ub9ac\uac00 \ud544\uc694\ud574\uc694./n Sun.com\uc5d0\uc11c \uad6c\ud560 \uc218 \uc788\uc5b4\uc694.
+error.3d=3D \ud45c\uc2dc\ud558\ub2e4\uac00 \uc5d0\ub7ec\ubc1c\uc0dd
+error.readme.notfound=Readme \ud30c\uc77c\uc744 \ubabb \ucc3e\uc74c
+error.osmimage.dialogtitle=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30\uc911 \uc624\ub958
+error.osmimage.failed=\uc9c0\ub3c4 \uc774\ubbf8\uc9c0 \ubd88\ub7ec\uc624\uae30 \uc2e4\ud328./n\uc778\ud130\ub137 \uc5f0\uacb0\uc744 \ud655\uc778\ud574\uc8fc\uc138\uc694.
+error.language.wrongfile=\uc120\ud0dd\ud558\uc2e0 \ud30c\uc77c\uc774 Prune\uc744 \uc704\ud55c \uc5b8\uc5b4\ud30c\uc77c\uc774 \uc544\ub2cc\uac70 \uac19\uc544\uc694.
+error.convertnamestotimes.nonames=\uc774\ub984\uc744 \uc2dc\uac04\uc73c\ub85c \ubcc0\ud658 \ud560 \uc218 \uc5c6\uc5b4\uc694./n(\uacbd\uc720\uc9c0\uc774\ub984\uc744 \ucc3e\uc9c0 \ubabb\ud588\uac70\ub098 \ubcc0\ud658\ud560 \uc218 \uc5c6\ub294 \uacbd\uc6b0\uc5d0\uc694.)
+error.lookupsrtm.nonefound=\uc774 \uc9c0\uc810\ub4e4\uc5d0\uc11c \uace0\ub3c4\uac12\uc744 \uc0ac\uc6a9\ud560 \uc218 \uc5c6\uc5b4\uc694./n\uc774 \uc601\uc5ed\uc5d0\uc11c \ud0c0\uc77c\uc774 \uc5c6\uac70\ub098, \uc790\ub8cc\uac00 \uc54c\uc218 \uc5c6\ub294 \uac83\ub4e4\uc744 \ud3ec\ud568\ud558\uace0 \uc788\ub294 \uacbd\uc720.
+error.lookupsrtm.nonerequired=\ubaa8\ub4e0 \uc9c0\uc810\uc774 \uace0\ub3c4 \uc815\ubcf4\uac00 \uc788\uc5b4\uc11c, \ucc3e\uc744\uac8c \uc544\ubb34\uac83\ub3c4 \uc5c6\ub124\uc694.
+error.gpsies.uploadnotok=gpsies \uc11c\ubc84\uac00 \uba54\uc138\uc9c0\ub97c \ub2e4\uc74c\uacfc \uac19\uc740 \ub3cc\ub824\uc90d\ub2c8\ub2e4
+error.gpsies.uploadfailed=\ub2e4\uc74c\uacfc \uac19\uc740 \uc774\uc720\ub85c \uc5c5\ub85c\ub4dc\uc5d0 \uc2e4\ud328\ud588\uc2b5\ub2c8\ub2e4
+error.playaudiofailed=\uc18c\ub9ac\ud30c\uc77c \uc7ac\uc0dd \uc2e4\ud328
menu.point.deletepoint=Verwijder punt
menu.photo=Foto
menu.photo.saveexif=Opslaan naar Exif
-menu.photo.connect=Aan punt vastzetten
-menu.photo.disconnect=Van punt losmaken
-menu.photo.delete=Verwijder foto
+menu.audio=Audio
menu.view=Bekijken
menu.view.showsidebars=Toon panelen
menu.view.browser=Kaart in browser
menu.map.zoomout=Zoom -
menu.map.zoomfull=Zoom alles
menu.map.newpoint=Maak nieuw punt
+menu.map.drawpoints=Maak een serie punten
menu.map.connect=Verbind route punten
menu.map.autopan=Autopan
menu.map.showmap=Toon kaart
altkey.menu.point=P
altkey.menu.view=B
altkey.menu.photo=F
+altkey.menu.audio=A
altkey.menu.settings=I
altkey.menu.help=H
function.getgpsies=Routes van Gpsies ophalen
function.uploadgpsies=Upload routes naar Gpsies
function.lookupsrtm=Hoogtes van SRTM ophalen
+function.getwikipedia=Wikipedia artikelen uit de buurt ophalen
+function.searchwikipedianames=Wikipedia zoeken op naam
+function.downloadosm=Downloaden OSM data voor gebied
function.duplicatepoint=Dupliceer punt
function.setcolours=Instellen kleuren
+function.setlinewidth=Instellen lijndikte
function.setlanguage=Instellen taal
+function.connecttopoint=Aan punt vastzetten
+function.disconnectfrompoint=Van punt losmaken
+function.removephoto=Verwijder foto
function.correlatephotos=Correleer foto's
function.rearrangephotos=Foto's herschikken
function.rotatephotoleft=Roteer foto linksom
function.rotatephotoright=Roteer foto rechtsom
+function.photopopup=Toon foto in pop-up
function.ignoreexifthumb=Negeer exif thumbnail
+function.loadaudio=Toevoegen audiobestanden
+function.removeaudio=Verwijder audiobestand
+function.correlateaudios=Correleeer audiobestanden
+function.playaudio=Afspelen audiobestand
+function.stopaudio=Stop audiobestand
function.help=Help
function.showkeys=Toon sneltoetsen
function.about=Over Prune
dialog.openoptions.deliminfo.fields=velden
dialog.openoptions.deliminfo.norecords=Geen bestanden
dialog.openoptions.altitudeunits=Hoogte eenheden
-dialog.open.contentsdoubled=Dit bestand bevat twee kopie\u00ebn van ieder punt,\neenkeer als als waypoint en eenkeer als punt
+dialog.open.contentsdoubled=Dit bestand bevat twee kopie\u00ebn van ieder punt,\neen keer als waypoint en een keer als punt
dialog.selecttracks.intro=Selecteer route of routes om te laden
dialog.selecttracks.noname=Onbenoemd
dialog.jpegload.subdirectories=Submappen meenemen
dialog.exportpov.cameraz=Camera Z
dialog.exportpov.modelstyle=Model stijl
dialog.exportpov.ballsandsticks=Balletjes en stokjes
-dialog.exportpov.tubesandwalls=Tubes en muren
+dialog.exportpov.tubesandwalls=Buizen en muren
dialog.exportpov.warningtracksize=Deze route heeft een groot aantal punten. Java3D kan deze mogelijk niet tonen.\nWeet u zeker dat u door wilt gaan?
dialog.exportsvg.text=Selecteer de camera hoeken voor SVG export
dialog.exportsvg.phi=Azimut hoek \u03d5
dialog.pointtype.track=Routepunten
dialog.pointtype.waypoint=Waypoints
dialog.pointtype.photo=Fotopunten
+dialog.pointtype.audio=Audiopunten
dialog.pointtype.selection=Alleen selectie
dialog.confirmreversetrack.title=Bevestig omkering
dialog.confirmreversetrack.text=Deze route bevat tijd-informatie die niet meer klopt na een omkering.\nWeet u zeker dat u deze sectie wilt omkeren?
dialog.gpsies.activity.walking=Lopen
dialog.gpsies.activity.jogging=Hardlopen
dialog.gpsies.activity.biking=Fietsen
-dialog.gpsies.activity.motorbiking=Motor
-dialog.gpsies.activity.snowshoe=Sneeuwschoen
+dialog.gpsies.activity.motorbiking=Motorrijden
+dialog.gpsies.activity.snowshoe=Sneeuwschoen-lopen
dialog.gpsies.activity.sailing=Zeilen
dialog.gpsies.activity.skating=Skating
+dialog.wikipedia.column.name=Artikelnaam
+dialog.wikipedia.column.distance=Afstand
dialog.correlate.notimestamps=Er zit geen tijdinformatie in de punten, dus kunnen ze niet aan foto's gekoppeld worden.
dialog.correlate.nouncorrelatedphotos=Er zijn geen ongekoppelde foto's.\nWeet u zeker dat u wilt doorgaan?
dialog.correlate.photoselect.intro=Selecteer \u00e9\u00e9n van deze gekoppelde foto's om het tijdsverschil te gebruiken
-dialog.correlate.photoselect.photoname=Fotonaam
-dialog.correlate.photoselect.timediff=Tijdsverschil
-dialog.correlate.photoselect.photolater=Foto later
+dialog.correlate.select.photoname=Fotonaam
+dialog.correlate.select.timediff=Tijdsverschil
+dialog.correlate.select.photolater=Foto later
dialog.correlate.options.tip=Tip: Door handmatig een foto te koppelen kan het tijdsverschil voor u berekend worden.
dialog.correlate.options.intro=Selecteer de opties voor automatisch koppelen
dialog.correlate.options.offsetpanel=Tijdverschil
dialog.correlate.options.offset.minutes=minuten en
dialog.correlate.options.offset.seconds=seconden
dialog.correlate.options.photolater=Foto later dan punt
-dialog.correlate.options.pointlater=Punt later dan foto
+dialog.correlate.options.pointlaterphoto=Punt later dan foto
+dialog.correlate.options.audiolater=Audio later dan punt
+dialog.correlate.options.pointlateraudio=Punt later dan audio
dialog.correlate.options.limitspanel=Koppelingslimieten
dialog.correlate.options.notimelimit=Geen tijdslimiet
dialog.correlate.options.timelimit=Tijdslimiet
dialog.correlate.options.distancelimit=Afstandlimeit
dialog.correlate.options.correlate=Koppelen
dialog.correlate.alloutsiderange=Alle foto's zijn buiten het bereik van de route en kunnen dus niet gekoppeld worden.\nPas het tijdsverschil aan, of maak minimaal \u00e9\u00e9n koppeling handmatig.
+dialog.correlate.filetimes=Tijdstip bestand geeft aan:
+dialog.correlate.filetimes2=van audiobestand
+dialog.correlate.correltimes=Voor correleren, gebruik:
+dialog.correlate.timestamp.beginning=Begin
+dialog.correlate.timestamp.middle=Midden
+dialog.correlate.timestamp.end=Einde
+dialog.correlate.audioselect.intro=Gebruk \u00e9\u00e9n van deze gecorreleerde audiobestanden als tijdsveschil
+dialog.correlate.select.audioname=Naam audiobestsnd
+dialog.correlate.select.audiolater=Audio later
dialog.rearrangephotos.desc=Selecteer doel en sorteervolgorde van de foto punten
dialog.rearrangephotos.tostart=Naar begin
dialog.rearrangephotos.toend=Naar einde
dialog.saveconfig.prune.kmzimagewidth=KMZ afbeelding breedte
dialog.saveconfig.prune.kmzimageheight=KMZ afbeelding hoogte
dialog.saveconfig.prune.colourscheme=Kleurenschema
+dialog.saveconfig.prune.linewidth=Lijndikte
dialog.saveconfig.prune.kmltrackcolour=KML routekleur
dialog.setpaths.intro=Indien nodig kan je de paden naar externe applicaties kiezen:
dialog.setpaths.found=Pad gevonden?
dialog.diskcache.createdir=Cre\u00eber map
dialog.diskcache.nocreate=Cache map niet aangemaakt
dialog.deletefieldvalues.intro=Selecteer het te verwijderen veld voor de huidige reeks
+dialog.setlinewidth.text=Geef lijndikte voor routes (1-4)
+dialog.downloadosm.desc=Bevestig het downloaden van ruwe OSM data voor dit gebied:
+dialog.searchwikipedianames.search=Zoeken naar:
# 3d window
dialog.3d.title=Prune in 3D
-dialog.3d.altitudecap=Minimale hoogte
dialog.3d.altitudefactor=Hoogte overdrijvingsfactor
dialog.3dlines.title=Prune raster
dialog.3dlines.empty=Geen raster om af te beelden
confirm.undo.multi=Acties geannuleerd
confirm.jpegload.single=foto was toegevoegd
confirm.jpegload.multi=foto's waren toegevoegd
-confirm.photo.connect=foto's gekoppeld
+confirm.media.connect=Media gekoppeld
confirm.photo.disconnect=foto's ontkoppeld
-confirm.correlate.single=Foto was gecorreleerd
-confirm.correlate.multi=Foto's waren gecorreleerd
+confirm.audio.disconnect=audio ontkoppeld
+confirm.media.removed=verwijderd
+confirm.correlatephotos.single=Foto was gecorreleerd
+confirm.correlatephotos.multi=Foto's waren gecorreleerd
confirm.createpoint=punt aangemaakt
-confirm.rotatephoto=foto rgeroteerd
+confirm.rotatephoto=foto geroteerd
confirm.running=Bezig...
confirm.lookupsrtm1=Gevonden
confirm.lookupsrtm2=hoote waarden
confirm.deletefieldvalues=Veldwaarden gewist
+confirm.audioload=Audiobestanden toegevoegd
+confirm.correlateaudios.single=audiobestand gecorreleerd
+confirm.correlateaudios.multi=audiobestanden gecorreleerd
# Buttons
button.ok=OK
button.selectnone=Selecteer niets
button.preview=Voorbeeld
button.load=Laden
+button.upload=Upload
button.guessfields=Raad velden
button.showwebpage=Toon webpagina
button.check=Controleren
filetype.gpx=GPX bestand
filetype.pov=POV bestand
filetype.svg=SVG bestand
+filetype.audio=MP3, OGG, WAV bestanden
# Display components
display.nodata=Geen gegevens geladen
details.range.numsegments=Aantal segmenten
details.range.pace=Tempo
details.range.gradient=Helling
-details.waypointsphotos.waypoints=Waypoints
-details.waypointsphotos.photos=Foto
+details.lists.waypoints=Waypoints
+details.lists.photos=Foto
+details.lists.audio=Audio
details.photodetails=Foto details
details.nophoto=Geen foto geselecteerd
details.photo.loading=Bezig met laden
-details.photo.connected=Geconnecteerd
+details.media.connected=Geconnecteerd
+details.audiodetails=Audio details
+details.noaudio=Geen audiobestand geselecteerd
+details.audio.file=Audiobestand
+details.audio.playing=Afspelen...
map.overzoom=Geen kaarten beschikbaar op dit zoom-niveau
# Field names
# External urls
url.googlemaps=maps.google.nl
+wikipedia.lang=nl
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=gegevens laden
undo.loadphotos=foto's laden
+undo.loadaudios=audiobestanden laden
undo.editpoint=wijzigen punt
undo.deletepoint=verwijderen punt
-undo.deletephoto=verwijderen foto
+undo.removephoto=verwijderen foto
+undo.removeaudio=verwijderen audiobestand
undo.deleterange=verwijderen reeks
undo.compress=comprimeren route
undo.insert=punten invoegen
undo.reverse=reeks omkeren
-undo.mergetracksegments=Samenvoegen route segmenten
-undo.addtimeoffset=Tijdsverschil toevoegen
-undo.addaltitudeoffset=Hoogteverschil toevoegen
-undo.rearrangewaypoints=Herschikken waypoint
+undo.mergetracksegments=samenvoegen route segmenten
+undo.addtimeoffset=tijdsverschil toevoegen
+undo.addaltitudeoffset=hoogteverschil toevoegen
+undo.rearrangewaypoints=herschikken waypoint
undo.cutandmove=Verschuif sectie
-undo.connectphoto=Koppel foto
-undo.disconnectphoto=Loskoppelen foto
-undo.correlate=Correleer foto
-undo.rearrangephotos=Herschikken foto's
-undo.createpoint=Cre\u00eber punt
-undo.rotatephoto=Roteer foto
-undo.convertnamestotimes=Converteer namen naar tijden
-undo.lookupsrtm=Opzoeken hoogtes in SRTM
-undo.deletefieldvalues=Verwijder veldwaarden
+undo.connect=koppel
+undo.disconnect=loskoppelen
+undo.correlatephotos=correleer foto
+undo.rearrangephotos=herschikken foto's
+undo.createpoint=cre\u00eber punt
+undo.rotatephoto=roteer foto
+undo.convertnamestotimes=converteer namen naar tijden
+undo.lookupsrtm=opzoeken hoogtes in SRTM
+undo.deletefieldvalues=verwijder veldwaarden
+undo.correlateaudios=correleer audiobestanden
# Error messages
error.save.dialogtitle=Fout bij opslaan gegevens
error.jpegload.dialogtitle=Fout bij inlezen foto's
error.jpegload.nofilesfound=Bestanden niet gevonden
error.jpegload.nojpegsfound=Geen jpeg-bestanden gevonden
-error.jpegload.noexiffound=Geen EXIF informatie gevonden
error.jpegload.nogpsfound=Geen GPS informatie gevonden
error.jpegload.exifreadfailed=Kon geen EXIF informatie inlezen. EXIF informatie kan niet worden gelezen\n zonder interne of externe bibliotheek.
+error.audioload.nofilesfound=Geen audiobestanden gevonden
error.gpsload.unknown=Onbekende fout
error.undofailed.title=Terugdraaien mislukt
error.undofailed.text=Kon actie niet terugdraaien
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
menu.point.deletepoint=Usu\u0144 punkt
menu.photo=Zdj\u0119cie
menu.photo.saveexif=Zapisz Exif
-menu.photo.connect=Przy\u0142\u0105cz do punktu
-menu.photo.disconnect=Od\u0142\u0105cz od punktu
-menu.photo.delete=Usu\u0144 zdj\u0119cie
+menu.audio=Audio
menu.view=Widok
menu.view.showsidebars=Poka\u017c boczne panele
menu.view.browser=Mapa w przegl\u0105darce
menu.map.zoomout=Pomniejsz
menu.map.zoomfull=Dostosuj powi\u0119kszenie
menu.map.newpoint=Stw\u00f3rz nowy punkt
+menu.map.drawpoints=Stw\u00f3rz seri\u0119 punkt\u00f3w
menu.map.connect=Po\u0142\u0105cz punkty \u015bcie\u017cki
menu.map.autopan=Przesuwanie mapy
menu.map.showmap=Poka\u017c map\u0119
altkey.menu.point=U
altkey.menu.view=W
altkey.menu.photo=Z
+altkey.menu.audio=A
altkey.menu.settings=T
altkey.menu.help=M
function.getgpsies=Pobierz \u015bcie\u017cki z Gpsies
function.uploadgpsies=Wy\u015blij \u015bcie\u017cki do Gpsies
function.lookupsrtm=Pobierz wysoko\u015bci z SRTM
+function.getwikipedia=Szukaj w Wikipedii o okolicy
+function.searchwikipedianames=Szukaj nazwy w Wikipedii
+function.downloadosm=Za\u0142aduj dane obszaru z OSM
function.duplicatepoint=Duplikuj plik
function.setcolours=Ustaw kolory
+function.setlinewidth=Ustaw szeroko\u015b\u0107 linii
function.setlanguage=Zmie\u0144 j\u0119zyk
+function.connecttopoint=Przy\u0142\u0105cz do punktu
+function.disconnectfrompoint=Od\u0142\u0105cz od punktu
+function.removephoto=Usu\u0144 zdj\u0119cie
function.correlatephotos=Powi\u0105\u017c zdj\u0119cia
function.rearrangephotos=Zmie\u0144 kolejno\u015b\u0107 zdj\u0119\u0107
function.rotatephotoleft=Obr\u00f3\u0107 zdj\u0119cie w lewo
function.rotatephotoright=Obr\u00f3\u0107 zdj\u0119cie wprawo
+function.photopopup=Poka\u017c zdj\u0119cie
function.ignoreexifthumb=Ignoruj miniaturk\u0119 z exif
+function.loadaudio=Dodaj pliki audio
+function.removeaudio=Usu\u0144 pliki audio
+function.correlateaudios=Powi\u0105\u017c audio
+function.playaudio=Odtw\u00f3rz plik audio
+function.stopaudio=Zatrzymaj plik audio
function.help=Pomoc
function.showkeys=Poka\u017c klawisze skr\u00f3tu
function.about=O Prune
dialog.pointtype.track=punkty \u015bcie\u017cki
dialog.pointtype.waypoint=punkty po\u015brednie
dialog.pointtype.photo=punkty zdj\u0119\u0107
+dialog.pointtype.audio=punkty audio
dialog.pointtype.selection=Tylko wybrane
dialog.confirmreversetrack.title=Potwierd\u017a odwr\u00f3cenie
dialog.confirmreversetrack.text=Ta \u015bcie\u017cka zawiera znaczniki czasu, kt\u00f3re po odwr\u00f3ceniu nie b\u0119d\u0105 ustawione w kolejno\u015bci.\nCzy na pewno chcesz odwr\u00f3ci\u0107 ten fragment?
dialog.gpsies.activity.snowshoe=Snowshoeing
dialog.gpsies.activity.sailing=\u017beglarstwo
dialog.gpsies.activity.skating=Wrotki/rolki
+dialog.wikipedia.column.name=Tytu\u0142 artyku\u0142u
+dialog.wikipedia.column.distance=Odleg\u0142o\u015b\u0107
dialog.correlate.notimestamps=Punkty nie maj\u0105 znacznik\u00f3w czasu, nie mo\u017cna ich powi\u0105za\u0107 ze zdj\u0119ciami.
dialog.correlate.nouncorrelatedphotos=Nie ma nie powi\u0105zanych zdj\u0119\u0107.\nCzy na pewno chcesz kontynuowa\u0107?
dialog.correlate.photoselect.intro=Wybierz jedno z powi\u0105zanych zdj\u0119\u0107 i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
-dialog.correlate.photoselect.photoname=Nazwa zdj\u0119cia
-dialog.correlate.photoselect.timediff=R\u00f3\u017cnica czasowa
-dialog.correlate.photoselect.photolater=P\u00f3\u017aniejsze zdj\u0119cie
+dialog.correlate.select.photoname=Nazwa zdj\u0119cia
+dialog.correlate.select.timediff=R\u00f3\u017cnica czasowa
+dialog.correlate.select.photolater=P\u00f3\u017aniejsze zdj\u0119cie
dialog.correlate.options.tip=Porada: Gdy powi\u0105\u017cesz r\u0119cznie przynajmniej jedno zdj\u0119cie, r\u00f3\u017cnica czasowa zostanie policzona automatycznie.
dialog.correlate.options.intro=Wybierz opcje dla automatycznej korelacji
dialog.correlate.options.offsetpanel=Przesuni\u0119cie czasowe
dialog.correlate.options.offset.minutes=minut i
dialog.correlate.options.offset.seconds=sekund
dialog.correlate.options.photolater=Zdj\u0119cie p\u00f3\u017aniejsze ni\u017c punkt
-dialog.correlate.options.pointlater=Punkt p\u00f3\u017aniejszy ni\u017c zdj\u0119cie
+dialog.correlate.options.pointlaterphoto=Punkt p\u00f3\u017aniejszy ni\u017c zdj\u0119cie
+dialog.correlate.options.audiolater=Plik audio p\u00f3\u017aniejszy ni\u017c zdj\u0119cie
+dialog.correlate.options.pointlateraudio=Punkt p\u00f3\u017aniejszy ni\u017c plik audio
dialog.correlate.options.limitspanel=Ograniczenia korelacji
dialog.correlate.options.notimelimit=Bez limitu czasu
dialog.correlate.options.timelimit=Limit czasu
dialog.correlate.options.distancelimit=Limit odleg\u0142o\u015bci
dialog.correlate.options.correlate=Powi\u0105\u017c ze sob\u0105
dialog.correlate.alloutsiderange=Wszystkie zdj\u0119cia s\u0105 poza zakresem czasu \u015bcie\u017cki, tak \u017ce \u017cadne nie mo\u017ce zosta\u0107 z ni\u0105 skorelowane.\nSpr\u00f3buj zmieni\u0107 przesuni\u0119cie lub r\u0119cznie skoreluj przynajmniej jedno zdj\u0119cie.
+dialog.correlate.filetimes=Czas pliku oznacza:
+dialog.correlate.filetimes2=pliku audio
+dialog.correlate.correltimes=Do korelacji u\u017cyj:
+dialog.correlate.timestamp.beginning=pocz\u0105tku
+dialog.correlate.timestamp.middle=\u015brodka
+dialog.correlate.timestamp.end=ko\u0144ca
+dialog.correlate.audioselect.intro=Wybierz jeden z powi\u0105zanych plik\u00f3w audio i u\u017cyj go jako wzorca do przesuni\u0119cia czasu
+dialog.correlate.select.audioname=nazwa pliku audio
+dialog.correlate.select.audiolater=p\u00f3\u017aniejszy plik audio
dialog.rearrangephotos.desc=Wybierz przeznaczenie i porz\u0105dek sortowania punkt\u00f3w ze zdj\u0119ciami
dialog.rearrangephotos.tostart=Przesu\u0144 na pocz\u0105tek
dialog.rearrangephotos.toend=Przesu\u0144 na koniec
dialog.checkversion.download=Aby \u015bci\u0105gn\u0105\u0107 now\u0105 wersj\u0119 przejd\u017a na http://activityworkshop.net/software/prune/download.html.
dialog.keys.intro=U\u017cuwaj nast\u0119puj\u0105cych klawiszy skr\u00f3t\u00f3w zamiast myszki
dialog.keys.keylist=<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.keys.macmodifier=Command
dialog.saveconfig.desc=Nast\u0119puj\u0105ce ustawienia mog\u0105 zosta\u0107 zapisane w pliku konfiguracyjnym:
dialog.saveconfig.prune.trackdirectory=Katalog ze \u015bcie\u017ckami
dialog.saveconfig.prune.photodirectory=Katalog ze zdj\u0119ciami
dialog.saveconfig.prune.kmzimagewidth=szeroko\u015b\u0107 obrazka w KMZ
dialog.saveconfig.prune.kmzimageheight=wysoko\u015b\u0107 obrazka w KMZ
dialog.saveconfig.prune.colourscheme=Schemat kolor\u00f3w
+dialog.saveconfig.prune.linewidth=Szeroko\u015b\u0107 linii
dialog.saveconfig.prune.kmltrackcolour=Kolor \u015bcie\u017cki w pliku KML
dialog.setpaths.intro=Je\u015bli zachodzi tak potrzeba, mo\u017cesz wybra\u0107 \u015bcie\u017cki do aplikacji zewn\u0119trznych
dialog.setpaths.found=Znalezione \u015bcie\u017cki?
dialog.diskcache.createdir=stw\u00f3rz katalog
dialog.diskcache.nocreate=Nie utworzono katalogu pami\u0119ci podr\u0119cznej
dialog.deletefieldvalues.intro=Wybierz pola do skasowania z wybranego zakresu
+dialog.setlinewidth.text=Wprowad\u017a grubo\u015b\u0107 linii do rysowania \u015bcie\u017cek
+dialog.downloadosm.desc=Potwierd\u017a \u015bci\u0105gni\u0119cie danych dla tego obszaru z OSM:
+dialog.searchwikipedianames.search=Szukaj
# 3d window
dialog.3d.title=Prune widok tr\u00f3jwymiarowy
-dialog.3d.altitudecap=Minimalny zakres wysoko\u015bci
dialog.3d.altitudefactor=Wsp\u00f3\u0142czynnik skalowania wysoko\u015bci
dialog.3dlines.title=Linie siatki
dialog.3dlines.empty=Brak siatki do wy\u015bwietlenia!
confirm.undo.multi=operacje zosta\u0142y cofni\u0119te
confirm.jpegload.single=dodano zdj\u0119cie
confirm.jpegload.multi=zdj\u0119\u0107 dodano
-confirm.photo.connect=przy\u0142\u0105czono zdj\u0119cie
+confirm.media.connect=przy\u0142\u0105czono
confirm.photo.disconnect=od\u0142\u0105czono zdj\u0119cie
-confirm.correlate.single=zdj\u0119cie zosta\u0142o po\u0142\u0105czone
-confirm.correlate.multi=zdj\u0119cia zosta\u0142y po\u0142\u0105czone
+confirm.audio.disconnect=od\u0142\u0105czono
+confirm.media.removed=usuni\u0119to
+confirm.correlatephotos.single=zdj\u0119cie zosta\u0142o po\u0142\u0105czone
+confirm.correlatephotos.multi=zdj\u0119cia zosta\u0142y po\u0142\u0105czone
confirm.createpoint=stworzono punkt
confirm.rotatephoto=obr\u00f3cono zdj\u0119cie
confirm.running=Przetwarzam dane ...
confirm.lookupsrtm1=Znaleziono
confirm.lookupsrtm2=warto\u015bci wysoko\u015bci
confirm.deletefieldvalues=Warto\u015bci p\u00f3l usuni\u0119to
+confirm.audioload=dodano
+confirm.correlateaudios.single=audio zosta\u0142o po\u0142\u0105czone
+confirm.correlateaudios.multi=audio zosta\u0142y po\u0142\u0105czone
# Buttons
button.ok=OK
filetype.gpx=Pliki GPX
filetype.pov=Pliki POV
filetype.svg=Pliki SVG
+filetype.audio=Pliki MP3, OGG, WAV
# Display components
display.nodata=Nie za\u0142adowano danych
details.range.numsegments=Liczba segment\u00f3w
details.range.pace=Tempo
details.range.gradient=Nachylenie
-details.waypointsphotos.waypoints=Punkty po\u015brednie
-details.waypointsphotos.photos=Zdj\u0119cia
+details.lists.waypoints=Punkty po\u015brednie
+details.lists.photos=Zdj\u0119cia
+details.lists.audio=Audio
details.photodetails=Szczeg\u00f3\u0142y zdj\u0119cia
details.nophoto=Brak zaznaczonego zdj\u0119cia
details.photo.loading=Wczytywanie
-details.photo.connected=Pod\u0142\u0105czony
+details.media.connected=Pod\u0142\u0105czony
+details.audiodetails=Szczeg\u00f3\u0142y audio
+details.noaudio=Brak zaznaczonego audio
+details.audio.file=Plik audio
+details.audio.playing=odtwarzam...
map.overzoom=Brak map dla danego powi\u0119kszenia
# Field names
# External urls
url.googlemaps=maps.google.pl
+wikipedia.lang=pl
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=za\u0142aduj dane
undo.loadphotos=za\u0142aduj zdj\u0119cia
+undo.loadaudios=za\u0142aduj audio
undo.editpoint=edycja punktu
undo.deletepoint=usu\u0144 punkt
-undo.deletephoto=usu\u0144 zdj\u0119cie (nie z dysku)
+undo.removephoto=usu\u0144 zdj\u0119cie (nie z dysku)
+undo.removeaudio=usu\u0144 audio
undo.deleterange=usu\u0144 zakres
undo.compress=skompresuj \u015bcie\u017ck\u0119
undo.insert=wstaw punkty
undo.addaltitudeoffset=dodaj przeuni\u0119cie wysoko\u015bci
undo.rearrangewaypoints=przestaw punkty po\u015brednie
undo.cutandmove=przesu\u0144 fragment
-undo.connectphoto=do\u0142\u0105cz zdj\u0119cie
-undo.disconnectphoto=od\u0142\u0105cz zdj\u0119cie
-undo.correlate=po\u0142\u0105cz ze zdj\u0119ciami
+undo.connect=do\u0142\u0105cz
+undo.disconnect=od\u0142\u0105cz
+undo.correlatephotos=po\u0142\u0105cz ze zdj\u0119ciami
undo.rearrangephotos=zmie\u0144 kolejno\u015b\u0107 zdj\u0119\u0107
undo.createpoint=stw\u00f3rz punkt
undo.rotatephoto=obr\u00f3\u0107 zdj\u0119cie
undo.convertnamestotimes=zamie\u0144 nazwy punkt\u00f3w
undo.lookupsrtm=szukaj wysoko\u015bci w SRTM
undo.deletefieldvalues=usu\u0144 warto\u015bci p\u00f3l
+undo.correlateaudios=skoreluj audio
# Error messages
error.save.dialogtitle=B\u0142\u0105d zapisu danych
error.jpegload.dialogtitle=B\u0142\u0105d \u0142adowania zdj\u0119cia
error.jpegload.nofilesfound=Nie znaleziono plik\u00f3w
error.jpegload.nojpegsfound=Nie znaleziono plik\u00f3w jpeg
-error.jpegload.noexiffound=Nie znaleziono informacji EXIF
error.jpegload.nogpsfound=Nie znaleziono informacji GPS
error.jpegload.exifreadfailed=Nie powiod\u0142o si\u0119 odczytanie informacji EXIF\nInformacji tych nie mo\u017cna przeczyta\u0107 bez wewn\u0119trznej lub zewn\u0119trznej biblioteki.
+error.audioload.nofilesfound=Nie znaleziono plik\u00f3w audio
error.gpsload.unknown=Nieznany b\u0142\u0105d
error.undofailed.title=Cofnij nie powiod\u0142o si\u0119
error.undofailed.text=Nie mo\u017cna cofn\u0105\u0107
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
menu.file.addphotos=Adicionar fotos
menu.file.save=Salvar
menu.file.exit=Sair
-menu.track=Track
+menu.track=Rota
menu.track.undo=Desfazer
menu.track.clearundo=Limpar lista de desfazer
menu.track.deletemarked=Remover pontos marcados
menu.point.deletepoint=Remover ponto
menu.photo=Foto
menu.photo.saveexif=Salvar para Exif
-menu.photo.connect=Conectar ao ponto
-menu.photo.disconnect=Desconectar do ponto
-menu.photo.delete=Remover foto
+menu.audio=\u00c1udio
menu.view=Exibir
menu.view.showsidebars=Mostrar barras laterais
menu.view.browser=Mapear no navegador
menu.map.zoomout=Reduzir
menu.map.zoomfull=Ajustar para tela
menu.map.newpoint=Criar novo ponto
+menu.map.drawpoints=Criar s\u00e9ries de pontos
menu.map.connect=Conectar pontos da rota
menu.map.autopan=Auto-ajustar
menu.map.showmap=Mostrar o mapa
# Alt keys for menus
altkey.menu.file=A
-altkey.menu.track=T
+altkey.menu.track=R
altkey.menu.range=I
altkey.menu.point=P
altkey.menu.view=X
altkey.menu.photo=F
+altkey.menu.audio=U
altkey.menu.settings=C
altkey.menu.help=J
function.getgpsies=Obter rotas Gpsies
function.uploadgpsies=Enviar rotas para o Gpsies
function.lookupsrtm=Obter altitudes a partir do SRTM
+function.getwikipedia=Obter artigos da Wikipedia das redondezas
+function.searchwikipedianames=Procurar na Wikipedia por nome
+function.downloadosm=Baixar dados OSM para a \u00e1rea
function.duplicatepoint=Duplicar ponto
function.setcolours=Definir cores
+function.setlinewidth=Definir espessura da linha
function.setlanguage=Definir idioma
+function.connecttopoint=Conectar ao ponto
+function.disconnectfrompoint=Desconectar do ponto
+function.removephoto=Remover foto
function.correlatephotos=Correlacionar fotos
function.rearrangephotos=Rearrumar fotos
function.rotatephotoleft=Roda foto \u00e0 esquerda
function.rotatephotoright=Roda foto \u00e0 direita
+function.photopopup=Mostrar janela da foto
function.ignoreexifthumb=Ignorar miniatura do exif
+function.loadaudio=Adicionar arquivos de \u00e1udio
+function.removeaudio=Remover arquivo de \u00e1udio
+function.correlateaudios=Correlacionar \u00e1udios
+function.playaudio=Reproduzir arquivo de \u00e1udio
+function.stopaudio=Parar arquivo de \u00e1udio
function.help=Ajuda
function.showkeys=Mostrar atalhos de teclado
function.about=Sobre o Prune
dialog.pointtype.track=Pontos de rotas
dialog.pointtype.waypoint=Pontos
dialog.pointtype.photo=Pontos de foto
+dialog.pointtype.audio=Pontos de \u00e1udio
dialog.pointtype.selection=Apenas sele\u00e7\u00e3o
dialog.confirmreversetrack.title=Confirmar invers\u00e3o
dialog.confirmreversetrack.text=Esta rota possui informa\u00e7\u00f5es de data-hora, as quais estar\u00e3o fora de sequ\u00eancia ap\u00f3s a revers\u00e3o.\n Voc\u00ea tem certeza que deseja reverter esta se\u00e7\u00e3o?
dialog.gpsies.activity.snowshoe=Snowshoeing
dialog.gpsies.activity.sailing=Sailing
dialog.gpsies.activity.skating=Patina\u00e7\u00e3o
+dialog.wikipedia.column.name=Nome do artigo
+dialog.wikipedia.column.distance=Dist\u00e2ncia
dialog.correlate.notimestamps=N\u00e3o existem data-hora nos dados dos pontos, assim n\u00e3o h\u00e1 nada para correlacionar com as fotos
dialog.correlate.nouncorrelatedphotos=Existem fotos n\u00e3o correlacionadas.\nVoc\u00ea tem certeza que deseja continuar?
dialog.correlate.photoselect.intro=Selecione uma destas fotos correlacionadas para usar como diferen\u00e7a de tempo
-dialog.correlate.photoselect.photoname=Nome da foto
-dialog.correlate.photoselect.timediff=Diferen\u00e7a de tempo
-dialog.correlate.photoselect.photolater=Foto \u00e9 mais recente
+dialog.correlate.select.photoname=Nome da foto
+dialog.correlate.select.timediff=Diferen\u00e7a de tempo
+dialog.correlate.select.photolater=Foto \u00e9 mais recente
dialog.correlate.options.tip=Dica: Correlacionando pelo menos uma foto manualmente, a diferen\u00e7a de tempo pode ser calculada para voc\u00ea.
dialog.correlate.options.intro=Selecione as op\u00e7\u00f5es para correla\u00e7\u00e3o autom\u00e1tica
dialog.correlate.options.offsetpanel=Diferen\u00e7a de tempo
dialog.correlate.options.offset.minutes=minutos e
dialog.correlate.options.offset.seconds=segundos
dialog.correlate.options.photolater=Foto mais recente que o ponto
-dialog.correlate.options.pointlater=Ponto mais recente que a foto
+dialog.correlate.options.pointlaterphoto=Ponto mais recente que a foto
+dialog.correlate.options.audiolater=\u00e1udio mais recente que o ponto
+dialog.correlate.options.pointlateraudio=Ponto mais recente que a \u00e1udio
dialog.correlate.options.limitspanel=Limites de correla\u00e7\u00e3o
dialog.correlate.options.notimelimit=Nenhum limite de tempo
dialog.correlate.options.timelimit=Limite de tempo
dialog.correlate.options.distancelimit=Limite de dist\u00e2ncia
dialog.correlate.options.correlate=Correlacionar
dialog.correlate.alloutsiderange=Todas as fotos est\u00e3o fora do intervalo de tempo da rota, assim nenhuma pode ser correlacionada.\n Tente mudar a diferen\u00e7a de tempo ou manualmente correlacionar pelo menos uma foto.
+dialog.correlate.filetimes=Hora do arquivo denota:
+dialog.correlate.filetimes2=do clipe de \u00e1udio
+dialog.correlate.correltimes=Para correlacionar, usar:
+dialog.correlate.timestamp.beginning=In\u00edcio
+dialog.correlate.timestamp.middle=Meio
+dialog.correlate.timestamp.end=Fim
+dialog.correlate.audioselect.intro=Selecione um destes \u00e1udios correlacionados para usar como intervalo de tempo
+dialog.correlate.select.audioname=Nome do \u00e1udio
+dialog.correlate.select.audiolater=\u00c1udio posterior
dialog.rearrangephotos.desc=Selecione o destino e a ordena\u00e7\u00e3o dos pontos das fotos
dialog.rearrangephotos.tostart=Mover para o in\u00edcio
dialog.rearrangephotos.toend=Mover para o fim
dialog.saveconfig.prune.kmzimagewidth=Largura da imagem KMZ
dialog.saveconfig.prune.kmzimageheight=Altura da imagem KMZ
dialog.saveconfig.prune.colourscheme=Esquema de cores
+dialog.saveconfig.prune.linewidth=Espessura da linha
dialog.saveconfig.prune.kmltrackcolour=Cor da rota KML
dialog.setpaths.intro=Se voc\u00ea precisar, voc\u00ea pode escolher os caminhos para as aplica\u00e7\u00f5es externas:
dialog.setpaths.found=Caminho encontrado?
dialog.diskcache.createdir=Criar diret\u00f3rio
dialog.diskcache.nocreate=Diret\u00f3rio da cache n\u00e3o foi criado
dialog.deletefieldvalues.intro=Selecione o campo a remover para o intervalo atual
+dialog.setlinewidth.text=Insira a espessura das linhas para desenhar as rotas (1-4)
+dialog.downloadosm.desc=Confirmar a transfer\u00eancia de dados OSM brutos para a \u00e1rea especificada:
+dialog.searchwikipedianames.search=Procurar por:
# 3d window
dialog.3d.title=Vista 3D do Prune
-dialog.3d.altitudecap=Intervalo de altitude m\u00ednimo
dialog.3d.altitudefactor=Fator de exagera\u00e7\u00e3o de altitude
dialog.3dlines.title=Linhas da grade do Prune
dialog.3dlines.empty=Nenhuma linha de grade para exibir!
confirm.undo.multi=opera\u00e7\u00f5es desfeitas
confirm.jpegload.single=foto foi adicionada
confirm.jpegload.multi=fotos foram adicionadas
-confirm.photo.connect=foto conectada
+confirm.media.connect=m\u00eddia conectada
confirm.photo.disconnect=foto desconectada
-confirm.correlate.single=foto foi correlacionada
-confirm.correlate.multi=fotos foram correlacionadas
+confirm.audio.disconnect=\u00e1udio desconectado
+confirm.media.removed=removido
+confirm.correlatephotos.single=foto foi correlacionada
+confirm.correlatephotos.multi=fotos foram correlacionadas
confirm.createpoint=ponto criado
confirm.rotatephoto=foto rotacionada
confirm.running=Rodando...
confirm.lookupsrtm1=Encontrado
confirm.lookupsrtm2=valores de altitude
confirm.deletefieldvalues=Valores do campo removidos
+confirm.audioload=Arquivos de \u00e1udio adicionados
+confirm.correlateaudios.single=\u00e1udio foi correlacionado
+confirm.correlateaudios.multi=\u00e1udios foram correlacionados
# Buttons
button.ok=Ok
details.range.gradient=Gradiente
details.lists.waypoints=Pontos
details.lists.photos=Fotos
+details.lists.audio=\u00c1udio
details.photodetails=Detalhes da foto
details.nophoto=Nenhuma foto selecionada
details.photo.loading=Carregando
-details.photo.connected=Conectada
+details.media.connected=Conectada
+details.audiodetails=Detalhes do \u00e1udio
+details.noaudio=Nenhum arquivo de \u00e1udio selecionado
+details.audio.file=Arquivo de \u00e1udio
+details.audio.playing=reproduzindo...
map.overzoom=Nenhum mapa dispon\u00edvel neste n\u00edvel de amplia\u00e7\u00e3o
# Field names
# External urls
url.googlemaps=maps.google.com.br
+wikipedia.lang=pt
# Cardinals for 3d plots
cardinal.n=N
# Undo operations
undo.load=carregar dados
undo.loadphotos=carregar fotos
+undo.loadaudios=carregar arquivos de \u00e1udio
undo.editpoint=editar ponto
undo.deletepoint=remover ponto
-undo.deletephoto=remover foto
+undo.removephoto=remover foto
+undo.removeaudio=remover arquivo de \u00e1udio
undo.deleterange=remover intervalo
undo.compress=comprimir rota
undo.insert=inserir pontos
undo.addaltitudeoffset=adicionar diferen\u00e7a de altitude
undo.rearrangewaypoints=rearrumar pontos
undo.cutandmove=mover se\u00e7\u00e3o
-undo.connectphoto=conectar foto
-undo.disconnectphoto=desconectar foto
-undo.correlate=conectar fotos
+undo.connect=conectar
+undo.disconnect=desconectar
+undo.correlatephotos=conectar fotos
undo.rearrangephotos=rearrumar fotos
undo.createpoint=criar ponto
undo.rotatephoto=rotacionar foto
undo.convertnamestotimes=converter nomes para tempos
undo.lookupsrtm=procurar altitudes a partir do STRM
undo.deletefieldvalues=remover valores do campo
+undo.correlateaudios=\u00e1udios correlacionados
# Error messages
error.save.dialogtitle=Erro ao salvar dados
error.jpegload.dialogtitle=Erro ao carregar fotos
error.jpegload.nofilesfound=Nenhum arquivo encontrado
error.jpegload.nojpegsfound=Nenhum arquivo jpeg encontrado
-error.jpegload.noexiffound=Nenhuma informa\u00e7\u00e3o EXIF encontrada
error.jpegload.nogpsfound=Nenhuma informa\u00e7\u00e3o de GPS encontrada
error.jpegload.exifreadfailed=Falha ao ler informa\u00e7\u00f5es do EXIF. Nenhuma informa\u00e7\u00e3o do EXIF pode ser lida\nseja na biblioteca interna, seja na externa.
+error.audioload.nofilesfound=Nenhum arquivo de \u00e1udio encontrado
error.gpsload.unknown=Erro desconhecido
error.undofailed.title=Falha ao desfazer
error.undofailed.text=Falha para desfazer opera\u00e7\u00e3o
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
menu.range.end=Seteaza sfarsitul selectiei
menu.photo=Foto
menu.photo.saveexif=Salveaza la Exif
-menu.photo.connect=Conecteaza la punct
-menu.photo.disconnect=Deconecteaza de la punct
-menu.photo.delete=Elimina foto
+function.connecttopoint=Conecteaza la punct
+function.disconnectfrompoint=Deconecteaza de la punct
+function.removephoto=Elimina foto
menu.view=Vizualizare
menu.view.browser=Harta in browser
menu.view.browser.google=Harti Google
menu.range.end=S\u0131ran\u0131n sonu se\u00e7
menu.photo=Foto
menu.photo.saveexif=Exif'te kaydet
-menu.photo.connect=Noktaya ba\u011flan
-menu.photo.disconnect=Noktadan kopart
-menu.photo.delete=Fotoyu kald\u0131r
+function.connecttopoint=Noktaya ba\u011flan
+function.disconnectfrompoint=Noktadan kopart
+function.removephoto=Fotoyu kald\u0131r
menu.view=G\u00f6r\u00fcn\u00fcm
menu.view.browser=Taray\u0131c\u0131n\u0131n haritas\u0131
menu.view.browser.google=Google haritalar\u0131
dialog.gpsies.activity.biking=Bisiklet
dialog.gpsies.activity.sailing=Yelken
dialog.gpsies.activity.skating=Paten
-dialog.correlate.photoselect.photoname=Foto ad\u0131
-dialog.correlate.photoselect.photolater=Foto sonra
+dialog.correlate.select.photoname=Foto ad\u0131
+dialog.correlate.select.photolater=Foto sonra
dialog.correlate.options.offset.hours=saat,
dialog.correlate.options.offset.minutes=dakika ve
dialog.correlate.options.offset.seconds=saniye
dialog.correlate.options.photolater=Foto noktadan sonra
-dialog.correlate.options.pointlater=Nokta fotodan sonra
+dialog.correlate.options.pointlaterphoto=Nokta fotodan sonra
dialog.pastecoordinates.coords=Koordinatlar
dialog.help.help=Ayr\u0131nt\u0131l\u0131 bilgi ve kullanma k\u0131lavuzu i\u00e7in l\u00fctfen\n http://activityworkshop.net/software/prune/\n sitesinde bak.
dialog.about.version=S\u00fcr\u00fcm
dialog.colourchooser.green=Ye\u015fil
dialog.colourchooser.blue=Mavi
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
confirm.save.ok1=Ba\u011far\u0131yla kaydedildi
confirm.save.ok2=points to file
confirm.mergetracksegments=\u0130z par\u00e7alar\u0131 birle\u015ftirildi
-# Buttons || These are all the texts for buttons
+# Buttons
button.ok=Tamam
button.back=Geri
button.next=\u0130leri
filetype.pov=POV dosyalar\u0131
filetype.svg=SVG dosyalar\u0131
-# Display components || These are all for the side panels showing point/range details
+# Display components
display.nodata=Hergangi veri y\u00fcklenmedi
display.noaltitudes=Track verisinde y\u00fckseklik bilgisi yok
details.trackdetails=\u0130z ayr\u0131nt\u0131lar\u0131
display.range.time.days=g\u011fn
details.range.avespeed=Ortalama h\u0131z\u0131
details.range.avemovingspeed=Ortalama hareketi
-details.waypointsphotos.waypoints=Noktalar
-details.waypointsphotos.photos=Fotolar
+details.lists.waypoints=Noktalar
+details.lists.photos=Fotolar
details.photodetails=Foto ayr\u0131nt\u0131lar\u0131
details.nophoto=Herhangi foto se\u00e7ili de\u011fil
details.photo.loading=Y\u00fckleniyor
-details.photo.connected=Ba\u011fland\u0131
+details.media.connected=Ba\u011fland\u0131
map.overzoom=Fazla yak\u0131nla\u015ft\u0131n salak - harita g\u00f6r\u00fcnt\u00fclenmiyor
# Field names
cardinal.e=D
cardinal.w=B
-# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
+# Undo operations
undo.load=veri y\u00fckle
undo.loadphotos=fotolar y\u00fckle
undo.editpoint=noktay\u0131 d\u00fczenle
undo.deletepoint=noktay\u0131 sil
-undo.deletephoto=foto kald\u0131r
+undo.removephoto=foto kald\u0131r
undo.deleterange=s\u0131ra sil
undo.compress=izi s\u0131k\u0131\u015ft\u0131r
undo.insert=noktalar\u0131 ekle
undo.addaltitudeoffset=y\u00fckseklik de\u011fi\u015ftir
undo.rearrangewaypoints=noktalar\u0131 yeniden diz
undo.cutandmove=par\u00e7as\u0131 ta\u015f\u0131
-undo.connectphoto=fotoyu ba\u011flan
-undo.disconnectphoto=fotonun ba\u011flant\u0131s\u0131 kes
-undo.correlate=fotolar\u0131n ba\u011fl\u0131la\u015f\u0131m\u0131
+undo.connect=ba\u011flan
+undo.disconnect=ba\u011flant\u0131s\u0131 kes
+undo.correlatephotos=fotolar\u0131n ba\u011fl\u0131la\u015f\u0131m\u0131
undo.rearrangephotos=fotolar\u0131 yeniden diz
undo.createpoint=noktay\u0131 olu\u015ftur
undo.rotatephoto=fotoyu d\u00f6nder
menu.point.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9
menu.photo=\u76f8\u7247
menu.photo.saveexif=\u5750\u6807\u4fdd\u5b58\u81f3Exif
-menu.photo.connect=\u94fe\u63a5\u76f8\u7247
-menu.photo.disconnect=\u64a4\u9500\u94fe\u63a5
-menu.photo.delete=\u5220\u9664\u7167\u7247
+function.connecttopoint=\u94fe\u63a5\u76f8\u7247
+function.disconnectfrompoint=\u64a4\u9500\u94fe\u63a5
+function.removephoto=\u5220\u9664\u7167\u7247
menu.view=\u67e5\u770b
+menu.view.showsidebars=\u663e\u793a\u8fb9\u6846
menu.view.browser=\u5728\u6d4f\u89c8\u5668\u4e2d\u6253\u5f00\u5730\u56fe
menu.view.browser.google=Google\u5730\u56fe
menu.view.browser.openstreetmap=Openstreet\u5730\u56fe
function.exportkml=\u8f93\u51faKML\u6587\u4ef6
function.exportgpx=\u8f93\u51faGPX\u6587\u4ef6
function.exportpov=\u8f93\u51faPOV\u6587\u4ef6
+function.exportsvg=\u8f93\u51faSVG\u6587\u4ef6
function.editwaypointname=\u7f16\u8f91\u822a\u70b9\u540d
function.compress=\u538b\u7f29\u8f68\u8ff9(\u6807\u793a\u8981\u5220\u9664\u822a\u70b9\uff09
function.addtimeoffset=\u52a0\u5165\u65f6\u95f4\u5dee
function.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
function.convertnamestotimes=\u822a\u70b9\u540d\u79f0\u8f6c\u4e3a\u65f6\u95f4
+function.deletefieldvalues=\u5220\u9664\u533a\u57df\u6570\u503c
function.findwaypoint=\u67e5\u627e\u822a\u70b9
function.pastecoordinates=\u8f93\u5165\u65b0\u5750\u6807
function.charts=\u9ad8\u5ea6\u901f\u5ea6\u56fe\u8868
function.setkmzimagesize=\u8bbe\u7f6eKMZ\u56fe\u50cf\u5c3a\u5bf8
function.setpaths=\u8bbe\u7f6e\u7a0b\u5e8f\u8def\u5f84
function.getgpsies=Gpsies\u8f68\u8ff9
+function.uploadgpsies=\u8f68\u8ff9\u4e0a\u4f20\u5230 Gpsies
function.lookupsrtm=\u4eceSRTM\u83b7\u5f97\u9ad8\u5ea6\u4fe1\u606f
function.duplicatepoint=\u590d\u5236\u70b9
function.setcolours=\u8bbe\u7f6e\u989c\u8272
dialog.openoptions.deliminfo.norecords=\u65e0\u7eaa\u5f55
dialog.openoptions.altitudeunits=\u9ad8\u5ea6\u5355\u4f4d
dialog.open.contentsdoubled=\u6587\u4ef6\u542b\u6709\u4e24\u5957\u70b9\u4fe1\u606f\uff0c\u4e00\u5957\u822a\u70b9\u4fe1\u606f\u548c\u4e00\u5957\u8f68\u8ff9\u70b9\u4fe1\u606f
+dialog.selecttracks.intro=\u9009\u62e9\u8981\u5bfc\u5165\u7684\u8f68\u8ff9
+dialog.selecttracks.noname=\u672a\u547d\u540d
dialog.jpegload.subdirectories=\u542b\u6b21\u7ea7\u65b9\u4f4d
dialog.jpegload.loadjpegswithoutcoords=\u542b\u65e0\u5750\u6807\u70b9\u76f8\u7247
dialog.jpegload.loadjpegsoutsidearea=\u542b\u533a\u57df\u5916\u76f8\u7247
dialog.exportpov.ballsandsticks=\u7403\u548c\u6746
dialog.exportpov.tubesandwalls=\u7ba1\u548c\u5899
dialog.exportpov.warningtracksize=\u8f68\u8ff9\u542b\u6709\u592a\u591a\u822a\u70b9\uff0cJAVA3D\u53ef\u80fd\u65e0\u6cd5\u663e\u793a\n\u662f\u5426\u7ee7\u7eed\uff1f
+dialog.exportsvg.text=\u9009\u62e9\u8f93\u51faSVG\u6587\u4ef6\u7684\u53c2\u6570
+dialog.exportsvg.phi=\u963f\u6cfd\u8def\u89d2\u5ea6
+dialog.exportsvg.theta=\u9ad8\u7a0b\u89d2\u5ea6
+dialog.exportsvg.gradients=\u4f7f\u7528\u6e10\u53d8\u8272
dialog.pointtype.desc=\u4fdd\u5b58\u4e0b\u5217\u70b9\uff1a
dialog.pointtype.track=\u8f68\u8ff9\u70b9
dialog.pointtype.waypoint=\u822a\u70b9
dialog.gpsies.description=\u63cf\u8ff0
dialog.gpsies.nodescription=\u65e0\u63cf\u8ff0
dialog.gpsies.nonefound=\u672a\u627e\u5230\u8f68\u8ff9
-dialog.gpsies.activities=\u6d3b\u52a8,\u9002\u5408
+dialog.gpsies.username=Gpsies\u7f51\u7ad9\u7528\u6237\u540d
+dialog.gpsies.password=Gpsies\u7f51\u7ad9\u5bc6\u7801
+dialog.gpsies.keepprivate=\u8f68\u8ff9\u4fdd\u5bc6\uff08\u975e\u516c\u5f00\uff09
+dialog.gpsies.confirmopenpage=\u6253\u5f00\u4e0a\u4f20\u8f68\u8ff9\u7684\u7f51\u7ad9\uff1f
+dialog.gpsies.activities=\u6d3b\u52a8\u7c7b\u578b
dialog.gpsies.activity.trekking=\u5f92\u6b65
dialog.gpsies.activity.walking=\u7ade\u8d70
dialog.gpsies.activity.jogging=\u8dd1\u6b65
dialog.correlate.notimestamps=\u6570\u636e\u70b9\u4e2d\u65e0\u65f6\u95f4\u4fe1\u606f\uff0c\u76f8\u7247\u65e0\u6cd5\u94fe\u63a5
dialog.correlate.nouncorrelatedphotos=\u6240\u6709\u76f8\u7247\u5df2\u94fe\u63a5\n\u7ee7\u7eed\uff1f
dialog.correlate.photoselect.intro=\u9009\u62e9\u5df2\u94fe\u63a5\u76f8\u7247\u4f5c\u4e3a\u65f6\u95f4\u504f\u79fb
-dialog.correlate.photoselect.photoname=\u76f8\u7247\u540d
-dialog.correlate.photoselect.timediff=\u65f6\u95f4\u5dee
-dialog.correlate.photoselect.photolater=\u76f8\u7247\u5ef6\u540e
+dialog.correlate.select.photoname=\u76f8\u7247\u540d
+dialog.correlate.select.timediff=\u65f6\u95f4\u5dee
+dialog.correlate.select.photolater=\u76f8\u7247\u5ef6\u540e
dialog.correlate.options.tip=\u63d0\u793a\uff1a\u624b\u52a8\u94fe\u63a5\u81f3\u5c11\u4e00\u5f20\u76f8\u7247\uff0c\u53ef\u81ea\u52a8\u8ba1\u7b97\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.intro=\u9009\u62e9\u81ea\u52a8\u94fe\u63a5\u8bbe\u7f6e
dialog.correlate.options.offsetpanel=\u65f6\u95f4\u504f\u79fb
dialog.correlate.options.offset.minutes=\u5206\u949f
dialog.correlate.options.offset.seconds=\u79d2
dialog.correlate.options.photolater=\u76f8\u7247\u6ede\u540e\u4e8e\u8f68\u8ff9\u70b9
-dialog.correlate.options.pointlater=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u76f8\u7247
+dialog.correlate.options.pointlaterphoto=\u8f68\u8ff9\u70b9\u6ede\u540e\u4e8e\u76f8\u7247
dialog.correlate.options.limitspanel=\u94fe\u63a5\u9650\u5236
dialog.correlate.options.notimelimit=\u65e0\u65f6\u95f4\u9650\u5236
dialog.correlate.options.timelimit=\u65f6\u95f4\u9650\u5236
dialog.setpaths.found=\u627e\u5230\u8def\u5f84\uff1f
dialog.addaltitude.noaltitudes=\u8f68\u8ff9\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
dialog.addaltitude.desc=\u9ad8\u5ea6\u504f\u79fb
+dialog.lookupsrtm.overwritezeros=\u8986\u76d6\u9ad8\u5ea6\u503c\u4e3a\u96f6\u7684\u70b9\uff1f
dialog.setcolours.intro=\u70b9\u6309\u8272\u677f\u4ee5\u6539\u53d8\u989c\u8272
dialog.setcolours.background=\u80cc\u666f
dialog.setcolours.borders=\u8fb9\u754c
dialog.diskcache.dir=\u4fdd\u5b58\u8def\u5f84
dialog.diskcache.createdir=\u65b0\u5efa\u8def\u5f84
dialog.diskcache.nocreate=\u672a\u65b0\u5efa\u8def\u5f84
+dialog.deletefieldvalues.intro=\u9009\u62e9\u5f53\u524d\u8303\u56f4\u5185\u8981\u5220\u9664\u7684\u533a\u57df
# 3d window
dialog.3d.title=Prune 3D \u663e\u793a
-dialog.3d.altitudecap=\u9ad8\u5ea6\u6bd4\u4f8b
dialog.3dlines.title=Prune \u7f51\u683c\u7ebf
dialog.3dlines.empty=\u65e0\u6cd5\u663e\u793a\u7f51\u683c\u7ebf
dialog.3dlines.intro=3D \u7f51\u683c\u7ebf
-# Confirm messages || These are displayed as confirmation in the status bar
+# Confirm messages
confirm.loadfile=\u6570\u636e\u5df2\u4ece\u6587\u4ef6\u5bfc\u5165
confirm.save.ok1=\u4fdd\u5b58\u6210\u529f
confirm.save.ok2=\u5df2\u4fdd\u5b58\u7684\u8f68\u8ff9\u70b9
confirm.undo.multi=\u5df2\u64a4\u9500\u7684\u64cd\u4f5c
confirm.jpegload.single=\u5df2\u52a0\u5165\u76f8\u7247
confirm.jpegload.multi=\u5df2\u52a0\u5165\u76f8\u7247
-confirm.photo.connect=\u76f8\u7247\u5df2\u94fe\u63a5
confirm.photo.disconnect=\u76f8\u7247\u672a\u94fe\u63a5
-confirm.correlate.single=\u76f8\u7247\u5df2\u94fe\u63a5
-confirm.correlate.multi=\u76f8\u7247\u5df2\u94fe\u63a5
+confirm.correlatephotos.single=\u76f8\u7247\u5df2\u94fe\u63a5
+confirm.correlatephotos.multi=\u76f8\u7247\u5df2\u94fe\u63a5
confirm.createpoint=\u5df2\u521b\u5efa\u70b9
confirm.rotatephoto=\u76f8\u7247\u5df2\u65cb\u8f6c
confirm.running=\u8bf7\u7a0d\u7b49...
confirm.lookupsrtm1=\u627e\u5230
confirm.lookupsrtm2=\u9ad8\u5ea6\u503c
-# Buttons || These are all the texts for buttons
+# Buttons
button.ok=\u786e\u5b9a
button.back=\u8fd4\u56de
button.next=\u4e0b\u4e00\u6b65
filetype.gpx=GPX\u6587\u4ef6
filetype.pov=POV\u6587\u4ef6
filetype.svg=SVG\u6587\u4ef6
+filetype.audio=WAV,OGG,MP3\u6587\u4ef6
-# Display components || These are all for the side panels showing point/range details
+# Display components
display.nodata=\u65e0\u6570\u636e
display.noaltitudes=\u8f68\u8ff9\u6570\u636e\u4e0d\u542b\u9ad8\u5ea6\u4fe1\u606f
display.notimestamps=\u8f68\u8ff9\u6570\u636e\u672a\u542b\u65f6\u95f4\u4fe1\u606f
details.range.numsegments=\u6bb5\u6570
details.range.pace=\u6b65\u901f
details.range.gradient=\u5761\u5ea6
-details.waypointsphotos.waypoints=\u822a\u70b9
-details.waypointsphotos.photos=\u76f8\u7247
+details.lists.waypoints=\u822a\u70b9
+details.lists.photos=\u76f8\u7247
details.photodetails=\u76f8\u7247\u4fe1\u606f
details.nophoto=\u65e0\u76f8\u7247\u88ab\u9009\u4e2d
details.photo.loading=\u6b63\u5bfc\u5165
-details.photo.connected=\u5df2\u94fe\u63a5
+details.media.connected=\u5df2\u94fe\u63a5
map.overzoom=\u5728\u6b64\u653e\u5927\u5c3a\u5bf8\u4e0b\u65e0\u5730\u56fe\u8d44\u6599
# Field names
# External urls
url.googlemaps=ditu.google.cn
+wikipedia.lang=zh
# Cardinals for 3d plots
cardinal.n=N
cardinal.e=E
cardinal.w=W
-# Undo operations || These will be displayed in the undo list after you've performed the operation, to tell you what you did
+# Undo operations
undo.load=\u5bfc\u5165\u6570\u636e
undo.loadphotos=\u5bfc\u5165\u76f8\u7247
undo.editpoint=\u7f16\u8f91\u8f68\u8ff9\u70b9
undo.deletepoint=\u5220\u9664\u8f68\u8ff9\u70b9
-undo.deletephoto=\u5220\u9664\u76f8\u7247
+undo.removephoto=\u5220\u9664\u76f8\u7247
undo.deleterange=\u5220\u9664\u6bb5
undo.compress=\u538b\u7f29\u8f68\u8ff9
undo.insert=\u63d2\u5165\u822a\u70b9
undo.addaltitudeoffset=\u52a0\u5165\u9ad8\u5ea6\u504f\u79fb
undo.rearrangewaypoints=\u91cd\u65b0\u914d\u7f6e\u822a\u70b9
undo.cutandmove=\u79fb\u52a8\u6bb5
-undo.connectphoto=\u94fe\u63a5\u76f8\u7247
-undo.disconnectphoto=\u65ad\u5f00\u94fe\u63a5
-undo.correlate=\u94fe\u63a5\u76f8\u7247
+undo.connect=\u94fe\u63a5\u76f8\u7247
+undo.disconnect=\u65ad\u5f00\u94fe\u63a5
+undo.correlatephotos=\u94fe\u63a5\u76f8\u7247
undo.rearrangephotos=\u91cd\u62cd\u76f8\u7247
undo.createpoint=\u521b\u5efa\u8f68\u8ff9\u70b9
undo.rotatephoto=\u65cb\u8f6c\u76f8\u7247
error.jpegload.dialogtitle=\u5bfc\u5165\u76f8\u7247\u9519\u8bef
error.jpegload.nofilesfound=\u627e\u4e0d\u5230\u6587\u4ef6
error.jpegload.nojpegsfound=\u627e\u4e0d\u5230Jpeg\u6587\u4ef6
-error.jpegload.noexiffound=\u627e\u4e0d\u5230EXIF\u4fe1\u606f
error.jpegload.nogpsfound=\u627e\u4e0d\u5230GPS\u4fe1\u606f
error.jpegload.exifreadfailed=Exif\u8bfb\u53d6\u9519\u8bef\u3002\u9700\u8981\u5185\u90e8\n\u6216\u8005\u5916\u90e8\u5e93\u624d\u80fd\u8bfb\u53d6
error.gpsload.unknown=\u672a\u77e5\u9519\u8bef
--- /dev/null
+package tim.prune.load;
+
+/**
+ * File filter for audio files
+ */
+public class AudioFileFilter extends GenericFileFilter
+{
+ /** Constructor */
+ public AudioFileFilter()
+ {
+ super("filetype.audio", new String[] {"mp3", "ogg", "wav"});
+ }
+}
--- /dev/null
+package tim.prune.load;
+
+import java.io.File;
+import java.util.TreeSet;
+import javax.swing.JFileChooser;
+
+import tim.prune.App;
+import tim.prune.GenericFunction;
+import tim.prune.I18nManager;
+import tim.prune.UpdateMessageBroker;
+import tim.prune.config.Config;
+import tim.prune.data.AudioFile;
+import tim.prune.undo.UndoLoadAudios;
+
+/**
+ * Class to manage the loading of audio files
+ */
+public class AudioLoader extends GenericFunction
+{
+ private JFileChooser _fileChooser = null;
+ private GenericFileFilter _fileFilter = null;
+ private TreeSet<AudioFile> _fileList = null;
+
+
+ /**
+ * Constructor
+ * @param inApp Application object to inform of data load
+ */
+ public AudioLoader(App inApp)
+ {
+ super(inApp);
+ _fileFilter = new AudioFileFilter();
+ }
+
+ /** Get the name key */
+ public String getNameKey() {
+ return "function.loadaudio";
+ }
+
+ /**
+ * Open the GUI to select options and start the load
+ */
+ public void begin()
+ {
+ // Create file chooser if necessary
+ if (_fileChooser == null)
+ {
+ _fileChooser = new JFileChooser();
+ _fileChooser.setMultiSelectionEnabled(true);
+ _fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
+ _fileChooser.setFileFilter(_fileFilter);
+ _fileChooser.setDialogTitle(I18nManager.getText(getNameKey()));
+ // start from directory in config if already set by other operations
+ String configDir = Config.getConfigString(Config.KEY_PHOTO_DIR);
+ if (configDir == null) {configDir = Config.getConfigString(Config.KEY_TRACK_DIR);}
+ if (configDir != null) {_fileChooser.setCurrentDirectory(new File(configDir));}
+ }
+ // Show file dialog to choose file / directory(ies)
+ if (_fileChooser.showOpenDialog(_parentFrame) == JFileChooser.APPROVE_OPTION)
+ {
+ _fileList = new TreeSet<AudioFile>(new MediaSorter());
+ processFileList(_fileChooser.getSelectedFiles());
+ final int numFiles = _fileList.size();
+ if (numFiles == 0) {
+ _app.showErrorMessage(getNameKey(), "error.audioload.nofilesfound");
+ }
+ else
+ {
+ // Construct undo object
+ UndoLoadAudios undo = new UndoLoadAudios(numFiles);
+ _app.getTrackInfo().addAudios(_fileList);
+ _app.completeFunction(undo, I18nManager.getText("confirm.audioload"));
+ UpdateMessageBroker.informSubscribers();
+ }
+ }
+ }
+
+ /**
+ * Process an array of File objects to load
+ * @param inFiles array of selected Files
+ */
+ private void processFileList(File[] inFiles)
+ {
+ for (File file : inFiles)
+ {
+ if (file.exists() && file.canRead())
+ {
+ if (file.isFile()) {
+ if (_fileFilter.accept(file)) {
+ _fileList.add(new AudioFile(file));
+ }
+ }
+ else if (file.isDirectory()) {
+ processFileList(file.listFiles());
+ }
+ }
+ }
+ }
+}
_threeCharSuffixes = new String[inSuffixes.length];
_fourCharSuffixes = new String[inSuffixes.length];
int threeIndex = 0, fourIndex = 0;
- for (int i=0; i<inSuffixes.length; i++)
+ for (String suffix : inSuffixes)
{
- String suffix = inSuffixes[i];
if (suffix != null)
{
suffix = suffix.trim().toLowerCase();
{
if (inName != null)
{
- int nameLen = inName.length();
+ final int nameLen = inName.length();
if (nameLen > 4)
{
// Check for three character suffixes
* @param inAllowedSuffixes array of allowed suffixes
* @return true if accepted, false otherwise
*/
- public boolean acceptFilename(String inSuffixToCheck, String[] inAllowedSuffixes)
+ private static boolean acceptFilename(String inSuffixToCheck, String[] inAllowedSuffixes)
{
if (inSuffixToCheck != null && inAllowedSuffixes != null)
{
// Loop over allowed suffixes
- for (int i=0; i<inAllowedSuffixes.length; i++)
+ for (String allowed : inAllowedSuffixes)
{
- if (inAllowedSuffixes[i] != null && inSuffixToCheck.equals(inAllowedSuffixes[i]))
- {
+ if (allowed != null && inSuffixToCheck.equals(allowed)) {
return true;
}
}
{
return _filterDesc;
}
-
}
--- /dev/null
+package tim.prune.load;
+
+/**
+ * File filter for jpegs
+ */
+public class JpegFileFilter extends GenericFileFilter
+{
+ /** Constructor */
+ public JpegFileFilter()
+ {
+ super("filetype.jpeg", new String[] {"jpg", "jpe", "jpeg"});
+ }
+}
{
_app = inApp;
_parentFrame = inParentFrame;
- String[] fileTypes = {"jpg", "jpe", "jpeg"};
- _fileFilter = new GenericFileFilter("filetype.jpeg", fileTypes);
+ _fileFilter = new JpegFileFilter();
}
public void run()
{
// Initialise arrays, errors, summaries
- _fileCounts = new int[4]; // files, jpegs, exifs, gps
- _photos = new TreeSet<Photo>(new PhotoSorter());
+ _fileCounts = new int[3]; // files, jpegs, gps
+ _photos = new TreeSet<Photo>(new MediaSorter());
File[] files = _fileChooser.getSelectedFiles();
// Loop recursively over selected files/directories to count files
int numFiles = countFileList(files, true, _subdirCheckbox.isSelected());
_progressDialog.dispose(); // Sometimes dialog doesn't disappear without this dispose
if (_cancelled) {return;}
- //System.out.println("Finished - counts are: " + _fileCounts[0] + ", " + _fileCounts[1]
- // + ", " + _fileCounts[2] + ", " + _fileCounts[3]);
if (_fileCounts[0] == 0)
{
// No files found at all
_app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nojpegsfound");
}
else if (!_noExifCheckbox.isSelected() && _fileCounts[2] == 0)
- {
- // Need coordinates but no exif found
- _app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.noexiffound");
- }
- else if (!_noExifCheckbox.isSelected() && _fileCounts[3] == 0)
{
// Need coordinates but no gps information found
_app.showErrorMessage("error.jpegload.dialogtitle", "error.jpegload.nogpsfound");
if (!_fileFilter.acceptFilename(inFile.getName())) {return;}
// If it's a Jpeg, we can use ExifReader to get coords, otherwise we could try exiftool (if it's installed)
- // Create Photo object
- Photo photo = new Photo(inFile);
if (inFile.exists() && inFile.canRead()) {
_fileCounts[1]++; // jpeg found
}
+ Photo photo = createPhoto(inFile);
+ if (photo.getDataPoint() != null) {
+ _fileCounts[2]++; // photo has coordinates
+ }
+ // Check the criteria for adding the photo - check whether the photo has coordinates and if so if they're within the rectangle
+ if ( (photo.getDataPoint() != null || _noExifCheckbox.isSelected())
+ && (photo.getDataPoint() == null || !_outsideAreaCheckbox.isEnabled()
+ || _outsideAreaCheckbox.isSelected() || _trackRectangle.containsPoint(photo.getDataPoint())))
+ {
+ _photos.add(photo);
+ }
+ }
+
+ /**
+ * Create a Photo object for the given file, including reading exif information
+ * @param inFile file object
+ * @return Photo object
+ */
+ public static Photo createPhoto(File inFile)
+ {
+ // Create Photo object
+ Photo photo = new Photo(inFile);
// Try to get information out of exif
JpegData jpegData = ExifGateway.getJpegData(inFile);
Timestamp timestamp = null;
if (jpegData != null)
{
- if (jpegData.getExifDataPresent())
- {_fileCounts[2]++;} // exif found
if (jpegData.isGpsValid())
{
timestamp = createTimestamp(jpegData.getGpsDatestamp(), jpegData.getGpsTimestamp());
point.setSegmentStart(true);
photo.setDataPoint(point);
photo.setOriginalStatus(Photo.Status.TAGGED);
- _fileCounts[3]++;
}
// Use exif timestamp if gps timestamp not available
if (timestamp == null && jpegData.getOriginalTimestamp() != null) {
if (photo.getDataPoint() != null) {
photo.getDataPoint().setFieldValue(Field.TIMESTAMP, timestamp.getText(Timestamp.FORMAT_ISO_8601), false);
}
- // Check the criteria for adding the photo - check whether the photo has coordinates and if so if they're within the rectangle
- if ( (photo.getDataPoint() != null || _noExifCheckbox.isSelected())
- && (photo.getDataPoint() == null || !_outsideAreaCheckbox.isEnabled()
- || _outsideAreaCheckbox.isSelected() || _trackRectangle.containsPoint(photo.getDataPoint())))
- {
- _photos.add(photo);
- }
+ return photo;
}
--- /dev/null
+package tim.prune.load;
+
+import java.io.File;
+
+import tim.prune.data.AudioFile;
+import tim.prune.data.MediaFile;
+import tim.prune.data.MediaList;
+import tim.prune.data.Photo;
+import tim.prune.data.Track;
+
+/**
+ * Class to provide helper functions for loading media
+ */
+public abstract class MediaHelper
+{
+ /** File filters */
+ private static GenericFileFilter _jpegFilter = null, _audioFilter = null;
+
+ /**
+ * Construct a MediaFile object for the given path
+ * @param inPath path to file
+ * @return either Photo or AudioFile object as appropriate, or null
+ */
+ public static MediaFile createMediaFile(String inPath)
+ {
+ if (inPath == null) {return null;}
+ File file = new File(inPath);
+ if (!file.exists() || !file.canRead() || !file.isFile()) {return null;}
+ // Initialise filters if necessary
+ if (_jpegFilter == null) {
+ _jpegFilter = new JpegFileFilter();
+ _audioFilter = new AudioFileFilter();
+ }
+ // Check if filename looks like a jpeg
+ if (_jpegFilter.acceptFilename(file.getName())) {
+ return JpegLoader.createPhoto(file);
+ }
+ // Check if filename looks like an audio file
+ if (_audioFilter.acceptFilename(file.getName())) {
+ return new AudioFile(file);
+ }
+ // Neither photo nor audio
+ return null;
+ }
+
+ /**
+ * Add all the media from the given track into the specified list
+ * @param inTrack track from which media to take
+ * @param inMediaList list to which media should be added
+ * @param inMediaClass class of media, either Photo or AudioFile
+ */
+ public static void addMediaFromTrack(Track inTrack, MediaList inMediaList,
+ Class<?> inMediaClass)
+ {
+ final int numPoints = inTrack.getNumPoints();
+ for (int i=0; i<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);
+ }
+ }
+ }
+}
import java.io.File;
import java.util.Comparator;
+import tim.prune.data.MediaFile;
-import tim.prune.data.Photo;
/**
* Class to sort photos by name
*/
-public class PhotoSorter implements Comparator<Photo>
+public class MediaSorter implements Comparator<MediaFile>
{
/**
- * Compare two photos
+ * Compare two media files
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
- public int compare(Photo o1, Photo o2)
+ public int compare(MediaFile o1, MediaFile o2)
{
File file1 = o1.getFile();
File file2 = o2.getFile();
_pointNum++;
if (inIsTrackpoint)
{
- // System.out.println("Point " + _pointNum + " is in track '" + inTrackName + "' (" + inTrackNum + ")");
if (inTrackNum != _trackNum) {
_trackNames.add(inTrackName);
- _startIndices.add(new Integer(_pointNum));
+ _startIndices.add(Integer.valueOf(_pointNum));
}
}
_trackNum = inTrackNum;
if (inTrackNum < 0 || inTrackNum >= getNumTracks()) {return 0;}
return _startIndices.get(inTrackNum);
}
-
- /**
- * Print summary for debug
- * @deprecated
- */
- public void summarize()
- {
- System.out.println("File has " + getNumTracks() + " tracks:");
- for (int i=0; i<getNumTracks(); i++) {
- System.out.println(" Track " + i + " is called '" + _trackNames.get(i) + "' and has " + getNumPointsInTrack(i) + " points");
- }
- }
}
{
private boolean _insidePoint = false;
private boolean _insideWaypoint = false;
- private boolean _insideName = false;
- private boolean _insideElevation = false;
- private boolean _insideTime = false;
- private boolean _insideType = false;
private boolean _startSegment = true;
private boolean _isTrackPoint = false;
private int _trackNum = -1;
- private String _trackName = null;
- private String _name = null, _latitude = null, _longitude = null;
- private String _elevation = null;
- private String _time = null;
- private String _type = null;
+ 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 _currentTag = null;
private ArrayList<String[]> _pointList = new ArrayList<String[]>();
+ private ArrayList<String> _linkList = new ArrayList<String>();
private TrackNameList _trackNameList = new TrackNameList();
Attributes attributes) throws SAXException
{
// Read the parameters for waypoints and track points
- if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
+ String tag = qName.toLowerCase();
+ if (tag.equals("wpt") || tag.equals("trkpt") || tag.equals("rtept"))
{
_insidePoint = true;
- _insideWaypoint = qName.equalsIgnoreCase("wpt");
- _isTrackPoint = qName.equalsIgnoreCase("trkpt");
- int numAttributes = attributes.getLength();
+ _insideWaypoint = tag.equals("wpt");
+ _isTrackPoint = tag.equals("trkpt");
+ final int numAttributes = attributes.getLength();
for (int i=0; i<numAttributes; i++)
{
- String att = attributes.getQName(i);
+ String att = attributes.getQName(i).toLowerCase();
if (att.equals("lat")) {_latitude = attributes.getValue(i);}
else if (att.equals("lon")) {_longitude = attributes.getValue(i);}
}
- _elevation = null;
- _name = null;
- _time = null;
- _type = null;
+ _elevation.setValue(null);
+ _name.setValue(null);
+ _time.setValue(null);
+ _type.setValue(null);
+ _link.setValue(null);
}
- else if (qName.equalsIgnoreCase("ele"))
- {
- _insideElevation = true;
+ else if (tag.equals("ele")) {
+ _currentTag = _elevation;
}
- else if (qName.equalsIgnoreCase("name"))
- {
- _name = null;
- _insideName = true;
+ else if (tag.equals("name")) {
+ _currentTag = (_insidePoint?_name:_trackName);
}
- else if (qName.equalsIgnoreCase("time"))
- {
- _insideTime = true;
+ else if (tag.equals("time")) {
+ _currentTag = _time;
}
- else if (qName.equalsIgnoreCase("type"))
- {
- _insideType = true;
+ else if (tag.equals("type")) {
+ _currentTag = _type;
}
- else if (qName.equalsIgnoreCase("trkseg"))
- {
+ else if (tag.equals("link")) {
+ _link.setValue(attributes.getValue("href"));
+ }
+ else if (tag.equals("trkseg")) {
_startSegment = true;
}
- else if (qName.equalsIgnoreCase("trk"))
+ else if (tag.equals("trk"))
{
_trackNum++;
- _trackName = null;
+ _trackName.setValue(null);
}
super.startElement(uri, localName, qName, attributes);
}
public void endElement(String uri, String localName, String qName)
throws SAXException
{
- if (qName.equalsIgnoreCase("wpt") || qName.equalsIgnoreCase("trkpt") || qName.equalsIgnoreCase("rtept"))
+ String tag = qName.toLowerCase();
+ if (tag.equals("wpt") || tag.equals("trkpt") || tag.equals("rtept"))
{
processPoint();
_insidePoint = false;
}
- else if (qName.equalsIgnoreCase("ele"))
- {
- _insideElevation = false;
- }
- else if (qName.equalsIgnoreCase("name"))
- {
- _insideName = false;
- }
- else if (qName.equalsIgnoreCase("time"))
- {
- _insideTime = false;
- }
- else if (qName.equalsIgnoreCase("type"))
- {
- _insideType = false;
+ else {
+ _currentTag = null;
}
super.endElement(uri, localName, qName);
}
throws SAXException
{
String value = new String(ch, start, length);
- if (_insideName && _insideWaypoint) {_name = checkCharacters(_name, value);}
- if (_insideName && !_insidePoint) {_trackName = checkCharacters(_trackName, value);}
- else if (_insideElevation) {_elevation = checkCharacters(_elevation, value);}
- else if (_insideTime) {_time = checkCharacters(_time, value);}
- else if (_insideType) {_type = checkCharacters(_type, value);}
+ if (_currentTag != null) {
+ _currentTag.setValue(checkCharacters(_currentTag.getValue(), value));
+ }
super.characters(ch, start, length);
}
{
// Put the values into a String array matching the order in getFieldArray()
String[] values = new String[7];
- values[0] = _latitude; values[1] = _longitude;
- values[2] = _elevation; values[3] = _name;
- values[4] = _time;
+ values[0] = _latitude;
+ values[1] = _longitude;
+ values[2] = _elevation.getValue();
+ if (_insideWaypoint) {values[3] = _name.getValue();}
+ values[4] = _time.getValue();
if (_startSegment && !_insideWaypoint) {
values[5] = "1";
_startSegment = false;
}
- values[6] = _type;
+ values[6] = _type.getValue();
_pointList.add(values);
- _trackNameList.addPoint(_trackNum, _trackName, _isTrackPoint);
+ _trackNameList.addPoint(_trackNum, _trackName.getValue(), _isTrackPoint);
+ _linkList.add(_link.getValue());
}
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;
+ }
/**
* @return track name list
--- /dev/null
+package tim.prune.load.xml;
+
+/**
+ * Class to hold a single tag value from a gpx file
+ */
+public class GpxTag
+{
+ /** value of tag */
+ private String _value = null;
+
+ /**
+ * @param inVal value to set
+ */
+ public void setValue(String inVal) {
+ _value = inVal;
+ }
+
+ /**
+ * @return value
+ */
+ public String getValue() {
+ return _value;
+ }
+}
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());
+ Altitude.Format.METRES, sourceInfo, handler.getTrackNameList(), handler.getLinkArray());
}
}
catch (Exception e) {
import java.io.File;
import java.io.FileInputStream;
+import java.io.IOException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
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());
+ Altitude.Format.METRES, sourceInfo, _handler.getTrackNameList(), _handler.getLinkArray());
}
}
catch (Exception e)
{
- // clean up file stream
- try {
- inStream.close();
- }
- catch (Exception e2) {}
// Show error dialog
_app.showErrorMessageNoLookup("error.load.dialogtitle",
I18nManager.getText("error.load.othererror") + " " + e.getMessage());
}
+ finally {
+ try {inStream.close();} catch (IOException e2) {}
+ }
}
* Can be overriden (eg by gpx handler) to provide a track name list
* @return track name list object if any, or null
*/
- public TrackNameList getTrackNameList()
- {
+ public TrackNameList getTrackNameList() {
+ return null;
+ }
+
+ /**
+ * Can be overriden (eg by gpx handler) to provide an array of links to media
+ * @return array of Strings if any, or null
+ */
+ public String[] getLinkArray() {
return null;
}
}
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());
+ Altitude.Format.METRES, sourceInfo, handler.getTrackNameList(), handler.getLinkArray());
xmlFound = true;
}
}
-Prune version 11.2
-==================
+Prune version 12
+================
Prune is an application for viewing, editing and managing coordinate data from GPS systems,
including format conversion, charting and photo correlation.
=======
To run Prune from the jar file, simply call it from a command prompt or shell:
- java -jar prune_11.2.jar
+ java -jar prune_12.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_11.2.jar --lang=DE
-
-New with version 11.2
-=====================
+ java -jar prune_12.jar --lang=DE
-The following were added since version 11.1:
- - Correction of xml encoding for ISO8859_15 systems so exported GPX is loadable by Google Earth
- - Updating of Polish and Portuguese texts
-
-New with version 11.1
-=====================
-
-The following were added since version 11:
- - Bugfix for exporting non-ASCII characters to a GPX file on a non-UTF8 system
- - Bugfix for freeing file resources when GPX load fails
- - Bugfix for resizing photo thumbnail
- - Menu item for deselecting current photo
+New with version 12
+===================
+The following features were added since version 11:
+ - Options to load audio files, play them, connect to points and auto-correlate them
+ - Extend Gpx export to include links to photos and audio files
+ - Extend Gpx load to also load referenced photos, audio files
+ - Function to search for places in Wikipedia by name
+ - Function to search for places in Wikipedia near the current point
+ - Function to download OSM data using the XAPI and save to osm file
+ - New drawing mode to add points with one click each
+ - Hungarian translations thanks to György
+ - Korean translations thanks to HooAU
New with version 11
===================
- Statusbar showing confirmation of actions
- Much improved French texts thanks to generous user input
-New with version 4.1
-====================
-
-The following fix was added since version 4:
- - Exiftool calls now made in series rather than in parallel to avoid performance problems in Windows
-
New with version 4
==================
{
DataPoint point = track.getPoint(p);
boolean savePoint = ((point.isWaypoint() && _pointTypeSelector.getWaypointsSelected())
- || (!point.isWaypoint() && point.getPhoto()==null && _pointTypeSelector.getTrackpointsSelected())
- || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected()))
+ || (!point.isWaypoint() && !point.hasMedia() && _pointTypeSelector.getTrackpointsSelected())
+ || (!point.isWaypoint() && point.getPhoto()!=null && _pointTypeSelector.getPhotopointsSelected())
+ || (!point.isWaypoint() && point.getAudio()!=null && _pointTypeSelector.getAudiopointsSelected()))
&& (!_pointTypeSelector.getJustSelection() || (p>=selStart && p<=selEnd));
if (!savePoint) {continue;}
numSaved++;
info = _model.getFieldInfo(f);
if (info.isSelected())
{
- if (!firstField)
- {
- // output field separator
+ // output field separator
+ if (!firstField) {
buffer.append(delimiter);
}
saveField(buffer, point, info.getField(), coordFormat, altitudeFormat, timestampFormat);
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
gridPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 20));
mainPanel.add(gridPanel);
+ // close dialog when escape pressed
+ KeyAdapter closer = new KeyAdapter() {
+ public void keyReleased(KeyEvent e)
+ {
+ // close dialog if escape pressed
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ _dialog.dispose();
+ }
+ }
+ };
+ _deviceField.addKeyListener(closer);
+ _formatField.addKeyListener(closer);
+ _trackNameField.addKeyListener(closer);
// checkboxes
ChangeListener checkboxListener = new ChangeListener() {
if (trackName == null || trackName.equals("")) {trackName = "prune";}
// Generate the GPX file and send to the GPS
OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream());
- boolean[] saveFlags = {true, true, true, false, true}; // export everything
+ boolean[] saveFlags = {true, true, true, true, false, true}; // export everything
GpxExporter.exportData(writer, _app.getTrackInfo(), trackName, null, saveFlags, false);
writer.close();
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import tim.prune.UpdateMessageBroker;
import tim.prune.config.Config;
import tim.prune.data.Altitude;
+import tim.prune.data.AudioFile;
import tim.prune.data.Coordinate;
import tim.prune.data.DataPoint;
import tim.prune.data.Field;
+import tim.prune.data.MediaFile;
+import tim.prune.data.Photo;
import tim.prune.data.Timestamp;
import tim.prune.data.TrackInfo;
+import tim.prune.gui.DialogCloser;
import tim.prune.load.GenericFileFilter;
import tim.prune.save.xml.GpxCacherList;
dialogPanel.add(mainPanel, BorderLayout.CENTER);
// close dialog if escape pressed
- _nameField.addKeyListener(new KeyAdapter() {
- public void keyReleased(KeyEvent e) {
- if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
- _dialog.dispose();
- }
- }
- });
+ _nameField.addKeyListener(new DialogCloser(_dialog));
// button panel at bottom
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
{
// normal writing to file
writer = new OutputStreamWriter(new FileOutputStream(_exportFile));
- boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
- _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getJustSelection(),
- _timestampsCheckbox.isSelected()};
+ final boolean[] saveFlags = {_pointTypeSelector.getTrackpointsSelected(), _pointTypeSelector.getWaypointsSelected(),
+ _pointTypeSelector.getPhotopointsSelected(), _pointTypeSelector.getAudiopointsSelected(),
+ _pointTypeSelector.getJustSelection(), _timestampsCheckbox.isSelected()};
// write file
final int numPoints = exportData(writer, _trackInfo, _nameField.getText(),
_descriptionField.getText(), saveFlags, _copySourceCheckbox.isSelected());
* @param inInfo track info object
* @param inName name of track (optional)
* @param inDesc description of track (optional)
- * @param inSaveFlags array of booleans to export tracks, waypoints, photos, timestamps
+ * @param inSaveFlags array of booleans to export tracks, waypoints, photos, audios, selection, timestamps
* @param inUseCopy true to copy source if available
* @return number of points written
* @throws IOException if io errors occur on write
int i = 0;
DataPoint point = null;
- boolean hasTrackpoints = false;
final boolean exportTrackpoints = inSaveFlags[0];
final boolean exportWaypoints = inSaveFlags[1];
final boolean exportPhotos = inSaveFlags[2];
- final boolean exportSelection = inSaveFlags[3];
- final boolean exportTimestamps = inSaveFlags[4];
+ final boolean exportAudios = inSaveFlags[3];
+ final boolean exportSelection = inSaveFlags[4];
+ final boolean exportTimestamps = inSaveFlags[5];
// Examine selection
int selStart = -1, selEnd = -1;
if (exportSelection) {
inWriter.write('\n');
}
else {
- exportWaypoint(point, inWriter, exportTimestamps);
+ exportWaypoint(point, inWriter, exportTimestamps, exportPhotos, exportAudios);
}
numSaved++;
}
}
- else {
- hasTrackpoints = true;
- }
}
}
// Export both route points and then track points
- if (hasTrackpoints && (exportTrackpoints || exportPhotos))
+ if (exportTrackpoints || exportPhotos || exportAudios)
{
// Output all route points (if any)
numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
- exportTimestamps, true, gpxCachers, "<rtept", "\t<rte><number>1</number>\n", null, "\t</rte>\n");
+ exportAudios, exportTimestamps, true, gpxCachers, "<rtept", "\t<rte><number>1</number>\n",
+ null, "\t</rte>\n");
// Output all track points, if any
String trackStart = "\t<trk><name>" + trackName + "</name><number>1</number><trkseg>\n";
numSaved += writeTrackPoints(inWriter, inInfo, exportSelection, exportTrackpoints, exportPhotos,
- exportTimestamps, false, gpxCachers, "<trkpt", trackStart, "\t</trkseg>\n\t<trkseg>\n",
- "\t</trkseg></trk>\n");
+ exportAudios, exportTimestamps, false, gpxCachers, "<trkpt", trackStart,
+ "\t</trkseg>\n\t<trkseg>\n", "\t</trkseg></trk>\n");
}
inWriter.write("</gpx>\n");
* @param inExportSelection true to just output current selection
* @param inExportTrackpoints true to output track points
* @param inExportPhotos true to output photo points
- * @param exportTimestamps true to include timestamps in export
+ * @param inExportAudios true to output audio points
+ * @param inExportTimestamps true to include timestamps in export
* @param inOnlyCopies true to only export if source can be copied
* @param inCachers list of GpxCachers
* @param inPointTag tag to match for each point
*/
private static int writeTrackPoints(OutputStreamWriter inWriter,
TrackInfo inInfo, boolean inExportSelection, boolean inExportTrackpoints,
- boolean inExportPhotos, boolean exportTimestamps, boolean inOnlyCopies,
- GpxCacherList inCachers, String inPointTag, String inStartTag,
- String inSegmentTag, String inEndTag)
+ boolean inExportPhotos, boolean inExportAudios, boolean exportTimestamps,
+ boolean inOnlyCopies, GpxCacherList inCachers, String inPointTag,
+ String inStartTag, String inSegmentTag, String inEndTag)
throws IOException
{
// Note: far too many input parameters to this method but avoids duplication
DataPoint point = inInfo.getTrack().getPoint(i);
if ((!inExportSelection || (i>=selStart && i<=selEnd)) && !point.isWaypoint())
{
- if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos))
+ if ((point.getPhoto()==null && inExportTrackpoints) || (point.getPhoto()!=null && inExportPhotos)
+ || (point.getAudio()!=null && inExportAudios))
{
+ if (point.getPhoto() != null) System.out.println("Writetrackpoints: Point has photo " + point.getPhoto().getFile().getName());
// get the source from the point (if any)
String pointSource = getPointSource(inCachers, point);
- boolean writePoint = (pointSource != null && pointSource.toLowerCase().startsWith(inPointTag))
- || (pointSource == null && !inOnlyCopies);
- if (writePoint)
+ // Clear point source if it's the wrong type of point (eg changed from waypoint or route point)
+ if (pointSource != null && !pointSource.toLowerCase().startsWith(inPointTag)) {pointSource = null;}
+ if (pointSource != null || !inOnlyCopies)
{
// restart track segment if necessary
if ((numSaved > 0) && point.getSegmentStart() && (inSegmentTag != null)) {
}
if (numSaved == 0) {inWriter.write(inStartTag);}
if (pointSource != null) {
+ if (point.getPhoto() != null) System.out.println("Point has photo but using source");
inWriter.write(pointSource);
inWriter.write('\n');
}
else {
- if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps);}
+ if (!inOnlyCopies) {exportTrackpoint(point, inWriter, exportTimestamps, inExportPhotos, inExportAudios);}
}
numSaved++;
}
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
+ // photo / audio links
+ if (source != null && (inPoint.hasMedia() || source.indexOf("</link>") > 0)) {
+ source = replaceMediaLinks(source, makeMediaLink(inPoint));
+ }
return source;
}
return null;
}
+ /**
+ * Replace the media tags in the given XML string
+ * @param inSource source XML for point
+ * @param inValue value for the current point
+ * @return modified String, or null if not possible
+ */
+ private static String replaceMediaLinks(String inSource, String inValue)
+ {
+ if (inSource == null) {return null;}
+ // Note that this method is very similar to replaceGpxTags except there can be multiple link tags
+ // and the tags must have attributes. So either one heavily parameterized method or two.
+ // Look for start and end tags within source
+ final String STARTTEXT = "<link";
+ final String ENDTEXT = "</link>";
+ final int startPos = inSource.indexOf(STARTTEXT);
+ final int endPos = inSource.lastIndexOf(ENDTEXT);
+ if (startPos > 0 && endPos > 0)
+ {
+ String origValue = inSource.substring(startPos, endPos + ENDTEXT.length());
+ if (inValue != null && origValue.equals(inValue)) {
+ // Value unchanged
+ return inSource;
+ }
+ else if (inValue == null || inValue.equals("")) {
+ // Need to delete value
+ return inSource.substring(0, startPos) + inSource.substring(endPos + ENDTEXT.length());
+ }
+ else {
+ // Need to replace value
+ return inSource.substring(0, startPos) + inValue + inSource.substring(endPos + ENDTEXT.length());
+ }
+ }
+ // Value not found for this field in original source
+ if (inValue == null || inValue.equals("")) {return inSource;}
+ return null;
+ }
+
/**
* Get the header string for the xml document including encoding
* @param inWriter writer object
* @param inPoint waypoint to export
* @param inWriter writer object
* @param inTimestamps true to export timestamps too
+ * @param inPhoto true to export link to photo
+ * @param inAudio true to export link to audio
* @throws IOException on write failure
*/
- private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
+ private static void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
+ boolean inPhoto, boolean inAudio)
throws IOException
{
inWriter.write("\t<wpt lat=\"");
inWriter.write("\t\t<name>");
inWriter.write(inPoint.getWaypointName().trim());
inWriter.write("</name>\n");
+ // Media links, if any
+ if (inPhoto && inPoint.getPhoto() != null)
+ {
+ inWriter.write("\t\t");
+ inWriter.write(makeMediaLink(inPoint.getPhoto()));
+ inWriter.write('\n');
+ }
+ if (inAudio && inPoint.getAudio() != null)
+ {
+ inWriter.write("\t\t");
+ inWriter.write(makeMediaLink(inPoint.getAudio()));
+ inWriter.write('\n');
+ }
// write waypoint type if any
String type = inPoint.getFieldValue(Field.WAYPT_TYPE);
if (type != null)
* @param inPoint trackpoint to export
* @param inWriter writer object
* @param inTimestamps true to export timestamps too
+ * @param inExportPhoto true to export photo link
+ * @param inExportAudio true to export audio link
*/
- private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps)
+ private static void exportTrackpoint(DataPoint inPoint, Writer inWriter, boolean inTimestamps,
+ boolean inExportPhoto, boolean inExportAudio)
throws IOException
{
+ if (inPoint.getPhoto() != null) System.out.println("Point has photo " + inPoint.getPhoto().getFile().getName());
inWriter.write("\t\t<trkpt lat=\"");
inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
inWriter.write("\" lon=\"");
inWriter.write(inPoint.getTimestamp().getText(Timestamp.FORMAT_ISO_8601));
inWriter.write("</time>");
}
+ // photo, audio
+ if (inPoint.getPhoto() != null && inExportPhoto) {
+ inWriter.write(makeMediaLink(inPoint.getPhoto()));
+ }
+ if (inPoint.getAudio() != null && inExportAudio) {
+ inWriter.write(makeMediaLink(inPoint.getAudio()));
+ }
inWriter.write("</trkpt>\n");
}
+
+ /**
+ * Make the xml for the media link(s)
+ * @param inPoint point to generate text for
+ * @return link tags, or null if no links
+ */
+ private static String makeMediaLink(DataPoint inPoint)
+ {
+ Photo photo = inPoint.getPhoto();
+ AudioFile audio = inPoint.getAudio();
+ if (photo == null && audio == null) {
+ return null;
+ }
+ String linkText = "";
+ if (photo != null) {
+ linkText = makeMediaLink(photo);
+ }
+ if (audio != null) {
+ linkText += makeMediaLink(audio);
+ }
+ return linkText;
+ }
+
+ /**
+ * Make the media link for a single media item
+ * @param inMedia media item, either photo or audio
+ * @return link for this media
+ */
+ private static String makeMediaLink(MediaFile inMedia)
+ {
+ return "<link href=\"" + inMedia.getFile().getAbsolutePath() + "\"><text>" + inMedia.getFile().getName() + "</text></link>";
+ }
}
import tim.prune.data.TrackInfo;
import tim.prune.gui.ColourChooser;
import tim.prune.gui.ColourPatch;
+import tim.prune.gui.DialogCloser;
import tim.prune.gui.ImageUtils;
import tim.prune.load.GenericFileFilter;
// Default width and height of thumbnail images in Kmz
private static final int DEFAULT_THUMBNAIL_WIDTH = 240;
private static final int DEFAULT_THUMBNAIL_HEIGHT = 240;
- // Actual selected width and height of thumbnail images in Kmz
- private static int THUMBNAIL_WIDTH = 0;
- private static int THUMBNAIL_HEIGHT = 0;
// Default track colour
private static final Color DEFAULT_TRACK_COLOUR = new Color(204, 0, 0); // red
descPanel.setLayout(new FlowLayout());
descPanel.add(new JLabel(I18nManager.getText("dialog.exportkml.text")));
_descriptionField = new JTextField(20);
+ _descriptionField.addKeyListener(new DialogCloser(_dialog));
descPanel.add(_descriptionField);
descPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(descPanel);
_progressBar.setMaximum(exportImages?getNumPhotosToExport():1);
// Determine photo thumbnail size from config
- THUMBNAIL_WIDTH = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH);
- if (THUMBNAIL_WIDTH < DEFAULT_THUMBNAIL_WIDTH) {THUMBNAIL_WIDTH = DEFAULT_THUMBNAIL_WIDTH;}
- THUMBNAIL_HEIGHT = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT);
- if (THUMBNAIL_HEIGHT < DEFAULT_THUMBNAIL_HEIGHT) {THUMBNAIL_HEIGHT = DEFAULT_THUMBNAIL_HEIGHT;}
+ int thumbWidth = Config.getConfigInt(Config.KEY_KMZ_IMAGE_WIDTH);
+ if (thumbWidth < DEFAULT_THUMBNAIL_WIDTH) {thumbWidth = DEFAULT_THUMBNAIL_WIDTH;}
+ int thumbHeight = Config.getConfigInt(Config.KEY_KMZ_IMAGE_HEIGHT);
+ if (thumbHeight < DEFAULT_THUMBNAIL_HEIGHT) {thumbHeight = DEFAULT_THUMBNAIL_HEIGHT;}
// Create array for image dimensions in case it's required
_imageDimensions = new Dimension[_track.getNumPoints()];
{
// Create thumbnails of each photo in turn and add to zip as images/image<n>.jpg
// This is done first so that photo sizes are known for later
- exportThumbnails(zipOutputStream);
+ exportThumbnails(zipOutputStream, thumbWidth, thumbHeight);
}
writer = new OutputStreamWriter(zipOutputStream);
// Make an entry in the zip file for the kml file
boolean writeTrack = _pointTypeSelector.getTrackpointsSelected();
boolean writeWaypoints = _pointTypeSelector.getWaypointsSelected();
boolean writePhotos = _pointTypeSelector.getPhotopointsSelected();
+ boolean writeAudios = _pointTypeSelector.getAudiopointsSelected();
boolean justSelection = _pointTypeSelector.getJustSelection();
inWriter.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://earth.google.com/kml/2.1\">\n<Folder>\n");
inWriter.write("\t<name>");
{
inWriter.write(_descriptionField.getText());
}
- else
- {
+ else {
inWriter.write("Export from Prune");
}
inWriter.write("</name>\n");
int i = 0;
DataPoint point = null;
boolean hasTrackpoints = false;
- boolean writtenPhotoHeader = false;
+ boolean writtenPhotoHeader = false, writtenAudioHeader = false;
final int numPoints = _track.getNumPoints();
int numSaved = 0;
int photoNum = 0;
numSaved++;
}
}
- else if (point.getPhoto() == null)
+ else if (!point.hasMedia())
{
hasTrackpoints = true;
}
exportPhotoPoint(point, inWriter, inExportImages, i, photoNum, absoluteAltitudes);
numSaved++;
}
+ // Make a blob with description for each audio file
+ if (point.getAudio() != null && writeAudios && writeCurrentPoint)
+ {
+ if (!writtenAudioHeader)
+ {
+ inWriter.write("<Style id=\"audio_icon\"><IconStyle><color>ff00ffff</color><Icon><href>http://maps.google.com/mapfiles/kml/shapes/star.png</href></Icon></IconStyle></Style>");
+ writtenAudioHeader = true;
+ }
+ exportAudioPoint(point, inWriter, absoluteAltitudes);
+ numSaved++;
+ }
}
// Make a line for the track, if there is one
if (hasTrackpoints && writeTrack)
*/
private void exportWaypoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException
{
- inWriter.write("\t<Placemark>\n\t\t<name>");
- inWriter.write(inPoint.getWaypointName().trim());
- inWriter.write("</name>\n");
- inWriter.write("\t\t<Point>\n");
- if (inAbsoluteAltitude && inPoint.hasAltitude()) {
- inWriter.write("\t\t\t<altitudeMode>absolute</altitudeMode>\n");
- }
- else {
- inWriter.write("\t\t\t<altitudeMode>clampToGround</altitudeMode>\n");
- }
- inWriter.write("\t\t\t<coordinates>");
- inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
- inWriter.write(',');
- inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
- inWriter.write(",");
- if (inPoint.hasAltitude()) {
- inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
- }
- else {
- inWriter.write("0");
- }
- inWriter.write("</coordinates>\n\t\t</Point>\n\t</Placemark>\n");
+ String name = inPoint.getWaypointName().trim();
+ exportNamedPoint(inPoint, inWriter, name, null, null, inAbsoluteAltitude);
+ }
+
+
+ /**
+ * Export the specified audio point into the file
+ * @param inPoint audio point to export
+ * @param inWriter writer object
+ * @param inAbsoluteAltitude true for absolute altitude
+ * @throws IOException on write failure
+ */
+ private void exportAudioPoint(DataPoint inPoint, Writer inWriter, boolean inAbsoluteAltitude) throws IOException
+ {
+ String name = inPoint.getAudio().getFile().getName();
+ String desc = inPoint.getAudio().getFile().getAbsolutePath();
+ exportNamedPoint(inPoint, inWriter, name, desc, "audio_icon", inAbsoluteAltitude);
}
int inPointNumber, int inImageNumber, boolean inAbsoluteAltitude)
throws IOException
{
- inWriter.write("\t<Placemark>\n\t\t<name>");
- inWriter.write(inPoint.getPhoto().getFile().getName());
- inWriter.write("</name>\n");
+ String name = inPoint.getPhoto().getFile().getName();
+ String desc = null;
if (inImageLink)
{
Dimension imageSize = _imageDimensions[inPointNumber];
- // Write out some html for the thumbnail images
- inWriter.write("<description><![CDATA[<br/><table border='0'><tr><td><center><img src='images/image"
+ // 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>]]></description>");
+ + "<tr><td><center>" + inPoint.getPhoto().getFile().getName() + "</center></td></tr></table>]]>";
+ }
+ // Export point
+ exportNamedPoint(inPoint, inWriter, name, desc, "camera_icon", inAbsoluteAltitude);
+ }
+
+
+ /**
+ * Export the specified named point into the file, like waypoint or photo point
+ * @param inPoint data point
+ * @param inWriter writer object
+ * @param inName name of point
+ * @param inDesc description of point, or null
+ * @param inStyle style of point, or null
+ * @param inAbsoluteAltitude true for absolute altitudes
+ * @throws IOException on write failure
+ */
+ private void exportNamedPoint(DataPoint inPoint, Writer inWriter, String inName,
+ String inDesc, String inStyle, boolean inAbsoluteAltitude)
+ throws IOException
+ {
+ inWriter.write("\t<Placemark>\n\t\t<name>");
+ inWriter.write(inName);
+ inWriter.write("</name>\n");
+ if (inDesc != null)
+ {
+ // Write out description
+ inWriter.write("<description>");
+ inWriter.write(inDesc);
+ inWriter.write("</description>");
+ }
+ if (inStyle != null)
+ {
+ inWriter.write("<styleUrl>#");
+ inWriter.write(inStyle);
+ inWriter.write("</styleUrl>\n");
}
- inWriter.write("<styleUrl>#camera_icon</styleUrl>\n");
inWriter.write("\t\t<Point>\n");
if (inAbsoluteAltitude && inPoint.hasAltitude()) {
inWriter.write("\t\t\t<altitudeMode>absolute</altitudeMode>\n");
inWriter.write(inPoint.getLongitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
inWriter.write(',');
inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
- inWriter.write(",");
+ inWriter.write(',');
// Altitude if point has one
if (inPoint.hasAltitude()) {
inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
}
else {
- inWriter.write("0");
+ inWriter.write('0');
}
inWriter.write("</coordinates>\n\t\t</Point>\n\t</Placemark>\n");
}
inWriter.write(',');
inWriter.write(inPoint.getLatitude().output(Coordinate.FORMAT_DECIMAL_FORCE_POINT));
// Altitude if point has one
- inWriter.write(",");
+ inWriter.write(',');
if (inPoint.hasAltitude()) {
inWriter.write("" + inPoint.getAltitude().getStringValue(Altitude.Format.METRES));
}
else {
- inWriter.write("0");
+ inWriter.write('0');
}
- inWriter.write("\n");
+ inWriter.write('\n');
}
/**
* Loop through the photos and create thumbnails
* @param inZipStream zip stream to save image files to
+ * @param inThumbWidth thumbnail width
+ * @param inThumbHeight thumbnail height
*/
- private void exportThumbnails(ZipOutputStream inZipStream) throws IOException
+ private void exportThumbnails(ZipOutputStream inZipStream, int inThumbWidth, int inThumbHeight)
+ throws IOException
{
// set up image writer
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
// Scale and smooth image to required size
BufferedImage bufferedImage = ImageUtils.rotateImage(icon.getImage(),
- THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT, point.getPhoto().getRotationDegrees());
+ inThumbWidth, inThumbHeight, point.getPhoto().getRotationDegrees());
// Store image dimensions so that it doesn't have to be calculated again for the points
_imageDimensions[i] = new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight());
/**
* GUI element to allow the selection of point types for saving,
- * including checkboxes for track points, waypoints, photo points
+ * including checkboxes for track points, waypoints, photo points, audio points
* and also a checkbox for the current selection
*/
public class PointTypeSelector extends JPanel
{
/** Array of checkboxes */
- private JCheckBox[] _checkboxes = new JCheckBox[4];
+ private JCheckBox[] _checkboxes = new JCheckBox[5];
+ /** Grid panel for top row */
+ private JPanel _gridPanel = null;
/**
private void createComponents()
{
setLayout(new BorderLayout());
- // Need label to explain what it is
add(new JLabel(I18nManager.getText("dialog.pointtype.desc")), BorderLayout.NORTH);
// panel for the checkboxes
- JPanel gridPanel = new JPanel();
- gridPanel.setLayout(new GridLayout(0, 3, 15, 3));
- final String[] keys = {"track", "waypoint", "photo"};
- for (int i=0; i<3; i++)
+ _gridPanel = new JPanel();
+ _gridPanel.setLayout(new GridLayout(0, 3, 15, 3));
+ final String[] keys = {"track", "waypoint", "photo", "audio"};
+ for (int i=0; i<4; i++)
{
_checkboxes[i] = new JCheckBox(I18nManager.getText("dialog.pointtype." + keys[i]));
_checkboxes[i].setSelected(true);
- gridPanel.add(_checkboxes[i]);
+ if (i<3) {_gridPanel.add(_checkboxes[i]);}
}
- add(gridPanel, BorderLayout.CENTER);
- _checkboxes[3] = new JCheckBox(I18nManager.getText("dialog.pointtype.selection"));
- add(_checkboxes[3], BorderLayout.SOUTH);
+ add(_gridPanel, BorderLayout.CENTER);
+ _checkboxes[4] = new JCheckBox(I18nManager.getText("dialog.pointtype.selection"));
+ add(_checkboxes[4], BorderLayout.SOUTH);
}
+
/**
* Initialize the checkboxes from the given data
* @param inTrackInfo TrackInfo object
public void init(TrackInfo inTrackInfo)
{
// Get whether track has track points, waypoints, photos
- boolean[] flags = {inTrackInfo.getTrack().hasTrackPoints(),
+ boolean[] dataFlags = {inTrackInfo.getTrack().hasTrackPoints(),
inTrackInfo.getTrack().hasWaypoints(),
- inTrackInfo.getPhotoList().getNumPhotos() > 0
+ inTrackInfo.getPhotoList().getNumPhotos() > 0,
+ inTrackInfo.getAudioList().getNumAudios() > 0
};
+ // Rearrange grid to just show the appropriate entries
+ final boolean[] showFlags = {true, true, dataFlags[2] || !dataFlags[3], dataFlags[3]};
+ _gridPanel.removeAll();
+ for (int i=0; i<4; i++) {
+ if (showFlags[i]) {_gridPanel.add(_checkboxes[i]);}
+ }
// Enable or disable checkboxes according to data present
- for (int i=0; i<3; i++)
+ for (int i=0; i<4; i++)
{
- if (flags[i]) {
+ if (dataFlags[i]) {
+ if (!_checkboxes[i].isEnabled()) {_checkboxes[i].setSelected(true);}
_checkboxes[i].setEnabled(true);
}
else {
_checkboxes[i].setEnabled(false);
}
}
- _checkboxes[3].setEnabled(inTrackInfo.getSelection().hasRangeSelected());
- _checkboxes[3].setSelected(false);
+ _checkboxes[4].setEnabled(inTrackInfo.getSelection().hasRangeSelected());
+ _checkboxes[4].setSelected(false);
}
/**
return _checkboxes[2].isSelected();
}
+ /**
+ * @return true if audio points selected
+ */
+ public boolean getAudiopointsSelected()
+ {
+ return _checkboxes[3].isSelected();
+ }
+
/**
* @return true if only the current selection should be saved
*/
public boolean getJustSelection()
{
- return _checkboxes[3].isSelected();
+ return _checkboxes[4].isSelected();
}
/**
public boolean getAnythingSelected()
{
return getTrackpointsSelected() || getWaypointsSelected()
- || getPhotopointsSelected();
+ || getPhotopointsSelected() || getAudiopointsSelected();
}
}
import tim.prune.data.NumberUtils;
import tim.prune.data.Track;
import tim.prune.function.Export3dFunction;
+import tim.prune.gui.DialogCloser;
import tim.prune.load.GenericFileFilter;
import tim.prune.threedee.LineDialog;
import tim.prune.threedee.ThreeDModel;
}
_fontName = new JTextField(defaultFont, 12);
_fontName.setAlignmentX(Component.LEFT_ALIGNMENT);
+ _fontName.addKeyListener(new DialogCloser(_dialog));
centralPanel.add(_fontName);
//coordinates of camera
JLabel cameraXLabel = new JLabel(I18nManager.getText("dialog.exportpov.camerax"));
import tim.prune.config.Config;
import tim.prune.data.Track;
import tim.prune.function.Export3dFunction;
+import tim.prune.gui.DialogCloser;
import tim.prune.load.GenericFileFilter;
import tim.prune.threedee.ThreeDModel;
buttonPanel.add(okButton);
JButton cancelButton = new JButton(I18nManager.getText("button.cancel"));
cancelButton.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e)
- {
+ public void actionPerformed(ActionEvent e) {
_dialog.dispose();
}
});
phiLabel.setHorizontalAlignment(SwingConstants.TRAILING);
centralPanel.add(phiLabel);
_phiField = new JTextField("" + _phi);
+ _phiField.addKeyListener(new DialogCloser(_dialog));
centralPanel.add(_phiField);
JLabel thetaLabel = new JLabel(I18nManager.getText("dialog.exportsvg.theta"));
thetaLabel.setHorizontalAlignment(SwingConstants.TRAILING);
{
return _fragment.equals(inOther._fragment);
}
+
+ /**
+ * @param inOther other object to compare this one with
+ * @return true if the objects are equal
+ */
+ public boolean equals(Object inOther) {
+ return (inOther instanceof SvgFragment?equals((SvgFragment) inOther):false);
+ }
}
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.UpdateMessageBroker;\r
+import tim.prune.data.AudioFile;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Photo;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo the connection of a photo and/or audio to a point\r
+ */\r
+public class UndoConnectMedia implements UndoOperation\r
+{\r
+ private DataPoint _point = null;\r
+ private String _photoFilename = null;\r
+ private String _audioFilename = null;\r
+\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inPoint data point\r
+ * @param inPhotoFilename filename of photo, or null if photo not connected\r
+ * @param inAudioFilename filename of audio, or null of audio not connected\r
+ */\r
+ public UndoConnectMedia(DataPoint inPoint, String inPhotoFilename, String inAudioFilename)\r
+ {\r
+ _point = inPoint;\r
+ _photoFilename = inPhotoFilename;\r
+ _audioFilename = inAudioFilename;\r
+ }\r
+\r
+\r
+ /**\r
+ * @return description of operation including photo and/or audio filename(s)\r
+ */\r
+ public String getDescription()\r
+ {\r
+ String desc = I18nManager.getText("undo.connect") + " " + (_photoFilename==null?"":_photoFilename)\r
+ + (_photoFilename!=null && _audioFilename!=null?", ":"")\r
+ + (_audioFilename==null?"":_audioFilename);\r
+ return desc;\r
+ }\r
+\r
+\r
+ /**\r
+ * Perform the undo operation on the given Track\r
+ * @param inTrackInfo TrackInfo object on which to perform the operation\r
+ */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ if (_photoFilename != null)\r
+ {\r
+ // Disconnect photo\r
+ Photo photo = _point.getPhoto();\r
+ if (photo != null)\r
+ {\r
+ _point.setPhoto(null);\r
+ photo.setDataPoint(null);\r
+ }\r
+ }\r
+ if (_audioFilename != null)\r
+ {\r
+ // Disconnect audio\r
+ AudioFile audio = _point.getAudio();\r
+ if (audio != null)\r
+ {\r
+ _point.setAudio(null);\r
+ audio.setDataPoint(null);\r
+ }\r
+ }\r
+ // inform subscribers\r
+ UpdateMessageBroker.informSubscribers();\r
+ }\r
+}
\ No newline at end of file
+++ /dev/null
-package tim.prune.undo;\r
-\r
-import tim.prune.I18nManager;\r
-import tim.prune.UpdateMessageBroker;\r
-import tim.prune.data.DataPoint;\r
-import tim.prune.data.Photo;\r
-import tim.prune.data.TrackInfo;\r
-\r
-/**\r
- * Operation to undo the connection of a photo to a point\r
- */\r
-public class UndoConnectPhoto implements UndoOperation\r
-{\r
- private DataPoint _point = null;\r
- private String _filename = null;\r
-\r
-\r
- /**\r
- * Constructor\r
- * @param inPoint data point\r
- * @param inFilename filename of photo\r
- */\r
- public UndoConnectPhoto(DataPoint inPoint, String inFilename)\r
- {\r
- _point = inPoint;\r
- _filename = inFilename;\r
- }\r
-\r
-\r
- /**\r
- * @return description of operation including photo filename\r
- */\r
- public String getDescription()\r
- {\r
- String desc = I18nManager.getText("undo.connectphoto") + " " + _filename;\r
- return desc;\r
- }\r
-\r
-\r
- /**\r
- * Perform the undo operation on the given Track\r
- * @param inTrackInfo TrackInfo object on which to perform the operation\r
- */\r
- public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
- {\r
- // Disconnect again\r
- Photo photo = _point.getPhoto();\r
- if (photo != null)\r
- {\r
- _point.setPhoto(null);\r
- photo.setDataPoint(null);\r
- // inform subscribers\r
- UpdateMessageBroker.informSubscribers();\r
- }\r
- else\r
- {\r
- // throw exception if failed\r
- throw new UndoException(getDescription());\r
- }\r
- }\r
-}
\ No newline at end of file
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.data.AudioFile;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo an auto-correlation of audios with points\r
+ * (very similar to UndoCorrelatePhotos)\r
+ */\r
+public class UndoCorrelateAudios implements UndoOperation\r
+{\r
+ private DataPoint[] _contents = null;\r
+ private DataPoint[] _audioPoints = null;\r
+ private int _numCorrelated = -1;\r
+\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inTrackInfo track information\r
+ */\r
+ public UndoCorrelateAudios(TrackInfo inTrackInfo)\r
+ {\r
+ // Copy track contents\r
+ _contents = inTrackInfo.getTrack().cloneContents();\r
+ // Copy points associated with audios before correlation\r
+ int numAudios = inTrackInfo.getAudioList().getNumAudios();\r
+ _audioPoints = new DataPoint[numAudios];\r
+ for (int i=0; i<numAudios; i++) {\r
+ _audioPoints[i] = inTrackInfo.getAudioList().getAudio(i).getDataPoint();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param inNumCorrelated number of audios correlated\r
+ */\r
+ public void setNumAudiosCorrelated(int inNumCorrelated)\r
+ {\r
+ _numCorrelated = inNumCorrelated;\r
+ }\r
+\r
+ /**\r
+ * @return description of operation including parameters\r
+ */\r
+ public String getDescription()\r
+ {\r
+ return I18nManager.getText("undo.correlateaudios") + " (" + _numCorrelated + ")";\r
+ }\r
+\r
+\r
+ /**\r
+ * Perform the undo operation on the given Track\r
+ * @param inTrackInfo TrackInfo object on which to perform the operation\r
+ */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ // restore track to previous values\r
+ inTrackInfo.getTrack().replaceContents(_contents);\r
+ // restore audio association\r
+ for (int i=0; i<_audioPoints.length; i++)\r
+ {\r
+ AudioFile 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
+ {\r
+ DataPoint prevPoint = _audioPoints[i];\r
+ DataPoint currPoint = audio.getDataPoint();\r
+ audio.setDataPoint(prevPoint);\r
+ if (currPoint != null) {\r
+ currPoint.setAudio(null); // disconnect\r
+ }\r
+ if (prevPoint != null) {\r
+ prevPoint.setAudio(audio); // reconnect to prev point\r
+ }\r
+ }\r
+ }\r
+ // clear selection\r
+ inTrackInfo.getSelection().clearAll();\r
+ }\r
+}\r
*/\r
public String getDescription()\r
{\r
- return I18nManager.getText("undo.correlate") + " (" + _numPhotosCorrelated + ")";\r
+ return I18nManager.getText("undo.correlatephotos") + " (" + _numPhotosCorrelated + ")";\r
}\r
\r
\r
for (int i=0; i<_photoPoints.length; i++)\r
{\r
Photo photo = inTrackInfo.getPhotoList().getPhoto(i);\r
- // Only need to look at connected photos, if they're still tagged then leave them\r
+ // Only need to look at connected photos, since correlation wouldn't disconnect\r
if (photo.getCurrentStatus() == Photo.Status.CONNECTED)\r
{\r
- DataPoint point = _photoPoints[i];\r
- photo.setDataPoint(point);\r
- if (point != null) {\r
- point.setPhoto(photo);\r
+ DataPoint prevPoint = _photoPoints[i];\r
+ DataPoint currPoint = photo.getDataPoint();\r
+ photo.setDataPoint(prevPoint);\r
+ if (currPoint != null) {\r
+ currPoint.setPhoto(null); // disconnect\r
+ }\r
+ if (prevPoint != null) {\r
+ prevPoint.setPhoto(photo); // reconnect to prev point\r
}\r
}\r
}\r
// clear selection\r
inTrackInfo.getSelection().clearAll();\r
}\r
-}
\ No newline at end of file
+}\r
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.UpdateMessageBroker;\r
+import tim.prune.data.AudioFile;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo a delete of a single audio item, either with or without point\r
+ */\r
+public class UndoDeleteAudio implements UndoOperation\r
+{\r
+ private int _audioIndex = -1;\r
+ private AudioFile _audio = null;\r
+ private int _pointIndex = -1;\r
+ private DataPoint _point = null;\r
+\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inAudio audio item\r
+ * @param inAudioIndex index number of audio within list\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
+ {\r
+ _audio = inAudio;\r
+ _audioIndex = inAudioIndex;\r
+ _point = inPoint;\r
+ _pointIndex = inPointIndex;\r
+ }\r
+\r
+\r
+ /**\r
+ * @return description of operation including filename\r
+ */\r
+ public String getDescription() {\r
+ return I18nManager.getText("undo.removeaudio") + " " + _audio.getFile().getName();\r
+ }\r
+\r
+\r
+ /**\r
+ * Perform the undo operation on the given Track\r
+ * @param inTrackInfo TrackInfo object on which to perform the operation\r
+ */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ // restore audio\r
+ inTrackInfo.getAudioList().addAudio(_audio, _audioIndex);\r
+ // if there's a point to restore, restore it\r
+ if (_point != null)\r
+ {\r
+ if (!inTrackInfo.getTrack().insertPoint(_point, _pointIndex)) {\r
+ throw new UndoException(getDescription());\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // update needed if not already triggered by track update\r
+ UpdateMessageBroker.informSubscribers();\r
+ }\r
+ // Ensure that audio is associated with point and vice versa\r
+ _audio.setDataPoint(_point);\r
+ if (_point != null) {\r
+ _point.setAudio(_audio);\r
+ }\r
+ }\r
+}\r
*/\r
public String getDescription()\r
{\r
- String desc = I18nManager.getText("undo.deletephoto") + " " + _photo.getFile().getName();\r
+ String desc = I18nManager.getText("undo.removephoto") + " " + _photo.getFile().getName();\r
return desc;\r
}\r
\r
}\r
// Ensure that photo is associated with point and vice versa\r
_photo.setDataPoint(_point);\r
- if (_point != null)\r
- {\r
+ if (_point != null) {\r
_point.setPhoto(_photo);\r
}\r
}\r
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.data.AudioFile;\r
+import tim.prune.data.DataPoint;\r
+import tim.prune.data.Photo;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo the disconnection of a photo or audio from a point\r
+ */\r
+public class UndoDisconnectMedia implements UndoOperation\r
+{\r
+ private DataPoint _point = null;\r
+ private Photo _photo = null;\r
+ private AudioFile _audio = null;\r
+ private String _filename = null;\r
+\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inPoint data point\r
+ * @param inPhoto true if photo was disconnected\r
+ * @param inAudio true if audio was disconnected\r
+ * @param inFilename filename of photo / audio\r
+ */\r
+ public UndoDisconnectMedia(DataPoint inPoint, boolean inPhoto, boolean inAudio, String inFilename)\r
+ {\r
+ _point = inPoint;\r
+ if (inPhoto) {\r
+ _photo = inPoint.getPhoto();\r
+ }\r
+ if (inAudio) {\r
+ _audio = inPoint.getAudio();\r
+ }\r
+ _filename = inFilename;\r
+ }\r
+\r
+\r
+ /**\r
+ * @return description of operation including filename\r
+ */\r
+ public String getDescription()\r
+ {\r
+ return I18nManager.getText("undo.disconnect") + " " + _filename;\r
+ }\r
+\r
+\r
+ /**\r
+ * Perform the undo operation on the given Track\r
+ * @param inTrackInfo TrackInfo object on which to perform the operation\r
+ */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ // Connect again\r
+ if (_point != null && _photo != null)\r
+ {\r
+ _point.setPhoto(_photo);\r
+ _photo.setDataPoint(_point);\r
+ }\r
+ else if (_point != null && _audio != null)\r
+ {\r
+ _point.setAudio(_audio);\r
+ _audio.setDataPoint(_point);\r
+ }\r
+ else {\r
+ // throw exception if failed\r
+ throw new UndoException(getDescription());\r
+ }\r
+ // clear selection\r
+ inTrackInfo.getSelection().clearAll();\r
+ }\r
+}
\ No newline at end of file
+++ /dev/null
-package tim.prune.undo;\r
-\r
-import tim.prune.I18nManager;\r
-import tim.prune.UpdateMessageBroker;\r
-import tim.prune.data.DataPoint;\r
-import tim.prune.data.Photo;\r
-import tim.prune.data.TrackInfo;\r
-\r
-/**\r
- * Operation to undo the disconnection of a photo from a point\r
- */\r
-public class UndoDisconnectPhoto implements UndoOperation\r
-{\r
- private DataPoint _point = null;\r
- private Photo _photo = null;\r
- private String _filename = null;\r
-\r
-\r
- /**\r
- * Constructor\r
- * @param inPoint data point\r
- * @param inFilename filename of photo\r
- */\r
- public UndoDisconnectPhoto(DataPoint inPoint, String inFilename)\r
- {\r
- _point = inPoint;\r
- _photo = inPoint.getPhoto();\r
- _filename = inFilename;\r
- }\r
-\r
-\r
- /**\r
- * @return description of operation including photo filename\r
- */\r
- public String getDescription()\r
- {\r
- String desc = I18nManager.getText("undo.disconnectphoto") + " " + _filename;\r
- return desc;\r
- }\r
-\r
-\r
- /**\r
- * Perform the undo operation on the given Track\r
- * @param inTrackInfo TrackInfo object on which to perform the operation\r
- */\r
- public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
- {\r
- // Connect again\r
- if (_point != null && _photo != null)\r
- {\r
- _point.setPhoto(_photo);\r
- _photo.setDataPoint(_point);\r
- // inform subscribers\r
- UpdateMessageBroker.informSubscribers();\r
- }\r
- else\r
- {\r
- // throw exception if failed\r
- throw new UndoException(getDescription());\r
- }\r
- }\r
-}
\ No newline at end of file
private DataPoint[] _contents = null;\r
private PhotoList _photoList = null;\r
private FileInfo _oldFileInfo = null;\r
+ // Numbers of each media before operation\r
+ private int _numPhotos = -1, _numAudios = -1;\r
\r
\r
/**\r
return desc;\r
}\r
\r
+ /**\r
+ * Set the number of photos and audios before the load operation\r
+ * @param inNumPhotos number of photos\r
+ * @param inNumAudios number of audios\r
+ */\r
+ public void setNumPhotosAudios(int inNumPhotos, int inNumAudios)\r
+ {\r
+ _numPhotos = inNumPhotos;\r
+ _numAudios = inNumAudios;\r
+ }\r
\r
/**\r
* Perform the undo operation on the given Track\r
else\r
{\r
// replace photos how they were\r
- if (_photoList != null)\r
- {\r
+ if (_photoList != null) {\r
inTrackInfo.getPhotoList().restore(_photoList);\r
}\r
+ // Crop media lists to previous size (if specified)\r
+ if (_numPhotos > -1) {inTrackInfo.getPhotoList().cropTo(_numPhotos);}\r
+ if (_numAudios > -1) {inTrackInfo.getAudioList().cropTo(_numAudios);}\r
// replace track contents with old\r
if (!inTrackInfo.getTrack().replaceContents(_contents))\r
{\r
// clear selection\r
inTrackInfo.getSelection().clearAll();\r
}\r
-}
\ No newline at end of file
+}\r
--- /dev/null
+package tim.prune.undo;\r
+\r
+import tim.prune.I18nManager;\r
+import tim.prune.data.TrackInfo;\r
+\r
+/**\r
+ * Operation to undo a load audios operation\r
+ */\r
+public class UndoLoadAudios implements UndoOperation\r
+{\r
+ /** Number of audio files added */\r
+ private int _numAudios = -1;\r
+\r
+\r
+ /**\r
+ * Constructor\r
+ * @param inNumAudios number of audios loaded\r
+ */\r
+ public UndoLoadAudios(int inNumAudios)\r
+ {\r
+ _numAudios = inNumAudios;\r
+ }\r
+\r
+\r
+ /**\r
+ * @return description of operation including number of audios loaded\r
+ */\r
+ public String getDescription()\r
+ {\r
+ String desc = I18nManager.getText("undo.loadaudios");\r
+ if (_numAudios > 0)\r
+ desc = desc + " (" + _numAudios + ")";\r
+ return desc;\r
+ }\r
+\r
+\r
+ /**\r
+ * Perform the undo operation on the given Track\r
+ * @param inTrackInfo TrackInfo object on which to perform the operation\r
+ */\r
+ public void performUndo(TrackInfo inTrackInfo) throws UndoException\r
+ {\r
+ // crop audio list to previous size\r
+ int cropIndex = inTrackInfo.getAudioList().getNumAudios() - _numAudios;\r
+ inTrackInfo.getAudioList().cropTo(cropIndex);\r
+ // clear selection\r
+ inTrackInfo.getSelection().clearAll();\r
+ }\r
+}\r