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

Last change on this file since 13437 was 13437, checked in by Don-vip, 5 months ago

fix #15967 - proper loading of notes layers

File size: 18.6 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.awt.GraphicsEnvironment;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.List;
10import java.util.ListIterator;
11import java.util.concurrent.CopyOnWriteArrayList;
12
13import javax.swing.JOptionPane;
14
15import org.openstreetmap.josm.data.osm.DataSet;
16import org.openstreetmap.josm.gui.MainApplication;
17import org.openstreetmap.josm.gui.io.AsynchronousUploadPrimitivesTask;
18import org.openstreetmap.josm.gui.util.GuiHelper;
19import org.openstreetmap.josm.tools.Logging;
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
28 * @since 10279
29 */
30public class MainLayerManager extends LayerManager {
31    /**
32     * This listener listens to changes of the active or the edit layer.
33     * @author Michael Zangl
34     * @since 10600 (functional interface)
35     */
36    @FunctionalInterface
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         */
46        void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
47    }
48
49    /**
50     * This event is fired whenever the active or the data layer changes.
51     * @author Michael Zangl
52     */
53    public static class ActiveLayerChangeEvent extends LayerManagerEvent {
54
55        private final OsmDataLayer previousDataLayer;
56
57        private final Layer previousActiveLayer;
58
59        /**
60         * Create a new {@link ActiveLayerChangeEvent}
61         * @param source The source
62         * @param previousDataLayer the previous data layer
63         * @param previousActiveLayer the previous active layer
64         */
65        ActiveLayerChangeEvent(MainLayerManager source, OsmDataLayer previousDataLayer,
66                Layer previousActiveLayer) {
67            super(source);
68            this.previousDataLayer = previousDataLayer;
69            this.previousActiveLayer = previousActiveLayer;
70        }
71
72        /**
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}
76         */
77        @Deprecated
78        public OsmDataLayer getPreviousEditLayer() {
79            return getPreviousDataLayer();
80        }
81
82        /**
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        /**
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
99        /**
100         * Gets the data set that was previously used.
101         * @return The data set of {@link #getPreviousDataLayer()}.
102         * @deprecated use {@link #getPreviousDataSet}
103         */
104        @Deprecated
105        public DataSet getPreviousEditDataSet() {
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;
117            } else {
118                return null;
119            }
120        }
121
122        @Override
123        public MainLayerManager getSource() {
124            return (MainLayerManager) super.getSource();
125        }
126    }
127
128    /**
129     * This event is fired for {@link LayerAvailabilityListener}
130     * @author Michael Zangl
131     * @since 10508
132     */
133    public static class LayerAvailabilityEvent extends LayerManagerEvent {
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    /**
170     * The layer from the layers list that is currently active.
171     */
172    private Layer activeLayer;
173
174    /**
175     * The current active data layer. It might be editable or not, based on its read-only status.
176     */
177    private OsmDataLayer dataLayer;
178
179    private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
180    private final List<LayerAvailabilityListener> layerAvailabilityListeners = new CopyOnWriteArrayList<>();
181
182    /**
183     * Adds a active/edit layer change listener
184     *
185     * @param listener the listener.
186     */
187    public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener) {
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    /**
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    /**
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    /**
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    /**
238     * Set the active layer, unless the layer is being uploaded.
239     * If the layer is an OsmDataLayer, the edit layer is also changed.
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.
245        if (layer instanceof OsmDataLayer && ((OsmDataLayer) layer).isUploadInProgress()) {
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        }
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) {
264        ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
265        activeLayer = layer;
266        if (activeLayer instanceof OsmDataLayer) {
267            dataLayer = (OsmDataLayer) activeLayer;
268        } else if (forceEditLayerUpdate) {
269            dataLayer = null;
270        }
271        fireActiveLayerChange(event);
272    }
273
274    private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
275        GuiHelper.assertCallFromEdt();
276        if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousDataLayer() != dataLayer) {
277            for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
278                l.activeOrEditLayerChanged(event);
279            }
280        }
281    }
282
283    @Override
284    protected synchronized void realAddLayer(Layer layer, boolean initialZoom) {
285        if (getLayers().isEmpty()) {
286            LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true);
287            for (LayerAvailabilityListener l : layerAvailabilityListeners) {
288                l.beforeFirstLayerAdded(e);
289            }
290        }
291        super.realAddLayer(layer, initialZoom);
292
293        // update the active layer automatically.
294        if (layer instanceof OsmDataLayer || activeLayer == null) {
295            setActiveLayer(layer);
296        }
297    }
298
299    @Override
300    protected Collection<Layer> realRemoveSingleLayer(Layer layer) {
301        if ((layer instanceof OsmDataLayer) && (((OsmDataLayer) layer).isUploadInProgress())) {
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
309        if (layer == activeLayer || layer == dataLayer) {
310            Layer nextActive = suggestNextActiveLayer(layer);
311            setActiveLayer(nextActive, true);
312        }
313
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;
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() {
360        if (activeLayer instanceof OsmDataLayer) {
361            if (!((OsmDataLayer) activeLayer).isUploadInProgress()) {
362                return activeLayer;
363            } else {
364                return null;
365            }
366        } else {
367            return activeLayer;
368        }
369    }
370
371    /**
372     * Replies the current edit layer, if present and not readOnly
373     *
374     * @return the current edit layer. May be null.
375     * @see #getActiveDataLayer
376     */
377    public synchronized OsmDataLayer getEditLayer() {
378        if (dataLayer != null && !dataLayer.isReadOnly())
379            return dataLayer;
380        else
381            return null;
382    }
383
384    /**
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.
400     * @return That data set, <code>null</code> if there is no edit layer.
401     * @see #getActiveDataSet
402     */
403    public synchronized DataSet getEditDataSet() {
404        if (dataLayer != null && !dataLayer.isReadOnly()) {
405            return dataLayer.data;
406        } else {
407            return null;
408        }
409    }
410
411    /**
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    /**
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    /**
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    }
471
472    /**
473     * Invalidates current edit layer, if any. Does nothing if there is no edit layer.
474     * @since 13150
475     */
476    public void invalidateEditLayer() {
477        if (dataLayer != null) {
478            dataLayer.invalidate();
479        }
480    }
481
482    @Override
483    protected synchronized void realResetState() {
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();
488
489            activeLayerChangeListeners.clear();
490            layerAvailabilityListeners.clear();
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            }
497        }
498    }
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     */
508    public synchronized void prepareLayerForUpload(OsmDataLayer layer) {
509        GuiHelper.assertCallFromEdt();
510        layer.setUploadInProgress();
511        layer.setReadOnly();
512
513        // Reset only the edit layer as empty
514        if (dataLayer == layer) {
515            ActiveLayerChangeEvent activeLayerChangeEvent = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
516            dataLayer = null;
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     */
528    public synchronized void processLayerAfterUpload(OsmDataLayer layer) {
529        GuiHelper.assertCallFromEdt();
530        layer.unsetReadOnly();
531        layer.unsetUploadInProgress();
532
533        // Set the layer as edit layer if the edit layer is empty.
534        if (dataLayer == null) {
535            ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, dataLayer, activeLayer);
536            dataLayer = layer;
537            fireActiveLayerChange(layerChangeEvent);
538        }
539    }
540}
Note: See TracBrowser for help on using the repository browser.