Index: /trunk/src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/MapView.java	(revision 10458)
@@ -26,4 +26,5 @@
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -51,4 +52,5 @@
 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;
@@ -62,13 +64,13 @@
 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;
@@ -493,4 +495,9 @@
 
     /**
+     * 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.
@@ -594,23 +601,22 @@
     @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);
-         }
-
-        ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
-        if (viewProjectionBounds != null) {
-            scheduleZoomTo(new ViewportData(viewProjectionBounds));
-        }
-
-        layer.addPropertyChangeListener(this);
-        Main.addProjectionChangeListener(layer);
-        invalidatedListener.addTo(layer);
-        AudioPlayer.reset();
-
-        repaint();
+        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));
+            }
+
+            layer.addPropertyChangeListener(this);
+            Main.addProjectionChangeListener(layer);
+            invalidatedListener.addTo(layer);
+            AudioPlayer.reset();
+
+            repaint();
+        } catch (RuntimeException t) {
+            throw BugReport.intercept(t).put("layer", e.getAddedLayer());
+        }
     }
 
@@ -690,4 +696,9 @@
         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);
@@ -770,10 +781,18 @@
     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);
         }
@@ -1075,21 +1094,20 @@
     @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();
Index: /trunk/src/org/openstreetmap/josm/gui/MapViewState.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MapViewState.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/MapViewState.java	(revision 10458)
@@ -4,4 +4,5 @@
 import java.awt.Container;
 import java.awt.Point;
+import java.awt.Rectangle;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Point2D;
@@ -135,4 +136,14 @@
 
     /**
+     * Gets a rectangle of the view as map view area.
+     * @param rectangle The rectangle to get.
+     * @return The view area.
+     * @since 10458
+     */
+    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.
@@ -194,5 +205,5 @@
     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 {
@@ -397,4 +408,5 @@
          * 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() {
@@ -403,4 +415,14 @@
             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.
+         * @since 10458
+         */
+        public Bounds getLatLonBoundsBox() {
+            return projection.getLatLonBoundsBox(getProjectionBounds());
+        }
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/NavigatableComponent.java	(revision 10458)
@@ -52,5 +52,4 @@
 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;
@@ -454,8 +453,5 @@
 
     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();
     }
 
@@ -1511,6 +1507,11 @@
      */
     public int getViewID() {
-        String x = getCenter().east() + '_' + getCenter().north() + '_' + getScale() + '_' +
-                getWidth() + '_' + getHeight() + '_' + getProjection().toString();
+        EastNorth center = getCenter();
+        String x = new StringBuilder().append(center.east())
+                          .append('_').append(center.north())
+                          .append('_').append(getScale())
+                          .append('_').append(getWidth())
+                          .append('_').append(getHeight())
+                          .append('_').append(getProjection()).toString();
         CRC32 id = new CRC32();
         id.update(x.getBytes(StandardCharsets.UTF_8));
Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java	(revision 10458)
@@ -5,5 +5,5 @@
 
 /**
- * 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
@@ -13,7 +13,60 @@
 
     /**
+     * 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 10458
+     */
+    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 MapViewPaintable.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 Layer#hookUpMapView} in the long run.
+     * @param event the event.
+     * @return A layer painter.
+     * @since 10458
+     */
+    public LayerPainter attachToMapView(MapViewEvent event) {
+        return createMapViewPainter(event);
+    }
+
+    /**
+     * Creates a new LayerPainter.
+     * @param event The event that triggered the creation.
+     * @return The painter.
+     * @since 10458
+     */
+    protected LayerPainter createMapViewPainter(MapViewEvent event) {
+        return new CompatibilityModeLayerPainter();
+    }
 
     /**
Index: /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java	(revision 10458)
@@ -82,8 +82,4 @@
 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;
@@ -159,4 +155,16 @@
     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
@@ -607,52 +615,51 @@
         // 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 void layerRemoving(LayerRemoveEvent e) {
-                if (e.getRemovedLayer() == AbstractTileSourceLayer.this) {
-                    Main.map.mapView.removeMouseListener(adapter);
-                    e.getSource().removeLayerChangeListener(this);
-                    MapView.removeZoomChangeListener(AbstractTileSourceLayer.this);
-                }
-            }
-
-            @Override
-            public void layerOrderChanged(LayerOrderChangeEvent e) {
-                // ignored
-            }
-
-            @Override
-            public void layerAdded(LayerAddEvent e) {
-                if (e.getAddedLayer() == AbstractTileSourceLayer.this) {
-                    Main.map.mapView.addMouseListener(adapter);
-                    MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
-                }
-            }
-        }, true);
+    }
+
+    @Override
+    public LayerPainter attachToMapView(MapViewEvent event) {
+        initializeIfRequired();
+
+        event.getMapView().addMouseListener(adapter);
+        MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
+
+        if (this instanceof NativeScaleLayer) {
+            event.getMapView().setNativeScaleLayer((NativeScaleLayer) this);
+        }
+
         // 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);
+            }
+        };
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/Layer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/Layer.java	(revision 10458)
@@ -17,4 +17,5 @@
 import javax.swing.JOptionPane;
 import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
 
 import org.openstreetmap.josm.Main;
@@ -169,17 +170,22 @@
      */
     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));
         }
     }
@@ -526,13 +532,19 @@
     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);
+                }
+            });
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java	(revision 10458)
@@ -9,4 +9,5 @@
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.bugreport.BugReport;
 
 /**
@@ -82,4 +83,9 @@
             return addedLayer;
         }
+
+        @Override
+        public String toString() {
+            return "LayerAddEvent [addedLayer=" + addedLayer + ']';
+        }
     }
 
@@ -114,4 +120,9 @@
             return lastLayer;
         }
+
+        @Override
+        public String toString() {
+            return "LayerRemoveEvent [removedLayer=" + removedLayer + ", lastLayer=" + lastLayer + ']';
+        }
     }
 
@@ -127,4 +138,8 @@
         }
 
+        @Override
+        public String toString() {
+            return "LayerOrderChangeEvent []";
+        }
     }
 
@@ -339,5 +354,9 @@
         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);
+            }
         }
     }
@@ -347,5 +366,9 @@
         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);
+            }
         }
     }
@@ -355,5 +378,9 @@
         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);
+            }
         }
     }
Index: /trunk/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java	(revision 10458)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java	(revision 10458)
@@ -0,0 +1,62 @@
+// 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 10458
+ */
+public class MapViewGraphics {
+
+    private final Graphics2D graphics;
+    private final MapView mapView;
+    private final MapViewRectangle clipBounds;
+
+    /**
+     * Constructs a new {@code MapViewGraphics}.
+     * @param mapView map view
+     * @param graphics default graphics
+     * @param clipBounds clip bounds for this graphics instance
+     */
+    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;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java	(revision 10458)
@@ -61,4 +61,73 @@
 
     /**
+     * Gets a new LayerPainter that paints this {@link MapViewPaintable} to the given map view.
+     *
+     * @author Michael Zangl
+     * @since 10458
+     */
+    public 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.
+         */
+        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.
+         */
+        void detachFromMapView(MapViewEvent event);
+    }
+
+    /**
+     * A event that is fired whenever the map view is attached or detached from any layer.
+     * @author Michael Zangl
+     * @see Layer#attachToMapView(MapViewEvent)
+     * @since 10458
+     */
+    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
Index: /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 10457)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java	(revision 10458)
@@ -139,6 +139,6 @@
 
     @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) {
@@ -179,4 +179,10 @@
             }
         });
+
+        if (event.getMapView().playHeadMarker == null) {
+            event.getMapView().playHeadMarker = PlayHeadMarker.create();
+        }
+
+        return super.attachToMapView(event);
     }
 
