diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
index 16e892c..345914b 100644
--- a/src/org/openstreetmap/josm/Main.java
+++ b/src/org/openstreetmap/josm/Main.java
@@ -86,12 +86,12 @@ import org.openstreetmap.josm.gui.MainApplication.Option;
 import org.openstreetmap.josm.gui.MainMenu;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.MapFrameListener;
-import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 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;
@@ -189,10 +189,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.
+     */
+    private static final LayerManagerWithActive layerManager = new LayerManagerWithActive();
+
+    /**
      * The toolbar preference control to register new actions.
      */
     public static volatile ToolbarPreferences toolbar;
@@ -530,8 +537,8 @@ public abstract class Main {
      */
     public final synchronized void removeLayer(final Layer layer) {
         if (map != null) {
-            map.mapView.removeLayer(layer);
-            if (isDisplayingMapView() && map.mapView.getAllLayers().isEmpty()) {
+            getLayerManager().removeLayer(layer);
+            if (isDisplayingMapView() && getLayerManager().getLayers().isEmpty()) {
                 setMapFrame(null);
             }
         }
@@ -607,7 +614,7 @@ public abstract class Main {
             @Override
             public void initialize() {
                 validator = new OsmValidator();
-                MapView.addLayerChangeListener(validator);
+                getLayerManager().addLayerChangeListener(validator);
             }
         });
 
@@ -729,6 +736,14 @@ public abstract class Main {
     }
 
     /**
+     * Returns the main layer manager that is used by the map view.
+     * @return The layer manager. The value returned will never change.
+     */
+    public static LayerManagerWithActive getLayerManager() {
+        return layerManager;
+    }
+
+    /**
      * Add a new layer to the map.
      *
      * If no map exists, create one.
@@ -772,7 +787,7 @@ public abstract class Main {
             createMapFrame(layer, viewport);
         }
         layer.hookUpMapView();
-        map.mapView.addLayer(layer);
+        getLayerManager().addLayer(layer);
         if (noMap) {
             Main.map.setVisible(true);
         } else if (viewport != null) {
@@ -812,7 +827,7 @@ public abstract class Main {
      */
     public OsmDataLayer getEditLayer() {
         if (!isDisplayingMapView()) return null;
-        return map.mapView.getEditLayer();
+        return getLayerManager().getEditLayer();
     }
 
     /**
@@ -851,7 +866,7 @@ public abstract class Main {
      */
     public Layer getActiveLayer() {
         if (!isDisplayingMapView()) return null;
-        return map.mapView.getActiveLayer();
+        return getLayerManager().getActiveLayer();
     }
 
     protected static final JPanel contentPanePrivate = new JPanel(new BorderLayout());
@@ -1032,7 +1047,7 @@ public abstract class Main {
      */
     public static boolean saveUnsavedModifications() {
         if (!isDisplayingMapView()) return true;
-        return saveUnsavedModifications(map.mapView.getLayersOfType(AbstractModifiableLayer.class), true);
+        return saveUnsavedModifications(getLayerManager().getLayersOfType(AbstractModifiableLayer.class), true);
     }
 
     /**
@@ -1099,7 +1114,7 @@ public abstract class Main {
             pref.put("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0);
             // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
             if (Main.isDisplayingMapView()) {
-                Collection<Layer> layers = new ArrayList<>(Main.map.mapView.getAllLayers());
+                Collection<Layer> layers = new ArrayList<>(getLayerManager().getLayers());
                 for (Layer l: layers) {
                     Main.main.removeLayer(l);
                 }
diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
index 18ecda2..d640398 100644
--- a/src/org/openstreetmap/josm/data/validation/OsmValidator.java
+++ b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
@@ -58,8 +58,10 @@ import org.openstreetmap.josm.data.validation.tests.UntaggedNode;
 import org.openstreetmap.josm.data.validation.tests.UntaggedWay;
 import org.openstreetmap.josm.data.validation.tests.WayConnectedToArea;
 import org.openstreetmap.josm.data.validation.tests.WronglyOrderedWays;
-import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
-import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.ValidatorLayer;
 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
@@ -338,22 +340,22 @@ public class OsmValidator implements LayerChangeListener {
     /* interface LayerChangeListener                                              */
     /* -------------------------------------------------------------------------- */
     @Override
-    public void activeLayerChange(Layer oldLayer, Layer newLayer) {
-        // Do nothing
+    public void layerAdded(LayerAddEvent e) {
+        // do nothing
     }
 
     @Override
-    public void layerAdded(Layer newLayer) {
-        // Do nothing
+    public void layerOrderChanged(LayerOrderChangeEvent e) {
+        // do nothing
     }
 
     @Override
-    public void layerRemoved(Layer oldLayer) {
-        if (oldLayer == errorLayer) {
+    public void layerRemoving(LayerRemoveEvent e) {
+        if (e.getRemovedLayer() == errorLayer) {
             errorLayer = null;
             return;
         }
-        if (Main.map.mapView.getLayersOfType(OsmDataLayer.class).isEmpty()) {
+        if (e.getSource().getLayersOfType(OsmDataLayer.class).isEmpty()) {
             if (errorLayer != null) {
                 Main.main.removeLayer(errorLayer);
             }
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index 9a84eb7..6c0e557 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;
 
@@ -189,7 +192,7 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
         setSize(400, 400);
         setLayout(new BorderLayout());
 
-        mapView = new MapView(contentPane, viewportData);
+        mapView = new MapView(Main.getLayerManager(), contentPane, viewportData);
         if (!GraphicsEnvironment.isHeadless()) {
             new FileDrop(mapView);
         }
@@ -774,7 +777,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 dceb83c..580ec60 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;
 
@@ -58,11 +56,16 @@ import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
 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.LayerPositionStrategy;
+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.MapViewPaintable.PaintableInvalidationEvent;
 import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
-import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
@@ -86,12 +89,15 @@ import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
  * @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.
+     * <p>
+     * To be removed: end of 2016. Use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener} instead.
      * @author imi
      */
+    @Deprecated
     public interface LayerChangeListener {
 
         /**
@@ -116,7 +122,10 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
 
     /**
      * An interface that needs to be implemented in order to listen for changes to the active edit layer.
+     * <p>
+     * To be removed: end of 2016. Use {@link ActiveLayerChangeListener} instead.
      */
+    @Deprecated
     public interface EditLayerChangeListener {
 
         /**
@@ -171,111 +180,243 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
         }
     }
 
-    public boolean viewportFollowing;
+    /**
+     * This class is an adapter for the old layer change interface.
+     * <p>
+     * New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener}
+     * @author Michael Zangl
+     */
+    protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener {
+
+        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 + "]";
+        }
+
+    }
 
     /**
-     * the layer listeners
+     * This class is an adapter for the old layer change interface.
+     * <p>
+     * New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener}
+     * @author Michael Zangl
      */
-    private static final CopyOnWriteArrayList<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
-    private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<>();
+    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
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      *
-     * @param listener the listener. Ignored if null or already registered.
+     * @param listener the listener. Ignored if null or not registered.
      */
+    @Deprecated
     public static void removeLayerChangeListener(LayerChangeListener listener) {
-        layerChangeListeners.remove(listener);
+        LayerChangeAdapter adapter = new LayerChangeAdapter(listener);
+        try {
+            Main.getLayerManager().removeLayerChangeListener(adapter);
+        } catch (IllegalArgumentException e) {
+            // Ignored in old implementation
+        }
+        try {
+            Main.getLayerManager().removeActiveLayerChangeListener(adapter);
+        } catch (IllegalArgumentException e) {
+            // Ignored in old implementation
+        }
     }
 
+    /**
+     * Removes an edit layer change listener
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
+     *
+     * @param listener the listener. Ignored if null or not registered.
+     */
+    @Deprecated
     public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
-        editLayerChangeListeners.remove(listener);
+        try {
+            Main.getLayerManager().removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
+        } catch (IllegalArgumentException e) {
+            // Ignored in old implementation
+        }
     }
 
     /**
      * Adds a layer change listener
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      *
      * @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);
     }
 
     /**
      * Adds a layer change listener
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      *
      * @param listener the listener. Ignored if null or already registered.
      * @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.getLayerManager().addLayerChangeListener(adapter, false);
+            Main.getLayerManager().addActiveLayerChangeListener(adapter, initialFire);
+            adapter.receiveOneInitialFire = false;
         }
     }
 
     /**
      * Adds an edit layer change listener
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      *
      * @param listener the listener. Ignored if null or already registered.
      * @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.getLayerManager().addActiveLayerChangeListener(new EditLayerChangeAdapter(listener), initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null);
         }
     }
 
     /**
      * Adds an edit layer change listener
+     * <p>
+     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      *
      * @param listener the listener. Ignored if null or already registered.
      */
+    @Deprecated
     public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
-        if (listener != null) {
-            editLayerChangeListeners.addIfAbsent(listener);
-        }
+        addEditLayerChangeListener(listener, false);
     }
 
-    /**
-     * 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);
-        }
-    }
-
-    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.
+     * A list of all layers currently loaded. If we support multiple map views, this list may be different for each of them.
      */
-    private final transient List<Layer> layers = new ArrayList<>();
+    private final LayerManagerWithActive layerManager;
 
     /**
      * The play head marker: there is only one of these so it isn't in any specific layer
@@ -283,16 +424,6 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     public transient PlayHeadMarker playHeadMarker;
 
     /**
-     * The layer from the layers list that is currently active.
-     */
-    private transient Layer activeLayer;
-
-    /**
-     * The edit layer is the current active data layer.
-     */
-    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
@@ -321,13 +452,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() {
@@ -404,84 +539,43 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     }
 
     /**
-     * Add a layer to the current MapView. The layer will be added at topmost
-     * position.
+     * Add a layer to the current MapView.
+     * <p>
+     * Use {@link Main#getLayerManager()}.addLayer() instead. To be removed: end of 2016.
      * @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();
-            }
-
-            LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition();
-            int position = positionStrategy.getPosition(this);
-            checkPosition(position);
-            insertLayerAt(layer, position);
-
-            if (isOsmDataLayer || oldActiveLayer == null) {
-                // autoselect the new layer
-                listenersToFire.addAll(setActiveLayer(layer, true));
-            }
-
-            if (isOsmDataLayer) {
-                ((OsmDataLayer) layer).addLayerStateChangeListener(this);
-            }
-
-            if (layer instanceof NativeScaleLayer) {
-                Main.map.mapView.setNativeScaleLayer((NativeScaleLayer) layer);
-            }
+        layerManager.addLayer(layer);
+    }
 
-            layer.addPropertyChangeListener(this);
-            invalidatedListener.addTo(layer);
-            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);
         }
-    }
 
-    /**
-     * Check if the (new) position is valid
-     * @param position The position index
-     * @throws IndexOutOfBoundsException if it is not.
-     */
-    private void checkPosition(int position) {
-        if (position < 0 || position > layers.size()) {
-            throw new IndexOutOfBoundsException("Position " + position + " out of range.");
-        }
+        layer.addPropertyChangeListener(this);
+        Main.addProjectionChangeListener(layer);
+        invalidatedListener.addTo(layer);
+        AudioPlayer.reset();
+
+        repaint();
     }
 
     /**
-     * Insert a layer at a given position.
-     * @param layer The layer to add.
-     * @param position The position on which we should add it.
+     * Use {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      */
-    private void insertLayerAt(Layer layer, int position) {
-        if (position == layers.size()) {
-            layers.add(layer);
-        } else {
-            layers.add(position, layer);
-        }
-    }
-
     @Override
+    @Deprecated
     protected DataSet getCurrentDataSet() {
-        synchronized (layers) {
-            if (editLayer != null)
-                return editLayer.data;
-            else
-                return null;
-        }
+        return layerManager.getEditDataSet();
     }
 
     /**
@@ -490,9 +584,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;
     }
 
     /**
@@ -501,9 +593,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();
-        }
+        OsmDataLayer e = getEditLayer();
+        return e != null && e.isVisible();
     }
 
     /**
@@ -514,10 +605,12 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
      *     becomes active</li>
      *   <li>otherwise, the top most layer of any type becomes active</li>
      * </ul>
+     * To be removed: end of 2016 (now handled by {@link LayerManagerWithActive}
      * @param layersList lit of layers
      *
      * @return the next active data layer
      */
+    @Deprecated
     protected Layer determineNextActiveLayer(List<Layer> layersList) {
         // First look for data layer
         for (Layer layer:layersList) {
@@ -531,51 +624,34 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
 
         // and then give up
         return null;
-
     }
 
     /**
      * Remove the layer from the mapview. If the layer was in the list before,
      * an LayerChange event is fired.
+     * <p>
+     * Use {@link Main#getLayerManager()}.removeLayer() instead. To be removed: end of 2016.
      * @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);
-            invalidatedListener.removeFrom(layer);
-            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);
+        invalidatedListener.removeFrom(layer);
+        layer.destroy();
+        AudioPlayer.reset();
 
-    private void onEditLayerChanged(OsmDataLayer oldEditLayer) {
-        fireEditLayerChanged(oldEditLayer, editLayer);
-        refreshTitle();
+        repaint();
     }
 
     private boolean virtualNodesEnabled;
@@ -604,84 +680,31 @@ 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();
     }
 
     /**
      * Gets the index of the layer in the layer list.
+     * <p>
+     * Access the layer list using {@link Main#getLayerManager()} instead. To be removed: end of 2016.
      * @param layer The layer to search for.
      * @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);
-        }
+        int curLayerPos = layerManager.getLayers().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) {
             g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
@@ -699,7 +722,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
             return;
         }
 
-        List<Layer> visibleLayers = getVisibleLayersInZOrder();
+        List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
 
         int nonChangedLayersCount = 0;
         for (Layer l: visibleLayers) {
@@ -892,186 +915,102 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
     }
 
     /**
+     * Use {@link LayerManager#getLayers()} instead. To be removed: end of 2016.
+     *
      * @return An unmodifiable collection of all layers
      */
+    @Deprecated
     public Collection<Layer> getAllLayers() {
-        synchronized (layers) {
-            return Collections.unmodifiableCollection(new ArrayList<>(layers));
-        }
+        return layerManager.getLayers();
     }
 
     /**
+     * Use {@link LayerManager#getLayers()} instead. To be removed: end of 2016.
+     *
      * @return An unmodifiable ordered list of all layers
      */
+    @Deprecated
     public List<Layer> getAllLayersAsList() {
-        synchronized (layers) {
-            return Collections.unmodifiableList(new ArrayList<>(layers));
-        }
+        return layerManager.getLayers();
     }
 
     /**
-     * Replies an unmodifiable list of layers of a certain type.
+     * Replies an unmodifiable list of layers of a certain type. To be removed: end of 2016.
      *
      * Example:
      * <pre>
      *     List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
      * </pre>
+     * Use {@link LayerManager#getLayersOfType(Class)} instead.
+     *
      * @param <T> layer type
      *
      * @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);
     }
 
     /**
-     * Replies the number of layers managed by this map view
+     * Replies the number of layers managed by this map view. To be removed: end of 2016.
+     * <p>
+     * You can use {@link Main#getLayerManager()}.getLayers().size() instead.
      *
      * @return the number of layers managed by this map view
      */
+    @Deprecated
     public int getNumLayers() {
-        synchronized (layers) {
-            return layers.size();
-        }
+        return getAllLayers().size();
     }
 
     /**
      * Replies true if there is at least one layer in this map view
+     * <p>
+     * You can use !{@link Main#getLayerManager()}.getLayers().isEmpty() instead.
      *
      * @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>
-     * @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>.
+     * of {@link OsmDataLayer} also sets editLayer to <code>layer</code>.
+     * <p>
+     * You can use !{@link Main#getLayerManager()}.setActiveLayer() instead.
      *
      * @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 list 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
+     * <p>
+     * You can use !{@link Main#getLayerManager()}.getActiveLayer() instead.
      *
      * @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 {
@@ -1084,30 +1023,33 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
             });
         }
         AudioPlayer.reset();
+        refreshTitle();
         repaint();
     }
 
     /**
      * Replies the current edit layer, if any
+     * <p>
+     * You can use !{@link Main#getLayerManager()}.getEditLayer() instead. To be made private: end of 2016.
      *
      * @return the current edit layer. May be null.
      */
+    @Deprecated
     public OsmDataLayer getEditLayer() {
-        synchronized (layers) {
-            return editLayer;
-        }
+        return layerManager.getEditLayer();
     }
 
     /**
      * replies true if the list of layers managed by this map view contain layer
+     * <p>
+     * You can use !{@link Main#getLayerManager()}.containsLayer() instead.
      *
      * @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);
     }
 
     /**
@@ -1179,12 +1121,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);
         }
     }
 
@@ -1203,19 +1144,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();
         }
@@ -1223,7 +1160,7 @@ implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer
 
     @Override
     public void uploadDiscouragedChanged(OsmDataLayer layer, boolean newValue) {
-        if (layer == getEditLayer()) {
+        if (layer == layerManager.getEditLayer()) {
             refreshTitle();
         }
     }
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..2040ff2
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
@@ -0,0 +1,346 @@
+// 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.gui.util.GuiHelper;
+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.
+         * <p>
+         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
+         * @param e The new added layer event
+         */
+        void layerAdded(LayerAddEvent e);
+
+        /**
+         * Notifies this listener that a layer is about to be removed.
+         * <p>
+         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
+         * @param e The layer to be removed (as event)
+         */
+        void layerRemoving(LayerRemoveEvent e);
+
+        /**
+         * Notifies this listener that the order of layers was changed.
+         * <p>
+         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
+         * @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.
+     * <p>
+     * We currently do not report the exact changes.
+     * @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 void addLayer(final Layer layer) {
+        // we force this on to the EDT Thread to make events fire from there.
+        // The synchronization lock needs to be held by the EDT.
+        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
+            @Override
+            public void run() {
+                realAddLayer(layer);
+            }
+        });
+    }
+
+    protected synchronized void realAddLayer(Layer layer) {
+        if (containsLayer(layer)) {
+            throw new IllegalArgumentException("Cannot add a layer twice.");
+        }
+        LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition();
+        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 void removeLayer(final Layer layer) {
+        // we force this on to the EDT Thread to make events fire from there.
+        // The synchronization lock needs to be held by the EDT.
+        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
+            @Override
+            public void run() {
+                realRemoveLayer(layer);
+            }
+        });
+    }
+
+    protected synchronized void realRemoveLayer(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 void moveLayer(final Layer layer, final int position) {
+        // we force this on to the EDT Thread to make events fire from there.
+        // The synchronization lock needs to be held by the EDT.
+        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
+            @Override
+            public void run() {
+                realMoveLayer(layer, position);
+            }
+        });
+    }
+
+    protected synchronized void realMoveLayer(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();
+    }
+
+    /**
+     * Insert a layer at a given position.
+     * @param layer The layer to add.
+     * @param position The position on which we should add it.
+     */
+    private void insertLayerAt(Layer layer, int position) {
+        if (position == layers.size()) {
+            layers.add(layer);
+        } else {
+            layers.add(position, layer);
+        }
+    }
+
+    /**
+     * Check if the (new) position is valid
+     * @param position The position index
+     * @throws IndexOutOfBoundsException if it is not.
+     */
+    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.
+     * @throws IllegalArgumentException If the listener was added twice.
+     */
+    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.
+     * @throws IllegalArgumentException If the listener was added twice.
+     */
+    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);
+        } else {
+            if (fireRemove) {
+                for (Layer l : getLayers()) {
+                    listener.layerRemoving(new LayerRemoveEvent(this, l));
+                }
+            }
+        }
+    }
+
+    private void fireLayerAdded(Layer layer) {
+        GuiHelper.assertCallFromEdt();
+        LayerAddEvent e = new LayerAddEvent(this, layer);
+        for (LayerChangeListener l : layerChangeListeners) {
+            l.layerAdded(e);
+        }
+    }
+
+    private void fireLayerRemoving(Layer layer) {
+        GuiHelper.assertCallFromEdt();
+        LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
+        for (LayerChangeListener l : layerChangeListeners) {
+            l.layerRemoving(e);
+        }
+    }
+
+    private void fireLayerOrderChanged() {
+        GuiHelper.assertCallFromEdt();
+        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..04f5258
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java
@@ -0,0 +1,281 @@
+// 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;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+
+/**
+ * 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.
+         * <p>
+         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
+         * @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();
+        }
+    }
+
+    /**
+     * 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;
+
+    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. The listener is notified in the current thread.
+     */
+    public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) {
+        if (activeLayerChangeListeners.contains(listener)) {
+            throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
+        }
+        activeLayerChangeListeners.add(listener);
+        if (initialFire) {
+            listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
+        }
+    }
+
+    /**
+     * Removes an active/edit layer change listener.
+     * @param listener the listener.
+     */
+    public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
+        if (!activeLayerChangeListeners.contains(listener)) {
+            throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
+        }
+        activeLayerChangeListeners.remove(listener);
+    }
+
+    /**
+     * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed.
+     * @param layer The active layer.
+     */
+    public void setActiveLayer(final Layer layer) {
+        // we force this on to the EDT Thread to make events fire from there.
+        // The synchronization lock needs to be held by the EDT.
+        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
+            @Override
+            public void run() {
+                realSetActiveLayer(layer);
+            }
+        });
+    }
+
+    protected synchronized void realSetActiveLayer(final Layer layer) {
+        // to be called in EDT thread
+        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) {
+        GuiHelper.assertCallFromEdt();
+        if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
+            for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
+                l.activeOrEditLayerChanged(event);
+            }
+        }
+    }
+
+    @Override
+    protected synchronized void realAddLayer(Layer layer) {
+        super.realAddLayer(layer);
+
+        // update the active layer automatically.
+        if (layer instanceof OsmDataLayer || activeLayer == null) {
+            setActiveLayer(layer);
+        }
+    }
+
+    @Override
+    protected synchronized void realRemoveLayer(Layer layer) {
+        if (layer == activeLayer || layer == editLayer) {
+            Layer nextActive = suggestNextActiveLayer(layer);
+            setActiveLayer(nextActive, true);
+        }
+
+        super.realRemoveLayer(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() {
+        if (editLayer != null) {
+            return editLayer.data;
+        } else {
+            return 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
index 46d88d4..d577e30 100644
--- a/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java
+++ b/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java
@@ -3,7 +3,6 @@ package org.openstreetmap.josm.gui.layer;
 
 import java.util.List;
 
-import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.Predicates;
 
@@ -19,7 +18,7 @@ public abstract class LayerPositionStrategy {
      */
     public static final LayerPositionStrategy IN_FRONT = new LayerPositionStrategy() {
         @Override
-        public int getPosition(MapView manager) {
+        public int getPosition(LayerManager manager) {
             return 0;
         }
     };
@@ -72,8 +71,8 @@ public abstract class LayerPositionStrategy {
     public static LayerPositionStrategy inFrontOfFirst(final Predicate<Layer> what) {
         return new LayerPositionStrategy() {
             @Override
-            public int getPosition(MapView manager) {
-                List<Layer> layers = manager.getAllLayersAsList();
+            public int getPosition(LayerManager manager) {
+                List<Layer> layers = manager.getLayers();
                 for (int i = 0; i < layers.size(); i++) {
                     if (what.evaluate(layers.get(i))) {
                         return i;
@@ -92,8 +91,8 @@ public abstract class LayerPositionStrategy {
     public static LayerPositionStrategy afterLast(final Predicate<Layer> what) {
         return new LayerPositionStrategy() {
             @Override
-            public int getPosition(MapView manager) {
-                List<Layer> layers = manager.getAllLayersAsList();
+            public int getPosition(LayerManager manager) {
+                List<Layer> layers = manager.getLayers();
                 for (int i = layers.size() - 1; i >= 0; i--) {
                     if (what.evaluate(layers.get(i))) {
                         return i + 1;
@@ -109,5 +108,5 @@ public abstract class LayerPositionStrategy {
      * @param manager The layer manager to insert the layer in.
      * @return The position in the range 0...layers.size
      */
-    public abstract int getPosition(MapView manager);
+    public abstract int getPosition(LayerManager manager);
 }
diff --git a/src/org/openstreetmap/josm/gui/util/GuiHelper.java b/src/org/openstreetmap/josm/gui/util/GuiHelper.java
index cc87355..a627abf 100644
--- a/src/org/openstreetmap/josm/gui/util/GuiHelper.java
+++ b/src/org/openstreetmap/josm/gui/util/GuiHelper.java
@@ -130,6 +130,32 @@ public final class GuiHelper {
     }
 
     /**
+     * Executes synchronously a runnable in
+     * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html">Event Dispatch Thread</a>.
+     * <p>
+     * Passes on the exception that was thrown to the thread calling this. The exception is wrapped in a {@link RuntimeException} if it was a normal {@link Throwable}.
+     * @param task The runnable to execute
+     * @see SwingUtilities#invokeAndWait
+     */
+    public static void runInEDTAndWaitWithException(Runnable task) {
+        if (SwingUtilities.isEventDispatchThread()) {
+            task.run();
+        } else {
+            try {
+                SwingUtilities.invokeAndWait(task);
+            } catch (InterruptedException e) {
+                Main.error(e);
+            } catch (InvocationTargetException e) {
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new RuntimeException("Exception while clling " + task, e.getCause());
+                }
+            }
+        }
+    }
+
+    /**
      * Executes synchronously a callable in
      * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html">Event Dispatch Thread</a>
      * and return a value.
@@ -159,6 +185,17 @@ public final class GuiHelper {
     }
 
     /**
+     * This function fails if it was not called from the EDT thread.
+     * @throws IllegalStateException if called from wrong thread.
+     */
+    public static void assertCallFromEdt() {
+        if (!SwingUtilities.isEventDispatchThread()) {
+            throw new IllegalStateException(
+                    "Needs to be called from the EDT thread, not from " + Thread.currentThread().getName());
+        }
+    }
+
+    /**
      * Warns user about a dangerous action requiring confirmation.
      * @param title Title of dialog
      * @param content Content of dialog
diff --git a/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java b/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java
index cd0e944..a05c332 100644
--- a/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java
+++ b/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java
@@ -5,6 +5,7 @@ import java.awt.Cursor;
 import java.awt.event.MouseListener;
 import java.awt.geom.Point2D;
 
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.MapView;
@@ -16,7 +17,7 @@ class MapViewMock extends MapView {
     private final transient DataSet currentDataSet;
 
     MapViewMock(DataSet dataSet, OsmDataLayer layer) {
-        super(null, null);
+        super(Main.getLayerManager(), null, null);
         this.layer = layer;
         this.currentDataSet = dataSet;
     }
diff --git a/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java
new file mode 100644
index 0000000..f4a5139
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java
@@ -0,0 +1,431 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.awt.Graphics2D;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.swing.Action;
+import javax.swing.Icon;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.Predicates;
+
+/**
+ * Test the {@link LayerManager} class.
+ * @author Michael Zangl
+ *
+ */
+public class LayerManagerTest {
+
+    protected static class AbstractTestLayer extends Layer {
+        protected AbstractTestLayer() {
+            super("Test Layer");
+        }
+
+        @Override
+        public void paint(Graphics2D g, MapView mv, Bounds bbox) {
+        }
+
+        @Override
+        public void visitBoundingBox(BoundingXYVisitor v) {
+        }
+
+        @Override
+        public void mergeFrom(Layer from) {
+        }
+
+        @Override
+        public boolean isMergable(Layer other) {
+            return false;
+        }
+
+        @Override
+        public String getToolTipText() {
+            return null;
+        }
+
+        @Override
+        public Action[] getMenuEntries() {
+            return null;
+        }
+
+        @Override
+        public Object getInfoComponent() {
+            return null;
+        }
+
+        @Override
+        public Icon getIcon() {
+            return null;
+        }
+
+        @Override
+        public LayerPositionStrategy getDefaultLayerPosition() {
+            return LayerPositionStrategy.afterLast(Predicates.<Layer> alwaysTrue());
+        }
+    }
+
+    protected static class AbstractTestLayer2 extends AbstractTestLayer {}
+
+    /**
+     * Intercepts the events for easier testing.
+     * @author Michael Zangl
+     *
+     */
+    protected class CapturingLayerChangeListener implements LayerChangeListener {
+        private LayerAddEvent layerAdded;
+        private LayerRemoveEvent layerRemoved;
+        private LayerOrderChangeEvent layerOrderChanged;
+
+        @Override
+        public void layerAdded(LayerAddEvent e) {
+            GuiHelper.assertCallFromEdt();
+            assertNull(layerAdded);
+            assertSame(layerManager, e.getSource());
+            layerAdded = e;
+        }
+
+        @Override
+        public void layerRemoving(LayerRemoveEvent e) {
+            GuiHelper.assertCallFromEdt();
+            assertNull(layerRemoved);
+            assertSame(layerManager, e.getSource());
+            layerRemoved = e;
+        }
+
+        @Override
+        public void layerOrderChanged(LayerOrderChangeEvent e) {
+            GuiHelper.assertCallFromEdt();
+            assertNull(layerOrderChanged);
+            assertSame(layerManager, e.getSource());
+            layerOrderChanged = e;
+        }
+
+    }
+
+    protected LayerManager layerManager;
+
+    /**
+     * Set up test layer manager.
+     */
+    @Before
+    public void setUp()   {
+        layerManager = new LayerManager();
+    }
+
+    /**
+     * {@link LayerManager#addLayer(Layer)}
+     */
+    @Test
+    public void testAddLayer() {
+        Layer layer1 = new AbstractTestLayer() {
+            @Override
+            public LayerPositionStrategy getDefaultLayerPosition() {
+                return LayerPositionStrategy.IN_FRONT;
+            }
+
+            @Override
+            public boolean isBackgroundLayer() {
+                return true;
+            }
+        };
+        Layer layer2 = new AbstractTestLayer() {
+            @Override
+            public LayerPositionStrategy getDefaultLayerPosition() {
+                return LayerPositionStrategy.IN_FRONT;
+            }
+        };
+        Layer layer3 = new AbstractTestLayer() {
+            @Override
+            public LayerPositionStrategy getDefaultLayerPosition() {
+                return LayerPositionStrategy.BEFORE_FIRST_BACKGROUND_LAYER;
+            }
+        };
+
+        layerManager.addLayer(layer1);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1));
+        layerManager.addLayer(layer2);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer1));
+        layerManager.addLayer(layer3);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer3, layer1));
+
+        // event
+        AbstractTestLayer layer4 = new AbstractTestLayer();
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        layerManager.addLayer(layer4);
+
+        assertSame(layer4, l.layerAdded.getAddedLayer());
+        assertNull(l.layerRemoved);
+        assertNull(l.layerOrderChanged);
+    }
+
+    /**
+     * {@link LayerManager#addLayer(Layer)}: duplicate layers
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddLayerFails() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer1);
+    }
+
+    /**
+     * {@link LayerManager#addLayer(Layer)}: illegal default layer position
+     */
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void testAddLayerIllegalPosition() {
+        AbstractTestLayer layer1 = new AbstractTestLayer() {
+            @Override
+            public LayerPositionStrategy getDefaultLayerPosition() {
+                return new LayerPositionStrategy() {
+                    @Override
+                    public int getPosition(LayerManager manager) {
+                        return 42;
+                    }
+                };
+            }
+        };
+        layerManager.addLayer(layer1);
+    }
+
+    /**
+     * {@link LayerManager#removeLayer(Layer)}
+     */
+    @Test
+    public void testRemoveLayer() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
+
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        layerManager.removeLayer(layer2);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1));
+
+        assertNull(l.layerAdded);
+        assertSame(layer2, l.layerRemoved.getRemovedLayer());
+        assertNull(l.layerOrderChanged);
+    }
+
+    /**
+     * {@link LayerManager#moveLayer(Layer, int)}
+     */
+    @Test
+    public void testMoveLayer() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
+
+        layerManager.moveLayer(layer2, 0);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer1));
+
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        layerManager.moveLayer(layer2, 1);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
+
+        assertNull(l.layerAdded);
+        assertNull(l.layerRemoved);
+        assertNotNull(l.layerOrderChanged);
+
+        // This should not change anything and not fire any event
+        layerManager.moveLayer(layer2, 1);
+        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
+    }
+
+    /**
+     * {@link LayerManager#moveLayer(Layer, int)} fails for wrong index
+     */
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void testMoveLayerFailsRange() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        layerManager.moveLayer(layer2, 2);
+    }
+
+    /**
+     * {@link LayerManager#moveLayer(Layer, int)} fails for wrong layer
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testMoveLayerFailsNotInList() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.moveLayer(layer2, 0);
+    }
+
+    /**
+     * {@link LayerManager#getLayers()} unmodifiable
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetLayers() {
+        // list should be immutable
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        layerManager.getLayers().remove(0);
+    }
+
+    /**
+     * {@link LayerManager#getLayersOfType(Class)}
+     */
+    @Test
+    public void testGetLayersOfType() {
+        AbstractTestLayer2 layer1 = new AbstractTestLayer2();
+        AbstractTestLayer2 layer2 = new AbstractTestLayer2();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(new AbstractTestLayer());
+        layerManager.addLayer(layer2);
+
+        assertEquals(layerManager.getLayersOfType(AbstractTestLayer2.class), Arrays.asList(layer1, layer2));
+    }
+
+    /**
+     * {@link LayerManager#containsLayer(Layer)}
+     */
+    @Test
+    public void testContainsLayer() {
+        AbstractTestLayer layer = new AbstractTestLayer();
+        layerManager.addLayer(layer);
+        layerManager.addLayer(new AbstractTestLayer());
+
+        assertTrue(layerManager.containsLayer(layer));
+        assertFalse(layerManager.containsLayer(new AbstractTestLayer()));
+    }
+
+    /**
+     * {@link LayerManager#addLayerChangeListener(LayerChangeListener)}
+     */
+    @Test
+    public void testAddLayerChangeListener() {
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        assertNull(l.layerAdded);
+        assertNull(l.layerRemoved);
+        assertNull(l.layerOrderChanged);
+    }
+
+    /**
+     * {@link LayerManager#addLayerChangeListener(LayerChangeListener)} twice
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddLayerChangeListenerDupplicates() {
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        layerManager.addLayerChangeListener(l);
+    }
+
+    /**
+     * {@link LayerManager#addLayerChangeListener(LayerChangeListener, boolean)} fires fake add events
+     */
+    @Test
+    public void testAddLayerChangeListenerFire() {
+        final ArrayList<Layer> fired = new ArrayList<>();
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        layerManager.addLayerChangeListener(new LayerChangeListener() {
+            @Override
+            public void layerRemoving(LayerRemoveEvent e) {
+                fail();
+            }
+
+            @Override
+            public void layerOrderChanged(LayerOrderChangeEvent e) {
+                fail();
+            }
+
+            @Override
+            public void layerAdded(LayerAddEvent e) {
+                fired.add(e.getAddedLayer());
+            }
+        }, true);
+
+        assertEquals(Arrays.asList(layer1, layer2), fired);
+    }
+
+    /**
+     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener)}
+     */
+    @Test
+    public void testRemoveLayerChangeListener() {
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.addLayerChangeListener(l);
+        layerManager.addLayer(new AbstractTestLayer());
+        layerManager.removeLayerChangeListener(l);
+        layerManager.addLayer(new AbstractTestLayer());
+        // threw exception when fired twice.
+        assertNotNull(l.layerAdded);
+        assertNull(l.layerRemoved);
+        assertNull(l.layerOrderChanged);
+    }
+
+    /**
+     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener)} listener not in list
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRemoveLayerChangeListenerNotAdded() {
+        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
+        layerManager.removeLayerChangeListener(l);
+    }
+
+    /**
+     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener, boolean)} fires fake remove events
+     */
+    @Test
+    public void testRemoveLayerChangeListenerFire() {
+        final ArrayList<Layer> fired = new ArrayList<>();
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManager.addLayer(layer1);
+        layerManager.addLayer(layer2);
+        LayerChangeListener listener = new LayerChangeListener() {
+            @Override
+            public void layerRemoving(LayerRemoveEvent e) {
+                fired.add(e.getRemovedLayer());
+            }
+
+            @Override
+            public void layerOrderChanged(LayerOrderChangeEvent e) {
+                fail();
+            }
+
+            @Override
+            public void layerAdded(LayerAddEvent e) {
+                fail();
+            }
+        };
+        layerManager.addLayerChangeListener(listener, false);
+        layerManager.removeLayerChangeListener(listener, true);
+
+        assertEquals(Arrays.asList(layer1, layer2), fired);
+    }
+
+}
diff --git a/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java
new file mode 100644
index 0000000..cd1e8f1
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java
@@ -0,0 +1,261 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+
+import java.util.Arrays;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.tools.Predicates;
+
+/**
+ * Tests {@link LayerManagerWithActive}
+ * @author Michael Zangl
+ *
+ */
+public class LayerManagerWithActiveTest extends LayerManagerTest {
+
+    private LayerManagerWithActive layerManagerWithActive;
+
+    private class CapturingActiveLayerChangeListener implements ActiveLayerChangeListener {
+        private ActiveLayerChangeEvent lastEvent;
+
+        @Override
+        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+            assertSame(layerManager, e.getSource());
+            lastEvent = e;
+        }
+    }
+
+    private final class CapturingThreadCheckingActiveLayerChangeListener extends CapturingActiveLayerChangeListener {
+        @Override
+        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
+            GuiHelper.assertCallFromEdt();
+            super.activeOrEditLayerChanged(e);
+        }
+    }
+
+    protected class AbstractTestOsmLayer extends OsmDataLayer {
+        public AbstractTestOsmLayer() {
+            super(new DataSet(), "OSM layer", null);
+        }
+
+        @Override
+        public LayerPositionStrategy getDefaultLayerPosition() {
+            return LayerPositionStrategy.afterLast(Predicates.<Layer> alwaysTrue());
+        }
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+        JOSMFixture.createUnitTestFixture().init();
+    }
+
+    @Override
+    @Before
+    public void setUp() {
+        layerManager = layerManagerWithActive = new LayerManagerWithActive();
+    }
+
+    @Test
+    public void testAddLayerSetsActiveLayer() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        AbstractTestLayer layer3 = new AbstractTestLayer();
+        assertNull(layerManagerWithActive.getActiveLayer());
+        assertNull(layerManagerWithActive.getEditLayer());
+        layerManagerWithActive.addLayer(layer1);
+        assertSame(layer1, layerManagerWithActive.getActiveLayer());
+        assertNull(layerManagerWithActive.getEditLayer());
+        layerManagerWithActive.addLayer(layer2);
+        assertSame(layer2, layerManagerWithActive.getActiveLayer());
+        assertSame(layer2, layerManagerWithActive.getEditLayer());
+        layerManagerWithActive.addLayer(layer3);
+        assertSame(layer2, layerManagerWithActive.getActiveLayer());
+        assertSame(layer2, layerManagerWithActive.getEditLayer());
+    }
+
+    @Test
+    public void testRemoveLayerUnsetsActiveLayer() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        AbstractTestLayer layer3 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer4 = new AbstractTestOsmLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+        layerManagerWithActive.addLayer(layer3);
+        layerManagerWithActive.addLayer(layer4);
+        assertSame(layer4, layerManagerWithActive.getActiveLayer());
+        assertSame(layer4, layerManagerWithActive.getEditLayer());
+        layerManagerWithActive.removeLayer(layer4);
+        //prefer osm layers
+        assertSame(layer2, layerManagerWithActive.getActiveLayer());
+        assertSame(layer2, layerManagerWithActive.getEditLayer());
+        layerManagerWithActive.removeLayer(layer2);
+        assertSame(layer1, layerManagerWithActive.getActiveLayer());
+        assertNull(layerManagerWithActive.getEditLayer());
+
+        layerManagerWithActive.removeLayer(layer1);
+        layerManagerWithActive.removeLayer(layer3);
+        assertNull(layerManagerWithActive.getActiveLayer());
+        assertNull(layerManagerWithActive.getEditLayer());
+    }
+
+    @Test
+    public void testAddActiveLayerChangeListener() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+
+        CapturingActiveLayerChangeListener listener = new CapturingThreadCheckingActiveLayerChangeListener();
+        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
+        assertNull(listener.lastEvent);
+
+        CapturingActiveLayerChangeListener listener2 = new CapturingActiveLayerChangeListener();
+        layerManagerWithActive.addActiveLayerChangeListener(listener2, true);
+        assertSame(listener2.lastEvent.getPreviousActiveLayer(), null);
+        assertSame(listener2.lastEvent.getPreviousEditLayer(), null);
+
+        layerManagerWithActive.setActiveLayer(layer1);
+        assertSame(listener2.lastEvent.getPreviousActiveLayer(), layer2);
+        assertSame(listener2.lastEvent.getPreviousEditLayer(), layer2);
+
+        layerManagerWithActive.setActiveLayer(layer2);
+        assertSame(listener2.lastEvent.getPreviousActiveLayer(), layer1);
+        assertSame(listener2.lastEvent.getPreviousEditLayer(), layer2);
+    }
+
+    /**
+     * Test if {@link LayerManagerWithActive#addActiveLayerChangeListener(ActiveLayerChangeListener)} prevents listener from beeing added twice.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddActiveLayerChangeListenerTwice() {
+        CapturingActiveLayerChangeListener listener = new CapturingActiveLayerChangeListener();
+        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
+        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
+    }
+
+    /**
+     * Test if {@link LayerManagerWithActive#removeActiveLayerChangeListener(ActiveLayerChangeListener)} works.
+     */
+    @Test
+    public void testRemoveActiveLayerChangeListener() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+
+        CapturingActiveLayerChangeListener listener = new CapturingActiveLayerChangeListener();
+        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
+        layerManagerWithActive.removeActiveLayerChangeListener(listener);
+
+        layerManagerWithActive.setActiveLayer(layer2);
+        assertNull(listener.lastEvent);
+    }
+
+    /**
+     * Test if {@link LayerManagerWithActive#removeActiveLayerChangeListener(ActiveLayerChangeListener)} checks if listener is in list.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRemoveActiveLayerChangeListenerNotInList() {
+        layerManagerWithActive.removeActiveLayerChangeListener(new CapturingActiveLayerChangeListener());
+    }
+
+    /**
+     * Tests {@link LayerManagerWithActive#setActiveLayer(Layer)} and {@link LayerManagerWithActive#getActiveLayer()}.
+     * <p>
+     * Edit and active layer getters are also tested in {@link #testAddLayerSetsActiveLayer()}
+     */
+    @Test
+    public void testSetGetActiveLayer() {
+        AbstractTestLayer layer1 = new AbstractTestLayer();
+        AbstractTestLayer layer2 = new AbstractTestLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+
+        layerManagerWithActive.setActiveLayer(layer1);
+        assertSame(layer1, layerManagerWithActive.getActiveLayer());
+
+        layerManagerWithActive.setActiveLayer(layer2);
+        assertSame(layer2, layerManagerWithActive.getActiveLayer());
+    }
+
+    /**
+     * Tests {@link LayerManagerWithActive#getEditDataSet()}
+     */
+    @Test
+    public void testGetEditDataSet() {
+        assertNull(layerManagerWithActive.getEditDataSet());
+        AbstractTestLayer layer0 = new AbstractTestLayer();
+        layerManagerWithActive.addLayer(layer0);
+        assertNull(layerManagerWithActive.getEditDataSet());
+
+        AbstractTestOsmLayer layer1 = new AbstractTestOsmLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+
+        layerManagerWithActive.setActiveLayer(layer1);
+        assertSame(layer1.data, layerManagerWithActive.getEditDataSet());
+
+        layerManagerWithActive.setActiveLayer(layer2);
+        assertSame(layer2.data, layerManagerWithActive.getEditDataSet());
+    }
+
+    /**
+     * Tests {@link LayerManagerWithActive#getVisibleLayersInZOrder()}
+     */
+    @Test
+    public void testGetVisibleLayersInZOrder() {
+        AbstractTestOsmLayer layer1 = new AbstractTestOsmLayer();
+        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
+        AbstractTestLayer layer3 = new AbstractTestLayer();
+        layer3.setVisible(false);
+        AbstractTestOsmLayer layer4 = new AbstractTestOsmLayer();
+        AbstractTestLayer layer5 = new AbstractTestLayer();
+        AbstractTestOsmLayer layer6 = new AbstractTestOsmLayer();
+        AbstractTestOsmLayer layer7 = new AbstractTestOsmLayer();
+        layerManagerWithActive.addLayer(layer1);
+        layerManagerWithActive.addLayer(layer2);
+        layerManagerWithActive.addLayer(layer3);
+        layerManagerWithActive.addLayer(layer4);
+        layerManagerWithActive.addLayer(layer5);
+        layerManagerWithActive.addLayer(layer6);
+        layerManagerWithActive.addLayer(layer7);
+
+        layerManagerWithActive.setActiveLayer(layer1);
+        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+        layerManagerWithActive.setActiveLayer(layer4);
+        assertEquals(Arrays.asList(layer7, layer6, layer5, layer2, layer1, layer4),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+
+        // should not be moved ouside edit layer block
+        layerManagerWithActive.setActiveLayer(layer6);
+        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+        layerManagerWithActive.setActiveLayer(layer7);
+        assertEquals(Arrays.asList(layer6, layer7, layer5, layer4, layer2, layer1),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+
+        // ignored
+        layerManagerWithActive.setActiveLayer(layer3);
+        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+        layerManagerWithActive.setActiveLayer(layer5);
+        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
+                layerManagerWithActive.getVisibleLayersInZOrder());
+
+    }
+
+}
