Ticket #11631: 0005-Synchronized-access-to-MapView-layers.patch
| File 0005-Synchronized-access-to-MapView-layers.patch, 29.2 KB (added by , 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; 24 24 import java.util.ArrayList; 25 25 import java.util.Collection; 26 26 import java.util.Collections; 27 import java.util.Comparator;28 27 import java.util.LinkedList; 29 28 import java.util.List; 29 import java.util.ListIterator; 30 30 import java.util.concurrent.CopyOnWriteArrayList; 31 import java.util.concurrent.locks.ReentrantReadWriteLock; 31 32 32 33 import javax.swing.AbstractButton; 33 34 import javax.swing.ActionMap; … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 104 105 void layerRemoved(Layer oldLayer); 105 106 } 106 107 108 /** 109 * An interface that needs to be implemented in order to listen for changes to the active edit layer. 110 */ 107 111 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 */ 108 118 void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer); 109 119 } 110 120 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 179 189 } 180 190 } 181 191 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(); 183 200 for (LayerChangeListener l : layerChangeListeners) { 184 201 l.activeLayerChange(oldLayer, newLayer); 185 202 } 186 203 } 187 204 188 protected static void fireLayerAdded(Layer newLayer) { 205 protected void fireLayerAdded(Layer newLayer) { 206 checkLayerLockNotHeld(); 189 207 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) { 190 208 l.layerAdded(newLayer); 191 209 } 192 210 } 193 211 194 protected static void fireLayerRemoved(Layer layer) { 212 protected void fireLayerRemoved(Layer layer) { 213 checkLayerLockNotHeld(); 195 214 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) { 196 215 l.layerRemoved(layer); 197 216 } 198 217 } 199 218 200 protected static void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 219 protected void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 220 checkLayerLockNotHeld(); 201 221 for (EditLayerChangeListener l : editLayerChangeListeners) { 202 222 l.editLayerChanged(oldLayer, newLayer); 203 223 } 204 224 } 205 225 206 226 /** 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}. 208 237 */ 209 238 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 210 247 /** 211 248 * The play head marker: there is only one of these so it isn't in any specific layer 212 249 */ 213 250 public transient PlayHeadMarker playHeadMarker = null; 214 251 215 252 /** 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}. 217 254 */ 218 255 private transient Layer activeLayer; 219 256 257 /** 258 * The edit layer is the current active data layer. Locked by {@link #layerLock}. 259 */ 220 260 private transient OsmDataLayer editLayer; 221 261 222 262 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 307 347 308 348 /** 309 349 * Adds a GPX layer. A GPX layer is added below the lowest data layer. 350 * <p> 351 * Does not call {@link #fireLayerAdded(Layer)}. 310 352 * 311 353 * @param layer the GPX layer 312 354 */ 313 355 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); 325 360 return; 326 361 } 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(); 327 375 } 328 layers.add(0, layer);329 376 } 330 377 331 378 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 334 381 * @param layer The layer to add 335 382 */ 336 383 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); 350 414 } 415 } finally { 416 layerLock.writeLock().unlock(); 351 417 } 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 layer364 setActiveLayer(layer); // also repaints this MapView418 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(); 365 431 } 366 layer.addPropertyChangeListener(this); 367 Main.addProjectionChangeListener(layer); 368 AudioPlayer.reset(); 369 if (!callSetActiveLayer) { 432 if (!fireSetActiveLayer) { 370 433 repaint(); 371 434 } 372 435 } 373 436 374 437 @Override 375 438 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 } 380 448 } 381 449 382 450 /** 383 * Replies true if the active layeris drawable.451 * Replies true if the active data layer (edit layer) is drawable. 384 452 * 385 * @return true if the active layeris drawable, false otherwise453 * @return true if the active data layer (edit layer) is drawable, false otherwise 386 454 */ 387 455 public boolean isActiveLayerDrawable() { 388 return editLayer != null; 456 layerLock.readLock().lock(); 457 try { 458 return editLayer != null; 459 } finally { 460 layerLock.readLock().unlock(); 461 } 389 462 } 390 463 391 464 /** 392 * Replies true if the active layeris visible.465 * Replies true if the active data layer (edit layer) is visible. 393 466 * 394 * @return true if the active layeris visible, false otherwise467 * @return true if the active data layer (edit layer) is visible, false otherwise 395 468 */ 396 469 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 } 398 476 } 399 477 400 478 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 430 508 * @param layer The layer to remove 431 509 */ 432 510 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(); 434 515 435 if (!layersList.remove(layer))436 return;516 OsmDataLayer oldEditLayer = editLayer; 517 Layer oldActiveLayer = activeLayer; 437 518 438 setEditLayer(layersList); 519 try { 520 try { 521 List<Layer> layersList = new ArrayList<>(layers); 439 522 440 if (layer == activeLayer) { 441 setActiveLayer(determineNextActiveLayer(layersList), false); 442 } 523 if (!layersList.remove(layer)) 524 return; 443 525 444 if (layer instanceof OsmDataLayer) { 445 ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this); 446 } 526 fireEditLayerChanged = setEditLayer(layersList); 447 527 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 } 454 555 repaint(); 455 556 } 456 557 558 private void onEditLayerChanged(OsmDataLayer oldEditLayer) { 559 fireEditLayerChanged(oldEditLayer, editLayer); 560 refreshTitle(); 561 } 562 457 563 private boolean virtualNodesEnabled = false; 458 564 459 565 public void setVirtualNodesEnabled(boolean enabled) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 475 581 * @param pos The new position of the layer 476 582 */ 477 583 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(); 488 611 } 489 setEditLayer(layers);490 AudioPlayer.reset();491 612 repaint(); 492 613 } 493 614 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 */ 494 621 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 } 496 629 if (curLayerPos == -1) 497 630 throw new IllegalArgumentException(tr("Layer not in list.")); 498 631 return curLayerPos; … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 505 638 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order 506 639 * first, layer with the highest Z-Order last. 507 640 */ 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; 528 659 } 660 ret.add(l); 529 661 } 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 } 533 670 } 534 671 535 672 private void paintLayer(Layer layer, Graphics2D g, Bounds box) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 703 840 * @return An unmodifiable collection of all layers 704 841 */ 705 842 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 } 707 849 } 708 850 709 851 /** 710 852 * @return An unmodifiable ordered list of all layers 711 853 */ 712 854 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 } 714 861 } 715 862 716 863 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 721 868 * List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class); 722 869 * </pre> 723 870 * 871 * @param ofType The layer type. 724 872 * @return an unmodifiable list of layers of a certain type. 725 873 */ 726 874 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 728 876 } 729 877 730 878 /** 731 * Replies the number of layers managed by this ma vview879 * Replies the number of layers managed by this map view 732 880 * 733 * @return the number of layers managed by this ma vview881 * @return the number of layers managed by this map view 734 882 */ 735 883 public int getNumLayers() { 736 return layers.size(); 884 layerLock.readLock().lock(); 885 try { 886 return layers.size(); 887 } finally { 888 layerLock.readLock().unlock(); 889 } 737 890 } 738 891 739 892 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 745 898 return getNumLayers() > 0; 746 899 } 747 900 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); 751 910 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; 752 928 // Find new edit layer 753 929 if (activeLayer != editLayer || !layersList.contains(editLayer)) { 754 930 if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 762 938 } 763 939 } 764 940 } 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; 777 942 } 778 943 779 944 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 784 949 * @throws IllegalArgumentException if layer is not in the lis of layers 785 950 */ 786 951 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(); 788 969 } 789 970 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) { 791 978 if (layer != null && !layers.contains(layer)) 792 979 throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString())); 793 980 794 981 if (layer == activeLayer) 795 return ;982 return false; 796 983 797 984 Layer old = activeLayer; 798 985 activeLayer = layer; 799 986 if (setEditLayer) { 800 987 setEditLayer(layers); 801 988 } 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); 803 1009 804 1010 /* This only makes the buttons look disabled. Disabling the actions as well requires 805 1011 * the user to re-select the tool after i.e. moving a layer. While testing I found 806 1012 * that I switch layers and actions at the same time and it was annoying to mind the 807 1013 * order. This way it works as visual clue for new users */ 808 1014 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) { 811 1018 Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876 812 GuiHelper.runInEDTAndWait(new Runnable() {813 @Override public void run() {814 b.setEnabled(true);815 }816 });817 1019 } else { 818 1020 Main.unregisterShortcut(mode.getShortcut()); 819 GuiHelper.runInEDTAndWait(new Runnable() {820 @Override public void run() {821 b.setEnabled(false);822 }823 });824 1021 } 1022 GuiHelper.runInEDTAndWait(new Runnable() { 1023 @Override public void run() { 1024 b.setEnabled(activeLayerSupported); 1025 } 1026 }); 825 1027 } 826 1028 AudioPlayer.reset(); 827 1029 repaint(); 828 1030 } 829 1031 830 1032 /** 831 * Replies the currently active layer832 *833 * @return the currently active layer (may be null)834 */835 public Layer getActiveLayer() {836 return activeLayer;837 }838 839 /**840 1033 * Replies the current edit layer, if any 841 1034 * 842 1035 * @return the current edit layer. May be null. 843 1036 */ 844 1037 public OsmDataLayer getEditLayer() { 845 return editLayer; 1038 layerLock.readLock().lock(); 1039 try { 1040 return editLayer; 1041 } finally { 1042 layerLock.readLock().unlock(); 1043 } 846 1044 } 847 1045 848 1046 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 852 1050 * @return true if the list of layers managed by this map view contain layer 853 1051 */ 854 1052 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 } 856 1059 } 857 1060 858 1061 public boolean addTemporaryLayer(MapViewPaintable mvp) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 883 1086 } 884 1087 } 885 1088 1089 /** 1090 * Sets the title of the JOSM main window, adding a star if there are dirty layers. 1091 * @see Main#parent 1092 */ 886 1093 protected void refreshTitle() { 887 1094 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 } 892 1104 } 893 1105 } 894 1106 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 913 1125 if (mapMover != null) { 914 1126 mapMover.destroy(); 915 1127 } 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 } 920 1137 nonChangedLayers.clear(); 921 1138 temporaryLayers.clear(); 922 1139 } … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 930 1147 931 1148 /** 932 1149 * Get a string representation of all layers suitable for the {@code source} changeset tag. 1150 * @return A String of sources separated by ';' 933 1151 */ 934 1152 public String getLayerInformationForSourceTag() { 935 1153 final Collection<String> layerInfo = new ArrayList<>();
