Ticket #12863: patch-add-layer-manager.patch

File patch-add-layer-manager.patch, 100.1 KB (added by michael2402, 10 years ago)
  • src/org/openstreetmap/josm/Main.java

    diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
    index 16e892c..345914b 100644
    a b import org.openstreetmap.josm.gui.MainApplication.Option;  
    8686import org.openstreetmap.josm.gui.MainMenu;
    8787import org.openstreetmap.josm.gui.MapFrame;
    8888import org.openstreetmap.josm.gui.MapFrameListener;
    89 import org.openstreetmap.josm.gui.MapView;
    9089import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    9190import org.openstreetmap.josm.gui.help.HelpUtil;
    9291import org.openstreetmap.josm.gui.io.SaveLayersDialog;
    9392import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    9493import org.openstreetmap.josm.gui.layer.Layer;
     94import org.openstreetmap.josm.gui.layer.LayerManagerWithActive;
    9595import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    9696import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
    9797import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
    public abstract class Main {  
    189189
    190190    /**
    191191     * The MapFrame. Use {@link Main#setMapFrame} to set or clear it.
     192     * <p>
     193     * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
    192194     */
    193195    public static MapFrame map;
    194196
    195197    /**
     198     * Provides access to the layers displayed in the main view.
     199     */
     200    private static final LayerManagerWithActive layerManager = new LayerManagerWithActive();
     201
     202    /**
    196203     * The toolbar preference control to register new actions.
    197204     */
    198205    public static volatile ToolbarPreferences toolbar;
    public abstract class Main {  
    530537     */
    531538    public final synchronized void removeLayer(final Layer layer) {
    532539        if (map != null) {
    533             map.mapView.removeLayer(layer);
    534             if (isDisplayingMapView() && map.mapView.getAllLayers().isEmpty()) {
     540            getLayerManager().removeLayer(layer);
     541            if (isDisplayingMapView() && getLayerManager().getLayers().isEmpty()) {
    535542                setMapFrame(null);
    536543            }
    537544        }
    public abstract class Main {  
    607614            @Override
    608615            public void initialize() {
    609616                validator = new OsmValidator();
    610                 MapView.addLayerChangeListener(validator);
     617                getLayerManager().addLayerChangeListener(validator);
    611618            }
    612619        });
    613620
    public abstract class Main {  
    729736    }
    730737
    731738    /**
     739     * Returns the main layer manager that is used by the map view.
     740     * @return The layer manager. The value returned will never change.
     741     */
     742    public static LayerManagerWithActive getLayerManager() {
     743        return layerManager;
     744    }
     745
     746    /**
    732747     * Add a new layer to the map.
    733748     *
    734749     * If no map exists, create one.
    public abstract class Main {  
    772787            createMapFrame(layer, viewport);
    773788        }
    774789        layer.hookUpMapView();
    775         map.mapView.addLayer(layer);
     790        getLayerManager().addLayer(layer);
    776791        if (noMap) {
    777792            Main.map.setVisible(true);
    778793        } else if (viewport != null) {
    public abstract class Main {  
    812827     */
    813828    public OsmDataLayer getEditLayer() {
    814829        if (!isDisplayingMapView()) return null;
    815         return map.mapView.getEditLayer();
     830        return getLayerManager().getEditLayer();
    816831    }
    817832
    818833    /**
    public abstract class Main {  
    851866     */
    852867    public Layer getActiveLayer() {
    853868        if (!isDisplayingMapView()) return null;
    854         return map.mapView.getActiveLayer();
     869        return getLayerManager().getActiveLayer();
    855870    }
    856871
    857872    protected static final JPanel contentPanePrivate = new JPanel(new BorderLayout());
    public abstract class Main {  
    10321047     */
    10331048    public static boolean saveUnsavedModifications() {
    10341049        if (!isDisplayingMapView()) return true;
    1035         return saveUnsavedModifications(map.mapView.getLayersOfType(AbstractModifiableLayer.class), true);
     1050        return saveUnsavedModifications(getLayerManager().getLayersOfType(AbstractModifiableLayer.class), true);
    10361051    }
    10371052
    10381053    /**
    public abstract class Main {  
    10991114            pref.put("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0);
    11001115            // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
    11011116            if (Main.isDisplayingMapView()) {
    1102                 Collection<Layer> layers = new ArrayList<>(Main.map.mapView.getAllLayers());
     1117                Collection<Layer> layers = new ArrayList<>(getLayerManager().getLayers());
    11031118                for (Layer l: layers) {
    11041119                    Main.main.removeLayer(l);
    11051120                }
  • src/org/openstreetmap/josm/data/validation/OsmValidator.java

    diff --git a/src/org/openstreetmap/josm/data/validation/OsmValidator.java b/src/org/openstreetmap/josm/data/validation/OsmValidator.java
    index 18ecda2..d640398 100644
    a b import org.openstreetmap.josm.data.validation.tests.UntaggedNode;  
    5858import org.openstreetmap.josm.data.validation.tests.UntaggedWay;
    5959import org.openstreetmap.josm.data.validation.tests.WayConnectedToArea;
    6060import org.openstreetmap.josm.data.validation.tests.WronglyOrderedWays;
    61 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
    62 import org.openstreetmap.josm.gui.layer.Layer;
     61import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
     62import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
     63import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     64import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    6365import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    6466import org.openstreetmap.josm.gui.layer.ValidatorLayer;
    6567import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
    public class OsmValidator implements LayerChangeListener {  
    338340    /* interface LayerChangeListener                                              */
    339341    /* -------------------------------------------------------------------------- */
    340342    @Override
    341     public void activeLayerChange(Layer oldLayer, Layer newLayer) {
    342         // Do nothing
     343    public void layerAdded(LayerAddEvent e) {
     344        // do nothing
    343345    }
    344346
    345347    @Override
    346     public void layerAdded(Layer newLayer) {
    347         // Do nothing
     348    public void layerOrderChanged(LayerOrderChangeEvent e) {
     349        // do nothing
    348350    }
    349351
    350352    @Override
    351     public void layerRemoved(Layer oldLayer) {
    352         if (oldLayer == errorLayer) {
     353    public void layerRemoving(LayerRemoveEvent e) {
     354        if (e.getRemovedLayer() == errorLayer) {
    353355            errorLayer = null;
    354356            return;
    355357        }
    356         if (Main.map.mapView.getLayersOfType(OsmDataLayer.class).isEmpty()) {
     358        if (e.getSource().getLayersOfType(OsmDataLayer.class).isEmpty()) {
    357359            if (errorLayer != null) {
    358360                Main.main.removeLayer(errorLayer);
    359361            }
  • src/org/openstreetmap/josm/gui/MapFrame.java

    diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
    index 9a84eb7..6c0e557 100644
    a b import org.openstreetmap.josm.gui.dialogs.UserListDialog;  
    7575import org.openstreetmap.josm.gui.dialogs.ValidatorDialog;
    7676import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog;
    7777import org.openstreetmap.josm.gui.layer.Layer;
     78import org.openstreetmap.josm.gui.layer.LayerManager;
    7879import org.openstreetmap.josm.gui.util.AdvancedKeyPressDetector;
    7980import org.openstreetmap.josm.tools.Destroyable;
    8081import org.openstreetmap.josm.tools.GBC;
    public class MapFrame extends JPanel implements Destroyable, LayerChangeListener  
    9798
    9899    /**
    99100     * The view control displayed.
     101     * <p>
     102     * Accessing this is discouraged. Use the {@link LayerManager} to access map data.
    100103     */
    101104    public final MapView mapView;
    102105
    public class MapFrame extends JPanel implements Destroyable, LayerChangeListener  
    189192        setSize(400, 400);
    190193        setLayout(new BorderLayout());
    191194
    192         mapView = new MapView(contentPane, viewportData);
     195        mapView = new MapView(Main.getLayerManager(), contentPane, viewportData);
    193196        if (!GraphicsEnvironment.isHeadless()) {
    194197            new FileDrop(mapView);
    195198        }
    public class MapFrame extends JPanel implements Destroyable, LayerChangeListener  
    774777                mapMode.enterMode();
    775778            }
    776779            // invalidate repaint cache
    777             Main.map.mapView.preferenceChanged(null);
     780            mapView.preferenceChanged(null);
    778781        }
    779782
    780783        // After all listeners notice new layer, some buttons will be disabled/enabled
  • 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 dceb83c..580ec60 100644
    a b import java.util.ArrayList;  
    2525import java.util.Arrays;
    2626import java.util.Collection;
    2727import java.util.Collections;
    28 import java.util.EnumSet;
    2928import java.util.LinkedHashSet;
    3029import java.util.List;
    31 import java.util.ListIterator;
    3230import java.util.Set;
    3331import java.util.concurrent.CopyOnWriteArrayList;
    3432
    import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;  
    5856import org.openstreetmap.josm.gui.layer.GpxLayer;
    5957import org.openstreetmap.josm.gui.layer.ImageryLayer;
    6058import org.openstreetmap.josm.gui.layer.Layer;
    61 import org.openstreetmap.josm.gui.layer.LayerPositionStrategy;
     59import org.openstreetmap.josm.gui.layer.LayerManager;
     60import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
     61import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     62import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
     63import org.openstreetmap.josm.gui.layer.LayerManagerWithActive;
     64import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeEvent;
     65import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener;
    6266import org.openstreetmap.josm.gui.layer.MapViewPaintable;
    6367import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent;
    6468import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
    65 import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
    6669import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    6770import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
    6871import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
    import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;  
    8689 * @author imi
    8790 */
    8891public class MapView extends NavigatableComponent
    89 implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener {
    90 
     92implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener,
     93LayerManager.LayerChangeListener, LayerManagerWithActive.ActiveLayerChangeListener {
    9194    /**
    9295     * Interface to notify listeners of a layer change.
     96     * <p>
     97     * To be removed: end of 2016. Use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener} instead.
    9398     * @author imi
    9499     */
     100    @Deprecated
    95101    public interface LayerChangeListener {
    96102
    97103        /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    116122
    117123    /**
    118124     * An interface that needs to be implemented in order to listen for changes to the active edit layer.
     125     * <p>
     126     * To be removed: end of 2016. Use {@link ActiveLayerChangeListener} instead.
    119127     */
     128    @Deprecated
    120129    public interface EditLayerChangeListener {
    121130
    122131        /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    171180        }
    172181    }
    173182
    174     public boolean viewportFollowing;
     183    /**
     184     * This class is an adapter for the old layer change interface.
     185     * <p>
     186     * New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener}
     187     * @author Michael Zangl
     188     */
     189    protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener {
     190
     191        private final LayerChangeListener wrapped;
     192        private boolean receiveOneInitialFire;
     193
     194        public LayerChangeAdapter(LayerChangeListener wrapped) {
     195            this.wrapped = wrapped;
     196        }
     197
     198        public LayerChangeAdapter(LayerChangeListener wrapped, boolean initialFire) {
     199            this(wrapped);
     200            this.receiveOneInitialFire = initialFire;
     201        }
     202
     203        @Override
     204        public void layerAdded(LayerAddEvent e) {
     205            wrapped.layerAdded(e.getAddedLayer());
     206        }
     207
     208        @Override
     209        public void layerRemoving(LayerRemoveEvent e) {
     210            wrapped.layerRemoved(e.getRemovedLayer());
     211        }
     212
     213        @Override
     214        public void layerOrderChanged(LayerOrderChangeEvent e) {
     215            // not in old API
     216        }
     217
     218        @Override
     219        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
     220            Layer oldActive = receiveOneInitialFire ? null : e.getPreviousActiveLayer();
     221            Layer newActive = e.getSource().getActiveLayer();
     222            if (oldActive != newActive) {
     223                wrapped.activeLayerChange(oldActive, newActive);
     224            }
     225            receiveOneInitialFire = false;
     226        }
     227
     228        @Override
     229        public int hashCode() {
     230            final int prime = 31;
     231            int result = 1;
     232            result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
     233            return result;
     234        }
     235
     236        @Override
     237        public boolean equals(Object obj) {
     238            if (this == obj)
     239                return true;
     240            if (obj == null)
     241                return false;
     242            if (getClass() != obj.getClass())
     243                return false;
     244            LayerChangeAdapter other = (LayerChangeAdapter) obj;
     245            if (wrapped == null) {
     246                if (other.wrapped != null)
     247                    return false;
     248            } else if (!wrapped.equals(other.wrapped))
     249                return false;
     250            return true;
     251        }
     252
     253        @Override
     254        public String toString() {
     255            return "LayerChangeAdapter [wrapped=" + wrapped + "]";
     256        }
     257
     258    }
    175259
    176260    /**
    177      * the layer listeners
     261     * This class is an adapter for the old layer change interface.
     262     * <p>
     263     * New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener}
     264     * @author Michael Zangl
    178265     */
    179     private static final CopyOnWriteArrayList<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
    180     private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<>();
     266    protected static class EditLayerChangeAdapter implements ActiveLayerChangeListener {
     267
     268        private final EditLayerChangeListener wrapped;
     269
     270        public EditLayerChangeAdapter(EditLayerChangeListener wrapped) {
     271            this.wrapped = wrapped;
     272        }
     273
     274        @Override
     275        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
     276            OsmDataLayer oldLayer = e.getPreviousEditLayer();
     277            OsmDataLayer newLayer = e.getSource().getEditLayer();
     278            if (oldLayer != newLayer) {
     279                wrapped.editLayerChanged(oldLayer, newLayer);
     280            }
     281        }
     282
     283        @Override
     284        public int hashCode() {
     285            final int prime = 31;
     286            int result = 1;
     287            result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
     288            return result;
     289        }
     290
     291        @Override
     292        public boolean equals(Object obj) {
     293            if (this == obj)
     294                return true;
     295            if (obj == null)
     296                return false;
     297            if (getClass() != obj.getClass())
     298                return false;
     299            EditLayerChangeAdapter other = (EditLayerChangeAdapter) obj;
     300            if (wrapped == null) {
     301                if (other.wrapped != null)
     302                    return false;
     303            } else if (!wrapped.equals(other.wrapped))
     304                return false;
     305            return true;
     306        }
     307
     308        @Override
     309        public String toString() {
     310            return "EditLayerChangeAdapter [wrapped=" + wrapped + "]";
     311        }
     312
     313    }
    181314
    182315    /**
    183316     * Removes a layer change listener
     317     * <p>
     318     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    184319     *
    185      * @param listener the listener. Ignored if null or already registered.
     320     * @param listener the listener. Ignored if null or not registered.
    186321     */
     322    @Deprecated
    187323    public static void removeLayerChangeListener(LayerChangeListener listener) {
    188         layerChangeListeners.remove(listener);
     324        LayerChangeAdapter adapter = new LayerChangeAdapter(listener);
     325        try {
     326            Main.getLayerManager().removeLayerChangeListener(adapter);
     327        } catch (IllegalArgumentException e) {
     328            // Ignored in old implementation
     329        }
     330        try {
     331            Main.getLayerManager().removeActiveLayerChangeListener(adapter);
     332        } catch (IllegalArgumentException e) {
     333            // Ignored in old implementation
     334        }
    189335    }
    190336
     337    /**
     338     * Removes an edit layer change listener
     339     * <p>
     340     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
     341     *
     342     * @param listener the listener. Ignored if null or not registered.
     343     */
     344    @Deprecated
    191345    public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
    192         editLayerChangeListeners.remove(listener);
     346        try {
     347            Main.getLayerManager().removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
     348        } catch (IllegalArgumentException e) {
     349            // Ignored in old implementation
     350        }
    193351    }
    194352
    195353    /**
    196354     * Adds a layer change listener
     355     * <p>
     356     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    197357     *
    198358     * @param listener the listener. Ignored if null or already registered.
    199359     */
     360    @Deprecated
    200361    public static void addLayerChangeListener(LayerChangeListener listener) {
    201         if (listener != null) {
    202             layerChangeListeners.addIfAbsent(listener);
    203         }
     362        addLayerChangeListener(listener, false);
    204363    }
    205364
    206365    /**
    207366     * Adds a layer change listener
     367     * <p>
     368     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    208369     *
    209370     * @param listener the listener. Ignored if null or already registered.
    210371     * @param initialFire fire an active-layer-changed-event right after adding
    211372     * the listener in case there is a layer present (should be)
    212373     */
     374    @Deprecated
    213375    public static void addLayerChangeListener(LayerChangeListener listener, boolean initialFire) {
    214         addLayerChangeListener(listener);
    215         if (initialFire && Main.isDisplayingMapView()) {
    216             listener.activeLayerChange(null, Main.map.mapView.getActiveLayer());
     376        if (listener != null) {
     377            initialFire = initialFire && Main.isDisplayingMapView();
     378
     379            LayerChangeAdapter adapter = new LayerChangeAdapter(listener, initialFire);
     380            Main.getLayerManager().addLayerChangeListener(adapter, false);
     381            Main.getLayerManager().addActiveLayerChangeListener(adapter, initialFire);
     382            adapter.receiveOneInitialFire = false;
    217383        }
    218384    }
    219385
    220386    /**
    221387     * Adds an edit layer change listener
     388     * <p>
     389     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    222390     *
    223391     * @param listener the listener. Ignored if null or already registered.
    224392     * @param initialFire fire an edit-layer-changed-event right after adding
    225393     * the listener in case there is an edit layer present
    226394     */
     395    @Deprecated
    227396    public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
    228         addEditLayerChangeListener(listener);
    229         if (initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null) {
    230             listener.editLayerChanged(null, Main.map.mapView.getEditLayer());
     397        if (listener != null) {
     398            Main.getLayerManager().addActiveLayerChangeListener(new EditLayerChangeAdapter(listener), initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null);
    231399        }
    232400    }
    233401
    234402    /**
    235403     * Adds an edit layer change listener
     404     * <p>
     405     * You should register the listener on {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    236406     *
    237407     * @param listener the listener. Ignored if null or already registered.
    238408     */
     409    @Deprecated
    239410    public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
    240         if (listener != null) {
    241             editLayerChangeListeners.addIfAbsent(listener);
    242         }
     411        addEditLayerChangeListener(listener, false);
    243412    }
    244413
    245     /**
    246      * Calls the {@link LayerChangeListener#activeLayerChange(Layer, Layer)} method of all listeners.
    247      *
    248      * @param oldLayer The old layer
    249      * @param newLayer The new active layer.
    250      */
    251     protected void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
    252         for (LayerChangeListener l : layerChangeListeners) {
    253             l.activeLayerChange(oldLayer, newLayer);
    254         }
    255     }
    256 
    257     protected void fireLayerAdded(Layer newLayer) {
    258         for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
    259             l.layerAdded(newLayer);
    260         }
    261     }
    262 
    263     protected void fireLayerRemoved(Layer layer) {
    264         for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
    265             l.layerRemoved(layer);
    266         }
    267     }
    268 
    269     protected void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
    270         for (EditLayerChangeListener l : editLayerChangeListeners) {
    271             l.editLayerChanged(oldLayer, newLayer);
    272         }
    273     }
     414    public boolean viewportFollowing = false;
    274415
    275416    /**
    276      * A list of all layers currently loaded.
     417     * A list of all layers currently loaded. If we support multiple map views, this list may be different for each of them.
    277418     */
    278     private final transient List<Layer> layers = new ArrayList<>();
     419    private final LayerManagerWithActive layerManager;
    279420
    280421    /**
    281422     * The play head marker: there is only one of these so it isn't in any specific layer
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    283424    public transient PlayHeadMarker playHeadMarker;
    284425
    285426    /**
    286      * The layer from the layers list that is currently active.
    287      */
    288     private transient Layer activeLayer;
    289 
    290     /**
    291      * The edit layer is the current active data layer.
    292      */
    293     private transient OsmDataLayer editLayer;
    294 
    295     /**
    296427     * The last event performed by mouse.
    297428     */
    298429    public MouseEvent lastMEvent = new MouseEvent(this, 0, 0, 0, 0, 0, 0, false); // In case somebody reads it before first mouse move
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    321452
    322453    /**
    323454     * Constructs a new {@code MapView}.
     455     * @param layerManager The layers to display.
    324456     * @param contentPane The content pane used to register shortcuts in its
    325457     * {@link InputMap} and {@link ActionMap}
    326458     * @param viewportData the initial viewport of the map. Can be null, then
    327459     * the viewport is derived from the layer data.
    328460     */
    329     public MapView(final JPanel contentPane, final ViewportData viewportData) {
     461    public MapView(LayerManagerWithActive layerManager, final JPanel contentPane, final ViewportData viewportData) {
     462        this.layerManager = layerManager;
    330463        initialViewport = viewportData;
     464        layerManager.addLayerChangeListener(this);
     465        layerManager.addActiveLayerChangeListener(this, false);
    331466        Main.pref.addPreferenceChangeListener(this);
    332467
    333468        addComponentListener(new ComponentAdapter() {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    404539    }
    405540
    406541    /**
    407      * Add a layer to the current MapView. The layer will be added at topmost
    408      * position.
     542     * Add a layer to the current MapView.
     543     * <p>
     544     * Use {@link Main#getLayerManager()}.addLayer() instead. To be removed: end of 2016.
    409545     * @param layer The layer to add
    410546     */
     547    @Deprecated
    411548    public void addLayer(Layer layer) {
    412         boolean isOsmDataLayer = layer instanceof OsmDataLayer;
    413         EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class);
    414         Layer oldActiveLayer = activeLayer;
    415         OsmDataLayer oldEditLayer = editLayer;
    416 
    417         synchronized (layers) {
    418             if (layer instanceof MarkerLayer && playHeadMarker == null) {
    419                 playHeadMarker = PlayHeadMarker.create();
    420             }
    421 
    422             LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition();
    423             int position = positionStrategy.getPosition(this);
    424             checkPosition(position);
    425             insertLayerAt(layer, position);
    426 
    427             if (isOsmDataLayer || oldActiveLayer == null) {
    428                 // autoselect the new layer
    429                 listenersToFire.addAll(setActiveLayer(layer, true));
    430             }
    431 
    432             if (isOsmDataLayer) {
    433                 ((OsmDataLayer) layer).addLayerStateChangeListener(this);
    434             }
    435 
    436             if (layer instanceof NativeScaleLayer) {
    437                 Main.map.mapView.setNativeScaleLayer((NativeScaleLayer) layer);
    438             }
     549        layerManager.addLayer(layer);
     550    }
    439551
    440             layer.addPropertyChangeListener(this);
    441             invalidatedListener.addTo(layer);
    442             Main.addProjectionChangeListener(layer);
    443             AudioPlayer.reset();
     552    @Override
     553    public void layerAdded(LayerAddEvent e) {
     554        Layer layer = e.getAddedLayer();
     555        if (layer instanceof MarkerLayer && playHeadMarker == null) {
     556            playHeadMarker = PlayHeadMarker.create();
    444557        }
    445         fireLayerAdded(layer);
    446         onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
    447558
    448         if (!listenersToFire.isEmpty()) {
    449             repaint();
     559        boolean isOsmDataLayer = layer instanceof OsmDataLayer;
     560        if (isOsmDataLayer) {
     561            ((OsmDataLayer) layer).addLayerStateChangeListener(this);
    450562        }
    451     }
    452563
    453     /**
    454      * Check if the (new) position is valid
    455      * @param position The position index
    456      * @throws IndexOutOfBoundsException if it is not.
    457      */
    458     private void checkPosition(int position) {
    459         if (position < 0 || position > layers.size()) {
    460             throw new IndexOutOfBoundsException("Position " + position + " out of range.");
    461         }
     564        layer.addPropertyChangeListener(this);
     565        Main.addProjectionChangeListener(layer);
     566        invalidatedListener.addTo(layer);
     567        AudioPlayer.reset();
     568
     569        repaint();
    462570    }
    463571
    464572    /**
    465      * Insert a layer at a given position.
    466      * @param layer The layer to add.
    467      * @param position The position on which we should add it.
     573     * Use {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    468574     */
    469     private void insertLayerAt(Layer layer, int position) {
    470         if (position == layers.size()) {
    471             layers.add(layer);
    472         } else {
    473             layers.add(position, layer);
    474         }
    475     }
    476 
    477575    @Override
     576    @Deprecated
    478577    protected DataSet getCurrentDataSet() {
    479         synchronized (layers) {
    480             if (editLayer != null)
    481                 return editLayer.data;
    482             else
    483                 return null;
    484         }
     578        return layerManager.getEditDataSet();
    485579    }
    486580
    487581    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    490584     * @return true if the active data layer (edit layer) is drawable, false otherwise
    491585     */
    492586    public boolean isActiveLayerDrawable() {
    493         synchronized (layers) {
    494             return editLayer != null;
    495         }
     587         return getEditLayer() != null;
    496588    }
    497589
    498590    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    501593     * @return true if the active data layer (edit layer) is visible, false otherwise
    502594     */
    503595    public boolean isActiveLayerVisible() {
    504         synchronized (layers) {
    505             return isActiveLayerDrawable() && editLayer.isVisible();
    506         }
     596        OsmDataLayer e = getEditLayer();
     597        return e != null && e.isVisible();
    507598    }
    508599
    509600    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    514605     *     becomes active</li>
    515606     *   <li>otherwise, the top most layer of any type becomes active</li>
    516607     * </ul>
     608     * To be removed: end of 2016 (now handled by {@link LayerManagerWithActive}
    517609     * @param layersList lit of layers
    518610     *
    519611     * @return the next active data layer
    520612     */
     613    @Deprecated
    521614    protected Layer determineNextActiveLayer(List<Layer> layersList) {
    522615        // First look for data layer
    523616        for (Layer layer:layersList) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    531624
    532625        // and then give up
    533626        return null;
    534 
    535627    }
    536628
    537629    /**
    538630     * Remove the layer from the mapview. If the layer was in the list before,
    539631     * an LayerChange event is fired.
     632     * <p>
     633     * Use {@link Main#getLayerManager()}.removeLayer() instead. To be removed: end of 2016.
    540634     * @param layer The layer to remove
    541635     */
     636    @Deprecated
    542637    public void removeLayer(Layer layer) {
    543         EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class);
    544         Layer oldActiveLayer = activeLayer;
    545         OsmDataLayer oldEditLayer = editLayer;
    546 
    547         synchronized (layers) {
    548             List<Layer> layersList = new ArrayList<>(layers);
    549 
    550             if (!layersList.remove(layer))
    551                 return;
    552 
    553             listenersToFire = setEditLayer(layersList);
    554 
    555             if (layer == activeLayer) {
    556                 listenersToFire.addAll(setActiveLayer(determineNextActiveLayer(layersList), false));
    557             }
    558 
    559             if (layer instanceof OsmDataLayer) {
    560                 ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this);
    561             }
     638        layerManager.removeLayer(layer);
     639    }
    562640
    563             layers.remove(layer);
    564             Main.removeProjectionChangeListener(layer);
    565             layer.removePropertyChangeListener(this);
    566             invalidatedListener.removeFrom(layer);
    567             layer.destroy();
    568             AudioPlayer.reset();
     641    @Override
     642    public void layerRemoving(LayerRemoveEvent e) {
     643        Layer layer = e.getRemovedLayer();
     644        if (layer instanceof OsmDataLayer) {
     645            ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this);
    569646        }
    570         onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
    571         fireLayerRemoved(layer);
    572647
    573         repaint();
    574     }
     648        Main.removeProjectionChangeListener(layer);
     649        layer.removePropertyChangeListener(this);
     650        invalidatedListener.removeFrom(layer);
     651        layer.destroy();
     652        AudioPlayer.reset();
    575653
    576     private void onEditLayerChanged(OsmDataLayer oldEditLayer) {
    577         fireEditLayerChanged(oldEditLayer, editLayer);
    578         refreshTitle();
     654        repaint();
    579655    }
    580656
    581657    private boolean virtualNodesEnabled;
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    604680     * @param pos       The new position of the layer
    605681     */
    606682    public void moveLayer(Layer layer, int pos) {
    607         EnumSet<LayerListenerType> listenersToFire;
    608         Layer oldActiveLayer = activeLayer;
    609         OsmDataLayer oldEditLayer = editLayer;
    610 
    611         synchronized (layers) {
    612             int curLayerPos = layers.indexOf(layer);
    613             if (curLayerPos == -1)
    614                 throw new IllegalArgumentException(tr("Layer not in list."));
    615             if (pos == curLayerPos)
    616                 return; // already in place.
    617             layers.remove(curLayerPos);
    618             if (pos >= layers.size()) {
    619                 layers.add(layer);
    620             } else {
    621                 layers.add(pos, layer);
    622             }
    623             listenersToFire = setEditLayer(layers);
    624             AudioPlayer.reset();
    625         }
    626         onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
     683        layerManager.moveLayer(layer, pos);
     684    }
    627685
     686    @Override
     687    public void layerOrderChanged(LayerOrderChangeEvent e) {
     688        AudioPlayer.reset();
    628689        repaint();
    629690    }
    630691
    631692    /**
    632693     * Gets the index of the layer in the layer list.
     694     * <p>
     695     * Access the layer list using {@link Main#getLayerManager()} instead. To be removed: end of 2016.
    633696     * @param layer The layer to search for.
    634697     * @return The index in the list.
    635698     * @throws IllegalArgumentException if that layer does not belong to this view.
    636699     */
     700    @Deprecated
    637701    public int getLayerPos(Layer layer) {
    638         int curLayerPos;
    639         synchronized (layers) {
    640             curLayerPos = layers.indexOf(layer);
    641         }
     702        int curLayerPos = layerManager.getLayers().indexOf(layer);
    642703        if (curLayerPos == -1)
    643704            throw new IllegalArgumentException(tr("Layer not in list."));
    644705        return curLayerPos;
    645706    }
    646707
    647     /**
    648      * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
    649      * first, layer with the highest Z-Order last.
    650      * <p>
    651      * The active data layer is pulled above all adjacent data layers.
    652      *
    653      * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
    654      * first, layer with the highest Z-Order last.
    655      */
    656     public List<Layer> getVisibleLayersInZOrder() {
    657         synchronized (layers) {
    658             List<Layer> ret = new ArrayList<>();
    659             // This is set while we delay the addition of the active layer.
    660             boolean activeLayerDelayed = false;
    661             for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
    662                 Layer l = iterator.previous();
    663                 if (!l.isVisible()) {
    664                     // ignored
    665                 } else if (l == activeLayer && l instanceof OsmDataLayer) {
    666                     // delay and add after the current block of OsmDataLayer
    667                     activeLayerDelayed = true;
    668                 } else {
    669                     if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
    670                         // add active layer before the current one.
    671                         ret.add(activeLayer);
    672                         activeLayerDelayed = false;
    673                     }
    674                     // Add this layer now
    675                     ret.add(l);
    676                 }
    677             }
    678             if (activeLayerDelayed) {
    679                 ret.add(activeLayer);
    680             }
    681             return ret;
    682         }
    683     }
    684 
    685708    private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
    686709        if (layer.getOpacity() < 1) {
    687710            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    699722            return;
    700723        }
    701724
    702         List<Layer> visibleLayers = getVisibleLayersInZOrder();
     725        List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
    703726
    704727        int nonChangedLayersCount = 0;
    705728        for (Layer l: visibleLayers) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    892915    }
    893916
    894917    /**
     918     * Use {@link LayerManager#getLayers()} instead. To be removed: end of 2016.
     919     *
    895920     * @return An unmodifiable collection of all layers
    896921     */
     922    @Deprecated
    897923    public Collection<Layer> getAllLayers() {
    898         synchronized (layers) {
    899             return Collections.unmodifiableCollection(new ArrayList<>(layers));
    900         }
     924        return layerManager.getLayers();
    901925    }
    902926
    903927    /**
     928     * Use {@link LayerManager#getLayers()} instead. To be removed: end of 2016.
     929     *
    904930     * @return An unmodifiable ordered list of all layers
    905931     */
     932    @Deprecated
    906933    public List<Layer> getAllLayersAsList() {
    907         synchronized (layers) {
    908             return Collections.unmodifiableList(new ArrayList<>(layers));
    909         }
     934        return layerManager.getLayers();
    910935    }
    911936
    912937    /**
    913      * Replies an unmodifiable list of layers of a certain type.
     938     * Replies an unmodifiable list of layers of a certain type. To be removed: end of 2016.
    914939     *
    915940     * Example:
    916941     * <pre>
    917942     *     List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
    918943     * </pre>
     944     * Use {@link LayerManager#getLayersOfType(Class)} instead.
     945     *
    919946     * @param <T> layer type
    920947     *
    921948     * @param ofType The layer type.
    922949     * @return an unmodifiable list of layers of a certain type.
    923950     */
     951    @Deprecated
    924952    public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
    925         return new ArrayList<>(Utils.filteredCollection(getAllLayers(), ofType));
     953        return layerManager.getLayersOfType(ofType);
    926954    }
    927955
    928956    /**
    929      * Replies the number of layers managed by this map view
     957     * Replies the number of layers managed by this map view. To be removed: end of 2016.
     958     * <p>
     959     * You can use {@link Main#getLayerManager()}.getLayers().size() instead.
    930960     *
    931961     * @return the number of layers managed by this map view
    932962     */
     963    @Deprecated
    933964    public int getNumLayers() {
    934         synchronized (layers) {
    935             return layers.size();
    936         }
     965        return getAllLayers().size();
    937966    }
    938967
    939968    /**
    940969     * Replies true if there is at least one layer in this map view
     970     * <p>
     971     * You can use !{@link Main#getLayerManager()}.getLayers().isEmpty() instead.
    941972     *
    942973     * @return true if there is at least one layer in this map view
    943974     */
     975    @Deprecated
    944976    public boolean hasLayers() {
    945977        return getNumLayers() > 0;
    946978    }
    947979
    948980    /**
    949      * Sets the active edit layer.
    950      * <p>
    951      * @param layersList A list to select that layer from.
    952      * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}
    953      */
    954     private EnumSet<LayerListenerType> setEditLayer(List<Layer> layersList) {
    955         final OsmDataLayer newEditLayer = findNewEditLayer(layersList);
    956 
    957         // Set new edit layer
    958         if (newEditLayer != editLayer) {
    959             if (newEditLayer == null) {
    960                 // Note: Unsafe to call while layer write lock is held.
    961                 getCurrentDataSet().setSelected();
    962             }
    963 
    964             editLayer = newEditLayer;
    965             return EnumSet.of(LayerListenerType.EDIT_LAYER_CHANGE);
    966         } else {
    967             return EnumSet.noneOf(LayerListenerType.class);
    968         }
    969 
    970     }
    971 
    972     private OsmDataLayer findNewEditLayer(List<Layer> layersList) {
    973         OsmDataLayer newEditLayer = layersList.contains(editLayer) ? editLayer : null;
    974         // Find new edit layer
    975         if (activeLayer != editLayer || !layersList.contains(editLayer)) {
    976             if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {
    977                 newEditLayer = (OsmDataLayer) activeLayer;
    978             } else {
    979                 for (Layer layer:layersList) {
    980                     if (layer instanceof OsmDataLayer) {
    981                         newEditLayer = (OsmDataLayer) layer;
    982                         break;
    983                     }
    984                 }
    985             }
    986         }
    987         return newEditLayer;
    988     }
    989 
    990     /**
    991981     * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance
    992      * of {@link OsmDataLayer} also sets {@link #editLayer} to <code>layer</code>.
     982     * of {@link OsmDataLayer} also sets editLayer to <code>layer</code>.
     983     * <p>
     984     * You can use !{@link Main#getLayerManager()}.setActiveLayer() instead.
    993985     *
    994986     * @param layer the layer to be activate; must be one of the layers in the list of layers
    995987     * @throws IllegalArgumentException if layer is not in the list of layers
    996988     */
     989    @Deprecated
    997990    public void setActiveLayer(Layer layer) {
    998         EnumSet<LayerListenerType> listenersToFire;
    999         Layer oldActiveLayer;
    1000         OsmDataLayer oldEditLayer;
    1001 
    1002         synchronized (layers) {
    1003             oldActiveLayer = activeLayer;
    1004             oldEditLayer = editLayer;
    1005             listenersToFire = setActiveLayer(layer, true);
    1006         }
    1007         onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);
    1008 
    1009         repaint();
     991        layerManager.setActiveLayer(layer);
    1010992    }
    1011 
    1012     /**
    1013      * Sets the active layer. Propagates this change to all map buttons.
    1014      * @param layer The layer to be active.
    1015      * @param setEditLayer if this is <code>true</code>, the edit layer is also set.
    1016      * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}
    1017      */
    1018     private EnumSet<LayerListenerType> setActiveLayer(final Layer layer, boolean setEditLayer) {
    1019         if (layer != null && !layers.contains(layer))
    1020             throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));
    1021 
    1022         if (layer == activeLayer)
    1023             return EnumSet.noneOf(LayerListenerType.class);
    1024 
    1025         activeLayer = layer;
    1026         EnumSet<LayerListenerType> listenersToFire = EnumSet.of(LayerListenerType.ACTIVE_LAYER_CHANGE);
    1027         if (setEditLayer) {
    1028             listenersToFire.addAll(setEditLayer(layers));
    1029         }
    1030 
    1031         return listenersToFire;
    1032     }
    1033 
    1034993    /**
    1035994     * Replies the currently active layer
     995     * <p>
     996     * You can use !{@link Main#getLayerManager()}.getActiveLayer() instead.
    1036997     *
    1037998     * @return the currently active layer (may be null)
    1038999     */
     1000    @Deprecated
    10391001    public Layer getActiveLayer() {
    1040         synchronized (layers) {
    1041             return activeLayer;
    1042         }
    1043     }
    1044 
    1045     private enum LayerListenerType {
    1046         ACTIVE_LAYER_CHANGE,
    1047         EDIT_LAYER_CHANGE
     1002        return layerManager.getActiveLayer();
    10481003    }
    10491004
    1050     /**
    1051      * This is called whenever one of active layer/edit layer or both may have been changed,
    1052      * @param oldActive The old active layer
    1053      * @param oldEdit The old edit layer.
    1054      * @param listenersToFire A mask of listeners to fire using {@link LayerListenerType}s
    1055      */
    1056     private void onActiveEditLayerChanged(final Layer oldActive, final OsmDataLayer oldEdit, EnumSet<LayerListenerType> listenersToFire) {
    1057         if (listenersToFire.contains(LayerListenerType.EDIT_LAYER_CHANGE)) {
    1058             onEditLayerChanged(oldEdit);
    1059         }
    1060         if (listenersToFire.contains(LayerListenerType.ACTIVE_LAYER_CHANGE)) {
    1061             onActiveLayerChanged(oldActive);
    1062         }
    1063     }
    1064 
    1065     private void onActiveLayerChanged(final Layer old) {
    1066         fireActiveLayerChanged(old, activeLayer);
    1067 
     1005    @Override
     1006    public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
    10681007        /* This only makes the buttons look disabled. Disabling the actions as well requires
    10691008         * the user to re-select the tool after i.e. moving a layer. While testing I found
    10701009         * that I switch layers and actions at the same time and it was annoying to mind the
    10711010         * order. This way it works as visual clue for new users */
    10721011        for (final AbstractButton b: Main.map.allMapModeButtons) {
    10731012            MapMode mode = (MapMode) b.getAction();
    1074             final boolean activeLayerSupported = mode.layerIsSupported(activeLayer);
     1013            final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
    10751014            if (activeLayerSupported) {
    10761015                Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
    10771016            } else {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    10841023            });
    10851024        }
    10861025        AudioPlayer.reset();
     1026        refreshTitle();
    10871027        repaint();
    10881028    }
    10891029
    10901030    /**
    10911031     * Replies the current edit layer, if any
     1032     * <p>
     1033     * You can use !{@link Main#getLayerManager()}.getEditLayer() instead. To be made private: end of 2016.
    10921034     *
    10931035     * @return the current edit layer. May be null.
    10941036     */
     1037    @Deprecated
    10951038    public OsmDataLayer getEditLayer() {
    1096         synchronized (layers) {
    1097             return editLayer;
    1098         }
     1039        return layerManager.getEditLayer();
    10991040    }
    11001041
    11011042    /**
    11021043     * replies true if the list of layers managed by this map view contain layer
     1044     * <p>
     1045     * You can use !{@link Main#getLayerManager()}.containsLayer() instead.
    11031046     *
    11041047     * @param layer the layer
    11051048     * @return true if the list of layers managed by this map view contain layer
    11061049     */
     1050    @Deprecated
    11071051    public boolean hasLayer(Layer layer) {
    1108         synchronized (layers) {
    1109             return layers.contains(layer);
    1110         }
     1052        return layerManager.containsLayer(layer);
    11111053    }
    11121054
    11131055    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    11791121     */
    11801122    protected void refreshTitle() {
    11811123        if (Main.parent != null) {
    1182             synchronized (layers) {
    1183                 boolean dirty = editLayer != null &&
    1184                         (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
    1185                 ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
    1186                 ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
    1187             }
     1124            OsmDataLayer editLayer = layerManager.getEditLayer();
     1125            boolean dirty = editLayer != null &&
     1126                    (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
     1127            ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
     1128            ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
    11881129        }
    11891130    }
    11901131
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    12031144    };
    12041145
    12051146    public void destroy() {
     1147        layerManager.removeLayerChangeListener(this);
     1148        layerManager.removeActiveLayerChangeListener(this);
    12061149        Main.pref.removePreferenceChangeListener(this);
    12071150        DataSet.removeSelectionListener(repaintSelectionChangedListener);
    12081151        MultipolygonCache.getInstance().clear(this);
    12091152        if (mapMover != null) {
    12101153            mapMover.destroy();
    12111154        }
    1212         synchronized (layers) {
    1213             activeLayer = null;
    1214             changedLayer = null;
    1215             editLayer = null;
    1216             layers.clear();
    1217             nonChangedLayers.clear();
    1218         }
     1155        nonChangedLayers.clear();
    12191156        synchronized (temporaryLayers) {
    12201157            temporaryLayers.clear();
    12211158        }
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    12231160
    12241161    @Override
    12251162    public void uploadDiscouragedChanged(OsmDataLayer layer, boolean newValue) {
    1226         if (layer == getEditLayer()) {
     1163        if (layer == layerManager.getEditLayer()) {
    12271164            refreshTitle();
    12281165        }
    12291166    }
  • new file 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
    new file mode 100644
    index 0000000..2040ff2
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.layer;
     3
     4import java.util.ArrayList;
     5import java.util.Collections;
     6import java.util.List;
     7import java.util.concurrent.CopyOnWriteArrayList;
     8
     9import org.openstreetmap.josm.gui.util.GuiHelper;
     10import org.openstreetmap.josm.tools.Utils;
     11
     12/**
     13 * This class handles the layer management.
     14 * <p>
     15 * This manager handles a list of layers with the first layer being the front layer.
     16 * <h1>Threading</h1>
     17 * Methods of this manager may be called from any thread in any order. Listeners are called while this layer manager is locked, so they should not block.
     18 *
     19 * @author Michael Zangl
     20 */
     21public class LayerManager {
     22    /**
     23     * Interface to notify listeners of a layer change.
     24     */
     25    public interface LayerChangeListener {
     26        /**
     27         * Notifies this listener that a layer has been added.
     28         * <p>
     29         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
     30         * @param e The new added layer event
     31         */
     32        void layerAdded(LayerAddEvent e);
     33
     34        /**
     35         * Notifies this listener that a layer is about to be removed.
     36         * <p>
     37         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
     38         * @param e The layer to be removed (as event)
     39         */
     40        void layerRemoving(LayerRemoveEvent e);
     41
     42        /**
     43         * Notifies this listener that the order of layers was changed.
     44         * <p>
     45         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
     46         * @param e The order change event.
     47         */
     48        void layerOrderChanged(LayerOrderChangeEvent e);
     49    }
     50
     51    protected static class LayerManagerEvent {
     52        private final LayerManager source;
     53
     54        LayerManagerEvent(LayerManager source) {
     55            this.source = source;
     56        }
     57
     58        public LayerManager getSource() {
     59            return source;
     60        }
     61    }
     62
     63    /**
     64     * The event that is fired whenever a layer was added.
     65     * @author Michael Zangl
     66     */
     67    public static class LayerAddEvent extends LayerManagerEvent {
     68        private final Layer addedLayer;
     69
     70        LayerAddEvent(LayerManager source, Layer addedLayer) {
     71            super(source);
     72            this.addedLayer = addedLayer;
     73        }
     74
     75        /**
     76         * Gets the layer that was added.
     77         * @return The added layer.
     78         */
     79        public Layer getAddedLayer() {
     80            return addedLayer;
     81        }
     82    }
     83
     84    /**
     85     * The event that is fired before removing a layer.
     86     * @author Michael Zangl
     87     */
     88    public static class LayerRemoveEvent extends LayerManagerEvent {
     89        private final Layer removedLayer;
     90
     91        LayerRemoveEvent(LayerManager source, Layer removedLayer) {
     92            super(source);
     93            this.removedLayer = removedLayer;
     94        }
     95
     96        /**
     97         * Gets the layer that is about to be removed.
     98         * @return The layer.
     99         */
     100        public Layer getRemovedLayer() {
     101            return removedLayer;
     102        }
     103    }
     104
     105    /**
     106     * An event that is fired whenever the order of layers changed.
     107     * <p>
     108     * We currently do not report the exact changes.
     109     * @author Michael Zangl
     110     */
     111    public static class LayerOrderChangeEvent extends LayerManagerEvent {
     112        LayerOrderChangeEvent(LayerManager source) {
     113            super(source);
     114        }
     115
     116    }
     117
     118    /**
     119     * This is the list of layers we manage.
     120     */
     121    private final List<Layer> layers = new ArrayList<>();
     122
     123    private final List<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
     124
     125    /**
     126     * Add a layer. The layer will be added at a given psoition.
     127     * @param layer The layer to add
     128     */
     129    public void addLayer(final Layer layer) {
     130        // we force this on to the EDT Thread to make events fire from there.
     131        // The synchronization lock needs to be held by the EDT.
     132        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
     133            @Override
     134            public void run() {
     135                realAddLayer(layer);
     136            }
     137        });
     138    }
     139
     140    protected synchronized void realAddLayer(Layer layer) {
     141        if (containsLayer(layer)) {
     142            throw new IllegalArgumentException("Cannot add a layer twice.");
     143        }
     144        LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition();
     145        int position = positionStrategy.getPosition(this);
     146        checkPosition(position);
     147        insertLayerAt(layer, position);
     148        fireLayerAdded(layer);
     149    }
     150
     151    /**
     152     * Remove the layer from the mapview. If the layer was in the list before,
     153     * an LayerChange event is fired.
     154     * @param layer The layer to remove
     155     */
     156    public void removeLayer(final Layer layer) {
     157        // we force this on to the EDT Thread to make events fire from there.
     158        // The synchronization lock needs to be held by the EDT.
     159        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
     160            @Override
     161            public void run() {
     162                realRemoveLayer(layer);
     163            }
     164        });
     165    }
     166
     167    protected synchronized void realRemoveLayer(Layer layer) {
     168        checkContainsLayer(layer);
     169
     170        fireLayerRemoving(layer);
     171        layers.remove(layer);
     172    }
     173
     174    /**
     175     * Move a layer to a new position.
     176     * @param layer The layer to move.
     177     * @param position The position.
     178     * @throws IndexOutOfBoundsException if the position is out of bounds.
     179     */
     180    public void moveLayer(final Layer layer, final int position) {
     181        // we force this on to the EDT Thread to make events fire from there.
     182        // The synchronization lock needs to be held by the EDT.
     183        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
     184            @Override
     185            public void run() {
     186                realMoveLayer(layer, position);
     187            }
     188        });
     189    }
     190
     191    protected synchronized void realMoveLayer(Layer layer, int position) {
     192        checkContainsLayer(layer);
     193        checkPosition(position);
     194
     195        int curLayerPos = layers.indexOf(layer);
     196        if (position == curLayerPos)
     197            return; // already in place.
     198        layers.remove(curLayerPos);
     199        insertLayerAt(layer, position);
     200        fireLayerOrderChanged();
     201    }
     202
     203    /**
     204     * Insert a layer at a given position.
     205     * @param layer The layer to add.
     206     * @param position The position on which we should add it.
     207     */
     208    private void insertLayerAt(Layer layer, int position) {
     209        if (position == layers.size()) {
     210            layers.add(layer);
     211        } else {
     212            layers.add(position, layer);
     213        }
     214    }
     215
     216    /**
     217     * Check if the (new) position is valid
     218     * @param position The position index
     219     * @throws IndexOutOfBoundsException if it is not.
     220     */
     221    private void checkPosition(int position) {
     222        if (position < 0 || position > layers.size()) {
     223            throw new IndexOutOfBoundsException("Position " + position + " out of range.");
     224        }
     225    }
     226
     227    /**
     228     * Gets an unmodifiable list of all layers that are currently in this manager. This list won't update once layers are added or removed.
     229     * @return The list of layers.
     230     */
     231    public List<Layer> getLayers() {
     232        return Collections.unmodifiableList(new ArrayList<>(layers));
     233    }
     234
     235    /**
     236     * Replies an unmodifiable list of layers of a certain type.
     237     *
     238     * Example:
     239     * <pre>
     240     *     List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
     241     * </pre>
     242     *
     243     * @param ofType The layer type.
     244     * @return an unmodifiable list of layers of a certain type.
     245     */
     246    public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
     247        return new ArrayList<>(Utils.filteredCollection(getLayers(), ofType));
     248    }
     249
     250    /**
     251     * replies true if the list of layers managed by this map view contain layer
     252     *
     253     * @param layer the layer
     254     * @return true if the list of layers managed by this map view contain layer
     255     */
     256    public synchronized boolean containsLayer(Layer layer) {
     257        return layers.contains(layer);
     258    }
     259
     260    protected void checkContainsLayer(Layer layer) {
     261        if (!containsLayer(layer)) {
     262            throw new IllegalArgumentException(layer + " is not managed by us.");
     263        }
     264    }
     265
     266    /**
     267     * Adds a layer change listener
     268     *
     269     * @param listener the listener.
     270     * @throws IllegalArgumentException If the listener was added twice.
     271     */
     272    public synchronized void addLayerChangeListener(LayerChangeListener listener) {
     273        addLayerChangeListener(listener, false);
     274    }
     275
     276    /**
     277     * Adds a layer change listener
     278     *
     279     * @param listener the listener.
     280     * @param fireAdd if we should fire an add event for every layer in this manager.
     281     * @throws IllegalArgumentException If the listener was added twice.
     282     */
     283    public synchronized void addLayerChangeListener(LayerChangeListener listener, boolean fireAdd) {
     284        if (layerChangeListeners.contains(listener)) {
     285            throw new IllegalArgumentException("Listener already registered.");
     286        }
     287        layerChangeListeners.add(listener);
     288        if (fireAdd) {
     289            for (Layer l : getLayers()) {
     290                listener.layerAdded(new LayerAddEvent(this, l));
     291            }
     292        }
     293    }
     294
     295    /**
     296     * Removes a layer change listener
     297     *
     298     * @param listener the listener. Ignored if null or already registered.
     299     */
     300    public synchronized void removeLayerChangeListener(LayerChangeListener listener) {
     301        removeLayerChangeListener(listener, false);
     302    }
     303
     304
     305    /**
     306     * Removes a layer change listener
     307     *
     308     * @param listener the listener.
     309     * @param fireRemove if we should fire a remove event for every layer in this manager.
     310     */
     311    public synchronized void removeLayerChangeListener(LayerChangeListener listener, boolean fireRemove) {
     312        if (!layerChangeListeners.remove(listener)) {
     313            throw new IllegalArgumentException("Listener was not registered before: " + listener);
     314        } else {
     315            if (fireRemove) {
     316                for (Layer l : getLayers()) {
     317                    listener.layerRemoving(new LayerRemoveEvent(this, l));
     318                }
     319            }
     320        }
     321    }
     322
     323    private void fireLayerAdded(Layer layer) {
     324        GuiHelper.assertCallFromEdt();
     325        LayerAddEvent e = new LayerAddEvent(this, layer);
     326        for (LayerChangeListener l : layerChangeListeners) {
     327            l.layerAdded(e);
     328        }
     329    }
     330
     331    private void fireLayerRemoving(Layer layer) {
     332        GuiHelper.assertCallFromEdt();
     333        LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
     334        for (LayerChangeListener l : layerChangeListeners) {
     335            l.layerRemoving(e);
     336        }
     337    }
     338
     339    private void fireLayerOrderChanged() {
     340        GuiHelper.assertCallFromEdt();
     341        LayerOrderChangeEvent e = new LayerOrderChangeEvent(this);
     342        for (LayerChangeListener l : layerChangeListeners) {
     343            l.layerOrderChanged(e);
     344        }
     345    }
     346}
  • new file src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java b/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java
    new file mode 100644
    index 0000000..04f5258
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.layer;
     3
     4import java.util.ArrayList;
     5import java.util.List;
     6import java.util.ListIterator;
     7import java.util.concurrent.CopyOnWriteArrayList;
     8
     9import org.openstreetmap.josm.data.osm.DataSet;
     10import org.openstreetmap.josm.gui.util.GuiHelper;
     11
     12/**
     13 * This class extends the layer manager by adding an active and an edit layer.
     14 * <p>
     15 * The active layer is the layer the user is currently working on.
     16 * <p>
     17 * The edit layer is an data layer that we currently work with.
     18 * @author Michael Zangl
     19 */
     20public class LayerManagerWithActive extends LayerManager {
     21    /**
     22     * This listener listens to changes of the active or the edit layer.
     23     * @author Michael Zangl
     24     *
     25     */
     26    public interface ActiveLayerChangeListener {
     27        /**
     28         * Called whenever the active or edit layer changed.
     29         * <p>
     30         * You can be sure that this layer is still contained in this set.
     31         * <p>
     32         * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
     33         * @param e The change event.
     34         */
     35        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
     36    }
     37
     38    /**
     39     * This event is fired whenever the active or the edit layer changes.
     40     * @author Michael Zangl
     41     */
     42    public class ActiveLayerChangeEvent extends LayerManagerEvent {
     43
     44        private final OsmDataLayer previousEditLayer;
     45
     46        private final Layer previousActiveLayer;
     47
     48        /**
     49         * Create a new {@link ActiveLayerChangeEvent}
     50         * @param source The source
     51         * @param previousEditLayer the previous edit layer
     52         * @param previousActiveLayer the previous active layer
     53         */
     54        ActiveLayerChangeEvent(LayerManagerWithActive source, OsmDataLayer previousEditLayer,
     55                Layer previousActiveLayer) {
     56            super(source);
     57            this.previousEditLayer = previousEditLayer;
     58            this.previousActiveLayer = previousActiveLayer;
     59        }
     60
     61        /**
     62         * Gets the edit layer that was previously used.
     63         * @return The old edit layer, <code>null</code> if there is none.
     64         */
     65        public OsmDataLayer getPreviousEditLayer() {
     66            return previousEditLayer;
     67        }
     68
     69        /**
     70         * Gets the active layer that was previously used.
     71         * @return The old active layer, <code>null</code> if there is none.
     72         */
     73        public Layer getPreviousActiveLayer() {
     74            return previousActiveLayer;
     75        }
     76
     77        @Override
     78        public LayerManagerWithActive getSource() {
     79            return (LayerManagerWithActive) super.getSource();
     80        }
     81    }
     82
     83    /**
     84     * The layer from the layers list that is currently active.
     85     */
     86    private Layer activeLayer;
     87
     88    /**
     89     * The edit layer is the current active data layer.
     90     */
     91    private OsmDataLayer editLayer;
     92
     93    private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
     94
     95    /**
     96     * Adds a active/edit layer change listener
     97     *
     98     * @param listener the listener.
     99     * @param initialFire fire a fake active-layer-changed-event right after adding
     100     * the listener. The previous layers will be null. The listener is notified in the current thread.
     101     */
     102    public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) {
     103        if (activeLayerChangeListeners.contains(listener)) {
     104            throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
     105        }
     106        activeLayerChangeListeners.add(listener);
     107        if (initialFire) {
     108            listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
     109        }
     110    }
     111
     112    /**
     113     * Removes an active/edit layer change listener.
     114     * @param listener the listener.
     115     */
     116    public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
     117        if (!activeLayerChangeListeners.contains(listener)) {
     118            throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
     119        }
     120        activeLayerChangeListeners.remove(listener);
     121    }
     122
     123    /**
     124     * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed.
     125     * @param layer The active layer.
     126     */
     127    public void setActiveLayer(final Layer layer) {
     128        // we force this on to the EDT Thread to make events fire from there.
     129        // The synchronization lock needs to be held by the EDT.
     130        GuiHelper.runInEDTAndWaitWithException(new Runnable() {
     131            @Override
     132            public void run() {
     133                realSetActiveLayer(layer);
     134            }
     135        });
     136    }
     137
     138    protected synchronized void realSetActiveLayer(final Layer layer) {
     139        // to be called in EDT thread
     140        checkContainsLayer(layer);
     141        setActiveLayer(layer, false);
     142    }
     143
     144    private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
     145        ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
     146        activeLayer = layer;
     147        if (activeLayer instanceof OsmDataLayer) {
     148            editLayer = (OsmDataLayer) activeLayer;
     149        } else if (forceEditLayerUpdate) {
     150            editLayer = null;
     151        }
     152        fireActiveLayerChange(event);
     153    }
     154
     155    private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
     156        GuiHelper.assertCallFromEdt();
     157        if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
     158            for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
     159                l.activeOrEditLayerChanged(event);
     160            }
     161        }
     162    }
     163
     164    @Override
     165    protected synchronized void realAddLayer(Layer layer) {
     166        super.realAddLayer(layer);
     167
     168        // update the active layer automatically.
     169        if (layer instanceof OsmDataLayer || activeLayer == null) {
     170            setActiveLayer(layer);
     171        }
     172    }
     173
     174    @Override
     175    protected synchronized void realRemoveLayer(Layer layer) {
     176        if (layer == activeLayer || layer == editLayer) {
     177            Layer nextActive = suggestNextActiveLayer(layer);
     178            setActiveLayer(nextActive, true);
     179        }
     180
     181        super.realRemoveLayer(layer);
     182    }
     183
     184    /**
     185     * Determines the next active data layer according to the following
     186     * rules:
     187     * <ul>
     188     *   <li>if there is at least one {@link OsmDataLayer} the first one
     189     *     becomes active</li>
     190     *   <li>otherwise, the top most layer of any type becomes active</li>
     191     * </ul>
     192     *
     193     * @param except A layer to ignore.
     194     * @return the next active data layer
     195     */
     196    private Layer suggestNextActiveLayer(Layer except) {
     197        List<Layer> layersList = new ArrayList<>(getLayers());
     198        layersList.remove(except);
     199        // First look for data layer
     200        for (Layer layer : layersList) {
     201            if (layer instanceof OsmDataLayer) {
     202                return layer;
     203            }
     204        }
     205
     206        // Then any layer
     207        if (!layersList.isEmpty())
     208            return layersList.get(0);
     209
     210        // and then give up
     211        return null;
     212    }
     213
     214    /**
     215     * Replies the currently active layer
     216     *
     217     * @return the currently active layer (may be null)
     218     */
     219    public synchronized Layer getActiveLayer() {
     220        return activeLayer;
     221    }
     222
     223    /**
     224     * Replies the current edit layer, if any
     225     *
     226     * @return the current edit layer. May be null.
     227     */
     228    public synchronized OsmDataLayer getEditLayer() {
     229        return editLayer;
     230    }
     231
     232    /**
     233     * Gets the data set of the active edit layer.
     234     * @return That data set, <code>null</code> if there is no edit layer.
     235     */
     236    public synchronized DataSet getEditDataSet() {
     237        if (editLayer != null) {
     238            return editLayer.data;
     239        } else {
     240            return null;
     241        }
     242    }
     243
     244
     245    /**
     246     * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
     247     * first, layer with the highest Z-Order last.
     248     * <p>
     249     * The active data layer is pulled above all adjacent data layers.
     250     *
     251     * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
     252     * first, layer with the highest Z-Order last.
     253     */
     254    public synchronized List<Layer> getVisibleLayersInZOrder() {
     255        List<Layer> ret = new ArrayList<>();
     256        // This is set while we delay the addition of the active layer.
     257        boolean activeLayerDelayed = false;
     258        List<Layer> layers = getLayers();
     259        for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
     260            Layer l = iterator.previous();
     261            if (!l.isVisible()) {
     262                // ignored
     263            } else if (l == activeLayer && l instanceof OsmDataLayer) {
     264                // delay and add after the current block of OsmDataLayer
     265                activeLayerDelayed = true;
     266            } else {
     267                if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
     268                    // add active layer before the current one.
     269                    ret.add(activeLayer);
     270                    activeLayerDelayed = false;
     271                }
     272                // Add this layer now
     273                ret.add(l);
     274            }
     275        }
     276        if (activeLayerDelayed) {
     277            ret.add(activeLayer);
     278        }
     279        return ret;
     280    }
     281}
  • src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java b/src/org/openstreetmap/josm/gui/layer/LayerPositionStrategy.java
    index 46d88d4..d577e30 100644
    a b package org.openstreetmap.josm.gui.layer;  
    33
    44import java.util.List;
    55
    6 import org.openstreetmap.josm.gui.MapView;
    76import org.openstreetmap.josm.tools.Predicate;
    87import org.openstreetmap.josm.tools.Predicates;
    98
    public abstract class LayerPositionStrategy {  
    1918     */
    2019    public static final LayerPositionStrategy IN_FRONT = new LayerPositionStrategy() {
    2120        @Override
    22         public int getPosition(MapView manager) {
     21        public int getPosition(LayerManager manager) {
    2322            return 0;
    2423        }
    2524    };
    public abstract class LayerPositionStrategy {  
    7271    public static LayerPositionStrategy inFrontOfFirst(final Predicate<Layer> what) {
    7372        return new LayerPositionStrategy() {
    7473            @Override
    75             public int getPosition(MapView manager) {
    76                 List<Layer> layers = manager.getAllLayersAsList();
     74            public int getPosition(LayerManager manager) {
     75                List<Layer> layers = manager.getLayers();
    7776                for (int i = 0; i < layers.size(); i++) {
    7877                    if (what.evaluate(layers.get(i))) {
    7978                        return i;
    public abstract class LayerPositionStrategy {  
    9291    public static LayerPositionStrategy afterLast(final Predicate<Layer> what) {
    9392        return new LayerPositionStrategy() {
    9493            @Override
    95             public int getPosition(MapView manager) {
    96                 List<Layer> layers = manager.getAllLayersAsList();
     94            public int getPosition(LayerManager manager) {
     95                List<Layer> layers = manager.getLayers();
    9796                for (int i = layers.size() - 1; i >= 0; i--) {
    9897                    if (what.evaluate(layers.get(i))) {
    9998                        return i + 1;
    public abstract class LayerPositionStrategy {  
    109108     * @param manager The layer manager to insert the layer in.
    110109     * @return The position in the range 0...layers.size
    111110     */
    112     public abstract int getPosition(MapView manager);
     111    public abstract int getPosition(LayerManager manager);
    113112}
  • src/org/openstreetmap/josm/gui/util/GuiHelper.java

    diff --git a/src/org/openstreetmap/josm/gui/util/GuiHelper.java b/src/org/openstreetmap/josm/gui/util/GuiHelper.java
    index cc87355..a627abf 100644
    a b public final class GuiHelper {  
    130130    }
    131131
    132132    /**
     133     * Executes synchronously a runnable in
     134     * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html">Event Dispatch Thread</a>.
     135     * <p>
     136     * Passes on the exception that was thrown to the thread calling this. The exception is wrapped in a {@link RuntimeException} if it was a normal {@link Throwable}.
     137     * @param task The runnable to execute
     138     * @see SwingUtilities#invokeAndWait
     139     */
     140    public static void runInEDTAndWaitWithException(Runnable task) {
     141        if (SwingUtilities.isEventDispatchThread()) {
     142            task.run();
     143        } else {
     144            try {
     145                SwingUtilities.invokeAndWait(task);
     146            } catch (InterruptedException e) {
     147                Main.error(e);
     148            } catch (InvocationTargetException e) {
     149                if (e.getCause() instanceof RuntimeException) {
     150                    throw (RuntimeException) e.getCause();
     151                } else {
     152                    throw new RuntimeException("Exception while clling " + task, e.getCause());
     153                }
     154            }
     155        }
     156    }
     157
     158    /**
    133159     * Executes synchronously a callable in
    134160     * <a href="http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html">Event Dispatch Thread</a>
    135161     * and return a value.
    public final class GuiHelper {  
    159185    }
    160186
    161187    /**
     188     * This function fails if it was not called from the EDT thread.
     189     * @throws IllegalStateException if called from wrong thread.
     190     */
     191    public static void assertCallFromEdt() {
     192        if (!SwingUtilities.isEventDispatchThread()) {
     193            throw new IllegalStateException(
     194                    "Needs to be called from the EDT thread, not from " + Thread.currentThread().getName());
     195        }
     196    }
     197
     198    /**
    162199     * Warns user about a dangerous action requiring confirmation.
    163200     * @param title Title of dialog
    164201     * @param content Content of dialog
  • test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java

    diff --git a/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java b/test/unit/org/openstreetmap/josm/actions/mapmode/MapViewMock.java
    index cd0e944..a05c332 100644
    a b import java.awt.Cursor;  
    55import java.awt.event.MouseListener;
    66import java.awt.geom.Point2D;
    77
     8import org.openstreetmap.josm.Main;
    89import org.openstreetmap.josm.data.coor.EastNorth;
    910import org.openstreetmap.josm.data.osm.DataSet;
    1011import org.openstreetmap.josm.gui.MapView;
    class MapViewMock extends MapView {  
    1617    private final transient DataSet currentDataSet;
    1718
    1819    MapViewMock(DataSet dataSet, OsmDataLayer layer) {
    19         super(null, null);
     20        super(Main.getLayerManager(), null, null);
    2021        this.layer = layer;
    2122        this.currentDataSet = dataSet;
    2223    }
  • new file test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerTest.java
    new file mode 100644
    index 0000000..f4a5139
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.layer;
     3
     4import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertFalse;
     6import static org.junit.Assert.assertNotNull;
     7import static org.junit.Assert.assertNull;
     8import static org.junit.Assert.assertSame;
     9import static org.junit.Assert.assertTrue;
     10import static org.junit.Assert.fail;
     11
     12import java.awt.Graphics2D;
     13import java.util.ArrayList;
     14import java.util.Arrays;
     15
     16import javax.swing.Action;
     17import javax.swing.Icon;
     18
     19import org.junit.Before;
     20import org.junit.Test;
     21import org.openstreetmap.josm.data.Bounds;
     22import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
     23import org.openstreetmap.josm.gui.MapView;
     24import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
     25import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
     26import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     27import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
     28import org.openstreetmap.josm.gui.util.GuiHelper;
     29import org.openstreetmap.josm.tools.Predicates;
     30
     31/**
     32 * Test the {@link LayerManager} class.
     33 * @author Michael Zangl
     34 *
     35 */
     36public class LayerManagerTest {
     37
     38    protected static class AbstractTestLayer extends Layer {
     39        protected AbstractTestLayer() {
     40            super("Test Layer");
     41        }
     42
     43        @Override
     44        public void paint(Graphics2D g, MapView mv, Bounds bbox) {
     45        }
     46
     47        @Override
     48        public void visitBoundingBox(BoundingXYVisitor v) {
     49        }
     50
     51        @Override
     52        public void mergeFrom(Layer from) {
     53        }
     54
     55        @Override
     56        public boolean isMergable(Layer other) {
     57            return false;
     58        }
     59
     60        @Override
     61        public String getToolTipText() {
     62            return null;
     63        }
     64
     65        @Override
     66        public Action[] getMenuEntries() {
     67            return null;
     68        }
     69
     70        @Override
     71        public Object getInfoComponent() {
     72            return null;
     73        }
     74
     75        @Override
     76        public Icon getIcon() {
     77            return null;
     78        }
     79
     80        @Override
     81        public LayerPositionStrategy getDefaultLayerPosition() {
     82            return LayerPositionStrategy.afterLast(Predicates.<Layer> alwaysTrue());
     83        }
     84    }
     85
     86    protected static class AbstractTestLayer2 extends AbstractTestLayer {}
     87
     88    /**
     89     * Intercepts the events for easier testing.
     90     * @author Michael Zangl
     91     *
     92     */
     93    protected class CapturingLayerChangeListener implements LayerChangeListener {
     94        private LayerAddEvent layerAdded;
     95        private LayerRemoveEvent layerRemoved;
     96        private LayerOrderChangeEvent layerOrderChanged;
     97
     98        @Override
     99        public void layerAdded(LayerAddEvent e) {
     100            GuiHelper.assertCallFromEdt();
     101            assertNull(layerAdded);
     102            assertSame(layerManager, e.getSource());
     103            layerAdded = e;
     104        }
     105
     106        @Override
     107        public void layerRemoving(LayerRemoveEvent e) {
     108            GuiHelper.assertCallFromEdt();
     109            assertNull(layerRemoved);
     110            assertSame(layerManager, e.getSource());
     111            layerRemoved = e;
     112        }
     113
     114        @Override
     115        public void layerOrderChanged(LayerOrderChangeEvent e) {
     116            GuiHelper.assertCallFromEdt();
     117            assertNull(layerOrderChanged);
     118            assertSame(layerManager, e.getSource());
     119            layerOrderChanged = e;
     120        }
     121
     122    }
     123
     124    protected LayerManager layerManager;
     125
     126    /**
     127     * Set up test layer manager.
     128     */
     129    @Before
     130    public void setUp()   {
     131        layerManager = new LayerManager();
     132    }
     133
     134    /**
     135     * {@link LayerManager#addLayer(Layer)}
     136     */
     137    @Test
     138    public void testAddLayer() {
     139        Layer layer1 = new AbstractTestLayer() {
     140            @Override
     141            public LayerPositionStrategy getDefaultLayerPosition() {
     142                return LayerPositionStrategy.IN_FRONT;
     143            }
     144
     145            @Override
     146            public boolean isBackgroundLayer() {
     147                return true;
     148            }
     149        };
     150        Layer layer2 = new AbstractTestLayer() {
     151            @Override
     152            public LayerPositionStrategy getDefaultLayerPosition() {
     153                return LayerPositionStrategy.IN_FRONT;
     154            }
     155        };
     156        Layer layer3 = new AbstractTestLayer() {
     157            @Override
     158            public LayerPositionStrategy getDefaultLayerPosition() {
     159                return LayerPositionStrategy.BEFORE_FIRST_BACKGROUND_LAYER;
     160            }
     161        };
     162
     163        layerManager.addLayer(layer1);
     164        assertEquals(layerManager.getLayers(), Arrays.asList(layer1));
     165        layerManager.addLayer(layer2);
     166        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer1));
     167        layerManager.addLayer(layer3);
     168        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer3, layer1));
     169
     170        // event
     171        AbstractTestLayer layer4 = new AbstractTestLayer();
     172        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     173        layerManager.addLayerChangeListener(l);
     174        layerManager.addLayer(layer4);
     175
     176        assertSame(layer4, l.layerAdded.getAddedLayer());
     177        assertNull(l.layerRemoved);
     178        assertNull(l.layerOrderChanged);
     179    }
     180
     181    /**
     182     * {@link LayerManager#addLayer(Layer)}: duplicate layers
     183     */
     184    @Test(expected = IllegalArgumentException.class)
     185    public void testAddLayerFails() {
     186        AbstractTestLayer layer1 = new AbstractTestLayer();
     187        layerManager.addLayer(layer1);
     188        layerManager.addLayer(layer1);
     189    }
     190
     191    /**
     192     * {@link LayerManager#addLayer(Layer)}: illegal default layer position
     193     */
     194    @Test(expected = IndexOutOfBoundsException.class)
     195    public void testAddLayerIllegalPosition() {
     196        AbstractTestLayer layer1 = new AbstractTestLayer() {
     197            @Override
     198            public LayerPositionStrategy getDefaultLayerPosition() {
     199                return new LayerPositionStrategy() {
     200                    @Override
     201                    public int getPosition(LayerManager manager) {
     202                        return 42;
     203                    }
     204                };
     205            }
     206        };
     207        layerManager.addLayer(layer1);
     208    }
     209
     210    /**
     211     * {@link LayerManager#removeLayer(Layer)}
     212     */
     213    @Test
     214    public void testRemoveLayer() {
     215        AbstractTestLayer layer1 = new AbstractTestLayer();
     216        AbstractTestLayer layer2 = new AbstractTestLayer();
     217        layerManager.addLayer(layer1);
     218        layerManager.addLayer(layer2);
     219        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
     220
     221        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     222        layerManager.addLayerChangeListener(l);
     223        layerManager.removeLayer(layer2);
     224        assertEquals(layerManager.getLayers(), Arrays.asList(layer1));
     225
     226        assertNull(l.layerAdded);
     227        assertSame(layer2, l.layerRemoved.getRemovedLayer());
     228        assertNull(l.layerOrderChanged);
     229    }
     230
     231    /**
     232     * {@link LayerManager#moveLayer(Layer, int)}
     233     */
     234    @Test
     235    public void testMoveLayer() {
     236        AbstractTestLayer layer1 = new AbstractTestLayer();
     237        AbstractTestLayer layer2 = new AbstractTestLayer();
     238        layerManager.addLayer(layer1);
     239        layerManager.addLayer(layer2);
     240        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
     241
     242        layerManager.moveLayer(layer2, 0);
     243        assertEquals(layerManager.getLayers(), Arrays.asList(layer2, layer1));
     244
     245        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     246        layerManager.addLayerChangeListener(l);
     247        layerManager.moveLayer(layer2, 1);
     248        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
     249
     250        assertNull(l.layerAdded);
     251        assertNull(l.layerRemoved);
     252        assertNotNull(l.layerOrderChanged);
     253
     254        // This should not change anything and not fire any event
     255        layerManager.moveLayer(layer2, 1);
     256        assertEquals(layerManager.getLayers(), Arrays.asList(layer1, layer2));
     257    }
     258
     259    /**
     260     * {@link LayerManager#moveLayer(Layer, int)} fails for wrong index
     261     */
     262    @Test(expected = IndexOutOfBoundsException.class)
     263    public void testMoveLayerFailsRange() {
     264        AbstractTestLayer layer1 = new AbstractTestLayer();
     265        AbstractTestLayer layer2 = new AbstractTestLayer();
     266        layerManager.addLayer(layer1);
     267        layerManager.addLayer(layer2);
     268        layerManager.moveLayer(layer2, 2);
     269    }
     270
     271    /**
     272     * {@link LayerManager#moveLayer(Layer, int)} fails for wrong layer
     273     */
     274    @Test(expected = IllegalArgumentException.class)
     275    public void testMoveLayerFailsNotInList() {
     276        AbstractTestLayer layer1 = new AbstractTestLayer();
     277        AbstractTestLayer layer2 = new AbstractTestLayer();
     278        layerManager.addLayer(layer1);
     279        layerManager.moveLayer(layer2, 0);
     280    }
     281
     282    /**
     283     * {@link LayerManager#getLayers()} unmodifiable
     284     */
     285    @Test(expected = UnsupportedOperationException.class)
     286    public void testGetLayers() {
     287        // list should be immutable
     288        AbstractTestLayer layer1 = new AbstractTestLayer();
     289        AbstractTestLayer layer2 = new AbstractTestLayer();
     290        layerManager.addLayer(layer1);
     291        layerManager.addLayer(layer2);
     292        layerManager.getLayers().remove(0);
     293    }
     294
     295    /**
     296     * {@link LayerManager#getLayersOfType(Class)}
     297     */
     298    @Test
     299    public void testGetLayersOfType() {
     300        AbstractTestLayer2 layer1 = new AbstractTestLayer2();
     301        AbstractTestLayer2 layer2 = new AbstractTestLayer2();
     302        layerManager.addLayer(layer1);
     303        layerManager.addLayer(new AbstractTestLayer());
     304        layerManager.addLayer(layer2);
     305
     306        assertEquals(layerManager.getLayersOfType(AbstractTestLayer2.class), Arrays.asList(layer1, layer2));
     307    }
     308
     309    /**
     310     * {@link LayerManager#containsLayer(Layer)}
     311     */
     312    @Test
     313    public void testContainsLayer() {
     314        AbstractTestLayer layer = new AbstractTestLayer();
     315        layerManager.addLayer(layer);
     316        layerManager.addLayer(new AbstractTestLayer());
     317
     318        assertTrue(layerManager.containsLayer(layer));
     319        assertFalse(layerManager.containsLayer(new AbstractTestLayer()));
     320    }
     321
     322    /**
     323     * {@link LayerManager#addLayerChangeListener(LayerChangeListener)}
     324     */
     325    @Test
     326    public void testAddLayerChangeListener() {
     327        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     328        layerManager.addLayerChangeListener(l);
     329        assertNull(l.layerAdded);
     330        assertNull(l.layerRemoved);
     331        assertNull(l.layerOrderChanged);
     332    }
     333
     334    /**
     335     * {@link LayerManager#addLayerChangeListener(LayerChangeListener)} twice
     336     */
     337    @Test(expected = IllegalArgumentException.class)
     338    public void testAddLayerChangeListenerDupplicates() {
     339        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     340        layerManager.addLayerChangeListener(l);
     341        layerManager.addLayerChangeListener(l);
     342    }
     343
     344    /**
     345     * {@link LayerManager#addLayerChangeListener(LayerChangeListener, boolean)} fires fake add events
     346     */
     347    @Test
     348    public void testAddLayerChangeListenerFire() {
     349        final ArrayList<Layer> fired = new ArrayList<>();
     350        AbstractTestLayer layer1 = new AbstractTestLayer();
     351        AbstractTestLayer layer2 = new AbstractTestLayer();
     352        layerManager.addLayer(layer1);
     353        layerManager.addLayer(layer2);
     354        layerManager.addLayerChangeListener(new LayerChangeListener() {
     355            @Override
     356            public void layerRemoving(LayerRemoveEvent e) {
     357                fail();
     358            }
     359
     360            @Override
     361            public void layerOrderChanged(LayerOrderChangeEvent e) {
     362                fail();
     363            }
     364
     365            @Override
     366            public void layerAdded(LayerAddEvent e) {
     367                fired.add(e.getAddedLayer());
     368            }
     369        }, true);
     370
     371        assertEquals(Arrays.asList(layer1, layer2), fired);
     372    }
     373
     374    /**
     375     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener)}
     376     */
     377    @Test
     378    public void testRemoveLayerChangeListener() {
     379        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     380        layerManager.addLayerChangeListener(l);
     381        layerManager.addLayer(new AbstractTestLayer());
     382        layerManager.removeLayerChangeListener(l);
     383        layerManager.addLayer(new AbstractTestLayer());
     384        // threw exception when fired twice.
     385        assertNotNull(l.layerAdded);
     386        assertNull(l.layerRemoved);
     387        assertNull(l.layerOrderChanged);
     388    }
     389
     390    /**
     391     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener)} listener not in list
     392     */
     393    @Test(expected = IllegalArgumentException.class)
     394    public void testRemoveLayerChangeListenerNotAdded() {
     395        CapturingLayerChangeListener l = new CapturingLayerChangeListener();
     396        layerManager.removeLayerChangeListener(l);
     397    }
     398
     399    /**
     400     * {@link LayerManager#removeLayerChangeListener(LayerChangeListener, boolean)} fires fake remove events
     401     */
     402    @Test
     403    public void testRemoveLayerChangeListenerFire() {
     404        final ArrayList<Layer> fired = new ArrayList<>();
     405        AbstractTestLayer layer1 = new AbstractTestLayer();
     406        AbstractTestLayer layer2 = new AbstractTestLayer();
     407        layerManager.addLayer(layer1);
     408        layerManager.addLayer(layer2);
     409        LayerChangeListener listener = new LayerChangeListener() {
     410            @Override
     411            public void layerRemoving(LayerRemoveEvent e) {
     412                fired.add(e.getRemovedLayer());
     413            }
     414
     415            @Override
     416            public void layerOrderChanged(LayerOrderChangeEvent e) {
     417                fail();
     418            }
     419
     420            @Override
     421            public void layerAdded(LayerAddEvent e) {
     422                fail();
     423            }
     424        };
     425        layerManager.addLayerChangeListener(listener, false);
     426        layerManager.removeLayerChangeListener(listener, true);
     427
     428        assertEquals(Arrays.asList(layer1, layer2), fired);
     429    }
     430
     431}
  • new file test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java

    diff --git a/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java b/test/unit/org/openstreetmap/josm/gui/layer/LayerManagerWithActiveTest.java
    new file mode 100644
    index 0000000..cd1e8f1
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.layer;
     3
     4import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertNull;
     6import static org.junit.Assert.assertSame;
     7
     8import java.util.Arrays;
     9
     10import org.junit.Before;
     11import org.junit.BeforeClass;
     12import org.junit.Test;
     13import org.openstreetmap.josm.JOSMFixture;
     14import org.openstreetmap.josm.data.osm.DataSet;
     15import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeEvent;
     16import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener;
     17import org.openstreetmap.josm.gui.util.GuiHelper;
     18import org.openstreetmap.josm.tools.Predicates;
     19
     20/**
     21 * Tests {@link LayerManagerWithActive}
     22 * @author Michael Zangl
     23 *
     24 */
     25public class LayerManagerWithActiveTest extends LayerManagerTest {
     26
     27    private LayerManagerWithActive layerManagerWithActive;
     28
     29    private class CapturingActiveLayerChangeListener implements ActiveLayerChangeListener {
     30        private ActiveLayerChangeEvent lastEvent;
     31
     32        @Override
     33        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
     34            assertSame(layerManager, e.getSource());
     35            lastEvent = e;
     36        }
     37    }
     38
     39    private final class CapturingThreadCheckingActiveLayerChangeListener extends CapturingActiveLayerChangeListener {
     40        @Override
     41        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
     42            GuiHelper.assertCallFromEdt();
     43            super.activeOrEditLayerChanged(e);
     44        }
     45    }
     46
     47    protected class AbstractTestOsmLayer extends OsmDataLayer {
     48        public AbstractTestOsmLayer() {
     49            super(new DataSet(), "OSM layer", null);
     50        }
     51
     52        @Override
     53        public LayerPositionStrategy getDefaultLayerPosition() {
     54            return LayerPositionStrategy.afterLast(Predicates.<Layer> alwaysTrue());
     55        }
     56    }
     57
     58    @BeforeClass
     59    public static void setUpClass() {
     60        JOSMFixture.createUnitTestFixture().init();
     61    }
     62
     63    @Override
     64    @Before
     65    public void setUp() {
     66        layerManager = layerManagerWithActive = new LayerManagerWithActive();
     67    }
     68
     69    @Test
     70    public void testAddLayerSetsActiveLayer() {
     71        AbstractTestLayer layer1 = new AbstractTestLayer();
     72        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     73        AbstractTestLayer layer3 = new AbstractTestLayer();
     74        assertNull(layerManagerWithActive.getActiveLayer());
     75        assertNull(layerManagerWithActive.getEditLayer());
     76        layerManagerWithActive.addLayer(layer1);
     77        assertSame(layer1, layerManagerWithActive.getActiveLayer());
     78        assertNull(layerManagerWithActive.getEditLayer());
     79        layerManagerWithActive.addLayer(layer2);
     80        assertSame(layer2, layerManagerWithActive.getActiveLayer());
     81        assertSame(layer2, layerManagerWithActive.getEditLayer());
     82        layerManagerWithActive.addLayer(layer3);
     83        assertSame(layer2, layerManagerWithActive.getActiveLayer());
     84        assertSame(layer2, layerManagerWithActive.getEditLayer());
     85    }
     86
     87    @Test
     88    public void testRemoveLayerUnsetsActiveLayer() {
     89        AbstractTestLayer layer1 = new AbstractTestLayer();
     90        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     91        AbstractTestLayer layer3 = new AbstractTestLayer();
     92        AbstractTestOsmLayer layer4 = new AbstractTestOsmLayer();
     93        layerManagerWithActive.addLayer(layer1);
     94        layerManagerWithActive.addLayer(layer2);
     95        layerManagerWithActive.addLayer(layer3);
     96        layerManagerWithActive.addLayer(layer4);
     97        assertSame(layer4, layerManagerWithActive.getActiveLayer());
     98        assertSame(layer4, layerManagerWithActive.getEditLayer());
     99        layerManagerWithActive.removeLayer(layer4);
     100        //prefer osm layers
     101        assertSame(layer2, layerManagerWithActive.getActiveLayer());
     102        assertSame(layer2, layerManagerWithActive.getEditLayer());
     103        layerManagerWithActive.removeLayer(layer2);
     104        assertSame(layer1, layerManagerWithActive.getActiveLayer());
     105        assertNull(layerManagerWithActive.getEditLayer());
     106
     107        layerManagerWithActive.removeLayer(layer1);
     108        layerManagerWithActive.removeLayer(layer3);
     109        assertNull(layerManagerWithActive.getActiveLayer());
     110        assertNull(layerManagerWithActive.getEditLayer());
     111    }
     112
     113    @Test
     114    public void testAddActiveLayerChangeListener() {
     115        AbstractTestLayer layer1 = new AbstractTestLayer();
     116        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     117        layerManagerWithActive.addLayer(layer1);
     118        layerManagerWithActive.addLayer(layer2);
     119
     120        CapturingActiveLayerChangeListener listener = new CapturingThreadCheckingActiveLayerChangeListener();
     121        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
     122        assertNull(listener.lastEvent);
     123
     124        CapturingActiveLayerChangeListener listener2 = new CapturingActiveLayerChangeListener();
     125        layerManagerWithActive.addActiveLayerChangeListener(listener2, true);
     126        assertSame(listener2.lastEvent.getPreviousActiveLayer(), null);
     127        assertSame(listener2.lastEvent.getPreviousEditLayer(), null);
     128
     129        layerManagerWithActive.setActiveLayer(layer1);
     130        assertSame(listener2.lastEvent.getPreviousActiveLayer(), layer2);
     131        assertSame(listener2.lastEvent.getPreviousEditLayer(), layer2);
     132
     133        layerManagerWithActive.setActiveLayer(layer2);
     134        assertSame(listener2.lastEvent.getPreviousActiveLayer(), layer1);
     135        assertSame(listener2.lastEvent.getPreviousEditLayer(), layer2);
     136    }
     137
     138    /**
     139     * Test if {@link LayerManagerWithActive#addActiveLayerChangeListener(ActiveLayerChangeListener)} prevents listener from beeing added twice.
     140     */
     141    @Test(expected = IllegalArgumentException.class)
     142    public void testAddActiveLayerChangeListenerTwice() {
     143        CapturingActiveLayerChangeListener listener = new CapturingActiveLayerChangeListener();
     144        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
     145        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
     146    }
     147
     148    /**
     149     * Test if {@link LayerManagerWithActive#removeActiveLayerChangeListener(ActiveLayerChangeListener)} works.
     150     */
     151    @Test
     152    public void testRemoveActiveLayerChangeListener() {
     153        AbstractTestLayer layer1 = new AbstractTestLayer();
     154        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     155        layerManagerWithActive.addLayer(layer1);
     156        layerManagerWithActive.addLayer(layer2);
     157
     158        CapturingActiveLayerChangeListener listener = new CapturingActiveLayerChangeListener();
     159        layerManagerWithActive.addActiveLayerChangeListener(listener, false);
     160        layerManagerWithActive.removeActiveLayerChangeListener(listener);
     161
     162        layerManagerWithActive.setActiveLayer(layer2);
     163        assertNull(listener.lastEvent);
     164    }
     165
     166    /**
     167     * Test if {@link LayerManagerWithActive#removeActiveLayerChangeListener(ActiveLayerChangeListener)} checks if listener is in list.
     168     */
     169    @Test(expected = IllegalArgumentException.class)
     170    public void testRemoveActiveLayerChangeListenerNotInList() {
     171        layerManagerWithActive.removeActiveLayerChangeListener(new CapturingActiveLayerChangeListener());
     172    }
     173
     174    /**
     175     * Tests {@link LayerManagerWithActive#setActiveLayer(Layer)} and {@link LayerManagerWithActive#getActiveLayer()}.
     176     * <p>
     177     * Edit and active layer getters are also tested in {@link #testAddLayerSetsActiveLayer()}
     178     */
     179    @Test
     180    public void testSetGetActiveLayer() {
     181        AbstractTestLayer layer1 = new AbstractTestLayer();
     182        AbstractTestLayer layer2 = new AbstractTestLayer();
     183        layerManagerWithActive.addLayer(layer1);
     184        layerManagerWithActive.addLayer(layer2);
     185
     186        layerManagerWithActive.setActiveLayer(layer1);
     187        assertSame(layer1, layerManagerWithActive.getActiveLayer());
     188
     189        layerManagerWithActive.setActiveLayer(layer2);
     190        assertSame(layer2, layerManagerWithActive.getActiveLayer());
     191    }
     192
     193    /**
     194     * Tests {@link LayerManagerWithActive#getEditDataSet()}
     195     */
     196    @Test
     197    public void testGetEditDataSet() {
     198        assertNull(layerManagerWithActive.getEditDataSet());
     199        AbstractTestLayer layer0 = new AbstractTestLayer();
     200        layerManagerWithActive.addLayer(layer0);
     201        assertNull(layerManagerWithActive.getEditDataSet());
     202
     203        AbstractTestOsmLayer layer1 = new AbstractTestOsmLayer();
     204        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     205        layerManagerWithActive.addLayer(layer1);
     206        layerManagerWithActive.addLayer(layer2);
     207
     208        layerManagerWithActive.setActiveLayer(layer1);
     209        assertSame(layer1.data, layerManagerWithActive.getEditDataSet());
     210
     211        layerManagerWithActive.setActiveLayer(layer2);
     212        assertSame(layer2.data, layerManagerWithActive.getEditDataSet());
     213    }
     214
     215    /**
     216     * Tests {@link LayerManagerWithActive#getVisibleLayersInZOrder()}
     217     */
     218    @Test
     219    public void testGetVisibleLayersInZOrder() {
     220        AbstractTestOsmLayer layer1 = new AbstractTestOsmLayer();
     221        AbstractTestOsmLayer layer2 = new AbstractTestOsmLayer();
     222        AbstractTestLayer layer3 = new AbstractTestLayer();
     223        layer3.setVisible(false);
     224        AbstractTestOsmLayer layer4 = new AbstractTestOsmLayer();
     225        AbstractTestLayer layer5 = new AbstractTestLayer();
     226        AbstractTestOsmLayer layer6 = new AbstractTestOsmLayer();
     227        AbstractTestOsmLayer layer7 = new AbstractTestOsmLayer();
     228        layerManagerWithActive.addLayer(layer1);
     229        layerManagerWithActive.addLayer(layer2);
     230        layerManagerWithActive.addLayer(layer3);
     231        layerManagerWithActive.addLayer(layer4);
     232        layerManagerWithActive.addLayer(layer5);
     233        layerManagerWithActive.addLayer(layer6);
     234        layerManagerWithActive.addLayer(layer7);
     235
     236        layerManagerWithActive.setActiveLayer(layer1);
     237        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
     238                layerManagerWithActive.getVisibleLayersInZOrder());
     239        layerManagerWithActive.setActiveLayer(layer4);
     240        assertEquals(Arrays.asList(layer7, layer6, layer5, layer2, layer1, layer4),
     241                layerManagerWithActive.getVisibleLayersInZOrder());
     242
     243        // should not be moved ouside edit layer block
     244        layerManagerWithActive.setActiveLayer(layer6);
     245        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
     246                layerManagerWithActive.getVisibleLayersInZOrder());
     247        layerManagerWithActive.setActiveLayer(layer7);
     248        assertEquals(Arrays.asList(layer6, layer7, layer5, layer4, layer2, layer1),
     249                layerManagerWithActive.getVisibleLayersInZOrder());
     250
     251        // ignored
     252        layerManagerWithActive.setActiveLayer(layer3);
     253        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
     254                layerManagerWithActive.getVisibleLayersInZOrder());
     255        layerManagerWithActive.setActiveLayer(layer5);
     256        assertEquals(Arrays.asList(layer7, layer6, layer5, layer4, layer2, layer1),
     257                layerManagerWithActive.getVisibleLayersInZOrder());
     258
     259    }
     260
     261}