diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index c2a4bae..a68a415 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -25,6 +25,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -50,6 +51,7 @@ import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
 import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
+import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
 import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
 import org.openstreetmap.josm.gui.layer.ImageryLayer;
@@ -61,15 +63,15 @@ import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
+import org.openstreetmap.josm.gui.layer.MapViewGraphics;
 import org.openstreetmap.josm.gui.layer.MapViewPaintable;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable.LayerPainter;
+import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent;
 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;
 import org.openstreetmap.josm.gui.layer.markerlayer.PlayHeadMarker;
-import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.tools.AudioPlayer;
 import org.openstreetmap.josm.tools.Shortcut;
 import org.openstreetmap.josm.tools.Utils;
@@ -492,6 +494,11 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
     private final LayerInvalidatedListener invalidatedListener = new LayerInvalidatedListener();
 
     /**
+     * This is a map of all Layers that have been added to this view.
+     */
+    private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
+
+    /**
      * Constructs a new {@code MapView}.
      * @param layerManager The layers to display.
      * @param contentPane Ignored. Main content pane is used.
@@ -593,25 +600,24 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
 
     @Override
     public void layerAdded(LayerAddEvent e) {
-        Layer layer = e.getAddedLayer();
-        if (layer instanceof MarkerLayer && playHeadMarker == null) {
-            playHeadMarker = PlayHeadMarker.create();
-        }
-        if (layer instanceof NativeScaleLayer) {
-            setNativeScaleLayer((NativeScaleLayer) layer);
-         }
+        try {
+            Layer layer = e.getAddedLayer();
+            registeredLayers.put(layer, layer.attachToMapView(new MapViewEvent(this, false)));
 
-        ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
-        if (viewProjectionBounds != null) {
-            scheduleZoomTo(new ViewportData(viewProjectionBounds));
-        }
+            ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
+            if (viewProjectionBounds != null) {
+                scheduleZoomTo(new ViewportData(viewProjectionBounds));
+            }
 
-        layer.addPropertyChangeListener(this);
-        Main.addProjectionChangeListener(layer);
-        invalidatedListener.addTo(layer);
-        AudioPlayer.reset();
+            layer.addPropertyChangeListener(this);
+            Main.addProjectionChangeListener(layer);
+            invalidatedListener.addTo(layer);
+            AudioPlayer.reset();
 
-        repaint();
+            repaint();
+        } catch (RuntimeException t) {
+            throw BugReport.intercept(t).put("layer", e.getAddedLayer());
+        }
     }
 
     /**
@@ -689,6 +695,11 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
     public void layerRemoving(LayerRemoveEvent e) {
         Layer layer = e.getRemovedLayer();
 
+        LayerPainter painter = registeredLayers.remove(layer);
+        if (painter == null) {
+            throw new IllegalArgumentException("The painter for layer " + layer + " was not registered.");
+        }
+        painter.detachFromMapView(new MapViewEvent(this, false));
         Main.removeProjectionChangeListener(layer);
         layer.removePropertyChangeListener(this);
         invalidatedListener.removeFrom(layer);
@@ -769,12 +780,20 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
 
     private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
         try {
+            LayerPainter painter = registeredLayers.get(layer);
+            if (painter == null) {
+                throw new IllegalArgumentException("Cannot paint layer, it is not registered.");
+            }
+            MapViewRectangle clipBounds = getState().getViewArea(g.getClipBounds());
+            MapViewGraphics paintGraphics = new MapViewGraphics(this, g, clipBounds);
+
             if (layer.getOpacity() < 1) {
                 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
             }
-            layer.paint(g, this, box);
+            painter.paint(paintGraphics);
             g.setPaintMode();
         } catch (RuntimeException t) {
+            //TODO: only display.
             throw BugReport.intercept(t).put("layer", layer).put("bounds", box);
         }
     }
@@ -1074,23 +1093,22 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
 
     @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(layerManager.getActiveLayer());
-            if (activeLayerSupported) {
-                Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
-            } else {
-                Main.unregisterShortcut(mode.getShortcut());
-            }
-            GuiHelper.runInEDTAndWait(new Runnable() {
-                @Override public void run() {
-                    b.setEnabled(activeLayerSupported);
+        if  (Main.map != null) {
+            /* 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 */
+            // FIXME: This does not belong here.
+            for (final AbstractButton b: Main.map.allMapModeButtons) {
+                MapMode mode = (MapMode) b.getAction();
+                final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
+                if (activeLayerSupported) {
+                    Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
+                } else {
+                    Main.unregisterShortcut(mode.getShortcut());
                 }
-            });
+                b.setEnabled(activeLayerSupported);
+            }
         }
         AudioPlayer.reset();
         repaint();
diff --git a/src/org/openstreetmap/josm/gui/MapViewState.java b/src/org/openstreetmap/josm/gui/MapViewState.java
index bf0cf4a..7bf0b73 100644
--- a/src/org/openstreetmap/josm/gui/MapViewState.java
+++ b/src/org/openstreetmap/josm/gui/MapViewState.java
@@ -3,6 +3,7 @@ package org.openstreetmap.josm.gui;
 
 import java.awt.Container;
 import java.awt.Point;
+import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
 import java.awt.geom.Point2D.Double;
@@ -134,6 +135,15 @@ public final class MapViewState {
     }
 
     /**
+     * Gets a rectangle of the view as map view area.
+     * @param rectangle The rectangle to get.
+     * @return The view area.
+     */
+    public MapViewRectangle getViewArea(Rectangle rectangle) {
+        return getForView(rectangle.getMinX(), rectangle.getMinY()).rectTo(getForView(rectangle.getMaxX(), rectangle.getMaxY()));
+    }
+
+    /**
      * Gets the center of the view.
      * @return The center position.
      */
@@ -193,7 +203,7 @@ public final class MapViewState {
      */
     public MapViewState movedTo(MapViewPoint mapViewPoint, EastNorth newEastNorthThere) {
         EastNorth delta = newEastNorthThere.subtract(mapViewPoint.getEastNorth());
-        if (delta.distanceSq(0, 0) < .000001) {
+        if (delta.distanceSq(0, 0) < .1e-20) {
             return this;
         } else {
             return new MapViewState(topLeft.add(delta), this);
@@ -396,12 +406,21 @@ public final class MapViewState {
         /**
          * Gets a rough estimate of the bounds by assuming lat/lon are parallel to x/y.
          * @return The bounds computed by converting the corners of this rectangle.
+         * @see #getLatLonBoundsBox()
          */
         public Bounds getCornerBounds() {
             Bounds b = new Bounds(p1.getLatLon());
             b.extend(p2.getLatLon());
             return b;
         }
+
+        /**
+         * Gets the real bounds that enclose this rectangle. This is computed respecting that the borders of this rectangle may not be a straignt line in latlon coordinates.
+         * @return The bounds.
+         */
+        public Bounds getLatLonBoundsBox() {
+            return projection.getLatLonBoundsBox(getProjectionBounds());
+        }
     }
 
 }
diff --git a/src/org/openstreetmap/josm/gui/NavigatableComponent.java b/src/org/openstreetmap/josm/gui/NavigatableComponent.java
index a73bb86..ae603b9 100644
--- a/src/org/openstreetmap/josm/gui/NavigatableComponent.java
+++ b/src/org/openstreetmap/josm/gui/NavigatableComponent.java
@@ -51,7 +51,6 @@ import org.openstreetmap.josm.data.preferences.DoubleProperty;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 import org.openstreetmap.josm.data.projection.Projection;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
 import org.openstreetmap.josm.gui.help.Helpful;
 import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
 import org.openstreetmap.josm.gui.layer.NativeScaleLayer.Scale;
@@ -453,10 +452,7 @@ public class NavigatableComponent extends JComponent implements Helpful {
     }
 
     public ProjectionBounds getProjectionBounds(Rectangle r) {
-        MapViewState state = getState();
-        MapViewPoint p1 = state.getForView(r.getMinX(), r.getMinY());
-        MapViewPoint p2 = state.getForView(r.getMaxX(), r.getMaxY());
-        return p1.rectTo(p2).getProjectionBounds();
+        return getState().getViewArea(r).getProjectionBounds();
     }
 
     /**
@@ -1510,8 +1506,8 @@ public class NavigatableComponent extends JComponent implements Helpful {
      * @return A unique ID, as long as viewport dimensions are the same
      */
     public int getViewID() {
-        String x = getCenter().east() + '_' + getCenter().north() + '_' + getScale() + '_' +
-                getWidth() + '_' + getHeight() + '_' + getProjection().toString();
+        String x = getCenter().east() + "_" + getCenter().north() + "_" + getScale() + "_" +
+                getWidth() + "_" + getHeight() + "_" + getProjection().toString();
         CRC32 id = new CRC32();
         id.update(x.getBytes(StandardCharsets.UTF_8));
         return (int) id.getValue();
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java b/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java
index 7d249cf..0272d46 100644
--- a/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java
+++ b/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java
@@ -4,7 +4,7 @@ package org.openstreetmap.josm.gui.layer;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
- * This class implements the invalidation listener mechanism suggested by {@link MapViewPaintable}.
+ * This class implements the invalidation listener mechanism suggested by {@link MapViewPaintable} and a default #atta
  *
  * @author Michael Zangl
  * @since 10031
@@ -12,11 +12,61 @@ import java.util.concurrent.CopyOnWriteArrayList;
 public abstract class AbstractMapViewPaintable implements MapViewPaintable {
 
     /**
+     * This is the default implementation of the layer painter.
+     * <p>
+     * You should not use it. Write your own implementation and put your paint code into that class.
+     * <p>
+     * It propagates all calls to the
+     * {@link MapViewPaintable#paint(java.awt.Graphics2D, org.openstreetmap.josm.gui.MapView, org.openstreetmap.josm.data.Bounds)} method.
+     * @author Michael Zangl
+     * @since xxx
+     */
+    protected class CompatibilityModeLayerPainter implements LayerPainter {
+        @Override
+        public void paint(MapViewGraphics graphics) {
+            AbstractMapViewPaintable.this.paint(
+                    graphics.getDefaultGraphics(),
+                    graphics.getMapView(),
+                    graphics.getClipBounds().getLatLonBoundsBox());
+        }
+
+        @Override
+        public void detachFromMapView(MapViewEvent event) {
+            // ignored in old implementation
+        }
+    }
+
+    /**
      * A list of invalidation listeners to call when this layer is invalidated.
      */
     private final CopyOnWriteArrayList<PaintableInvalidationListener> invalidationListeners = new CopyOnWriteArrayList<>();
 
     /**
+     * This method is called whenever this layer is added to a map view.
+     * <p>
+     * You need to return a painter here. The {@link LayerPainter#detachFromMapView(MapViewEvent)} method is called when the layer is removed
+     * from that map view. You are free to reuse painters.
+     * <p>
+     * You should always call the super method. See {@link #createMapViewPainter()} if you want to influence painter creation.
+     * <p>
+     * This replaces {@link #hookUpMapView()} in the long run.
+     * @param event the event.
+     * @return A layer painter.
+     */
+    public LayerPainter attachToMapView(MapViewEvent event) {
+        return createMapViewPainter(event);
+    }
+
+    /**
+     * Creates a new LayerPainter.
+     * @param event The event that triggered the creation.
+     * @return The painter.
+     */
+    protected LayerPainter createMapViewPainter(MapViewEvent event) {
+        return new CompatibilityModeLayerPainter();
+    }
+
+    /**
      * Adds a new paintable invalidation listener.
      * @param l The listener to add.
      */
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
index 2569187..a0b359f 100644
--- a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
@@ -81,10 +81,6 @@ import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
-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.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.WMSLayerImporter;
@@ -158,6 +154,18 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener {
     protected T tileSource;
     protected TileLoader tileLoader;
 
+    private final MouseAdapter adapter = new MouseAdapter() {
+        @Override
+        public void mouseClicked(MouseEvent e) {
+            if (!isVisible()) return;
+            if (e.getButton() == MouseEvent.BUTTON3) {
+                clickedTileHolder.setTile(getTileForPixelpos(e.getX(), e.getY()));
+                new TileSourceLayerPopup().show(e.getComponent(), e.getX(), e.getY());
+            } else if (e.getButton() == MouseEvent.BUTTON1) {
+                attribution.handleAttribution(e.getPoint(), true);
+            }
+        }
+    };
     /**
      * Creates Tile Source based Imagery Layer based on Imagery Info
      * @param info imagery info
@@ -606,54 +614,53 @@ implements ImageObserver, TileLoaderListener, ZoomChangeListener {
     public void hookUpMapView() {
         // this needs to be here and not in constructor to allow empty TileSource class construction
         // using SessionWriter
-        this.tileSource = getTileSource(info);
-        if (this.tileSource == null) {
-            throw new IllegalArgumentException(tr("Failed to create tile source"));
-        }
+        initializeIfRequired();
 
         super.hookUpMapView();
-        projectionChanged(null, Main.getProjection()); // check if projection is supported
-        initTileSource(this.tileSource);
+    }
 
-        final MouseAdapter adapter = new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                if (!isVisible()) return;
-                if (e.getButton() == MouseEvent.BUTTON3) {
-                    clickedTileHolder.setTile(getTileForPixelpos(e.getX(), e.getY()));
-                    new TileSourceLayerPopup().show(e.getComponent(), e.getX(), e.getY());
-                } else if (e.getButton() == MouseEvent.BUTTON1) {
-                    attribution.handleAttribution(e.getPoint(), true);
-                }
-            }
-        };
-        Main.getLayerManager().addLayerChangeListener(new LayerChangeListener() {
+    @Override
+    public LayerPainter attachToMapView(MapViewEvent event) {
+        initializeIfRequired();
 
-            @Override
-            public void layerRemoving(LayerRemoveEvent e) {
-                if (e.getRemovedLayer() == AbstractTileSourceLayer.this) {
-                    Main.map.mapView.removeMouseListener(adapter);
-                    e.getSource().removeLayerChangeListener(this);
-                    MapView.removeZoomChangeListener(AbstractTileSourceLayer.this);
-                }
-            }
+        event.getMapView().addMouseListener(adapter);
+        MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
 
-            @Override
-            public void layerOrderChanged(LayerOrderChangeEvent e) {
-                // ignored
-            }
+        if (this instanceof NativeScaleLayer) {
+            event.getMapView().setNativeScaleLayer((NativeScaleLayer) this);
+        }
 
-            @Override
-            public void layerAdded(LayerAddEvent e) {
-                if (e.getAddedLayer() == AbstractTileSourceLayer.this) {
-                    Main.map.mapView.addMouseListener(adapter);
-                    MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
-                }
-            }
-        }, true);
         // FIXME: why do we need this? Without this, if you add a WMS layer and do not move the mouse, sometimes, tiles do not
         // start loading.
+        // FIXME: Check if this is still required.
         Main.map.repaint(500);
+
+        return super.attachToMapView(event);
+    }
+
+    private void initializeIfRequired() {
+        if (tileSource == null) {
+            tileSource = getTileSource(info);
+            if (tileSource == null) {
+                throw new IllegalArgumentException(tr("Failed to create tile source"));
+            }
+            checkLayerMemoryDoesNotExceedMaximum();
+            // check if projection is supported
+            projectionChanged(null, Main.getProjection());
+            initTileSource(this.tileSource);
+        }
+    }
+
+    @Override
+    protected LayerPainter createMapViewPainter(MapViewEvent event) {
+        return new CompatibilityModeLayerPainter() {
+            @Override
+            public void detachFromMapView(MapViewEvent event) {
+                event.getMapView().removeMouseListener(adapter);
+                MapView.removeZoomChangeListener(AbstractTileSourceLayer.this);
+                super.detachFromMapView(event);
+            }
+        };
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java
index 5f0006c..b4f5797 100644
--- a/src/org/openstreetmap/josm/gui/layer/Layer.java
+++ b/src/org/openstreetmap/josm/gui/layer/Layer.java
@@ -16,6 +16,7 @@ import javax.swing.Action;
 import javax.swing.Icon;
 import javax.swing.JOptionPane;
 import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.GpxExportAction;
@@ -168,19 +169,24 @@ public abstract class Layer extends AbstractMapViewPaintable implements Destroya
      * Remember to call {@code super.hookUpMapView()} when overriding this method
      */
     public void hookUpMapView() {
+        checkLayerMemoryDoesNotExceedMaximum();
+    }
+
+    /**
+     * Checks that the memory required for the layers is no greather than the max memory.
+     */
+    protected static void checkLayerMemoryDoesNotExceedMaximum() {
         // calculate total memory needed for all layers
         long memoryBytesRequired = 50L * 1024L * 1024L; // assumed minimum JOSM memory footprint
-        if (Main.map != null && Main.map.mapView != null) {
-            for (Layer layer: Main.getLayerManager().getLayers()) {
-                memoryBytesRequired += layer.estimateMemoryUsage();
-            }
-            if (memoryBytesRequired > Runtime.getRuntime().maxMemory()) {
-                throw new IllegalArgumentException(
-                        tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
-                        + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
-                        + "Currently you have {1,number,#}MB memory allocated for JOSM",
-                        memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
-            }
+        for (Layer layer: Main.getLayerManager().getLayers()) {
+            memoryBytesRequired += layer.estimateMemoryUsage();
+        }
+        if (memoryBytesRequired > Runtime.getRuntime().maxMemory()) {
+            throw new IllegalArgumentException(
+                    tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
+                            + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
+                            + "Currently you have {1,number,#}MB memory allocated for JOSM",
+                            memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
         }
     }
 
@@ -525,15 +531,21 @@ public abstract class Layer extends AbstractMapViewPaintable implements Destroya
     @Override
     public void projectionChanged(Projection oldValue, Projection newValue) {
         if (!isProjectionSupported(newValue)) {
-            String message = "<html><body><p>" +
+            final String message = "<html><body><p>" +
                     tr("The layer {0} does not support the new projection {1}.", getName(), newValue.toCode()) + "</p>" +
                     "<p style='width: 450px;'>" + tr("Supported projections are: {0}", nameSupportedProjections()) + "</p>" +
                     tr("Change the projection again or remove the layer.");
 
-            JOptionPane.showMessageDialog(Main.parent,
-                    message,
-                    tr("Warning"),
-                    JOptionPane.WARNING_MESSAGE);
+            // run later to not block loading the UI.
+            SwingUtilities.invokeLater(new Runnable() {
+                @Override
+                public void run() {
+                    JOptionPane.showMessageDialog(Main.parent,
+                            message,
+                            tr("Warning"),
+                            JOptionPane.WARNING_MESSAGE);
+                }
+            });
         }
     }
 
diff --git a/src/org/openstreetmap/josm/gui/layer/LayerManager.java b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
index 8d77678..2c41f04 100644
--- a/src/org/openstreetmap/josm/gui/layer/LayerManager.java
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
@@ -8,6 +8,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.bugreport.BugReport;
 
 /**
  * This class handles the layer management.
@@ -81,6 +82,11 @@ public class LayerManager {
         public Layer getAddedLayer() {
             return addedLayer;
         }
+
+        @Override
+        public String toString() {
+            return "LayerAddEvent [addedLayer=" + addedLayer + "]";
+        }
     }
 
     /**
@@ -113,6 +119,11 @@ public class LayerManager {
         public boolean isLastLayer() {
             return lastLayer;
         }
+
+        @Override
+        public String toString() {
+            return "LayerRemoveEvent [removedLayer=" + removedLayer + ", lastLayer=" + lastLayer + "]";
+        }
     }
 
     /**
@@ -126,6 +137,10 @@ public class LayerManager {
             super(source);
         }
 
+        @Override
+        public String toString() {
+            return "LayerOrderChangeEvent []";
+        }
     }
 
     /**
@@ -338,7 +353,11 @@ public class LayerManager {
         GuiHelper.assertCallFromEdt();
         LayerAddEvent e = new LayerAddEvent(this, layer);
         for (LayerChangeListener l : layerChangeListeners) {
-            l.layerAdded(e);
+            try {
+                l.layerAdded(e);
+            } catch (RuntimeException t) {
+                throw BugReport.intercept(t).put("listener", l).put("event", e);
+            }
         }
     }
 
@@ -346,7 +365,11 @@ public class LayerManager {
         GuiHelper.assertCallFromEdt();
         LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
         for (LayerChangeListener l : layerChangeListeners) {
-            l.layerRemoving(e);
+            try {
+                l.layerRemoving(e);
+            } catch (RuntimeException t) {
+                throw BugReport.intercept(t).put("listener", l).put("event", e);
+            }
         }
     }
 
@@ -354,7 +377,11 @@ public class LayerManager {
         GuiHelper.assertCallFromEdt();
         LayerOrderChangeEvent e = new LayerOrderChangeEvent(this);
         for (LayerChangeListener l : layerChangeListeners) {
-            l.layerOrderChanged(e);
+            try {
+                l.layerOrderChanged(e);
+            } catch (RuntimeException t) {
+                throw BugReport.intercept(t).put("listener", l).put("event", e);
+            }
         }
     }
 
diff --git a/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java b/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java
new file mode 100644
index 0000000..689bebe
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java
@@ -0,0 +1,56 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import java.awt.Graphics2D;
+
+import org.openstreetmap.josm.gui.MapView;
+import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
+
+/**
+ * This class provides layers with access to drawing on the map view.
+ * <p>
+ * It contains information about the state of the map view.
+ * <p>
+ * In the future, it may add support for parallel drawing or layer caching.
+ * <p>
+ * It is intended to be used during {@link MapView#paint(java.awt.Graphics)}
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class MapViewGraphics {
+
+    private final Graphics2D graphics;
+    private final MapView mapView;
+    private final MapViewRectangle clipBounds;
+
+    public MapViewGraphics(MapView mapView, Graphics2D graphics, MapViewRectangle clipBounds) {
+        this.mapView = mapView;
+        this.graphics = graphics;
+        this.clipBounds = clipBounds;
+    }
+
+    /**
+     * Gets the {@link Graphics2D} you should use to paint on this graphics object. It may already have some data painted on it.
+     * You should paint your layer data on this graphics.
+     * @return The {@link Graphics2D} instance.
+     */
+    public Graphics2D getDefaultGraphics() {
+        return graphics;
+    }
+
+    /**
+     * Gets the {@link MapView} that is the base to this draw call.
+     * @return The map view.
+     */
+    public MapView getMapView() {
+        return mapView;
+    }
+
+    /**
+     * Gets the clip bounds for this graphics instance.
+     * @return The clip bounds.
+     */
+    public MapViewRectangle getClipBounds() {
+        return clipBounds;
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java b/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java
index 76cb47b..fb4b0c1 100644
--- a/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java
+++ b/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java
@@ -60,6 +60,75 @@ public interface MapViewPaintable {
     }
 
     /**
+     * Gets a new LayerPainter that paints this {@link MapViewPaintable} to the given map view.
+     *
+     * @author Michael Zangl
+     * @since xxx
+     */
+    interface LayerPainter {
+
+        /**
+         * Paints the given layer.
+         * <p>
+         * This can be called in any thread at any time. You will not receive parallel calls for the same map view but you can receive parallel
+         * calls if you use the same {@link LayerPainter} for different map views.
+         * @param graphics The graphics object of the map view you should use.
+         *                 It provides you with a content pane, the bounds and the view state.
+         */
+        public void paint(MapViewGraphics graphics);
+
+        /**
+         * Called when the layer is removed from the map view and this painter is not used any more.
+         * <p>
+         * This method is called once on the painter returned by {@link Layer#attachToMapView(MapViewEvent)}
+         * @param event The event.
+         */
+        public void detachFromMapView(MapViewEvent event);
+    }
+
+    /**
+     * A event that is fired whenever the map view is attached or detached from any layer.
+     * @author Michael Zangl
+     * @since xxx
+     * @see Layer#attachToMapView(MapViewEvent)
+     */
+    public static class MapViewEvent {
+        private final MapView mapView;
+        private final boolean temporaryLayer;
+
+        /**
+         * Create a new {@link MapViewEvent}
+         * @param mapView The map view
+         * @param temporaryLayer <code>true</code> if this layer is in the temporary layer list of the view.
+         */
+        public MapViewEvent(MapView mapView, boolean temporaryLayer) {
+            super();
+            this.mapView = mapView;
+            this.temporaryLayer = temporaryLayer;
+        }
+
+        /**
+         * Gets the map view.
+         * @return The map view.
+         */
+        public MapView getMapView() {
+            return mapView;
+        }
+
+        /**
+         * @return true if this {@link MapViewPaintable} is used as a temporary layer.
+         */
+        public boolean isTemporaryLayer() {
+            return temporaryLayer;
+        }
+
+        @Override
+        public String toString() {
+            return "AttachToMapViewEvent [mapView=" + mapView + ", temporaryLayer=" + temporaryLayer + "]";
+        }
+    }
+
+    /**
      * Paint the dataset using the engine set.
      * @param g Graphics
      * @param mv The object that can translate GeoPoints to screen coordinates.
diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
index 8ec6bb0..d313e2d 100644
--- a/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
+++ b/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
@@ -138,8 +138,8 @@ public class MarkerLayer extends Layer implements JumpToMarkerLayer {
     }
 
     @Override
-    public void hookUpMapView() {
-        Main.map.mapView.addMouseListener(new MouseAdapter() {
+    public LayerPainter attachToMapView(MapViewEvent event) {
+        event.getMapView().addMouseListener(new MouseAdapter() {
             @Override
             public void mousePressed(MouseEvent e) {
                 if (e.getButton() != MouseEvent.BUTTON1)
@@ -178,6 +178,12 @@ public class MarkerLayer extends Layer implements JumpToMarkerLayer {
                 invalidate();
             }
         });
+
+        if (event.getMapView().playHeadMarker == null) {
+            event.getMapView().playHeadMarker = PlayHeadMarker.create();
+        }
+
+        return super.attachToMapView(event);
     }
 
     /**
