Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 6392)
@@ -1209,4 +1209,5 @@
                     curImg.tmp.setElevation(curElevation);
                     curImg.tmp.setGpsTime(new Date(curImg.getExifTime().getTime() - offset));
+                    curImg.flagNewGpsData();
                     ret++;
                 }
@@ -1238,4 +1239,5 @@
                 }
                 curImg.tmp.setGpsTime(new Date(curImg.getExifTime().getTime() - offset));
+                curImg.flagNewGpsData();
 
                 ret++;
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 6392)
@@ -23,6 +23,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
+import java.util.GregorianCalendar;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -30,4 +33,5 @@
 import java.util.List;
 import java.util.Set;
+import java.util.TimeZone;
 
 import javax.swing.Action;
@@ -270,9 +274,43 @@
      */
     public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer) {
-        super(tr("Geotagged Images"));
-
+        this(data, gpxLayer, null, false);
+    }
+
+    /**
+     * Constructs a new {@code GeoImageLayer}.
+     * @param data The list of images to display
+     * @param gpxLayer The associated GPX layer
+     * @param name Layer name
+     * @since 6392
+     */
+    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, final String name) {
+        this(data, gpxLayer, name, false);
+    }
+
+    /**
+     * Constructs a new {@code GeoImageLayer}.
+     * @param data The list of images to display
+     * @param gpxLayer The associated GPX layer
+     * @param useThumbs Thumbnail display flag
+     * @since 6392
+     */
+    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, boolean useThumbs) {
+        this(data, gpxLayer, null, useThumbs);
+    }
+
+    /**
+     * Constructs a new {@code GeoImageLayer}.
+     * @param data The list of images to display
+     * @param gpxLayer The associated GPX layer
+     * @param name Layer name
+     * @param useThumbs Thumbnail display flag
+     * @since 6392
+     */
+    public GeoImageLayer(final List<ImageEntry> data, GpxLayer gpxLayer, final String name, boolean useThumbs) {
+        super(name != null ? name : tr("Geotagged Images"));
         Collections.sort(data);
         this.data = data;
         this.gpxLayer = gpxLayer;
+        this.useThumbs = useThumbs;
     }
 
@@ -403,4 +441,8 @@
         Rectangle clip = g.getClipBounds();
         if (useThumbs) {
+            if (!thumbsLoaded) {
+                loadThumbs();
+            }
+
             if (null == offscreenBuffer || offscreenBuffer.getWidth() != width  // reuse the old buffer if possible
                     || offscreenBuffer.getHeight() != height) {
@@ -458,5 +500,5 @@
                 Point p = mv.getPoint(e.getPos());
 
-                if (e.thumbnail != null) {
+                if (useThumbs && e.thumbnail != null) {
                     Dimension d = scaledDimension(e.thumbnail);
                     g.setColor(new Color(128, 0, 0, 122));
@@ -572,4 +614,41 @@
             // Do nothing
         }
+
+        // Time and date. We can have these cases:
+        // 1) GPS_TIME_STAMP not set -> date/time will be null
+        // 2) GPS_DATE_STAMP not set -> use EXIF date or set to default
+        // 3) GPS_TIME_STAMP and GPS_DATE_STAMP are set
+        int[] timeStampComps = dirGps.getIntArray(GpsDirectory.TAG_GPS_TIME_STAMP);
+        if (timeStampComps != null) {
+            int gpsHour = timeStampComps[0];
+            int gpsMin = timeStampComps[1];
+            int gpsSec = timeStampComps[2];
+            Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+
+            // We have the time. Next step is to check if the GPS date stamp is set.
+            // dirGps.getString() always succeeds, but the return value might be null.
+            String dateStampStr = dirGps.getString(GpsDirectory.TAG_GPS_DATE_STAMP);
+            if (dateStampStr != null && dateStampStr.matches("^\\d+:\\d+:\\d+$")) {
+                String[] dateStampComps = dateStampStr.split(":");
+                cal.set(Calendar.YEAR, Integer.parseInt(dateStampComps[0]));
+                cal.set(Calendar.MONTH, Integer.parseInt(dateStampComps[1]) - 1);
+                cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateStampComps[2]));
+            }
+            else {
+                // No GPS date stamp in EXIF data.  Copy it from EXIF time.
+                // Date is not set if EXIF time is not available.
+                Date exifTime = e.getExifTime();
+                if (exifTime != null) {
+                    // Time not set yet, so we can copy everything, not just date.
+                    cal.setTime(exifTime);
+                }
+            }
+
+            cal.set(Calendar.HOUR_OF_DAY, gpsHour);
+            cal.set(Calendar.MINUTE, gpsMin);
+            cal.set(Calendar.SECOND, gpsSec);
+
+            e.setExifGpsTime(cal.getTime());
+        }
     }
 
@@ -668,4 +747,105 @@
     }
 
+    /**
+     * Removes a photo from the list of images by index.
+     * @param idx Image index
+     * @since 6392
+     */
+    public void removePhotoByIdx(int idx) {
+        if (idx >= 0 && data != null && idx < data.size()) {
+            data.remove(idx);
+        }
+    }
+
+    /**
+     * Returns the image that matches the position of the mouse event.
+     * @param evt Mouse event
+     * @return Image at mouse position, or {@code null} if there is no image at the mouse position
+     * @since 6392
+     */
+    public ImageEntry getPhotoUnderMouse(MouseEvent evt) {
+        if (data != null) {
+            for (int idx = data.size() - 1; idx >= 0; --idx) {
+                ImageEntry img = data.get(idx);
+                if (img.getPos() == null) {
+                    continue;
+                }
+                Point p = Main.map.mapView.getPoint(img.getPos());
+                Rectangle r;
+                if (useThumbs && img.thumbnail != null) {
+                    Dimension d = scaledDimension(img.thumbnail);
+                    r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
+                } else {
+                    r = new Rectangle(p.x - icon.getIconWidth() / 2,
+                                      p.y - icon.getIconHeight() / 2,
+                                      icon.getIconWidth(),
+                                      icon.getIconHeight());
+                }
+                if (r.contains(evt.getPoint())) {
+                    return img;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Clears the currentPhoto, i.e. remove select marker, and optionally repaint.
+     * @param repaint Repaint flag
+     * @since 6392
+     */
+    public void clearCurrentPhoto(boolean repaint) {
+        currentPhoto = -1;
+        if (repaint) {
+            updateBufferAndRepaint();
+        }
+    }
+
+    /**
+     * Clears the currentPhoto of the other GeoImageLayer's. Otherwise there could be multiple selected photos.
+     */
+    private void clearOtherCurrentPhotos() {
+        for (GeoImageLayer layer:
+                 Main.map.mapView.getLayersOfType(GeoImageLayer.class)) {
+            if (layer != this) {
+                layer.clearCurrentPhoto(false);
+            }
+        }
+    }
+
+    private static List<MapMode> supportedMapModes = null;
+
+    /**
+     * Registers a map mode for which the functionality of this layer should be available.
+     * @param mapMode Map mode to be registered
+     * @since 6392
+     */
+    public static void registerSupportedMapMode(MapMode mapMode) {
+        if (supportedMapModes == null) {
+            supportedMapModes = new ArrayList<MapMode>();
+        }
+        supportedMapModes.add(mapMode);
+    }
+
+    /**
+     * Determines if the functionality of this layer is available in
+     * the specified map mode.  SelectAction is supported by default,
+     * other map modes can be registered.
+     * @param mapMode Map mode to be checked
+     * @return {@code true} if the map mode is supported,
+     *         {@code false} otherwise
+     */
+    private static final boolean isSupportedMapMode(MapMode mapMode) {
+        if (mapMode instanceof SelectAction) return true;
+        if (supportedMapModes != null) {
+            for (MapMode supmmode: supportedMapModes) {
+                if (mapMode == supmmode) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private MouseAdapter mouseAdapter = null;
     private MapModeChangeListener mapModeListener = null;
@@ -675,5 +855,5 @@
         mouseAdapter = new MouseAdapter() {
             private final boolean isMapModeOk() {
-                return Main.map.mapMode == null || Main.map.mapMode instanceof SelectAction;
+                return Main.map.mapMode == null || isSupportedMapMode(Main.map.mapMode);
             }
             @Override public void mousePressed(MouseEvent e) {
@@ -699,5 +879,5 @@
                     Point p = Main.map.mapView.getPoint(e.getPos());
                     Rectangle r;
-                    if (e.thumbnail != null) {
+                    if (useThumbs && e.thumbnail != null) {
                         Dimension d = scaledDimension(e.thumbnail);
                         r = new Rectangle(p.x - d.width / 2, p.y - d.height / 2, d.width, d.height);
@@ -709,4 +889,5 @@
                     }
                     if (r.contains(ev.getPoint())) {
+                        clearOtherCurrentPhotos();
                         currentPhoto = i;
                         ImageViewerDialog.showImage(GeoImageLayer.this, e);
@@ -721,5 +902,5 @@
             @Override
             public void mapModeChange(MapMode oldMapMode, MapMode newMapMode) {
-                if (newMapMode == null || (newMapMode instanceof org.openstreetmap.josm.actions.mapmode.SelectAction)) {
+                if (newMapMode == null || isSupportedMapMode(newMapMode)) {
                     Main.map.mapView.addMouseListener(mouseAdapter);
                 } else {
@@ -816,3 +997,25 @@
         showPreviousPhoto();
     }
+
+    /**
+     * Returns the current thumbnail display status.
+     * {@code true}: thumbnails are displayed, {@code false}: an icon is displayed instead of thumbnails.
+     * @return Current thumbnail display status
+     * @since 6392
+     */
+    public boolean isUseThumbs() {
+        return useThumbs;
+    }
+
+    /**
+     * Enables or disables the display of thumbnails.  Does not update the display.
+     * @param useThumbs New thumbnail display status
+     * @since 6392
+     */
+    public void setUseThumbs(boolean useThumbs) {
+        this.useThumbs = useThumbs;
+        if (useThumbs && !thumbsLoaded) {
+            loadThumbs();
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 6392)
@@ -18,7 +18,18 @@
     private Double exifImgDir;
     private Date exifTime;
+    /**
+     * Flag isNewGpsData indicates that the GPS data of the image is new or has changed.
+     * GPS data includes the position, speed, elevation, time (e.g. as extracted from the GPS track).
+     * The flag can used to decide for which image file the EXIF GPS data is (re-)written.
+     */
+    private boolean isNewGpsData = false;
+    /** Temporary source of GPS time if not correlated with GPX track. */
+    private Date exifGpsTime = null;
     Image thumbnail;
 
-    /** The following values are computed from the correlation with the gpx track */
+    /**
+     * The following values are computed from the correlation with the gpx track
+     * or extracted from the image EXIF data.
+     */
     private CachedLatLon pos;
     /** Speed in kilometer per second */
@@ -75,4 +86,14 @@
         return exifTime;
     }
+    
+    /**
+     * Returns the EXIF GPS time.
+     * @return the EXIF GPS time
+     * @since 6392
+     */
+    public final Date getExifGpsTime() {
+        return exifGpsTime;
+    }
+    
     public LatLon getExifCoor() {
         return exifCoor;
@@ -110,4 +131,14 @@
         this.exifTime = exifTime;
     }
+    
+    /**
+     * Sets the EXIF GPS time.
+     * @param exifGpsTime the EXIF GPS time
+     * @since 6392
+     */
+    public final void setExifGpsTime(Date exifGpsTime) {
+        this.exifGpsTime = exifGpsTime;
+    }
+    
     public void setGpsTime(Date gpsTime) {
         this.gpsTime = gpsTime;
@@ -184,3 +215,40 @@
         return result;
     }
+
+    /**
+     * Indicates that the image has new GPS data. 
+     * That flag is used e.g. by the photo_geotagging plugin to decide for which image
+     * file the EXIF GPS data needs to be (re-)written.
+     * @since 6392
+     */
+    public void flagNewGpsData() {
+        isNewGpsData = true;
+        // We need to set the GPS time to tell the system (mainly the photo_geotagging plug-in) 
+        // that the GPS data has changed. Check for existing GPS time and take EXIF time otherwise.
+        // This can be removed once isNewGpsData is used instead of the GPS time.
+        if (gpsTime == null) {
+            Date gpsTime = getExifGpsTime();
+            if (gpsTime == null) {
+                gpsTime = getExifTime();
+                if (gpsTime == null) {
+                    // Time still not set, take the current time.
+                    gpsTime = new Date();
+                }
+            }
+            setGpsTime(gpsTime);
+        }
+        if (tmp != null && tmp.getGpsTime() == null) {
+            // tmp.gpsTime overrides gpsTime, so we set it too.
+            tmp.setGpsTime(getGpsTime());
+        }
+    }
+
+    /**
+     * Queries whether the GPS data changed.
+     * @return {@code true} if GPS data changed, {@code false} otherwise
+     * @since 6392
+     */
+    public boolean hasNewGpsData() {
+        return isNewGpsData;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 6392)
@@ -214,7 +214,11 @@
 
     public void displayImage(GeoImageLayer layer, ImageEntry entry) {
+        boolean imageChanged;
+
         synchronized(this) {
             // TODO: pop up image dialog but don't load image again
 
+            imageChanged = currentEntry != entry;
+
             if (centerView && Main.isDisplayingMapView() && entry != null && entry.getPos() != null) {
                 Main.map.mapView.zoomTo(entry.getPos());
@@ -226,5 +230,9 @@
 
         if (entry != null) {
-            imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
+            if (imageChanged) {
+                // Set only if the image is new to preserve zoom and position if the same image is redisplayed 
+                // (e.g. to update the OSD).
+                imgDisplay.setImage(entry.getFile(), entry.getExifOrientation());
+            }
             setTitle("Geotagged Images" + (entry.getFile() != null ? " - " + entry.getFile().getName() : ""));
             StringBuffer osd = new StringBuffer(entry.getFile() != null ? entry.getFile().getName() : "");
@@ -302,3 +310,21 @@
         return currentEntry != null;
     }
+
+    /**
+     * Returns the currently displayed image.
+     * @return Currently displayed image or {@code null}
+     * @since 6392
+     */
+    public static ImageEntry getCurrentImage() {
+        return getInstance().currentEntry;
+    }
+
+    /**
+     * Returns the layer associated with the image.
+     * @return Layer associated with the image
+     * @since 6392
+     */
+    public static GeoImageLayer getCurrentLayer() {
+        return getInstance().currentLayer;
+    }
 }
Index: trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionExporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionExporter.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionExporter.java	(revision 6392)
@@ -66,4 +66,5 @@
         layerElem.setAttribute("type", "geoimage");
         layerElem.setAttribute("version", "0.1");
+        addAttr("show-thumbnails", Boolean.toString(layer.isUseThumbs()), layerElem, support);
 
         for (ImageEntry entry : layer.getImages()) {
@@ -101,4 +102,7 @@
                 addAttr("exif-time", Long.toString(entry.getExifTime().getTime()), imgElem, support);
             }
+            if (entry.getExifGpsTime() != null) {
+                addAttr("exif-gps-time", Long.toString(entry.getExifGpsTime().getTime()), imgElem, support);
+            }
             if (entry.getExifCoor() != null) {
                 Element posElem = support.createElement("exif-coordinates");
@@ -109,4 +113,7 @@
             if (entry.getExifImgDir() != null) {
                 addAttr("exif-image-direction", entry.getExifImgDir().toString(), imgElem, support);
+            }
+            if (entry.hasNewGpsData()) {
+                addAttr("is-new-gps-data", Boolean.toString(entry.hasNewGpsData()), imgElem, support);
             }
 
Index: trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java	(revision 6391)
+++ trunk/src/org/openstreetmap/josm/io/session/GeoImageSessionImporter.java	(revision 6392)
@@ -32,4 +32,5 @@
         List<ImageEntry> entries = new ArrayList<ImageEntry>();
         NodeList imgNodes = elem.getChildNodes();
+        boolean useThumbs = false;
         for (int i=0; i<imgNodes.getLength(); ++i) {
             Node imgNode = imgNodes.item(i);
@@ -56,8 +57,10 @@
                                 } else if (attrElem.getTagName().equals("gps-time")) {
                                     entry.setGpsTime(new Date(Long.parseLong(attrElem.getTextContent())));
-                                } else if (attrElem.getTagName().equals("gps-orientation")) {
+                                } else if (attrElem.getTagName().equals("exif-orientation")) {
                                     entry.setExifOrientation(Integer.parseInt(attrElem.getTextContent()));
                                 } else if (attrElem.getTagName().equals("exif-time")) {
                                     entry.setExifTime(new Date(Long.parseLong(attrElem.getTextContent())));
+                                } else if (attrElem.getTagName().equals("exif-gps-time")) {
+                                    entry.setExifGpsTime(new Date(Long.parseLong(attrElem.getTextContent())));
                                 } else if (attrElem.getTagName().equals("exif-coordinates")) {
                                     double lat = Double.parseDouble(attrElem.getAttribute("lat"));
@@ -66,4 +69,8 @@
                                 } else if (attrElem.getTagName().equals("exif-image-direction")) {
                                     entry.setExifImgDir(Double.parseDouble(attrElem.getTextContent()));
+                                } else if (attrElem.getTagName().equals("is-new-gps-data")) {
+                                    if (Boolean.parseBoolean(attrElem.getTextContent())) {
+                                        entry.flagNewGpsData();
+                                    }
                                 }
                                 // TODO: handle thumbnail loading
@@ -74,4 +81,6 @@
                     }
                     entries.add(entry);
+                } else if (imgElem.getTagName().equals("show-thumbnails")) {
+                    useThumbs = Boolean.parseBoolean(imgElem.getTextContent());
                 }
             }
@@ -87,5 +96,5 @@
         }
 
-        return new GeoImageLayer(entries, gpxLayer);
+        return new GeoImageLayer(entries, gpxLayer, useThumbs);
     }
 
