Index: /applications/editors/josm/plugins/photo_geotagging/build.xml
===================================================================
--- /applications/editors/josm/plugins/photo_geotagging/build.xml	(revision 19844)
+++ /applications/editors/josm/plugins/photo_geotagging/build.xml	(revision 19845)
@@ -25,7 +25,7 @@
 
 	<!-- enter the SVN commit message -->
-	<property name="commit.message" value="josm plugin photo_geotagging: update" />
+	<property name="commit.message" value="josm plugin photo_geotagging: added mtime handling and GPSTimeStamp writing" />
 	<!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
-	<property name="plugin.main.version" value="2904" />
+	<property name="plugin.main.version" value="2931" />
 
 
Index: /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTagger.java
===================================================================
--- /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTagger.java	(revision 19844)
+++ /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTagger.java	(revision 19845)
@@ -1,4 +1,3 @@
-// This is from a file of the sanselan project that is supposed to show, how the library can be used:
-// https://svn.apache.org/repos/asf/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java
+// Wrapper class for sanselan library
 package org.openstreetmap.josm.plugins.photo_geotagging;
 
@@ -6,8 +5,12 @@
 
 import java.io.BufferedOutputStream;
-import java.io.IOException;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.text.DecimalFormat;
 
 import org.apache.sanselan.ImageReadException;
@@ -18,4 +21,9 @@
 import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
 import org.apache.sanselan.formats.tiff.TiffImageMetadata;
+import org.apache.sanselan.formats.tiff.constants.TagInfo;
+import org.apache.sanselan.formats.tiff.constants.TiffConstants;
+import org.apache.sanselan.formats.tiff.fieldtypes.FieldType;
+import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
+import org.apache.sanselan.formats.tiff.write.TiffOutputField;
 import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
 
@@ -24,16 +32,14 @@
      * Set the GPS values in JPEG EXIF metadata.
      * This is taken from one of the examples of the sanselan project.
-     * 
-     * @param jpegImageFile
-     *            A source image file.
-     * @param dst
-     *            The output file.
-     * @throws IOException
-     * @throws ImageReadException
-     * @throws ImageWriteException
+     *
+     * @param jpegImageFile A source image file.
+     * @param dst The output file.
+     * @param lat latitude
+     * @param lon longitude
+     * @param gpsTime time in milliseconds
      */
-    public static void setExifGPSTag(File jpegImageFile, File dst, double lat, double lon) throws IOException {
+    public static void setExifGPSTag(File jpegImageFile, File dst, double lat, double lon, long gpsTime) throws IOException {
         try {
-            setExifGPSTagWorker(jpegImageFile, dst, lat, lon);
+            setExifGPSTagWorker(jpegImageFile, dst, lat, lon, gpsTime);
         } catch (ImageReadException ire) {
             throw new IOException(tr("Read error!"));
@@ -41,45 +47,67 @@
             throw new IOException(tr("Write error!"));
         }
-    }       
-   
-    public static void setExifGPSTagWorker(File jpegImageFile, File dst, double lat, double lon) throws IOException,
+    }
+
+    public static void setExifGPSTagWorker(File jpegImageFile, File dst, double lat, double lon, long gpsTime) throws IOException,
             ImageReadException, ImageWriteException
     {
         OutputStream os = null;
-        try
-        {
+        try {
             TiffOutputSet outputSet = null;
 
-            // note that metadata might be null if no metadata is found.
             IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
             JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
-            if (null != jpegMetadata)
-            {
-                // note that exif might be null if no Exif metadata is found.
+            if (null != jpegMetadata) {
                 TiffImageMetadata exif = jpegMetadata.getExif();
 
-                if (null != exif)
-                {
-                    // TiffImageMetadata class is immutable (read-only).
-                    // TiffOutputSet class represents the Exif data to write.
-                    //
-                    // Usually, we want to update existing Exif metadata by
-                    // changing
-                    // the values of a few fields, or adding a field.
-                    // In these cases, it is easiest to use getOutputSet() to
-                    // start with a "copy" of the fields read from the image.
+                if (null != exif) {
                     outputSet = exif.getOutputSet();
                 }
             }
 
-            // if file does not contain any exif metadata, we create an empty
-            // set of exif metadata. Otherwise, we keep all of the other
-            // existing tags.
-            if (null == outputSet)
+            if (null == outputSet) {
                 outputSet = new TiffOutputSet();
+            }
 
-            {
-                outputSet.setGPSInDegrees(lon, lat);
-            }
+            Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+
+            calendar.setTimeInMillis(gpsTime);
+
+            final int year =   calendar.get(Calendar.YEAR);
+            final int month =  calendar.get(Calendar.MONTH);
+            final int day =    calendar.get(Calendar.DAY_OF_MONTH);
+            final int hour =   calendar.get(Calendar.HOUR_OF_DAY);
+            final int minute = calendar.get(Calendar.MINUTE);
+            final int second = calendar.get(Calendar.SECOND);
+
+            DecimalFormat yearFormatter = new DecimalFormat("0000");
+            DecimalFormat monthFormatter = new DecimalFormat("00");
+            DecimalFormat dayFormatter = new DecimalFormat("00");
+
+            final String yearStr = yearFormatter.format(year);
+            final String monthStr = monthFormatter.format(month);
+            final String dayStr = dayFormatter.format(day);
+            final String dateStamp = yearStr+":"+monthStr+":"+dayStr;
+            //System.err.println("date: "+dateStamp+"  h/m/s: "+hour+"/"+minute+"/"+second);
+
+            Double[] timeStamp = {new Double(hour), new Double(minute), new Double(second)};
+            TiffOutputField gpsTimeStamp = TiffOutputField.create(
+                    TiffConstants.GPS_TAG_GPS_TIME_STAMP,
+                    outputSet.byteOrder, timeStamp);
+            TiffOutputDirectory exifDirectory = outputSet.getOrCreateGPSDirectory();
+            // make sure to remove old value if present (this method will
+            // not fail if the tag does not exist).
+            exifDirectory.removeField(TiffConstants.GPS_TAG_GPS_TIME_STAMP);
+            exifDirectory.add(gpsTimeStamp);
+
+            TiffOutputField gpsDateStamp = SanselanFixes.create(
+                    TiffConstants.GPS_TAG_GPS_DATE_STAMP,
+                    outputSet.byteOrder, dateStamp);
+            // make sure to remove old value if present (this method will
+            // not fail if the tag does not exist).
+            exifDirectory.removeField(TiffConstants.GPS_TAG_GPS_DATE_STAMP);
+            exifDirectory.add(gpsDateStamp);
+
+            SanselanFixes.setGPSInDegrees(outputSet, lon, lat);
 
             os = new FileOutputStream(dst);
@@ -91,14 +119,10 @@
             os.close();
             os = null;
-        } finally
-        {
-            if (os != null)
-                try
-                {
+        } finally {
+            if (os != null) {
+                try {
                     os.close();
-                } catch (IOException e)
-                {
-
-                }
+                } catch (IOException e) {}
+            }
         }
     }
Index: /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingAction.java
===================================================================
--- /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingAction.java	(revision 19845)
+++ /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingAction.java	(revision 19845)
@@ -0,0 +1,307 @@
+//License: GPL (v2 or above)
+package org.openstreetmap.josm.plugins.photo_geotagging;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import java.io.IOException;
+import java.io.File;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.DefaultListModel;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+import java.text.DecimalFormat;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.PleaseWaitRunnable;
+import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
+import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry;
+import org.openstreetmap.josm.tools.GBC;
+
+/**
+ * The action to aks the user for confirmation and then do the tagging.
+ */
+class GeotaggingAction implements ActionListener {
+    final static boolean debug = false;
+    final static String KEEP_BACKUP = "plugins.photo_geotagging.keep_backup";
+    final static int MTIME_MODE_GPS = 1;
+    final static int MTIME_MODE_PREVIOUS_VALUE = 2;
+
+    final private GeoImageLayer layer;
+    public GeotaggingAction(GeoImageLayer layer) {
+        this.layer = layer;
+    }
+
+    public void actionPerformed(ActionEvent arg0) {
+        final List<ImageEntry> images = new ArrayList<ImageEntry>();
+        for (ImageEntry e : layer.getImages()) {
+             /* Only write lat/lon to the file, if the position is known and
+                we have a time from the correlation to the gpx track. */
+            if (e.getPos() != null && e.getGpsTime() != null) {
+                images.add(e);
+            }
+        }
+
+        final JPanel cont = new JPanel(new GridBagLayout());
+        cont.add(new JLabel(tr("Write position information into the exif header of the following files:")), GBC.eol());
+
+        DefaultListModel listModel = new DefaultListModel();
+        DecimalFormat dFormatter = new DecimalFormat("###0.000000");
+        for (ImageEntry e : images) {
+            listModel.addElement(e.getFile().getAbsolutePath()+
+                " ("+dFormatter.format(e.getPos().lat())+","+dFormatter.format(e.getPos().lon())+")");
+        }
+
+        JList entryList = new JList(listModel);
+
+        JScrollPane scroll = new JScrollPane(entryList);
+        scroll.setPreferredSize(new Dimension(900, 250));
+        cont.add(scroll, GBC.eol().fill(GBC.BOTH));
+
+        final JPanel settingsPanel = new JPanel(new GridBagLayout());
+        settingsPanel.setBorder(BorderFactory.createTitledBorder(tr("settings")));
+        cont.add(settingsPanel, GBC.eol().insets(3,10,3,0));
+
+        final JCheckBox backups = new JCheckBox(tr("keep backup files"), Main.pref.getBoolean(KEEP_BACKUP, true));
+        settingsPanel.add(backups, GBC.eol().insets(3,3,0,0));
+
+        final JCheckBox setMTime = new JCheckBox(tr("change file modification time:"), false);
+        settingsPanel.add(setMTime, GBC.std().insets(3,3,5,3));
+
+        final String[] mTimeModeArray = {"----", tr("to gps time"), tr("to previous value (unchanged mtime)")};
+        final JComboBox mTimeMode = new JComboBox(mTimeModeArray);
+        settingsPanel.add(mTimeMode, GBC.eol().insets(3,3,3,3));
+
+        setMTime.addActionListener(new ActionListener(){
+            public void actionPerformed(ActionEvent e) {
+                if (setMTime.isSelected()) {
+                    mTimeMode.setEnabled(true);
+                } else {
+                    mTimeMode.setSelectedIndex(0);
+                    mTimeMode.setEnabled(false);
+                }
+            }
+        });
+
+        // Toggle the checkbox to fire actionListener
+        setMTime.setSelected(!setMTime.isSelected());
+        setMTime.doClick();
+
+        int result = new ExtendedDialog(
+                Main.parent,
+                tr("Photo Geotagging Plugin"),
+                new String[] {tr("OK"), tr("Cancel")})
+            .setButtonIcons(new String[] {"ok.png", "cancel.png"})
+            .setContent(cont)
+            .setCancelButton(2)
+            .setDefaultButton(1)
+            .showDialog()
+            .getValue();
+
+        if (result != 1)
+            return;
+
+        final boolean keep_backup = backups.isSelected();
+        Main.pref.put(KEEP_BACKUP, keep_backup);
+
+        Main.worker.execute(new GeoTaggingRunnable(images, keep_backup, mTimeMode.getSelectedIndex()));
+    }
+
+    class GeoTaggingRunnable extends PleaseWaitRunnable {
+        final private List<ImageEntry> images;
+        final private boolean keep_backup;
+        final private int mTimeMode;
+
+        private boolean cancelled = false;
+        private Boolean override_backup = null;
+
+        private File fileFrom;
+        private File fileTo;
+        private File fileDelete;
+
+        public GeoTaggingRunnable(List<ImageEntry> images, boolean keep_backup, int mTimeMode) {
+            super(tr("Photo Geotagging Plugin"));
+            this.images = images;
+            this.keep_backup = keep_backup;
+            this.mTimeMode = mTimeMode;
+        }
+        @Override
+        protected void realRun() {
+            progressMonitor.subTask(tr("Writing position information to image files..."));
+            progressMonitor.setTicksCount(images.size());
+
+            for (int i=0; i<images.size(); ++i) {
+                if (cancelled) return;
+
+                ImageEntry e = images.get(i);
+                if (debug) {
+                    System.err.print("i:"+i+" "+e.getFile().getName()+" ");
+                }
+
+                fileFrom = null;
+                fileTo = null;
+                fileDelete = null;
+
+                try {
+                    if (mTimeMode != 0) {
+                        testMTimeReadAndWrite(e.getFile());
+                    }
+                    
+                    Long mTime = null;
+                    if (mTimeMode == MTIME_MODE_PREVIOUS_VALUE) {
+                        mTime = e.getFile().lastModified();
+                        if (mTime.equals(0L))
+                            throw new IOException(tr("Could not read mtime."));
+                    }
+
+                    chooseFiles(e.getFile());
+                    if (cancelled) return;
+                    ExifGPSTagger.setExifGPSTag(fileFrom, fileTo, e.getPos().lat(), e.getPos().lon(), e.getGpsTime().getTime());
+
+                    if (mTimeMode == MTIME_MODE_GPS) {
+                        mTime = e.getGpsTime().getTime();
+                    }
+
+                    if (mTime != null) {
+                        if (!fileTo.setLastModified(mTime))
+                            throw new IOException(tr("Could not write mtime."));
+                    }
+
+                    cleanupFiles();
+
+                } catch (final IOException ioe) {
+                    ioe.printStackTrace();
+                    // need this so the dialogs don't block
+                    SwingUtilities.invokeLater(new Runnable() {
+                        public void run() {
+                            JOptionPane.showMessageDialog(Main.parent, ioe.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
+                        }
+                    });
+                    return;
+                }
+                progressMonitor.worked(1);
+                if (debug) {
+                    System.err.println("");
+                }
+            }
+        }
+
+        private void chooseFiles(File file) throws IOException {
+            if (debug) {
+                System.err.println("f: "+file.getAbsolutePath());
+            }
+
+            if (!keep_backup) {
+                chooseFilesNoBackup(file);
+                return;
+            }
+
+            File fileBackup = new File(file.getParentFile(),file.getName()+"_");
+            if (fileBackup.exists()) {
+                confirm_override();
+                if (cancelled)
+                    return;
+
+                if (override_backup) {
+                    if (!fileBackup.delete())
+                        throw new IOException(tr("File could not be deleted!"));
+                } else {
+                    chooseFilesNoBackup(file);
+                    return;
+                }
+            }
+            if (!file.renameTo(fileBackup))
+                throw new IOException(tr("Could not rename file!"));
+
+            fileFrom = fileBackup;
+            fileTo = file;
+            fileDelete = null;
+        }
+
+        private void chooseFilesNoBackup(File file) throws IOException {
+            File fileTmp;
+            fileTmp = File.createTempFile("img", ".jpg", file.getParentFile());
+            if (debug) {
+                System.err.println("TMP: "+fileTmp.getAbsolutePath());
+            }
+            if (! file.renameTo(fileTmp))
+                throw new IOException(tr("Could not rename file!"));
+
+            fileFrom = fileTmp;
+            fileTo = file;
+            fileDelete = fileTmp;
+        }
+
+        private void confirm_override() {
+            if (override_backup == null) {
+                JLabel l = new JLabel(tr("<html><h3>There are old backup files in the image directory!</h3>"));
+                l.setIcon(UIManager.getIcon("OptionPane.warningIcon"));
+                int override = new ExtendedDialog(
+                        Main.parent,
+                        tr("Override old backup files?"),
+                        new String[] {tr("Cancel"), tr("Keep old backups and continue"), tr("Override")})
+                    .setButtonIcons(new String[] {"cancel.png", "ok.png", "dialogs/delete.png"})
+                    .setContent(l)
+                    .setCancelButton(1)
+                    .setDefaultButton(2)
+                    .showDialog()
+                    .getValue();
+                if (override == 2) {
+                    override_backup = false;
+                } else if (override == 3) {
+                    override_backup = true;
+                } else {
+                    cancelled = true;
+                    return;
+                }
+            }
+        }
+
+        private void cleanupFiles() throws IOException {
+            if (fileDelete != null) {
+                if (!fileDelete.delete())
+                    throw new IOException(tr("Could not delete temporary file!"));
+            }
+        }
+        
+        boolean testMTimeReadAndWriteDone = false;
+        
+        private void testMTimeReadAndWrite(File file) throws IOException {
+            if (testMTimeReadAndWriteDone)  // do this only once
+                return;
+            File fileTest = File.createTempFile("geo", ".txt", file.getParentFile());
+            long mTimeTest = fileTest.lastModified();
+            if (mTimeTest == 0L)
+                throw new IOException(tr("Test failed: Could not read mtime."));
+            if (!fileTest.setLastModified(mTimeTest))
+                throw new IOException(tr("Test failed: Could not write mtime."));
+            testMTimeReadAndWriteDone = true;
+        }
+
+        @Override
+        protected void finish() {
+        }
+
+        @Override
+        protected void cancel() {
+            cancelled = true;
+        }
+    }
+}
Index: /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingPlugin.java
===================================================================
--- /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingPlugin.java	(revision 19844)
+++ /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingPlugin.java	(revision 19845)
@@ -5,38 +5,12 @@
 
 import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.GridBagLayout;
-import java.awt.Rectangle;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 
-import java.io.IOException;
-import java.io.File;
+import javax.swing.JMenuItem;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.AbstractListModel;
-import javax.swing.JCheckBox;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.UIManager;
-
-import java.text.DecimalFormat;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.ExtendedDialog;
-import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.Layer;
 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
 import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.plugins.Plugin;
 import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -46,260 +20,35 @@
  * It extends the core geoimage feature of JOSM by adding a new entry
  * to the right click menu of any image layer.
- * 
+ *
  * The real work (writing lat/lon values to file) is done by the pure Java
  * sanselan library.
  */
 public class GeotaggingPlugin extends Plugin {
-    final static boolean debug = false;
-    final static String KEEP_BACKUP = "plugins.photo_geotagging.keep_backup";
-    
     public GeotaggingPlugin(PluginInformation info) {
         super(info);
         GeoImageLayer.registerMenuAddition(new GeotaggingMenuAddition());
     }
-    
+
+    /**
+     * Adds a menu entry to the right click menu of each geoimage layer.
+     */
     class GeotaggingMenuAddition implements GeoImageLayer.LayerMenuAddition {
         public Component getComponent(Layer layer) {
             JMenuItem geotaggingItem = new JMenuItem(tr("Write coordinates to image header"), ImageProvider.get("geotagging"));;
-            geotaggingItem.addActionListener(new GeotagImages((GeoImageLayer) layer));
+            geotaggingItem.addActionListener(new GeotaggingAction((GeoImageLayer) layer));
+            geotaggingItem.setEnabled(enabled((GeoImageLayer) layer));
             return geotaggingItem;
         }
-    }
-    
-    class GeotagImages implements ActionListener {
-        final private GeoImageLayer layer;
-        public GeotagImages(GeoImageLayer layer) {
-            this.layer = layer;
-        }
-        
-        public void actionPerformed(ActionEvent arg0) {
-            final List<ImageEntry> images = new ArrayList<ImageEntry>();
+
+        /**
+         * Check if there is any suitable image.
+         */
+        private boolean enabled(GeoImageLayer layer) {
             for (ImageEntry e : layer.getImages()) {
-                if (e.getPos() != null) {
-                    images.add(e);
-                }
+                if (e.getPos() != null && e.getGpsTime() != null)
+                    return true;
             }
-
-            final JPanel cont = new JPanel(new GridBagLayout());
-            cont.add(new JLabel(tr("Write position information into the exif header of the following files:")), GBC.eol());
-            
-            FileList files = new FileList();
-            files.setVisibleRowCount(Math.min(files.getModel().getSize(), 10));
-            final List<String> strs = new ArrayList<String>();
-            DecimalFormat cDdFormatter = new DecimalFormat("###0.000000");
-
-            for (ImageEntry e : images) {
-                strs.add(e.getFile().getAbsolutePath()+" ("+cDdFormatter.format(e.getPos().lat())+","+cDdFormatter.format(e.getPos().lon())+")");
-            }
-            files.getFileListModel().setFiles(strs);
-            JScrollPane scroll = new JScrollPane(files);
-            scroll.setPreferredSize(new Dimension(300, 250));
-            cont.add(scroll, GBC.eol().fill(GBC.BOTH));
-            
-            final JCheckBox backups = new JCheckBox(tr("keep backup files"), Main.pref.getBoolean(KEEP_BACKUP, true));
-            cont.add(backups, GBC.eol());
-            
-            int result = new ExtendedDialog(
-                    Main.parent,
-                    tr("Photo Geotagging Plugin"),
-                    new String[] {tr("OK"), tr("Cancel")})
-                .setButtonIcons(new String[] {"ok.png", "cancel.png"})
-                .setContent(cont)
-                .setCancelButton(2)
-                .setDefaultButton(1)
-                .showDialog()
-                .getValue();
-
-            if (result != 1) 
-                return;
-            
-            final boolean keep_backup = backups.isSelected();
-            Main.pref.put(KEEP_BACKUP, keep_backup);
-
-            Main.worker.execute(new GeoTaggingRunnable(images, keep_backup));
+            return false;
         }
     }
-
-    class GeoTaggingRunnable extends PleaseWaitRunnable {
-        private boolean cancelled = false;
-        final private boolean keep_backup;
-        final List<ImageEntry> images;
-        private Boolean override_backup = null;
-
-        private File fileFrom;
-        private File fileTo;
-        private File fileDelete;
-        
-        public GeoTaggingRunnable(List<ImageEntry> images, boolean keep_backup) {
-            super(tr("Photo Geotagging Plugin"));
-            this.images = images;
-            this.keep_backup = keep_backup;
-        }
-        @Override 
-        protected void realRun() {
-            progressMonitor.subTask(tr("Writing position information to image files..."));
-            progressMonitor.setTicksCount(images.size());
-        
-            
-            for (int i=0; i<images.size(); ++i) {
-                if (cancelled) return;
-
-                ImageEntry e = images.get(i);
-                if (debug) {
-                    System.err.print("i:"+i+" "+e.getFile().getName()+" ");
-                }
-                
-                fileFrom = null;
-                fileTo = null;
-                fileDelete = null;
-                    
-                try {
-                    chooseFiles(e.getFile());
-                    if (cancelled) return;
-                    ExifGPSTagger.setExifGPSTag(fileFrom, fileTo, e.getPos().lat(), e.getPos().lon());
-                    cleanupFiles();
-                } catch (IOException ioe) {
-                    ioe.printStackTrace();
-                    JOptionPane.showMessageDialog(Main.parent, tr("Error: ")+ioe.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
-                    return;
-                }
-                progressMonitor.worked(1);
-                if (debug) {
-                System.err.println("");
-                }
-            }
-        }
-
-        private void chooseFiles(File file) throws IOException {
-            if (debug) {                    
-            System.err.println("f: "+file.getAbsolutePath());
-            }
-
-            if (!keep_backup) {
-                chooseFilesNoBackup(file);
-                return;
-            }
-
-            File fileBackup = new File(file.getParentFile(),file.getName()+"_");
-            if (fileBackup.exists()) {
-                if (debug) {
-                    System.err.println("FILE EXISTS");
-                }
-
-                confirm_override();
-                if (cancelled) 
-                    return;
-                
-                if (override_backup) {
-                    if (!fileBackup.delete())
-                        throw new IOException(tr("File could not be deleted!"));
-                } else {
-                    chooseFilesNoBackup(file);
-                    return;
-                }
-            }
-            if (!file.renameTo(fileBackup))
-                throw new IOException(tr("Could not rename file!"));
-                
-            fileFrom = fileBackup;
-            fileTo = file;
-            fileDelete = null;
-        }
-
-        private void chooseFilesNoBackup(File file) throws IOException {
-            File fileTmp;
-            fileTmp = File.createTempFile("img", ".jpg", file.getParentFile());
-            if (debug) {
-                System.err.println("TMP: "+fileTmp.getAbsolutePath());
-            }
-            if (! file.renameTo(fileTmp))
-                throw new IOException(tr("Could not rename file!"));
-                
-            fileFrom = fileTmp;
-            fileTo = file;
-            fileDelete = fileTmp;
-        }        
-
-        private void confirm_override() {
-            if (override_backup == null) {
-                JLabel l = new JLabel(tr("<html><h3>There are old backup files in the image directory!</h3>"));
-                l.setIcon(UIManager.getIcon("OptionPane.warningIcon"));
-                int override = new ExtendedDialog(
-                        Main.parent,
-                        tr("Override old backup files?"),
-                        new String[] {tr("Cancel"), tr("Keep old backups and continue"), tr("Override")})
-                    .setButtonIcons(new String[] {"cancel.png", "ok.png", "dialogs/delete.png"})
-                    .setContent(l)
-                    .setCancelButton(1)
-                    .setDefaultButton(2)
-                    .showDialog()
-                    .getValue();
-                if (override == 2) {
-                    override_backup = false;
-                } else if (override == 3) {
-                    override_backup = true;
-                } else {
-                    cancelled = true;
-                    return;
-                }
-            }
-        }
-
-        private void cleanupFiles() throws IOException {
-            if (fileDelete != null) {
-                if (!fileDelete.delete()) 
-                    throw new IOException(tr("Could not delete temporary file!"));
-            }
-        }
-        
-        @Override 
-        protected void finish() {
-        }
-        
-        @Override 
-        protected void cancel() {
-            cancelled = true;
-        }
-    }
-    
-    static class FileList extends JList {
-        public FileList() {
-            super(new FileListModel());
-        }
-
-        public FileListModel getFileListModel() {
-            return (FileListModel)getModel();
-        }
-    }
-
-    static class FileListModel extends AbstractListModel{
-        private List<String> files;
-
-        public FileListModel() {
-            files = new ArrayList<String>();
-        }
-
-        public FileListModel(List<String> files) {
-            setFiles(files);
-        }
-
-        public void setFiles(List<String> files) {
-            if (files == null) {
-                this.files = new ArrayList<String>();
-            } else {
-                this.files = files;
-            }
-            fireContentsChanged(this,0,getSize());
-        }
-
-        public Object getElementAt(int index) {
-            if (files == null) return null;
-            return files.get(index);
-        }
-
-        public int getSize() {
-            if (files == null) return 0;
-            return files.size();
-        }
-    }   
 }
Index: /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/SanselanFixes.java
===================================================================
--- /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/SanselanFixes.java	(revision 19845)
+++ /applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/SanselanFixes.java	(revision 19845)
@@ -0,0 +1,119 @@
+// This file contains fixes for some bugs of sanselan.
+// License: Apache Software License, Version 2.0
+package org.openstreetmap.josm.plugins.photo_geotagging;
+
+import org.apache.sanselan.ImageWriteException;
+import org.apache.sanselan.formats.tiff.constants.TagInfo;
+import org.apache.sanselan.formats.tiff.constants.TiffConstants;
+import org.apache.sanselan.formats.tiff.fieldtypes.FieldType;
+import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
+import org.apache.sanselan.formats.tiff.write.TiffOutputField;
+import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
+
+public class SanselanFixes implements TiffConstants {
+    /**
+     * The setGPSInDegrees method produces Strings without trailing NULL character.
+     * We simply redirect the create() call to SanselanFixes.create().
+     */
+
+    /**
+     * A convenience method to update GPS values in EXIF metadata.
+     *
+     * @param longitude Longitude in degrees E, negative values are W.
+     * @param latitude latitude in degrees N, negative values are S.
+     * @throws ImageWriteException
+     */
+    public static void setGPSInDegrees(TiffOutputSet outputSet, double longitude, double latitude)
+            throws ImageWriteException
+    {
+        TiffOutputDirectory gpsDirectory = outputSet.getOrCreateGPSDirectory();
+
+        String longitudeRef = longitude < 0 ? "W" : "E";
+        longitude = Math.abs(longitude);
+        String latitudeRef = latitude < 0 ? "S" : "N";
+        latitude = Math.abs(latitude);
+
+        {
+            TiffOutputField longitudeRefField = SanselanFixes.create(
+                    TiffConstants.GPS_TAG_GPS_LONGITUDE_REF, outputSet.byteOrder,
+                    longitudeRef);
+            gpsDirectory.removeField(TiffConstants.GPS_TAG_GPS_LONGITUDE_REF);
+            gpsDirectory.add(longitudeRefField);
+        }
+
+        {
+            TiffOutputField latitudeRefField = SanselanFixes.create(
+                    TiffConstants.GPS_TAG_GPS_LATITUDE_REF, outputSet.byteOrder,
+                    latitudeRef);
+            gpsDirectory.removeField(TiffConstants.GPS_TAG_GPS_LATITUDE_REF);
+            gpsDirectory.add(latitudeRefField);
+        }
+
+        {
+            double value = longitude;
+            double longitudeDegrees = (long) value;
+            value %= 1;
+            value *= 60.0;
+            double longitudeMinutes = (long) value;
+            value %= 1;
+            value *= 60.0;
+            double longitudeSeconds = value;
+            Double values[] = {
+                    new Double(longitudeDegrees), new Double(longitudeMinutes),
+                    new Double(longitudeSeconds),
+            };
+
+            TiffOutputField longitudeField = TiffOutputField.create(
+                    TiffConstants.GPS_TAG_GPS_LONGITUDE, outputSet.byteOrder, values);
+            gpsDirectory.removeField(TiffConstants.GPS_TAG_GPS_LONGITUDE);
+            gpsDirectory.add(longitudeField);
+        }
+
+        {
+            double value = latitude;
+            double latitudeDegrees = (long) value;
+            value %= 1;
+            value *= 60.0;
+            double latitudeMinutes = (long) value;
+            value %= 1;
+            value *= 60.0;
+            double latitudeSeconds = value;
+            Double values[] = {
+                    new Double(latitudeDegrees), new Double(latitudeMinutes),
+                    new Double(latitudeSeconds),
+            };
+
+            TiffOutputField latitudeField = TiffOutputField.create(
+                    TiffConstants.GPS_TAG_GPS_LATITUDE, outputSet.byteOrder, values);
+            gpsDirectory.removeField(TiffConstants.GPS_TAG_GPS_LATITUDE);
+            gpsDirectory.add(latitudeField);
+        }
+
+    }
+
+    /**
+     * fix for 2 Problems:
+     *     - ASII Fields have always length 1
+     *     - Trailing NULL character is missing
+     */
+    public static TiffOutputField create(TagInfo tagInfo, int byteOrder,
+        String value) throws ImageWriteException
+    {
+        FieldType fieldType;
+        if (tagInfo.dataTypes == null)
+            fieldType = FIELD_TYPE_ASCII;
+        else if (tagInfo.dataTypes == FIELD_TYPE_DESCRIPTION_ASCII)
+            fieldType = FIELD_TYPE_ASCII;
+        else
+            throw new ImageWriteException("Tag has unexpected data type.");
+
+        // According to EXIF spec, we need to append a NULL byte.
+        // This way we have the same output as we would get with the exiftools library.
+        String newValue = value+'\0';
+        byte bytes[] = fieldType.writeData(newValue, byteOrder);
+        // the count "1" in the original code (see commented out original)
+        // is wrong as it assumes the field being updated is a single ascii char
+        //return new TiffOutputField(tagInfo.tag, tagInfo, fieldType, 1, bytes);
+        return new TiffOutputField(tagInfo.tag, tagInfo, fieldType, newValue.length(), bytes);
+    }
+}
