Index: /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 12156)
@@ -3,4 +3,7 @@
 
 import java.io.File;
+import java.text.MessageFormat;
+import java.util.AbstractCollection;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -9,8 +12,8 @@
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Set;
+import java.util.stream.Stream;
 
 import org.openstreetmap.josm.Main;
@@ -19,4 +22,6 @@
 import org.openstreetmap.josm.data.DataSource;
 import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.data.gpx.GpxTrack.GpxTrackChangeListener;
+import org.openstreetmap.josm.tools.ListenerList;
 
 /**
@@ -35,10 +40,39 @@
     public String creator;
 
-    /** Tracks */
-    public final Collection<GpxTrack> tracks = new LinkedList<>();
-    /** Routes */
-    public final Collection<GpxRoute> routes = new LinkedList<>();
-    /** Waypoints */
-    public final Collection<WayPoint> waypoints = new LinkedList<>();
+    private ArrayList<GpxTrack> privateTracks = new ArrayList<>();
+    private ArrayList<GpxRoute> privateRoutes = new ArrayList<>();
+    private ArrayList<WayPoint> privateWaypoints = new ArrayList<>();
+    private final GpxTrackChangeListener proxy = e -> fireInvalidate();
+
+    /**
+     * Tracks. Access is discouraged, use {@link #getTracks()} to read.
+     * @see #getTracks()
+     */
+    public final Collection<GpxTrack> tracks = new ListeningCollection<GpxTrack>(privateTracks, this::fireInvalidate) {
+
+        @Override
+        protected void removed(GpxTrack cursor) {
+            cursor.removeListener(proxy);
+            super.removed(cursor);
+        }
+
+        @Override
+        protected void added(GpxTrack cursor) {
+            super.added(cursor);
+            cursor.addListener(proxy);
+        }
+    };
+
+    /**
+     * Routes. Access is discouraged, use {@link #getTracks()} to read.
+     * @see #getRoutes()
+     */
+    public final Collection<GpxRoute> routes = new ListeningCollection<>(privateRoutes, this::fireInvalidate);
+
+    /**
+     * Waypoints. Access is discouraged, use {@link #getTracks()} to read.
+     * @see #getWaypoints()
+     */
+    public final Collection<WayPoint> waypoints = new ListeningCollection<>(privateWaypoints, this::fireInvalidate);
 
     /**
@@ -49,4 +83,6 @@
      */
     public final Set<DataSource> dataSources = new HashSet<>();
+
+    private final ListenerList<GpxDataChangeListener> listeners = ListenerList.create();
 
     /**
@@ -72,8 +108,112 @@
             }
         }
-        tracks.addAll(other.tracks);
-        routes.addAll(other.routes);
-        waypoints.addAll(other.waypoints);
+        privateTracks.addAll(other.getTracks());
+        privateRoutes.addAll(other.getRoutes());
+        privateWaypoints.addAll(other.getWaypoints());
         dataSources.addAll(other.dataSources);
+        fireInvalidate();
+    }
+
+    /**
+     * Get all tracks contained in this data set.
+     * @return The tracks.
+     */
+    public Collection<GpxTrack> getTracks() {
+        return Collections.unmodifiableCollection(privateTracks);
+    }
+
+    /**
+     * Add a new track
+     * @param track The new track
+     * @since 12156
+     */
+    public void addTrack(GpxTrack track) {
+        if (privateTracks.contains(track)) {
+            throw new IllegalArgumentException(MessageFormat.format("The track was already added to this data: {0}", track));
+        }
+        privateTracks.add(track);
+        track.addListener(proxy);
+        fireInvalidate();
+    }
+
+    /**
+     * Remove a track
+     * @param track The old track
+     * @since 12156
+     */
+    public void removeTrack(GpxTrack track) {
+        if (!privateTracks.remove(track)) {
+            throw new IllegalArgumentException(MessageFormat.format("The track was not in this data: {0}", track));
+        }
+        track.removeListener(proxy);
+        fireInvalidate();
+    }
+
+    /**
+     * Gets the list of all routes defined in this data set.
+     * @return The routes
+     * @since 12156
+     */
+    public Collection<GpxRoute> getRoutes() {
+        return Collections.unmodifiableCollection(privateRoutes);
+    }
+
+    /**
+     * Add a new route
+     * @param route The new route
+     * @since 12156
+     */
+    public void addRoute(GpxRoute route) {
+        if (privateRoutes.contains(route)) {
+            throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", route));
+        }
+        privateRoutes.add(route);
+        fireInvalidate();
+    }
+
+    /**
+     * Remove a route
+     * @param route The old route
+     * @since 12156
+     */
+    public void removeRoute(GpxRoute route) {
+        if (!privateRoutes.remove(route)) {
+            throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", route));
+        }
+        fireInvalidate();
+    }
+
+    /**
+     * Gets a list of all way points in this data set.
+     * @return The way points.
+     * @since 12156
+     */
+    public Collection<WayPoint> getWaypoints() {
+        return Collections.unmodifiableCollection(privateWaypoints);
+    }
+
+    /**
+     * Add a new waypoint
+     * @param waypoint The new waypoint
+     * @since 12156
+     */
+    public void addWaypoint(WayPoint waypoint) {
+        if (privateWaypoints.contains(waypoint)) {
+            throw new IllegalArgumentException(MessageFormat.format("The route was already added to this data: {0}", waypoint));
+        }
+        privateWaypoints.add(waypoint);
+        fireInvalidate();
+    }
+
+    /**
+     * Remove a waypoint
+     * @param waypoint The old waypoint
+     * @since 12156
+     */
+    public void removeWaypoint(WayPoint waypoint) {
+        if (!privateWaypoints.remove(waypoint)) {
+            throw new IllegalArgumentException(MessageFormat.format("The route was not in this data: {0}", waypoint));
+        }
+        fireInvalidate();
     }
 
@@ -83,11 +223,17 @@
      */
     public boolean hasTrackPoints() {
-        for (GpxTrack trk : tracks) {
-            for (GpxTrackSegment trkseg : trk.getSegments()) {
-                if (!trkseg.getWayPoints().isEmpty())
-                    return true;
-            }
-        }
-        return false;
+        return getTrackPoints().findAny().isPresent();
+    }
+
+    /**
+     * Gets a stream of all track points in the segments of the tracks of this data.
+     * @return The stream
+     * @see #getTracks()
+     * @see GpxTrack#getSegments()
+     * @see GpxTrackSegment#getWayPoints()
+     * @since 12156
+     */
+    public Stream<WayPoint> getTrackPoints() {
+        return getTracks().stream().flatMap(trk -> trk.getSegments().stream()).flatMap(trkseg -> trkseg.getWayPoints().stream());
     }
 
@@ -97,9 +243,5 @@
      */
     public boolean hasRoutePoints() {
-        for (GpxRoute rte : routes) {
-            if (!rte.routePoints.isEmpty())
-                return true;
-        }
-        return false;
+        return getRoutes().stream().anyMatch(rte -> !rte.routePoints.isEmpty());
     }
 
@@ -144,5 +286,5 @@
     public Bounds recalculateBounds() {
         Bounds bounds = null;
-        for (WayPoint wpt : waypoints) {
+        for (WayPoint wpt : getWaypoints()) {
             if (bounds == null) {
                 bounds = new Bounds(wpt.getCoor());
@@ -151,5 +293,5 @@
             }
         }
-        for (GpxRoute rte : routes) {
+        for (GpxRoute rte : getRoutes()) {
             for (WayPoint wpt : rte.routePoints) {
                 if (bounds == null) {
@@ -160,5 +302,5 @@
             }
         }
-        for (GpxTrack trk : tracks) {
+        for (GpxTrack trk : getTracks()) {
             Bounds trkBounds = trk.getBounds();
             if (trkBounds != null) {
@@ -178,11 +320,5 @@
      */
     public double length() {
-        double result = 0.0; // in meters
-
-        for (GpxTrack trk : tracks) {
-            result += trk.length();
-        }
-
-        return result;
+        return getTracks().stream().mapToDouble(GpxTrack::length).sum();
     }
 
@@ -261,7 +397,5 @@
         double py = p.north();
         double rx = 0.0, ry = 0.0, sx, sy, x, y;
-        if (tracks == null)
-            return null;
-        for (GpxTrack track : tracks) {
+        for (GpxTrack track : getTracks()) {
             for (GpxTrackSegment seg : track.getSegments()) {
                 WayPoint r = null;
@@ -352,26 +486,12 @@
      */
     public void resetEastNorthCache() {
-        if (waypoints != null) {
-            for (WayPoint wp : waypoints) {
+        getWaypoints().forEach(WayPoint::invalidateEastNorthCache);
+        getTrackPoints().forEach(WayPoint::invalidateEastNorthCache);
+        for (GpxRoute route: getRoutes()) {
+            if (route.routePoints == null) {
+                continue;
+            }
+            for (WayPoint wp: route.routePoints) {
                 wp.invalidateEastNorthCache();
-            }
-        }
-        if (tracks != null) {
-            for (GpxTrack track: tracks) {
-                for (GpxTrackSegment segment: track.getSegments()) {
-                    for (WayPoint wp: segment.getWayPoints()) {
-                        wp.invalidateEastNorthCache();
-                    }
-                }
-            }
-        }
-        if (routes != null) {
-            for (GpxRoute route: routes) {
-                if (route.routePoints == null) {
-                    continue;
-                }
-                for (WayPoint wp: route.routePoints) {
-                    wp.invalidateEastNorthCache();
-                }
             }
         }
@@ -498,3 +618,144 @@
         return true;
     }
+
+    /**
+     * Adds a listener that gets called whenever the data changed.
+     * @param listener The listener
+     * @since 12156
+     */
+    public void addChangeListener(GpxDataChangeListener listener) {
+        listeners.addListener(listener);
+    }
+
+    /**
+     * Adds a listener that gets called whenever the data changed. It is added with a weak link
+     * @param listener The listener
+     */
+    public void addWeakChangeListener(GpxDataChangeListener listener) {
+        listeners.addWeakListener(listener);
+    }
+
+    /**
+     * Removes a listener that gets called whenever the data changed.
+     * @param listener The listener
+     * @since 12156
+     */
+    public void removeChangeListener(GpxDataChangeListener listener) {
+        listeners.removeListener(listener);
+    }
+
+    private void fireInvalidate() {
+        GpxDataChangeEvent e = new GpxDataChangeEvent(this);
+        listeners.fireEvent(l -> l.gpxDataChanged(e));
+    }
+
+    /**
+     * This is a proxy of a collection that notifies a listener on every collection change
+     * @author Michael Zangl
+     *
+     * @param <T> The entry type
+     * @since 12156
+     */
+    private static class ListeningCollection<T> extends AbstractCollection<T> {
+        private final ArrayList<T> base;
+        private final Runnable runOnModification;
+
+        ListeningCollection(ArrayList<T> base, Runnable runOnModification) {
+            this.base = base;
+            this.runOnModification = runOnModification;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            Iterator<T> it = base.iterator();
+            return new Iterator<T>() {
+                private T cursor;
+
+                @Override
+                public boolean hasNext() {
+                    return it.hasNext();
+                }
+
+                @Override
+                public T next() {
+                    cursor = it.next();
+                    return cursor;
+                }
+
+                @Override
+                public void remove() {
+                    if (cursor != null) {
+                        removed(cursor);
+                        cursor = null;
+                    }
+                    it.remove();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            return base.size();
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            boolean remove = base.remove(o);
+            if (remove) {
+                removed((T) o);
+            }
+            return remove;
+        }
+
+        @Override
+        public boolean add(T e) {
+            boolean add = base.add(e);
+            added(e);
+            return add;
+        }
+
+        protected void removed(T cursor) {
+            runOnModification.run();
+        }
+
+        protected void added(T cursor) {
+            runOnModification.run();
+        }
+    }
+
+    /**
+     * A listener that listens to GPX data changes.
+     * @author Michael Zangl
+     * @since 12156
+     */
+    @FunctionalInterface
+    public interface GpxDataChangeListener {
+        /**
+         * Called when the gpx data changed.
+         * @param e The event
+         */
+        void gpxDataChanged(GpxDataChangeEvent e);
+    }
+
+    /**
+     * A data change event in any of the gpx data.
+     * @author Michael Zangl
+     * @since 12156
+     */
+    public static class GpxDataChangeEvent {
+        private final GpxData source;
+
+        GpxDataChangeEvent(GpxData source) {
+            super();
+            this.source = source;
+        }
+
+        /**
+         * Get the data that was changed.
+         * @return The data.
+         */
+        public GpxData getSource() {
+            return source;
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/data/gpx/GpxTrack.java	(revision 12156)
@@ -40,5 +40,62 @@
      * Returns the number of times this track has been changed.
      * @return Number of times this track has been changed. Always 0 for read-only tracks
+     * @deprecated since 12156 Replaced by change listeners.
      */
-    int getUpdateCount();
+    @Deprecated
+    default int getUpdateCount() {
+        // to allow removal
+        return 0;
+    }
+
+    /**
+     * Add a listener that listens to changes in the GPX track.
+     * @param l The listener
+     */
+    default void addListener(GpxTrackChangeListener l) {
+        // nop
+    }
+
+    /**
+     * Remove a listener that listens to changes in the GPX track.
+     * @param l The listener
+     */
+    default void removeListener(GpxTrackChangeListener l) {
+        // nop
+    }
+
+    /**
+     * A listener that listens to GPX track changes.
+     * @author Michael Zangl
+     * @since 12156
+     */
+    @FunctionalInterface
+    public interface GpxTrackChangeListener {
+        /**
+         * Called when the gpx data changed.
+         * @param e The event
+         */
+        void gpxDataChanged(GpxTrackChangeEvent e);
+    }
+
+    /**
+     * A track change event for the current track.
+     * @author Michael Zangl
+     * @since 12156
+     */
+    class GpxTrackChangeEvent {
+        private final GpxTrack source;
+
+        GpxTrackChangeEvent(GpxTrack source) {
+            super();
+            this.source = source;
+        }
+
+        /**
+         * Get the track that was changed.
+         * @return The track.
+         */
+        public GpxTrack getSource() {
+            return source;
+        }
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 12156)
@@ -9,5 +9,4 @@
 import java.io.File;
 import java.text.DateFormat;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -60,9 +59,10 @@
     /**
      * used by {@link ChooseTrackVisibilityAction} to determine which tracks to show/hide
+     *
+     * Call {@link #invalidate()} after each change!
+     *
+     * TODO: Make it private, make it respond to track changes.
      */
     public boolean[] trackVisibility = new boolean[0];
-
-    private final List<GpxTrack> lastTracks = new ArrayList<>(); // List of tracks at last paint
-    private int lastUpdateCount;
 
     private final GpxDrawHelper drawHelper;
@@ -94,4 +94,5 @@
         super(d.getString(GpxConstants.META_NAME));
         data = d;
+        data.addWeakChangeListener(e -> this.invalidate());
         drawHelper = new GpxDrawHelper(data);
         SystemOfMeasurement.addSoMChangeListener(drawHelper);
@@ -151,5 +152,5 @@
         }
 
-        if (!data.tracks.isEmpty()) {
+        if (!data.getTracks().isEmpty()) {
             info.append("<table><thead align='center'><tr><td colspan='5'>")
                 .append(trn("{0} track", "{0} tracks", data.tracks.size(), data.tracks.size()))
@@ -159,5 +160,5 @@
                 .append("</td></tr></thead>");
 
-            for (GpxTrack trk : data.tracks) {
+            for (GpxTrack trk : data.getTracks()) {
                 info.append("<tr><td>");
                 if (trk.getAttributes().containsKey(GpxConstants.GPX_NAME)) {
@@ -182,6 +183,6 @@
 
         info.append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length()))).append("<br>")
-            .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></html>");
+            .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size()))
+            .append(trn("{0} waypoint", "{0} waypoints", data.getWaypoints().size(), data.getWaypoints().size())).append("<br></html>");
 
         final JScrollPane sp = new JScrollPane(new HtmlPanel(info.toString()));
@@ -240,7 +241,7 @@
         }
 
-        info.append(trn("{0} track, ", "{0} tracks, ", data.tracks.size(), data.tracks.size()))
-            .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>")
+        info.append(trn("{0} track, ", "{0} tracks, ", data.getTracks().size(), data.getTracks().size()))
+            .append(trn("{0} route, ", "{0} routes, ", data.getRoutes().size(), data.getRoutes().size()))
+            .append(trn("{0} waypoint", "{0} waypoints", data.getWaypoints().size(), data.getWaypoints().size())).append("<br>")
             .append(tr("Length: {0}", SystemOfMeasurement.getSystemOfMeasurement().getDistText(data.length())))
             .append("<br></html>");
@@ -251,20 +252,4 @@
     public boolean isMergable(Layer other) {
         return other instanceof GpxLayer;
-    }
-
-    private int sumUpdateCount() {
-        int updateCount = 0;
-        for (GpxTrack track: data.tracks) {
-            updateCount += track.getUpdateCount();
-        }
-        return updateCount;
-    }
-
-    @Override
-    public boolean isChanged() {
-        if (data.tracks.equals(lastTracks))
-            return sumUpdateCount() != lastUpdateCount;
-        else
-            return true;
     }
 
@@ -279,5 +264,5 @@
         long from = fromDate.getTime();
         long to = toDate.getTime();
-        for (GpxTrack trk : data.tracks) {
+        for (GpxTrack trk : data.getTracks()) {
             Date[] t = GpxData.getMinMaxTimeForTrack(trk);
 
@@ -287,4 +272,5 @@
             i++;
         }
+        invalidate();
     }
 
@@ -299,8 +285,4 @@
     @Override
     public void paint(Graphics2D g, MapView mv, Bounds box) {
-        lastUpdateCount = sumUpdateCount();
-        lastTracks.clear();
-        lastTracks.addAll(data.tracks);
-
         List<WayPoint> visibleSegments = listVisibleSegments(box);
         if (!visibleSegments.isEmpty()) {
@@ -363,5 +345,5 @@
      */
     private void ensureTrackVisibilityLength() {
-        final int l = data.tracks.size();
+        final int l = data.getTracks().size();
         if (l == trackVisibility.length)
             return;
@@ -371,4 +353,5 @@
             trackVisibility[i] = true;
         }
+        invalidate();
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 12156)
@@ -743,5 +743,5 @@
             }
 
-            gpxData.tracks.add(new ImmutableGpxTrack(trk, trkAttr));
+            gpxData.addTrack(new ImmutableGpxTrack(trk, trkAttr));
         });
     }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 12156)
@@ -218,6 +218,5 @@
             layer.trackVisibility[table.convertRowIndexToModel(i)] = s.isSelectedIndex(i);
         }
-        Main.map.mapView.preferenceChanged(null);
-        Main.map.repaint(100);
+        layer.invalidate();
     }
 
@@ -292,4 +291,6 @@
             layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i);
         }
+        // layer has been changed
+        layer.invalidate();
         // ...sync with layer visibility instead to avoid having two ways to hide everything
         layer.setVisible(v == 1 || !s.isSelectionEmpty());
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ConvertToDataLayerAction.java	(revision 12156)
@@ -74,5 +74,5 @@
         public DataSet convert() {
             final DataSet ds = new DataSet();
-            for (GpxTrack trk : layer.data.tracks) {
+            for (GpxTrack trk : layer.data.getTracks()) {
                 for (GpxTrackSegment segment : trk.getSegments()) {
                     List<Node> nodes = new ArrayList<>();
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/MarkersFromNamedPointsAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/MarkersFromNamedPointsAction.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/MarkersFromNamedPointsAction.java	(revision 12156)
@@ -11,7 +11,4 @@
 import org.openstreetmap.josm.Main;
 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.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
@@ -30,13 +27,7 @@
     public void actionPerformed(ActionEvent e) {
         GpxData namedTrackPoints = new GpxData();
-        for (GpxTrack track : layer.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);
-                    }
-                }
-            }
-        }
+        layer.data.getTrackPoints()
+            .filter(point -> point.attr.containsKey("name") || point.attr.containsKey("desc"))
+            .forEach(namedTrackPoints.waypoints::add);
         MarkerLayer ml = new MarkerLayer(namedTrackPoints, tr("Named Trackpoints from {0}", layer.getName()), layer.getAssociatedFile(), layer);
         if (!ml.data.isEmpty()) {
Index: /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/PlayHeadMarker.java	(revision 12156)
@@ -317,5 +317,5 @@
         WayPoint w2 = null;
 
-        for (GpxTrack track : trackLayer.data.tracks) {
+        for (GpxTrack track : trackLayer.data.getTracks()) {
             for (GpxTrackSegment trackseg : track.getSegments()) {
                 for (WayPoint w: trackseg.getWayPoints()) {
Index: /trunk/src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/io/GpxReader.java	(revision 12156)
@@ -466,5 +466,5 @@
                     currentState = states.pop();
                     convertUrlToLink(currentTrackAttr);
-                    data.tracks.add(new ImmutableGpxTrack(currentTrack, currentTrackAttr));
+                    data.addTrack(new ImmutableGpxTrack(currentTrack, currentTrackAttr));
                     break;
                 case "name":
@@ -497,5 +497,5 @@
                     currentState = states.pop();
                     convertUrlToLink(currentRoute.attr);
-                    data.routes.add(currentRoute);
+                    data.addRoute(currentRoute);
                     break;
                 default: // Do nothing
Index: /trunk/src/org/openstreetmap/josm/io/GpxWriter.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 12155)
+++ /trunk/src/org/openstreetmap/josm/io/GpxWriter.java	(revision 12156)
@@ -186,5 +186,5 @@
 
     private void writeWayPoints() {
-        for (WayPoint pnt : data.waypoints) {
+        for (WayPoint pnt : data.getWaypoints()) {
             wayPoint(pnt, WAY_POINT);
         }
@@ -192,5 +192,5 @@
 
     private void writeRoutes() {
-        for (GpxRoute rte : data.routes) {
+        for (GpxRoute rte : data.getRoutes()) {
             openln("rte");
             writeAttr(rte, RTE_TRK_KEYS);
@@ -203,5 +203,5 @@
 
     private void writeTracks() {
-        for (GpxTrack trk : data.tracks) {
+        for (GpxTrack trk : data.getTracks()) {
             openln("trk");
             writeAttr(trk, RTE_TRK_KEYS);
Index: /trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 12155)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java	(revision 12156)
@@ -6,7 +6,6 @@
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import org.junit.BeforeClass;
@@ -16,5 +15,4 @@
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.data.gpx.WayPoint;
 import org.openstreetmap.josm.io.GpxReaderTest;
 import org.openstreetmap.josm.tools.ColorHelper;
@@ -128,10 +126,5 @@
         gdh.readPreferences(layerName);
         gdh.calculateColors();
-        final Iterator<WayPoint> wayPointIterator = data.tracks.iterator().next().getSegments().iterator().next().getWayPoints().iterator();
-        final List<String> colorCodes = new ArrayList<>(n);
-        while (colorCodes.size() < n) {
-            colorCodes.add(ColorHelper.color2html(wayPointIterator.next().customColoring));
-        }
-        return colorCodes;
+        return data.getTrackPoints().limit(n).map(p -> ColorHelper.color2html(p.customColoring)).collect(Collectors.toList());
     }
 }
Index: /trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java	(revision 12155)
+++ /trunk/test/unit/org/openstreetmap/josm/io/GpxReaderTest.java	(revision 12156)
@@ -47,7 +47,7 @@
     public void testMunich() throws Exception {
         final GpxData result = parseGpxData("data_nodist/munich.gpx");
-        assertEquals(2762, result.tracks.size());
-        assertEquals(0, result.routes.size());
-        assertEquals(903, result.waypoints.size());
+        assertEquals(2762, result.getTracks().size());
+        assertEquals(0, result.getRoutes().size());
+        assertEquals(903, result.getWaypoints().size());
 
         final WayPoint tenthWayPoint = ((List<WayPoint>) result.waypoints).get(10);
