001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import java.util.ListIterator; 008import java.util.concurrent.CopyOnWriteArrayList; 009 010import org.openstreetmap.josm.data.osm.DataSet; 011import org.openstreetmap.josm.gui.util.GuiHelper; 012 013/** 014 * This class extends the layer manager by adding an active and an edit layer. 015 * <p> 016 * The active layer is the layer the user is currently working on. 017 * <p> 018 * The edit layer is an data layer that we currently work with. 019 * @author Michael Zangl 020 * @since 10279 021 */ 022public class MainLayerManager extends LayerManager { 023 /** 024 * This listener listens to changes of the active or the edit layer. 025 * @author Michael Zangl 026 * 027 */ 028 public interface ActiveLayerChangeListener { 029 /** 030 * Called whenever the active or edit layer changed. 031 * <p> 032 * You can be sure that this layer is still contained in this set. 033 * <p> 034 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread. 035 * @param e The change event. 036 */ 037 void activeOrEditLayerChanged(ActiveLayerChangeEvent e); 038 } 039 040 /** 041 * This event is fired whenever the active or the edit layer changes. 042 * @author Michael Zangl 043 */ 044 public class ActiveLayerChangeEvent extends LayerManagerEvent { 045 046 private final OsmDataLayer previousEditLayer; 047 048 private final Layer previousActiveLayer; 049 050 /** 051 * Create a new {@link ActiveLayerChangeEvent} 052 * @param source The source 053 * @param previousEditLayer the previous edit layer 054 * @param previousActiveLayer the previous active layer 055 */ 056 ActiveLayerChangeEvent(MainLayerManager source, OsmDataLayer previousEditLayer, 057 Layer previousActiveLayer) { 058 super(source); 059 this.previousEditLayer = previousEditLayer; 060 this.previousActiveLayer = previousActiveLayer; 061 } 062 063 /** 064 * Gets the edit layer that was previously used. 065 * @return The old edit layer, <code>null</code> if there is none. 066 */ 067 public OsmDataLayer getPreviousEditLayer() { 068 return previousEditLayer; 069 } 070 071 /** 072 * Gets the active layer that was previously used. 073 * @return The old active layer, <code>null</code> if there is none. 074 */ 075 public Layer getPreviousActiveLayer() { 076 return previousActiveLayer; 077 } 078 079 /** 080 * Gets the data set that was previously used. 081 * @return The data set of {@link #getPreviousEditLayer()}. 082 */ 083 public DataSet getPreviousEditDataSet() { 084 if (previousEditLayer != null) { 085 return previousEditLayer.data; 086 } else { 087 return null; 088 } 089 } 090 091 @Override 092 public MainLayerManager getSource() { 093 return (MainLayerManager) super.getSource(); 094 } 095 } 096 097 /** 098 * This event is fired for {@link LayerAvailabilityListener} 099 * @author Michael Zangl 100 * @since 10508 101 */ 102 public class LayerAvailabilityEvent extends LayerManagerEvent { 103 private final boolean hasLayers; 104 105 LayerAvailabilityEvent(LayerManager source, boolean hasLayers) { 106 super(source); 107 this.hasLayers = hasLayers; 108 } 109 110 /** 111 * Checks if this layer manager will have layers afterwards 112 * @return true if layers will be added. 113 */ 114 public boolean hasLayers() { 115 return hasLayers; 116 } 117 } 118 119 /** 120 * A listener that gets informed before any layer is displayed and after all layers are removed. 121 * @author Michael Zangl 122 * @since 10508 123 */ 124 public interface LayerAvailabilityListener { 125 /** 126 * This method is called in the UI thread right before the first layer is added. 127 * @param e The event. 128 */ 129 void beforeFirstLayerAdded(LayerAvailabilityEvent e); 130 131 /** 132 * This method is called in the UI thread after the last layer was removed. 133 * @param e The event. 134 */ 135 void afterLastLayerRemoved(LayerAvailabilityEvent e); 136 } 137 138 /** 139 * The layer from the layers list that is currently active. 140 */ 141 private Layer activeLayer; 142 143 /** 144 * The edit layer is the current active data layer. 145 */ 146 private OsmDataLayer editLayer; 147 148 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>(); 149 private final List<LayerAvailabilityListener> layerAvailabilityListeners = new CopyOnWriteArrayList<>(); 150 151 /** 152 * Adds a active/edit layer change listener 153 * 154 * @param listener the listener. 155 * @param initialFire use {@link #addAndFireActiveLayerChangeListener(ActiveLayerChangeListener)} instead. 156 * @deprecated Do not use the second parameter. To be removed in a few weeks. 157 */ 158 @Deprecated 159 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) { 160 if (initialFire) { 161 addAndFireActiveLayerChangeListener(listener); 162 } else { 163 addActiveLayerChangeListener(listener); 164 } 165 } 166 167 /** 168 * Adds a active/edit layer change listener 169 * 170 * @param listener the listener. 171 */ 172 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener) { 173 if (activeLayerChangeListeners.contains(listener)) { 174 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener); 175 } 176 activeLayerChangeListeners.add(listener); 177 } 178 179 /** 180 * Adds a active/edit layer change listener. Fire a fake active-layer-changed-event right after adding 181 * the listener. The previous layers will be null. The listener is notified in the current thread. 182 * @param listener the listener. 183 */ 184 public synchronized void addAndFireActiveLayerChangeListener(ActiveLayerChangeListener listener) { 185 addActiveLayerChangeListener(listener); 186 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null)); 187 } 188 189 /** 190 * Removes an active/edit layer change listener. 191 * @param listener the listener. 192 */ 193 public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) { 194 if (!activeLayerChangeListeners.contains(listener)) { 195 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener); 196 } 197 activeLayerChangeListeners.remove(listener); 198 } 199 200 /** 201 * Add a new {@link LayerAvailabilityListener}. 202 * @param listener The listener 203 * @since 10508 204 */ 205 public synchronized void addLayerAvailabilityListener(LayerAvailabilityListener listener) { 206 if (!layerAvailabilityListeners.add(listener)) { 207 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener); 208 } 209 } 210 211 /** 212 * Remove an {@link LayerAvailabilityListener}. 213 * @param listener The listener 214 * @since 10508 215 */ 216 public synchronized void removeLayerAvailabilityListener(LayerAvailabilityListener listener) { 217 if (!layerAvailabilityListeners.remove(listener)) { 218 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener); 219 } 220 } 221 222 /** 223 * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed. 224 * @param layer The active layer. 225 */ 226 public void setActiveLayer(final Layer layer) { 227 // we force this on to the EDT Thread to make events fire from there. 228 // The synchronization lock needs to be held by the EDT. 229 GuiHelper.runInEDTAndWaitWithException(new Runnable() { 230 @Override 231 public void run() { 232 realSetActiveLayer(layer); 233 } 234 }); 235 } 236 237 protected synchronized void realSetActiveLayer(final Layer layer) { 238 // to be called in EDT thread 239 checkContainsLayer(layer); 240 setActiveLayer(layer, false); 241 } 242 243 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) { 244 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer); 245 activeLayer = layer; 246 if (activeLayer instanceof OsmDataLayer) { 247 editLayer = (OsmDataLayer) activeLayer; 248 } else if (forceEditLayerUpdate) { 249 editLayer = null; 250 } 251 fireActiveLayerChange(event); 252 } 253 254 private void fireActiveLayerChange(ActiveLayerChangeEvent event) { 255 GuiHelper.assertCallFromEdt(); 256 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) { 257 for (ActiveLayerChangeListener l : activeLayerChangeListeners) { 258 l.activeOrEditLayerChanged(event); 259 } 260 } 261 } 262 263 @Override 264 protected synchronized void realAddLayer(Layer layer) { 265 if (getLayers().isEmpty()) { 266 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true); 267 for (LayerAvailabilityListener l : layerAvailabilityListeners) { 268 l.beforeFirstLayerAdded(e); 269 } 270 } 271 super.realAddLayer(layer); 272 273 // update the active layer automatically. 274 if (layer instanceof OsmDataLayer || activeLayer == null) { 275 setActiveLayer(layer); 276 } 277 } 278 279 @Override 280 protected Collection<Layer> realRemoveSingleLayer(Layer layer) { 281 if (layer == activeLayer || layer == editLayer) { 282 Layer nextActive = suggestNextActiveLayer(layer); 283 setActiveLayer(nextActive, true); 284 } 285 286 Collection<Layer> toDelete = super.realRemoveSingleLayer(layer); 287 if (getLayers().isEmpty()) { 288 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, false); 289 for (LayerAvailabilityListener l : layerAvailabilityListeners) { 290 l.afterLastLayerRemoved(e); 291 } 292 } 293 return toDelete; 294 } 295 296 /** 297 * Determines the next active data layer according to the following 298 * rules: 299 * <ul> 300 * <li>if there is at least one {@link OsmDataLayer} the first one 301 * becomes active</li> 302 * <li>otherwise, the top most layer of any type becomes active</li> 303 * </ul> 304 * 305 * @param except A layer to ignore. 306 * @return the next active data layer 307 */ 308 private Layer suggestNextActiveLayer(Layer except) { 309 List<Layer> layersList = new ArrayList<>(getLayers()); 310 layersList.remove(except); 311 // First look for data layer 312 for (Layer layer : layersList) { 313 if (layer instanceof OsmDataLayer) { 314 return layer; 315 } 316 } 317 318 // Then any layer 319 if (!layersList.isEmpty()) 320 return layersList.get(0); 321 322 // and then give up 323 return null; 324 } 325 326 /** 327 * Replies the currently active layer 328 * 329 * @return the currently active layer (may be null) 330 */ 331 public synchronized Layer getActiveLayer() { 332 return activeLayer; 333 } 334 335 /** 336 * Replies the current edit layer, if any 337 * 338 * @return the current edit layer. May be null. 339 */ 340 public synchronized OsmDataLayer getEditLayer() { 341 return editLayer; 342 } 343 344 /** 345 * Gets the data set of the active edit layer. 346 * @return That data set, <code>null</code> if there is no edit layer. 347 */ 348 public synchronized DataSet getEditDataSet() { 349 if (editLayer != null) { 350 return editLayer.data; 351 } else { 352 return null; 353 } 354 } 355 356 /** 357 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order 358 * first, layer with the highest Z-Order last. 359 * <p> 360 * The active data layer is pulled above all adjacent data layers. 361 * 362 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order 363 * first, layer with the highest Z-Order last. 364 */ 365 public synchronized List<Layer> getVisibleLayersInZOrder() { 366 List<Layer> ret = new ArrayList<>(); 367 // This is set while we delay the addition of the active layer. 368 boolean activeLayerDelayed = false; 369 List<Layer> layers = getLayers(); 370 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) { 371 Layer l = iterator.previous(); 372 if (!l.isVisible()) { 373 // ignored 374 } else if (l == activeLayer && l instanceof OsmDataLayer) { 375 // delay and add after the current block of OsmDataLayer 376 activeLayerDelayed = true; 377 } else { 378 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) { 379 // add active layer before the current one. 380 ret.add(activeLayer); 381 activeLayerDelayed = false; 382 } 383 // Add this layer now 384 ret.add(l); 385 } 386 } 387 if (activeLayerDelayed) { 388 ret.add(activeLayer); 389 } 390 return ret; 391 } 392 393 @Override 394 public void resetState() { 395 // active and edit layer are unset automatically 396 super.resetState(); 397 398 activeLayerChangeListeners.clear(); 399 layerAvailabilityListeners.clear(); 400 } 401}