source: josm/trunk/src/org/openstreetmap/josm/gui/layer/LayerManager.java@ 10458

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

fix #13029 - Replace hookUpMapView by attachToMapView (patch by michael2402, modified) - gsoc-core

File size: 12.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import java.util.ArrayList;
5import java.util.Collections;
6import java.util.List;
7import java.util.concurrent.CopyOnWriteArrayList;
8
9import org.openstreetmap.josm.gui.util.GuiHelper;
10import org.openstreetmap.josm.tools.Utils;
11import org.openstreetmap.josm.tools.bugreport.BugReport;
12
13/**
14 * This class handles the layer management.
15 * <p>
16 * This manager handles a list of layers with the first layer being the front layer.
17 * <h1>Threading</h1>
18 * Methods of this manager may be called from any thread in any order.
19 * Listeners are called while this layer manager is locked, so they should not block.
20 *
21 * @author Michael Zangl
22 * @since 10273
23 */
24public class LayerManager {
25 /**
26 * Interface to notify listeners of a layer change.
27 */
28 public interface LayerChangeListener {
29 /**
30 * Notifies this listener that a layer has been added.
31 * <p>
32 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
33 * @param e The new added layer event
34 */
35 void layerAdded(LayerAddEvent e);
36
37 /**
38 * Notifies this listener that a layer is about to be removed.
39 * <p>
40 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
41 * @param e The layer to be removed (as event)
42 */
43 void layerRemoving(LayerRemoveEvent e);
44
45 /**
46 * Notifies this listener that the order of layers was changed.
47 * <p>
48 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
49 * @param e The order change event.
50 */
51 void layerOrderChanged(LayerOrderChangeEvent e);
52 }
53
54 protected static class LayerManagerEvent {
55 private final LayerManager source;
56
57 LayerManagerEvent(LayerManager source) {
58 this.source = source;
59 }
60
61 public LayerManager getSource() {
62 return source;
63 }
64 }
65
66 /**
67 * The event that is fired whenever a layer was added.
68 * @author Michael Zangl
69 */
70 public static class LayerAddEvent extends LayerManagerEvent {
71 private final Layer addedLayer;
72
73 LayerAddEvent(LayerManager source, Layer addedLayer) {
74 super(source);
75 this.addedLayer = addedLayer;
76 }
77
78 /**
79 * Gets the layer that was added.
80 * @return The added layer.
81 */
82 public Layer getAddedLayer() {
83 return addedLayer;
84 }
85
86 @Override
87 public String toString() {
88 return "LayerAddEvent [addedLayer=" + addedLayer + ']';
89 }
90 }
91
92 /**
93 * The event that is fired before removing a layer.
94 * @author Michael Zangl
95 */
96 public static class LayerRemoveEvent extends LayerManagerEvent {
97 private final Layer removedLayer;
98 private final boolean lastLayer;
99
100 LayerRemoveEvent(LayerManager source, Layer removedLayer) {
101 super(source);
102 this.removedLayer = removedLayer;
103 this.lastLayer = source.getLayers().size() == 1;
104 }
105
106 /**
107 * Gets the layer that is about to be removed.
108 * @return The layer.
109 */
110 public Layer getRemovedLayer() {
111 return removedLayer;
112 }
113
114 /**
115 * Check if the layer that was removed is the last layer in the list.
116 * @return <code>true</code> if this was the last layer.
117 * @since 10432
118 */
119 public boolean isLastLayer() {
120 return lastLayer;
121 }
122
123 @Override
124 public String toString() {
125 return "LayerRemoveEvent [removedLayer=" + removedLayer + ", lastLayer=" + lastLayer + ']';
126 }
127 }
128
129 /**
130 * An event that is fired whenever the order of layers changed.
131 * <p>
132 * We currently do not report the exact changes.
133 * @author Michael Zangl
134 */
135 public static class LayerOrderChangeEvent extends LayerManagerEvent {
136 LayerOrderChangeEvent(LayerManager source) {
137 super(source);
138 }
139
140 @Override
141 public String toString() {
142 return "LayerOrderChangeEvent []";
143 }
144 }
145
146 /**
147 * This is the list of layers we manage.
148 */
149 private final List<Layer> layers = new ArrayList<>();
150
151 private final List<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>();
152
153 /**
154 * Add a layer. The layer will be added at a given psoition.
155 * @param layer The layer to add
156 */
157 public void addLayer(final Layer layer) {
158 // we force this on to the EDT Thread to make events fire from there.
159 // The synchronization lock needs to be held by the EDT.
160 GuiHelper.runInEDTAndWaitWithException(new Runnable() {
161 @Override
162 public void run() {
163 realAddLayer(layer);
164 }
165 });
166 }
167
168 protected synchronized void realAddLayer(Layer layer) {
169 if (containsLayer(layer)) {
170 throw new IllegalArgumentException("Cannot add a layer twice.");
171 }
172 LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition();
173 int position = positionStrategy.getPosition(this);
174 checkPosition(position);
175 insertLayerAt(layer, position);
176 fireLayerAdded(layer);
177 layer.hookUpMapView(); // needs to be after fireLayerAdded
178 }
179
180 /**
181 * Remove the layer from the mapview. If the layer was in the list before,
182 * an LayerChange event is fired.
183 * @param layer The layer to remove
184 */
185 public void removeLayer(final Layer layer) {
186 // we force this on to the EDT Thread to make events fire from there.
187 // The synchronization lock needs to be held by the EDT.
188 GuiHelper.runInEDTAndWaitWithException(new Runnable() {
189 @Override
190 public void run() {
191 realRemoveLayer(layer);
192 }
193 });
194 }
195
196 protected synchronized void realRemoveLayer(Layer layer) {
197 checkContainsLayer(layer);
198
199 fireLayerRemoving(layer);
200 layers.remove(layer);
201 }
202
203 /**
204 * Move a layer to a new position.
205 * @param layer The layer to move.
206 * @param position The position.
207 * @throws IndexOutOfBoundsException if the position is out of bounds.
208 */
209 public void moveLayer(final Layer layer, final int position) {
210 // we force this on to the EDT Thread to make events fire from there.
211 // The synchronization lock needs to be held by the EDT.
212 GuiHelper.runInEDTAndWaitWithException(new Runnable() {
213 @Override
214 public void run() {
215 realMoveLayer(layer, position);
216 }
217 });
218 }
219
220 protected synchronized void realMoveLayer(Layer layer, int position) {
221 checkContainsLayer(layer);
222 checkPosition(position);
223
224 int curLayerPos = layers.indexOf(layer);
225 if (position == curLayerPos)
226 return; // already in place.
227 layers.remove(curLayerPos);
228 insertLayerAt(layer, position);
229 fireLayerOrderChanged();
230 }
231
232 /**
233 * Insert a layer at a given position.
234 * @param layer The layer to add.
235 * @param position The position on which we should add it.
236 */
237 private void insertLayerAt(Layer layer, int position) {
238 if (position == layers.size()) {
239 layers.add(layer);
240 } else {
241 layers.add(position, layer);
242 }
243 }
244
245 /**
246 * Check if the (new) position is valid
247 * @param position The position index
248 * @throws IndexOutOfBoundsException if it is not.
249 */
250 private void checkPosition(int position) {
251 if (position < 0 || position > layers.size()) {
252 throw new IndexOutOfBoundsException("Position " + position + " out of range.");
253 }
254 }
255
256 /**
257 * Gets an unmodifiable list of all layers that are currently in this manager. This list won't update once layers are added or removed.
258 * @return The list of layers.
259 */
260 public List<Layer> getLayers() {
261 return Collections.unmodifiableList(new ArrayList<>(layers));
262 }
263
264 /**
265 * Replies an unmodifiable list of layers of a certain type.
266 *
267 * Example:
268 * <pre>
269 * List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
270 * </pre>
271 * @param <T> The layer type
272 * @param ofType The layer type.
273 * @return an unmodifiable list of layers of a certain type.
274 */
275 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
276 return new ArrayList<>(Utils.filteredCollection(getLayers(), ofType));
277 }
278
279 /**
280 * replies true if the list of layers managed by this map view contain layer
281 *
282 * @param layer the layer
283 * @return true if the list of layers managed by this map view contain layer
284 */
285 public synchronized boolean containsLayer(Layer layer) {
286 return layers.contains(layer);
287 }
288
289 protected void checkContainsLayer(Layer layer) {
290 if (!containsLayer(layer)) {
291 throw new IllegalArgumentException(layer + " is not managed by us.");
292 }
293 }
294
295 /**
296 * Adds a layer change listener
297 *
298 * @param listener the listener.
299 * @throws IllegalArgumentException If the listener was added twice.
300 */
301 public synchronized void addLayerChangeListener(LayerChangeListener listener) {
302 addLayerChangeListener(listener, false);
303 }
304
305 /**
306 * Adds a layer change listener
307 *
308 * @param listener the listener.
309 * @param fireAdd if we should fire an add event for every layer in this manager.
310 * @throws IllegalArgumentException If the listener was added twice.
311 */
312 public synchronized void addLayerChangeListener(LayerChangeListener listener, boolean fireAdd) {
313 if (layerChangeListeners.contains(listener)) {
314 throw new IllegalArgumentException("Listener already registered.");
315 }
316 layerChangeListeners.add(listener);
317 if (fireAdd) {
318 for (Layer l : getLayers()) {
319 listener.layerAdded(new LayerAddEvent(this, l));
320 }
321 }
322 }
323
324 /**
325 * Removes a layer change listener
326 *
327 * @param listener the listener. Ignored if null or already registered.
328 */
329 public synchronized void removeLayerChangeListener(LayerChangeListener listener) {
330 removeLayerChangeListener(listener, false);
331 }
332
333
334 /**
335 * Removes a layer change listener
336 *
337 * @param listener the listener.
338 * @param fireRemove if we should fire a remove event for every layer in this manager.
339 */
340 public synchronized void removeLayerChangeListener(LayerChangeListener listener, boolean fireRemove) {
341 if (!layerChangeListeners.remove(listener)) {
342 throw new IllegalArgumentException("Listener was not registered before: " + listener);
343 } else {
344 if (fireRemove) {
345 for (Layer l : getLayers()) {
346 listener.layerRemoving(new LayerRemoveEvent(this, l));
347 }
348 }
349 }
350 }
351
352 private void fireLayerAdded(Layer layer) {
353 GuiHelper.assertCallFromEdt();
354 LayerAddEvent e = new LayerAddEvent(this, layer);
355 for (LayerChangeListener l : layerChangeListeners) {
356 try {
357 l.layerAdded(e);
358 } catch (RuntimeException t) {
359 throw BugReport.intercept(t).put("listener", l).put("event", e);
360 }
361 }
362 }
363
364 private void fireLayerRemoving(Layer layer) {
365 GuiHelper.assertCallFromEdt();
366 LayerRemoveEvent e = new LayerRemoveEvent(this, layer);
367 for (LayerChangeListener l : layerChangeListeners) {
368 try {
369 l.layerRemoving(e);
370 } catch (RuntimeException t) {
371 throw BugReport.intercept(t).put("listener", l).put("event", e);
372 }
373 }
374 }
375
376 private void fireLayerOrderChanged() {
377 GuiHelper.assertCallFromEdt();
378 LayerOrderChangeEvent e = new LayerOrderChangeEvent(this);
379 for (LayerChangeListener l : layerChangeListeners) {
380 try {
381 l.layerOrderChanged(e);
382 } catch (RuntimeException t) {
383 throw BugReport.intercept(t).put("listener", l).put("event", e);
384 }
385 }
386 }
387
388 /**
389 * Reset all layer manager state. This includes removing all layers and then unregistering all listeners
390 * @since 10432
391 */
392 public void resetState() {
393 // some layer remove listeners remove other layers.
394 while (!getLayers().isEmpty()) {
395 removeLayer(getLayers().get(0));
396 }
397
398 layerChangeListeners.clear();
399 }
400}
Note: See TracBrowser for help on using the repository browser.