Ticket #11631: 0005-Synchronized-access-to-MapView-layers.patch

File 0005-Synchronized-access-to-MapView-layers.patch, 29.2 KB (added by michael2402, 11 years ago)
  • src/org/openstreetmap/josm/gui/MapView.java

    From f743daa7798b17cb095c62cecd9df959062a13b5 Mon Sep 17 00:00:00 2001
    From: Michael Zangl <michael.zangl@student.kit.edu>
    Date: Wed, 1 Jul 2015 14:33:56 +0200
    Subject: [PATCH 5/8] Synchronized access to MapView#layers
    
    ---
     src/org/openstreetmap/josm/gui/MapView.java | 550 +++++++++++++++++++---------
     1 file changed, 384 insertions(+), 166 deletions(-)
    
    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index 0278ca2..a32db86 100644
    a b import java.beans.PropertyChangeListener;  
    2424import java.util.ArrayList;
    2525import java.util.Collection;
    2626import java.util.Collections;
    27 import java.util.Comparator;
    2827import java.util.LinkedList;
    2928import java.util.List;
     29import java.util.ListIterator;
    3030import java.util.concurrent.CopyOnWriteArrayList;
     31import java.util.concurrent.locks.ReentrantReadWriteLock;
    3132
    3233import javax.swing.AbstractButton;
    3334import javax.swing.ActionMap;
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    104105        void layerRemoved(Layer oldLayer);
    105106    }
    106107
     108    /**
     109     * An interface that needs to be implemented in order to listen for changes to the active edit layer.
     110     */
    107111    public interface EditLayerChangeListener {
     112
     113        /**
     114         * Called after the active edit layer was changed.
     115         * @param oldLayer The old edit layer
     116         * @param newLayer The current (new) edit layer
     117         */
    108118        void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer);
    109119    }
    110120
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    179189        }
    180190    }
    181191
    182     protected static void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
     192    /**
     193     * Calls the {@link LayerChangeListener#activeLayerChange(Layer, Layer)} method of all listeners.
     194     *
     195     * @param oldLayer The old layer
     196     * @param newLayer The new active layer.
     197     */
     198    protected void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
     199        checkLayerLockNotHeld();
    183200        for (LayerChangeListener l : layerChangeListeners) {
    184201            l.activeLayerChange(oldLayer, newLayer);
    185202        }
    186203    }
    187204
    188     protected static void fireLayerAdded(Layer newLayer) {
     205    protected void fireLayerAdded(Layer newLayer) {
     206        checkLayerLockNotHeld();
    189207        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
    190208            l.layerAdded(newLayer);
    191209        }
    192210    }
    193211
    194     protected static void fireLayerRemoved(Layer layer) {
     212    protected void fireLayerRemoved(Layer layer) {
     213        checkLayerLockNotHeld();
    195214        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
    196215            l.layerRemoved(layer);
    197216        }
    198217    }
    199218
    200     protected static void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
     219    protected void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
     220        checkLayerLockNotHeld();
    201221        for (EditLayerChangeListener l : editLayerChangeListeners) {
    202222            l.editLayerChanged(oldLayer, newLayer);
    203223        }
    204224    }
    205225
    206226    /**
    207      * A list of all layers currently loaded.
     227     * This is a simple invariant check that tests if the {@link #layerLock} is not write locked. This should be the case whenever a layer listener is invoked.
     228     */
     229    private void checkLayerLockNotHeld() {
     230        if (layerLock.isWriteLockedByCurrentThread()) {
     231            Main.warn("layerLock is write-held while a listener was called.");
     232        }
     233    }
     234
     235    /**
     236     * A list of all layers currently loaded. Locked by {@link #layerLock}.
    208237     */
    209238    private final transient List<Layer> layers = new ArrayList<>();
     239
     240    /**
     241     * This lock locks access to {@link #layers}, {@link #editLayer} and {@link #activeLayer}.
     242     * <p>
     243     * The read lock is always held while those fields are read or while layer change listeners are fired.
     244     */
     245    private final ReentrantReadWriteLock layerLock = new ReentrantReadWriteLock();
     246
    210247    /**
    211248     * The play head marker: there is only one of these so it isn't in any specific layer
    212249     */
    213250    public transient PlayHeadMarker playHeadMarker = null;
    214251
    215252    /**
    216      * The layer from the layers list that is currently active.
     253     * The layer from the layers list that is currently active. Locked by {@link #layerLock}.
    217254     */
    218255    private transient Layer activeLayer;
    219256
     257    /**
     258     * The edit layer is the current active data layer. Locked by {@link #layerLock}.
     259     */
    220260    private transient OsmDataLayer editLayer;
    221261
    222262    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    307347
    308348    /**
    309349     * Adds a GPX layer. A GPX layer is added below the lowest data layer.
     350     * <p>
     351     * Does not call {@link #fireLayerAdded(Layer)}.
    310352     *
    311353     * @param layer the GPX layer
    312354     */
    313355    protected void addGpxLayer(GpxLayer layer) {
    314         if (layers.isEmpty()) {
    315             layers.add(layer);
    316             return;
    317         }
    318         for (int i = layers.size()-1; i >= 0; i--) {
    319             if (layers.get(i) instanceof OsmDataLayer) {
    320                 if (i == layers.size()-1) {
    321                     layers.add(layer);
    322                 } else {
    323                     layers.add(i+1, layer);
    324                 }
     356        layerLock.writeLock().lock();
     357        try {
     358            if (layers.isEmpty()) {
     359                layers.add(layer);
    325360                return;
    326361            }
     362            for (int i=layers.size()-1; i>= 0; i--) {
     363                if (layers.get(i) instanceof OsmDataLayer) {
     364                    if (i == layers.size()-1) {
     365                        layers.add(layer);
     366                    } else {
     367                        layers.add(i+1, layer);
     368                    }
     369                    return;
     370                }
     371            }
     372            layers.add(0, layer);
     373        } finally {
     374            layerLock.writeLock().unlock();
    327375        }
    328         layers.add(0, layer);
    329376    }
    330377
    331378    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    334381     * @param layer The layer to add
    335382     */
    336383    public void addLayer(Layer layer) {
    337         if (layer instanceof MarkerLayer && playHeadMarker == null) {
    338             playHeadMarker = PlayHeadMarker.create();
    339         }
    340 
    341         if (layer instanceof GpxLayer) {
    342             addGpxLayer((GpxLayer) layer);
    343         } else if (layers.isEmpty()) {
    344             layers.add(layer);
    345         } else if (layer.isBackgroundLayer()) {
    346             int i = 0;
    347             for (; i < layers.size(); i++) {
    348                 if (layers.get(i).isBackgroundLayer()) {
    349                     break;
     384        boolean isOsmDataLayer = layer instanceof OsmDataLayer;
     385        layerLock.writeLock().lock();
     386        layerLock.readLock().lock();
     387        boolean fireSetActiveLayer = false;
     388        Layer oldActiveLayer = activeLayer;
     389        try {
     390            try {
     391                if (layer instanceof MarkerLayer && playHeadMarker == null) {
     392                    playHeadMarker = PlayHeadMarker.create();
     393                }
     394
     395                if (layer instanceof GpxLayer) {
     396                    addGpxLayer((GpxLayer)layer);
     397                } else if (layers.isEmpty()) {
     398                    layers.add(layer);
     399                } else if (layer.isBackgroundLayer()) {
     400                    int i = 0;
     401                    for (; i < layers.size(); i++) {
     402                        if (layers.get(i).isBackgroundLayer()) {
     403                            break;
     404                        }
     405                    }
     406                    layers.add(i, layer);
     407                } else {
     408                    layers.add(0, layer);
     409                }
     410
     411                if (isOsmDataLayer || oldActiveLayer == null) {
     412                    // autoselect the new layer
     413                    fireSetActiveLayer = setActiveLayer(layer, true);
    350414                }
     415            } finally {
     416                layerLock.writeLock().unlock();
    351417            }
    352             layers.add(i, layer);
    353         } else {
    354             layers.add(0, layer);
    355         }
    356         fireLayerAdded(layer);
    357         boolean isOsmDataLayer = layer instanceof OsmDataLayer;
    358         if (isOsmDataLayer) {
    359             ((OsmDataLayer) layer).addLayerStateChangeListener(this);
    360         }
    361         boolean callSetActiveLayer = isOsmDataLayer || activeLayer == null;
    362         if (callSetActiveLayer) {
    363             // autoselect the new layer
    364             setActiveLayer(layer); // also repaints this MapView
     418
     419            fireLayerAdded(layer);
     420            if (isOsmDataLayer) {
     421                ((OsmDataLayer)layer).addLayerStateChangeListener(this);
     422            }
     423            if (fireSetActiveLayer) {
     424                onActiveLayerChanged(oldActiveLayer);
     425            }
     426            layer.addPropertyChangeListener(this);
     427            Main.addProjectionChangeListener(layer);
     428            AudioPlayer.reset();
     429        } finally {
     430            layerLock.readLock().unlock();
    365431        }
    366         layer.addPropertyChangeListener(this);
    367         Main.addProjectionChangeListener(layer);
    368         AudioPlayer.reset();
    369         if (!callSetActiveLayer) {
     432        if (!fireSetActiveLayer) {
    370433            repaint();
    371434        }
    372435    }
    373436
    374437    @Override
    375438    protected DataSet getCurrentDataSet() {
    376         if (editLayer != null)
    377             return editLayer.data;
    378         else
    379             return null;
     439        layerLock.readLock().lock();
     440        try {
     441            if (editLayer != null)
     442                return editLayer.data;
     443            else
     444                return null;
     445        } finally {
     446            layerLock.readLock().unlock();
     447        }
    380448    }
    381449
    382450    /**
    383      * Replies true if the active layer is drawable.
     451     * Replies true if the active data layer (edit layer) is drawable.
    384452     *
    385      * @return true if the active layer is drawable, false otherwise
     453     * @return true if the active data layer (edit layer) is drawable, false otherwise
    386454     */
    387455    public boolean isActiveLayerDrawable() {
    388         return editLayer != null;
     456        layerLock.readLock().lock();
     457        try {
     458            return editLayer != null;
     459        } finally {
     460            layerLock.readLock().unlock();
     461        }
    389462    }
    390463
    391464    /**
    392      * Replies true if the active layer is visible.
     465     * Replies true if the active data layer (edit layer) is visible.
    393466     *
    394      * @return true if the active layer is visible, false otherwise
     467     * @return true if the active data layer (edit layer) is visible, false otherwise
    395468     */
    396469    public boolean isActiveLayerVisible() {
    397         return isActiveLayerDrawable() && editLayer.isVisible();
     470        layerLock.readLock().lock();
     471        try {
     472            return isActiveLayerDrawable() && editLayer.isVisible();
     473        } finally {
     474            layerLock.readLock().unlock();
     475        }
    398476    }
    399477
    400478    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    430508     * @param layer The layer to remove
    431509     */
    432510    public void removeLayer(Layer layer) {
    433         List<Layer> layersList = new ArrayList<>(layers);
     511        boolean fireEditLayerChanged;
     512        boolean fireSetActiveLayer = false;
     513        layerLock.writeLock().lock();
     514        layerLock.readLock().lock();
    434515
    435         if (!layersList.remove(layer))
    436             return;
     516        OsmDataLayer oldEditLayer = editLayer;
     517        Layer oldActiveLayer = activeLayer;
    437518
    438         setEditLayer(layersList);
     519        try {
     520            try {
     521                List<Layer> layersList = new ArrayList<>(layers);
    439522
    440         if (layer == activeLayer) {
    441             setActiveLayer(determineNextActiveLayer(layersList), false);
    442         }
     523                if (!layersList.remove(layer))
     524                    return;
    443525
    444         if (layer instanceof OsmDataLayer) {
    445             ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this);
    446         }
     526                fireEditLayerChanged = setEditLayer(layersList);
    447527
    448         layers.remove(layer);
    449         Main.removeProjectionChangeListener(layer);
    450         fireLayerRemoved(layer);
    451         layer.removePropertyChangeListener(this);
    452         layer.destroy();
    453         AudioPlayer.reset();
     528                if (layer == activeLayer) {
     529                    fireSetActiveLayer = setActiveLayer(determineNextActiveLayer(layersList), false);
     530                }
     531
     532                if (layer instanceof OsmDataLayer) {
     533                    ((OsmDataLayer)layer).removeLayerPropertyChangeListener(this);
     534                }
     535
     536                layers.remove(layer);
     537                Main.removeProjectionChangeListener(layer);
     538
     539            } finally {
     540                layerLock.writeLock().unlock();
     541            }
     542            if (fireEditLayerChanged) {
     543               onEditLayerChanged(oldEditLayer);
     544            }
     545            if (fireSetActiveLayer) {
     546                onActiveLayerChanged(oldActiveLayer);
     547            }
     548            fireLayerRemoved(layer);
     549            layer.removePropertyChangeListener(this);
     550            layer.destroy();
     551            AudioPlayer.reset();
     552        } finally {
     553            layerLock.readLock().unlock();
     554        }
    454555        repaint();
    455556    }
    456557
     558    private void onEditLayerChanged(OsmDataLayer oldEditLayer) {
     559        fireEditLayerChanged(oldEditLayer, editLayer);
     560           refreshTitle();
     561    }
     562
    457563    private boolean virtualNodesEnabled = false;
    458564
    459565    public void setVirtualNodesEnabled(boolean enabled) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    475581     * @param pos       The new position of the layer
    476582     */
    477583    public void moveLayer(Layer layer, int pos) {
    478         int curLayerPos = layers.indexOf(layer);
    479         if (curLayerPos == -1)
    480             throw new IllegalArgumentException(tr("Layer not in list."));
    481         if (pos == curLayerPos)
    482             return; // already in place.
    483         layers.remove(curLayerPos);
    484         if (pos >= layers.size()) {
    485             layers.add(layer);
    486         } else {
    487             layers.add(pos, layer);
     584        layerLock.writeLock().lock();
     585        layerLock.readLock().lock();
     586        boolean fireEditLayerChanged;
     587        OsmDataLayer oldEditLayer = editLayer;
     588        try {
     589            try {
     590                int curLayerPos = layers.indexOf(layer);
     591                if (curLayerPos == -1)
     592                    throw new IllegalArgumentException(tr("Layer not in list."));
     593                if (pos == curLayerPos)
     594                    return; // already in place.
     595                layers.remove(curLayerPos);
     596                if (pos >= layers.size()) {
     597                    layers.add(layer);
     598                } else {
     599                    layers.add(pos, layer);
     600                }
     601                fireEditLayerChanged = setEditLayer(layers);
     602            } finally {
     603                layerLock.writeLock().unlock();
     604            }
     605            if (fireEditLayerChanged) {
     606                onEditLayerChanged(editLayer);
     607            }
     608            AudioPlayer.reset();
     609        } finally {
     610            layerLock.readLock().unlock();
    488611        }
    489         setEditLayer(layers);
    490         AudioPlayer.reset();
    491612        repaint();
    492613    }
    493614
     615    /**
     616     * Gets the index of the layer in the layer list.
     617     * @param layer The layer to search for.
     618     * @return The index in the list.
     619     * @throws IllegalArgumentException if that layer does not belong to this view.
     620     */
    494621    public int getLayerPos(Layer layer) {
    495         int curLayerPos = layers.indexOf(layer);
     622        int curLayerPos;
     623        layerLock.readLock().lock();
     624        try {
     625            curLayerPos = layers.indexOf(layer);
     626        } finally {
     627            layerLock.readLock().unlock();
     628        }
    496629        if (curLayerPos == -1)
    497630            throw new IllegalArgumentException(tr("Layer not in list."));
    498631        return curLayerPos;
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    505638     * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
    506639     * first, layer with the highest Z-Order last.
    507640     */
    508     protected List<Layer> getVisibleLayersInZOrder() {
    509         List<Layer> ret = new ArrayList<>();
    510         for (Layer l: layers) {
    511             if (l.isVisible()) {
    512                 ret.add(l);
    513             }
    514         }
    515         // sort according to position in the list of layers, with one exception:
    516         // an active data layer always becomes a higher Z-Order than all other data layers
    517         Collections.sort(
    518                 ret,
    519                 new Comparator<Layer>() {
    520                     @Override
    521                     public int compare(Layer l1, Layer l2) {
    522                         if (l1 instanceof OsmDataLayer && l2 instanceof OsmDataLayer) {
    523                             if (l1 == getActiveLayer()) return -1;
    524                             if (l2 == getActiveLayer()) return 1;
    525                             return Integer.compare(layers.indexOf(l1), layers.indexOf(l2));
    526                         } else
    527                             return Integer.compare(layers.indexOf(l1), layers.indexOf(l2));
     641    public List<Layer> getVisibleLayersInZOrder() {
     642        layerLock.readLock().lock();
     643        try {
     644            List<Layer> ret = new ArrayList<>();
     645            // This is set while we delay the addition of the active layer.
     646            boolean activeLayerDelayed = false;
     647            for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
     648                Layer l = iterator.previous();
     649                if (!l.isVisible()) {
     650                    // ignored
     651                } else if (l == activeLayer && l instanceof OsmDataLayer) {
     652                    activeLayerDelayed = true;
     653                } else {
     654                    // Add this layer now
     655                    if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
     656                        // add active layer before the current one.
     657                        ret.add(activeLayer);
     658                        activeLayerDelayed = false;
    528659                    }
     660                    ret.add(l);
    529661                }
    530         );
    531         Collections.reverse(ret);
    532         return ret;
     662            }
     663            if (activeLayerDelayed) {
     664                ret.add(activeLayer);
     665            }
     666            return ret;
     667        } finally {
     668            layerLock.readLock().unlock();
     669        }
    533670    }
    534671
    535672    private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    703840     * @return An unmodifiable collection of all layers
    704841     */
    705842    public Collection<Layer> getAllLayers() {
    706         return Collections.unmodifiableCollection(new ArrayList<>(layers));
     843        layerLock.readLock().lock();
     844        try {
     845            return Collections.unmodifiableCollection(new ArrayList<>(layers));
     846        } finally {
     847            layerLock.readLock().unlock();
     848        }
    707849    }
    708850
    709851    /**
    710852     * @return An unmodifiable ordered list of all layers
    711853     */
    712854    public List<Layer> getAllLayersAsList() {
    713         return Collections.unmodifiableList(new ArrayList<>(layers));
     855        layerLock.readLock().lock();
     856        try {
     857            return Collections.unmodifiableList(new ArrayList<>(layers));
     858        } finally {
     859            layerLock.readLock().unlock();
     860        }
    714861    }
    715862
    716863    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    721868     *     List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
    722869     * </pre>
    723870     *
     871     * @param ofType The layer type.
    724872     * @return an unmodifiable list of layers of a certain type.
    725873     */
    726874    public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    728876    }
    729877
    730878    /**
    731      * Replies the number of layers managed by this mav view
     879     * Replies the number of layers managed by this map view
    732880     *
    733      * @return the number of layers managed by this mav view
     881     * @return the number of layers managed by this map view
    734882     */
    735883    public int getNumLayers() {
    736         return layers.size();
     884        layerLock.readLock().lock();
     885        try {
     886            return layers.size();
     887        } finally {
     888            layerLock.readLock().unlock();
     889        }
    737890    }
    738891
    739892    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    745898        return getNumLayers() > 0;
    746899    }
    747900
    748     private void setEditLayer(List<Layer> layersList) {
    749         OsmDataLayer newEditLayer = layersList.contains(editLayer) ? editLayer : null;
    750         OsmDataLayer oldEditLayer = editLayer;
     901    /**
     902     * Sets the active edit layer.
     903     * <p>
     904     * You must own a write {@link #layerLock} when calling this method.
     905     * @param layersList A list to select that layer from.
     906     * @return <code>true</code> if the edit layer was really changed and the listeners should be informed.
     907     */
     908    private boolean setEditLayer(List<Layer> layersList) {
     909        final OsmDataLayer newEditLayer = findNewEditLayer(layersList);
    751910
     911        // Set new edit layer
     912        if (newEditLayer != editLayer) {
     913            if (newEditLayer == null) {
     914                // Note: Unsafe to call while layer write lock is held.
     915                getCurrentDataSet().setSelected();
     916            }
     917
     918            editLayer = newEditLayer;
     919            return true;
     920        } else {
     921            return false;
     922        }
     923
     924    }
     925
     926    private OsmDataLayer findNewEditLayer(List<Layer> layersList) {
     927        OsmDataLayer newEditLayer = layersList.contains(editLayer)?editLayer:null;
    752928        // Find new edit layer
    753929        if (activeLayer != editLayer || !layersList.contains(editLayer)) {
    754930            if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    762938                }
    763939            }
    764940        }
    765 
    766         // Set new edit layer
    767         if (newEditLayer != editLayer) {
    768             if (newEditLayer == null) {
    769                 getCurrentDataSet().setSelected();
    770             }
    771 
    772             editLayer = newEditLayer;
    773             fireEditLayerChanged(oldEditLayer, newEditLayer);
    774             refreshTitle();
    775         }
    776 
     941        return newEditLayer;
    777942    }
    778943
    779944    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    784949     * @throws IllegalArgumentException if layer is not in the lis of layers
    785950     */
    786951    public void setActiveLayer(Layer layer) {
    787         setActiveLayer(layer, true);
     952        layerLock.writeLock().lock();
     953        layerLock.readLock().lock();
     954        Layer oldActiveLayer = activeLayer;
     955        try {
     956            boolean fireSetActiveLayer;
     957            try {
     958                fireSetActiveLayer = setActiveLayer(layer, true);
     959            } finally {
     960                layerLock.writeLock().unlock();
     961            }
     962            if (fireSetActiveLayer) {
     963                onActiveLayerChanged(oldActiveLayer);
     964            }
     965        } finally {
     966            layerLock.readLock().unlock();
     967        }
     968        repaint();
    788969    }
    789970
    790     private void setActiveLayer(Layer layer, boolean setEditLayer) {
     971    /**
     972     * Sets the active layer. Propagates this change to all map buttons.
     973     * @param layer The layer to be active.
     974     * @param setEditLayer if this is <code>true</code>, the edit layer is also set.
     975     * @return
     976     */
     977    private boolean setActiveLayer(final Layer layer, boolean setEditLayer) {
    791978        if (layer != null && !layers.contains(layer))
    792979            throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));
    793980
    794981        if (layer == activeLayer)
    795             return;
     982            return false;
    796983
    797984        Layer old = activeLayer;
    798985        activeLayer = layer;
    799986        if (setEditLayer) {
    800987            setEditLayer(layers);
    801988        }
    802         fireActiveLayerChanged(old, layer);
     989
     990        return true;
     991    }
     992
     993    /**
     994     * Replies the currently active layer
     995     *
     996     * @return the currently active layer (may be null)
     997     */
     998    public Layer getActiveLayer() {
     999        layerLock.readLock().lock();
     1000        try {
     1001            return activeLayer;
     1002        } finally {
     1003            layerLock.readLock().unlock();
     1004        }
     1005    }
     1006
     1007    private void onActiveLayerChanged(final Layer old) {
     1008        fireActiveLayerChanged(old, activeLayer);
    8031009
    8041010        /* This only makes the buttons look disabled. Disabling the actions as well requires
    8051011         * the user to re-select the tool after i.e. moving a layer. While testing I found
    8061012         * that I switch layers and actions at the same time and it was annoying to mind the
    8071013         * order. This way it works as visual clue for new users */
    8081014        for (final AbstractButton b: Main.map.allMapModeButtons) {
    809             MapMode mode = (MapMode) b.getAction();
    810             if (mode.layerIsSupported(layer)) {
     1015            MapMode mode = (MapMode)b.getAction();
     1016            final boolean activeLayerSupported = mode.layerIsSupported(activeLayer);
     1017            if (activeLayerSupported) {
    8111018                Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
    812                 GuiHelper.runInEDTAndWait(new Runnable() {
    813                     @Override public void run() {
    814                         b.setEnabled(true);
    815                     }
    816                 });
    8171019            } else {
    8181020                Main.unregisterShortcut(mode.getShortcut());
    819                 GuiHelper.runInEDTAndWait(new Runnable() {
    820                     @Override public void run() {
    821                         b.setEnabled(false);
    822                     }
    823                 });
    8241021            }
     1022            GuiHelper.runInEDTAndWait(new Runnable() {
     1023                @Override public void run() {
     1024                    b.setEnabled(activeLayerSupported);
     1025                }
     1026            });
    8251027        }
    8261028        AudioPlayer.reset();
    8271029        repaint();
    8281030    }
    8291031
    8301032    /**
    831      * Replies the currently active layer
    832      *
    833      * @return the currently active layer (may be null)
    834      */
    835     public Layer getActiveLayer() {
    836         return activeLayer;
    837     }
    838 
    839     /**
    8401033     * Replies the current edit layer, if any
    8411034     *
    8421035     * @return the current edit layer. May be null.
    8431036     */
    8441037    public OsmDataLayer getEditLayer() {
    845         return editLayer;
     1038        layerLock.readLock().lock();
     1039        try {
     1040            return editLayer;
     1041        } finally {
     1042            layerLock.readLock().unlock();
     1043        }
    8461044    }
    8471045
    8481046    /**
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    8521050     * @return true if the list of layers managed by this map view contain layer
    8531051     */
    8541052    public boolean hasLayer(Layer layer) {
    855         return layers.contains(layer);
     1053        layerLock.readLock().lock();
     1054        try {
     1055            return layers.contains(layer);
     1056        } finally {
     1057            layerLock.readLock().unlock();
     1058        }
    8561059    }
    8571060
    8581061    public boolean addTemporaryLayer(MapViewPaintable mvp) {
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    8831086        }
    8841087    }
    8851088
     1089    /**
     1090     * Sets the title of the JOSM main window, adding a star if there are dirty layers.
     1091     * @see Main#parent
     1092     */
    8861093    protected void refreshTitle() {
    8871094        if (Main.parent != null) {
    888             boolean dirty = editLayer != null &&
    889                     (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
    890             ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
    891             ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
     1095            layerLock.readLock().lock();
     1096            try {
     1097                boolean dirty = editLayer != null &&
     1098                        (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged()));
     1099                ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor"));
     1100                ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty);
     1101            } finally {
     1102                layerLock.readLock().unlock();
     1103            }
    8921104        }
    8931105    }
    8941106
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    9131125        if (mapMover != null) {
    9141126            mapMover.destroy();
    9151127        }
    916         activeLayer = null;
    917         changedLayer = null;
    918         editLayer = null;
    919         layers.clear();
     1128        layerLock.writeLock().lock();
     1129        try {
     1130            activeLayer = null;
     1131            changedLayer = null;
     1132            editLayer = null;
     1133            layers.clear();
     1134        } finally {
     1135            layerLock.writeLock().unlock();
     1136        }
    9201137        nonChangedLayers.clear();
    9211138        temporaryLayers.clear();
    9221139    }
    implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer  
    9301147
    9311148    /**
    9321149     * Get a string representation of all layers suitable for the {@code source} changeset tag.
     1150     * @return A String of sources separated by ';'
    9331151     */
    9341152    public String getLayerInformationForSourceTag() {
    9351153        final Collection<String> layerInfo = new ArrayList<>();