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

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

fix #15653, see #8509 - IllegalStateException during Upload (regression from r13133 - patch by udit, minor changes)

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