Ticket #13029: patch-layer-manager-mapview-attach.patch

File patch-layer-manager-mapview-attach.patch, 33.4 KB (added by michael2402, 9 years ago)
  • src/org/openstreetmap/josm/gui/MapView.java

    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index c2a4bae..a68a415 100644
    a b import java.util.ArrayList;  
    2525import java.util.Arrays;
    2626import java.util.Collection;
    2727import java.util.Collections;
     28import java.util.HashMap;
    2829import java.util.LinkedHashSet;
    2930import java.util.List;
    3031import java.util.Set;
    import org.openstreetmap.josm.data.osm.OsmPrimitive;  
    5051import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
    5152import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
    5253import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
     54import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
    5355import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
    5456import org.openstreetmap.josm.gui.layer.GpxLayer;
    5557import org.openstreetmap.josm.gui.layer.ImageryLayer;
    import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;  
    6163import org.openstreetmap.josm.gui.layer.MainLayerManager;
    6264import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
    6365import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
     66import org.openstreetmap.josm.gui.layer.MapViewGraphics;
    6467import org.openstreetmap.josm.gui.layer.MapViewPaintable;
     68import org.openstreetmap.josm.gui.layer.MapViewPaintable.LayerPainter;
     69import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent;
    6570import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent;
    6671import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
    67 import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
    6872import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    6973import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
    70 import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    7174import org.openstreetmap.josm.gui.layer.markerlayer.PlayHeadMarker;
    72 import org.openstreetmap.josm.gui.util.GuiHelper;
    7375import org.openstreetmap.josm.tools.AudioPlayer;
    7476import org.openstreetmap.josm.tools.Shortcut;
    7577import org.openstreetmap.josm.tools.Utils;
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    492494    private final LayerInvalidatedListener invalidatedListener = new LayerInvalidatedListener();
    493495
    494496    /**
     497     * This is a map of all Layers that have been added to this view.
     498     */
     499    private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
     500
     501    /**
    495502     * Constructs a new {@code MapView}.
    496503     * @param layerManager The layers to display.
    497504     * @param contentPane Ignored. Main content pane is used.
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    593600
    594601    @Override
    595602    public void layerAdded(LayerAddEvent e) {
    596         Layer layer = e.getAddedLayer();
    597         if (layer instanceof MarkerLayer && playHeadMarker == null) {
    598             playHeadMarker = PlayHeadMarker.create();
    599         }
    600         if (layer instanceof NativeScaleLayer) {
    601             setNativeScaleLayer((NativeScaleLayer) layer);
    602          }
     603        try {
     604            Layer layer = e.getAddedLayer();
     605            registeredLayers.put(layer, layer.attachToMapView(new MapViewEvent(this, false)));
    603606
    604         ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
    605         if (viewProjectionBounds != null) {
    606             scheduleZoomTo(new ViewportData(viewProjectionBounds));
    607         }
     607            ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
     608            if (viewProjectionBounds != null) {
     609                scheduleZoomTo(new ViewportData(viewProjectionBounds));
     610            }
    608611
    609         layer.addPropertyChangeListener(this);
    610         Main.addProjectionChangeListener(layer);
    611         invalidatedListener.addTo(layer);
    612         AudioPlayer.reset();
     612            layer.addPropertyChangeListener(this);
     613            Main.addProjectionChangeListener(layer);
     614            invalidatedListener.addTo(layer);
     615            AudioPlayer.reset();
    613616
    614         repaint();
     617            repaint();
     618        } catch (RuntimeException t) {
     619            throw BugReport.intercept(t).put("layer", e.getAddedLayer());
     620        }
    615621    }
    616622
    617623    /**
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    689695    public void layerRemoving(LayerRemoveEvent e) {
    690696        Layer layer = e.getRemovedLayer();
    691697
     698        LayerPainter painter = registeredLayers.remove(layer);
     699        if (painter == null) {
     700            throw new IllegalArgumentException("The painter for layer " + layer + " was not registered.");
     701        }
     702        painter.detachFromMapView(new MapViewEvent(this, false));
    692703        Main.removeProjectionChangeListener(layer);
    693704        layer.removePropertyChangeListener(this);
    694705        invalidatedListener.removeFrom(layer);
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    769780
    770781    private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
    771782        try {
     783            LayerPainter painter = registeredLayers.get(layer);
     784            if (painter == null) {
     785                throw new IllegalArgumentException("Cannot paint layer, it is not registered.");
     786            }
     787            MapViewRectangle clipBounds = getState().getViewArea(g.getClipBounds());
     788            MapViewGraphics paintGraphics = new MapViewGraphics(this, g, clipBounds);
     789
    772790            if (layer.getOpacity() < 1) {
    773791                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
    774792            }
    775             layer.paint(g, this, box);
     793            painter.paint(paintGraphics);
    776794            g.setPaintMode();
    777795        } catch (RuntimeException t) {
     796            //TODO: only display.
    778797            throw BugReport.intercept(t).put("layer", layer).put("bounds", box);
    779798        }
    780799    }
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    10741093
    10751094    @Override
    10761095    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
    1077         /* This only makes the buttons look disabled. Disabling the actions as well requires
    1078          * the user to re-select the tool after i.e. moving a layer. While testing I found
    1079          * that I switch layers and actions at the same time and it was annoying to mind the
    1080          * order. This way it works as visual clue for new users */
    1081         for (final AbstractButton b: Main.map.allMapModeButtons) {
    1082             MapMode mode = (MapMode) b.getAction();
    1083             final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
    1084             if (activeLayerSupported) {
    1085                 Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
    1086             } else {
    1087                 Main.unregisterShortcut(mode.getShortcut());
    1088             }
    1089             GuiHelper.runInEDTAndWait(new Runnable() {
    1090                 @Override public void run() {
    1091                     b.setEnabled(activeLayerSupported);
     1096        if  (Main.map != null) {
     1097            /* This only makes the buttons look disabled. Disabling the actions as well requires
     1098             * the user to re-select the tool after i.e. moving a layer. While testing I found
     1099             * that I switch layers and actions at the same time and it was annoying to mind the
     1100             * order. This way it works as visual clue for new users */
     1101            // FIXME: This does not belong here.
     1102            for (final AbstractButton b: Main.map.allMapModeButtons) {
     1103                MapMode mode = (MapMode) b.getAction();
     1104                final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
     1105                if (activeLayerSupported) {
     1106                    Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
     1107                } else {
     1108                    Main.unregisterShortcut(mode.getShortcut());
    10921109                }
    1093             });
     1110                b.setEnabled(activeLayerSupported);
     1111            }
    10941112        }
    10951113        AudioPlayer.reset();
    10961114        repaint();
  • src/org/openstreetmap/josm/gui/MapViewState.java

    diff --git a/src/org/openstreetmap/josm/gui/MapViewState.java b/src/org/openstreetmap/josm/gui/MapViewState.java
    index bf0cf4a..7bf0b73 100644
    a b package org.openstreetmap.josm.gui;  
    33
    44import java.awt.Container;
    55import java.awt.Point;
     6import java.awt.Rectangle;
    67import java.awt.geom.AffineTransform;
    78import java.awt.geom.Point2D;
    89import java.awt.geom.Point2D.Double;
    public final class MapViewState {  
    134135    }
    135136
    136137    /**
     138     * Gets a rectangle of the view as map view area.
     139     * @param rectangle The rectangle to get.
     140     * @return The view area.
     141     */
     142    public MapViewRectangle getViewArea(Rectangle rectangle) {
     143        return getForView(rectangle.getMinX(), rectangle.getMinY()).rectTo(getForView(rectangle.getMaxX(), rectangle.getMaxY()));
     144    }
     145
     146    /**
    137147     * Gets the center of the view.
    138148     * @return The center position.
    139149     */
    public final class MapViewState {  
    193203     */
    194204    public MapViewState movedTo(MapViewPoint mapViewPoint, EastNorth newEastNorthThere) {
    195205        EastNorth delta = newEastNorthThere.subtract(mapViewPoint.getEastNorth());
    196         if (delta.distanceSq(0, 0) < .000001) {
     206        if (delta.distanceSq(0, 0) < .1e-20) {
    197207            return this;
    198208        } else {
    199209            return new MapViewState(topLeft.add(delta), this);
    public final class MapViewState {  
    396406        /**
    397407         * Gets a rough estimate of the bounds by assuming lat/lon are parallel to x/y.
    398408         * @return The bounds computed by converting the corners of this rectangle.
     409         * @see #getLatLonBoundsBox()
    399410         */
    400411        public Bounds getCornerBounds() {
    401412            Bounds b = new Bounds(p1.getLatLon());
    402413            b.extend(p2.getLatLon());
    403414            return b;
    404415        }
     416
     417        /**
     418         * 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.
     419         * @return The bounds.
     420         */
     421        public Bounds getLatLonBoundsBox() {
     422            return projection.getLatLonBoundsBox(getProjectionBounds());
     423        }
    405424    }
    406425
    407426}
  • src/org/openstreetmap/josm/gui/NavigatableComponent.java

    diff --git a/src/org/openstreetmap/josm/gui/NavigatableComponent.java b/src/org/openstreetmap/josm/gui/NavigatableComponent.java
    index a73bb86..ae603b9 100644
    a b import org.openstreetmap.josm.data.preferences.DoubleProperty;  
    5151import org.openstreetmap.josm.data.preferences.IntegerProperty;
    5252import org.openstreetmap.josm.data.projection.Projection;
    5353import org.openstreetmap.josm.data.projection.Projections;
    54 import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
    5554import org.openstreetmap.josm.gui.help.Helpful;
    5655import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
    5756import org.openstreetmap.josm.gui.layer.NativeScaleLayer.Scale;
    public class NavigatableComponent extends JComponent implements Helpful {  
    453452    }
    454453
    455454    public ProjectionBounds getProjectionBounds(Rectangle r) {
    456         MapViewState state = getState();
    457         MapViewPoint p1 = state.getForView(r.getMinX(), r.getMinY());
    458         MapViewPoint p2 = state.getForView(r.getMaxX(), r.getMaxY());
    459         return p1.rectTo(p2).getProjectionBounds();
     455        return getState().getViewArea(r).getProjectionBounds();
    460456    }
    461457
    462458    /**
    public class NavigatableComponent extends JComponent implements Helpful {  
    15101506     * @return A unique ID, as long as viewport dimensions are the same
    15111507     */
    15121508    public int getViewID() {
    1513         String x = getCenter().east() + '_' + getCenter().north() + '_' + getScale() + '_' +
    1514                 getWidth() + '_' + getHeight() + '_' + getProjection().toString();
     1509        String x = getCenter().east() + "_" + getCenter().north() + "_" + getScale() + "_" +
     1510                getWidth() + "_" + getHeight() + "_" + getProjection().toString();
    15151511        CRC32 id = new CRC32();
    15161512        id.update(x.getBytes(StandardCharsets.UTF_8));
    15171513        return (int) id.getValue();
  • src/org/openstreetmap/josm/gui/layer/AbstractMapViewPaintable.java

    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 b package org.openstreetmap.josm.gui.layer;  
    44import java.util.concurrent.CopyOnWriteArrayList;
    55
    66/**
    7  * This class implements the invalidation listener mechanism suggested by {@link MapViewPaintable}.
     7 * This class implements the invalidation listener mechanism suggested by {@link MapViewPaintable} and a default #atta
    88 *
    99 * @author Michael Zangl
    1010 * @since 10031
    import java.util.concurrent.CopyOnWriteArrayList;  
    1212public abstract class AbstractMapViewPaintable implements MapViewPaintable {
    1313
    1414    /**
     15     * This is the default implementation of the layer painter.
     16     * <p>
     17     * You should not use it. Write your own implementation and put your paint code into that class.
     18     * <p>
     19     * It propagates all calls to the
     20     * {@link MapViewPaintable#paint(java.awt.Graphics2D, org.openstreetmap.josm.gui.MapView, org.openstreetmap.josm.data.Bounds)} method.
     21     * @author Michael Zangl
     22     * @since xxx
     23     */
     24    protected class CompatibilityModeLayerPainter implements LayerPainter {
     25        @Override
     26        public void paint(MapViewGraphics graphics) {
     27            AbstractMapViewPaintable.this.paint(
     28                    graphics.getDefaultGraphics(),
     29                    graphics.getMapView(),
     30                    graphics.getClipBounds().getLatLonBoundsBox());
     31        }
     32
     33        @Override
     34        public void detachFromMapView(MapViewEvent event) {
     35            // ignored in old implementation
     36        }
     37    }
     38
     39    /**
    1540     * A list of invalidation listeners to call when this layer is invalidated.
    1641     */
    1742    private final CopyOnWriteArrayList<PaintableInvalidationListener> invalidationListeners = new CopyOnWriteArrayList<>();
    1843
    1944    /**
     45     * This method is called whenever this layer is added to a map view.
     46     * <p>
     47     * You need to return a painter here. The {@link LayerPainter#detachFromMapView(MapViewEvent)} method is called when the layer is removed
     48     * from that map view. You are free to reuse painters.
     49     * <p>
     50     * You should always call the super method. See {@link #createMapViewPainter()} if you want to influence painter creation.
     51     * <p>
     52     * This replaces {@link #hookUpMapView()} in the long run.
     53     * @param event the event.
     54     * @return A layer painter.
     55     */
     56    public LayerPainter attachToMapView(MapViewEvent event) {
     57        return createMapViewPainter(event);
     58    }
     59
     60    /**
     61     * Creates a new LayerPainter.
     62     * @param event The event that triggered the creation.
     63     * @return The painter.
     64     */
     65    protected LayerPainter createMapViewPainter(MapViewEvent event) {
     66        return new CompatibilityModeLayerPainter();
     67    }
     68
     69    /**
    2070     * Adds a new paintable invalidation listener.
    2171     * @param l The listener to add.
    2272     */
  • src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java

    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 b import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;  
    8181import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    8282import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    8383import org.openstreetmap.josm.gui.dialogs.LayerListPopup;
    84 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    85 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
    86 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
    87 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    8884import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    8985import org.openstreetmap.josm.gui.util.GuiHelper;
    9086import org.openstreetmap.josm.io.WMSLayerImporter;
    implements ImageObserver, TileLoaderListener, ZoomChangeListener {  
    158154    protected T tileSource;
    159155    protected TileLoader tileLoader;
    160156
     157    private final MouseAdapter adapter = new MouseAdapter() {
     158        @Override
     159        public void mouseClicked(MouseEvent e) {
     160            if (!isVisible()) return;
     161            if (e.getButton() == MouseEvent.BUTTON3) {
     162                clickedTileHolder.setTile(getTileForPixelpos(e.getX(), e.getY()));
     163                new TileSourceLayerPopup().show(e.getComponent(), e.getX(), e.getY());
     164            } else if (e.getButton() == MouseEvent.BUTTON1) {
     165                attribution.handleAttribution(e.getPoint(), true);
     166            }
     167        }
     168    };
    161169    /**
    162170     * Creates Tile Source based Imagery Layer based on Imagery Info
    163171     * @param info imagery info
    implements ImageObserver, TileLoaderListener, ZoomChangeListener {  
    606614    public void hookUpMapView() {
    607615        // this needs to be here and not in constructor to allow empty TileSource class construction
    608616        // using SessionWriter
    609         this.tileSource = getTileSource(info);
    610         if (this.tileSource == null) {
    611             throw new IllegalArgumentException(tr("Failed to create tile source"));
    612         }
     617        initializeIfRequired();
    613618
    614619        super.hookUpMapView();
    615         projectionChanged(null, Main.getProjection()); // check if projection is supported
    616         initTileSource(this.tileSource);
     620    }
    617621
    618         final MouseAdapter adapter = new MouseAdapter() {
    619             @Override
    620             public void mouseClicked(MouseEvent e) {
    621                 if (!isVisible()) return;
    622                 if (e.getButton() == MouseEvent.BUTTON3) {
    623                     clickedTileHolder.setTile(getTileForPixelpos(e.getX(), e.getY()));
    624                     new TileSourceLayerPopup().show(e.getComponent(), e.getX(), e.getY());
    625                 } else if (e.getButton() == MouseEvent.BUTTON1) {
    626                     attribution.handleAttribution(e.getPoint(), true);
    627                 }
    628             }
    629         };
    630         Main.getLayerManager().addLayerChangeListener(new LayerChangeListener() {
     622    @Override
     623    public LayerPainter attachToMapView(MapViewEvent event) {
     624        initializeIfRequired();
    631625
    632             @Override
    633             public void layerRemoving(LayerRemoveEvent e) {
    634                 if (e.getRemovedLayer() == AbstractTileSourceLayer.this) {
    635                     Main.map.mapView.removeMouseListener(adapter);
    636                     e.getSource().removeLayerChangeListener(this);
    637                     MapView.removeZoomChangeListener(AbstractTileSourceLayer.this);
    638                 }
    639             }
     626        event.getMapView().addMouseListener(adapter);
     627        MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
    640628
    641             @Override
    642             public void layerOrderChanged(LayerOrderChangeEvent e) {
    643                 // ignored
    644             }
     629        if (this instanceof NativeScaleLayer) {
     630            event.getMapView().setNativeScaleLayer((NativeScaleLayer) this);
     631        }
    645632
    646             @Override
    647             public void layerAdded(LayerAddEvent e) {
    648                 if (e.getAddedLayer() == AbstractTileSourceLayer.this) {
    649                     Main.map.mapView.addMouseListener(adapter);
    650                     MapView.addZoomChangeListener(AbstractTileSourceLayer.this);
    651                 }
    652             }
    653         }, true);
    654633        // FIXME: why do we need this? Without this, if you add a WMS layer and do not move the mouse, sometimes, tiles do not
    655634        // start loading.
     635        // FIXME: Check if this is still required.
    656636        Main.map.repaint(500);
     637
     638        return super.attachToMapView(event);
     639    }
     640
     641    private void initializeIfRequired() {
     642        if (tileSource == null) {
     643            tileSource = getTileSource(info);
     644            if (tileSource == null) {
     645                throw new IllegalArgumentException(tr("Failed to create tile source"));
     646            }
     647            checkLayerMemoryDoesNotExceedMaximum();
     648            // check if projection is supported
     649            projectionChanged(null, Main.getProjection());
     650            initTileSource(this.tileSource);
     651        }
     652    }
     653
     654    @Override
     655    protected LayerPainter createMapViewPainter(MapViewEvent event) {
     656        return new CompatibilityModeLayerPainter() {
     657            @Override
     658            public void detachFromMapView(MapViewEvent event) {
     659                event.getMapView().removeMouseListener(adapter);
     660                MapView.removeZoomChangeListener(AbstractTileSourceLayer.this);
     661                super.detachFromMapView(event);
     662            }
     663        };
    657664    }
    658665
    659666    /**
  • src/org/openstreetmap/josm/gui/layer/Layer.java

    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 b import javax.swing.Action;  
    1616import javax.swing.Icon;
    1717import javax.swing.JOptionPane;
    1818import javax.swing.JSeparator;
     19import javax.swing.SwingUtilities;
    1920
    2021import org.openstreetmap.josm.Main;
    2122import org.openstreetmap.josm.actions.GpxExportAction;
    public abstract class Layer extends AbstractMapViewPaintable implements Destroya  
    168169     * Remember to call {@code super.hookUpMapView()} when overriding this method
    169170     */
    170171    public void hookUpMapView() {
     172        checkLayerMemoryDoesNotExceedMaximum();
     173    }
     174
     175    /**
     176     * Checks that the memory required for the layers is no greather than the max memory.
     177     */
     178    protected static void checkLayerMemoryDoesNotExceedMaximum() {
    171179        // calculate total memory needed for all layers
    172180        long memoryBytesRequired = 50L * 1024L * 1024L; // assumed minimum JOSM memory footprint
    173         if (Main.map != null && Main.map.mapView != null) {
    174             for (Layer layer: Main.getLayerManager().getLayers()) {
    175                 memoryBytesRequired += layer.estimateMemoryUsage();
    176             }
    177             if (memoryBytesRequired > Runtime.getRuntime().maxMemory()) {
    178                 throw new IllegalArgumentException(
    179                         tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
    180                         + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
    181                         + "Currently you have {1,number,#}MB memory allocated for JOSM",
    182                         memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
    183             }
     181        for (Layer layer: Main.getLayerManager().getLayers()) {
     182            memoryBytesRequired += layer.estimateMemoryUsage();
     183        }
     184        if (memoryBytesRequired > Runtime.getRuntime().maxMemory()) {
     185            throw new IllegalArgumentException(
     186                    tr("To add another layer you need to allocate at least {0,number,#}MB memory to JOSM using -Xmx{0,number,#}M "
     187                            + "option (see http://forum.openstreetmap.org/viewtopic.php?id=25677).\n"
     188                            + "Currently you have {1,number,#}MB memory allocated for JOSM",
     189                            memoryBytesRequired / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024));
    184190        }
    185191    }
    186192
    public abstract class Layer extends AbstractMapViewPaintable implements Destroya  
    525531    @Override
    526532    public void projectionChanged(Projection oldValue, Projection newValue) {
    527533        if (!isProjectionSupported(newValue)) {
    528             String message = "<html><body><p>" +
     534            final String message = "<html><body><p>" +
    529535                    tr("The layer {0} does not support the new projection {1}.", getName(), newValue.toCode()) + "</p>" +
    530536                    "<p style='width: 450px;'>" + tr("Supported projections are: {0}", nameSupportedProjections()) + "</p>" +
    531537                    tr("Change the projection again or remove the layer.");
    532538
    533             JOptionPane.showMessageDialog(Main.parent,
    534                     message,
    535                     tr("Warning"),
    536                     JOptionPane.WARNING_MESSAGE);
     539            // run later to not block loading the UI.
     540            SwingUtilities.invokeLater(new Runnable() {
     541                @Override
     542                public void run() {
     543                    JOptionPane.showMessageDialog(Main.parent,
     544                            message,
     545                            tr("Warning"),
     546                            JOptionPane.WARNING_MESSAGE);
     547                }
     548            });
    537549        }
    538550    }
    539551
  • src/org/openstreetmap/josm/gui/layer/LayerManager.java

    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 b import java.util.concurrent.CopyOnWriteArrayList;  
    88
    99import org.openstreetmap.josm.gui.util.GuiHelper;
    1010import org.openstreetmap.josm.tools.Utils;
     11import org.openstreetmap.josm.tools.bugreport.BugReport;
    1112
    1213/**
    1314 * This class handles the layer management.
    public class LayerManager {  
    8182        public Layer getAddedLayer() {
    8283            return addedLayer;
    8384        }
     85
     86        @Override
     87        public String toString() {
     88            return "LayerAddEvent [addedLayer=" + addedLayer + "]";
     89        }
    8490    }
    8591
    8692    /**
    public class LayerManager {  
    113119        public boolean isLastLayer() {
    114120            return lastLayer;
    115121        }
     122
     123        @Override
     124        public String toString() {
     125            return "LayerRemoveEvent [removedLayer=" + removedLayer + ", lastLayer=" + lastLayer + "]";
     126        }
    116127    }
    117128
    118129    /**
    public class LayerManager {  
    126137            super(source);
    127138        }
    128139
     140        @Override
     141        public String toString() {
     142            return "LayerOrderChangeEvent []";
     143        }
    129144    }
    130145
    131146    /**
    public class LayerManager {  
    338353        GuiHelper.assertCallFromEdt();
    339354        LayerAddEvent e = new LayerAddEvent(this, layer);
    340355        for (LayerChangeListener l : layerChangeListeners) {
    341             l.layerAdded(e);
     356            try {
     357                l.layerAdded(e);
     358            } catch (RuntimeException t) {
     359                throw BugReport.intercept(t).put("listener", l).put("event", e);
     360            }
    342361        }
    343362    }
    344363
    public class LayerManager {  
    346365        GuiHelper.assertCallFromEdt();
    347366        LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
    348367        for (LayerChangeListener l : layerChangeListeners) {
    349             l.layerRemoving(e);
     368            try {
     369                l.layerRemoving(e);
     370            } catch (RuntimeException t) {
     371                throw BugReport.intercept(t).put("listener", l).put("event", e);
     372            }
    350373        }
    351374    }
    352375
    public class LayerManager {  
    354377        GuiHelper.assertCallFromEdt();
    355378        LayerOrderChangeEvent e = new LayerOrderChangeEvent(this);
    356379        for (LayerChangeListener l : layerChangeListeners) {
    357             l.layerOrderChanged(e);
     380            try {
     381                l.layerOrderChanged(e);
     382            } catch (RuntimeException t) {
     383                throw BugReport.intercept(t).put("listener", l).put("event", e);
     384            }
    358385        }
    359386    }
    360387
  • new file src/org/openstreetmap/josm/gui/layer/MapViewGraphics.java

    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
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.layer;
     3
     4import java.awt.Graphics2D;
     5
     6import org.openstreetmap.josm.gui.MapView;
     7import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
     8
     9/**
     10 * This class provides layers with access to drawing on the map view.
     11 * <p>
     12 * It contains information about the state of the map view.
     13 * <p>
     14 * In the future, it may add support for parallel drawing or layer caching.
     15 * <p>
     16 * It is intended to be used during {@link MapView#paint(java.awt.Graphics)}
     17 * @author Michael Zangl
     18 * @since xxx
     19 */
     20public class MapViewGraphics {
     21
     22    private final Graphics2D graphics;
     23    private final MapView mapView;
     24    private final MapViewRectangle clipBounds;
     25
     26    public MapViewGraphics(MapView mapView, Graphics2D graphics, MapViewRectangle clipBounds) {
     27        this.mapView = mapView;
     28        this.graphics = graphics;
     29        this.clipBounds = clipBounds;
     30    }
     31
     32    /**
     33     * Gets the {@link Graphics2D} you should use to paint on this graphics object. It may already have some data painted on it.
     34     * You should paint your layer data on this graphics.
     35     * @return The {@link Graphics2D} instance.
     36     */
     37    public Graphics2D getDefaultGraphics() {
     38        return graphics;
     39    }
     40
     41    /**
     42     * Gets the {@link MapView} that is the base to this draw call.
     43     * @return The map view.
     44     */
     45    public MapView getMapView() {
     46        return mapView;
     47    }
     48
     49    /**
     50     * Gets the clip bounds for this graphics instance.
     51     * @return The clip bounds.
     52     */
     53    public MapViewRectangle getClipBounds() {
     54        return clipBounds;
     55    }
     56}
  • src/org/openstreetmap/josm/gui/layer/MapViewPaintable.java

    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 b public interface MapViewPaintable {  
    6060    }
    6161
    6262    /**
     63     * Gets a new LayerPainter that paints this {@link MapViewPaintable} to the given map view.
     64     *
     65     * @author Michael Zangl
     66     * @since xxx
     67     */
     68    interface LayerPainter {
     69
     70        /**
     71         * Paints the given layer.
     72         * <p>
     73         * 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
     74         * calls if you use the same {@link LayerPainter} for different map views.
     75         * @param graphics The graphics object of the map view you should use.
     76         *                 It provides you with a content pane, the bounds and the view state.
     77         */
     78        public void paint(MapViewGraphics graphics);
     79
     80        /**
     81         * Called when the layer is removed from the map view and this painter is not used any more.
     82         * <p>
     83         * This method is called once on the painter returned by {@link Layer#attachToMapView(MapViewEvent)}
     84         * @param event The event.
     85         */
     86        public void detachFromMapView(MapViewEvent event);
     87    }
     88
     89    /**
     90     * A event that is fired whenever the map view is attached or detached from any layer.
     91     * @author Michael Zangl
     92     * @since xxx
     93     * @see Layer#attachToMapView(MapViewEvent)
     94     */
     95    public static class MapViewEvent {
     96        private final MapView mapView;
     97        private final boolean temporaryLayer;
     98
     99        /**
     100         * Create a new {@link MapViewEvent}
     101         * @param mapView The map view
     102         * @param temporaryLayer <code>true</code> if this layer is in the temporary layer list of the view.
     103         */
     104        public MapViewEvent(MapView mapView, boolean temporaryLayer) {
     105            super();
     106            this.mapView = mapView;
     107            this.temporaryLayer = temporaryLayer;
     108        }
     109
     110        /**
     111         * Gets the map view.
     112         * @return The map view.
     113         */
     114        public MapView getMapView() {
     115            return mapView;
     116        }
     117
     118        /**
     119         * @return true if this {@link MapViewPaintable} is used as a temporary layer.
     120         */
     121        public boolean isTemporaryLayer() {
     122            return temporaryLayer;
     123        }
     124
     125        @Override
     126        public String toString() {
     127            return "AttachToMapViewEvent [mapView=" + mapView + ", temporaryLayer=" + temporaryLayer + "]";
     128        }
     129    }
     130
     131    /**
    63132     * Paint the dataset using the engine set.
    64133     * @param g Graphics
    65134     * @param mv The object that can translate GeoPoints to screen coordinates.
  • src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java

    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 b public class MarkerLayer extends Layer implements JumpToMarkerLayer {  
    138138    }
    139139
    140140    @Override
    141     public void hookUpMapView() {
    142         Main.map.mapView.addMouseListener(new MouseAdapter() {
     141    public LayerPainter attachToMapView(MapViewEvent event) {
     142        event.getMapView().addMouseListener(new MouseAdapter() {
    143143            @Override
    144144            public void mousePressed(MouseEvent e) {
    145145                if (e.getButton() != MouseEvent.BUTTON1)
    public class MarkerLayer extends Layer implements JumpToMarkerLayer {  
    178178                invalidate();
    179179            }
    180180        });
     181
     182        if (event.getMapView().playHeadMarker == null) {
     183            event.getMapView().playHeadMarker = PlayHeadMarker.create();
     184        }
     185
     186        return super.attachToMapView(event);
    181187    }
    182188
    183189    /**