diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
index c52a9c2..adabb81 100644
--- a/src/org/openstreetmap/josm/Main.java
+++ b/src/org/openstreetmap/josm/Main.java
@@ -89,6 +89,7 @@ import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.io.SaveLayersDialog;
 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
@@ -186,10 +187,17 @@ public abstract class Main {
 
     /**
      * The MapFrame. Use {@link Main#setMapFrame} to set or clear it.
+     * <p>
+     * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
      */
     public static MapFrame map;
 
     /**
+     * Provides access to the layers displayed in the main view.
+     */
+    public static LayerManagerWithActive layerManager = new LayerManagerWithActive();
+
+    /**
      * The toolbar preference control to register new actions.
      */
     public static volatile ToolbarPreferences toolbar;
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index 723ebd9..a413a3a 100644
--- a/src/org/openstreetmap/josm/gui/MapFrame.java
+++ b/src/org/openstreetmap/josm/gui/MapFrame.java
@@ -75,6 +75,7 @@ import org.openstreetmap.josm.gui.dialogs.UserListDialog;
 import org.openstreetmap.josm.gui.dialogs.ValidatorDialog;
 import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog;
 import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManager;
 import org.openstreetmap.josm.gui.util.AdvancedKeyPressDetector;
 import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.GBC;
@@ -97,6 +98,8 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
 
     /**
      * The view control displayed.
+     * <p>
+     * Accessing this is discouraged. Use the {@link LayerManager} to access map data.
      */
     public final MapView mapView;
 
@@ -174,7 +177,7 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
         setSize(400, 400);
         setLayout(new BorderLayout());
 
-        mapView = new MapView(contentPane, viewportData);
+        mapView = new MapView(Main.layerManager, contentPane, viewportData);
         if (!GraphicsEnvironment.isHeadless()) {
             new FileDrop(mapView);
         }
@@ -724,7 +727,7 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
                 mapMode.enterMode();
             }
             // invalidate repaint cache
-            Main.map.mapView.preferenceChanged(null);
+            mapView.preferenceChanged(null);
         }
 
         // After all listeners notice new layer, some buttons will be disabled/enabled
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index eff7bb6..f7a71da 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -25,10 +25,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -57,6 +55,13 @@ import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManager;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener;
 import org.openstreetmap.josm.gui.layer.MapViewPaintable;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
@@ -81,12 +86,13 @@ import org.openstreetmap.josm.tools.Utils;
  * @author imi
  */
 public class MapView extends NavigatableComponent
-implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener {
-
+implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener,
+LayerManager.LayerChangeListener, LayerManagerWithActive.ActiveLayerChangeListener {
     /**
      * Interface to notify listeners of a layer change.
      * @author imi
      */
+    @Deprecated
     public interface LayerChangeListener {
 
         /**
@@ -112,6 +118,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     /**
      * An interface that needs to be implemented in order to listen for changes to the active edit layer.
      */
+    @Deprecated
     public interface EditLayerChangeListener {
 
         /**
@@ -122,25 +129,141 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
         void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer);
     }
 
-    public boolean viewportFollowing = false;
+    protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener {
 
-    /**
-     * the layer listeners
-     */
-    private static final CopyOnWriteArrayList<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
-    private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<>();
+        private final LayerChangeListener wrapped;
+        private boolean receiveOneInitialFire;
+
+        public LayerChangeAdapter(LayerChangeListener wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        public LayerChangeAdapter(LayerChangeListener wrapped, boolean initialFire) {
+            this(wrapped);
+            this.receiveOneInitialFire = initialFire;
+        }
+
+        @Override
+        public void layerAdded(LayerAddEvent e) {
+            wrapped.layerAdded(e.getAddedLayer());
+        }
+
+        @Override
+        public void layerRemoving(LayerRemoveEvent e) {
+            wrapped.layerRemoved(e.getRemovedLayer());
+        }
+
+        @Override
+        public void layerOrderChanged(LayerOrderChangeEvent e) {
+            // not in old API
+        }
+
+        @Override
+        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+            Layer oldActive = receiveOneInitialFire ? null : e.getPreviousActiveLayer();
+            Layer newActive = e.getSource().getActiveLayer();
+            if (oldActive != newActive) {
+                wrapped.activeLayerChange(oldActive, newActive);
+            }
+            receiveOneInitialFire = false;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            LayerChangeAdapter other = (LayerChangeAdapter) obj;
+            if (wrapped == null) {
+                if (other.wrapped != null)
+                    return false;
+            } else if (!wrapped.equals(other.wrapped))
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "LayerChangeAdapter [wrapped=" + wrapped + "]";
+        }
+
+    }
+
+    protected static class EditLayerChangeAdapter implements ActiveLayerChangeListener {
+
+        private final EditLayerChangeListener wrapped;
+
+        public EditLayerChangeAdapter(EditLayerChangeListener wrapped) {
+            this.wrapped = wrapped;
+        }
+
+        @Override
+        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+            OsmDataLayer oldLayer = e.getPreviousEditLayer();
+            OsmDataLayer newLayer = e.getSource().getEditLayer();
+            if (oldLayer != newLayer) {
+                wrapped.editLayerChanged(oldLayer, newLayer);
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            EditLayerChangeAdapter other = (EditLayerChangeAdapter) obj;
+            if (wrapped == null) {
+                if (other.wrapped != null)
+                    return false;
+            } else if (!wrapped.equals(other.wrapped))
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "EditLayerChangeAdapter [wrapped=" + wrapped + "]";
+        }
+
+    }
 
     /**
      * Removes a layer change listener
      *
      * @param listener the listener. Ignored if null or already registered.
      */
+    @Deprecated
     public static void removeLayerChangeListener(LayerChangeListener listener) {
-        layerChangeListeners.remove(listener);
+        LayerChangeAdapter adapter = new LayerChangeAdapter(listener);
+        Main.layerManager.removeLayerChangeListener(adapter);
+        Main.layerManager.removeActiveLayerChangeListener(adapter);
     }
 
+    @Deprecated
     public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
-        editLayerChangeListeners.remove(listener);
+        Main.layerManager.removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
     }
 
     /**
@@ -148,10 +271,9 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *
      * @param listener the listener. Ignored if null or already registered.
      */
+    @Deprecated
     public static void addLayerChangeListener(LayerChangeListener listener) {
-        if (listener != null) {
-            layerChangeListeners.addIfAbsent(listener);
-        }
+        addLayerChangeListener(listener, false);
     }
 
     /**
@@ -161,10 +283,15 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @param initialFire fire an active-layer-changed-event right after adding
      * the listener in case there is a layer present (should be)
      */
+    @Deprecated
     public static void addLayerChangeListener(LayerChangeListener listener, boolean initialFire) {
-        addLayerChangeListener(listener);
-        if (initialFire && Main.isDisplayingMapView()) {
-            listener.activeLayerChange(null, Main.map.mapView.getActiveLayer());
+        if (listener != null) {
+            initialFire = initialFire && Main.isDisplayingMapView();
+
+            LayerChangeAdapter adapter = new LayerChangeAdapter(listener, initialFire);
+            Main.layerManager.addLayerChangeListener(adapter, false);
+            Main.layerManager.addActiveLayerChangeListener(adapter, initialFire);
+            adapter.receiveOneInitialFire = false;
         }
     }
 
@@ -175,10 +302,10 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @param initialFire fire an edit-layer-changed-event right after adding
      * the listener in case there is an edit layer present
      */
+    @Deprecated
     public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
-        addEditLayerChangeListener(listener);
-        if (initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null) {
-            listener.editLayerChanged(null, Main.map.mapView.getEditLayer());
+        if (listener != null) {
+            Main.layerManager.addActiveLayerChangeListener(new EditLayerChangeAdapter(listener), initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null);
         }
     }
 
@@ -187,54 +314,14 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *
      * @param listener the listener. Ignored if null or already registered.
      */
+    @Deprecated
     public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
-        if (listener != null) {
-            editLayerChangeListeners.addIfAbsent(listener);
-        }
-    }
-
-    /**
-     * Calls the {@link LayerChangeListener#activeLayerChange(Layer, Layer)} method of all listeners.
-     *
-     * @param oldLayer The old layer
-     * @param newLayer The new active layer.
-     */
-    protected void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
-        for (LayerChangeListener l : layerChangeListeners) {
-            l.activeLayerChange(oldLayer, newLayer);
-        }
+        addEditLayerChangeListener(listener, false);
     }
 
-    protected void fireLayerAdded(Layer newLayer) {
-        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
-            l.layerAdded(newLayer);
-        }
-    }
-
-    protected void fireLayerRemoved(Layer layer) {
-        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
-            l.layerRemoved(layer);
-        }
-    }
-
-    protected void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
-        for (EditLayerChangeListener l : editLayerChangeListeners) {
-            l.editLayerChanged(oldLayer, newLayer);
-        }
-    }
+    public boolean viewportFollowing = false;
 
-    /**
-     * A list of all layers currently loaded. Locked by {@link #layerLock}.
-     */
-    private final transient List<Layer> layers = new ArrayList<>();
-
-    /**
-     * This lock manages concurrent access to {@link #layers},
-     * {@link #editLayer} and {@link #activeLayer}.
-     * <p>
-     * The read lock is always held while those fields are read or while layer change listeners are fired.
-     */
-    //private final ReentrantReadWriteLock layerLock = new ReentrantReadWriteLock();
+    private LayerManagerWithActive layerManager = new LayerManagerWithActive();
 
     /**
      * The play head marker: there is only one of these so it isn't in any specific layer
@@ -242,16 +329,6 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     public transient PlayHeadMarker playHeadMarker = null;
 
     /**
-     * The layer from the layers list that is currently active. Locked by {@link #layerLock}.
-     */
-    private transient Layer activeLayer;
-
-    /**
-     * The edit layer is the current active data layer. Locked by {@link #layerLock}.
-     */
-    private transient OsmDataLayer editLayer;
-
-    /**
      * The last event performed by mouse.
      */
     public MouseEvent lastMEvent = new MouseEvent(this, 0, 0, 0, 0, 0, 0, false); // In case somebody reads it before first mouse move
@@ -275,13 +352,17 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
 
     /**
      * Constructs a new {@code MapView}.
+     * @param layerManager The layers to display.
      * @param contentPane The content pane used to register shortcuts in its
      * {@link InputMap} and {@link ActionMap}
      * @param viewportData the initial viewport of the map. Can be null, then
      * the viewport is derived from the layer data.
      */
-    public MapView(final JPanel contentPane, final ViewportData viewportData) {
+    public MapView(LayerManagerWithActive layerManager, final JPanel contentPane, final ViewportData viewportData) {
+        this.layerManager = layerManager;
         initialViewport = viewportData;
+        layerManager.addLayerChangeListener(this);
+        layerManager.addActiveLayerChangeListener(this, false);
         Main.pref.addPreferenceChangeListener(this);
 
         addComponentListener(new ComponentAdapter() {
@@ -353,93 +434,38 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     }
 
     /**
-     * Adds a GPX layer. A GPX layer is added below the lowest data layer.
-     * <p>
-     * Does not call {@link #fireLayerAdded(Layer)}.
-     *
-     * @param layer the GPX layer
-     */
-    protected void addGpxLayer(GpxLayer layer) {
-        synchronized (layers) {
-            if (layers.isEmpty()) {
-                layers.add(layer);
-                return;
-            }
-            for (int i = layers.size()-1; i >= 0; i--) {
-                if (layers.get(i) instanceof OsmDataLayer) {
-                    if (i == layers.size()-1) {
-                        layers.add(layer);
-                    } else {
-                        layers.add(i+1, layer);
-                    }
-                    return;
-                }
-            }
-            layers.add(0, layer);
-        }
-    }
-
-    /**
      * Add a layer to the current MapView. The layer will be added at topmost
      * position.
      * @param layer The layer to add
      */
+    @Deprecated
     public void addLayer(Layer layer) {
-        boolean isOsmDataLayer = layer instanceof OsmDataLayer;
-        EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class);
-        Layer oldActiveLayer = activeLayer;
-        OsmDataLayer oldEditLayer = editLayer;
-
-        synchronized (layers) {
-            if (layer instanceof MarkerLayer && playHeadMarker == null) {
-                playHeadMarker = PlayHeadMarker.create();
-            }
-
-            if (layer instanceof GpxLayer) {
-                addGpxLayer((GpxLayer) layer);
-            } else if (layers.isEmpty()) {
-                layers.add(layer);
-            } else if (layer.isBackgroundLayer()) {
-                int i = 0;
-                for (; i < layers.size(); i++) {
-                    if (layers.get(i).isBackgroundLayer()) {
-                        break;
-                    }
-                }
-                layers.add(i, layer);
-            } else {
-                layers.add(0, layer);
-            }
-
-            if (isOsmDataLayer || oldActiveLayer == null) {
-                // autoselect the new layer
-                listenersToFire.addAll(setActiveLayer(layer, true));
-            }
-
-            if (isOsmDataLayer) {
-                ((OsmDataLayer) layer).addLayerStateChangeListener(this);
-            }
+        layerManager.addLayer(layer);
+    }
 
-            layer.addPropertyChangeListener(this);
-            Main.addProjectionChangeListener(layer);
-            AudioPlayer.reset();
+    @Override
+    public void layerAdded(LayerAddEvent e) {
+        Layer layer = e.getAddedLayer();
+        if (layer instanceof MarkerLayer && playHeadMarker == null) {
+            playHeadMarker = PlayHeadMarker.create();
         }
-        fireLayerAdded(layer);
-        onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
 
-        if (!listenersToFire.isEmpty()) {
-            repaint();
+        boolean isOsmDataLayer = layer instanceof OsmDataLayer;
+        if (isOsmDataLayer) {
+            ((OsmDataLayer) layer).addLayerStateChangeListener(this);
         }
+
+        layer.addPropertyChangeListener(this);
+        Main.addProjectionChangeListener(layer);
+        AudioPlayer.reset();
+
+        repaint();
     }
 
     @Override
+    @Deprecated
     protected DataSet getCurrentDataSet() {
-        synchronized (layers) {
-            if (editLayer != null)
-                return editLayer.data;
-            else
-                return null;
-        }
+        return layerManager.getEditDataSet();
     }
 
     /**
@@ -448,9 +474,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @return true if the active data layer (edit layer) is drawable, false otherwise
      */
     public boolean isActiveLayerDrawable() {
-        synchronized (layers) {
-            return editLayer != null;
-        }
+         return getEditLayer() != null;
     }
 
     /**
@@ -459,36 +483,8 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @return true if the active data layer (edit layer) is visible, false otherwise
      */
     public boolean isActiveLayerVisible() {
-        synchronized (layers) {
-            return isActiveLayerDrawable() && editLayer.isVisible();
-        }
-    }
-
-    /**
-     * Determines the next active data layer according to the following
-     * rules:
-     * <ul>
-     *   <li>if there is at least one {@link OsmDataLayer} the first one
-     *     becomes active</li>
-     *   <li>otherwise, the top most layer of any type becomes active</li>
-     * </ul>
-     *
-     * @return the next active data layer
-     */
-    protected Layer determineNextActiveLayer(List<Layer> layersList) {
-        // First look for data layer
-        for (Layer layer:layersList) {
-            if (layer instanceof OsmDataLayer)
-                return layer;
-        }
-
-        // Then any layer
-        if (!layersList.isEmpty())
-            return layersList.get(0);
-
-        // and then give up
-        return null;
-
+        OsmDataLayer e = getEditLayer();
+        return e != null && e.isVisible();
     }
 
     /**
@@ -496,42 +492,24 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * an LayerChange event is fired.
      * @param layer The layer to remove
      */
+    @Deprecated
     public void removeLayer(Layer layer) {
-        EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class);
-        Layer oldActiveLayer = activeLayer;
-        OsmDataLayer oldEditLayer = editLayer;
-
-        synchronized (layers) {
-            List<Layer> layersList = new ArrayList<>(layers);
-
-            if (!layersList.remove(layer))
-                return;
-
-            listenersToFire = setEditLayer(layersList);
-
-            if (layer == activeLayer) {
-                listenersToFire.addAll(setActiveLayer(determineNextActiveLayer(layersList), false));
-            }
-
-            if (layer instanceof OsmDataLayer) {
-                ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this);
-            }
+        layerManager.removeLayer(layer);
+    }
 
-            layers.remove(layer);
-            Main.removeProjectionChangeListener(layer);
-            layer.removePropertyChangeListener(this);
-            layer.destroy();
-            AudioPlayer.reset();
+    @Override
+    public void layerRemoving(LayerRemoveEvent e) {
+        Layer layer = e.getRemovedLayer();
+        if (layer instanceof OsmDataLayer) {
+            ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this);
         }
-        onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
-        fireLayerRemoved(layer);
 
-        repaint();
-    }
+        Main.removeProjectionChangeListener(layer);
+        layer.removePropertyChangeListener(this);
+        layer.destroy();
+        AudioPlayer.reset();
 
-    private void onEditLayerChanged(OsmDataLayer oldEditLayer) {
-        fireEditLayerChanged(oldEditLayer, editLayer);
-        refreshTitle();
+        repaint();
     }
 
     private boolean virtualNodesEnabled = false;
@@ -560,27 +538,12 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @param pos       The new position of the layer
      */
     public void moveLayer(Layer layer, int pos) {
-        EnumSet<LayerListenerType> listenersToFire;
-        Layer oldActiveLayer = activeLayer;
-        OsmDataLayer oldEditLayer = editLayer;
-
-        synchronized (layers) {
-            int curLayerPos = layers.indexOf(layer);
-            if (curLayerPos == -1)
-                throw new IllegalArgumentException(tr("Layer not in list."));
-            if (pos == curLayerPos)
-                return; // already in place.
-            layers.remove(curLayerPos);
-            if (pos >= layers.size()) {
-                layers.add(layer);
-            } else {
-                layers.add(pos, layer);
-            }
-            listenersToFire = setEditLayer(layers);
-            AudioPlayer.reset();
-        }
-        onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
+        layerManager.moveLayer(layer, pos);
+    }
 
+    @Override
+    public void layerOrderChanged(LayerOrderChangeEvent e) {
+        AudioPlayer.reset();
         repaint();
     }
 
@@ -590,53 +553,15 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @return The index in the list.
      * @throws IllegalArgumentException if that layer does not belong to this view.
      */
+    @Deprecated
     public int getLayerPos(Layer layer) {
         int curLayerPos;
-        synchronized (layers) {
-            curLayerPos = layers.indexOf(layer);
-        }
+            curLayerPos = getAllLayersAsList().indexOf(layer);
         if (curLayerPos == -1)
             throw new IllegalArgumentException(tr("Layer not in list."));
         return curLayerPos;
     }
 
-    /**
-     * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
-     * first, layer with the highest Z-Order last.
-     * <p>
-     * The active data layer is pulled above all adjacent data layers.
-     *
-     * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
-     * first, layer with the highest Z-Order last.
-     */
-    public List<Layer> getVisibleLayersInZOrder() {
-        synchronized (layers) {
-            List<Layer> ret = new ArrayList<>();
-            // This is set while we delay the addition of the active layer.
-            boolean activeLayerDelayed = false;
-            for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
-                Layer l = iterator.previous();
-                if (!l.isVisible()) {
-                    // ignored
-                } else if (l == activeLayer && l instanceof OsmDataLayer) {
-                    // delay and add after the current block of OsmDataLayer
-                    activeLayerDelayed = true;
-                } else {
-                    if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
-                        // add active layer before the current one.
-                        ret.add(activeLayer);
-                        activeLayerDelayed = false;
-                    }
-                    // Add this layer now
-                    ret.add(l);
-                }
-            }
-            if (activeLayerDelayed) {
-                ret.add(activeLayer);
-            }
-            return ret;
-        }
-    }
 
     private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
         if (layer.getOpacity() < 1) {
@@ -655,7 +580,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
             return;
         }
 
-        List<Layer> visibleLayers = getVisibleLayersInZOrder();
+        List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
 
         int nonChangedLayersCount = 0;
         for (Layer l: visibleLayers) {
@@ -822,19 +747,17 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     /**
      * @return An unmodifiable collection of all layers
      */
+    @Deprecated
     public Collection<Layer> getAllLayers() {
-        synchronized (layers) {
-            return Collections.unmodifiableCollection(new ArrayList<>(layers));
-        }
+        return layerManager.getLayers();
     }
 
     /**
      * @return An unmodifiable ordered list of all layers
      */
+    @Deprecated
     public List<Layer> getAllLayersAsList() {
-        synchronized (layers) {
-            return Collections.unmodifiableList(new ArrayList<>(layers));
-        }
+        return layerManager.getLayers();
     }
 
     /**
@@ -848,8 +771,9 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @param ofType The layer type.
      * @return an unmodifiable list of layers of a certain type.
      */
+    @Deprecated
     public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
-        return new ArrayList<>(Utils.filteredCollection(getAllLayers(), ofType));
+        return layerManager.getLayersOfType(ofType);
     }
 
     /**
@@ -857,10 +781,9 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *
      * @return the number of layers managed by this map view
      */
+    @Deprecated
     public int getNumLayers() {
-        synchronized (layers) {
-            return layers.size();
-        }
+        return getAllLayers().size();
     }
 
     /**
@@ -868,138 +791,41 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *
      * @return true if there is at least one layer in this map view
      */
+    @Deprecated
     public boolean hasLayers() {
         return getNumLayers() > 0;
     }
 
     /**
-     * Sets the active edit layer.
-     * <p>
-     * You must own a write {@link #layerLock} when calling this method.
-     * @param layersList A list to select that layer from.
-     * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}
-     */
-    private EnumSet<LayerListenerType> setEditLayer(List<Layer> layersList) {
-        final OsmDataLayer newEditLayer = findNewEditLayer(layersList);
-
-        // Set new edit layer
-        if (newEditLayer != editLayer) {
-            if (newEditLayer == null) {
-                // Note: Unsafe to call while layer write lock is held.
-                getCurrentDataSet().setSelected();
-            }
-
-            editLayer = newEditLayer;
-            return EnumSet.of(LayerListenerType.EDIT_LAYER_CHANGE);
-        } else {
-            return EnumSet.noneOf(LayerListenerType.class);
-        }
-
-    }
-
-    private OsmDataLayer findNewEditLayer(List<Layer> layersList) {
-        OsmDataLayer newEditLayer = layersList.contains(editLayer) ? editLayer : null;
-        // Find new edit layer
-        if (activeLayer != editLayer || !layersList.contains(editLayer)) {
-            if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {
-                newEditLayer = (OsmDataLayer) activeLayer;
-            } else {
-                for (Layer layer:layersList) {
-                    if (layer instanceof OsmDataLayer) {
-                        newEditLayer = (OsmDataLayer) layer;
-                        break;
-                    }
-                }
-            }
-        }
-        return newEditLayer;
-    }
-
-    /**
      * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance
      * of {@link OsmDataLayer} also sets {@link #editLayer} to <code>layer</code>.
      *
      * @param layer the layer to be activate; must be one of the layers in the list of layers
      * @throws IllegalArgumentException if layer is not in the lis of layers
      */
+    @Deprecated
     public void setActiveLayer(Layer layer) {
-        EnumSet<LayerListenerType> listenersToFire;
-        Layer oldActiveLayer;
-        OsmDataLayer oldEditLayer;
-
-        synchronized (layers) {
-            oldActiveLayer = activeLayer;
-            oldEditLayer = editLayer;
-            listenersToFire = setActiveLayer(layer, true);
-        }
-        onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
-
-        repaint();
+    	layerManager.setActiveLayer(layer);
     }
-
-    /**
-     * Sets the active layer. Propagates this change to all map buttons.
-     * @param layer The layer to be active.
-     * @param setEditLayer if this is <code>true</code>, the edit layer is also set.
-     * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}
-     */
-    private EnumSet<LayerListenerType> setActiveLayer(final Layer layer, boolean setEditLayer) {
-        if (layer != null && !layers.contains(layer))
-            throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));
-
-        if (layer == activeLayer)
-            return EnumSet.noneOf(LayerListenerType.class);
-
-        activeLayer = layer;
-        EnumSet<LayerListenerType> listenersToFire = EnumSet.of(LayerListenerType.ACTIVE_LAYER_CHANGE);
-        if (setEditLayer) {
-            listenersToFire.addAll(setEditLayer(layers));
-        }
-
-        return listenersToFire;
-    }
-
     /**
      * Replies the currently active layer
      *
      * @return the currently active layer (may be null)
      */
+    @Deprecated
     public Layer getActiveLayer() {
-        synchronized (layers) {
-            return activeLayer;
-        }
-    }
-
-    private enum LayerListenerType {
-        ACTIVE_LAYER_CHANGE,
-        EDIT_LAYER_CHANGE
+        return layerManager.getActiveLayer();
     }
 
-    /**
-     * This is called whenever one of active layer/edit layer or both may have been changed,
-     * @param oldActive The old active layer
-     * @param oldEdit The old edit layer.
-     * @param listenersToFire A mask of listeners to fire using {@link LayerListenerType}s
-     */
-    private void onActiveEditLayerChanged(final Layer oldActive, final OsmDataLayer oldEdit, EnumSet<LayerListenerType> listenersToFire) {
-        if (listenersToFire.contains(LayerListenerType.EDIT_LAYER_CHANGE)) {
-            onEditLayerChanged(oldEdit);
-        }
-        if (listenersToFire.contains(LayerListenerType.ACTIVE_LAYER_CHANGE)) {
-            onActiveLayerChanged(oldActive);
-        }
-    }
-
-    private void onActiveLayerChanged(final Layer old) {
-        fireActiveLayerChanged(old, activeLayer);
-
+    @Override
+    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
         /* This only makes the buttons look disabled. Disabling the actions as well requires
          * the user to re-select the tool after i.e. moving a layer. While testing I found
          * that I switch layers and actions at the same time and it was annoying to mind the
          * order. This way it works as visual clue for new users */
         for (final AbstractButton b: Main.map.allMapModeButtons) {
             MapMode mode = (MapMode) b.getAction();
-            final boolean activeLayerSupported = mode.layerIsSupported(activeLayer);
+            final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
             if (activeLayerSupported) {
                 Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
             } else {
@@ -1012,6 +838,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
             });
         }
         AudioPlayer.reset();
+        refreshTitle();
         repaint();
     }
 
@@ -1020,10 +847,9 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *
      * @return the current edit layer. May be null.
      */
+    @Deprecated
     public OsmDataLayer getEditLayer() {
-        synchronized (layers) {
-            return editLayer;
-        }
+        return layerManager.getEditLayer();
     }
 
     /**
@@ -1032,10 +858,9 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      * @param layer the layer
      * @return true if the list of layers managed by this map view contain layer
      */
+    @Deprecated
     public boolean hasLayer(Layer layer) {
-        synchronized (layers) {
-            return layers.contains(layer);
-        }
+        return layerManager.containsLayer(layer);
     }
 
     /**
@@ -1099,12 +924,11 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      */
     protected void refreshTitle() {
         if (Main.parent != null) {
-            synchronized (layers) {
-                boolean dirty = editLayer != null &&
-                        (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
-                ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
-                ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
-            }
+            OsmDataLayer editLayer = layerManager.getEditLayer();
+            boolean dirty = editLayer != null &&
+                    (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
+            ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
+            ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
         }
     }
 
@@ -1123,19 +947,15 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     };
 
     public void destroy() {
+        layerManager.removeLayerChangeListener(this);
+        layerManager.removeActiveLayerChangeListener(this);
         Main.pref.removePreferenceChangeListener(this);
         DataSet.removeSelectionListener(repaintSelectionChangedListener);
         MultipolygonCache.getInstance().clear(this);
         if (mapMover != null) {
             mapMover.destroy();
         }
-        synchronized (layers) {
-            activeLayer = null;
-            changedLayer = null;
-            editLayer = null;
-            layers.clear();
-            nonChangedLayers.clear();
-        }
+        nonChangedLayers.clear();
         synchronized (temporaryLayers) {
             temporaryLayers.clear();
         }
diff --git a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
index 25ca961..f20d465 100644
--- a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
@@ -364,4 +364,9 @@ public class GpxLayer extends Layer {
         return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), GpxImporter.FILE_FILTER);
     }
 
+    @Override
+    public LayerPositionStrategy getPositionStrategy() {
+        return LayerPositionStrategy.AFTER_LAST_DATA_LAYER;
+    }
+
 }
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java
index e16587b..3218636 100644
--- a/src/org/openstreetmap/josm/gui/layer/Layer.java
+++ b/src/org/openstreetmap/josm/gui/layer/Layer.java
@@ -506,4 +506,12 @@ public abstract class Layer implements Destroyable, MapViewPaintable, Projection
     public File createAndOpenSaveFileChooser() {
         return SaveActionBase.createAndOpenSaveFileChooser(tr("Save Layer"), "lay");
     }
+
+    /**
+     * Gets the strategy that specifies where this layer should be inserted in a layer list.
+     * @return That strategy.
+     */
+    public LayerPositionStrategy getPositionStrategy() {
+        return isBackgroundLayer() ? LayerPositionStrategy.BEFORE_FIRST_BACKGROUND_LAYER : LayerPositionStrategy.IN_FRONT;
+    }
 }
diff --git a/src/org/openstreetmap/josm/gui/layer/LayerManager.java b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
new file mode 100644
index 0000000..c39abdf
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
@@ -0,0 +1,291 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * This class handles the layer management.
+ * <p>
+ * This manager handles a list of layers with the first layer being the front layer.
+ * <h1>Threading</h1>
+ * Methods of this manager may be called from any thread in any order. Listeners are called while this layer manager is locked, so they should not block.
+ *
+ * @author Michael Zangl
+ */
+public class LayerManager {
+    /**
+     * Interface to notify listeners of a layer change.
+     */
+    public interface LayerChangeListener {
+        /**
+         * Notifies this listener that a layer has been added.
+         * @param e The new added layer event
+         */
+        void layerAdded(LayerAddEvent e);
+
+        /**
+         * Notifies this listener that a layer is about to be removed.
+         * @param e The layer to be removed (as event)
+         */
+        void layerRemoving(LayerRemoveEvent e);
+
+        /**
+         * Notifies this listener that the order of layers was changed.
+         * @param e The order change event.
+         */
+        void layerOrderChanged(LayerOrderChangeEvent e);
+    }
+
+    protected static class LayerManagerEvent {
+        private final LayerManager source;
+
+        LayerManagerEvent(LayerManager source) {
+            this.source = source;
+        }
+
+        public LayerManager getSource() {
+            return source;
+        }
+    }
+
+    /**
+     * The event that is fired whenever a layer was added.
+     * @author Michael Zangl
+     */
+    public static class LayerAddEvent extends LayerManagerEvent {
+        private final Layer addedLayer;
+
+        LayerAddEvent(LayerManager source, Layer addedLayer) {
+            super(source);
+            this.addedLayer = addedLayer;
+        }
+
+        /**
+         * Gets the layer that was added.
+         * @return The added layer.
+         */
+        public Layer getAddedLayer() {
+            return addedLayer;
+        }
+    }
+
+    /**
+     * The event that is fired before removing a layer.
+     * @author Michael Zangl
+     */
+    public static class LayerRemoveEvent extends LayerManagerEvent {
+        private final Layer removedLayer;
+
+        LayerRemoveEvent(LayerManager source, Layer removedLayer) {
+            super(source);
+            this.removedLayer = removedLayer;
+        }
+
+        /**
+         * Gets the layer that is about to be removed.
+         * @return The layer.
+         */
+        public Layer getRemovedLayer() {
+            return removedLayer;
+        }
+    }
+
+    /**
+     * An event that is fired whenever the order of layers changed.
+     * @author Michael Zangl
+     */
+    public static class LayerOrderChangeEvent extends LayerManagerEvent {
+        LayerOrderChangeEvent(LayerManager source) {
+            super(source);
+        }
+
+    }
+
+    /**
+     * This is the list of layers we manage.
+     */
+    private final List<Layer> layers = new ArrayList<>();
+
+    private final List<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
+
+    /**
+     * Add a layer. The layer will be added at a given psoition.
+     * @param layer The layer to add
+     */
+    public synchronized void addLayer(Layer layer) {
+        if (containsLayer(layer)) {
+            throw new IllegalArgumentException("Cannot add a layer twice.");
+        }
+        LayerPositionStrategy positionStrategy = layer.getPositionStrategy();
+        int position = positionStrategy.getPosition(this);
+        checkPosition(position);
+        insertLayerAt(layer, position);
+        fireLayerAdded(layer);
+    }
+
+    /**
+     * Remove the layer from the mapview. If the layer was in the list before,
+     * an LayerChange event is fired.
+     * @param layer The layer to remove
+     */
+    public synchronized void removeLayer(Layer layer) {
+        checkContainsLayer(layer);
+
+        fireLayerRemoving(layer);
+        layers.remove(layer);
+    }
+
+    /**
+     * Move a layer to a new position.
+     * @param layer The layer to move.
+     * @param position The position.
+     * @throws IndexOutOfBoundsException if the position is out of bounds.
+     */
+    public synchronized void moveLayer(Layer layer, int position) {
+        checkContainsLayer(layer);
+        checkPosition(position);
+
+        int curLayerPos = layers.indexOf(layer);
+        if (position == curLayerPos)
+            return; // already in place.
+        layers.remove(curLayerPos);
+        insertLayerAt(layer, position);
+        fireLayerOrderChanged();
+    }
+
+    private void insertLayerAt(Layer layer, int position) {
+        if (position == layers.size()) {
+            layers.add(layer);
+        } else {
+            layers.add(position, layer);
+        }
+    }
+
+    private void checkPosition(int position) {
+        if (position < 0 || position > layers.size()) {
+            throw new IndexOutOfBoundsException("Position " + position + " out of range.");
+        }
+    }
+
+    /**
+     * Gets an unmodifiable list of all layers that are currently in this manager. This list won't update once layers are added or removed.
+     * @return The list of layers.
+     */
+    public List<Layer> getLayers() {
+        return Collections.unmodifiableList(new ArrayList<>(layers));
+    }
+
+    /**
+     * Replies an unmodifiable list of layers of a certain type.
+     *
+     * Example:
+     * <pre>
+     *     List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
+     * </pre>
+     *
+     * @param ofType The layer type.
+     * @return an unmodifiable list of layers of a certain type.
+     */
+    public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
+        return new ArrayList<>(Utils.filteredCollection(getLayers(), ofType));
+    }
+
+    /**
+     * replies true if the list of layers managed by this map view contain layer
+     *
+     * @param layer the layer
+     * @return true if the list of layers managed by this map view contain layer
+     */
+    public synchronized boolean containsLayer(Layer layer) {
+        return layers.contains(layer);
+    }
+
+    protected void checkContainsLayer(Layer layer) {
+        if (!containsLayer(layer)) {
+            throw new IllegalArgumentException(layer + " is not managed by us.");
+        }
+    }
+
+    /**
+     * Adds a layer change listener
+     *
+     * @param listener the listener.
+     */
+    public synchronized void addLayerChangeListener(LayerChangeListener listener) {
+        addLayerChangeListener(listener, false);
+    }
+
+    /**
+     * Adds a layer change listener
+     *
+     * @param listener the listener.
+     * @param fireAdd if we should fire an add event for every layer in this manager.
+     */
+    public synchronized void addLayerChangeListener(LayerChangeListener listener, boolean fireAdd) {
+        if (layerChangeListeners.contains(listener)) {
+            throw new IllegalArgumentException("Listener already registered.");
+        }
+        layerChangeListeners.add(listener);
+        if (fireAdd) {
+            for (Layer l : getLayers()) {
+                listener.layerAdded(new LayerAddEvent(this, l));
+            }
+        }
+    }
+
+    /**
+     * Removes a layer change listener
+     *
+     * @param listener the listener. Ignored if null or already registered.
+     */
+    public synchronized void removeLayerChangeListener(LayerChangeListener listener) {
+        removeLayerChangeListener(listener, false);
+    }
+
+
+    /**
+     * Removes a layer change listener
+     *
+     * @param listener the listener.
+     * @param fireRemove if we should fire a remove event for every layer in this manager.
+     */
+    public synchronized void removeLayerChangeListener(LayerChangeListener listener, boolean fireRemove) {
+        if (!layerChangeListeners.remove(listener)) {
+            //throw new IllegalArgumentException("Listener was not registered before: " + listener);
+            Main.error("Listener was not registered before: " + listener);
+        } else {
+            if (fireRemove) {
+                for (Layer l : getLayers()) {
+                    listener.layerRemoving(new LayerRemoveEvent(this, l));
+                }
+            }
+        }
+    }
+
+    private void fireLayerAdded(Layer layer) {
+        LayerAddEvent e = new LayerAddEvent(this, layer);
+        for (LayerChangeListener l : layerChangeListeners) {
+            l.layerAdded(e);
+        }
+    }
+
+    private void fireLayerRemoving(Layer layer) {
+        LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
+        for (LayerChangeListener l : layerChangeListeners) {
+            l.layerRemoving(e);
+        }
+    }
+
+    private void fireLayerOrderChanged() {
+        LayerOrderChangeEvent e = new LayerOrderChangeEvent(this);
+        for (LayerChangeListener l : layerChangeListeners) {
+            l.layerOrderChanged(e);
+        }
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java b/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java
new file mode 100644
index 0000000..2f6aafe
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java
@@ -0,0 +1,253 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.openstreetmap.josm.data.osm.DataSet;
+
+/**
+ * This class extends the layer manager by adding an active and an edit layer.
+ * <p>
+ * The active layer is the layer the user is currently working on.
+ * <p>
+ * The edit layer is an data layer that we currently work with.
+ * @author Michael Zangl
+ */
+public class LayerManagerWithActive extends LayerManager {
+    /**
+     * This listener listens to changes of the active or the edit layer.
+     * @author Michael Zangl
+     *
+     */
+    public interface ActiveLayerChangeListener {
+        /**
+         * Called whenever the active or edit layer changed.
+         * <p>
+         * You can be sure that this layer is still contained in this set.
+         * @param e The change event.
+         */
+        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
+    }
+
+    /**
+     * This event is fired whenever the active or the edit layer changes.
+     * @author Michael Zangl
+     */
+    public class ActiveLayerChangeEvent extends LayerManagerEvent {
+
+        private final OsmDataLayer previousEditLayer;
+
+        private final Layer previousActiveLayer;
+
+        /**
+         * Create a new {@link ActiveLayerChangeEvent}
+         * @param source The source
+         * @param previousEditLayer the previous edit layer
+         * @param previousActiveLayer the previous active layer
+         */
+        ActiveLayerChangeEvent(LayerManagerWithActive source, OsmDataLayer previousEditLayer,
+                Layer previousActiveLayer) {
+            super(source);
+            this.previousEditLayer = previousEditLayer;
+            this.previousActiveLayer = previousActiveLayer;
+        }
+
+        /**
+         * Gets the edit layer that was previously used.
+         * @return The old edit layer, <code>null</code> if there is none.
+         */
+        public OsmDataLayer getPreviousEditLayer() {
+            return previousEditLayer;
+        }
+
+        /**
+         * Gets the active layer that was previously used.
+         * @return The old active layer, <code>null</code> if there is none.
+         */
+        public Layer getPreviousActiveLayer() {
+            return previousActiveLayer;
+        }
+
+        @Override
+        public LayerManagerWithActive getSource() {
+            return (LayerManagerWithActive) super.getSource();
+        }
+    }
+
+    private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
+
+    /**
+     * Adds a active/edit layer change listener
+     *
+     * @param listener the listener.
+     * @param initialFire fire a fake active-layer-changed-event right after adding
+     * the listener. The previous layers will be null.
+     */
+    public void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) {
+        activeLayerChangeListeners.add(listener);
+        if (initialFire) {
+            listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
+        }
+    }
+
+    /**
+     * Removes an active/edit layer change listener.
+     * @param listener the listener.
+     */
+    public void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
+        activeLayerChangeListeners.remove(listener);
+    }
+    /**
+     * The layer from the layers list that is currently active.
+     */
+    private Layer activeLayer;
+
+    /**
+     * The edit layer is the current active data layer.
+     */
+    private OsmDataLayer editLayer;
+
+    /**
+     * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed.
+     * @param layer The active layer.
+     */
+    public synchronized void setActiveLayer(Layer layer) {
+        checkContainsLayer(layer);
+        setActiveLayer(layer, false);
+    }
+
+    private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
+        ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
+        activeLayer = layer;
+        if (activeLayer instanceof OsmDataLayer) {
+            editLayer = (OsmDataLayer) activeLayer;
+        } else if (forceEditLayerUpdate) {
+            editLayer = null;
+        }
+        fireActiveLayerChange(event);
+    }
+
+    private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
+        if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
+            for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
+                l.activeOrEditLayerChanged(event);
+            }
+        }
+    }
+
+    @Override
+    public synchronized void addLayer(Layer layer) {
+        super.addLayer(layer);
+
+        // update the active layer automatically.
+        if (layer instanceof OsmDataLayer || activeLayer == null) {
+            setActiveLayer(layer);
+        }
+    }
+
+    @Override
+    public synchronized void removeLayer(Layer layer) {
+        if (layer == activeLayer || layer == editLayer) {
+            Layer nextActive = suggestNextActiveLayer(layer);
+            setActiveLayer(nextActive, true);
+        }
+
+        super.removeLayer(layer);
+    }
+
+    /**
+     * Determines the next active data layer according to the following
+     * rules:
+     * <ul>
+     *   <li>if there is at least one {@link OsmDataLayer} the first one
+     *     becomes active</li>
+     *   <li>otherwise, the top most layer of any type becomes active</li>
+     * </ul>
+     *
+     * @param except A layer to ignore.
+     * @return the next active data layer
+     */
+    private Layer suggestNextActiveLayer(Layer except) {
+        List<Layer> layersList = new ArrayList<>(getLayers());
+        layersList.remove(except);
+        // First look for data layer
+        for (Layer layer : layersList) {
+            if (layer instanceof OsmDataLayer)
+                return layer;
+        }
+
+        // Then any layer
+        if (!layersList.isEmpty())
+            return layersList.get(0);
+
+        // and then give up
+        return null;
+    }
+
+    /**
+     * Replies the currently active layer
+     *
+     * @return the currently active layer (may be null)
+     */
+    public synchronized Layer getActiveLayer() {
+        return activeLayer;
+    }
+
+    /**
+     * Replies the current edit layer, if any
+     *
+     * @return the current edit layer. May be null.
+     */
+    public synchronized OsmDataLayer getEditLayer() {
+        return editLayer;
+    }
+
+    /**
+     * Gets the data set of the active edit layer.
+     * @return That data set, <code>null</code> if there is no edit layer.
+     */
+    public synchronized DataSet getEditDataSet() {
+        return editLayer != null ? editLayer.data : null;
+    }
+
+
+    /**
+     * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
+     * first, layer with the highest Z-Order last.
+     * <p>
+     * The active data layer is pulled above all adjacent data layers.
+     *
+     * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
+     * first, layer with the highest Z-Order last.
+     */
+    public synchronized List<Layer> getVisibleLayersInZOrder() {
+        List<Layer> ret = new ArrayList<>();
+        // This is set while we delay the addition of the active layer.
+        boolean activeLayerDelayed = false;
+        List<Layer> layers = getLayers();
+        for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
+            Layer l = iterator.previous();
+            if (!l.isVisible()) {
+                // ignored
+            } else if (l == activeLayer && l instanceof OsmDataLayer) {
+                // delay and add after the current block of OsmDataLayer
+                activeLayerDelayed = true;
+            } else {
+                if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
+                    // add active layer before the current one.
+                    ret.add(activeLayer);
+                    activeLayerDelayed = false;
+                }
+                // Add this layer now
+                ret.add(l);
+            }
+        }
+        if (activeLayerDelayed) {
+            ret.add(activeLayer);
+        }
+        return ret;
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java b/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java
new file mode 100644
index 0000000..73d18e3
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java
@@ -0,0 +1,60 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import java.util.List;
+
+/**
+ * This class defines a position to insert a given layer in the list of layers.
+ * @author Michael Zangl
+ */
+public abstract class LayerPositionStrategy {
+
+    /**
+     * always inserts at the front of the stack.
+     */
+    public static final LayerPositionStrategy IN_FRONT = new LayerPositionStrategy() {
+        @Override
+        public int getPosition(LayerManager manager) {
+            return 0;
+        }
+    };
+
+    /**
+     * A GPX layer is added below the lowest data layer.
+     */
+    public static final LayerPositionStrategy AFTER_LAST_DATA_LAYER = new LayerPositionStrategy() {
+        @Override
+        public int getPosition(LayerManager manager) {
+            List<Layer> layers = manager.getLayers();
+            for (int i = layers.size() - 1; i >= 0; i--) {
+                if (layers.get(i) instanceof OsmDataLayer) {
+                    return i + 1;
+                }
+            }
+            return 0;
+        }
+    };
+
+    /**
+     * The default for background layers: They are added before the first background layer in the list. If there is n one, they are added at the end of the list.
+     */
+    public static final LayerPositionStrategy BEFORE_FIRST_BACKGROUND_LAYER = new LayerPositionStrategy() {
+        @Override
+        public int getPosition(LayerManager manager) {
+            List<Layer> layers = manager.getLayers();
+            for (int i = 0; i < layers.size(); i++) {
+                if (layers.get(i).isBackgroundLayer()) {
+                    return i;
+                }
+            }
+            return layers.size();
+        }
+    };
+
+    /**
+     * Gets the position where the layer should be inserted
+     * @param manager The layer manager to insert the layer in.
+     * @return The position in the range 0...layers.size
+     */
+    public abstract int getPosition(LayerManager manager);
+}
diff --git a/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java b/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java
index 82eb98b..f833384 100644
--- a/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java
+++ b/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java
@@ -49,7 +49,7 @@ public class SelectActionTest {
                 };
 
         MapViewMock(DataSet dataSet, OsmDataLayer layer) {
-            super(null, null);
+            super(Main.layerManager, null, null);
             this.layer = layer;
             this.currentDataSet = dataSet;
         }
