Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/AdjustTimezoneAndOffsetDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/AdjustTimezoneAndOffsetDialog.java	(revision 18043)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/AdjustTimezoneAndOffsetDialog.java	(revision 18043)
@@ -0,0 +1,167 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer.geoimage;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GridBagLayout;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+import javax.swing.event.ChangeListener;
+
+import org.openstreetmap.josm.data.gpx.GpxTimeOffset;
+import org.openstreetmap.josm.data.gpx.GpxTimezone;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.JosmRuntimeException;
+import org.openstreetmap.josm.tools.Logging;
+
+/**
+ * Dialog used to manually adjust timezone and offset for GPX correlation.
+ * @since 18043 (extracted from CorrelateGpxWithImages)
+ */
+public class AdjustTimezoneAndOffsetDialog extends ExtendedDialog {
+
+    private AdjustListener listener;
+
+    /**
+     * Constructs a new {@code AdjustTimezoneAndOffsetDialog}
+     * @param parent The parent element that will be used for position and maximum size
+     * @param tz initial timezone
+     * @param offset initial time offset
+     * @param dayOffset days offset
+     */
+    public AdjustTimezoneAndOffsetDialog(Component parent, GpxTimezone tz, GpxTimeOffset offset, int dayOffset) {
+        super(parent, tr("Adjust timezone and offset"), tr("Close"));
+        setContent(buildContent(tz, offset, dayOffset));
+        setButtonIcons("ok");
+    }
+
+    private Component buildContent(GpxTimezone a, GpxTimeOffset b, int dayOffset) {
+        // Info Labels
+        final JLabel lblMatches = new JLabel();
+
+        // Timezone Slider
+        // The slider allows to switch timezone 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);
+        Dictionary<Integer, JLabel> labelTable = new Hashtable<>();
+        // CHECKSTYLE.OFF: ParenPad
+        for (int i = -12; i <= 12; i += 6) {
+            labelTable.put(i * 2, new JLabel(new GpxTimezone(i).formatTimezone()));
+        }
+        // CHECKSTYLE.ON: ParenPad
+        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(-600, 600, 0);
+        sldSeconds.setPaintLabels(true);
+        labelTable = new Hashtable<>();
+        // CHECKSTYLE.OFF: ParenPad
+        for (int i = -60; i <= 60; i += 30) {
+            labelTable.put(i * 10, new JLabel(GpxTimeOffset.seconds(i).formatOffset()));
+        }
+        // CHECKSTYLE.ON: ParenPad
+        sldSeconds.setLabelTable(labelTable);
+        sldSeconds.setMajorTickSpacing(300);
+
+        // 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) (a.getHours() * 2));
+            sldMinutes.setValue((int) (b.getSeconds() / 60));
+            final long deciSeconds = b.getMilliseconds() / 100;
+            sldSeconds.setValue((int) (deciSeconds % 600));
+        } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException ex) {
+            Logging.warn(ex);
+            JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
+                    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);
+        }
+
+        // This is called whenever one of the sliders is moved.
+        // It updates the labels
+        ChangeListener sliderListener = x -> {
+            final GpxTimezone timezone = new GpxTimezone(sldTimezone.getValue() / 2.);
+            final int minutes = sldMinutes.getValue();
+            final int seconds = sldSeconds.getValue();
+
+            lblTimezone.setText(tr("Timezone: {0}", timezone.formatTimezone()));
+            lblMinutes.setText(tr("Minutes: {0}", minutes));
+            lblSeconds.setText(tr("Seconds: {0}", GpxTimeOffset.milliseconds(100L * seconds).formatOffset()));
+
+            StringBuilder sb = new StringBuilder("<html>");
+            if (listener != null) {
+                sb.append(listener.valuesChanged(timezone, minutes, seconds)).append("<br>");
+            }
+
+            lblMatches.setText(sb.append(trn("(Time difference of {0} day)", "Time difference of {0} days",
+                    Math.abs(dayOffset), Math.abs(dayOffset))).append("</html>").toString());
+        };
+
+        // Call the sliderListener once manually so labels get adjusted
+        sliderListener.stateChanged(null);
+
+        // Listeners added here, otherwise it tries to match three times
+        // (when setting the default values)
+        sldTimezone.addChangeListener(sliderListener);
+        sldMinutes.addChangeListener(sliderListener);
+        sldSeconds.addChangeListener(sliderListener);
+
+        return p;
+    }
+
+    /**
+     * Listener called when the sliders are moved.
+     */
+    public interface AdjustListener {
+        /**
+         * Provides a textual description matching the new state after the change of values.
+         * @param timezone new timezone
+         * @param minutes new minutes offset
+         * @param seconds new seconds offset
+         * @return an HTML textual description matching the new state after the change of values
+         */
+        String valuesChanged(GpxTimezone timezone, int minutes, int seconds);
+    }
+
+    /**
+     * Sets the {@link AdjustListener}.
+     * @param listener adjuust listener. Can be null
+     * @return {@code this}
+     */
+    public final AdjustTimezoneAndOffsetDialog adjustListener(AdjustListener listener) {
+        this.listener = listener;
+        return this;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 18042)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 18043)
@@ -33,6 +33,4 @@
 import java.util.Comparator;
 import java.util.Date;
-import java.util.Dictionary;
-import java.util.Hashtable;
 import java.util.List;
 import java.util.Objects;
@@ -56,5 +54,4 @@
 import javax.swing.JScrollPane;
 import javax.swing.JSeparator;
-import javax.swing.JSlider;
 import javax.swing.JSpinner;
 import javax.swing.ListSelectionModel;
@@ -63,6 +60,4 @@
 import javax.swing.SwingConstants;
 import javax.swing.border.Border;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
 import javax.swing.event.DocumentListener;
@@ -85,4 +80,5 @@
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.layer.geoimage.AdjustTimezoneAndOffsetDialog.AdjustListener;
 import org.openstreetmap.josm.gui.layer.gpx.GpxDataHelper;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
@@ -94,5 +90,4 @@
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Pair;
@@ -778,5 +773,5 @@
 
     private static class GpxLayerRenamedListener implements PropertyChangeListener {
-        private GpxDataWrapper gdw;
+        private final GpxDataWrapper gdw;
         GpxLayerRenamedListener(GpxDataWrapper gdw) {
             this.gdw = gdw;
@@ -972,4 +967,5 @@
         statusBar.add(statusBarText);
 
+        RepaintTheMapListener repaintTheMap = new RepaintTheMapListener();
         tfTimezone.addFocusListener(repaintTheMap);
         tfOffset.addFocusListener(repaintTheMap);
@@ -1030,15 +1026,15 @@
 
         @Override
-        public void insertUpdate(DocumentEvent ev) {
+        public void insertUpdate(DocumentEvent e) {
             matchAndUpdateStatusBar();
         }
 
         @Override
-        public void removeUpdate(DocumentEvent ev) {
+        public void removeUpdate(DocumentEvent e) {
             matchAndUpdateStatusBar();
         }
 
         @Override
-        public void changedUpdate(DocumentEvent ev) {
+        public void changedUpdate(DocumentEvent e) {
             // Do nothing
         }
@@ -1098,6 +1094,4 @@
     }
 
-    private final transient RepaintTheMapListener repaintTheMap = new RepaintTheMapListener();
-
     private class RepaintTheMapListener implements FocusListener {
         @Override
@@ -1117,5 +1111,5 @@
 
         @Override
-        public void actionPerformed(ActionEvent arg0) {
+        public void actionPerformed(ActionEvent e) {
 
             final GpxTimeOffset offset = GpxTimeOffset.milliseconds(
@@ -1124,114 +1118,34 @@
             final Pair<GpxTimezone, GpxTimeOffset> timezoneOffsetPair = offset.withoutDayOffset().splitOutTimezone();
 
-            // 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);
-            Dictionary<Integer, JLabel> labelTable = new Hashtable<>();
-            // CHECKSTYLE.OFF: ParenPad
-            for (int i = -12; i <= 12; i += 6) {
-                labelTable.put(i * 2, new JLabel(new GpxTimezone(i).formatTimezone()));
-            }
-            // CHECKSTYLE.ON: ParenPad
-            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(-600, 600, 0);
-            sldSeconds.setPaintLabels(true);
-            labelTable = new Hashtable<>();
-            // CHECKSTYLE.OFF: ParenPad
-            for (int i = -60; i <= 60; i += 30) {
-                labelTable.put(i * 10, new JLabel(GpxTimeOffset.seconds(i).formatOffset()));
-            }
-            // CHECKSTYLE.ON: ParenPad
-            sldSeconds.setLabelTable(labelTable);
-            sldSeconds.setMajorTickSpacing(300);
-
             // 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 {
-                @Override
-                public void stateChanged(ChangeEvent e) {
-                    timezone = new GpxTimezone(sldTimezone.getValue() / 2.);
-
-                    lblTimezone.setText(tr("Timezone: {0}", timezone.formatTimezone()));
-                    lblMinutes.setText(tr("Minutes: {0}", sldMinutes.getValue()));
-                    lblSeconds.setText(tr("Seconds: {0}", GpxTimeOffset.milliseconds(100L * sldSeconds.getValue()).formatOffset()));
-
-                    delta = GpxTimeOffset.milliseconds(100L * sldSeconds.getValue()
-                            + TimeUnit.MINUTES.toMillis(sldMinutes.getValue())
-                            + TimeUnit.DAYS.toMillis(dayOffset));
-
-                    tfTimezone.getDocument().removeDocumentListener(statusBarUpdater);
-                    tfOffset.getDocument().removeDocumentListener(statusBarUpdater);
-
-                    tfTimezone.setText(timezone.formatTimezone());
-                    tfOffset.setText(delta.formatOffset());
-
-                    tfTimezone.getDocument().addDocumentListener(statusBarUpdater);
-                    tfOffset.getDocument().addDocumentListener(statusBarUpdater);
-
-                    lblMatches.setText(statusBarText.getText() + "<br>" + trn("(Time difference of {0} day)",
-                            "Time difference of {0} days", Math.abs(dayOffset), Math.abs(dayOffset)));
-
-                    statusBarUpdater.matchAndUpdateStatusBar();
-                    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) (timezoneOffsetPair.a.getHours() * 2));
-                sldMinutes.setValue((int) (timezoneOffsetPair.b.getSeconds() / 60));
-                final long deciSeconds = timezoneOffsetPair.b.getMilliseconds() / 100;
-                sldSeconds.setValue((int) (deciSeconds % 600));
-            } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
-                Logging.warn(e);
-                JOptionPane.showMessageDialog(MainApplication.getMainFrame(),
-                        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());
+            // It calls the "match photos" code
+            AdjustListener listener = (tz, min, sec) -> {
+                timezone = tz;
+
+                delta = GpxTimeOffset.milliseconds(100L * sec
+                        + TimeUnit.MINUTES.toMillis(min)
+                        + TimeUnit.DAYS.toMillis(dayOffset));
+
+                tfTimezone.getDocument().removeDocumentListener(statusBarUpdater);
+                tfOffset.getDocument().removeDocumentListener(statusBarUpdater);
+
+                tfTimezone.setText(timezone.formatTimezone());
+                tfOffset.setText(delta.formatOffset());
+
+                tfTimezone.getDocument().addDocumentListener(statusBarUpdater);
+                tfOffset.getDocument().addDocumentListener(statusBarUpdater);
+
+                statusBarUpdater.matchAndUpdateStatusBar();
+                yLayer.updateBufferAndRepaint();
+
+                return statusBarText.getText();
+            };
 
             // 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(MainApplication.getMainFrame(),
-                    tr("Adjust timezone and offset"),
-                    tr("Close")).
-                    setContent(p).setButtonIcons("ok").showDialog();
+            new AdjustTimezoneAndOffsetDialog(MainApplication.getMainFrame(),
+                    timezoneOffsetPair.a, timezoneOffsetPair.b, dayOffset)
+            .adjustListener(listener).showDialog();
         }
     }
@@ -1284,5 +1198,5 @@
 
         @Override
-        public void actionPerformed(ActionEvent arg0) {
+        public void actionPerformed(ActionEvent e) {
             GpxDataWrapper gpxW = selectedGPX(true);
             if (gpxW == null)
