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

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

remove deprecated API

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