Ticket #11838: layer-manager.patch
| File layer-manager.patch, 60.2 KB (added by , 11 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 c52a9c2..adabb81 100644
a b import org.openstreetmap.josm.gui.help.HelpUtil; 89 89 import org.openstreetmap.josm.gui.io.SaveLayersDialog; 90 90 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer; 91 91 import org.openstreetmap.josm.gui.layer.Layer; 92 import org.openstreetmap.josm.gui.layer.LayerManagerWithActive; 92 93 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 93 94 import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener; 94 95 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences; … … public abstract class Main { 186 187 187 188 /** 188 189 * The MapFrame. Use {@link Main#setMapFrame} to set or clear it. 190 * <p> 191 * There should be no need to access this to access any map data. Use {@link #layerManager} instead. 189 192 */ 190 193 public static MapFrame map; 191 194 192 195 /** 196 * Provides access to the layers displayed in the main view. 197 */ 198 public static LayerManagerWithActive layerManager = new LayerManagerWithActive(); 199 200 /** 193 201 * The toolbar preference control to register new actions. 194 202 */ 195 203 public static volatile ToolbarPreferences toolbar; -
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 723ebd9..a413a3a 100644
a b import org.openstreetmap.josm.gui.dialogs.UserListDialog; 75 75 import org.openstreetmap.josm.gui.dialogs.ValidatorDialog; 76 76 import org.openstreetmap.josm.gui.dialogs.properties.PropertiesDialog; 77 77 import org.openstreetmap.josm.gui.layer.Layer; 78 import org.openstreetmap.josm.gui.layer.LayerManager; 78 79 import org.openstreetmap.josm.gui.util.AdvancedKeyPressDetector; 79 80 import org.openstreetmap.josm.tools.Destroyable; 80 81 import org.openstreetmap.josm.tools.GBC; … … public class MapFrame extends JPanel implements Destroyable, LayerChangeListener 97 98 98 99 /** 99 100 * The view control displayed. 101 * <p> 102 * Accessing this is discouraged. Use the {@link LayerManager} to access map data. 100 103 */ 101 104 public final MapView mapView; 102 105 … … public class MapFrame extends JPanel implements Destroyable, LayerChangeListener 174 177 setSize(400, 400); 175 178 setLayout(new BorderLayout()); 176 179 177 mapView = new MapView( contentPane, viewportData);180 mapView = new MapView(Main.layerManager, contentPane, viewportData); 178 181 if (!GraphicsEnvironment.isHeadless()) { 179 182 new FileDrop(mapView); 180 183 } … … public class MapFrame extends JPanel implements Destroyable, LayerChangeListener 724 727 mapMode.enterMode(); 725 728 } 726 729 // invalidate repaint cache 727 Main.map.mapView.preferenceChanged(null);730 mapView.preferenceChanged(null); 728 731 } 729 732 730 733 // 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 eff7bb6..f7a71da 100644
a b import java.util.ArrayList; 25 25 import java.util.Arrays; 26 26 import java.util.Collection; 27 27 import java.util.Collections; 28 import java.util.EnumSet;29 28 import java.util.LinkedHashSet; 30 29 import java.util.List; 31 import java.util.ListIterator;32 30 import java.util.Set; 33 31 import java.util.concurrent.CopyOnWriteArrayList; 34 32 … … import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache 57 55 import org.openstreetmap.josm.gui.layer.GpxLayer; 58 56 import org.openstreetmap.josm.gui.layer.ImageryLayer; 59 57 import org.openstreetmap.josm.gui.layer.Layer; 58 import org.openstreetmap.josm.gui.layer.LayerManager; 59 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent; 60 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent; 61 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 62 import org.openstreetmap.josm.gui.layer.LayerManagerWithActive; 63 import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeEvent; 64 import org.openstreetmap.josm.gui.layer.LayerManagerWithActive.ActiveLayerChangeListener; 60 65 import org.openstreetmap.josm.gui.layer.MapViewPaintable; 61 66 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 62 67 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer; … … import org.openstreetmap.josm.tools.Utils; 81 86 * @author imi 82 87 */ 83 88 public class MapView extends NavigatableComponent 84 implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener {85 89 implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.LayerStateChangeListener, 90 LayerManager.LayerChangeListener, LayerManagerWithActive.ActiveLayerChangeListener { 86 91 /** 87 92 * Interface to notify listeners of a layer change. 88 93 * @author imi 89 94 */ 95 @Deprecated 90 96 public interface LayerChangeListener { 91 97 92 98 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 112 118 /** 113 119 * An interface that needs to be implemented in order to listen for changes to the active edit layer. 114 120 */ 121 @Deprecated 115 122 public interface EditLayerChangeListener { 116 123 117 124 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 122 129 void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer); 123 130 } 124 131 125 p ublic boolean viewportFollowing = false;132 protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener { 126 133 127 /** 128 * the layer listeners 129 */ 130 private static final CopyOnWriteArrayList<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>(); 131 private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<>(); 134 private final LayerChangeListener wrapped; 135 private boolean receiveOneInitialFire; 136 137 public LayerChangeAdapter(LayerChangeListener wrapped) { 138 this.wrapped = wrapped; 139 } 140 141 public LayerChangeAdapter(LayerChangeListener wrapped, boolean initialFire) { 142 this(wrapped); 143 this.receiveOneInitialFire = initialFire; 144 } 145 146 @Override 147 public void layerAdded(LayerAddEvent e) { 148 wrapped.layerAdded(e.getAddedLayer()); 149 } 150 151 @Override 152 public void layerRemoving(LayerRemoveEvent e) { 153 wrapped.layerRemoved(e.getRemovedLayer()); 154 } 155 156 @Override 157 public void layerOrderChanged(LayerOrderChangeEvent e) { 158 // not in old API 159 } 160 161 @Override 162 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { 163 Layer oldActive = receiveOneInitialFire ? null : e.getPreviousActiveLayer(); 164 Layer newActive = e.getSource().getActiveLayer(); 165 if (oldActive != newActive) { 166 wrapped.activeLayerChange(oldActive, newActive); 167 } 168 receiveOneInitialFire = false; 169 } 170 171 @Override 172 public int hashCode() { 173 final int prime = 31; 174 int result = 1; 175 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode()); 176 return result; 177 } 178 179 @Override 180 public boolean equals(Object obj) { 181 if (this == obj) 182 return true; 183 if (obj == null) 184 return false; 185 if (getClass() != obj.getClass()) 186 return false; 187 LayerChangeAdapter other = (LayerChangeAdapter) obj; 188 if (wrapped == null) { 189 if (other.wrapped != null) 190 return false; 191 } else if (!wrapped.equals(other.wrapped)) 192 return false; 193 return true; 194 } 195 196 @Override 197 public String toString() { 198 return "LayerChangeAdapter [wrapped=" + wrapped + "]"; 199 } 200 201 } 202 203 protected static class EditLayerChangeAdapter implements ActiveLayerChangeListener { 204 205 private final EditLayerChangeListener wrapped; 206 207 public EditLayerChangeAdapter(EditLayerChangeListener wrapped) { 208 this.wrapped = wrapped; 209 } 210 211 @Override 212 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { 213 OsmDataLayer oldLayer = e.getPreviousEditLayer(); 214 OsmDataLayer newLayer = e.getSource().getEditLayer(); 215 if (oldLayer != newLayer) { 216 wrapped.editLayerChanged(oldLayer, newLayer); 217 } 218 } 219 220 @Override 221 public int hashCode() { 222 final int prime = 31; 223 int result = 1; 224 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode()); 225 return result; 226 } 227 228 @Override 229 public boolean equals(Object obj) { 230 if (this == obj) 231 return true; 232 if (obj == null) 233 return false; 234 if (getClass() != obj.getClass()) 235 return false; 236 EditLayerChangeAdapter other = (EditLayerChangeAdapter) obj; 237 if (wrapped == null) { 238 if (other.wrapped != null) 239 return false; 240 } else if (!wrapped.equals(other.wrapped)) 241 return false; 242 return true; 243 } 244 245 @Override 246 public String toString() { 247 return "EditLayerChangeAdapter [wrapped=" + wrapped + "]"; 248 } 249 250 } 132 251 133 252 /** 134 253 * Removes a layer change listener 135 254 * 136 255 * @param listener the listener. Ignored if null or already registered. 137 256 */ 257 @Deprecated 138 258 public static void removeLayerChangeListener(LayerChangeListener listener) { 139 layerChangeListeners.remove(listener); 259 LayerChangeAdapter adapter = new LayerChangeAdapter(listener); 260 Main.layerManager.removeLayerChangeListener(adapter); 261 Main.layerManager.removeActiveLayerChangeListener(adapter); 140 262 } 141 263 264 @Deprecated 142 265 public static void removeEditLayerChangeListener(EditLayerChangeListener listener) { 143 editLayerChangeListeners.remove(listener);266 Main.layerManager.removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener)); 144 267 } 145 268 146 269 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 148 271 * 149 272 * @param listener the listener. Ignored if null or already registered. 150 273 */ 274 @Deprecated 151 275 public static void addLayerChangeListener(LayerChangeListener listener) { 152 if (listener != null) { 153 layerChangeListeners.addIfAbsent(listener); 154 } 276 addLayerChangeListener(listener, false); 155 277 } 156 278 157 279 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 161 283 * @param initialFire fire an active-layer-changed-event right after adding 162 284 * the listener in case there is a layer present (should be) 163 285 */ 286 @Deprecated 164 287 public static void addLayerChangeListener(LayerChangeListener listener, boolean initialFire) { 165 addLayerChangeListener(listener); 166 if (initialFire && Main.isDisplayingMapView()) { 167 listener.activeLayerChange(null, Main.map.mapView.getActiveLayer()); 288 if (listener != null) { 289 initialFire = initialFire && Main.isDisplayingMapView(); 290 291 LayerChangeAdapter adapter = new LayerChangeAdapter(listener, initialFire); 292 Main.layerManager.addLayerChangeListener(adapter, false); 293 Main.layerManager.addActiveLayerChangeListener(adapter, initialFire); 294 adapter.receiveOneInitialFire = false; 168 295 } 169 296 } 170 297 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 175 302 * @param initialFire fire an edit-layer-changed-event right after adding 176 303 * the listener in case there is an edit layer present 177 304 */ 305 @Deprecated 178 306 public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) { 179 addEditLayerChangeListener(listener); 180 if (initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null) { 181 listener.editLayerChanged(null, Main.map.mapView.getEditLayer()); 307 if (listener != null) { 308 Main.layerManager.addActiveLayerChangeListener(new EditLayerChangeAdapter(listener), initialFire && Main.isDisplayingMapView() && Main.map.mapView.getEditLayer() != null); 182 309 } 183 310 } 184 311 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 187 314 * 188 315 * @param listener the listener. Ignored if null or already registered. 189 316 */ 317 @Deprecated 190 318 public static void addEditLayerChangeListener(EditLayerChangeListener listener) { 191 if (listener != null) { 192 editLayerChangeListeners.addIfAbsent(listener); 193 } 194 } 195 196 /** 197 * Calls the {@link LayerChangeListener#activeLayerChange(Layer, Layer)} method of all listeners. 198 * 199 * @param oldLayer The old layer 200 * @param newLayer The new active layer. 201 */ 202 protected void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) { 203 for (LayerChangeListener l : layerChangeListeners) { 204 l.activeLayerChange(oldLayer, newLayer); 205 } 319 addEditLayerChangeListener(listener, false); 206 320 } 207 321 208 protected void fireLayerAdded(Layer newLayer) { 209 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) { 210 l.layerAdded(newLayer); 211 } 212 } 213 214 protected void fireLayerRemoved(Layer layer) { 215 for (MapView.LayerChangeListener l : MapView.layerChangeListeners) { 216 l.layerRemoved(layer); 217 } 218 } 219 220 protected void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) { 221 for (EditLayerChangeListener l : editLayerChangeListeners) { 222 l.editLayerChanged(oldLayer, newLayer); 223 } 224 } 322 public boolean viewportFollowing = false; 225 323 226 /** 227 * A list of all layers currently loaded. Locked by {@link #layerLock}. 228 */ 229 private final transient List<Layer> layers = new ArrayList<>(); 230 231 /** 232 * This lock manages concurrent access to {@link #layers}, 233 * {@link #editLayer} and {@link #activeLayer}. 234 * <p> 235 * The read lock is always held while those fields are read or while layer change listeners are fired. 236 */ 237 //private final ReentrantReadWriteLock layerLock = new ReentrantReadWriteLock(); 324 private LayerManagerWithActive layerManager = new LayerManagerWithActive(); 238 325 239 326 /** 240 327 * The play head marker: there is only one of these so it isn't in any specific layer … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 242 329 public transient PlayHeadMarker playHeadMarker = null; 243 330 244 331 /** 245 * The layer from the layers list that is currently active. Locked by {@link #layerLock}.246 */247 private transient Layer activeLayer;248 249 /**250 * The edit layer is the current active data layer. Locked by {@link #layerLock}.251 */252 private transient OsmDataLayer editLayer;253 254 /**255 332 * The last event performed by mouse. 256 333 */ 257 334 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 275 352 276 353 /** 277 354 * Constructs a new {@code MapView}. 355 * @param layerManager The layers to display. 278 356 * @param contentPane The content pane used to register shortcuts in its 279 357 * {@link InputMap} and {@link ActionMap} 280 358 * @param viewportData the initial viewport of the map. Can be null, then 281 359 * the viewport is derived from the layer data. 282 360 */ 283 public MapView(final JPanel contentPane, final ViewportData viewportData) { 361 public MapView(LayerManagerWithActive layerManager, final JPanel contentPane, final ViewportData viewportData) { 362 this.layerManager = layerManager; 284 363 initialViewport = viewportData; 364 layerManager.addLayerChangeListener(this); 365 layerManager.addActiveLayerChangeListener(this, false); 285 366 Main.pref.addPreferenceChangeListener(this); 286 367 287 368 addComponentListener(new ComponentAdapter() { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 353 434 } 354 435 355 436 /** 356 * Adds a GPX layer. A GPX layer is added below the lowest data layer.357 * <p>358 * Does not call {@link #fireLayerAdded(Layer)}.359 *360 * @param layer the GPX layer361 */362 protected void addGpxLayer(GpxLayer layer) {363 synchronized (layers) {364 if (layers.isEmpty()) {365 layers.add(layer);366 return;367 }368 for (int i = layers.size()-1; i >= 0; i--) {369 if (layers.get(i) instanceof OsmDataLayer) {370 if (i == layers.size()-1) {371 layers.add(layer);372 } else {373 layers.add(i+1, layer);374 }375 return;376 }377 }378 layers.add(0, layer);379 }380 }381 382 /**383 437 * Add a layer to the current MapView. The layer will be added at topmost 384 438 * position. 385 439 * @param layer The layer to add 386 440 */ 441 @Deprecated 387 442 public void addLayer(Layer layer) { 388 boolean isOsmDataLayer = layer instanceof OsmDataLayer; 389 EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class); 390 Layer oldActiveLayer = activeLayer; 391 OsmDataLayer oldEditLayer = editLayer; 392 393 synchronized (layers) { 394 if (layer instanceof MarkerLayer && playHeadMarker == null) { 395 playHeadMarker = PlayHeadMarker.create(); 396 } 397 398 if (layer instanceof GpxLayer) { 399 addGpxLayer((GpxLayer) layer); 400 } else if (layers.isEmpty()) { 401 layers.add(layer); 402 } else if (layer.isBackgroundLayer()) { 403 int i = 0; 404 for (; i < layers.size(); i++) { 405 if (layers.get(i).isBackgroundLayer()) { 406 break; 407 } 408 } 409 layers.add(i, layer); 410 } else { 411 layers.add(0, layer); 412 } 413 414 if (isOsmDataLayer || oldActiveLayer == null) { 415 // autoselect the new layer 416 listenersToFire.addAll(setActiveLayer(layer, true)); 417 } 418 419 if (isOsmDataLayer) { 420 ((OsmDataLayer) layer).addLayerStateChangeListener(this); 421 } 443 layerManager.addLayer(layer); 444 } 422 445 423 layer.addPropertyChangeListener(this); 424 Main.addProjectionChangeListener(layer); 425 AudioPlayer.reset(); 446 @Override 447 public void layerAdded(LayerAddEvent e) { 448 Layer layer = e.getAddedLayer(); 449 if (layer instanceof MarkerLayer && playHeadMarker == null) { 450 playHeadMarker = PlayHeadMarker.create(); 426 451 } 427 fireLayerAdded(layer);428 onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);429 452 430 if (!listenersToFire.isEmpty()) { 431 repaint(); 453 boolean isOsmDataLayer = layer instanceof OsmDataLayer; 454 if (isOsmDataLayer) { 455 ((OsmDataLayer) layer).addLayerStateChangeListener(this); 432 456 } 457 458 layer.addPropertyChangeListener(this); 459 Main.addProjectionChangeListener(layer); 460 AudioPlayer.reset(); 461 462 repaint(); 433 463 } 434 464 435 465 @Override 466 @Deprecated 436 467 protected DataSet getCurrentDataSet() { 437 synchronized (layers) { 438 if (editLayer != null) 439 return editLayer.data; 440 else 441 return null; 442 } 468 return layerManager.getEditDataSet(); 443 469 } 444 470 445 471 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 448 474 * @return true if the active data layer (edit layer) is drawable, false otherwise 449 475 */ 450 476 public boolean isActiveLayerDrawable() { 451 synchronized (layers) { 452 return editLayer != null; 453 } 477 return getEditLayer() != null; 454 478 } 455 479 456 480 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 459 483 * @return true if the active data layer (edit layer) is visible, false otherwise 460 484 */ 461 485 public boolean isActiveLayerVisible() { 462 synchronized (layers) { 463 return isActiveLayerDrawable() && editLayer.isVisible(); 464 } 465 } 466 467 /** 468 * Determines the next active data layer according to the following 469 * rules: 470 * <ul> 471 * <li>if there is at least one {@link OsmDataLayer} the first one 472 * becomes active</li> 473 * <li>otherwise, the top most layer of any type becomes active</li> 474 * </ul> 475 * 476 * @return the next active data layer 477 */ 478 protected Layer determineNextActiveLayer(List<Layer> layersList) { 479 // First look for data layer 480 for (Layer layer:layersList) { 481 if (layer instanceof OsmDataLayer) 482 return layer; 483 } 484 485 // Then any layer 486 if (!layersList.isEmpty()) 487 return layersList.get(0); 488 489 // and then give up 490 return null; 491 486 OsmDataLayer e = getEditLayer(); 487 return e != null && e.isVisible(); 492 488 } 493 489 494 490 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 496 492 * an LayerChange event is fired. 497 493 * @param layer The layer to remove 498 494 */ 495 @Deprecated 499 496 public void removeLayer(Layer layer) { 500 EnumSet<LayerListenerType> listenersToFire = EnumSet.noneOf(LayerListenerType.class); 501 Layer oldActiveLayer = activeLayer; 502 OsmDataLayer oldEditLayer = editLayer; 503 504 synchronized (layers) { 505 List<Layer> layersList = new ArrayList<>(layers); 506 507 if (!layersList.remove(layer)) 508 return; 509 510 listenersToFire = setEditLayer(layersList); 511 512 if (layer == activeLayer) { 513 listenersToFire.addAll(setActiveLayer(determineNextActiveLayer(layersList), false)); 514 } 515 516 if (layer instanceof OsmDataLayer) { 517 ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this); 518 } 497 layerManager.removeLayer(layer); 498 } 519 499 520 layers.remove(layer);521 Main.removeProjectionChangeListener(layer);522 layer.removePropertyChangeListener(this);523 layer.destroy();524 AudioPlayer.reset();500 @Override 501 public void layerRemoving(LayerRemoveEvent e) { 502 Layer layer = e.getRemovedLayer(); 503 if (layer instanceof OsmDataLayer) { 504 ((OsmDataLayer) layer).removeLayerPropertyChangeListener(this); 525 505 } 526 onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire);527 fireLayerRemoved(layer);528 506 529 repaint(); 530 } 507 Main.removeProjectionChangeListener(layer); 508 layer.removePropertyChangeListener(this); 509 layer.destroy(); 510 AudioPlayer.reset(); 531 511 532 private void onEditLayerChanged(OsmDataLayer oldEditLayer) { 533 fireEditLayerChanged(oldEditLayer, editLayer); 534 refreshTitle(); 512 repaint(); 535 513 } 536 514 537 515 private boolean virtualNodesEnabled = false; … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 560 538 * @param pos The new position of the layer 561 539 */ 562 540 public void moveLayer(Layer layer, int pos) { 563 EnumSet<LayerListenerType> listenersToFire; 564 Layer oldActiveLayer = activeLayer; 565 OsmDataLayer oldEditLayer = editLayer; 566 567 synchronized (layers) { 568 int curLayerPos = layers.indexOf(layer); 569 if (curLayerPos == -1) 570 throw new IllegalArgumentException(tr("Layer not in list.")); 571 if (pos == curLayerPos) 572 return; // already in place. 573 layers.remove(curLayerPos); 574 if (pos >= layers.size()) { 575 layers.add(layer); 576 } else { 577 layers.add(pos, layer); 578 } 579 listenersToFire = setEditLayer(layers); 580 AudioPlayer.reset(); 581 } 582 onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire); 541 layerManager.moveLayer(layer, pos); 542 } 583 543 544 @Override 545 public void layerOrderChanged(LayerOrderChangeEvent e) { 546 AudioPlayer.reset(); 584 547 repaint(); 585 548 } 586 549 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 590 553 * @return The index in the list. 591 554 * @throws IllegalArgumentException if that layer does not belong to this view. 592 555 */ 556 @Deprecated 593 557 public int getLayerPos(Layer layer) { 594 558 int curLayerPos; 595 synchronized (layers) { 596 curLayerPos = layers.indexOf(layer); 597 } 559 curLayerPos = getAllLayersAsList().indexOf(layer); 598 560 if (curLayerPos == -1) 599 561 throw new IllegalArgumentException(tr("Layer not in list.")); 600 562 return curLayerPos; 601 563 } 602 564 603 /**604 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order605 * first, layer with the highest Z-Order last.606 * <p>607 * The active data layer is pulled above all adjacent data layers.608 *609 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order610 * first, layer with the highest Z-Order last.611 */612 public List<Layer> getVisibleLayersInZOrder() {613 synchronized (layers) {614 List<Layer> ret = new ArrayList<>();615 // This is set while we delay the addition of the active layer.616 boolean activeLayerDelayed = false;617 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {618 Layer l = iterator.previous();619 if (!l.isVisible()) {620 // ignored621 } else if (l == activeLayer && l instanceof OsmDataLayer) {622 // delay and add after the current block of OsmDataLayer623 activeLayerDelayed = true;624 } else {625 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {626 // add active layer before the current one.627 ret.add(activeLayer);628 activeLayerDelayed = false;629 }630 // Add this layer now631 ret.add(l);632 }633 }634 if (activeLayerDelayed) {635 ret.add(activeLayer);636 }637 return ret;638 }639 }640 565 641 566 private void paintLayer(Layer layer, Graphics2D g, Bounds box) { 642 567 if (layer.getOpacity() < 1) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 655 580 return; 656 581 } 657 582 658 List<Layer> visibleLayers = getVisibleLayersInZOrder();583 List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder(); 659 584 660 585 int nonChangedLayersCount = 0; 661 586 for (Layer l: visibleLayers) { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 822 747 /** 823 748 * @return An unmodifiable collection of all layers 824 749 */ 750 @Deprecated 825 751 public Collection<Layer> getAllLayers() { 826 synchronized (layers) { 827 return Collections.unmodifiableCollection(new ArrayList<>(layers)); 828 } 752 return layerManager.getLayers(); 829 753 } 830 754 831 755 /** 832 756 * @return An unmodifiable ordered list of all layers 833 757 */ 758 @Deprecated 834 759 public List<Layer> getAllLayersAsList() { 835 synchronized (layers) { 836 return Collections.unmodifiableList(new ArrayList<>(layers)); 837 } 760 return layerManager.getLayers(); 838 761 } 839 762 840 763 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 848 771 * @param ofType The layer type. 849 772 * @return an unmodifiable list of layers of a certain type. 850 773 */ 774 @Deprecated 851 775 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) { 852 return new ArrayList<>(Utils.filteredCollection(getAllLayers(), ofType));776 return layerManager.getLayersOfType(ofType); 853 777 } 854 778 855 779 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 857 781 * 858 782 * @return the number of layers managed by this map view 859 783 */ 784 @Deprecated 860 785 public int getNumLayers() { 861 synchronized (layers) { 862 return layers.size(); 863 } 786 return getAllLayers().size(); 864 787 } 865 788 866 789 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 868 791 * 869 792 * @return true if there is at least one layer in this map view 870 793 */ 794 @Deprecated 871 795 public boolean hasLayers() { 872 796 return getNumLayers() > 0; 873 797 } 874 798 875 799 /** 876 * Sets the active edit layer.877 * <p>878 * You must own a write {@link #layerLock} when calling this method.879 * @param layersList A list to select that layer from.880 * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}881 */882 private EnumSet<LayerListenerType> setEditLayer(List<Layer> layersList) {883 final OsmDataLayer newEditLayer = findNewEditLayer(layersList);884 885 // Set new edit layer886 if (newEditLayer != editLayer) {887 if (newEditLayer == null) {888 // Note: Unsafe to call while layer write lock is held.889 getCurrentDataSet().setSelected();890 }891 892 editLayer = newEditLayer;893 return EnumSet.of(LayerListenerType.EDIT_LAYER_CHANGE);894 } else {895 return EnumSet.noneOf(LayerListenerType.class);896 }897 898 }899 900 private OsmDataLayer findNewEditLayer(List<Layer> layersList) {901 OsmDataLayer newEditLayer = layersList.contains(editLayer) ? editLayer : null;902 // Find new edit layer903 if (activeLayer != editLayer || !layersList.contains(editLayer)) {904 if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {905 newEditLayer = (OsmDataLayer) activeLayer;906 } else {907 for (Layer layer:layersList) {908 if (layer instanceof OsmDataLayer) {909 newEditLayer = (OsmDataLayer) layer;910 break;911 }912 }913 }914 }915 return newEditLayer;916 }917 918 /**919 800 * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance 920 801 * of {@link OsmDataLayer} also sets {@link #editLayer} to <code>layer</code>. 921 802 * 922 803 * @param layer the layer to be activate; must be one of the layers in the list of layers 923 804 * @throws IllegalArgumentException if layer is not in the lis of layers 924 805 */ 806 @Deprecated 925 807 public void setActiveLayer(Layer layer) { 926 EnumSet<LayerListenerType> listenersToFire; 927 Layer oldActiveLayer; 928 OsmDataLayer oldEditLayer; 929 930 synchronized (layers) { 931 oldActiveLayer = activeLayer; 932 oldEditLayer = editLayer; 933 listenersToFire = setActiveLayer(layer, true); 934 } 935 onActiveEditLayerChanged(oldActiveLayer, oldEditLayer, listenersToFire); 936 937 repaint(); 808 layerManager.setActiveLayer(layer); 938 809 } 939 940 /**941 * Sets the active layer. Propagates this change to all map buttons.942 * @param layer The layer to be active.943 * @param setEditLayer if this is <code>true</code>, the edit layer is also set.944 * @return A list of change listeners that should be fired using {@link #onActiveEditLayerChanged(Layer, OsmDataLayer, EnumSet)}945 */946 private EnumSet<LayerListenerType> setActiveLayer(final Layer layer, boolean setEditLayer) {947 if (layer != null && !layers.contains(layer))948 throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));949 950 if (layer == activeLayer)951 return EnumSet.noneOf(LayerListenerType.class);952 953 activeLayer = layer;954 EnumSet<LayerListenerType> listenersToFire = EnumSet.of(LayerListenerType.ACTIVE_LAYER_CHANGE);955 if (setEditLayer) {956 listenersToFire.addAll(setEditLayer(layers));957 }958 959 return listenersToFire;960 }961 962 810 /** 963 811 * Replies the currently active layer 964 812 * 965 813 * @return the currently active layer (may be null) 966 814 */ 815 @Deprecated 967 816 public Layer getActiveLayer() { 968 synchronized (layers) { 969 return activeLayer; 970 } 971 } 972 973 private enum LayerListenerType { 974 ACTIVE_LAYER_CHANGE, 975 EDIT_LAYER_CHANGE 817 return layerManager.getActiveLayer(); 976 818 } 977 819 978 /** 979 * This is called whenever one of active layer/edit layer or both may have been changed, 980 * @param oldActive The old active layer 981 * @param oldEdit The old edit layer. 982 * @param listenersToFire A mask of listeners to fire using {@link LayerListenerType}s 983 */ 984 private void onActiveEditLayerChanged(final Layer oldActive, final OsmDataLayer oldEdit, EnumSet<LayerListenerType> listenersToFire) { 985 if (listenersToFire.contains(LayerListenerType.EDIT_LAYER_CHANGE)) { 986 onEditLayerChanged(oldEdit); 987 } 988 if (listenersToFire.contains(LayerListenerType.ACTIVE_LAYER_CHANGE)) { 989 onActiveLayerChanged(oldActive); 990 } 991 } 992 993 private void onActiveLayerChanged(final Layer old) { 994 fireActiveLayerChanged(old, activeLayer); 995 820 @Override 821 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { 996 822 /* This only makes the buttons look disabled. Disabling the actions as well requires 997 823 * the user to re-select the tool after i.e. moving a layer. While testing I found 998 824 * that I switch layers and actions at the same time and it was annoying to mind the 999 825 * order. This way it works as visual clue for new users */ 1000 826 for (final AbstractButton b: Main.map.allMapModeButtons) { 1001 827 MapMode mode = (MapMode) b.getAction(); 1002 final boolean activeLayerSupported = mode.layerIsSupported( activeLayer);828 final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer()); 1003 829 if (activeLayerSupported) { 1004 830 Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876 1005 831 } else { … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 1012 838 }); 1013 839 } 1014 840 AudioPlayer.reset(); 841 refreshTitle(); 1015 842 repaint(); 1016 843 } 1017 844 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 1020 847 * 1021 848 * @return the current edit layer. May be null. 1022 849 */ 850 @Deprecated 1023 851 public OsmDataLayer getEditLayer() { 1024 synchronized (layers) { 1025 return editLayer; 1026 } 852 return layerManager.getEditLayer(); 1027 853 } 1028 854 1029 855 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 1032 858 * @param layer the layer 1033 859 * @return true if the list of layers managed by this map view contain layer 1034 860 */ 861 @Deprecated 1035 862 public boolean hasLayer(Layer layer) { 1036 synchronized (layers) { 1037 return layers.contains(layer); 1038 } 863 return layerManager.containsLayer(layer); 1039 864 } 1040 865 1041 866 /** … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 1099 924 */ 1100 925 protected void refreshTitle() { 1101 926 if (Main.parent != null) { 1102 synchronized (layers) { 1103 boolean dirty = editLayer != null && 1104 (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); 1105 ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); 1106 ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty); 1107 } 927 OsmDataLayer editLayer = layerManager.getEditLayer(); 928 boolean dirty = editLayer != null && 929 (editLayer.requiresSaveToFile() || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); 930 ((JFrame) Main.parent).setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); 931 ((JFrame) Main.parent).getRootPane().putClientProperty("Window.documentModified", dirty); 1108 932 } 1109 933 } 1110 934 … … implements PropertyChangeListener, PreferenceChangedListener, OsmDataLayer.Layer 1123 947 }; 1124 948 1125 949 public void destroy() { 950 layerManager.removeLayerChangeListener(this); 951 layerManager.removeActiveLayerChangeListener(this); 1126 952 Main.pref.removePreferenceChangeListener(this); 1127 953 DataSet.removeSelectionListener(repaintSelectionChangedListener); 1128 954 MultipolygonCache.getInstance().clear(this); 1129 955 if (mapMover != null) { 1130 956 mapMover.destroy(); 1131 957 } 1132 synchronized (layers) { 1133 activeLayer = null; 1134 changedLayer = null; 1135 editLayer = null; 1136 layers.clear(); 1137 nonChangedLayers.clear(); 1138 } 958 nonChangedLayers.clear(); 1139 959 synchronized (temporaryLayers) { 1140 960 temporaryLayers.clear(); 1141 961 } -
src/org/openstreetmap/josm/gui/layer/GpxLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java index 25ca961..f20d465 100644
a b public class GpxLayer extends Layer { 364 364 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save GPX file"), GpxImporter.FILE_FILTER); 365 365 } 366 366 367 @Override 368 public LayerPositionStrategy getPositionStrategy() { 369 return LayerPositionStrategy.AFTER_LAST_DATA_LAYER; 370 } 371 367 372 } -
src/org/openstreetmap/josm/gui/layer/Layer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java index e16587b..3218636 100644
a b public abstract class Layer implements Destroyable, MapViewPaintable, Projection 506 506 public File createAndOpenSaveFileChooser() { 507 507 return SaveActionBase.createAndOpenSaveFileChooser(tr("Save Layer"), "lay"); 508 508 } 509 510 /** 511 * Gets the strategy that specifies where this layer should be inserted in a layer list. 512 * @return That strategy. 513 */ 514 public LayerPositionStrategy getPositionStrategy() { 515 return isBackgroundLayer() ? LayerPositionStrategy.BEFORE_FIRST_BACKGROUND_LAYER : LayerPositionStrategy.IN_FRONT; 516 } 509 517 } -
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..c39abdf
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.layer; 3 4 import java.util.ArrayList; 5 import java.util.Collections; 6 import java.util.List; 7 import java.util.concurrent.CopyOnWriteArrayList; 8 9 import org.openstreetmap.josm.Main; 10 import 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 */ 21 public 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 * @param e The new added layer event 29 */ 30 void layerAdded(LayerAddEvent e); 31 32 /** 33 * Notifies this listener that a layer is about to be removed. 34 * @param e The layer to be removed (as event) 35 */ 36 void layerRemoving(LayerRemoveEvent e); 37 38 /** 39 * Notifies this listener that the order of layers was changed. 40 * @param e The order change event. 41 */ 42 void layerOrderChanged(LayerOrderChangeEvent e); 43 } 44 45 protected static class LayerManagerEvent { 46 private final LayerManager source; 47 48 LayerManagerEvent(LayerManager source) { 49 this.source = source; 50 } 51 52 public LayerManager getSource() { 53 return source; 54 } 55 } 56 57 /** 58 * The event that is fired whenever a layer was added. 59 * @author Michael Zangl 60 */ 61 public static class LayerAddEvent extends LayerManagerEvent { 62 private final Layer addedLayer; 63 64 LayerAddEvent(LayerManager source, Layer addedLayer) { 65 super(source); 66 this.addedLayer = addedLayer; 67 } 68 69 /** 70 * Gets the layer that was added. 71 * @return The added layer. 72 */ 73 public Layer getAddedLayer() { 74 return addedLayer; 75 } 76 } 77 78 /** 79 * The event that is fired before removing a layer. 80 * @author Michael Zangl 81 */ 82 public static class LayerRemoveEvent extends LayerManagerEvent { 83 private final Layer removedLayer; 84 85 LayerRemoveEvent(LayerManager source, Layer removedLayer) { 86 super(source); 87 this.removedLayer = removedLayer; 88 } 89 90 /** 91 * Gets the layer that is about to be removed. 92 * @return The layer. 93 */ 94 public Layer getRemovedLayer() { 95 return removedLayer; 96 } 97 } 98 99 /** 100 * An event that is fired whenever the order of layers changed. 101 * @author Michael Zangl 102 */ 103 public static class LayerOrderChangeEvent extends LayerManagerEvent { 104 LayerOrderChangeEvent(LayerManager source) { 105 super(source); 106 } 107 108 } 109 110 /** 111 * This is the list of layers we manage. 112 */ 113 private final List<Layer> layers = new ArrayList<>(); 114 115 private final List<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>(); 116 117 /** 118 * Add a layer. The layer will be added at a given psoition. 119 * @param layer The layer to add 120 */ 121 public synchronized void addLayer(Layer layer) { 122 if (containsLayer(layer)) { 123 throw new IllegalArgumentException("Cannot add a layer twice."); 124 } 125 LayerPositionStrategy positionStrategy = layer.getPositionStrategy(); 126 int position = positionStrategy.getPosition(this); 127 checkPosition(position); 128 insertLayerAt(layer, position); 129 fireLayerAdded(layer); 130 } 131 132 /** 133 * Remove the layer from the mapview. If the layer was in the list before, 134 * an LayerChange event is fired. 135 * @param layer The layer to remove 136 */ 137 public synchronized void removeLayer(Layer layer) { 138 checkContainsLayer(layer); 139 140 fireLayerRemoving(layer); 141 layers.remove(layer); 142 } 143 144 /** 145 * Move a layer to a new position. 146 * @param layer The layer to move. 147 * @param position The position. 148 * @throws IndexOutOfBoundsException if the position is out of bounds. 149 */ 150 public synchronized void moveLayer(Layer layer, int position) { 151 checkContainsLayer(layer); 152 checkPosition(position); 153 154 int curLayerPos = layers.indexOf(layer); 155 if (position == curLayerPos) 156 return; // already in place. 157 layers.remove(curLayerPos); 158 insertLayerAt(layer, position); 159 fireLayerOrderChanged(); 160 } 161 162 private void insertLayerAt(Layer layer, int position) { 163 if (position == layers.size()) { 164 layers.add(layer); 165 } else { 166 layers.add(position, layer); 167 } 168 } 169 170 private void checkPosition(int position) { 171 if (position < 0 || position > layers.size()) { 172 throw new IndexOutOfBoundsException("Position " + position + " out of range."); 173 } 174 } 175 176 /** 177 * Gets an unmodifiable list of all layers that are currently in this manager. This list won't update once layers are added or removed. 178 * @return The list of layers. 179 */ 180 public List<Layer> getLayers() { 181 return Collections.unmodifiableList(new ArrayList<>(layers)); 182 } 183 184 /** 185 * Replies an unmodifiable list of layers of a certain type. 186 * 187 * Example: 188 * <pre> 189 * List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class); 190 * </pre> 191 * 192 * @param ofType The layer type. 193 * @return an unmodifiable list of layers of a certain type. 194 */ 195 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) { 196 return new ArrayList<>(Utils.filteredCollection(getLayers(), ofType)); 197 } 198 199 /** 200 * replies true if the list of layers managed by this map view contain layer 201 * 202 * @param layer the layer 203 * @return true if the list of layers managed by this map view contain layer 204 */ 205 public synchronized boolean containsLayer(Layer layer) { 206 return layers.contains(layer); 207 } 208 209 protected void checkContainsLayer(Layer layer) { 210 if (!containsLayer(layer)) { 211 throw new IllegalArgumentException(layer + " is not managed by us."); 212 } 213 } 214 215 /** 216 * Adds a layer change listener 217 * 218 * @param listener the listener. 219 */ 220 public synchronized void addLayerChangeListener(LayerChangeListener listener) { 221 addLayerChangeListener(listener, false); 222 } 223 224 /** 225 * Adds a layer change listener 226 * 227 * @param listener the listener. 228 * @param fireAdd if we should fire an add event for every layer in this manager. 229 */ 230 public synchronized void addLayerChangeListener(LayerChangeListener listener, boolean fireAdd) { 231 if (layerChangeListeners.contains(listener)) { 232 throw new IllegalArgumentException("Listener already registered."); 233 } 234 layerChangeListeners.add(listener); 235 if (fireAdd) { 236 for (Layer l : getLayers()) { 237 listener.layerAdded(new LayerAddEvent(this, l)); 238 } 239 } 240 } 241 242 /** 243 * Removes a layer change listener 244 * 245 * @param listener the listener. Ignored if null or already registered. 246 */ 247 public synchronized void removeLayerChangeListener(LayerChangeListener listener) { 248 removeLayerChangeListener(listener, false); 249 } 250 251 252 /** 253 * Removes a layer change listener 254 * 255 * @param listener the listener. 256 * @param fireRemove if we should fire a remove event for every layer in this manager. 257 */ 258 public synchronized void removeLayerChangeListener(LayerChangeListener listener, boolean fireRemove) { 259 if (!layerChangeListeners.remove(listener)) { 260 //throw new IllegalArgumentException("Listener was not registered before: " + listener); 261 Main.error("Listener was not registered before: " + listener); 262 } else { 263 if (fireRemove) { 264 for (Layer l : getLayers()) { 265 listener.layerRemoving(new LayerRemoveEvent(this, l)); 266 } 267 } 268 } 269 } 270 271 private void fireLayerAdded(Layer layer) { 272 LayerAddEvent e = new LayerAddEvent(this, layer); 273 for (LayerChangeListener l : layerChangeListeners) { 274 l.layerAdded(e); 275 } 276 } 277 278 private void fireLayerRemoving(Layer layer) { 279 LayerRemoveEvent e = new LayerRemoveEvent(this, layer); 280 for (LayerChangeListener l : layerChangeListeners) { 281 l.layerRemoving(e); 282 } 283 } 284 285 private void fireLayerOrderChanged() { 286 LayerOrderChangeEvent e = new LayerOrderChangeEvent(this); 287 for (LayerChangeListener l : layerChangeListeners) { 288 l.layerOrderChanged(e); 289 } 290 } 291 } -
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..2f6aafe
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.layer; 3 4 import java.util.ArrayList; 5 import java.util.List; 6 import java.util.ListIterator; 7 import java.util.concurrent.CopyOnWriteArrayList; 8 9 import org.openstreetmap.josm.data.osm.DataSet; 10 11 /** 12 * This class extends the layer manager by adding an active and an edit layer. 13 * <p> 14 * The active layer is the layer the user is currently working on. 15 * <p> 16 * The edit layer is an data layer that we currently work with. 17 * @author Michael Zangl 18 */ 19 public class LayerManagerWithActive extends LayerManager { 20 /** 21 * This listener listens to changes of the active or the edit layer. 22 * @author Michael Zangl 23 * 24 */ 25 public interface ActiveLayerChangeListener { 26 /** 27 * Called whenever the active or edit layer changed. 28 * <p> 29 * You can be sure that this layer is still contained in this set. 30 * @param e The change event. 31 */ 32 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e); 33 } 34 35 /** 36 * This event is fired whenever the active or the edit layer changes. 37 * @author Michael Zangl 38 */ 39 public class ActiveLayerChangeEvent extends LayerManagerEvent { 40 41 private final OsmDataLayer previousEditLayer; 42 43 private final Layer previousActiveLayer; 44 45 /** 46 * Create a new {@link ActiveLayerChangeEvent} 47 * @param source The source 48 * @param previousEditLayer the previous edit layer 49 * @param previousActiveLayer the previous active layer 50 */ 51 ActiveLayerChangeEvent(LayerManagerWithActive source, OsmDataLayer previousEditLayer, 52 Layer previousActiveLayer) { 53 super(source); 54 this.previousEditLayer = previousEditLayer; 55 this.previousActiveLayer = previousActiveLayer; 56 } 57 58 /** 59 * Gets the edit layer that was previously used. 60 * @return The old edit layer, <code>null</code> if there is none. 61 */ 62 public OsmDataLayer getPreviousEditLayer() { 63 return previousEditLayer; 64 } 65 66 /** 67 * Gets the active layer that was previously used. 68 * @return The old active layer, <code>null</code> if there is none. 69 */ 70 public Layer getPreviousActiveLayer() { 71 return previousActiveLayer; 72 } 73 74 @Override 75 public LayerManagerWithActive getSource() { 76 return (LayerManagerWithActive) super.getSource(); 77 } 78 } 79 80 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>(); 81 82 /** 83 * Adds a active/edit layer change listener 84 * 85 * @param listener the listener. 86 * @param initialFire fire a fake active-layer-changed-event right after adding 87 * the listener. The previous layers will be null. 88 */ 89 public void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) { 90 activeLayerChangeListeners.add(listener); 91 if (initialFire) { 92 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null)); 93 } 94 } 95 96 /** 97 * Removes an active/edit layer change listener. 98 * @param listener the listener. 99 */ 100 public void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) { 101 activeLayerChangeListeners.remove(listener); 102 } 103 /** 104 * The layer from the layers list that is currently active. 105 */ 106 private Layer activeLayer; 107 108 /** 109 * The edit layer is the current active data layer. 110 */ 111 private OsmDataLayer editLayer; 112 113 /** 114 * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed. 115 * @param layer The active layer. 116 */ 117 public synchronized void setActiveLayer(Layer layer) { 118 checkContainsLayer(layer); 119 setActiveLayer(layer, false); 120 } 121 122 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) { 123 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 124 activeLayer = layer; 125 if (activeLayer instanceof OsmDataLayer) { 126 editLayer = (OsmDataLayer) activeLayer; 127 } else if (forceEditLayerUpdate) { 128 editLayer = null; 129 } 130 fireActiveLayerChange(event); 131 } 132 133 private void fireActiveLayerChange(ActiveLayerChangeEvent event) { 134 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) { 135 for (ActiveLayerChangeListener l : activeLayerChangeListeners) { 136 l.activeOrEditLayerChanged(event); 137 } 138 } 139 } 140 141 @Override 142 public synchronized void addLayer(Layer layer) { 143 super.addLayer(layer); 144 145 // update the active layer automatically. 146 if (layer instanceof OsmDataLayer || activeLayer == null) { 147 setActiveLayer(layer); 148 } 149 } 150 151 @Override 152 public synchronized void removeLayer(Layer layer) { 153 if (layer == activeLayer || layer == editLayer) { 154 Layer nextActive = suggestNextActiveLayer(layer); 155 setActiveLayer(nextActive, true); 156 } 157 158 super.removeLayer(layer); 159 } 160 161 /** 162 * Determines the next active data layer according to the following 163 * rules: 164 * <ul> 165 * <li>if there is at least one {@link OsmDataLayer} the first one 166 * becomes active</li> 167 * <li>otherwise, the top most layer of any type becomes active</li> 168 * </ul> 169 * 170 * @param except A layer to ignore. 171 * @return the next active data layer 172 */ 173 private Layer suggestNextActiveLayer(Layer except) { 174 List<Layer> layersList = new ArrayList<>(getLayers()); 175 layersList.remove(except); 176 // First look for data layer 177 for (Layer layer : layersList) { 178 if (layer instanceof OsmDataLayer) 179 return layer; 180 } 181 182 // Then any layer 183 if (!layersList.isEmpty()) 184 return layersList.get(0); 185 186 // and then give up 187 return null; 188 } 189 190 /** 191 * Replies the currently active layer 192 * 193 * @return the currently active layer (may be null) 194 */ 195 public synchronized Layer getActiveLayer() { 196 return activeLayer; 197 } 198 199 /** 200 * Replies the current edit layer, if any 201 * 202 * @return the current edit layer. May be null. 203 */ 204 public synchronized OsmDataLayer getEditLayer() { 205 return editLayer; 206 } 207 208 /** 209 * Gets the data set of the active edit layer. 210 * @return That data set, <code>null</code> if there is no edit layer. 211 */ 212 public synchronized DataSet getEditDataSet() { 213 return editLayer != null ? editLayer.data : null; 214 } 215 216 217 /** 218 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order 219 * first, layer with the highest Z-Order last. 220 * <p> 221 * The active data layer is pulled above all adjacent data layers. 222 * 223 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order 224 * first, layer with the highest Z-Order last. 225 */ 226 public synchronized List<Layer> getVisibleLayersInZOrder() { 227 List<Layer> ret = new ArrayList<>(); 228 // This is set while we delay the addition of the active layer. 229 boolean activeLayerDelayed = false; 230 List<Layer> layers = getLayers(); 231 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) { 232 Layer l = iterator.previous(); 233 if (!l.isVisible()) { 234 // ignored 235 } else if (l == activeLayer && l instanceof OsmDataLayer) { 236 // delay and add after the current block of OsmDataLayer 237 activeLayerDelayed = true; 238 } else { 239 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) { 240 // add active layer before the current one. 241 ret.add(activeLayer); 242 activeLayerDelayed = false; 243 } 244 // Add this layer now 245 ret.add(l); 246 } 247 } 248 if (activeLayerDelayed) { 249 ret.add(activeLayer); 250 } 251 return ret; 252 } 253 } -
new file 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 new file mode 100644 index 0000000..73d18e3
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.layer; 3 4 import java.util.List; 5 6 /** 7 * This class defines a position to insert a given layer in the list of layers. 8 * @author Michael Zangl 9 */ 10 public abstract class LayerPositionStrategy { 11 12 /** 13 * always inserts at the front of the stack. 14 */ 15 public static final LayerPositionStrategy IN_FRONT = new LayerPositionStrategy() { 16 @Override 17 public int getPosition(LayerManager manager) { 18 return 0; 19 } 20 }; 21 22 /** 23 * A GPX layer is added below the lowest data layer. 24 */ 25 public static final LayerPositionStrategy AFTER_LAST_DATA_LAYER = new LayerPositionStrategy() { 26 @Override 27 public int getPosition(LayerManager manager) { 28 List<Layer> layers = manager.getLayers(); 29 for (int i = layers.size() - 1; i >= 0; i--) { 30 if (layers.get(i) instanceof OsmDataLayer) { 31 return i + 1; 32 } 33 } 34 return 0; 35 } 36 }; 37 38 /** 39 * The default for background layers: They are added before the first background layer in the list. If there is n one, they are added at the end of the list. 40 */ 41 public static final LayerPositionStrategy BEFORE_FIRST_BACKGROUND_LAYER = new LayerPositionStrategy() { 42 @Override 43 public int getPosition(LayerManager manager) { 44 List<Layer> layers = manager.getLayers(); 45 for (int i = 0; i < layers.size(); i++) { 46 if (layers.get(i).isBackgroundLayer()) { 47 return i; 48 } 49 } 50 return layers.size(); 51 } 52 }; 53 54 /** 55 * Gets the position where the layer should be inserted 56 * @param manager The layer manager to insert the layer in. 57 * @return The position in the range 0...layers.size 58 */ 59 public abstract int getPosition(LayerManager manager); 60 } -
test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java
diff --git a/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java b/test/unit/org/openstreetmap/josm/actions/mapmode/SelectActionTest.java index 82eb98b..f833384 100644
a b public class SelectActionTest { 49 49 }; 50 50 51 51 MapViewMock(DataSet dataSet, OsmDataLayer layer) { 52 super( null, null);52 super(Main.layerManager, null, null); 53 53 this.layer = layer; 54 54 this.currentDataSet = dataSet; 55 55 }
