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

Last change on this file since 10391 was 10391, checked in by wiktorn, 8 years ago

Fixes for LayerManager.

  • LayerManager: invoke hookupMapView at the end of addLayer so layer's initialization code depending on presence of MapView will be executed
  • Main: moved creation of mapFrame from addLayer(...) to anonymous LayerManager listener, so hookupMapView can depend on the fact, that mapFrame exists
  • AbstractTileSourceLayer: forced generation of the event for this layer, so MouseAdapter listeners will be properly registered

Closes: #12964, #12965

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