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

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

see #11390 - sonar - squid:S1609 - Java 8: @FunctionalInterface annotation should be used to flag Single Abstract Method interfaces

File size: 13.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.List;
7import java.util.ListIterator;
8import java.util.concurrent.CopyOnWriteArrayList;
9
10import org.openstreetmap.josm.data.osm.DataSet;
11import org.openstreetmap.josm.gui.util.GuiHelper;
12
13/**
14 * This class extends the layer manager by adding an active and an edit layer.
15 * <p>
16 * The active layer is the layer the user is currently working on.
17 * <p>
18 * The edit layer is an data layer that we currently work with.
19 * @author Michael Zangl
20 * @since 10279
21 */
22public class MainLayerManager extends LayerManager {
23 /**
24 * This listener listens to changes of the active or the edit layer.
25 * @author Michael Zangl
26 * @since 10600 (functional interface)
27 */
28 @FunctionalInterface
29 public interface ActiveLayerChangeListener {
30 /**
31 * Called whenever the active or edit layer changed.
32 * <p>
33 * You can be sure that this layer is still contained in this set.
34 * <p>
35 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
36 * @param e The change event.
37 */
38 void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
39 }
40
41 /**
42 * This event is fired whenever the active or the edit layer changes.
43 * @author Michael Zangl
44 */
45 public class ActiveLayerChangeEvent extends LayerManagerEvent {
46
47 private final OsmDataLayer previousEditLayer;
48
49 private final Layer previousActiveLayer;
50
51 /**
52 * Create a new {@link ActiveLayerChangeEvent}
53 * @param source The source
54 * @param previousEditLayer the previous edit layer
55 * @param previousActiveLayer the previous active layer
56 */
57 ActiveLayerChangeEvent(MainLayerManager source, OsmDataLayer previousEditLayer,
58 Layer previousActiveLayer) {
59 super(source);
60 this.previousEditLayer = previousEditLayer;
61 this.previousActiveLayer = previousActiveLayer;
62 }
63
64 /**
65 * Gets the edit layer that was previously used.
66 * @return The old edit layer, <code>null</code> if there is none.
67 */
68 public OsmDataLayer getPreviousEditLayer() {
69 return previousEditLayer;
70 }
71
72 /**
73 * Gets the active layer that was previously used.
74 * @return The old active layer, <code>null</code> if there is none.
75 */
76 public Layer getPreviousActiveLayer() {
77 return previousActiveLayer;
78 }
79
80 /**
81 * Gets the data set that was previously used.
82 * @return The data set of {@link #getPreviousEditLayer()}.
83 */
84 public DataSet getPreviousEditDataSet() {
85 if (previousEditLayer != null) {
86 return previousEditLayer.data;
87 } else {
88 return null;
89 }
90 }
91
92 @Override
93 public MainLayerManager getSource() {
94 return (MainLayerManager) super.getSource();
95 }
96 }
97
98 /**
99 * This event is fired for {@link LayerAvailabilityListener}
100 * @author Michael Zangl
101 * @since 10508
102 */
103 public class LayerAvailabilityEvent extends LayerManagerEvent {
104 private final boolean hasLayers;
105
106 LayerAvailabilityEvent(LayerManager source, boolean hasLayers) {
107 super(source);
108 this.hasLayers = hasLayers;
109 }
110
111 /**
112 * Checks if this layer manager will have layers afterwards
113 * @return true if layers will be added.
114 */
115 public boolean hasLayers() {
116 return hasLayers;
117 }
118 }
119
120 /**
121 * A listener that gets informed before any layer is displayed and after all layers are removed.
122 * @author Michael Zangl
123 * @since 10508
124 */
125 public interface LayerAvailabilityListener {
126 /**
127 * This method is called in the UI thread right before the first layer is added.
128 * @param e The event.
129 */
130 void beforeFirstLayerAdded(LayerAvailabilityEvent e);
131
132 /**
133 * This method is called in the UI thread after the last layer was removed.
134 * @param e The event.
135 */
136 void afterLastLayerRemoved(LayerAvailabilityEvent e);
137 }
138
139 /**
140 * The layer from the layers list that is currently active.
141 */
142 private Layer activeLayer;
143
144 /**
145 * The edit layer is the current active data layer.
146 */
147 private OsmDataLayer editLayer;
148
149 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
150 private final List<LayerAvailabilityListener> layerAvailabilityListeners = new CopyOnWriteArrayList<>();
151
152 /**
153 * Adds a active/edit layer change listener
154 *
155 * @param listener the listener.
156 * @param initialFire use {@link #addAndFireActiveLayerChangeListener(ActiveLayerChangeListener)} instead.
157 * @deprecated Do not use the second parameter. To be removed in a few weeks.
158 */
159 @Deprecated
160 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) {
161 if (initialFire) {
162 addAndFireActiveLayerChangeListener(listener);
163 } else {
164 addActiveLayerChangeListener(listener);
165 }
166 }
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. If the layer is an OsmDataLayer, the edit layer is also changed.
225 * @param layer The active layer.
226 */
227 public void setActiveLayer(final Layer layer) {
228 // we force this on to the EDT Thread to make events fire from there.
229 // The synchronization lock needs to be held by the EDT.
230 GuiHelper.runInEDTAndWaitWithException(new Runnable() {
231 @Override
232 public void run() {
233 realSetActiveLayer(layer);
234 }
235 });
236 }
237
238 protected synchronized void realSetActiveLayer(final Layer layer) {
239 // to be called in EDT thread
240 checkContainsLayer(layer);
241 setActiveLayer(layer, false);
242 }
243
244 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
245 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
246 activeLayer = layer;
247 if (activeLayer instanceof OsmDataLayer) {
248 editLayer = (OsmDataLayer) activeLayer;
249 } else if (forceEditLayerUpdate) {
250 editLayer = null;
251 }
252 fireActiveLayerChange(event);
253 }
254
255 private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
256 GuiHelper.assertCallFromEdt();
257 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
258 for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
259 l.activeOrEditLayerChanged(event);
260 }
261 }
262 }
263
264 @Override
265 protected synchronized void realAddLayer(Layer layer) {
266 if (getLayers().isEmpty()) {
267 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true);
268 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
269 l.beforeFirstLayerAdded(e);
270 }
271 }
272 super.realAddLayer(layer);
273
274 // update the active layer automatically.
275 if (layer instanceof OsmDataLayer || activeLayer == null) {
276 setActiveLayer(layer);
277 }
278 }
279
280 @Override
281 protected Collection<Layer> realRemoveSingleLayer(Layer layer) {
282 if (layer == activeLayer || layer == editLayer) {
283 Layer nextActive = suggestNextActiveLayer(layer);
284 setActiveLayer(nextActive, true);
285 }
286
287 Collection<Layer> toDelete = super.realRemoveSingleLayer(layer);
288 if (getLayers().isEmpty()) {
289 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, false);
290 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
291 l.afterLastLayerRemoved(e);
292 }
293 }
294 return toDelete;
295 }
296
297 /**
298 * Determines the next active data layer according to the following
299 * rules:
300 * <ul>
301 * <li>if there is at least one {@link OsmDataLayer} the first one
302 * becomes active</li>
303 * <li>otherwise, the top most layer of any type becomes active</li>
304 * </ul>
305 *
306 * @param except A layer to ignore.
307 * @return the next active data layer
308 */
309 private Layer suggestNextActiveLayer(Layer except) {
310 List<Layer> layersList = new ArrayList<>(getLayers());
311 layersList.remove(except);
312 // First look for data layer
313 for (Layer layer : layersList) {
314 if (layer instanceof OsmDataLayer) {
315 return layer;
316 }
317 }
318
319 // Then any layer
320 if (!layersList.isEmpty())
321 return layersList.get(0);
322
323 // and then give up
324 return null;
325 }
326
327 /**
328 * Replies the currently active layer
329 *
330 * @return the currently active layer (may be null)
331 */
332 public synchronized Layer getActiveLayer() {
333 return activeLayer;
334 }
335
336 /**
337 * Replies the current edit layer, if any
338 *
339 * @return the current edit layer. May be null.
340 */
341 public synchronized OsmDataLayer getEditLayer() {
342 return editLayer;
343 }
344
345 /**
346 * Gets the data set of the active edit layer.
347 * @return That data set, <code>null</code> if there is no edit layer.
348 */
349 public synchronized DataSet getEditDataSet() {
350 if (editLayer != null) {
351 return editLayer.data;
352 } else {
353 return null;
354 }
355 }
356
357 /**
358 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
359 * first, layer with the highest Z-Order last.
360 * <p>
361 * The active data layer is pulled above all adjacent data layers.
362 *
363 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
364 * first, layer with the highest Z-Order last.
365 */
366 public synchronized List<Layer> getVisibleLayersInZOrder() {
367 List<Layer> ret = new ArrayList<>();
368 // This is set while we delay the addition of the active layer.
369 boolean activeLayerDelayed = false;
370 List<Layer> layers = getLayers();
371 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
372 Layer l = iterator.previous();
373 if (!l.isVisible()) {
374 // ignored
375 } else if (l == activeLayer && l instanceof OsmDataLayer) {
376 // delay and add after the current block of OsmDataLayer
377 activeLayerDelayed = true;
378 } else {
379 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
380 // add active layer before the current one.
381 ret.add(activeLayer);
382 activeLayerDelayed = false;
383 }
384 // Add this layer now
385 ret.add(l);
386 }
387 }
388 if (activeLayerDelayed) {
389 ret.add(activeLayer);
390 }
391 return ret;
392 }
393
394 @Override
395 public void resetState() {
396 // active and edit layer are unset automatically
397 super.resetState();
398
399 activeLayerChangeListeners.clear();
400 layerAvailabilityListeners.clear();
401 }
402}
Note: See TracBrowser for help on using the repository browser.