Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 2907)
@@ -63,6 +63,6 @@
     public boolean hasTrackPoints() {
         for (GpxTrack trk : tracks) {
-            for (Collection<WayPoint> trkseg : trk.trackSegs) {
-                if (!trkseg.isEmpty())
+            for (GpxTrackSegment trkseg : trk.getSegments()) {
+                if (!trkseg.getWayPoints().isEmpty())
                     return true;
             }
@@ -103,11 +103,10 @@
         }
         for (GpxTrack trk : tracks) {
-            for (Collection<WayPoint> trkseg : trk.trackSegs) {
-                for (WayPoint wpt : trkseg) {
-                    if (bounds == null) {
-                        bounds = new Bounds(wpt.getCoor());
-                    } else {
-                        bounds.extend(wpt.getCoor());
-                    }
+            Bounds trkBounds = trk.getBounds();
+            if (trkBounds != null) {
+                if (bounds == null) {
+                    bounds = new Bounds(trkBounds);
+                } else {
+                    bounds.extend(trkBounds);
                 }
             }
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 2907)
@@ -5,30 +5,20 @@
 
 import java.util.Collection;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.Map;
 
-public class GpxTrack extends WithAttributes {
-    public final Collection<Collection<WayPoint>> trackSegs
-    = new ConcurrentLinkedQueue<Collection<WayPoint>>();
+import org.openstreetmap.josm.data.Bounds;
 
-    /**
-     * calculates the length of the track
-     */
-    public double length(){
-        double result = 0.0; // in meters
-        WayPoint last = null;
 
-        for (Collection<WayPoint> trkseg : trackSegs) {
-            for (WayPoint tpt : trkseg) {
-                if(last != null){
-                    Double d = last.getCoor().greatCircleDistance(tpt.getCoor());
-                    if(!d.isNaN() && !d.isInfinite()) {
-                        result += d;
-                    }
-                }
-                last = tpt;
-            }
-            last = null; // restart for each track segment
-        }
-        return result;
-    }
+/**
+ * Read-only gpx track. Implementations doesn't have to be immutable, but should always be thread safe.
+ *
+ */
+
+public interface GpxTrack {
+
+    Collection<GpxTrackSegment> getSegments();
+    Map<String, Object> getAttributes();
+    Bounds getBounds();
+    double length();
+
 }
Index: trunk/src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java	(revision 2907)
+++ trunk/src/org/openstreetmap/josm/data/gpx/GpxTrackSegment.java	(revision 2907)
@@ -0,0 +1,18 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.Collection;
+
+import org.openstreetmap.josm.data.Bounds;
+
+/**
+ * Read-only gpx track segments. Implementations doesn't have to be immutable, but should always be thread safe.
+ *
+ */
+public interface GpxTrackSegment {
+
+    Bounds getBounds();
+    Collection<WayPoint> getWayPoints();
+    double length();
+
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java	(revision 2907)
+++ trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrack.java	(revision 2907)
@@ -0,0 +1,75 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.Bounds;
+
+public class ImmutableGpxTrack implements GpxTrack {
+
+    private final Map<String, Object> attributes;
+    private final Collection<GpxTrackSegment> segments;
+    private final double length;
+    private final Bounds bounds;
+
+    public ImmutableGpxTrack(Collection<Collection<WayPoint>> trackSegs, Map<String, Object> attributes) {
+        List<GpxTrackSegment> newSegments = new ArrayList<GpxTrackSegment>();
+        for (Collection<WayPoint> trackSeg: trackSegs) {
+            if (trackSeg != null && !trackSeg.isEmpty()) {
+                newSegments.add(new ImmutableGpxTrackSegment(trackSeg));
+            }
+        }
+        this.attributes = Collections.unmodifiableMap(new HashMap<String, Object>(attributes));
+        this.segments = Collections.unmodifiableCollection(newSegments);
+        this.length = calculateLength();
+        this.bounds = calculateBounds();
+    }
+
+    private double calculateLength(){
+        double result = 0.0; // in meters
+
+        for (GpxTrackSegment trkseg : segments) {
+            result += trkseg.length();
+        }
+        return result;
+    }
+
+    private Bounds calculateBounds() {
+        Bounds result = null;
+        for (GpxTrackSegment segment: segments) {
+            Bounds segBounds = segment.getBounds();
+            if (segBounds != null) {
+                if (result == null) {
+                    result = new Bounds(segBounds);
+                } else {
+                    result.extend(segBounds);
+                }
+            }
+        }
+        return result;
+    }
+
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    public Bounds getBounds() {
+        if (bounds == null)
+            return null;
+        else
+            return new Bounds(bounds);
+    }
+
+    public double length() {
+        return length;
+    }
+
+    public Collection<GpxTrackSegment> getSegments() {
+        return segments;
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java	(revision 2907)
+++ trunk/src/org/openstreetmap/josm/data/gpx/ImmutableGpxTrackSegment.java	(revision 2907)
@@ -0,0 +1,64 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.Bounds;
+
+public class ImmutableGpxTrackSegment implements GpxTrackSegment {
+
+    private final Collection<WayPoint> wayPoints;
+    private final Bounds bounds;
+    private final double length;
+
+    public ImmutableGpxTrackSegment(Collection<WayPoint> wayPoints) {
+        this.wayPoints = Collections.unmodifiableCollection(new ArrayList<WayPoint>(wayPoints));
+        this.bounds = calculateBounds();
+        this.length = calculateLength();
+    }
+
+    private Bounds calculateBounds() {
+        Bounds result = null;
+        for (WayPoint wpt: wayPoints) {
+            if (result == null) {
+                result = new Bounds(wpt.getCoor());
+            } else {
+                result.extend(wpt.getCoor());
+            }
+        }
+        return result;
+    }
+
+    private double calculateLength() {
+        double result = 0.0; // in meters
+        WayPoint last = null;
+        for (WayPoint tpt : wayPoints) {
+            if(last != null){
+                Double d = last.getCoor().greatCircleDistance(tpt.getCoor());
+                if(!d.isNaN() && !d.isInfinite()) {
+                    result += d;
+                }
+            }
+            last = tpt;
+        }
+        return result;
+    }
+
+    public Bounds getBounds() {
+        if (bounds == null)
+            return null;
+        else
+            return new Bounds(bounds);
+    }
+
+    public Collection<WayPoint> getWayPoints() {
+        return wayPoints;
+    }
+
+    public double length() {
+        return length;
+    }
+
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/SingleSegmentGpxTrack.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/SingleSegmentGpxTrack.java	(revision 2907)
+++ trunk/src/org/openstreetmap/josm/data/gpx/SingleSegmentGpxTrack.java	(revision 2907)
@@ -0,0 +1,38 @@
+package org.openstreetmap.josm.data.gpx;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
+
+public class SingleSegmentGpxTrack implements GpxTrack {
+
+	private final Map<String, Object> attributes;
+	private final GpxTrackSegment trackSegment;
+
+	public SingleSegmentGpxTrack(GpxTrackSegment trackSegment, Map<String, Object> attributes) {
+		this.attributes = Collections.unmodifiableMap(attributes);
+		this.trackSegment = trackSegment;
+	}
+
+
+	public Map<String, Object> getAttributes() {
+		return attributes;
+	}
+
+	public Bounds getBounds() {
+		return trackSegment.getBounds();
+	}
+
+	public Collection<GpxTrackSegment> getSegments() {
+		return Collections.singleton(trackSegment);
+	}
+
+	public double length() {
+		return trackSegment.length();
+	}
+
+}
Index: trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 2907)
@@ -10,5 +10,4 @@
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.tools.DateUtils;
 import org.openstreetmap.josm.tools.PrimaryDateParser;
 
@@ -20,5 +19,5 @@
     public int dir;
 
-    private CachedLatLon coor;
+    private final CachedLatLon coor;
 
     public final LatLon getCoor() {
Index: trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 2907)
@@ -53,4 +53,5 @@
 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;
@@ -197,6 +198,6 @@
                 GpxData namedTrackPoints = new GpxData();
                 for (GpxTrack track : data.tracks) {
-                    for (Collection<WayPoint> seg : track.trackSegs) {
-                        for (WayPoint point : seg)
+                    for (GpxTrackSegment seg : track.getSegments()) {
+                        for (WayPoint point : seg.getWayPoints())
                             if (point.attr.containsKey("name") || point.attr.containsKey("desc")) {
                                 namedTrackPoints.waypoints.add(point);
@@ -385,15 +386,15 @@
 
                 info.append("<tr><td>");
-                if (trk.attr.containsKey("name")) {
-                    info.append(trk.attr.get("name"));
+                if (trk.getAttributes().containsKey("name")) {
+                    info.append(trk.getAttributes().get("name"));
                 }
                 info.append("</td><td>");
-                if (trk.attr.containsKey("desc")) {
-                    info.append(" ").append(trk.attr.get("desc"));
+                if (trk.getAttributes().containsKey("desc")) {
+                    info.append(" ").append(trk.getAttributes().get("desc"));
                 }
                 info.append("</td><td>");
 
-                for (Collection<WayPoint> seg : trk.trackSegs) {
-                    for (WayPoint pnt : seg) {
+                for (GpxTrackSegment seg : trk.getSegments()) {
+                    for (WayPoint pnt : seg.getWayPoints()) {
                         if (latest == null) {
                             latest = earliest = pnt;
@@ -429,6 +430,6 @@
                 info.append(new DecimalFormat("#0.00").format(trk.length() / 1000) + "km");
                 info.append("</td><td>");
-                if (trk.attr.containsKey("url")) {
-                    info.append(trk.attr.get("url"));
+                if (trk.getAttributes().containsKey("url")) {
+                    info.append(trk.getAttributes().get("url"));
                 }
                 info.append("</td></tr>");
@@ -552,9 +553,9 @@
             WayPoint oldWp = null;
             for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
+                for (GpxTrackSegment segment : trk.getSegments()) {
                     if (!forceLines) { // don't draw lines between segments, unless forced to
                         oldWp = null;
                     }
-                    for (WayPoint trkPnt : segment) {
+                    for (WayPoint trkPnt : segment.getWayPoints()) {
                         LatLon c = trkPnt.getCoor();
                         if (Double.isNaN(c.lat()) || Double.isNaN(c.lon())) {
@@ -609,4 +610,13 @@
         }
 
+        List<Collection<WayPoint>> visibleSegments = new ArrayList<Collection<WayPoint>>();
+        for (GpxTrack trk: data.tracks) {
+            for (GpxTrackSegment trkSeg: trk.getSegments()) {
+                if (trkSeg.getBounds().asRect().intersects(box.asRect())) {
+                    visibleSegments.add(trkSeg.getWayPoints());
+                }
+            }
+        }
+
         /****************************************************************
          ********** STEP 3a - DRAW LINES ********************************
@@ -614,23 +624,21 @@
         if (lines) {
             Point old = null;
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -641,30 +649,28 @@
             Point old = null;
             Point oldA = null; // last arrow painted
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -675,29 +681,27 @@
             Point old = null;
             Point oldA = null; // last arrow painted
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -707,29 +711,27 @@
         if (large || hdopcircle) {
             g.setColor(neutralColor);
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -739,18 +741,16 @@
         if (!large && lines) {
             g.setColor(neutralColor);
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -760,17 +760,15 @@
         if (!large && !lines) {
             g.setColor(neutralColor);
-            for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    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 for trk
+            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
 
@@ -802,7 +800,7 @@
             DataSet ds = new DataSet();
             for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
+                for (GpxTrackSegment segment : trk.getSegments()) {
                     List<Node> nodes = new ArrayList<Node>();
-                    for (WayPoint p : segment) {
+                    for (WayPoint p : segment.getWayPoints()) {
                         Node n = new Node(p.getCoor());
                         String timestr = p.getString("time");
@@ -887,6 +885,6 @@
 
             for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    for (WayPoint p : segment) {
+                for (GpxTrackSegment segment : trk.getSegments()) {
+                    for (WayPoint p : segment.getWayPoints()) {
                         latsum += p.getCoor().lat();
                         latcnt++;
@@ -920,6 +918,6 @@
             LatLon previous = null;
             for (GpxTrack trk : data.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    for (WayPoint p : segment) {
+                for (GpxTrackSegment segment : trk.getSegments()) {
+                    for (WayPoint p : segment.getWayPoints()) {
                         LatLon c = p.getCoor();
                         if (previous == null || c.greatCircleDistance(previous) > buffer_dist) {
@@ -1047,9 +1045,6 @@
         if (data.tracks != null && !data.tracks.isEmpty()) {
             for (GpxTrack track : data.tracks) {
-                if (track.trackSegs == null) {
-                    continue;
-                }
-                for (Collection<WayPoint> seg : track.trackSegs) {
-                    for (WayPoint w : seg) {
+                for (GpxTrackSegment seg : track.getSegments()) {
+                    for (WayPoint w : seg.getWayPoints()) {
                         firstTime = w.time;
                         break;
@@ -1111,9 +1106,6 @@
                 && !data.tracks.isEmpty()) {
             for (GpxTrack track : data.tracks) {
-                if (track.trackSegs == null) {
-                    continue;
-                }
-                for (Collection<WayPoint> seg : track.trackSegs) {
-                    for (WayPoint w : seg) {
+                for (GpxTrackSegment seg : track.getSegments()) {
+                    for (WayPoint w : seg.getWayPoints()) {
                         if (w.attr.containsKey("name") || w.attr.containsKey("desc")) {
                             waypoints.add(w);
@@ -1137,9 +1129,6 @@
 
             for (GpxTrack track : data.tracks) {
-                if (track.trackSegs == null) {
-                    continue;
-                }
-                for (Collection<WayPoint> seg : track.trackSegs) {
-                    for (WayPoint w : seg) {
+                for (GpxTrackSegment seg : track.getSegments()) {
+                    for (WayPoint w : seg.getWayPoints()) {
                         if (startTime < w.time) {
                             w2 = w;
@@ -1177,9 +1166,6 @@
             boolean gotOne = false;
             for (GpxTrack track : data.tracks) {
-                if (track.trackSegs == null) {
-                    continue;
-                }
-                for (Collection<WayPoint> seg : track.trackSegs) {
-                    for (WayPoint w : seg) {
+                for (GpxTrackSegment seg : track.getSegments()) {
+                    for (WayPoint w : seg.getWayPoints()) {
                         WayPoint wStart = new WayPoint(w.getCoor());
                         wStart.attr.put("name", "start");
@@ -1290,10 +1276,7 @@
             return null;
         for (GpxTrack track : data.tracks) {
-            if (track.trackSegs == null) {
-                continue;
-            }
-            for (Collection<WayPoint> seg : track.trackSegs) {
+            for (GpxTrackSegment seg : track.getSegments()) {
                 WayPoint R = null;
-                for (WayPoint S : seg) {
+                for (WayPoint S : seg.getWayPoints()) {
                     EastNorth c = S.getEastNorth();
                     if (R == null) {
Index: trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 2907)
@@ -23,7 +23,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.Map;
 import java.util.Set;
 
@@ -47,5 +49,5 @@
 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.ImmutableGpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -573,9 +575,9 @@
                 continue;
             }
-            GpxTrack trk = new GpxTrack();
-            gpxData.tracks.add(trk);
+            Collection<Collection<WayPoint>> trk = new ArrayList<Collection<WayPoint>>();
+            Map<String, Object> trkAttr = new HashMap<String, Object>();
 
             if (w.get("name") != null) {
-                trk.attr.put("name", w.get("name"));
+                trkAttr.put("name", w.get("name"));
             }
 
@@ -588,5 +590,5 @@
                 if (trkseg == null) {
                     trkseg = new ArrayList<WayPoint>();
-                    trk.trackSegs.add(trkseg);
+                    trk.add(trkseg);
                 }
                 if (!n.isTagged()) {
@@ -600,4 +602,6 @@
                 trkseg.add(wpt);
             }
+
+            gpxData.tracks.add(new ImmutableGpxTrack(trk, trkAttr));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/CorrelateGpxWithImages.java	(revision 2907)
@@ -66,4 +66,5 @@
 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.visitor.BoundingXYVisitor;
@@ -959,6 +960,6 @@
             // Finds first GPX point
             outer: for (GpxTrack trk : gpx.tracks) {
-                for (Collection<WayPoint> segment : trk.trackSegs) {
-                    for (WayPoint curWp : segment) {
+                for (GpxTrackSegment segment : trk.getSegments()) {
+                    for (WayPoint curWp : segment.getWayPoints()) {
                         String curDateWpStr = (String) curWp.attr.get("time");
                         if (curDateWpStr == null) {
@@ -1087,10 +1088,10 @@
 
         for (GpxTrack trk : selectedGpx.tracks) {
-            for (Collection<WayPoint> segment : trk.trackSegs) {
+            for (GpxTrackSegment segment : trk.getSegments()) {
 
                 long prevDateWp = 0;
                 WayPoint prevWp = null;
 
-                for (WayPoint curWp : segment) {
+                for (WayPoint curWp : segment.getWayPoints()) {
 
                     String curDateWpStr = (String) curWp.attr.get("time");
Index: trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 2907)
@@ -12,6 +12,4 @@
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.util.Collection;
-import java.util.Iterator;
 
 import javax.swing.JOptionPane;
@@ -24,4 +22,5 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 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.gui.MapView;
@@ -311,7 +310,6 @@
 
         for (GpxTrack track : trackLayer.data.tracks) {
-            for (Collection<WayPoint> trackseg : track.trackSegs) {
-                for (Iterator<WayPoint> it = trackseg.iterator(); it.hasNext();) {
-                    WayPoint w = it.next();
+            for (GpxTrackSegment trackseg : track.getSegments()) {
+                for (WayPoint w: trackseg.getWayPoints()) {
                     if (audioTime < w.time) {
                         w2 = w;
Index: trunk/src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 2907)
@@ -11,4 +11,5 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -23,5 +24,5 @@
 import org.openstreetmap.josm.data.gpx.GpxLink;
 import org.openstreetmap.josm.data.gpx.GpxRoute;
-import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.xml.sax.Attributes;
@@ -47,5 +48,6 @@
 
         private GpxData currentData;
-        private GpxTrack currentTrack;
+        private Collection<Collection<WayPoint>> currentTrack;
+        private Map<String, Object> currentTrackAttr;
         private Collection<WayPoint> currentTrackSeg;
         private GpxRoute currentRoute;
@@ -100,5 +102,6 @@
                     states.push(currentState);
                     currentState = State.trk;
-                    currentTrack = new GpxTrack();
+                    currentTrack = new ArrayList<Collection<WayPoint>>();
+                    currentTrackAttr = new HashMap<String, Object>();
                 } else if (qName.equals("extensions")) {
                     states.push(currentState);
@@ -207,5 +210,5 @@
             case metadata: return currentData.attr;
             case wpt: return currentWayPoint.attr;
-            case trk: return currentTrack.attr;
+            case trk: return currentTrackAttr;
             default: return null;
             }
@@ -303,5 +306,5 @@
                 if (qName.equals("trkseg")) {
                     currentState = states.pop();
-                    currentTrack.trackSegs.add(currentTrackSeg);
+                    currentTrack.add(currentTrackSeg);
                 }
                 break;
@@ -309,10 +312,10 @@
                 if (qName.equals("trk")) {
                     currentState = states.pop();
-                    currentData.tracks.add(currentTrack);
+                    currentData.tracks.add(new ImmutableGpxTrack(currentTrack, currentTrackAttr));
                 } else if (qName.equals("name") || qName.equals("cmt")
                         || qName.equals("desc") || qName.equals("src")
                         || qName.equals("type") || qName.equals("number")
                         || qName.equals("url")) {
-                    currentTrack.attr.put(qName, accumulator.toString());
+                    currentTrackAttr.put(qName, accumulator.toString());
                 }
                 break;
Index: trunk/src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 2907)
@@ -18,4 +18,5 @@
 import org.openstreetmap.josm.data.gpx.GpxRoute;
 import org.openstreetmap.josm.data.gpx.GpxTrack;
+import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 
@@ -161,8 +162,8 @@
         for (GpxTrack trk : data.tracks) {
             open("trk");
-            writeAttr(trk.attr);
-            for (Collection<WayPoint> seg : trk.trackSegs) {
+            writeAttr(trk.getAttributes());
+            for (GpxTrackSegment seg : trk.getSegments()) {
                 openln("trkseg");
-                for (WayPoint pnt : seg) {
+                for (WayPoint pnt : seg.getWayPoints()) {
                     wayPoint(pnt, TRACK_POINT);
                 }
Index: trunk/src/org/openstreetmap/josm/io/NmeaReader.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/NmeaReader.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/io/NmeaReader.java	(revision 2907)
@@ -12,9 +12,10 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 
 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.ImmutableGpxTrack;
 import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.tools.DateUtils;
@@ -173,6 +174,5 @@
         // create the data tree
         data = new GpxData();
-        GpxTrack currentTrack = new GpxTrack();
-        data.tracks.add(currentTrack);
+        Collection<Collection<WayPoint>> currentTrack = new ArrayList<Collection<WayPoint>>();
 
         try {
@@ -207,5 +207,6 @@
             }
             rd.close();
-            currentTrack.trackSegs.add(ps.waypoints);
+            currentTrack.add(ps.waypoints);
+            data.tracks.add(new ImmutableGpxTrack(currentTrack, Collections.<String, Object>emptyMap()));
             data.recalculateBounds();
 
Index: trunk/src/org/openstreetmap/josm/tools/CopyList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/CopyList.java	(revision 2906)
+++ trunk/src/org/openstreetmap/josm/tools/CopyList.java	(revision 2907)
@@ -49,5 +49,5 @@
     }
 
-    private CopyList(E[] array, int size) {
+    public CopyList(E[] array, int size) {
         this.array = array;
         this.size = size;
