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

Last change on this file since 10997 was 10997, checked in by michael2402, 8 years ago

Make layer read access synchronized.

File size: 12.8 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 static 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 static 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 */
157 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener) {
158 if (activeLayerChangeListeners.contains(listener)) {
159 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
160 }
161 activeLayerChangeListeners.add(listener);
162 }
163
164 /**
165 * Adds a active/edit layer change listener. Fire a fake active-layer-changed-event right after adding
166 * the listener. The previous layers will be null. The listener is notified in the current thread.
167 * @param listener the listener.
168 */
169 public synchronized void addAndFireActiveLayerChangeListener(ActiveLayerChangeListener listener) {
170 addActiveLayerChangeListener(listener);
171 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
172 }
173
174 /**
175 * Removes an active/edit layer change listener.
176 * @param listener the listener.
177 */
178 public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
179 if (!activeLayerChangeListeners.contains(listener)) {
180 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
181 }
182 activeLayerChangeListeners.remove(listener);
183 }
184
185 /**
186 * Add a new {@link LayerAvailabilityListener}.
187 * @param listener The listener
188 * @since 10508
189 */
190 public synchronized void addLayerAvailabilityListener(LayerAvailabilityListener listener) {
191 if (!layerAvailabilityListeners.add(listener)) {
192 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
193 }
194 }
195
196 /**
197 * Remove an {@link LayerAvailabilityListener}.
198 * @param listener The listener
199 * @since 10508
200 */
201 public synchronized void removeLayerAvailabilityListener(LayerAvailabilityListener listener) {
202 if (!layerAvailabilityListeners.remove(listener)) {
203 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
204 }
205 }
206
207 /**
208 * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed.
209 * @param layer The active layer.
210 */
211 public void setActiveLayer(final Layer layer) {
212 // we force this on to the EDT Thread to make events fire from there.
213 // The synchronization lock needs to be held by the EDT.
214 GuiHelper.runInEDTAndWaitWithException(() -> realSetActiveLayer(layer));
215 }
216
217 protected synchronized void realSetActiveLayer(final Layer layer) {
218 // to be called in EDT thread
219 checkContainsLayer(layer);
220 setActiveLayer(layer, false);
221 }
222
223 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
224 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
225 activeLayer = layer;
226 if (activeLayer instanceof OsmDataLayer) {
227 editLayer = (OsmDataLayer) activeLayer;
228 } else if (forceEditLayerUpdate) {
229 editLayer = null;
230 }
231 fireActiveLayerChange(event);
232 }
233
234 private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
235 GuiHelper.assertCallFromEdt();
236 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
237 for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
238 l.activeOrEditLayerChanged(event);
239 }
240 }
241 }
242
243 @Override
244 protected synchronized void realAddLayer(Layer layer) {
245 if (getLayers().isEmpty()) {
246 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true);
247 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
248 l.beforeFirstLayerAdded(e);
249 }
250 }
251 super.realAddLayer(layer);
252
253 // update the active layer automatically.
254 if (layer instanceof OsmDataLayer || activeLayer == null) {
255 setActiveLayer(layer);
256 }
257 }
258
259 @Override
260 protected Collection<Layer> realRemoveSingleLayer(Layer layer) {
261 if (layer == activeLayer || layer == editLayer) {
262 Layer nextActive = suggestNextActiveLayer(layer);
263 setActiveLayer(nextActive, true);
264 }
265
266 Collection<Layer> toDelete = super.realRemoveSingleLayer(layer);
267 if (getLayers().isEmpty()) {
268 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, false);
269 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
270 l.afterLastLayerRemoved(e);
271 }
272 }
273 return toDelete;
274 }
275
276 /**
277 * Determines the next active data layer according to the following
278 * rules:
279 * <ul>
280 * <li>if there is at least one {@link OsmDataLayer} the first one
281 * becomes active</li>
282 * <li>otherwise, the top most layer of any type becomes active</li>
283 * </ul>
284 *
285 * @param except A layer to ignore.
286 * @return the next active data layer
287 */
288 private Layer suggestNextActiveLayer(Layer except) {
289 List<Layer> layersList = new ArrayList<>(getLayers());
290 layersList.remove(except);
291 // First look for data layer
292 for (Layer layer : layersList) {
293 if (layer instanceof OsmDataLayer) {
294 return layer;
295 }
296 }
297
298 // Then any layer
299 if (!layersList.isEmpty())
300 return layersList.get(0);
301
302 // and then give up
303 return null;
304 }
305
306 /**
307 * Replies the currently active layer
308 *
309 * @return the currently active layer (may be null)
310 */
311 public synchronized Layer getActiveLayer() {
312 return activeLayer;
313 }
314
315 /**
316 * Replies the current edit layer, if any
317 *
318 * @return the current edit layer. May be null.
319 */
320 public synchronized OsmDataLayer getEditLayer() {
321 return editLayer;
322 }
323
324 /**
325 * Gets the data set of the active edit layer.
326 * @return That data set, <code>null</code> if there is no edit layer.
327 */
328 public synchronized DataSet getEditDataSet() {
329 if (editLayer != null) {
330 return editLayer.data;
331 } else {
332 return null;
333 }
334 }
335
336 /**
337 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
338 * first, layer with the highest Z-Order last.
339 * <p>
340 * The active data layer is pulled above all adjacent data layers.
341 *
342 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
343 * first, layer with the highest Z-Order last.
344 */
345 public synchronized List<Layer> getVisibleLayersInZOrder() {
346 List<Layer> ret = new ArrayList<>();
347 // This is set while we delay the addition of the active layer.
348 boolean activeLayerDelayed = false;
349 List<Layer> layers = getLayers();
350 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
351 Layer l = iterator.previous();
352 if (!l.isVisible()) {
353 // ignored
354 } else if (l == activeLayer && l instanceof OsmDataLayer) {
355 // delay and add after the current block of OsmDataLayer
356 activeLayerDelayed = true;
357 } else {
358 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
359 // add active layer before the current one.
360 ret.add(activeLayer);
361 activeLayerDelayed = false;
362 }
363 // Add this layer now
364 ret.add(l);
365 }
366 }
367 if (activeLayerDelayed) {
368 ret.add(activeLayer);
369 }
370 return ret;
371 }
372
373 @Override
374 public synchronized void resetState() {
375 // active and edit layer are unset automatically
376 super.resetState();
377
378 activeLayerChangeListeners.clear();
379 layerAvailabilityListeners.clear();
380 }
381}
Note: See TracBrowser for help on using the repository browser.