source: josm/trunk/src/org/openstreetmap/josm/gui/layer/LayerManagerWithActive.java@ 10271

Last change on this file since 10271 was 10271, checked in by stoecker, 9 years ago

GSOC core rework - LayerManager, fix #12863, by Michael Zangl

File size: 9.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import java.util.ArrayList;
5import java.util.List;
6import java.util.ListIterator;
7import java.util.concurrent.CopyOnWriteArrayList;
8
9import org.openstreetmap.josm.data.osm.DataSet;
10import org.openstreetmap.josm.gui.util.GuiHelper;
11
12/**
13 * This class extends the layer manager by adding an active and an edit layer.
14 * <p>
15 * The active layer is the layer the user is currently working on.
16 * <p>
17 * The edit layer is an data layer that we currently work with.
18 * @author Michael Zangl
19 * @since 10271
20 */
21public class LayerManagerWithActive extends LayerManager {
22 /**
23 * This listener listens to changes of the active or the edit layer.
24 * @author Michael Zangl
25 *
26 */
27 public interface ActiveLayerChangeListener {
28 /**
29 * Called whenever the active or edit layer changed.
30 * <p>
31 * You can be sure that this layer is still contained in this set.
32 * <p>
33 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread.
34 * @param e The change event.
35 */
36 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
37 }
38
39 /**
40 * This event is fired whenever the active or the edit layer changes.
41 * @author Michael Zangl
42 */
43 public class ActiveLayerChangeEvent extends LayerManagerEvent {
44
45 private final OsmDataLayer previousEditLayer;
46
47 private final Layer previousActiveLayer;
48
49 /**
50 * Create a new {@link ActiveLayerChangeEvent}
51 * @param source The source
52 * @param previousEditLayer the previous edit layer
53 * @param previousActiveLayer the previous active layer
54 */
55 ActiveLayerChangeEvent(LayerManagerWithActive source, OsmDataLayer previousEditLayer,
56 Layer previousActiveLayer) {
57 super(source);
58 this.previousEditLayer = previousEditLayer;
59 this.previousActiveLayer = previousActiveLayer;
60 }
61
62 /**
63 * Gets the edit layer that was previously used.
64 * @return The old edit layer, <code>null</code> if there is none.
65 */
66 public OsmDataLayer getPreviousEditLayer() {
67 return previousEditLayer;
68 }
69
70 /**
71 * Gets the active layer that was previously used.
72 * @return The old active layer, <code>null</code> if there is none.
73 */
74 public Layer getPreviousActiveLayer() {
75 return previousActiveLayer;
76 }
77
78 @Override
79 public LayerManagerWithActive getSource() {
80 return (LayerManagerWithActive) super.getSource();
81 }
82 }
83
84 /**
85 * The layer from the layers list that is currently active.
86 */
87 private Layer activeLayer;
88
89 /**
90 * The edit layer is the current active data layer.
91 */
92 private OsmDataLayer editLayer;
93
94 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
95
96 /**
97 * Adds a active/edit layer change listener
98 *
99 * @param listener the listener.
100 * @param initialFire fire a fake active-layer-changed-event right after adding
101 * the listener. The previous layers will be null. The listener is notified in the current thread.
102 */
103 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener, boolean initialFire) {
104 if (activeLayerChangeListeners.contains(listener)) {
105 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
106 }
107 activeLayerChangeListeners.add(listener);
108 if (initialFire) {
109 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
110 }
111 }
112
113 /**
114 * Removes an active/edit layer change listener.
115 * @param listener the listener.
116 */
117 public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
118 if (!activeLayerChangeListeners.contains(listener)) {
119 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
120 }
121 activeLayerChangeListeners.remove(listener);
122 }
123
124 /**
125 * Set the active layer. If the layer is an OsmDataLayer, the edit layer is also changed.
126 * @param layer The active layer.
127 */
128 public void setActiveLayer(final Layer layer) {
129 // we force this on to the EDT Thread to make events fire from there.
130 // The synchronization lock needs to be held by the EDT.
131 GuiHelper.runInEDTAndWaitWithException(new Runnable() {
132 @Override
133 public void run() {
134 realSetActiveLayer(layer);
135 }
136 });
137 }
138
139 protected synchronized void realSetActiveLayer(final Layer layer) {
140 // to be called in EDT thread
141 checkContainsLayer(layer);
142 setActiveLayer(layer, false);
143 }
144
145 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
146 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
147 activeLayer = layer;
148 if (activeLayer instanceof OsmDataLayer) {
149 editLayer = (OsmDataLayer) activeLayer;
150 } else if (forceEditLayerUpdate) {
151 editLayer = null;
152 }
153 fireActiveLayerChange(event);
154 }
155
156 private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
157 GuiHelper.assertCallFromEdt();
158 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
159 for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
160 l.activeOrEditLayerChanged(event);
161 }
162 }
163 }
164
165 @Override
166 protected synchronized void realAddLayer(Layer layer) {
167 super.realAddLayer(layer);
168
169 // update the active layer automatically.
170 if (layer instanceof OsmDataLayer || activeLayer == null) {
171 setActiveLayer(layer);
172 }
173 }
174
175 @Override
176 protected synchronized void realRemoveLayer(Layer layer) {
177 if (layer == activeLayer || layer == editLayer) {
178 Layer nextActive = suggestNextActiveLayer(layer);
179 setActiveLayer(nextActive, true);
180 }
181
182 super.realRemoveLayer(layer);
183 }
184
185 /**
186 * Determines the next active data layer according to the following
187 * rules:
188 * <ul>
189 * <li>if there is at least one {@link OsmDataLayer} the first one
190 * becomes active</li>
191 * <li>otherwise, the top most layer of any type becomes active</li>
192 * </ul>
193 *
194 * @param except A layer to ignore.
195 * @return the next active data layer
196 */
197 private Layer suggestNextActiveLayer(Layer except) {
198 List<Layer> layersList = new ArrayList<>(getLayers());
199 layersList.remove(except);
200 // First look for data layer
201 for (Layer layer : layersList) {
202 if (layer instanceof OsmDataLayer) {
203 return layer;
204 }
205 }
206
207 // Then any layer
208 if (!layersList.isEmpty())
209 return layersList.get(0);
210
211 // and then give up
212 return null;
213 }
214
215 /**
216 * Replies the currently active layer
217 *
218 * @return the currently active layer (may be null)
219 */
220 public synchronized Layer getActiveLayer() {
221 return activeLayer;
222 }
223
224 /**
225 * Replies the current edit layer, if any
226 *
227 * @return the current edit layer. May be null.
228 */
229 public synchronized OsmDataLayer getEditLayer() {
230 return editLayer;
231 }
232
233 /**
234 * Gets the data set of the active edit layer.
235 * @return That data set, <code>null</code> if there is no edit layer.
236 */
237 public synchronized DataSet getEditDataSet() {
238 if (editLayer != null) {
239 return editLayer.data;
240 } else {
241 return null;
242 }
243 }
244
245
246 /**
247 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
248 * first, layer with the highest Z-Order last.
249 * <p>
250 * The active data layer is pulled above all adjacent data layers.
251 *
252 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
253 * first, layer with the highest Z-Order last.
254 */
255 public synchronized List<Layer> getVisibleLayersInZOrder() {
256 List<Layer> ret = new ArrayList<>();
257 // This is set while we delay the addition of the active layer.
258 boolean activeLayerDelayed = false;
259 List<Layer> layers = getLayers();
260 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
261 Layer l = iterator.previous();
262 if (!l.isVisible()) {
263 // ignored
264 } else if (l == activeLayer && l instanceof OsmDataLayer) {
265 // delay and add after the current block of OsmDataLayer
266 activeLayerDelayed = true;
267 } else {
268 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
269 // add active layer before the current one.
270 ret.add(activeLayer);
271 activeLayerDelayed = false;
272 }
273 // Add this layer now
274 ret.add(l);
275 }
276 }
277 if (activeLayerDelayed) {
278 ret.add(activeLayer);
279 }
280 return ret;
281 }
282}
Note: See TracBrowser for help on using the repository browser.