Ticket #23057: josm_23057.patch
File josm_23057.patch, 12.7 KB (added by , 15 months ago) |
---|
-
src/org/openstreetmap/josm/gui/dialogs/layer/DeleteLayerAction.java
47 47 return; 48 48 if (!SaveLayersDialog.saveUnsavedModifications(selectedLayers, SaveLayersDialog.Reason.DELETE)) 49 49 return; 50 final Map<Integer, Layer> layerMap = model.selectedIndices().filter(i -> model.getLayer(i) != null)51 .collect(HashMap::new, (map, value) -> map.put(value, model.getLayer(value)), HashMap::putAll);52 50 for (Layer l: selectedLayers) { 53 51 if (model.getLayerManager().containsLayer(l)) { 54 52 // it may happen that this call removes other layers. … … 56 54 model.getLayerManager().removeLayer(l); 57 55 } 58 56 } 59 // Set the next active layer to the next visible layer60 if (layerMap.size() == 1) {61 final int selected = Math.min(layerMap.keySet().iterator().next(), model.getRowCount() - 1);62 int currentLayerIndex = selected;63 Layer layer = model.getLayer(currentLayerIndex);64 // If the user has the last layer selected, we need to wrap around.65 boolean reversed = false;66 while (layer != null && !layer.isVisible() && currentLayerIndex < model.getRowCount() && currentLayerIndex >= 0) {67 if (reversed) {68 currentLayerIndex--;69 } else {70 currentLayerIndex++;71 }72 if (currentLayerIndex == model.getRowCount()) {73 reversed = true;74 currentLayerIndex = selected;75 }76 layer = model.getLayer(currentLayerIndex);77 }78 if (layer != null) {79 model.getLayerManager().setActiveLayer(layer);80 // Reset the selection81 model.getSelectionModel().setSelectionInterval(selected, selected);82 }83 }84 57 } 85 58 86 59 @Override -
test/unit/org/openstreetmap/josm/gui/dialogs/layer/DeleteLayerActionTest.java
10 10 import static org.junit.jupiter.api.Assertions.assertSame; 11 11 import static org.junit.jupiter.api.Assertions.assertTrue; 12 12 13 import java.util.Collections; 13 14 import java.util.Objects; 14 15 import java.util.concurrent.atomic.AtomicInteger; 15 16 import java.util.function.Supplier; … … 21 22 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 22 23 import org.openstreetmap.josm.gui.layer.Layer; 23 24 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 25 import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer; 24 26 import org.openstreetmap.josm.testutils.annotations.Main; 25 27 import org.openstreetmap.josm.testutils.annotations.Projection; 26 28 … … 122 124 assertAll(model.getLayers().stream().map(layer -> () -> assertNotSame(toRemove, layer))); 123 125 } 124 126 127 @Test 128 void testRemoveBottomActiveWithBackgroundLayer() { 129 GeoImageLayer geoImageLayer = new GeoImageLayer(Collections.emptyList(), null, "imageLayer"); 130 OsmDataLayer osmDataLayer1 = new OsmDataLayer(new DataSet(), "dataLayer1", null); 131 OsmDataLayer osmDataLayer2 = new OsmDataLayer(new DataSet(), "dataLayer2", null); 132 133 // remove all the layers added in BeforeEach() 134 for (Layer l : MainApplication.getLayerManager().getLayers()) { 135 MainApplication.getLayerManager().removeLayer(l); 136 } 137 MainApplication.getLayerManager().addLayer(geoImageLayer); 138 MainApplication.getLayerManager().addLayer(osmDataLayer1); 139 MainApplication.getLayerManager().addLayer(osmDataLayer2); 140 141 model.getLayerManager().setActiveLayer(osmDataLayer1); 142 model.setSelectedLayer(osmDataLayer1); 143 144 deleteLayerAction.actionPerformed(null); 145 146 assertSame(model.getLayerManager().getActiveLayer(), model.getLayer(0)); 147 assertEquals("dataLayer2", Objects.requireNonNull(model.getLayerManager().getActiveLayer().getName())); 148 assertAll(model.getLayers().stream().map(layer -> () -> assertNotSame(osmDataLayer1, layer))); 149 } 150 151 @Test 152 void testRemoveBottomActiveAllHidden() { 153 hideRange(0, 9); 154 final Layer toRemove = model.getLayer(9); 155 assertNotNull(toRemove); 156 assertFalse(toRemove.isVisible()); 157 assertEquals(0, model.getLayers().stream().filter(Layer::isVisible).count()); 158 159 model.getLayerManager().setActiveLayer(toRemove); 160 model.setSelectedLayer(toRemove); 161 deleteLayerAction.actionPerformed(null); 162 163 assertSame(model.getLayerManager().getActiveLayer(), model.getLayer(8)); 164 assertEquals("testActiveLayer1", Objects.requireNonNull(model.getLayer(8)).getName()); 165 assertAll(model.getLayers().stream().map(layer -> () -> assertNotSame(toRemove, layer))); 166 } 167 125 168 private void hideRange(int start, int end) { 126 169 model.getSelectionModel().setSelectionInterval(start, end); 127 170 showHideLayerAction.actionPerformed(null); -
src/org/openstreetmap/josm/gui/layer/Layer.java
35 35 36 36 /** 37 37 * A layer encapsulates the gui component of one dataset and its representation. 38 * 38 * <p> 39 39 * Some layers may display data directly imported from OSM server. Other only 40 40 * display background images. Some can be edited, some not. Some are static and 41 41 * other changes dynamically (auto-updated). 42 * 42 * <p> 43 43 * Layers can be visible or not. Most actions the user can do applies only on 44 44 * selected layers. The available actions depend on the selected layers too. 45 * 45 * <p> 46 46 * All layers are managed by the MapView. They are displayed in a list to the 47 47 * right of the screen. 48 48 * … … 174 174 175 175 /** 176 176 * Initialization code, that depends on Main.map.mapView. 177 * 177 * <p> 178 178 * It is always called in the event dispatching thread. 179 179 * Note that Main.map is null as long as no layer has been added, so do 180 180 * not execute code in the constructor, that assumes Main.map.mapView is 181 181 * not null. 182 * 182 * <p> 183 183 * If you need to execute code when this layer is added to the map view, use 184 184 * {@link #attachToMapView(org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent)} 185 185 */ … … 269 269 * Returns list of actions. Action can implement LayerAction interface when it needs to be represented by other 270 270 * menu component than JMenuItem or when it supports multiple layers. Actions that support multiple layers should also 271 271 * have correct equals implementation. 272 * 272 * <p> 273 273 * Use {@link SeparatorLayerAction#INSTANCE} instead of new JSeparator 274 274 * @return menu actions for this layer 275 275 */ … … 277 277 278 278 /** 279 279 * Called, when the layer is removed from the mapview and is going to be destroyed. 280 * 281 * This is because the Layer constructor can not add itself safely aslistener280 * <p> 281 * This is because the Layer constructor cannot add itself safely as a listener 282 282 * to the layerlist dialog, because there may be no such dialog yet (loaded 283 283 * via command line parameter). 284 284 */ … … 302 302 303 303 /** 304 304 * Sets the associated file for this layer. 305 * 305 * <p> 306 306 * The associated file might be the one that the user opened. 307 307 * @param file The file, may be <code>null</code> 308 308 */ … … 353 353 } 354 354 355 355 /** 356 * Replies true if this layer was renamed by user356 * Replies true if user renamed this layer 357 357 * 358 * @return true if this layer was renamed by user358 * @return true if user renamed this layer 359 359 */ 360 360 public boolean isRenamed() { 361 361 return renamed; … … 483 483 } 484 484 485 485 /** 486 * fires a property change for the property {@link #FILTER_STATE_PROP}.486 * Fires a property change for the property {@link #FILTER_STATE_PROP}. 487 487 */ 488 488 protected void fireFilterStateChanged() { 489 489 propertyChangeSupport.firePropertyChange(FILTER_STATE_PROP, null, null); … … 490 490 } 491 491 492 492 /** 493 * allows to check whether a projection is supported or not493 * Allows to check whether a projection is supported or not. 494 494 * @param proj projection 495 *496 495 * @return True if projection is supported for this layer 497 496 */ 498 497 public boolean isProjectionSupported(Projection proj) { … … 609 608 } 610 609 611 610 /** 612 * Replies the savable state of this layer (i.e if it can be saved through a "File->Save" dialog).611 * Replies the savable state of this layer (i.e., if it can be saved through a "File->Save" dialog). 613 612 * @return true if this layer can be saved to a file 614 613 * @since 5459 615 614 */ … … 627 626 } 628 627 629 628 /** 630 * Creates a new "Save" dialog for this layer and makes it visible.<br> 631 * When the user has chosen a file, checks the file extension, and confirms overwrite if needed. 629 * Creates a new "Save" dialog for this layer and makes it visible. 630 * <p> 631 * When the user has chosen a file, checks the file extension, and confirms overwriting if needed. 632 632 * @return The output {@code File} 633 633 * @see SaveActionBase#createAndOpenSaveFileChooser 634 634 * @since 5459 … … 674 674 675 675 @Override 676 676 public String toString() { 677 return getClass().getSimpleName() + " [name=" + name + ", associatedFile=" + associatedFile + ']';677 return getClass().getSimpleName() + " [name=" + name + ", associatedFile=" + associatedFile + ", visible=" + visible + ']'; 678 678 } 679 679 } -
src/org/openstreetmap/josm/gui/layer/MainLayerManager.java
6 6 import java.awt.GraphicsEnvironment; 7 7 import java.util.ArrayList; 8 8 import java.util.Collection; 9 import java.util.Collections; 9 10 import java.util.List; 10 11 import java.util.ListIterator; 11 12 import java.util.concurrent.CopyOnWriteArrayList; … … 357 358 } 358 359 359 360 /** 360 * Determines the next active data layer according to the following 361 * rules: 362 * <ul> 363 * <li>if there is at least one {@link OsmDataLayer} the first one 364 * becomes active</li> 365 * <li>otherwise, the top most layer of any type becomes active</li> 366 * </ul> 361 * Determines the next active data layer. 362 * <p> 363 * The layer becomes active, which has the next highest index (closer to bottom) relative to {@code except} parameter 364 * in the following order: 365 * <ol> 366 * <li>{@link OsmDataLayer} and visible, or if there is none</li> 367 * <li>{@link OsmDataLayer} and hidden, or if there is none</li> 368 * <li>any type</li> 369 * </ol> 367 370 * 368 371 * @param except A layer to ignore. 369 372 * @return the next active data layer … … 370 373 */ 371 374 private Layer suggestNextActiveLayer(Layer except) { 372 375 List<Layer> layersList = new ArrayList<>(getLayers()); 373 layersList.remove(except); 374 // First look for data layer 375 for (Layer layer : layersList) { 376 377 // construct a new list with decreasing priority 378 int indexOfExcept = layersList.indexOf(except); 379 List<Layer> layersList2 = new ArrayList<>(layersList.subList(indexOfExcept, layersList.size())); 380 List<Layer> layersList3 = new ArrayList<>(layersList.subList(0, indexOfExcept)); 381 Collections.reverse(layersList3); 382 layersList2.addAll(layersList3); 383 layersList2.remove(except); 384 385 // First look for visible data layer 386 for (Layer layer : layersList2) { 387 if (layer instanceof OsmDataLayer && layer.isVisible()) { 388 return layer; 389 } 390 } 391 392 // Then any data layer 393 for (Layer layer : layersList2) { 376 394 if (layer instanceof OsmDataLayer) { 377 395 return layer; 378 396 } … … 379 397 } 380 398 381 399 // Then any layer 382 if (!layersList .isEmpty())383 return layersList .get(0);400 if (!layersList2.isEmpty()) 401 return layersList2.get(0); 384 402 385 403 // and then give up 386 404 return null;