source: josm/trunk/src/org/openstreetmap/josm/gui/layer/MainLayerManager.java@ 13453

Last change on this file since 13453 was 13453, checked in by Don-vip, 6 years ago

fix #8039, fix #10456: final fixes for the read-only/locked layers:

  • rename "read-only" to "locked" (in XML and Java classes/interfaces)
  • add a new download policy (true/never) to allow private layers forbidding only to download data, but allowing everything else

This leads to:

  • normal layers: download allowed, modifications allowed, upload allowed
  • private layers: download allowed or not (download=true/never), modifications allowed, upload allowed or not (upload=true/discouraged/never)
  • locked layers: nothing allowed, the data cannot be modified in any way
File size: 18.6 KB
RevLine 
[10271]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
[13133]4import static org.openstreetmap.josm.tools.I18n.tr;
5
[13215]6import java.awt.GraphicsEnvironment;
[10271]7import java.util.ArrayList;
[10507]8import java.util.Collection;
[10271]9import java.util.List;
10import java.util.ListIterator;
11import java.util.concurrent.CopyOnWriteArrayList;
12
[13133]13import javax.swing.JOptionPane;
14
[10271]15import org.openstreetmap.josm.data.osm.DataSet;
[13133]16import org.openstreetmap.josm.gui.MainApplication;
[13213]17import org.openstreetmap.josm.gui.io.AsynchronousUploadPrimitivesTask;
[10271]18import org.openstreetmap.josm.gui.util.GuiHelper;
[13219]19import org.openstreetmap.josm.tools.Logging;
[10271]20
21/**
22 * This class extends the layer manager by adding an active and an edit layer.
23 * <p>
24 * The active layer is the layer the user is currently working on.
25 * <p>
26 * The edit layer is an data layer that we currently work with.
27 * @author Michael Zangl
[10279]28 * @since 10279
[10271]29 */
[10279]30public class MainLayerManager extends LayerManager {
[10271]31 /**
32 * This listener listens to changes of the active or the edit layer.
33 * @author Michael Zangl
[10600]34 * @since 10600 (functional interface)
[10271]35 */
[10600]36 @FunctionalInterface
[10271]37 public interface ActiveLayerChangeListener {
38 /**
39 * Called whenever the active or edit layer changed.
40 * <p>
41 * You can be sure that this layer is still contained in this set.
42 * <p>
43 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
44 * @param e The change event.
45 */
[10277]46 void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
[10271]47 }
48
49 /**
[13434]50 * This event is fired whenever the active or the data layer changes.
[10271]51 * @author Michael Zangl
52 */
[10653]53 public static class ActiveLayerChangeEvent extends LayerManagerEvent {
[10271]54
[13434]55 private final OsmDataLayer previousDataLayer;
[10271]56
57 private final Layer previousActiveLayer;
58
59 /**
60 * Create a new {@link ActiveLayerChangeEvent}
61 * @param source The source
[13434]62 * @param previousDataLayer the previous data layer
[10271]63 * @param previousActiveLayer the previous active layer
64 */
[13434]65 ActiveLayerChangeEvent(MainLayerManager source, OsmDataLayer previousDataLayer,
[10271]66 Layer previousActiveLayer) {
67 super(source);
[13434]68 this.previousDataLayer = previousDataLayer;
[10271]69 this.previousActiveLayer = previousActiveLayer;
70 }
71
72 /**
[13434]73 * Gets the data layer that was previously used.
74 * @return The old data layer, <code>null</code> if there is none.
75 * @deprecated use {@link #getPreviousDataLayer}
[10271]76 */
[13434]77 @Deprecated
[10271]78 public OsmDataLayer getPreviousEditLayer() {
[13434]79 return getPreviousDataLayer();
[10271]80 }
81
82 /**
[13434]83 * Gets the data layer that was previously used.
84 * @return The old data layer, <code>null</code> if there is none.
85 * @since 13434
86 */
87 public OsmDataLayer getPreviousDataLayer() {
88 return previousDataLayer;
89 }
90
91 /**
[10271]92 * Gets the active layer that was previously used.
93 * @return The old active layer, <code>null</code> if there is none.
94 */
95 public Layer getPreviousActiveLayer() {
96 return previousActiveLayer;
97 }
98
[10332]99 /**
100 * Gets the data set that was previously used.
[13434]101 * @return The data set of {@link #getPreviousDataLayer()}.
102 * @deprecated use {@link #getPreviousDataSet}
[10332]103 */
[13434]104 @Deprecated
[10332]105 public DataSet getPreviousEditDataSet() {
[13434]106 return getPreviousDataSet();
107 }
108
109 /**
110 * Gets the data set that was previously used.
111 * @return The data set of {@link #getPreviousDataLayer()}.
112 * @since 13434
113 */
114 public DataSet getPreviousDataSet() {
115 if (previousDataLayer != null) {
116 return previousDataLayer.data;
[10332]117 } else {
118 return null;
119 }
120 }
121
[10271]122 @Override
[10279]123 public MainLayerManager getSource() {
124 return (MainLayerManager) super.getSource();
[10271]125 }
126 }
127
128 /**
[10508]129 * This event is fired for {@link LayerAvailabilityListener}
130 * @author Michael Zangl
131 * @since 10508
132 */
[10653]133 public static class LayerAvailabilityEvent extends LayerManagerEvent {
[10508]134 private final boolean hasLayers;
135
136 LayerAvailabilityEvent(LayerManager source, boolean hasLayers) {
137 super(source);
138 this.hasLayers = hasLayers;
139 }
140
141 /**
142 * Checks if this layer manager will have layers afterwards
143 * @return true if layers will be added.
144 */
145 public boolean hasLayers() {
146 return hasLayers;
147 }
148 }
149
150 /**
151 * A listener that gets informed before any layer is displayed and after all layers are removed.
152 * @author Michael Zangl
153 * @since 10508
154 */
155 public interface LayerAvailabilityListener {
156 /**
157 * This method is called in the UI thread right before the first layer is added.
158 * @param e The event.
159 */
160 void beforeFirstLayerAdded(LayerAvailabilityEvent e);
161
162 /**
163 * This method is called in the UI thread after the last layer was removed.
164 * @param e The event.
165 */
166 void afterLastLayerRemoved(LayerAvailabilityEvent e);
167 }
168
169 /**
[10271]170 * The layer from the layers list that is currently active.
171 */
172 private Layer activeLayer;
173
174 /**
[13434]175 * The current active data layer. It might be editable or not, based on its read-only status.
[10271]176 */
[13434]177 private OsmDataLayer dataLayer;
[10271]178
179 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
[10508]180 private final List<LayerAvailabilityListener> layerAvailabilityListeners = new CopyOnWriteArrayList<>();
[10271]181
182 /**
183 * Adds a active/edit layer change listener
184 *
185 * @param listener the listener.
186 */
[10332]187 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener) {
[10271]188 if (activeLayerChangeListeners.contains(listener)) {
189 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
190 }
191 activeLayerChangeListeners.add(listener);
192 }
193
194 /**
[10332]195 * Adds a active/edit layer change listener. Fire a fake active-layer-changed-event right after adding
196 * the listener. The previous layers will be null. The listener is notified in the current thread.
197 * @param listener the listener.
198 */
199 public synchronized void addAndFireActiveLayerChangeListener(ActiveLayerChangeListener listener) {
200 addActiveLayerChangeListener(listener);
201 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
202 }
203
204 /**
[10271]205 * Removes an active/edit layer change listener.
206 * @param listener the listener.
207 */
208 public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
209 if (!activeLayerChangeListeners.contains(listener)) {
210 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
211 }
212 activeLayerChangeListeners.remove(listener);
213 }
214
215 /**
[10508]216 * Add a new {@link LayerAvailabilityListener}.
217 * @param listener The listener
218 * @since 10508
219 */
220 public synchronized void addLayerAvailabilityListener(LayerAvailabilityListener listener) {
221 if (!layerAvailabilityListeners.add(listener)) {
222 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
223 }
224 }
225
226 /**
227 * Remove an {@link LayerAvailabilityListener}.
228 * @param listener The listener
229 * @since 10508
230 */
231 public synchronized void removeLayerAvailabilityListener(LayerAvailabilityListener listener) {
232 if (!layerAvailabilityListeners.remove(listener)) {
233 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
234 }
235 }
236
237 /**
[13434]238 * Set the active layer, unless the layer is being uploaded.
[13133]239 * If the layer is an OsmDataLayer, the edit layer is also changed.
[10271]240 * @param layer The active layer.
241 */
242 public void setActiveLayer(final Layer layer) {
243 // we force this on to the EDT Thread to make events fire from there.
244 // The synchronization lock needs to be held by the EDT.
[13434]245 if (layer instanceof OsmDataLayer && ((OsmDataLayer) layer).isUploadInProgress()) {
[13133]246 GuiHelper.runInEDT(() ->
247 JOptionPane.showMessageDialog(
248 MainApplication.parent,
249 tr("Trying to set a read only data layer as edit layer"),
250 tr("Warning"),
251 JOptionPane.WARNING_MESSAGE));
252 } else {
253 GuiHelper.runInEDTAndWaitWithException(() -> realSetActiveLayer(layer));
254 }
[10271]255 }
256
257 protected synchronized void realSetActiveLayer(final Layer layer) {
258 // to be called in EDT thread
259 checkContainsLayer(layer);
260 setActiveLayer(layer, false);
261 }
262
263 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
[13434]264 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
[10271]265 activeLayer = layer;
266 if (activeLayer instanceof OsmDataLayer) {
[13434]267 dataLayer = (OsmDataLayer) activeLayer;
[10271]268 } else if (forceEditLayerUpdate) {
[13434]269 dataLayer = null;
[10271]270 }
271 fireActiveLayerChange(event);
272 }
273
274 private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
275 GuiHelper.assertCallFromEdt();
[13434]276 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousDataLayer() != dataLayer) {
[10271]277 for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
278 l.activeOrEditLayerChanged(event);
279 }
280 }
281 }
282
283 @Override
[11774]284 protected synchronized void realAddLayer(Layer layer, boolean initialZoom) {
[10508]285 if (getLayers().isEmpty()) {
286 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true);
287 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
288 l.beforeFirstLayerAdded(e);
289 }
290 }
[11774]291 super.realAddLayer(layer, initialZoom);
[10271]292
293 // update the active layer automatically.
294 if (layer instanceof OsmDataLayer || activeLayer == null) {
295 setActiveLayer(layer);
296 }
297 }
298
299 @Override
[10507]300 protected Collection<Layer> realRemoveSingleLayer(Layer layer) {
[13434]301 if ((layer instanceof OsmDataLayer) && (((OsmDataLayer) layer).isUploadInProgress())) {
[13213]302 GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(MainApplication.parent,
303 tr("Trying to delete the layer with background upload. Please wait until the upload is finished.")));
304
305 // Return an empty collection for allowing to delete other layers
306 return new ArrayList<>();
307 }
308
[13434]309 if (layer == activeLayer || layer == dataLayer) {
[10271]310 Layer nextActive = suggestNextActiveLayer(layer);
311 setActiveLayer(nextActive, true);
312 }
313
[10508]314 Collection<Layer> toDelete = super.realRemoveSingleLayer(layer);
315 if (getLayers().isEmpty()) {
316 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, false);
317 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
318 l.afterLastLayerRemoved(e);
319 }
320 }
321 return toDelete;
[10271]322 }
323
324 /**
325 * Determines the next active data layer according to the following
326 * rules:
327 * <ul>
328 * <li>if there is at least one {@link OsmDataLayer} the first one
329 * becomes active</li>
330 * <li>otherwise, the top most layer of any type becomes active</li>
331 * </ul>
332 *
333 * @param except A layer to ignore.
334 * @return the next active data layer
335 */
336 private Layer suggestNextActiveLayer(Layer except) {
337 List<Layer> layersList = new ArrayList<>(getLayers());
338 layersList.remove(except);
339 // First look for data layer
340 for (Layer layer : layersList) {
341 if (layer instanceof OsmDataLayer) {
342 return layer;
343 }
344 }
345
346 // Then any layer
347 if (!layersList.isEmpty())
348 return layersList.get(0);
349
350 // and then give up
351 return null;
352 }
353
354 /**
355 * Replies the currently active layer
356 *
357 * @return the currently active layer (may be null)
358 */
359 public synchronized Layer getActiveLayer() {
[13133]360 if (activeLayer instanceof OsmDataLayer) {
[13434]361 if (!((OsmDataLayer) activeLayer).isUploadInProgress()) {
[13133]362 return activeLayer;
363 } else {
364 return null;
365 }
366 } else {
367 return activeLayer;
368 }
[10271]369 }
370
371 /**
[13133]372 * Replies the current edit layer, if present and not readOnly
[10271]373 *
374 * @return the current edit layer. May be null.
[13434]375 * @see #getActiveDataLayer
[10271]376 */
377 public synchronized OsmDataLayer getEditLayer() {
[13453]378 if (dataLayer != null && !dataLayer.isLocked())
[13434]379 return dataLayer;
[13133]380 else
381 return null;
[10271]382 }
383
384 /**
[13434]385 * Replies the active data layer. The layer can be read-only.
386 *
387 * @return the current data layer. May be null or read-only.
388 * @see #getEditLayer
389 * @since 13434
390 */
391 public synchronized OsmDataLayer getActiveDataLayer() {
392 if (dataLayer != null)
393 return dataLayer;
394 else
395 return null;
396 }
397
398 /**
399 * Gets the data set of the active edit layer, if not readOnly.
[10271]400 * @return That data set, <code>null</code> if there is no edit layer.
[13434]401 * @see #getActiveDataSet
[10271]402 */
403 public synchronized DataSet getEditDataSet() {
[13453]404 if (dataLayer != null && !dataLayer.isLocked()) {
[13434]405 return dataLayer.data;
[10271]406 } else {
407 return null;
408 }
409 }
410
411 /**
[13434]412 * Gets the data set of the active data layer. The dataset can be read-only.
413 * @return That data set, <code>null</code> if there is no active data layer.
414 * @see #getEditDataSet
415 * @since 13434
416 */
417 public synchronized DataSet getActiveDataSet() {
418 if (dataLayer != null) {
419 return dataLayer.data;
420 } else {
421 return null;
422 }
423 }
424
425 /**
[13437]426 * Returns the unique note layer, if present.
427 * @return the unique note layer, or null
428 * @since 13437
429 */
430 public NoteLayer getNoteLayer() {
431 List<NoteLayer> col = getLayersOfType(NoteLayer.class);
432 return col.isEmpty() ? null : col.get(0);
433 }
434
435 /**
[10271]436 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
437 * first, layer with the highest Z-Order last.
438 * <p>
439 * The active data layer is pulled above all adjacent data layers.
440 *
441 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
442 * first, layer with the highest Z-Order last.
443 */
444 public synchronized List<Layer> getVisibleLayersInZOrder() {
445 List<Layer> ret = new ArrayList<>();
446 // This is set while we delay the addition of the active layer.
447 boolean activeLayerDelayed = false;
448 List<Layer> layers = getLayers();
449 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
450 Layer l = iterator.previous();
451 if (!l.isVisible()) {
452 // ignored
453 } else if (l == activeLayer && l instanceof OsmDataLayer) {
454 // delay and add after the current block of OsmDataLayer
455 activeLayerDelayed = true;
456 } else {
457 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
458 // add active layer before the current one.
459 ret.add(activeLayer);
460 activeLayerDelayed = false;
461 }
462 // Add this layer now
463 ret.add(l);
464 }
465 }
466 if (activeLayerDelayed) {
467 ret.add(activeLayer);
468 }
469 return ret;
470 }
[10432]471
[13150]472 /**
[13152]473 * Invalidates current edit layer, if any. Does nothing if there is no edit layer.
[13150]474 * @since 13150
475 */
476 public void invalidateEditLayer() {
[13434]477 if (dataLayer != null) {
478 dataLayer.invalidate();
[13150]479 }
480 }
481
[10432]482 @Override
[10999]483 protected synchronized void realResetState() {
[13213]484 // Reset state if no asynchronous upload is under progress
485 if (!AsynchronousUploadPrimitivesTask.getCurrentAsynchronousUploadTask().isPresent()) {
486 // active and edit layer are unset automatically
487 super.realResetState();
[10432]488
[13213]489 activeLayerChangeListeners.clear();
490 layerAvailabilityListeners.clear();
[13219]491 } else {
492 String msg = tr("A background upload is already in progress. Cannot reset state until the upload is finished.");
493 Logging.warn(msg);
494 if (!GraphicsEnvironment.isHeadless()) {
495 GuiHelper.runInEDT(() -> JOptionPane.showMessageDialog(MainApplication.parent, msg));
496 }
[13213]497 }
[10432]498 }
[13133]499
500 /**
501 * Prepares an OsmDataLayer for upload. The layer to be uploaded is locked and
502 * if the layer to be uploaded is the current editLayer then editLayer is reset
503 * to null for disallowing any changes to the layer. An ActiveLayerChangeEvent
504 * is fired to notify the listeners
505 *
506 * @param layer The OsmDataLayer to be uploaded
507 */
[13435]508 public synchronized void prepareLayerForUpload(OsmDataLayer layer) {
[13133]509 GuiHelper.assertCallFromEdt();
[13434]510 layer.setUploadInProgress();
[13453]511 layer.lock();
[13133]512
513 // Reset only the edit layer as empty
[13434]514 if (dataLayer == layer) {
515 ActiveLayerChangeEvent activeLayerChangeEvent = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
516 dataLayer = null;
[13133]517 fireActiveLayerChange(activeLayerChangeEvent);
518 }
519 }
520
521 /**
522 * Post upload processing of the OsmDataLayer.
523 * If the current edit layer is empty this function sets the layer uploaded as the
524 * current editLayer. An ActiveLayerChangeEvent is fired to notify the listeners
525 *
526 * @param layer The OsmDataLayer uploaded
527 */
[13435]528 public synchronized void processLayerAfterUpload(OsmDataLayer layer) {
[13133]529 GuiHelper.assertCallFromEdt();
[13453]530 layer.unlock();
[13434]531 layer.unsetUploadInProgress();
[13133]532
533 // Set the layer as edit layer if the edit layer is empty.
[13434]534 if (dataLayer == null) {
535 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
536 dataLayer = layer;
[13133]537 fireActiveLayerChange(layerChangeEvent);
538 }
539 }
[10271]540}
Note: See TracBrowser for help on using the repository browser.