Index: /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/EGpxLayer.java
===================================================================
--- /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/EGpxLayer.java	(revision 27759)
+++ /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/EGpxLayer.java	(revision 27760)
@@ -3,114 +3,19 @@
 package gpxfilter;
 
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
-import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.trn;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.GridBagLayout;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.text.DateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.Future;
-
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.Box;
-import javax.swing.ButtonGroup;
-import javax.swing.Icon;
-import javax.swing.JColorChooser;
-import javax.swing.JFileChooser;
-import javax.swing.JLabel;
-import javax.swing.JList;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JScrollPane;
-import javax.swing.SwingUtilities;
-import javax.swing.filechooser.FileFilter;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.RenameLayerAction;
-import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTaskList;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.coor.EastNorth;
-import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.gpx.GpxTrack;
-import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
-import org.openstreetmap.josm.data.gpx.WayPoint;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
-import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
-import org.openstreetmap.josm.gui.HelpAwareOptionPane;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.NavigatableComponent;
-import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
-import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.layer.markerlayer.AudioMarker;
-import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
-import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
-import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
-import org.openstreetmap.josm.gui.widgets.HtmlPanel;
-import org.openstreetmap.josm.io.JpgImporter;
-import org.openstreetmap.josm.tools.AudioUtil;
-import org.openstreetmap.josm.tools.DateUtils;
-import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.ImageProvider;
-import org.openstreetmap.josm.tools.UrlLabel;
+import org.openstreetmap.josm.gui.layer.GpxLayer;
 
-public class EGpxLayer extends Layer {
-
-    private static final String PREF_DOWNLOAD_ALONG_TRACK_DISTANCE = "gpxLayer.downloadAlongTrack.distance";
-    private static final String PREF_DOWNLOAD_ALONG_TRACK_AREA = "gpxLayer.downloadAlongTrack.area";
-    private static final String PREF_DOWNLOAD_ALONG_TRACK_NEAR = "gpxLayer.downloadAlongTrack.near";
-
-    public final GpxData data = new GpxData();
-    protected static final double PHI = Math.toRadians(15);
-    private boolean computeCacheInSync;
-    private int computeCacheMaxLineLengthUsed;
-    private Color computeCacheColorUsed;
-    private colorModes computeCacheColored;
-    private int computeCacheColorTracksTune;
-    private boolean isLocalFile;
-
-    private final List<GpxTrack> lastTracks = new ArrayList<GpxTrack>(); // List of tracks at last paint
-    private int lastUpdateCount;
-    private final Bounds bounds;
-    private volatile boolean painting;
-
-    private static class Markers {
-        public boolean timedMarkersOmitted = false;
-        public boolean untimedMarkersOmitted = false;
-    }
-
-    public EGpxLayer(Bounds b) {
-        super("GPX Data");
-        this.bounds = b;
+public class EGpxLayer extends GpxLayer {
+    public EGpxLayer(final Bounds b) {
+        super(new GpxData(), tr("GPX Data"));
         Thread t = new Thread(new Runnable()
         {
             @Override
             public void run() {
-                GpxGrabber grabber = new GpxGrabber(EGpxLayer.this.bounds);
+                GpxGrabber grabber = new GpxGrabber(b);
                 while (true) {
                     GpxData newData;
@@ -126,7 +31,5 @@
                     }
 
-                    if (!EGpxLayer.this.painting) {
-                        Main.map.repaint();
-                    }
+                    Main.map.repaint();
                 }
                 grabber.cancel();
@@ -136,1369 +39,3 @@
         t.start();
     }
-
-    @Override
-    public Icon getIcon() {
-        return ImageProvider.get("layer", "gpx_small");
-    }
-
-    @Override
-    public synchronized Object getInfoComponent() {
-        StringBuilder info = new StringBuilder();
-
-        if (data.attr.containsKey("name")) {
-            info.append(tr("Name: {0}", data.attr.get(GpxData.META_NAME))).append("<br>");
-        }
-
-        if (data.attr.containsKey("desc")) {
-            info.append(tr("Description: {0}", data.attr.get(GpxData.META_DESC))).append("<br>");
-        }
-
-        if (data.tracks.size() > 0) {
-            info.append("<table><thead align='center'><tr><td colspan='5'>"
-                    + trn("{0} track", "{0} tracks", data.tracks.size(), data.tracks.size())
-                    + "</td></tr><tr align='center'><td>" + tr("Name") + "</td><td>"
-                    + tr("Description") + "</td><td>" + tr("Timespan")
-                    + "</td><td>" + tr("Length") + "</td><td>" + tr("URL")
-                    + "</td></tr></thead>");
-
-            for (GpxTrack trk : data.tracks) {
-                WayPoint earliest = null, latest = null;
-
-                info.append("<tr><td>");
-                if (trk.getAttributes().containsKey("name")) {
-                    info.append(trk.getAttributes().get("name"));
-                }
-                info.append("</td><td>");
-                if (trk.getAttributes().containsKey("desc")) {
-                    info.append(" ").append(trk.getAttributes().get("desc"));
-                }
-                info.append("</td><td>");
-
-                for (GpxTrackSegment seg : trk.getSegments()) {
-                    for (WayPoint pnt : seg.getWayPoints()) {
-                        if (latest == null) {
-                            latest = earliest = pnt;
-                        } else {
-                            if (pnt.compareTo(earliest) < 0) {
-                                earliest = pnt;
-                            } else {
-                                latest = pnt;
-                            }
-                        }
-                    }
-                }
-
-                if (earliest != null && latest != null) {
-                    DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
-                    String earliestDate = df.format(earliest.getTime());
-                    String latestDate = df.format(latest.getTime());
-
-                    if (earliestDate.equals(latestDate)) {
-                        DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT);
-                        info.append(earliestDate).append(" ");
-                        info.append(tf.format(earliest.getTime())).append(" - ").append(tf.format(latest.getTime()));
-                    } else {
-                        DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
-                        info.append(dtf.format(earliest.getTime())).append(" - ").append(dtf.format(latest.getTime()));
-                    }
-
-                    int diff = (int) (latest.time - earliest.time);
-                    info.append(String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60));
-                }
-
-                info.append("</td><td>");
-                info.append(NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length()));
-                info.append("</td><td>");
-                if (trk.getAttributes().containsKey("url")) {
-                    info.append(trk.getAttributes().get("url"));
-                }
-                info.append("</td></tr>");
-            }
-
-            info.append("</table><br><br>");
-
-        }
-
-        info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length()))).append("<br>");
-
-        info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size())).append(
-                trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");
-
-        final JScrollPane sp = new JScrollPane(new HtmlPanel(info.toString()), JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
-        sp.setPreferredSize(new Dimension(sp.getPreferredSize().width, 350));
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                sp.getVerticalScrollBar().setValue(0);
-            }
-        });
-        return sp;
-    }
-
-    static public Color getColor(String name) {
-        return Main.pref.getColor(marktr("gps point"), name != null ? "layer " + name : null, Color.gray);
-    }
-
-    @Override
-    public Action[] getMenuEntries() {
-        if (Main.applet)
-            return new Action[] {
-                LayerListDialog.getInstance().createShowHideLayerAction(),
-                LayerListDialog.getInstance().createDeleteLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new CustomizeColor(),
-                new CustomizeLineDrawing(),
-                new ConvertToDataLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new RenameLayerAction(getAssociatedFile(), this),
-                SeparatorLayerAction.INSTANCE,
-                new LayerListPopup.InfoAction(this) };
-        return new Action[] {
-                LayerListDialog.getInstance().createShowHideLayerAction(),
-                LayerListDialog.getInstance().createDeleteLayerAction(),
-                SeparatorLayerAction.INSTANCE,
-                new LayerSaveAction(this),
-                new LayerSaveAsAction(this),
-                new CustomizeColor(),
-                new CustomizeLineDrawing(),
-                new ImportImages(),
-                new ImportAudio(),
-                new MarkersFromNamedPoins(),
-                new ConvertToDataLayerAction(),
-                new DownloadAlongTrackAction(),
-                SeparatorLayerAction.INSTANCE,
-                new RenameLayerAction(getAssociatedFile(), this),
-                SeparatorLayerAction.INSTANCE,
-                new LayerListPopup.InfoAction(this) };
-    }
-
-    @Override
-    public synchronized String getToolTipText() {
-        StringBuilder info = new StringBuilder().append("<html>");
-
-        if (data.attr.containsKey("name")) {
-            info.append(tr("Name: {0}", data.attr.get(GpxData.META_NAME))).append("<br>");
-        }
-
-        if (data.attr.containsKey("desc")) {
-            info.append(tr("Description: {0}", data.attr.get(GpxData.META_DESC))).append("<br>");
-        }
-
-        info.append(trn("{0} track, ", "{0} tracks, ", data.tracks.size(), data.tracks.size()));
-        info.append(trn("{0} route, ", "{0} routes, ", data.routes.size(), data.routes.size()));
-        info.append(trn("{0} waypoint", "{0} waypoints", data.waypoints.size(), data.waypoints.size())).append("<br>");
-
-        info.append(tr("Length: {0}", NavigatableComponent.getSystemOfMeasurement().getDistText(data.length())));
-        info.append("<br>");
-
-        return info.append("</html>").toString();
-    }
-
-    @Override
-    public boolean isMergable(Layer other) {
-        return other instanceof EGpxLayer;
-    }
-
-    private synchronized int sumUpdateCount() {
-        int updateCount = 0;
-        for (GpxTrack track: data.tracks) {
-            updateCount += track.getUpdateCount();
-        }
-        return updateCount;
-    }
-
-    @Override
-    public synchronized boolean isChanged() {
-        if (data.tracks.equals(lastTracks))
-            return sumUpdateCount() != lastUpdateCount;
-        else
-            return true;
-    }
-
-    @Override
-    public synchronized void mergeFrom(Layer from) {
-        data.mergeFrom(((EGpxLayer) from).data);
-        computeCacheInSync = false;
-    }
-
-    private static Color[] colors = new Color[256];
-    static {
-        for (int i = 0; i < colors.length; i++) {
-            colors[i] = Color.getHSBColor(i / 300.0f, 1, 1);
-        }
-    }
-
-    // lookup array to draw arrows without doing any math
-    private static int ll0 = 9;
-    private static int sl4 = 5;
-    private static int sl9 = 3;
-    private static int[][] dir = { { +sl4, +ll0, +ll0, +sl4 }, { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 },
-        { -ll0, -sl9, -ll0, +sl9 }, { -sl4, -ll0, -ll0, -sl4 }, { +sl9, -ll0, -sl9, -ll0 },
-        { +ll0, -sl4, +sl4, -ll0 }, { +ll0, +sl9, +ll0, -sl9 }, { +sl4, +ll0, +ll0, +sl4 },
-        { -sl9, +ll0, +sl9, +ll0 }, { -ll0, +sl4, -sl4, +ll0 }, { -ll0, -sl9, -ll0, +sl9 } };
-
-    // the different color modes
-    enum colorModes {
-        none, velocity, dilution
-    }
-
-    @Override
-    public synchronized void paint(Graphics2D g, MapView mv, Bounds box) {
-        painting = true;
-        lastUpdateCount = sumUpdateCount();
-        lastTracks.clear();
-        lastTracks.addAll(data.tracks);
-
-        /****************************************************************
-         ********** STEP 1 - GET CONFIG VALUES **************************
-         ****************************************************************/
-        // Long startTime = System.currentTimeMillis();
-        Color neutralColor = getColor(getName());
-        // also draw lines between points belonging to different segments
-        boolean forceLines = Main.pref.getBoolean("draw.rawgps.lines.force");
-        // draw direction arrows on the lines
-        boolean direction = Main.pref.getBoolean("draw.rawgps.direction");
-        // don't draw lines if longer than x meters
-        int lineWidth = Main.pref.getInteger("draw.rawgps.linewidth",0);
-
-        int maxLineLength;
-        if (this.isLocalFile) {
-            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length.local", -1);
-        } else {
-            maxLineLength = Main.pref.getInteger("draw.rawgps.max-line-length", 200);
-        }
-        // draw line between points, global setting
-        boolean lines = (Main.pref.getBoolean("draw.rawgps.lines", true) || (Main.pref
-                .getBoolean("draw.rawgps.lines.localfiles") && this.isLocalFile));
-        String linesKey = "draw.rawgps.lines.layer " + getName();
-        // draw lines, per-layer setting
-        if (Main.pref.hasKey(linesKey)) {
-            lines = Main.pref.getBoolean(linesKey);
-        }
-        // paint large dots for points
-        boolean large = Main.pref.getBoolean("draw.rawgps.large");
-        boolean hdopcircle = Main.pref.getBoolean("draw.rawgps.hdopcircle", true);
-        // color the lines
-        colorModes colored = colorModes.none;
-        try {
-            colored = colorModes.values()[Main.pref.getInteger("draw.rawgps.colors", 0)];
-        } catch (Exception e) {
-        }
-        // paint direction arrow with alternate math. may be faster
-        boolean alternatedirection = Main.pref.getBoolean("draw.rawgps.alternatedirection");
-        // don't draw arrows nearer to each other than this
-        int delta = Main.pref.getInteger("draw.rawgps.min-arrow-distance", 0);
-        // allows to tweak line coloring for different speed levels.
-        int colorTracksTune = Main.pref.getInteger("draw.rawgps.colorTracksTune", 45);
-
-        if(lineWidth != 0)
-        {
-            g.setStroke(new BasicStroke(lineWidth,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND));
-        }
-
-        /****************************************************************
-         ********** STEP 2a - CHECK CACHE VALIDITY **********************
-         ****************************************************************/
-        if ((computeCacheMaxLineLengthUsed != maxLineLength) || (!neutralColor.equals(computeCacheColorUsed))
-                || (computeCacheColored != colored) || (computeCacheColorTracksTune != colorTracksTune)) {
-            // System.out.println("(re-)computing gpx line styles, reason: CCIS=" +
-            // computeCacheInSync + " CCMLLU=" + (computeCacheMaxLineLengthUsed != maxLineLength) +
-            // " CCCU=" + (!neutralColor.equals(computeCacheColorUsed)) + " CCC=" +
-            // (computeCacheColored != colored));
-            computeCacheMaxLineLengthUsed = maxLineLength;
-            computeCacheInSync = false;
-            computeCacheColorUsed = neutralColor;
-            computeCacheColored = colored;
-            computeCacheColorTracksTune = colorTracksTune;
-        }
-
-        /****************************************************************
-         ********** STEP 2b - RE-COMPUTE CACHE DATA *********************
-         ****************************************************************/
-        if (!computeCacheInSync) { // don't compute if the cache is good
-            WayPoint oldWp = null;
-            for (GpxTrack trk : data.tracks) {
-                for (GpxTrackSegment segment : trk.getSegments()) {
-                    if (!forceLines) { // don't draw lines between segments, unless forced to
-                        oldWp = null;
-                    }
-                    for (WayPoint trkPnt : segment.getWayPoints()) {
-                        LatLon c = trkPnt.getCoor();
-                        if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                            continue;
-                        }
-                        trkPnt.customColoring = neutralColor;
-                        if (oldWp != null) {
-                            double dist = c.greatCircleDistance(oldWp.getCoor());
-
-                            switch (colored) {
-                            case velocity:
-                                double dtime = trkPnt.time - oldWp.time;
-                                double vel = dist / dtime;
-                                double velColor = vel / colorTracksTune * 255;
-                                // Bad case first
-                                if (dtime <= 0 || vel < 0 || velColor > 255) {
-                                    trkPnt.customColoring = colors[255];
-                                } else {
-                                    trkPnt.customColoring = colors[(int) (velColor)];
-                                }
-                                break;
-
-                            case dilution:
-                                if (trkPnt.attr.get("hdop") != null) {
-                                    float hdop = ((Float) trkPnt.attr.get("hdop")).floatValue();
-                                    if (hdop < 0) {
-                                        hdop = 0;
-                                    }
-                                    int hdoplvl = Math.round(hdop * Main.pref.getInteger("hdop.factor", 25));
-                                    // High hdop is bad, but high values in colors are green.
-                                    // Therefore inverse the logic
-                                    int hdopcolor = 255 - (hdoplvl > 255 ? 255 : hdoplvl);
-                                    trkPnt.customColoring = colors[hdopcolor];
-                                }
-                                break;
-                            }
-
-                            if (maxLineLength == -1 || dist <= maxLineLength) {
-                                trkPnt.drawLine = true;
-                                trkPnt.dir = (int) oldWp.getCoor().heading(trkPnt.getCoor());
-                            } else {
-                                trkPnt.drawLine = false;
-                            }
-                        } else { // make sure we reset outdated data
-                            trkPnt.drawLine = false;
-                        }
-                        oldWp = trkPnt;
-                    }
-                }
-            }
-            computeCacheInSync = true;
-        }
-
-        List<Collection<WayPoint>> visibleSegments = new ArrayList<Collection<WayPoint>>();
-        for (GpxTrack trk: data.tracks) {
-            for (GpxTrackSegment trkSeg: trk.getSegments()) {
-                if (trkSeg.getBounds() != null && trkSeg.getBounds().intersects(box)) {
-                    visibleSegments.add(trkSeg.getWayPoints());
-                }
-            }
-        }
-
-        /****************************************************************
-         ********** STEP 3a - DRAW LINES ********************************
-         ****************************************************************/
-        if (lines) {
-            Point old = null;
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    if (trkPnt.drawLine) {
-                        // skip points that are on the same screenposition
-                        if (old != null && ((old.x != screen.x) || (old.y != screen.y))) {
-                            g.setColor(trkPnt.customColoring);
-                            g.drawLine(old.x, old.y, screen.x, screen.y);
-                        }
-                    }
-                    old = screen;
-                } // end for trkpnt
-            } // end for segment
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3b - DRAW NICE ARROWS **************************
-         ****************************************************************/
-        if (lines && direction && !alternatedirection) {
-            Point old = null;
-            Point oldA = null; // last arrow painted
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    if (trkPnt.drawLine) {
-                        Point screen = mv.getPoint(trkPnt.getEastNorth());
-                        // skip points that are on the same screenposition
-                        if (old != null
-                                && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
-                                        || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
-                            g.setColor(trkPnt.customColoring);
-                            double t = Math.atan2(screen.y - old.y, screen.x - old.x) + Math.PI;
-                            g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t - PHI)),
-                                    (int) (screen.y + 10 * Math.sin(t - PHI)));
-                            g.drawLine(screen.x, screen.y, (int) (screen.x + 10 * Math.cos(t + PHI)),
-                                    (int) (screen.y + 10 * Math.sin(t + PHI)));
-                            oldA = screen;
-                        }
-                        old = screen;
-                    }
-                } // end for trkpnt
-            } // end for segment
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3c - DRAW FAST ARROWS **************************
-         ****************************************************************/
-        if (lines && direction && alternatedirection) {
-            Point old = null;
-            Point oldA = null; // last arrow painted
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    if (trkPnt.drawLine) {
-                        Point screen = mv.getPoint(trkPnt.getEastNorth());
-                        // skip points that are on the same screenposition
-                        if (old != null
-                                && (oldA == null || screen.x < oldA.x - delta || screen.x > oldA.x + delta
-                                        || screen.y < oldA.y - delta || screen.y > oldA.y + delta)) {
-                            g.setColor(trkPnt.customColoring);
-                            g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][0], screen.y
-                                    + dir[trkPnt.dir][1]);
-                            g.drawLine(screen.x, screen.y, screen.x + dir[trkPnt.dir][2], screen.y
-                                    + dir[trkPnt.dir][3]);
-                            oldA = screen;
-                        }
-                        old = screen;
-                    }
-                } // end for trkpnt
-            } // end for segment
-        } // end if lines
-
-        /****************************************************************
-         ********** STEP 3d - DRAW LARGE POINTS AND HDOP CIRCLE *********
-         ****************************************************************/
-        if (large || hdopcircle) {
-            g.setColor(neutralColor);
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    g.setColor(trkPnt.customColoring);
-                    if (hdopcircle && trkPnt.attr.get("hdop") != null) {
-                        // hdop value
-                        float hdop = ((Float)trkPnt.attr.get("hdop")).floatValue();
-                        if (hdop < 0) {
-                            hdop = 0;
-                        }
-                        // hdop pixels
-                        int hdopp = mv.getPoint(new LatLon(trkPnt.getCoor().lat(), trkPnt.getCoor().lon() + 2*6*hdop*360/40000000)).x - screen.x;
-                        g.drawArc(screen.x-hdopp/2, screen.y-hdopp/2, hdopp, hdopp, 0, 360);
-                    }
-                    if (large) {
-                        g.fillRect(screen.x-1, screen.y-1, 3, 3);
-                    }
-                } // end for trkpnt
-            } // end for segment
-        } // end if large || hdopcircle
-
-        /****************************************************************
-         ********** STEP 3e - DRAW SMALL POINTS FOR LINES ***************
-         ****************************************************************/
-        if (!large && lines) {
-            g.setColor(neutralColor);
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    if (!trkPnt.drawLine) {
-                        Point screen = mv.getPoint(trkPnt.getEastNorth());
-                        g.drawRect(screen.x, screen.y, 0, 0);
-                    }
-                } // end for trkpnt
-            } // end for segment
-        } // end if large
-
-        /****************************************************************
-         ********** STEP 3f - DRAW SMALL POINTS INSTEAD OF LINES ********
-         ****************************************************************/
-        if (!large && !lines) {
-            g.setColor(neutralColor);
-            for (Collection<WayPoint> segment : visibleSegments) {
-                for (WayPoint trkPnt : segment) {
-                    LatLon c = trkPnt.getCoor();
-                    if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
-                        continue;
-                    }
-                    Point screen = mv.getPoint(trkPnt.getEastNorth());
-                    g.setColor(trkPnt.customColoring);
-                    g.drawRect(screen.x, screen.y, 0, 0);
-                } // end for trkpnt
-            } // end for segment
-        } // end if large
-
-        // Long duration = System.currentTimeMillis() - startTime;
-        // System.out.println(duration);
-        painting = false;
-    } // end paint
-
-    @Override
-    public synchronized void visitBoundingBox(BoundingXYVisitor v) {
-        v.visit(data.recalculateBounds());
-    }
-
-    public class ConvertToDataLayerAction extends AbstractAction {
-        public ConvertToDataLayerAction() {
-            super(tr("Convert to data layer"), ImageProvider.get("converttoosm"));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            JPanel msg = new JPanel(new GridBagLayout());
-            msg
-            .add(
-                    new JLabel(
-                            tr("<html>Upload of unprocessed GPS data as map data is considered harmful.<br>If you want to upload traces, look here:")),
-                            GBC.eol());
-            msg.add(new UrlLabel(tr("http://www.openstreetmap.org/traces")), GBC.eop());
-            if (!ConditionalOptionPaneUtil.showConfirmationDialog("convert_to_data", Main.parent, msg, tr("Warning"),
-                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, JOptionPane.OK_OPTION))
-                return;
-            DataSet ds = new DataSet();
-            for (GpxTrack trk : data.tracks) {
-                for (GpxTrackSegment segment : trk.getSegments()) {
-                    List<Node> nodes = new ArrayList<Node>();
-                    for (WayPoint p : segment.getWayPoints()) {
-                        Node n = new Node(p.getCoor());
-                        String timestr = p.getString("time");
-                        if (timestr != null) {
-                            n.setTimestamp(DateUtils.fromString(timestr));
-                        }
-                        ds.addPrimitive(n);
-                        nodes.add(n);
-                    }
-                    Way w = new Way();
-                    w.setNodes(nodes);
-                    ds.addPrimitive(w);
-                }
-            }
-            Main.main
-            .addLayer(new OsmDataLayer(ds, tr("Converted from: {0}", EGpxLayer.this.getName()), getAssociatedFile()));
-            Main.main.removeLayer(EGpxLayer.this);
-        }
-    }
-
-    @Override
-    public File getAssociatedFile() {
-        return data.storageFile;
-    }
-
-    @Override
-    public void setAssociatedFile(File file) {
-        data.storageFile = file;
-    }
-
-    /**
-     * Action that issues a series of download requests to the API, following the GPX track.
-     *
-     * @author fred
-     */
-    public class DownloadAlongTrackAction extends AbstractAction {
-        public DownloadAlongTrackAction() {
-            super(tr("Download from OSM along this track"), ImageProvider.get("downloadalongtrack"));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            JPanel msg = new JPanel(new GridBagLayout());
-            Integer dist[] = { 5000, 500, 50 };
-            Integer area[] = { 20, 10, 5, 1 };
-
-            msg.add(new JLabel(tr("Download everything within:")), GBC.eol());
-            String s[] = new String[dist.length];
-            for (int i = 0; i < dist.length; ++i) {
-                s[i] = tr("{0} meters", dist[i]);
-            }
-            JList buffer = new JList(s);
-            buffer.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, 0));
-            msg.add(buffer, GBC.eol());
-
-            msg.add(new JLabel(tr("Maximum area per request:")), GBC.eol());
-            s = new String[area.length];
-            for (int i = 0; i < area.length; ++i) {
-                s[i] = tr("{0} sq km", area[i]);
-            }
-            JList maxRect = new JList(s);
-            maxRect.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, 0));
-            msg.add(maxRect, GBC.eol());
-
-            msg.add(new JLabel(tr("Download near:")), GBC.eol());
-            JList downloadNear = new JList(new String[] { tr("track only"), tr("waypoints only"), tr("track and waypoints") });
-            int NEAR_TRACK=0;
-            int NEAR_WAYPOINTS=1;
-            int NEAR_BOTH=2;
-
-            downloadNear.setSelectedIndex(Main.pref.getInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, 0));
-            msg.add(downloadNear, GBC.eol());
-
-            int ret = JOptionPane.showConfirmDialog(
-                    Main.parent,
-                    msg,
-                    tr("Download from OSM along this track"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.QUESTION_MESSAGE
-            );
-            switch(ret) {
-            case JOptionPane.CANCEL_OPTION:
-            case JOptionPane.CLOSED_OPTION:
-                return;
-            default:
-                // continue
-            }
-
-            Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_DISTANCE, buffer.getSelectedIndex());
-            Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_AREA, maxRect.getSelectedIndex());
-            int near = downloadNear.getSelectedIndex();
-            Main.pref.putInteger(PREF_DOWNLOAD_ALONG_TRACK_NEAR, near);
-
-            /*
-             * Find the average latitude for the data we're contemplating, so we can know how many
-             * metres per degree of longitude we have.
-             */
-            double latsum = 0;
-            int latcnt = 0;
-
-            if (near == NEAR_TRACK || near == NEAR_BOTH) {
-                for (GpxTrack trk : data.tracks) {
-                    for (GpxTrackSegment segment : trk.getSegments()) {
-                        for (WayPoint p : segment.getWayPoints()) {
-                            latsum += p.getCoor().lat();
-                            latcnt++;
-                        }
-                    }
-                }
-            }
-
-            if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {
-                for (WayPoint p : data.waypoints) {
-                    latsum += p.getCoor().lat();
-                    latcnt++;
-                }
-            }
-
-            double avglat = latsum / latcnt;
-            double scale = Math.cos(Math.toRadians(avglat));
-
-            /*
-             * Compute buffer zone extents and maximum bounding box size. Note that the maximum we
-             * ever offer is a bbox area of 0.002, while the API theoretically supports 0.25, but as
-             * soon as you touch any built-up area, that kind of bounding box will download forever
-             * and then stop because it has more than 50k nodes.
-             */
-            Integer i = buffer.getSelectedIndex();
-            int buffer_dist = dist[i < 0 ? 0 : i];
-            double buffer_y = buffer_dist / 100000.0;
-            double buffer_x = buffer_y / scale;
-            i = maxRect.getSelectedIndex();
-            double max_area = area[i < 0 ? 0 : i] / 10000.0 / scale;
-            Area a = new Area();
-            Rectangle2D r = new Rectangle2D.Double();
-
-            /*
-             * Collect the combined area of all gpx points plus buffer zones around them. We ignore
-             * points that lie closer to the previous point than the given buffer size because
-             * otherwise this operation takes ages.
-             */
-            LatLon previous = null;
-            if (near == NEAR_TRACK || near == NEAR_BOTH) {
-                for (GpxTrack trk : data.tracks) {
-                    for (GpxTrackSegment segment : trk.getSegments()) {
-                        for (WayPoint p : segment.getWayPoints()) {
-                            LatLon c = p.getCoor();
-                            if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {
-                                // we add a buffer around the point.
-                                r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y);
-                                a.add(new Area(r));
-                                previous = c;
-                            }
-                        }
-                    }
-                }
-            }
-            if (near == NEAR_WAYPOINTS || near == NEAR_BOTH) {
-                for (WayPoint p : data.waypoints) {
-                    LatLon c = p.getCoor();
-                    if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {
-                        // we add a buffer around the point.
-                        r.setRect(c.lon() - buffer_x, c.lat() - buffer_y, 2 * buffer_x, 2 * buffer_y);
-                        a.add(new Area(r));
-                        previous = c;
-                    }
-                }
-            }
-
-            /*
-             * Area "a" now contains the hull that we would like to download data for. however we
-             * can only download rectangles, so the following is an attempt at finding a number of
-             * rectangles to download.
-             *
-             * The idea is simply: Start out with the full bounding box. If it is too large, then
-             * split it in half and repeat recursively for each half until you arrive at something
-             * small enough to download. The algorithm is improved by always using the intersection
-             * between the rectangle and the actual desired area. For example, if you have a track
-             * that goes like this: +----+ | /| | / | | / | |/ | +----+ then we would first look at
-             * downloading the whole rectangle (assume it's too big), after that we split it in half
-             * (upper and lower half), but we donot request the full upper and lower rectangle, only
-             * the part of the upper/lower rectangle that actually has something in it.
-             */
-
-            List<Rectangle2D> toDownload = new ArrayList<Rectangle2D>();
-
-            addToDownload(a, a.getBounds(), toDownload, max_area);
-
-            msg = new JPanel(new GridBagLayout());
-
-            msg.add(new JLabel(
-                    tr("<html>This action will require {0} individual<br>"
-                            + "download requests. Do you wish<br>to continue?</html>",
-                            toDownload.size())), GBC.eol());
-
-            if (toDownload.size() > 1) {
-                ret = JOptionPane.showConfirmDialog(
-                        Main.parent,
-                        msg,
-                        tr("Download from OSM along this track"),
-                        JOptionPane.OK_CANCEL_OPTION,
-                        JOptionPane.PLAIN_MESSAGE
-                );
-                switch(ret) {
-                case JOptionPane.CANCEL_OPTION:
-                case JOptionPane.CLOSED_OPTION:
-                    return;
-                default:
-                    // continue
-                }
-            }
-            final PleaseWaitProgressMonitor monitor = new PleaseWaitProgressMonitor(tr("Download data"));
-            final Future<?> future = new DownloadOsmTaskList().download(false, toDownload, monitor);
-            Main.worker.submit(
-                    new Runnable() {
-                        public void run() {
-                            try {
-                                future.get();
-                            } catch(Exception e) {
-                                e.printStackTrace();
-                                return;
-                            }
-                            monitor.close();
-                        }
-                    }
-            );
-        }
-    }
-
-    private static void addToDownload(Area a, Rectangle2D r, Collection<Rectangle2D> results, double max_area) {
-        Area tmp = new Area(r);
-        // intersect with sought-after area
-        tmp.intersect(a);
-        if (tmp.isEmpty())
-            return;
-        Rectangle2D bounds = tmp.getBounds2D();
-        if (bounds.getWidth() * bounds.getHeight() > max_area) {
-            // the rectangle gets too large; split it and make recursive call.
-            Rectangle2D r1;
-            Rectangle2D r2;
-            if (bounds.getWidth() > bounds.getHeight()) {
-                // rectangles that are wider than high are split into a left and right half,
-                r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth() / 2, bounds.getHeight());
-                r2 = new Rectangle2D.Double(bounds.getX() + bounds.getWidth() / 2, bounds.getY(),
-                        bounds.getWidth() / 2, bounds.getHeight());
-            } else {
-                // others into a top and bottom half.
-                r1 = new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight() / 2);
-                r2 = new Rectangle2D.Double(bounds.getX(), bounds.getY() + bounds.getHeight() / 2, bounds.getWidth(),
-                        bounds.getHeight() / 2);
-            }
-            addToDownload(a, r1, results, max_area);
-            addToDownload(a, r2, results, max_area);
-        } else {
-            results.add(bounds);
-        }
-    }
-
-    /**
-     * Makes a new marker layer derived from this GpxLayer containing at least one audio marker
-     * which the given audio file is associated with. Markers are derived from the following (a)
-     * explict waypoints in the GPX layer, or (b) named trackpoints in the GPX layer, or (d)
-     * timestamp on the wav file (e) (in future) voice recognised markers in the sound recording (f)
-     * a single marker at the beginning of the track
-     * @param wavFile : the file to be associated with the markers in the new marker layer
-     * @param markers : keeps track of warning messages to avoid repeated warnings
-     */
-    private void importAudio(File wavFile, MarkerLayer ml, double firstStartTime, Markers markers) {
-        URL url = null;
-        try {
-            url = wavFile.toURI().toURL();
-        } catch (MalformedURLException e) {
-            System.err.println("Unable to convert filename " + wavFile.getAbsolutePath() + " to URL");
-        }
-        Collection<WayPoint> waypoints = new ArrayList<WayPoint>();
-        boolean timedMarkersOmitted = false;
-        boolean untimedMarkersOmitted = false;
-        double snapDistance = Main.pref.getDouble("marker.audiofromuntimedwaypoints.distance", 1.0e-3); /*
-         * about
-         * 25
-         * m
-         */
-        WayPoint wayPointFromTimeStamp = null;
-
-        // determine time of first point in track
-        double firstTime = -1.0;
-        if (data.tracks != null && !data.tracks.isEmpty()) {
-            for (GpxTrack track : data.tracks) {
-                for (GpxTrackSegment seg : track.getSegments()) {
-                    for (WayPoint w : seg.getWayPoints()) {
-                        firstTime = w.time;
-                        break;
-                    }
-                    if (firstTime >= 0.0) {
-                        break;
-                    }
-                }
-                if (firstTime >= 0.0) {
-                    break;
-                }
-            }
-        }
-        if (firstTime < 0.0) {
-            JOptionPane.showMessageDialog(
-                    Main.parent,
-                    tr("No GPX track available in layer to associate audio with."),
-                    tr("Error"),
-                    JOptionPane.ERROR_MESSAGE
-            );
-            return;
-        }
-
-        // (a) try explicit timestamped waypoints - unless suppressed
-        if (Main.pref.getBoolean("marker.audiofromexplicitwaypoints", true) && data.waypoints != null
-                && !data.waypoints.isEmpty()) {
-            for (WayPoint w : data.waypoints) {
-                if (w.time > firstTime) {
-                    waypoints.add(w);
-                } else if (w.time > 0.0) {
-                    timedMarkersOmitted = true;
-                }
-            }
-        }
-
-        // (b) try explicit waypoints without timestamps - unless suppressed
-        if (Main.pref.getBoolean("marker.audiofromuntimedwaypoints", true) && data.waypoints != null
-                && !data.waypoints.isEmpty()) {
-            for (WayPoint w : data.waypoints) {
-                if (waypoints.contains(w)) {
-                    continue;
-                }
-                WayPoint wNear = nearestPointOnTrack(w.getEastNorth(), snapDistance);
-                if (wNear != null) {
-                    WayPoint wc = new WayPoint(w.getCoor());
-                    wc.time = wNear.time;
-                    if (w.attr.containsKey("name")) {
-                        wc.attr.put("name", w.getString("name"));
-                    }
-                    waypoints.add(wc);
-                } else {
-                    untimedMarkersOmitted = true;
-                }
-            }
-        }
-
-        // (c) use explicitly named track points, again unless suppressed
-        if ((Main.pref.getBoolean("marker.audiofromnamedtrackpoints", false)) && data.tracks != null
-                && !data.tracks.isEmpty()) {
-            for (GpxTrack track : data.tracks) {
-                for (GpxTrackSegment seg : track.getSegments()) {
-                    for (WayPoint w : seg.getWayPoints()) {
-                        if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {
-                            waypoints.add(w);
-                        }
-                    }
-                }
-            }
-        }
-
-        // (d) use timestamp of file as location on track
-        if ((Main.pref.getBoolean("marker.audiofromwavtimestamps", false)) && data.tracks != null
-                && !data.tracks.isEmpty()) {
-            double lastModified = wavFile.lastModified() / 1000.0; // lastModified is in
-            // milliseconds
-            double duration = AudioUtil.getCalibratedDuration(wavFile);
-            double startTime = lastModified - duration;
-            startTime = firstStartTime + (startTime - firstStartTime)
-            / Main.pref.getDouble("audio.calibration", "1.0" /* default, ratio */);
-            WayPoint w1 = null;
-            WayPoint w2 = null;
-
-            for (GpxTrack track : data.tracks) {
-                for (GpxTrackSegment seg : track.getSegments()) {
-                    for (WayPoint w : seg.getWayPoints()) {
-                        if (startTime < w.time) {
-                            w2 = w;
-                            break;
-                        }
-                        w1 = w;
-                    }
-                    if (w2 != null) {
-                        break;
-                    }
-                }
-            }
-
-            if (w1 == null || w2 == null) {
-                timedMarkersOmitted = true;
-            } else {
-                wayPointFromTimeStamp = new WayPoint(w1.getCoor().interpolate(w2.getCoor(),
-                        (startTime - w1.time) / (w2.time - w1.time)));
-                wayPointFromTimeStamp.time = startTime;
-                String name = wavFile.getName();
-                int dot = name.lastIndexOf(".");
-                if (dot > 0) {
-                    name = name.substring(0, dot);
-                }
-                wayPointFromTimeStamp.attr.put("name", name);
-                waypoints.add(wayPointFromTimeStamp);
-            }
-        }
-
-        // (e) analyse audio for spoken markers here, in due course
-
-        // (f) simply add a single marker at the start of the track
-        if ((Main.pref.getBoolean("marker.audiofromstart") || waypoints.isEmpty()) && data.tracks != null
-                && !data.tracks.isEmpty()) {
-            boolean gotOne = false;
-            for (GpxTrack track : data.tracks) {
-                for (GpxTrackSegment seg : track.getSegments()) {
-                    for (WayPoint w : seg.getWayPoints()) {
-                        WayPoint wStart = new WayPoint(w.getCoor());
-                        wStart.attr.put("name", "start");
-                        wStart.time = w.time;
-                        waypoints.add(wStart);
-                        gotOne = true;
-                        break;
-                    }
-                    if (gotOne) {
-                        break;
-                    }
-                }
-                if (gotOne) {
-                    break;
-                }
-            }
-        }
-
-        /* we must have got at least one waypoint now */
-
-        Collections.sort((ArrayList<WayPoint>) waypoints, new Comparator<WayPoint>() {
-            public int compare(WayPoint a, WayPoint b) {
-                return a.time <= b.time ? -1 : 1;
-            }
-        });
-
-        firstTime = -1.0; /* this time of the first waypoint, not first trackpoint */
-        for (WayPoint w : waypoints) {
-            if (firstTime < 0.0) {
-                firstTime = w.time;
-            }
-            double offset = w.time - firstTime;
-            AudioMarker am = new AudioMarker(w.getCoor(), w, url, ml, w.time, offset);
-            /*
-             * timeFromAudio intended for future use to shift markers of this type on
-             * synchronization
-             */
-            if (w == wayPointFromTimeStamp) {
-                am.timeFromAudio = true;
-            }
-            ml.data.add(am);
-        }
-
-        if (timedMarkersOmitted && !markers.timedMarkersOmitted) {
-            JOptionPane
-            .showMessageDialog(
-                    Main.parent,
-                    tr("Some waypoints with timestamps from before the start of the track or after the end were omitted or moved to the start."));
-            markers.timedMarkersOmitted = timedMarkersOmitted;
-        }
-        if (untimedMarkersOmitted && !markers.untimedMarkersOmitted) {
-            JOptionPane
-            .showMessageDialog(
-                    Main.parent,
-                    tr("Some waypoints which were too far from the track to sensibly estimate their time were omitted."));
-            markers.untimedMarkersOmitted = untimedMarkersOmitted;
-        }
-    }
-
-    /**
-     * Makes a WayPoint at the projection of point P onto the track providing P is less than
-     * tolerance away from the track
-     *
-     * @param P : the point to determine the projection for
-     * @param tolerance : must be no further than this from the track
-     * @return the closest point on the track to P, which may be the first or last point if off the
-     * end of a segment, or may be null if nothing close enough
-     */
-    public WayPoint nearestPointOnTrack(EastNorth P, double tolerance) {
-        /*
-         * assume the coordinates of P are xp,yp, and those of a section of track between two
-         * trackpoints are R=xr,yr and S=xs,ys. Let N be the projected point.
-         *
-         * The equation of RS is Ax + By + C = 0 where A = ys - yr B = xr - xs C = - Axr - Byr
-         *
-         * Also, note that the distance RS^2 is A^2 + B^2
-         *
-         * If RS^2 == 0.0 ignore the degenerate section of track
-         *
-         * PN^2 = (Axp + Byp + C)^2 / RS^2 that is the distance from P to the line
-         *
-         * so if PN^2 is less than PNmin^2 (initialized to tolerance) we can reject the line;
-         * otherwise... determine if the projected poijnt lies within the bounds of the line: PR^2 -
-         * PN^2 <= RS^2 and PS^2 - PN^2 <= RS^2
-         *
-         * where PR^2 = (xp - xr)^2 + (yp-yr)^2 and PS^2 = (xp - xs)^2 + (yp-ys)^2
-         *
-         * If so, calculate N as xn = xr + (RN/RS) B yn = y1 + (RN/RS) A
-         *
-         * where RN = sqrt(PR^2 - PN^2)
-         */
-
-        double PNminsq = tolerance * tolerance;
-        EastNorth bestEN = null;
-        double bestTime = 0.0;
-        double px = P.east();
-        double py = P.north();
-        double rx = 0.0, ry = 0.0, sx, sy, x, y;
-        if (data.tracks == null)
-            return null;
-        for (GpxTrack track : data.tracks) {
-            for (GpxTrackSegment seg : track.getSegments()) {
-                WayPoint R = null;
-                for (WayPoint S : seg.getWayPoints()) {
-                    EastNorth c = S.getEastNorth();
-                    if (R == null) {
-                        R = S;
-                        rx = c.east();
-                        ry = c.north();
-                        x = px - rx;
-                        y = py - ry;
-                        double PRsq = x * x + y * y;
-                        if (PRsq < PNminsq) {
-                            PNminsq = PRsq;
-                            bestEN = c;
-                            bestTime = R.time;
-                        }
-                    } else {
-                        sx = c.east();
-                        sy = c.north();
-                        double A = sy - ry;
-                        double B = rx - sx;
-                        double C = -A * rx - B * ry;
-                        double RSsq = A * A + B * B;
-                        if (RSsq == 0.0) {
-                            continue;
-                        }
-                        double PNsq = A * px + B * py + C;
-                        PNsq = PNsq * PNsq / RSsq;
-                        if (PNsq < PNminsq) {
-                            x = px - rx;
-                            y = py - ry;
-                            double PRsq = x * x + y * y;
-                            x = px - sx;
-                            y = py - sy;
-                            double PSsq = x * x + y * y;
-                            if (PRsq - PNsq <= RSsq && PSsq - PNsq <= RSsq) {
-                                double RNoverRS = Math.sqrt((PRsq - PNsq) / RSsq);
-                                double nx = rx - RNoverRS * B;
-                                double ny = ry + RNoverRS * A;
-                                bestEN = new EastNorth(nx, ny);
-                                bestTime = R.time + RNoverRS * (S.time - R.time);
-                                PNminsq = PNsq;
-                            }
-                        }
-                        R = S;
-                        rx = sx;
-                        ry = sy;
-                    }
-                }
-                if (R != null) {
-                    EastNorth c = R.getEastNorth();
-                    /* if there is only one point in the seg, it will do this twice, but no matter */
-                    rx = c.east();
-                    ry = c.north();
-                    x = px - rx;
-                    y = py - ry;
-                    double PRsq = x * x + y * y;
-                    if (PRsq < PNminsq) {
-                        PNminsq = PRsq;
-                        bestEN = c;
-                        bestTime = R.time;
-                    }
-                }
-            }
-        }
-        if (bestEN == null)
-            return null;
-        WayPoint best = new WayPoint(Main.getProjection().eastNorth2latlon(bestEN));
-        best.time = bestTime;
-        return best;
-    }
-
-    private class CustomizeLineDrawing extends AbstractAction {
-
-        CustomizeLineDrawing() {
-            super(tr("Customize line drawing"), ImageProvider.get("mapmode/addsegment"));
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            JRadioButton[] r = new JRadioButton[3];
-            r[0] = new JRadioButton(tr("Use global settings."));
-            r[1] = new JRadioButton(tr("Draw lines between points for this layer."));
-            r[2] = new JRadioButton(tr("Do not draw lines between points for this layer."));
-            ButtonGroup group = new ButtonGroup();
-            Box panel = Box.createVerticalBox();
-            for (JRadioButton b : r) {
-                group.add(b);
-                panel.add(b);
-            }
-            String propName = "draw.rawgps.lines.layer " + getName();
-            if (Main.pref.hasKey(propName)) {
-                group.setSelected(r[Main.pref.getBoolean(propName) ? 1 : 2].getModel(), true);
-            } else {
-                group.setSelected(r[0].getModel(), true);
-            }
-            int answer = JOptionPane.showConfirmDialog(Main.parent, panel,
-                    tr("Select line drawing options"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
-            switch (answer) {
-            case JOptionPane.CANCEL_OPTION:
-            case JOptionPane.CLOSED_OPTION:
-                return;
-            default:
-                // continue
-            }
-            if (group.getSelection() == r[0].getModel()) {
-                Main.pref.put(propName, null);
-            } else {
-                Main.pref.put(propName, group.getSelection() == r[1].getModel());
-            }
-            Main.map.repaint();
-        }
-    }
-
-    private class CustomizeColor extends AbstractAction {
-
-        public CustomizeColor() {
-            super(tr("Customize Color"), ImageProvider.get("colorchooser"));
-            putValue("help", "Action/LayerCustomizeColor");
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            JColorChooser c = new JColorChooser(getColor(getName()));
-            Object[] options = new Object[] { tr("OK"), tr("Cancel"), tr("Default") };
-            int answer = JOptionPane.showOptionDialog(
-                    Main.parent,
-                    c,
-                    tr("Choose a color"),
-                    JOptionPane.OK_CANCEL_OPTION,
-                    JOptionPane.PLAIN_MESSAGE,
-                    null,
-                    options, options[0]
-            );
-            switch (answer) {
-            case 0:
-                Main.pref.putColor("layer " + getName(), c.getColor());
-                break;
-            case 1:
-                return;
-            case 2:
-                Main.pref.putColor("layer " + getName(), null);
-                break;
-            }
-            Main.map.repaint();
-        }
-
-    }
-
-    private class MarkersFromNamedPoins extends AbstractAction {
-
-        public MarkersFromNamedPoins() {
-            super(tr("Markers From Named Points"), ImageProvider.get("addmarkers"));
-            putValue("help", "Action/MarkersFromNamedPoints");
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            GpxData namedTrackPoints = new GpxData();
-            for (GpxTrack track : data.tracks) {
-                for (GpxTrackSegment seg : track.getSegments()) {
-                    for (WayPoint point : seg.getWayPoints())
-                        if (point.attr.containsKey("name") || point.attr.containsKey("desc")) {
-                            namedTrackPoints.waypoints.add(point);
-                        }
-                }
-            }
-
-            MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", getName()),
-                    getAssociatedFile(), null);
-            if (ml.data.size() > 0) {
-                Main.main.addLayer(ml);
-            }
-
-        }
-    }
-
-    private class ImportAudio extends AbstractAction {
-
-        public ImportAudio() {
-            super(tr("Import Audio"), ImageProvider.get("importaudio"));
-            putValue("help", "ImportAudio");
-        }
-
-        private void warnCantImportIntoServerLayer(EGpxLayer layer) {
-            String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"
-                    + "Because its way points do not include a timestamp we cannot correlate them with audio data.</html>",
-                    layer.getName()
-            );
-            HelpAwareOptionPane.showOptionDialog(
-                    Main.parent,
-                    msg,
-                    tr("Import not possible"),
-                    JOptionPane.WARNING_MESSAGE,
-                    ht("/Action/ImportImages#CantImportIntoGpxLayerFromServer")
-            );
-        }
-
-        public void actionPerformed(ActionEvent e) {
-            if (EGpxLayer.this.data.fromServer) {
-                warnCantImportIntoServerLayer(EGpxLayer.this);
-                return;
-            }
-            String dir = Main.pref.get("markers.lastaudiodirectory");
-            JFileChooser fc = new JFileChooser(dir);
-            fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
-            fc.setAcceptAllFileFilterUsed(false);
-            fc.setFileFilter(new FileFilter() {
-                @Override
-                public boolean accept(File f) {
-                    return f.isDirectory() || f.getName().toLowerCase().endsWith(".wav");
-                }
-
-                @Override
-                public String getDescription() {
-                    return tr("Wave Audio files (*.wav)");
-                }
-            });
-            fc.setMultiSelectionEnabled(true);
-            if (fc.showOpenDialog(Main.parent) == JFileChooser.APPROVE_OPTION) {
-                if (!fc.getCurrentDirectory().getAbsolutePath().equals(dir)) {
-                    Main.pref.put("markers.lastaudiodirectory", fc.getCurrentDirectory().getAbsolutePath());
-                }
-
-                File sel[] = fc.getSelectedFiles();
-                // sort files in increasing order of timestamp (this is the end time, but so
-                // long as they don't overlap, that's fine)
-                if (sel.length > 1) {
-                    Arrays.sort(sel, new Comparator<File>() {
-                        public int compare(File a, File b) {
-                            return a.lastModified() <= b.lastModified() ? -1 : 1;
-                        }
-                    });
-                }
-
-                String names = null;
-                for (int i = 0; i < sel.length; i++) {
-                    if (names == null) {
-                        names = " (";
-                    } else {
-                        names += ", ";
-                    }
-                    names += sel[i].getName();
-                }
-                if (names != null) {
-                    names += ")";
-                } else {
-                    names = "";
-                }
-                MarkerLayer ml = new MarkerLayer(new GpxData(), tr("Audio markers from {0}", getName()) + names,
-                        getAssociatedFile(), null);
-                double firstStartTime = sel[0].lastModified() / 1000.0 /* ms -> seconds */
-                - AudioUtil.getCalibratedDuration(sel[0]);
-
-                Markers m = new Markers();
-                for (int i = 0; i < sel.length; i++) {
-                    importAudio(sel[i], ml, firstStartTime, m);
-                }
-                Main.main.addLayer(ml);
-                Main.map.repaint();
-            }
-
-        }
-    }
-
-    private class ImportImages extends AbstractAction {
-
-        public ImportImages() {
-            super(tr("Import images"), ImageProvider.get("dialogs/geoimage"));
-            putValue("help", ht("/Action/ImportImages"));
-        }
-
-        private void warnCantImportIntoServerLayer(EGpxLayer layer) {
-            String msg = tr("<html>The data in the GPX layer ''{0}'' has been downloaded from the server.<br>"
-                    + "Because its way points do not include a timestamp we cannot correlate them with images.</html>",
-                    layer.getName()
-            );
-            HelpAwareOptionPane.showOptionDialog(
-                    Main.parent,
-                    msg,
-                    tr("Import not possible"),
-                    JOptionPane.WARNING_MESSAGE,
-                    ht("/Action/ImportImages#CantImportIntoGpxLayerFromServer")
-            );
-        }
-
-        private void addRecursiveFiles(LinkedList<File> files, File[] sel) {
-            for (File f : sel) {
-                if (f.isDirectory()) {
-                    addRecursiveFiles(files, f.listFiles());
-                } else if (f.getName().toLowerCase().endsWith(".jpg")) {
-                    files.add(f);
-                }
-            }
-        }
-
-        public void actionPerformed(ActionEvent e) {
-
-            if (EGpxLayer.this.data.fromServer) {
-                warnCantImportIntoServerLayer(EGpxLayer.this);
-                return;
-            }
-            String curDir = Main.pref.get("geoimage.lastdirectory", Main.pref.get("lastDirectory"));
-            if (curDir.equals("")) {
-                curDir = ".";
-            }
-            JFileChooser fc = new JFileChooser(new File(curDir));
-
-            fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
-            fc.setMultiSelectionEnabled(true);
-            fc.setAcceptAllFileFilterUsed(false);
-            JpgImporter importer = new JpgImporter();
-            fc.setFileFilter(importer.filter);
-            fc.showOpenDialog(Main.parent);
-            LinkedList<File> files = new LinkedList<File>();
-            File[] sel = fc.getSelectedFiles();
-            if (sel == null || sel.length == 0)
-                return;
-            if (!fc.getCurrentDirectory().getAbsolutePath().equals(curDir)) {
-                Main.pref.put("geoimage.lastdirectory", fc.getCurrentDirectory().getAbsolutePath());
-            }
-            addRecursiveFiles(files, sel);
-            importer.importDataHandleExceptions(files, NullProgressMonitor.INSTANCE);
-        }
-
-    }
 }
Index: /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/GpxFilterPlugin.java
===================================================================
--- /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/GpxFilterPlugin.java	(revision 27759)
+++ /applications/editors/josm/plugins/gpxfilter/src/gpxfilter/GpxFilterPlugin.java	(revision 27760)
@@ -1,10 +1,3 @@
 package gpxfilter;
-
-import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
-import static org.openstreetmap.josm.tools.I18n.marktr;
-
-import java.awt.event.KeyEvent;
-
-import javax.swing.JMenu;
 
 import org.openstreetmap.josm.Main;
@@ -17,8 +10,5 @@
     public GpxFilterPlugin(PluginInformation info) {
         super(info);
-        JMenu historyMenu = Main.main.menu.addMenu(marktr("GPX"), KeyEvent.VK_R,
-                Main.main.menu.defaultMenuPos,null);
-        //MainMenu.add(historyMenu, new ObjectsHistoryAction());
-        MainMenu.add(historyMenu, new AddEGpxLayerAction());
+        MainMenu.add(Main.main.menu.toolsMenu, new AddEGpxLayerAction());
     }
 
Index: /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java
===================================================================
--- /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java	(revision 27759)
+++ /applications/editors/josm/plugins/graphview/src/org/openstreetmap/josm/plugins/graphview/plugin/preferences/GraphViewPreferences.java	(revision 27760)
@@ -1,3 +1,5 @@
 package org.openstreetmap.josm.plugins.graphview.plugin.preferences;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import static org.openstreetmap.josm.plugins.graphview.core.property.VehiclePropertyTypes.AXLELOAD;
@@ -234,8 +236,4 @@
         currentColorScheme = new PreferencesColorScheme(this);
 
-        nodeColor = Color.WHITE;
-        segmentColor = Color.WHITE;
-        arrowheadFillColor = Color.BLACK;
-
         separateDirections = false;
 
@@ -262,7 +260,7 @@
         }
 
-        Main.pref.put("graphview.defaultNodeColor", createColorString(nodeColor));
-        Main.pref.put("graphview.defaultSegmentColor", createColorString(segmentColor));
-        Main.pref.put("graphview.defaultArrowheadCoreColor", createColorString(arrowheadFillColor));
+        Main.pref.putColor(marktr("graphview default node"), Color.WHITE);
+        Main.pref.putColor(marktr("graphview default segment"), Color.WHITE);
+        Main.pref.putColor(marktr("graphview arrowhead core"), Color.BLACK);
 
         Main.pref.put("graphview.separateDirections", separateDirections);
@@ -274,10 +272,10 @@
     private void readPreferences() {
 
-        if (Main.pref.hasKey("graphview.parameterBookmarks")) {
+        if (!Main.pref.get("graphview.parameterBookmarks").isEmpty()) {
             String bookmarksString = Main.pref.get("graphview.parameterBookmarks");
             parameterBookmarks = parseAccessParameterBookmarksString(bookmarksString);
         }
 
-        if (Main.pref.hasKey("graphview.activeBookmark")) {
+        if (!Main.pref.get("graphview.activeBookmark").isEmpty()) {
             currentParameterBookmarkName = Main.pref.get("graphview.activeBookmark");
         }
@@ -289,14 +287,14 @@
         useInternalRulesets = Main.pref.getBoolean("graphview.useInternalRulesets", true);
 
-        if (Main.pref.hasKey("graphview.rulesetFolder")) {
+        if (!Main.pref.get("graphview.rulesetFolder").isEmpty()) {
             String dirString = Main.pref.get("graphview.rulesetFolder");
             rulesetFolder = new File(dirString);
         }
-        if (Main.pref.hasKey("graphview.rulesetFile")) {
+        if (!Main.pref.get("graphview.rulesetFile").isEmpty()) {
             String fileString = Main.pref.get("graphview.rulesetFile");
             currentRulesetFile = new File(fileString);
         }
 
-        if (Main.pref.hasKey("graphview.rulesetResource")) {
+        if (!Main.pref.get("graphview.rulesetResource").isEmpty()) {
             String rulesetString = Main.pref.get("graphview.rulesetResource");
             //get the enum value for the string
@@ -310,23 +308,7 @@
         }
 
-        if (Main.pref.hasKey("graphview.defaultNodeColor")) {
-            Color color = parseColorString(Main.pref.get("graphview.defaultNodeColor"));
-            if (color != null) {
-                nodeColor = color;
-            }
-        }
-        if (Main.pref.hasKey("graphview.defaultSegmentColor")) {
-            Color color = parseColorString(Main.pref.get("graphview.defaultSegmentColor"));
-            if (color != null) {
-                segmentColor = color;
-            }
-        }
-        if (Main.pref.hasKey("graphview.defaultArrowheadCoreColor")) {
-            Color color = parseColorString(Main.pref.get("graphview.defaultArrowheadCoreColor"));
-            if (color != null) {
-            	arrowheadFillColor = color;
-            }
-        }
-
+        nodeColor = Main.pref.getColor(marktr("graphview default node"), Color.WHITE);
+        segmentColor = Main.pref.getColor(marktr("graphview default segment"), Color.WHITE);
+        arrowheadFillColor = Main.pref.getColor(marktr("graphview arrowhead core"), Color.BLACK);
         separateDirections = Main.pref.getBoolean("graphview.separateDirections", false);
 
@@ -503,23 +485,3 @@
         }
     }
-
-    private static final Pattern COLOR_PATTERN =
-        Pattern.compile("^(\\d{1,3}),\\s*(\\d{1,3}),\\s*(\\d{1,3})$");
-
-    private String createColorString(Color color) {
-        return color.getRed() + ", " + color.getGreen() + ", " + color.getBlue();
-    }
-
-    private Color parseColorString(String string) {
-        Matcher matcher = COLOR_PATTERN.matcher(string);
-        if (!matcher.matches()) {
-            return null;
-        } else {
-            int r = Integer.parseInt(matcher.group(1));
-            int g = Integer.parseInt(matcher.group(2));
-            int b = Integer.parseInt(matcher.group(3));
-            return new Color(r, g, b);
-        }
-    }
-
 }
Index: /applications/editors/josm/plugins/print/src/org/openstreetmap/josm/plugins/print/PrintPlugin.java
===================================================================
--- /applications/editors/josm/plugins/print/src/org/openstreetmap/josm/plugins/print/PrintPlugin.java	(revision 27759)
+++ /applications/editors/josm/plugins/print/src/org/openstreetmap/josm/plugins/print/PrintPlugin.java	(revision 27760)
@@ -125,5 +125,5 @@
      */
     public static void adjustPrefs() {
-        if (! Main.pref.getBoolean("print.saved-prefs", false)) {
+        if (!Main.pref.getBoolean("print.saved-prefs", false)) {
             Main.pref.put("print.saved-prefs", true);
             adjustPref("draw.data.downloaded_area", false);
@@ -145,5 +145,5 @@
      */
     protected static void adjustPref(String key, int value) {
-        if (Main.pref.hasKey(key)) {
+        if (!Main.pref.get(key).isEmpty()) {
             Main.pref.put("print.saved-prefs."+key, Main.pref.get(key));
         }
@@ -160,5 +160,5 @@
      */
     protected static void adjustPref(String key, boolean value) {
-        if (Main.pref.hasKey(key)) {
+        if (!Main.pref.get(key).isEmpty()) {
             Main.pref.put("print.saved-prefs."+key, Main.pref.get(key));
         }
@@ -175,5 +175,5 @@
      */
     protected static void adjustPref(String key, String value) {
-        if (Main.pref.hasKey(key)) {
+        if (!Main.pref.get(key).isEmpty()) {
             Main.pref.put("print.saved-prefs."+key, Main.pref.get(key));
         }
Index: plications/editors/josm/plugins/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java
===================================================================
--- /applications/editors/josm/plugins/routing/src/com/innovant/josm/jrt/core/PreferencesKeys.java	(revision 27759)
+++ 	(revision )
@@ -1,41 +1,0 @@
-/*
- * Copyright (C) 2008 Innovant
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
- *
- * For more information, please contact:
- *
- *  Innovant
- *   juangui@gmail.com
- *   vidalfree@gmail.com
- *
- *  http://public.grupoinnovant.com/blog
- *
- */
-package com.innovant.josm.jrt.core;
-
-public enum PreferencesKeys {
-    KEY_ACTIVE_ROUTE_COLOR ("routing.active.route.color"),
-    KEY_INACTIVE_ROUTE_COLOR ("routing.inactive.route.color"),
-    KEY_ROUTE_WIDTH ("routing.route.width"),
-    KEY_ROUTE_SELECT ("routing.route.select");
-
-    public final String key;
-    PreferencesKeys (String key) {
-        this.key=key;
-    }
-
-    public String getKey() {return key;};
-}
Index: /applications/editors/josm/plugins/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java
===================================================================
--- /applications/editors/josm/plugins/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java	(revision 27759)
+++ /applications/editors/josm/plugins/routing/src/com/innovant/josm/plugin/routing/RoutingLayer.java	(revision 27760)
@@ -27,4 +27,6 @@
 
 package com.innovant.josm.plugin.routing;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
 
 import java.awt.BasicStroke;
@@ -59,5 +61,4 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 
-import com.innovant.josm.jrt.core.PreferencesKeys;
 import com.innovant.josm.jrt.osm.OsmEdge;
 
@@ -69,4 +70,18 @@
  */
 public class RoutingLayer extends Layer {
+
+    public enum PreferencesKeys {
+        KEY_ACTIVE_ROUTE_COLOR (marktr("routing active route")),
+        KEY_INACTIVE_ROUTE_COLOR (marktr("routing inactive route")),
+        KEY_ROUTE_WIDTH ("routing.route.width"),
+        KEY_ROUTE_SELECT ("routing.route.select");
+
+        public final String key;
+        PreferencesKeys (String key) {
+            this.key=key;
+        }
+
+        public String getKey() {return key;};
+    }
 
     /**
@@ -240,21 +255,10 @@
         // Get path stroke color from preferences
         // Color is different for active and inactive layers
-        String colorString;
+        Color color;
         if (isActiveLayer) {
-            if (Main.pref.hasKey(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key))
-                    colorString = Main.pref.get(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key);
-            else {
-                colorString = ColorHelper.color2html(Color.RED);
-                Main.pref.put(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, colorString);
-            }
+            color = Main.pref.getColor(PreferencesKeys.KEY_ACTIVE_ROUTE_COLOR.key, Color.RED);
         } else {
-            if (Main.pref.hasKey(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key))
-                colorString = Main.pref.get(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key);
-            else {
-                colorString = ColorHelper.color2html(Color.decode("#dd2222"));
-                Main.pref.put(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, colorString);
-            }
-        }
-        Color color = ColorHelper.html2color(colorString);
+            color = Main.pref.getColor(PreferencesKeys.KEY_INACTIVE_ROUTE_COLOR.key, Color.decode("#dd2222"));
+        }
 
         // Get path stroke width from preferences
