Index: trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 2661)
+++ trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java	(revision 2662)
@@ -192,5 +192,15 @@
     }
 
-    protected void setupDialog() {
+    private boolean setupDone = false;
+    
+    /**
+     * This is called by showDialog().
+     * Only invoke from outside if you need to modify the contentPane
+     */
+    public void setupDialog() {
+        if (setupDone)
+            return;
+        setupDone = true;
+
         setupEscListener();
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 2661)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 2662)
@@ -16,4 +16,8 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
 import java.io.File;
 import java.io.FileInputStream;
@@ -35,4 +39,5 @@
 
 import javax.swing.AbstractListModel;
+import javax.swing.BorderFactory;
 import javax.swing.ButtonGroup;
 import javax.swing.JButton;
@@ -46,9 +51,13 @@
 import javax.swing.JRadioButton;
 import javax.swing.JScrollPane;
+import javax.swing.JSeparator;
 import javax.swing.JSlider;
 import javax.swing.JTextField;
 import javax.swing.ListSelectionModel;
+import javax.swing.SwingConstants;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -63,5 +72,4 @@
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer.ImageEntry;
 import org.openstreetmap.josm.io.GpxReader;
 import org.openstreetmap.josm.tools.ExifReader;
@@ -79,11 +87,11 @@
     private static List<GpxData> loadedGpxData = new ArrayList<GpxData>();
 
-    public static class CorrelateParameters {
-        GpxData gpxData;
-        float timezone;
-        long offset;
-    }
-
     GeoImageLayer yLayer = null;
+    double timezone;
+    long delta;
+    
+    public CorrelateGpxWithImages(GeoImageLayer layer) {
+        this.yLayer = layer;
+    }
 
     private static class GpxDataWrapper {
@@ -104,12 +112,18 @@
     }
 
+    ExtendedDialog syncDialog;
     Vector<GpxDataWrapper> gpxLst = new Vector<GpxDataWrapper>();
-    JPanel panel = null;
-    JComboBox cbGpx = null;
-    JTextField tfTimezone = null;
-    JTextField tfOffset = null;
-    JRadioButton rbAllImg = null;
-    JRadioButton rbUntaggedImg = null;
-    JRadioButton rbNoExifImg = null;
+    JPanel outerPanel;
+    JComboBox cbGpx;
+    JTextField tfTimezone;
+    JTextField tfOffset;
+    JCheckBox cbExifImg;
+    JCheckBox cbTaggedImg;
+    JCheckBox cbShowThumbs;
+    JLabel statusBarText;
+    StatusBarListener statusBarListener;
+    
+    // remember the last number of matched photos
+    int lastNumMatched = 0;
 
     /** This class is called when the user doesn't find the GPX file he needs in the files that have
@@ -140,5 +154,5 @@
 
             try {
-                panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+                outerPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
 
                 Main.pref.put("lastDirectory", sel.getPath());
@@ -197,5 +211,5 @@
                 cbGpx.setSelectedIndex(cbGpx.getItemCount() - 1);
             } finally {
-                panel.setCursor(Cursor.getDefaultCursor());
+                outerPanel.setCursor(Cursor.getDefaultCursor());
             }
         }
@@ -257,7 +271,7 @@
             panelTf.add(new JLabel(tr("Gps time (read from the above photo): ")), gc);
 
-            tfGpsTime = new JTextField();
+            tfGpsTime = new JTextField(12);
             tfGpsTime.setEnabled(false);
-            tfGpsTime.setMinimumSize(new Dimension(150, tfGpsTime.getMinimumSize().height));
+            tfGpsTime.setMinimumSize(new Dimension(155, tfGpsTime.getMinimumSize().height));
             gc.gridx = 1;
             gc.weightx = 1.0;
@@ -334,7 +348,8 @@
                     if (date != null) {
                         lbExifTime.setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date));
-                        tfGpsTime.setText(new SimpleDateFormat("dd/MM/yyyy ").format(date));
+                        tfGpsTime.setText(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(date));
                         tfGpsTime.setCaretPosition(tfGpsTime.getText().length());
                         tfGpsTime.setEnabled(true);
+                        tfGpsTime.requestFocus();
                     } else {
                         lbExifTime.setText(tr("No date"));
@@ -418,10 +433,7 @@
 
             }
-
-        }
-    }
-
-    public CorrelateGpxWithImages(GeoImageLayer layer) {
-        this.yLayer = layer;
+            statusBarListener.updateStatusBar();
+            yLayer.updateBufferAndRepaint();
+        }
     }
 
@@ -454,5 +466,4 @@
 
         JPanel panelCb = new JPanel();
-        panelCb.setLayout(new FlowLayout());
 
         panelCb.add(new JLabel(tr("GPX track: ")));
@@ -465,7 +476,5 @@
 
         JButton buttonOpen = new JButton(tr("Open another GPX trace"));
-        buttonOpen.setIcon(ImageProvider.get("dialogs/geoimage/geoimage-open"));
         buttonOpen.addActionListener(new LoadGpxDataActionListener());
-
         panelCb.add(buttonOpen);
 
@@ -473,494 +482,585 @@
         panelTf.setLayout(new GridBagLayout());
 
-        GridBagConstraints gc = new GridBagConstraints();
-        gc.anchor = GridBagConstraints.WEST;
-
-        gc.gridx = gc.gridy = 0;
-        gc.gridwidth = gc.gridheight = 1;
-        gc.fill = GridBagConstraints.NONE;
-        gc.weightx = gc.weighty = 0.0;
-        panelTf.add(new JLabel(tr("Timezone: ")), gc);
-
-        float gpstimezone = Float.parseFloat(Main.pref.get("geoimage.doublegpstimezone", "0.0"));
-        if (gpstimezone == 0.0) {
-            gpstimezone = - Long.parseLong(Main.pref.get("geoimage.gpstimezone", "0"));
-        }
-        tfTimezone = new JTextField();
-        tfTimezone.setText(formatTimezone(gpstimezone));
-
-        gc.gridx = 1;
-        gc.gridy = 0;
-        gc.gridwidth = gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 1.0;
-        gc.weighty = 0.0;
-        panelTf.add(tfTimezone, gc);
-
-        gc.gridx = 0;
-        gc.gridy = 1;
-        gc.gridwidth = gc.gridheight = 1;
-        gc.fill = GridBagConstraints.NONE;
-        gc.weightx = gc.weighty = 0.0;
-        panelTf.add(new JLabel(tr("Offset:")), gc);
-
-        long delta = Long.parseLong(Main.pref.get("geoimage.delta", "0")) / 1000;
-        tfOffset = new JTextField();
+        String prefTimezone = Main.pref.get("geoimage.timezone", "0:00");
+        if (prefTimezone == null) {
+            prefTimezone = "0:00";
+        }
+        try {
+            timezone = parseTimezone(prefTimezone);
+        } catch (ParseException e) {
+            timezone = 0;
+        }
+        
+        tfTimezone = new JTextField(10);
+        tfTimezone.setText(formatTimezone(timezone));
+
+        try {
+        delta = parseOffset(Main.pref.get("geoimage.delta", "0"));
+        } catch (ParseException e) {
+            delta = 0;
+        }
+        delta = delta / 1000;
+        
+        tfOffset = new JTextField(10);
         tfOffset.setText(Long.toString(delta));
-        gc.gridx = gc.gridy = 1;
-        gc.gridwidth = gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 1.0;
-        gc.weighty = 0.0;
-        panelTf.add(tfOffset, gc);
-
-        JButton buttonViewGpsPhoto = new JButton(tr("<html>I can take a picture of my GPS receiver.<br>"
-                + "Can this help?</html>"));
+        
+        JPanel panelBtn = new JPanel();
+        
+        JButton buttonViewGpsPhoto = new JButton(tr("<html>Use photo of an accurate clock,<br>"
+                + "e.g. GPS receiver display</html>"));
+        buttonViewGpsPhoto.setIcon(ImageProvider.get("clock"));
         buttonViewGpsPhoto.addActionListener(new SetOffsetActionListener());
-        gc.gridx = 2;
-        gc.gridy = 0;
-        gc.gridwidth = 1;
-        gc.gridheight = 2;
-        gc.fill = GridBagConstraints.BOTH;
-        gc.weightx = 0.5;
-        gc.weighty = 1.0;
-        panelTf.add(buttonViewGpsPhoto, gc);
-
-        gc.gridx = 0;
-        gc.gridy = 2;
-        gc.gridwidth = gc.gridheight = 1;
-        gc.fill = GridBagConstraints.NONE;
-        gc.weightx = gc.weighty = 0.0;
-        panelTf.add(new JLabel(tr("Update position for: ")), gc);
-
-        gc.gridx = 1;
-        gc.gridy = 2;
-        gc.gridwidth = 2;
-        gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 1.0;
-        gc.weighty = 0.0;
-        rbAllImg = new JRadioButton(tr("All images"));
-        panelTf.add(rbAllImg, gc);
-
-        gc.gridx = 1;
-        gc.gridy = 3;
-        gc.gridwidth = 2;
-        gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 1.0;
-        gc.weighty = 0.0;
-        rbNoExifImg = new JRadioButton(tr("Images with no exif position"));
-        panelTf.add(rbNoExifImg, gc);
-
-        gc.gridx = 1;
-        gc.gridy = 4;
-        gc.gridwidth = 2;
-        gc.gridheight = 1;
-        gc.fill = GridBagConstraints.HORIZONTAL;
-        gc.weightx = 1.0;
-        gc.weighty = 0.0;
-        rbUntaggedImg = new JRadioButton(tr("Not yet tagged images"));
-        panelTf.add(rbUntaggedImg, gc);
-
-        gc.gridx = 0;
-        gc.gridy = 5;
-        gc.gridwidth = 2;
-        gc.gridheight = 1;
-        gc.fill = GridBagConstraints.NONE;
-        gc.weightx = gc.weighty = 0.0;
-        yLayer.useThumbs = Main.pref.getBoolean("geoimage.showThumbs", false);
-        JCheckBox cbShowThumbs = new JCheckBox(tr("Show Thumbnail images on the map"), yLayer.useThumbs);
-        panelTf.add(cbShowThumbs, gc);
-
-        ButtonGroup group = new ButtonGroup();
-        group.add(rbAllImg);
-        group.add(rbNoExifImg);
-        group.add(rbUntaggedImg);
-
-        rbUntaggedImg.setSelected(true);
-
-        panel = new JPanel();
-        panel.setLayout(new BorderLayout());
-
-        panel.add(panelCb, BorderLayout.PAGE_START);
-        panel.add(panelTf, BorderLayout.CENTER);
-
-        boolean isOk = false;
-        GpxDataWrapper selectedGpx = null;
-        while (! isOk) {
-            ExtendedDialog dialog = new ExtendedDialog(
-                    Main.parent,
-                    tr("Correlate images with GPX track"),
-                    new String[] { tr("Correlate"), tr("Auto-Guess"), tr("Cancel") }
-            );
-
-            dialog.setContent(panel);
-            dialog.setButtonIcons(new String[] { "ok.png", "dialogs/geoimage/gpx2imgManual.png", "cancel.png" });
-            dialog.showDialog();
-            int answer = dialog.getValue();
-            if(answer != 1 && answer != 2)
+
+        JButton buttonAutoGuess = new JButton(tr("Auto-Guess"));
+        buttonAutoGuess.addActionListener(new AutoGuessActionListener());
+
+        JButton buttonAdjust = new JButton(tr("Manual adjust"));
+        buttonAdjust.addActionListener(new AdjustActionListener());
+
+        JLabel labelPosition = new JLabel(tr("Override position for: "));
+ 
+        int numAll = getSortedImgList(true, true).size();
+        int numExif = numAll - getSortedImgList(false, true).size();
+        int numTagged = numAll - getSortedImgList(true, false).size();
+
+        cbExifImg = new JCheckBox(tr("Images with geo location in exif data ({0}/{1})", numExif, numAll));
+        cbExifImg.setEnabled(numExif != 0);
+
+        cbTaggedImg = new JCheckBox(tr("Images that are already tagged ({0}/{1})", numTagged, numAll), true);
+        cbTaggedImg.setEnabled(numTagged != 0);
+        
+        labelPosition.setEnabled(cbExifImg.isEnabled() || cbTaggedImg.isEnabled());
+
+        boolean ticked = yLayer.thumbsLoaded || Main.pref.getBoolean("geoimage.showThumbs", false);
+        cbShowThumbs = new JCheckBox(tr("Show Thumbnail images on the map"), ticked);
+        cbShowThumbs.setEnabled(!yLayer.thumbsLoaded);
+        /*cbShowThumbs.addItemListener(new ItemListener() {
+            public void itemStateChanged(ItemEvent e) {
+                if (e.getStateChange() == ItemEvent.SELECTED) {
+                    yLayer.loadThumbs();
+                } else {
+                }        
+            }
+        });*/
+
+        int y=0;
+        GBC gbc = GBC.eol();
+        gbc.gridx = 0;
+        gbc.gridy = y++;
+        panelTf.add(panelCb, gbc);
+        
+        
+        gbc = GBC.eol().fill(GBC.HORIZONTAL).insets(0,0,0,12);
+        gbc.gridx = 0;
+        gbc.gridy = y++;
+        panelTf.add(new JSeparator(SwingConstants.HORIZONTAL), gbc);
+
+
+        gbc = GBC.std();
+        gbc.gridx = 0;
+        gbc.gridy = y;
+        panelTf.add(new JLabel(tr("Timezone: ")), gbc);
+
+        gbc = GBC.std().fill(GBC.HORIZONTAL);
+        gbc.gridx = 1;
+        gbc.gridy = y++;
+        gbc.weightx = 1.;
+        panelTf.add(tfTimezone, gbc);
+
+        gbc = GBC.std();
+        gbc.gridx = 0;
+        gbc.gridy = y;
+        panelTf.add(new JLabel(tr("Offset:")), gbc);
+
+        gbc = GBC.std().fill(GBC.HORIZONTAL);
+        gbc.gridx = 1;
+        gbc.gridy = y++;
+        gbc.weightx = 1.;
+        panelTf.add(tfOffset, gbc);
+        
+        gbc = GBC.std().insets(5,5,5,5);
+        gbc.gridx = 2;
+        gbc.gridy = y-2;
+        gbc.gridheight = 2;
+        gbc.gridwidth = 2;
+        gbc.fill = GridBagConstraints.BOTH;
+        gbc.weightx = 0.5;
+        panelTf.add(buttonViewGpsPhoto, gbc);
+
+        gbc = GBC.std().fill(GBC.BOTH).insets(5,5,5,5);
+        gbc.gridx = 2;
+        gbc.gridy = y++;
+        gbc.weightx = 0.5;
+        panelTf.add(buttonAutoGuess, gbc);
+        
+        gbc.gridx = 3;
+        panelTf.add(buttonAdjust, gbc);
+
+        gbc = GBC.eol().fill(GBC.HORIZONTAL).insets(0,12,0,0);
+        gbc.gridx = 0;
+        gbc.gridy = y++;
+        panelTf.add(new JSeparator(SwingConstants.HORIZONTAL), gbc);
+
+        gbc = GBC.eol();
+        gbc.gridx = 0;
+        gbc.gridy = y++;
+        panelTf.add(labelPosition, gbc);
+
+        gbc = GBC.eol();
+        gbc.gridx = 1;
+        gbc.gridy = y++;
+        panelTf.add(cbExifImg, gbc);
+
+        gbc = GBC.eol();
+        gbc.gridx = 1;
+        gbc.gridy = y++;
+        panelTf.add(cbTaggedImg, gbc);
+
+        gbc = GBC.eol();
+        gbc.gridx = 0;
+        gbc.gridy = y++;
+        panelTf.add(cbShowThumbs, gbc);
+
+        final JPanel statusBar = new JPanel();
+        statusBar.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+        statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
+        statusBarText = new JLabel(" ");
+        statusBarText.setFont(statusBarText.getFont().deriveFont(8));
+        statusBar.add(statusBarText);
+        
+        statusBarListener = new StatusBarListener() {
+            @Override
+            public void updateStatusBar() {
+                statusBarText.setText(statusText());
+            }
+            private String statusText() {
+                try {
+                    timezone = parseTimezone(tfTimezone.getText().trim());
+                    delta = parseOffset(tfOffset.getText().trim());
+                } catch (ParseException e) {
+                     return e.getMessage();
+                }
+                
+                // Construct a list of images that have a date, and sort them on the date.
+                ArrayList<ImageEntry> dateImgLst = getSortedImgList();
+                for (ImageEntry ie : dateImgLst) {
+                    ie.cleanTmp();
+                }
+                
+                GpxDataWrapper selGpx = selectedGPX(false);
+                if (selGpx == null)
+                    return tr("No gpx selected");
+                    
+                lastNumMatched = matchGpxTrack(dateImgLst, selGpx.data, (long) (timezone * 3600) + delta);
+
+                return tr("<html>Matched <b>{0}</b> of <b>{1}</b> photos to GPX track.", lastNumMatched, dateImgLst.size());
+            }
+        };
+        
+        tfTimezone.getDocument().addDocumentListener(statusBarListener);
+        tfOffset.getDocument().addDocumentListener(statusBarListener);
+        cbExifImg.addItemListener(statusBarListener);
+        cbTaggedImg.addItemListener(statusBarListener);
+        
+        statusBarListener.updateStatusBar();
+
+        outerPanel = new JPanel();
+        outerPanel.setLayout(new BorderLayout());
+        outerPanel.add(statusBar, BorderLayout.PAGE_END);
+
+
+        syncDialog = new ExtendedDialog(
+                Main.parent,
+                tr("Correlate images with GPX track"),
+                new String[] { tr("Correlate"), tr("Cancel") },
+                false
+        );
+        syncDialog.setContent(panelTf, false);
+        syncDialog.setButtonIcons(new String[] { "ok.png", "cancel.png" });
+        syncDialog.setupDialog();
+        outerPanel.add(syncDialog.getContentPane(), BorderLayout.PAGE_START);
+        syncDialog.setContentPane(outerPanel);
+        syncDialog.pack();
+        syncDialog.addWindowListener(new WindowAdapter() {
+            final int CANCEL = -1;
+            final int DONE = 0;
+            final int AGAIN = 1;
+            final int NOTHING = 2;
+            private int checkAndSave() {
+                if (syncDialog.isVisible()) {
+                    // nothing happened: JOSM was minimized or similar
+                    return NOTHING;             
+                }
+                int answer = syncDialog.getValue();
+                if(answer != 1)
+                    return CANCEL;
+
+                // Parse values again, to display an error if the format is not recognized
+                try {
+                    timezone = parseTimezone(tfTimezone.getText().trim());
+                } catch (ParseException e) {
+                    JOptionPane.showMessageDialog(Main.parent, e.getMessage(),
+                            tr("Invalid timezone"), JOptionPane.ERROR_MESSAGE);
+                    return AGAIN;
+                }
+                
+                try {
+                    delta = parseOffset(tfOffset.getText().trim());
+                } catch (ParseException e) {
+                        JOptionPane.showMessageDialog(Main.parent, e.getMessage(),
+                                tr("Invalid offset"), JOptionPane.ERROR_MESSAGE);
+                    return AGAIN;
+                }
+                
+                if (lastNumMatched == 0) {
+                    if (new ExtendedDialog(
+                            Main.parent,
+                            tr("Correlate images with GPX track"),
+                            new String[] { tr("OK"), tr("Try Again") }).
+                        setContent(tr("No images could be matched!")).
+                        setButtonIcons(new String[] { "ok.png", "dialogs/refresh.png"}).
+                        showDialog().getValue() == 2)
+                            return AGAIN;
+                }
+                return DONE;
+            }
+            
+            public void windowDeactivated(WindowEvent e) {
+                int result = checkAndSave();
+                switch (result) {
+                    case NOTHING:
+                        break;
+                    case CANCEL:
+                    {
+                        for (ImageEntry ie : yLayer.data) {
+                            ie.tmp = null;
+                        }
+                        yLayer.updateBufferAndRepaint();
+                        break;
+                    }
+                    case AGAIN:
+                        actionPerformed(null);
+                        break;
+                    case DONE:
+                    {
+                        Main.pref.put("geoimage.timezone", formatTimezone(timezone));
+                        Main.pref.put("geoimage.delta", Long.toString(delta * 1000));
+                        Main.pref.put("geoimage.showThumbs", yLayer.useThumbs);
+
+                        yLayer.useThumbs = cbShowThumbs.isSelected();//FIXME
+                        yLayer.loadThumbs();
+                        
+                        // Search whether an other layer has yet defined some bounding box.
+                        // If none, we'll zoom to the bounding box of the layer with the photos.
+                        boolean boundingBoxedLayerFound = false;
+                        for (Layer l: Main.map.mapView.getAllLayers()) {
+                            if (l != yLayer) {
+                                BoundingXYVisitor bbox = new BoundingXYVisitor();
+                                l.visitBoundingBox(bbox);
+                                if (bbox.getBounds() != null) {
+                                    boundingBoxedLayerFound = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if (! boundingBoxedLayerFound) {
+                            BoundingXYVisitor bbox = new BoundingXYVisitor();
+                            yLayer.visitBoundingBox(bbox);
+                            Main.map.mapView.recalculateCenterScale(bbox);
+                        }
+
+
+                        for (ImageEntry ie : yLayer.data) {
+                            ie.applyTmp();
+                        }
+
+                        yLayer.updateBufferAndRepaint();
+
+
+                        break;
+                    }
+                    default:
+                        throw new IllegalStateException();
+                }
+            }
+        });
+        syncDialog.showDialog();
+    }
+
+    private static abstract class StatusBarListener implements  DocumentListener, ItemListener {
+        public void insertUpdate(DocumentEvent ev) {
+            updateStatusBar();
+        }
+        public void removeUpdate(DocumentEvent ev) {
+            updateStatusBar();
+        }
+        public void changedUpdate(DocumentEvent ev) {
+        }
+        public void itemStateChanged(ItemEvent e) {
+            updateStatusBar();
+        }
+        abstract public void updateStatusBar();
+    }
+
+    /**
+     * Presents dialog with sliders for manual adjust.
+     */
+    private class AdjustActionListener implements ActionListener {
+    
+        public void actionPerformed(ActionEvent arg0) {
+
+            long diff = delta + Math.round(timezone*60*60);
+            
+            double diffInH = (double)diff/(60*60);    // hours
+
+            // Find day difference
+            final int dayOffset = (int)Math.round(diffInH / 24); // days
+            double tmz = diff - dayOffset*24*60*60;  // seconds
+
+            // In hours, rounded to two decimal places
+            tmz = (double)Math.round(tmz*100/(60*60)) / 100;
+
+            // Due to imprecise clocks we might get a "+3:28" timezone, which should obviously be 3:30 with
+            // -2 minutes offset. This determines the real timezone and finds offset.
+            double fixTimezone = (double)Math.round(tmz * 2)/2; // hours, rounded to one decimal place
+            int offset = (int)Math.round(diff - fixTimezone*60*60) - dayOffset*24*60*60; // seconds
+
+            // Info Labels
+            final JLabel lblMatches = new JLabel();
+
+            // Timezone Slider
+            // The slider allows to switch timezon from -12:00 to 12:00 in 30 minutes
+            // steps. Therefore the range is -24 to 24.
+            final JLabel lblTimezone = new JLabel();
+            final JSlider sldTimezone = new JSlider(-24, 24, 0);
+            sldTimezone.setPaintLabels(true);
+            Hashtable<Integer,JLabel> labelTable = new Hashtable<Integer, JLabel>();
+            labelTable.put(-24, new JLabel("-12:00"));
+            labelTable.put(-12, new JLabel( "-6:00"));
+            labelTable.put(  0, new JLabel(  "0:00"));
+            labelTable.put( 12, new JLabel(  "6:00"));
+            labelTable.put( 24, new JLabel( "12:00"));
+            sldTimezone.setLabelTable(labelTable);
+
+            // Minutes Slider
+            final JLabel lblMinutes = new JLabel();
+            final JSlider sldMinutes = new JSlider(-15, 15, 0);
+            sldMinutes.setPaintLabels(true);
+            sldMinutes.setMajorTickSpacing(5);
+
+            // Seconds slider
+            final JLabel lblSeconds = new JLabel();
+            final JSlider sldSeconds = new JSlider(-60, 60, 0);
+            sldSeconds.setPaintLabels(true);
+            sldSeconds.setMajorTickSpacing(30);
+
+            // This is called whenever one of the sliders is moved.
+            // It updates the labels and also calls the "match photos" code
+            class sliderListener implements ChangeListener {
+                public void stateChanged(ChangeEvent e) {
+                    // parse slider position into real timezone
+                    double tz = Math.abs(sldTimezone.getValue());
+                    String zone = tz % 2 == 0
+                    ? (int)Math.floor(tz/2) + ":00"
+                            : (int)Math.floor(tz/2) + ":30";
+                    if(sldTimezone.getValue() < 0) {
+                        zone = "-" + zone;
+                    }
+
+                    lblTimezone.setText(tr("Timezone: {0}", zone));
+                    lblMinutes.setText(tr("Minutes: {0}", sldMinutes.getValue()));
+                    lblSeconds.setText(tr("Seconds: {0}", sldSeconds.getValue()));
+
+                    try {
+                        timezone = parseTimezone(zone);
+                    } catch (ParseException pe) {
+                        throw new RuntimeException();
+                    }
+                    delta = sldMinutes.getValue()*60 + sldSeconds.getValue();
+
+                    tfTimezone.getDocument().removeDocumentListener(statusBarListener);
+                    tfOffset.getDocument().removeDocumentListener(statusBarListener);
+                    
+                    tfTimezone.setText(formatTimezone(timezone));
+                    tfOffset.setText(Long.toString(delta + dayOffset*24*60*60));    // add the day offset to the offset field
+
+                    tfTimezone.getDocument().addDocumentListener(statusBarListener);
+                    tfOffset.getDocument().addDocumentListener(statusBarListener);
+
+
+
+                    lblMatches.setText(statusBarText.getText() + tr("<br>(Time difference of {0} days)", Math.abs(dayOffset)));
+
+                    statusBarListener.updateStatusBar();
+                    yLayer.updateBufferAndRepaint();
+                }
+            }
+
+            // Put everything together
+            JPanel p = new JPanel(new GridBagLayout());
+            p.setPreferredSize(new Dimension(400, 230));
+            p.add(lblMatches, GBC.eol().fill());
+            p.add(lblTimezone, GBC.eol().fill());
+            p.add(sldTimezone, GBC.eol().fill().insets(0, 0, 0, 10));
+            p.add(lblMinutes, GBC.eol().fill());
+            p.add(sldMinutes, GBC.eol().fill().insets(0, 0, 0, 10));
+            p.add(lblSeconds, GBC.eol().fill());
+            p.add(sldSeconds, GBC.eol().fill());
+
+            // If there's an error in the calculation the found values
+            // will be off range for the sliders. Catch this error
+            // and inform the user about it.
+            try {
+                sldTimezone.setValue((int)(fixTimezone*2));
+                sldMinutes.setValue(offset/60);
+                sldSeconds.setValue(offset%60);
+            } catch(Exception e) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("An error occurred while trying to match the photos to the GPX track."
+                                +" You can adjust the sliders to manually match the photos."),
+                                tr("Matching photos to track failed"),
+                                JOptionPane.WARNING_MESSAGE);
+            }
+
+            // Call the sliderListener once manually so labels get adjusted
+            new sliderListener().stateChanged(null);
+            // Listeners added here, otherwise it tries to match three times
+            // (when setting the default values)
+            sldTimezone.addChangeListener(new sliderListener());
+            sldMinutes.addChangeListener(new sliderListener());
+            sldSeconds.addChangeListener(new sliderListener());
+
+            // There is no way to cancel this dialog, all changes get applied
+            // immediately. Therefore "Close" is marked with an "OK" icon.
+            // Settings are only saved temporarily to the layer.
+            new ExtendedDialog(Main.parent,
+                    tr("Adjust timezone and offset"),
+                    new String[] { tr("Close")}).
+                setContent(p).setButtonIcons(new String[] {"ok.png"}).showDialog();
+        }
+    }
+
+    private class AutoGuessActionListener implements ActionListener {
+    
+        public void actionPerformed(ActionEvent arg0) {
+            GpxDataWrapper gpxW = selectedGPX(true);
+            if (gpxW == null)
                 return;
-
-            // Check the selected values
-            Object item = cbGpx.getSelectedItem();
-
-            if (item == null || ! (item instanceof GpxDataWrapper)) {
-                JOptionPane.showMessageDialog(Main.parent, tr("You should select a GPX track"),
-                        tr("No selected GPX track"), JOptionPane.ERROR_MESSAGE );
-                continue;
-            }
-            selectedGpx = ((GpxDataWrapper) item);
-
-            if (answer == 2) {
-                autoGuess(selectedGpx.data);
+            GpxData gpx = gpxW.data;
+            
+            ArrayList<ImageEntry> imgs = getSortedImgList();
+            PrimaryDateParser dateParser = new PrimaryDateParser();
+
+            // no images found, exit
+            if(imgs.size() <= 0) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("The selected photos don't contain time information."),
+                        tr("Photos don't contain time information"), JOptionPane.WARNING_MESSAGE);
                 return;
             }
 
-            Float timezoneValue = parseTimezone(tfTimezone.getText().trim());
-            if (timezoneValue == null) {
-                JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing timezone.\nExpected format: {0}", "+H:MM"),
-                        tr("Invalid timezone"), JOptionPane.ERROR_MESSAGE);
-                continue;
-            }
-            gpstimezone = timezoneValue.floatValue();
-
-            String deltaText = tfOffset.getText().trim();
-            if (deltaText.length() > 0) {
-                try {
-                    if(deltaText.startsWith("+")) {
-                        deltaText = deltaText.substring(1);
+            // Init variables
+            long firstExifDate = imgs.get(0).time.getTime()/1000;
+
+            long firstGPXDate = -1;
+            // Finds first GPX point
+            outer: for (GpxTrack trk : gpx.tracks) {
+                for (Collection<WayPoint> segment : trk.trackSegs) {
+                    for (WayPoint curWp : segment) {
+                        String curDateWpStr = (String) curWp.attr.get("time");
+                        if (curDateWpStr == null) {
+                            continue;
+                        }
+
+                        try {
+                            firstGPXDate = dateParser.parse(curDateWpStr).getTime()/1000;
+                            break outer;
+                        } catch(Exception e) {}
                     }
-                    delta = Long.parseLong(deltaText);
-                } catch(NumberFormatException nfe) {
-                    JOptionPane.showMessageDialog(Main.parent, tr("Error while parsing offset.\nExpected format: {0}", "number"),
-                            tr("Invalid offset"), JOptionPane.ERROR_MESSAGE);
-                    continue;
-                }
-            } else {
-                delta = 0;
-            }
-
-            yLayer.useThumbs = cbShowThumbs.isSelected();
-
-            Main.pref.put("geoimage.doublegpstimezone", Double.toString(gpstimezone));
-            Main.pref.put("geoimage.gpstimezone", Long.toString(- ((long) gpstimezone)));
-            Main.pref.put("geoimage.delta", Long.toString(delta * 1000));
-            Main.pref.put("geoimage.showThumbs", yLayer.useThumbs);
-            isOk = true;
-
-            if (yLayer.useThumbs) {
-                yLayer.thumbsloader = new ThumbsLoader(yLayer);
-                Thread t = new Thread(yLayer.thumbsloader);
-                t.setPriority(Thread.MIN_PRIORITY);
-                t.start();
-            }
-
-        }
-
-        // Construct a list of images that have a date, and sort them on the date.
-        ArrayList<ImageEntry> dateImgLst = getSortedImgList(rbAllImg.isSelected(), rbNoExifImg.isSelected());
-
-        int matched = matchGpxTrack(dateImgLst, selectedGpx.data, (long) (gpstimezone * 3600) + delta);
-
-        // Search whether an other layer has yet defined some bounding box.
-        // If none, we'll zoom to the bounding box of the layer with the photos.
-        boolean boundingBoxedLayerFound = false;
-        for (Layer l: Main.map.mapView.getAllLayers()) {
-            if (l != yLayer) {
-                BoundingXYVisitor bbox = new BoundingXYVisitor();
-                l.visitBoundingBox(bbox);
-                if (bbox.getBounds() != null) {
-                    boundingBoxedLayerFound = true;
-                    break;
-                }
-            }
-        }
-        if (! boundingBoxedLayerFound) {
-            BoundingXYVisitor bbox = new BoundingXYVisitor();
-            yLayer.visitBoundingBox(bbox);
-            Main.map.mapView.recalculateCenterScale(bbox);
-        }
-
-        Main.map.repaint();
-
-        JOptionPane.showMessageDialog(Main.parent, tr("Found {0} matches of {1} in GPX track {2}", matched, dateImgLst.size(), selectedGpx.name),
-                tr("GPX Track loaded"),
-                ((dateImgLst.size() > 0 && matched == 0) ? JOptionPane.WARNING_MESSAGE
-                        : JOptionPane.INFORMATION_MESSAGE));
-
-    }
-
-    // These variables all belong to "auto guess" but need to be accessible
-    // from the slider change listener
-    private int dayOffset;
-    private JLabel lblMatches;
-    private JLabel lblOffset;
-    private JLabel lblTimezone;
-    private JLabel lblMinutes;
-    private JLabel lblSeconds;
-    private JSlider sldTimezone;
-    private JSlider sldMinutes;
-    private JSlider sldSeconds;
-    private GpxData autoGpx;
-    private ArrayList<ImageEntry> autoImgs;
-    private long firstGPXDate = -1;
-    private long firstExifDate = -1;
-
-    /**
-     * Tries to automatically match opened photos to a given GPX track. Changes are applied
-     * immediately. Presents dialog with sliders for manual adjust.
-     * @param GpxData The GPX track to match against
-     */
-    private void autoGuess(GpxData gpx) {
-        autoGpx = gpx;
-        autoImgs = getSortedImgList(true, false);
-        PrimaryDateParser dateParser = new PrimaryDateParser();
-
-        // no images found, exit
-        if(autoImgs.size() <= 0) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("The selected photos don't contain time information."),
-                    tr("Photos don't contain time information"), JOptionPane.WARNING_MESSAGE);
-            return;
-        }
-
-        ImageViewerDialog dialog = ImageViewerDialog.getInstance();
-        dialog.showDialog();
-        // Will show first photo if none is selected yet
-        if(!dialog.hasImage()) {
-            yLayer.showNextPhoto();
-            // FIXME: If the dialog is minimized it will not be maximized. ToggleDialog is
-            // in need of a complete re-write to allow this in a reasonable way.
-        }
-
-        // Init variables
-        firstExifDate = autoImgs.get(0).time.getTime()/1000;
-
-
-        // Finds first GPX point
-        outer: for (GpxTrack trk : gpx.tracks) {
-            for (Collection<WayPoint> segment : trk.trackSegs) {
-                for (WayPoint curWp : segment) {
-                    String curDateWpStr = (String) curWp.attr.get("time");
-                    if (curDateWpStr == null) {
-                        continue;
-                    }
-
-                    try {
-                        firstGPXDate = dateParser.parse(curDateWpStr).getTime()/1000;
-                        break outer;
-                    } catch(Exception e) {}
-                }
-            }
-        }
-
-        // No GPX timestamps found, exit
-        if(firstGPXDate < 0) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("The selected GPX track doesn't contain timestamps. Please select another one."),
-                    tr("GPX Track has no time information"), JOptionPane.WARNING_MESSAGE);
-            return;
-        }
-
-        // seconds
-        long diff = (yLayer.hasTimeoffset)
-        ? yLayer.timeoffset
-                : firstExifDate - firstGPXDate;
-        yLayer.timeoffset = diff;
-        yLayer.hasTimeoffset = true;
-
-        double diffInH = (double)diff/(60*60);    // hours
-
-        // Find day difference
-        dayOffset = (int)Math.round(diffInH / 24); // days
-        double timezone = diff - dayOffset*24*60*60;  // seconds
-
-        // In hours, rounded to two decimal places
-        timezone = (double)Math.round(timezone*100/(60*60)) / 100;
-
-        // Due to imprecise clocks we might get a "+3:28" timezone, which should obviously be 3:30 with
-        // -2 minutes offset. This determines the real timezone and finds offset.
-        double fixTimezone = (double)Math.round(timezone * 2)/2; // hours, rounded to one decimal place
-        int offset = (int)Math.round(diff - fixTimezone*60*60) - dayOffset*24*60*60; // seconds
-
-        /*System.out.println("phto " + firstExifDate);
-        System.out.println("gpx  " + firstGPXDate);
-        System.out.println("diff " + diff);
-        System.out.println("difh " + diffInH);
-        System.out.println("days " + dayOffset);
-        System.out.println("time " + timezone);
-        System.out.println("fix  " + fixTimezone);
-        System.out.println("offt " + offset);*/
-
-        // This is called whenever one of the sliders is moved.
-        // It updates the labels and also calls the "match photos" code
-        class sliderListener implements ChangeListener {
-            public void stateChanged(ChangeEvent e) {
-                // parse slider position into real timezone
-                double tz = Math.abs(sldTimezone.getValue());
-                String zone = tz % 2 == 0
-                ? (int)Math.floor(tz/2) + ":00"
-                        : (int)Math.floor(tz/2) + ":30";
-                if(sldTimezone.getValue() < 0) {
-                    zone = "-" + zone;
-                }
-
-                lblTimezone.setText(tr("Timezone: {0}", zone));
-                lblMinutes.setText(tr("Minutes: {0}", sldMinutes.getValue()));
-                lblSeconds.setText(tr("Seconds: {0}", sldSeconds.getValue()));
-
-                float gpstimezone = parseTimezone(zone).floatValue();
-
-                // Reset previous position
-                for(ImageEntry x : autoImgs) {
-                    x.pos = null;
-                }
-
-                long timediff = (long) (gpstimezone * 3600)
-                + dayOffset*24*60*60
-                + sldMinutes.getValue()*60
-                + sldSeconds.getValue();
-
-                int matched = matchGpxTrack(autoImgs, autoGpx, timediff);
-
-                lblMatches.setText(
-                        tr("Matched {0} of {1} photos to GPX track.", matched, autoImgs.size())
-                        + ((Math.abs(dayOffset) == 0)
-                                ? ""
-                                        : " " + tr("(Time difference of {0} days)", Math.abs(dayOffset))
-                        )
-                );
-
-                int offset = (int)(firstGPXDate+timediff-firstExifDate);
-                int o = Math.abs(offset);
-                lblOffset.setText(
-                        tr("Offset between track and photos: {0}m {1}s",
-                                (offset < 0 ? "-" : "") + Long.toString(o/60),
-                                Long.toString(o%60)
-                        )
-                );
-
-                yLayer.timeoffset = timediff;
-                Main.main.map.repaint();
-            }
-        }
-
-        // Info Labels
-        lblMatches = new JLabel();
-        lblOffset = new JLabel();
-
-        // Timezone Slider
-        // The slider allows to switch timezon from -12:00 to 12:00 in 30 minutes
-        // steps. Therefore the range is -24 to 24.
-        lblTimezone = new JLabel();
-        sldTimezone = new JSlider(-24, 24, 0);
-        sldTimezone.setPaintLabels(true);
-        Hashtable<Integer,JLabel> labelTable = new Hashtable<Integer, JLabel>();
-        labelTable.put(-24, new JLabel("-12:00"));
-        labelTable.put(-12, new JLabel( "-6:00"));
-        labelTable.put(  0, new JLabel(  "0:00"));
-        labelTable.put( 12, new JLabel(  "6:00"));
-        labelTable.put( 24, new JLabel( "12:00"));
-        sldTimezone.setLabelTable(labelTable);
-
-        // Minutes Slider
-        lblMinutes = new JLabel();
-        sldMinutes = new JSlider(-15, 15, 0);
-        sldMinutes.setPaintLabels(true);
-        sldMinutes.setMajorTickSpacing(5);
-
-        // Seconds slider
-        lblSeconds = new JLabel();
-        sldSeconds = new JSlider(-60, 60, 0);
-        sldSeconds.setPaintLabels(true);
-        sldSeconds.setMajorTickSpacing(30);
-
-        // Put everything together
-        JPanel p = new JPanel(new GridBagLayout());
-        p.setPreferredSize(new Dimension(400, 230));
-        p.add(lblMatches, GBC.eol().fill());
-        p.add(lblOffset, GBC.eol().fill().insets(0, 0, 0, 10));
-        p.add(lblTimezone, GBC.eol().fill());
-        p.add(sldTimezone, GBC.eol().fill().insets(0, 0, 0, 10));
-        p.add(lblMinutes, GBC.eol().fill());
-        p.add(sldMinutes, GBC.eol().fill().insets(0, 0, 0, 10));
-        p.add(lblSeconds, GBC.eol().fill());
-        p.add(sldSeconds, GBC.eol().fill());
-
-        // If there's an error in the calculation the found values
-        // will be off range for the sliders. Catch this error
-        // and inform the user about it.
-        try {
-            sldTimezone.setValue((int)(fixTimezone*2));
-            sldMinutes.setValue(offset/60);
-            sldSeconds.setValue(offset%60);
-        } catch(Exception e) {
-            JOptionPane.showMessageDialog(Main.parent,
-                    tr("An error occurred while trying to match the photos to the GPX track."
-                            +" You can adjust the sliders to manually match the photos."),
-                            tr("Matching photos to track failed"),
-                            JOptionPane.WARNING_MESSAGE);
-        }
-
-        // Call the sliderListener once manually so labels get adjusted
-        new sliderListener().stateChanged(null);
-        // Listeners added here, otherwise it tries to match three times
-        // (when setting the default values)
-        sldTimezone.addChangeListener(new sliderListener());
-        sldMinutes.addChangeListener(new sliderListener());
-        sldSeconds.addChangeListener(new sliderListener());
-
-        // There is no way to cancel this dialog, all changes get applied
-        // immediately. Therefore "Close" is marked with an "OK" icon.
-        // Settings are only saved temporarily to the layer.
-        ExtendedDialog d = new ExtendedDialog(Main.parent,
-                tr("Adjust timezone and offset"),
-                new String[] { tr("Close"),  tr("Default Values") }
-        );
-
-        d.setContent(p);
-        d.setButtonIcons(new String[] { "ok.png", "dialogs/refresh.png"});
-        d.showDialog();
-        int answer = d.getValue();
-        // User wants default values; discard old result and re-open dialog
-        if(answer == 2) {
-            yLayer.hasTimeoffset = false;
-            autoGuess(gpx);
-        }
-    }
-
+                }
+            }
+
+            // No GPX timestamps found, exit
+            if(firstGPXDate < 0) {
+                JOptionPane.showMessageDialog(Main.parent,
+                        tr("The selected GPX track doesn't contain timestamps. Please select another one."),
+                        tr("GPX Track has no time information"), JOptionPane.WARNING_MESSAGE);
+                return;
+            }
+
+            // seconds
+            long diff = firstExifDate - firstGPXDate;
+
+            double diffInH = (double)diff/(60*60);    // hours
+
+            // Find day difference
+            int dayOffset = (int)Math.round(diffInH / 24); // days
+            double tz = diff - dayOffset*24*60*60;  // seconds
+
+            // In hours, rounded to two decimal places
+            tz = (double)Math.round(tz*100/(60*60)) / 100;
+
+            // Due to imprecise clocks we might get a "+3:28" timezone, which should obviously be 3:30 with
+            // -2 minutes offset. This determines the real timezone and finds offset.
+            timezone = (double)Math.round(tz * 2)/2; // hours, rounded to one decimal place
+            delta = (long)Math.round(diff - timezone*60*60); // seconds
+
+            /*System.out.println("phto " + firstExifDate);
+            System.out.println("gpx  " + firstGPXDate);
+            System.out.println("diff " + diff);
+            System.out.println("difh " + diffInH);
+            System.out.println("days " + dayOffset);
+            System.out.println("time " + tz);
+            System.out.println("fix  " + timezone);
+            System.out.println("offt " + delta);*/
+
+            tfTimezone.getDocument().removeDocumentListener(statusBarListener);
+            tfOffset.getDocument().removeDocumentListener(statusBarListener);
+            
+            tfTimezone.setText(formatTimezone(timezone));
+            tfOffset.setText(Long.toString(delta));
+            tfOffset.requestFocus();
+
+            tfTimezone.getDocument().addDocumentListener(statusBarListener);
+            tfOffset.getDocument().addDocumentListener(statusBarListener);
+            
+            statusBarListener.updateStatusBar();
+            yLayer.updateBufferAndRepaint();
+        }
+    }
+
+    private ArrayList<ImageEntry>  getSortedImgList() {
+        return getSortedImgList(cbExifImg.isSelected(), cbTaggedImg.isSelected());
+    }
+    
     /**
      * Returns a list of images that fulfill the given criteria.
      * Default setting is to return untagged images, but may be overwritten.
      * @param boolean all -- returns all available images
-     * @param boolean noexif -- returns untagged images without EXIF-GPS coords
+     * @param boolean noexif -- returns untagged images without EXIF-GPS coords 
+     *                          this parameter is irrelevant if <code>all</code> is true
+     * @param boolean exif -- also returns images with exif-gps info
+     * @param boolean tagged -- also returns tagged images
      * @return ArrayList<ImageEntry> matching images
      */
-    private ArrayList<ImageEntry> getSortedImgList(boolean all, boolean noexif) {
+    private ArrayList<ImageEntry> getSortedImgList(boolean exif, boolean tagged) {
         ArrayList<ImageEntry> dateImgLst = new ArrayList<ImageEntry>(yLayer.data.size());
-        if (all) {
-            for (ImageEntry e : yLayer.data) {
-                if (e.time != null) {
-                    // Reset previous position
-                    e.pos = null;
-                    dateImgLst.add(e);
-                }
-            }
-
-        } else if (noexif) {
-            for (ImageEntry e : yLayer.data) {
-                if (e.time != null && e.exifCoor == null) {
-                    dateImgLst.add(e);
-                }
-            }
-
-        } else {
-            for (ImageEntry e : yLayer.data) {
-                if (e.time != null && e.pos == null) {
-                    dateImgLst.add(e);
-                }
-            }
-        }
-
+        for (ImageEntry e : yLayer.data) {
+            if (e.time == null)
+                continue;
+                
+            if (e.exifCoor != null) {
+                if (!exif)
+                    continue;
+            }
+                
+            if (e.isTagged() && e.exifCoor == null) {
+                if (!tagged)
+                    continue;
+            }
+                
+            dateImgLst.add(e);
+        }
+        
         Collections.sort(dateImgLst, new Comparator<ImageEntry>() {
             public int compare(ImageEntry arg0, ImageEntry arg1) {
@@ -970,4 +1070,17 @@
 
         return dateImgLst;
+    }
+
+    private GpxDataWrapper selectedGPX(boolean complain) {
+        Object item = cbGpx.getSelectedItem();
+
+        if (item == null || ! (item instanceof GpxDataWrapper)) {
+            if (complain) {
+                JOptionPane.showMessageDialog(Main.parent, tr("You should select a GPX track"),
+                        tr("No selected GPX track"), JOptionPane.ERROR_MESSAGE );
+            }
+            return null;
+        }
+        return (GpxDataWrapper) item;
     }
 
@@ -1049,8 +1162,8 @@
             while(i >= 0 && (dateImgLst.get(i).time.getTime()/1000) <= curDateWp
                     && (dateImgLst.get(i).time.getTime()/1000) >= (curDateWp - interval)) {
-                if(dateImgLst.get(i).pos == null) {
-                    dateImgLst.get(i).setCoor(curWp.getCoor());
-                    dateImgLst.get(i).speed = speed;
-                    dateImgLst.get(i).elevation = curElevation;
+                if(dateImgLst.get(i).tmp.getPos() == null) {
+                    dateImgLst.get(i).tmp.setCoor(curWp.getCoor());
+                    dateImgLst.get(i).tmp.setSpeed(speed);
+                    dateImgLst.get(i).tmp.setElevation(curElevation);
                     ret++;
                 }
@@ -1065,13 +1178,13 @@
         while(i >= 0 && (imgDate = dateImgLst.get(i).time.getTime()/1000) >= prevDateWp) {
 
-            if(dateImgLst.get(i).pos == null) {
+            if(dateImgLst.get(i).tmp.getPos() == null) {
                 // The values of timeDiff are between 0 and 1, it is not seconds but a dimensionless
                 // variable
                 double timeDiff = (double)(imgDate - prevDateWp) / interval;
-                dateImgLst.get(i).setCoor(prevWp.getCoor().interpolate(curWp.getCoor(), timeDiff));
-                dateImgLst.get(i).speed = speed;
+                dateImgLst.get(i).tmp.setCoor(prevWp.getCoor().interpolate(curWp.getCoor(), timeDiff));
+                dateImgLst.get(i).tmp.setSpeed(speed);
 
                 if (curElevation != null && prevElevation != null) {
-                    dateImgLst.get(i).elevation = prevElevation + (curElevation - prevElevation) * timeDiff;
+                    dateImgLst.get(i).setElevation(prevElevation + (curElevation - prevElevation) * timeDiff);
                 }
 
@@ -1099,5 +1212,5 @@
         int endIndex= lstSize-1;
         while (endIndex - startIndex > 1) {
-            curIndex= (int) Math.round((double)(endIndex + startIndex)/2);
+            curIndex= (endIndex + startIndex) / 2;
             if (searchedDate > dateImgLst.get(curIndex).time.getTime()/1000) {
                 startIndex= curIndex;
@@ -1137,7 +1250,11 @@
     }
 
-    private Float parseTimezone(String timezone) {
+    private double parseTimezone(String timezone) throws ParseException {
+ 
+        String error = tr("Error while parsing timezone.\nExpected format: {0}", "+H:MM");
+ 
+ 
         if (timezone.length() == 0)
-            return new Float(0);
+            return 0;
 
         char sgnTimezone = '+';
@@ -1150,5 +1267,5 @@
             case ' ' :
                 if (state != 2 || hTimezone.length() != 0)
-                    return null;
+                    throw new ParseException(error,0);
                 break;
             case '+' :
@@ -1158,5 +1275,5 @@
                     state = 2;
                 } else
-                    return null;
+                    throw new ParseException(error,0);
                 break;
             case ':' :
@@ -1165,5 +1282,5 @@
                     state = 3;
                 } else
-                    return null;
+                    throw new ParseException(error,0);
                 break;
             case '0' : case '1' : case '2' : case '3' : case '4' :
@@ -1179,9 +1296,9 @@
                     break;
                 default :
-                    return null;
+                    throw new ParseException(error,0);
                 }
                 break;
             default :
-                return null;
+                throw new ParseException(error,0);
             }
         }
@@ -1196,11 +1313,28 @@
         } catch (NumberFormatException nfe) {
             // Invalid timezone
-            return null;
+            throw new ParseException(error,0);
         }
 
         if (h > 12 || m > 59 )
-            return null;
+            throw new ParseException(error,0);
         else
-            return new Float((h + m / 60.0) * (sgnTimezone == '-' ? -1 : 1));
+            return (h + m / 60.0) * (sgnTimezone == '-' ? -1 : 1);
+    }
+
+    private long parseOffset(String offset) throws ParseException {
+        String error = tr("Error while parsing offset.\nExpected format: {0}", "number");
+    
+        if (offset.length() > 0) {
+            try {
+                if(offset.startsWith("+")) {
+                    offset = offset.substring(1);
+                }
+                return Long.parseLong(offset);
+            } catch(NumberFormatException nfe) {
+                throw new ParseException(error,0);
+            }
+        } else {
+            return 0;
+        }
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 2661)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 2662)
@@ -79,45 +79,9 @@
     private int currentPhoto = -1;
 
-    // These are used by the auto-guess function to store the result,
-    // so when the dialig is re-opened the users modifications don't
-    // get overwritten
-    public boolean hasTimeoffset = false;
-    public long timeoffset = 0;
-
     boolean useThumbs = false;
     ThumbsLoader thumbsloader;
+    boolean thumbsLoaded = false;
     private BufferedImage offscreenBuffer;
     boolean updateOffscreenBuffer = true;
-
-    /*
-     * Stores info about each image
-     */
-
-    static final class ImageEntry implements Comparable<ImageEntry> {
-        File file;
-        Date time;
-        LatLon exifCoor;
-        CachedLatLon pos;
-        Image thumbnail;
-        /** Speed in kilometer per second */
-        Double speed;
-        /** Elevation (altitude) in meters */
-        Double elevation;
-
-        public void setCoor(LatLon latlon)
-        {
-            pos = new CachedLatLon(latlon);
-        }
-        public int compareTo(ImageEntry image) {
-            if (time != null && image.time != null)
-                return time.compareTo(image.time);
-            else if (time == null && image.time == null)
-                return 0;
-            else if (time == null)
-                return -1;
-            else
-                return 1;
-        }
-    }
 
     /** Loads a set of images, while displaying a dialog that indicates what the plugin is currently doing.
@@ -277,5 +241,5 @@
                     boolean noGeotagFound = true;
                     for (ImageEntry e : layer.data) {
-                        if (e.pos != null) {
+                        if (e.getPos() != null) {
                             noGeotagFound = false;
                         }
@@ -344,5 +308,5 @@
         int i = 0;
         for (ImageEntry e : data)
-            if (e.pos != null) {
+            if (e.getPos() != null) {
                 i++;
             }
@@ -397,5 +361,5 @@
     private Dimension scaledDimension(Image thumb) {
         final double d = Main.map.mapView.getDist100Pixel();
-        final double size = 40 /*meter*/;     /* size of the photo on the map */
+        final double size = 10 /*meter*/;     /* size of the photo on the map */
         double s = size * 100 /*px*/ / d;
 
@@ -441,8 +405,8 @@
 
                 for (ImageEntry e : data) {
-                    if (e.pos == null) {
+                    if (e.getPos() == null) {
                         continue;
                     }
-                    Point p = mv.getPoint(e.pos);
+                    Point p = mv.getPoint(e.getPos());
                     if (e.thumbnail != null) {
                         Dimension d = scaledDimension(e.thumbnail);
@@ -464,8 +428,8 @@
         else {
             for (ImageEntry e : data) {
-                if (e.pos == null) {
+                if (e.getPos() == null) {
                     continue;
                 }
-                Point p = mv.getPoint(e.pos);
+                Point p = mv.getPoint(e.getPos());
                 icon.paintIcon(mv, g,
                         p.x - icon.getIconWidth() / 2,
@@ -477,6 +441,6 @@
             ImageEntry e = data.get(currentPhoto);
 
-            if (e.pos != null) {
-                Point p = mv.getPoint(e.pos);
+            if (e.getPos() != null) {
+                Point p = mv.getPoint(e.getPos());
 
                 if (e.thumbnail != null) {
@@ -496,5 +460,5 @@
     public void visitBoundingBox(BoundingXYVisitor v) {
         for (ImageEntry e : data) {
-            v.visit(e.pos);
+            v.visit(e.getPos());
         }
     }
@@ -549,8 +513,9 @@
 
             e.setCoor(new LatLon(lat, lon));
-            e.exifCoor = e.pos;
+            e.exifCoor = e.getPos();
 
         } catch (Exception p) {
-            e.pos = null;
+            e.exifCoor = null;
+            e.setPos(null);
         }
     }
@@ -614,5 +579,5 @@
                     new String[] {tr("Cancel"), tr("Delete")})
                 .setButtonIcons(new String[] {"cancel.png", "dialogs/delete.png"})
-                .setContent(new JLabel(tr("<html><h3>Delete the file {0}  from the disk?<p>The image file will be permanently lost!"
+                .setContent(new JLabel(tr("<html><h3>Delete the file {0}  from disk?<p>The image file will be permanently lost!"
                     ,toDelete.file.getName()), ImageProvider.get("dialogs/geoimage/deletefromdisk"),SwingConstants.LEFT))
                 .toggleEnable("geoimage.deleteimagefromdisk")
@@ -674,8 +639,8 @@
                 for (int i = data.size() - 1; i >= 0; --i) {
                     ImageEntry e = data.get(i);
-                    if (e.pos == null) {
+                    if (e.getPos() == null) {
                         continue;
                     }
-                    Point p = Main.map.mapView.getPoint(e.pos);
+                    Point p = Main.map.mapView.getPoint(e.getPos());
                     Rectangle r;
                     if (e.thumbnail != null) {
@@ -744,3 +709,18 @@
         }
     }
+    
+    public void loadThumbs() {    
+        if (useThumbs && !thumbsLoaded) {
+            thumbsLoaded = true;
+            thumbsloader = new ThumbsLoader(this);
+            Thread t = new Thread(thumbsloader);
+            t.setPriority(Thread.MIN_PRIORITY);
+            t.start();
+        }
+    }
+    
+    public void updateBufferAndRepaint() {
+        updateOffscreenBuffer = true;
+        Main.map.mapView.repaint();
+    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 2662)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageEntry.java	(revision 2662)
@@ -0,0 +1,119 @@
+// License: GPL. See LICENSE file for details.
+// Copyright 2007 by Christian Gallioz (aka khris78)
+// Parts of code from Geotagged plugin (by Rob Neild)
+// and the core JOSM source code (by Immanuel Scholz and others)
+
+package org.openstreetmap.josm.gui.layer.geoimage;
+
+import java.io.File;
+import java.util.Date;
+import java.awt.Image;
+
+import org.openstreetmap.josm.data.coor.CachedLatLon;
+import org.openstreetmap.josm.data.coor.LatLon;
+
+/*
+ * Stores info about each image
+ */
+
+final class ImageEntry implements Comparable<ImageEntry>, Cloneable {
+    File file;
+    Date time;
+    LatLon exifCoor;
+
+    private CachedLatLon pos;
+    /** Speed in kilometer per second */
+    private Double speed;
+    /** Elevation (altitude) in meters */
+    private Double elevation;
+
+    Image thumbnail;
+    
+    ImageEntry tmp;
+
+    public CachedLatLon getPos() {
+        if (tmp != null) {
+            return tmp.pos;
+        }
+        return pos;
+    }
+    public Double getSpeed() {
+        if (tmp != null) {
+            return tmp.speed;
+        }
+        return speed;
+    }
+    public Double getElevation() {
+        if (tmp != null) {
+            return tmp.elevation;
+        }
+        return elevation;
+    }
+    public void setPos(CachedLatLon pos) {
+        this.pos = pos;
+    }
+    public void setSpeed(Double speed) {
+        this.speed = speed;
+    }
+    public void setElevation(Double speed) {
+        this.elevation = elevation;
+    }
+
+    @Override
+    public ImageEntry clone() {
+        Object c;
+        try {
+            c = super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new RuntimeException();
+        }
+        return (ImageEntry) c;
+    }
+    
+    public void setCoor(LatLon latlon)
+    {
+        pos = new CachedLatLon(latlon);
+    }
+
+    public int compareTo(ImageEntry image) {
+        if (time != null && image.time != null)
+            return time.compareTo(image.time);
+        else if (time == null && image.time == null)
+            return 0;
+        else if (time == null)
+            return -1;
+        else
+            return 1;
+    }
+    
+    public void applyTmp() {
+        if (tmp != null) {
+            pos = tmp.pos;
+            speed = tmp.speed;
+            elevation = tmp.elevation;
+            tmp = null;
+        }
+    }
+    public void cleanTmp() {
+        tmp = clone();
+        tmp.setPos(null);
+        tmp.tmp = null;
+    }
+    
+    public boolean isTagged() {
+        return pos != null;
+    }
+    
+    /**
+     * only partial info
+     */
+    public String toString() {
+        String result = file.getName()+": "+
+            "pos = "+pos+" | "+
+            "exifCoor = "+exifCoor+" | "+
+            (tmp == null ? " tmp==null" :
+                " [tmp] pos = "+tmp.pos+"");
+        return result;
+    }
+}
+
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 2661)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java	(revision 2662)
@@ -29,5 +29,4 @@
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
 import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
-import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer.ImageEntry;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -187,6 +186,6 @@
             } else if (COMMAND_CENTERVIEW.equals(action)) {
                 centerView = ((JToggleButton) e.getSource()).isSelected();
-                if (centerView && currentEntry != null && currentEntry.pos != null) {
-                    Main.map.mapView.zoomTo(currentEntry.pos);
+                if (centerView && currentEntry != null && currentEntry.getPos() != null) {
+                    Main.map.mapView.zoomTo(currentEntry.getPos());
                 }
 
@@ -231,6 +230,6 @@
 //            }                     TODO: pop up image dialog but don't load image again
 
-            if (centerView && Main.map != null && entry != null && entry.pos != null) {
-                Main.map.mapView.zoomTo(entry.pos);
+            if (centerView && Main.map != null && entry != null && entry.getPos() != null) {
+                Main.map.mapView.zoomTo(entry.getPos());
             }
 
@@ -243,9 +242,9 @@
             titleBar.setTitle("Geotagged Images" + (entry.file != null ? " - " + entry.file.getName() : ""));
             StringBuffer osd = new StringBuffer(entry.file != null ? entry.file.getName() : "");
-            if (entry.elevation != null) {
-                osd.append(tr("\nAltitude: {0} m", entry.elevation.longValue()));
-            }
-            if (entry.speed != null) {
-                osd.append(tr("\n{0} km/h", Math.round(entry.speed)));
+            if (entry.getElevation() != null) {
+                osd.append(tr("\nAltitude: {0} m", entry.getElevation().longValue()));
+            }
+            if (entry.getSpeed() != null) {
+                osd.append(tr("\n{0} km/h", Math.round(entry.getSpeed())));
             }
             imgDisplay.setOsdText(osd.toString());
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java	(revision 2661)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ThumbsLoader.java	(revision 2662)
@@ -17,5 +17,4 @@
 import org.openstreetmap.josm.io.CacheFiles;
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer.ImageEntry;
 
 public class ThumbsLoader implements Runnable {
