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

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

fix #14778 - stroke not reset after drawing of large areas produced graphics artifacts when painting world bounds and selection rectangles, bug seen when filtering ways only, as way rendering reset the stroke

File size: 15.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.List;
9import java.util.ListIterator;
10import java.util.concurrent.CopyOnWriteArrayList;
11
12import javax.swing.JOptionPane;
13
14import org.openstreetmap.josm.data.osm.DataSet;
15import org.openstreetmap.josm.gui.MainApplication;
16import org.openstreetmap.josm.gui.util.GuiHelper;
17
18/**
19 * This class extends the layer manager by adding an active and an edit layer.
20 * <p>
21 * The active layer is the layer the user is currently working on.
22 * <p>
23 * The edit layer is an data layer that we currently work with.
24 * @author Michael Zangl
25 * @since 10279
26 */
27public class MainLayerManager extends LayerManager {
28 /**
29 * This listener listens to changes of the active or the edit layer.
30 * @author Michael Zangl
31 * @since 10600 (functional interface)
32 */
33 @FunctionalInterface
34 public interface ActiveLayerChangeListener {
35 /**
36 * Called whenever the active or edit layer changed.
37 * <p>
38 * You can be sure that this layer is still contained in this set.
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 change event.
42 */
43 void activeOrEditLayerChanged(ActiveLayerChangeEvent e);
44 }
45
46 /**
47 * This event is fired whenever the active or the edit layer changes.
48 * @author Michael Zangl
49 */
50 public static class ActiveLayerChangeEvent extends LayerManagerEvent {
51
52 private final OsmDataLayer previousEditLayer;
53
54 private final Layer previousActiveLayer;
55
56 /**
57 * Create a new {@link ActiveLayerChangeEvent}
58 * @param source The source
59 * @param previousEditLayer the previous edit layer
60 * @param previousActiveLayer the previous active layer
61 */
62 ActiveLayerChangeEvent(MainLayerManager source, OsmDataLayer previousEditLayer,
63 Layer previousActiveLayer) {
64 super(source);
65 this.previousEditLayer = previousEditLayer;
66 this.previousActiveLayer = previousActiveLayer;
67 }
68
69 /**
70 * Gets the edit layer that was previously used.
71 * @return The old edit layer, <code>null</code> if there is none.
72 */
73 public OsmDataLayer getPreviousEditLayer() {
74 return previousEditLayer;
75 }
76
77 /**
78 * Gets the active layer that was previously used.
79 * @return The old active layer, <code>null</code> if there is none.
80 */
81 public Layer getPreviousActiveLayer() {
82 return previousActiveLayer;
83 }
84
85 /**
86 * Gets the data set that was previously used.
87 * @return The data set of {@link #getPreviousEditLayer()}.
88 */
89 public DataSet getPreviousEditDataSet() {
90 if (previousEditLayer != null) {
91 return previousEditLayer.data;
92 } else {
93 return null;
94 }
95 }
96
97 @Override
98 public MainLayerManager getSource() {
99 return (MainLayerManager) super.getSource();
100 }
101 }
102
103 /**
104 * This event is fired for {@link LayerAvailabilityListener}
105 * @author Michael Zangl
106 * @since 10508
107 */
108 public static class LayerAvailabilityEvent extends LayerManagerEvent {
109 private final boolean hasLayers;
110
111 LayerAvailabilityEvent(LayerManager source, boolean hasLayers) {
112 super(source);
113 this.hasLayers = hasLayers;
114 }
115
116 /**
117 * Checks if this layer manager will have layers afterwards
118 * @return true if layers will be added.
119 */
120 public boolean hasLayers() {
121 return hasLayers;
122 }
123 }
124
125 /**
126 * A listener that gets informed before any layer is displayed and after all layers are removed.
127 * @author Michael Zangl
128 * @since 10508
129 */
130 public interface LayerAvailabilityListener {
131 /**
132 * This method is called in the UI thread right before the first layer is added.
133 * @param e The event.
134 */
135 void beforeFirstLayerAdded(LayerAvailabilityEvent e);
136
137 /**
138 * This method is called in the UI thread after the last layer was removed.
139 * @param e The event.
140 */
141 void afterLastLayerRemoved(LayerAvailabilityEvent e);
142 }
143
144 /**
145 * The layer from the layers list that is currently active.
146 */
147 private Layer activeLayer;
148
149 /**
150 * The edit layer is the current active data layer.
151 */
152 private OsmDataLayer editLayer;
153
154 private final List<ActiveLayerChangeListener> activeLayerChangeListeners = new CopyOnWriteArrayList<>();
155 private final List<LayerAvailabilityListener> layerAvailabilityListeners = new CopyOnWriteArrayList<>();
156
157 /**
158 * Adds a active/edit layer change listener
159 *
160 * @param listener the listener.
161 */
162 public synchronized void addActiveLayerChangeListener(ActiveLayerChangeListener listener) {
163 if (activeLayerChangeListeners.contains(listener)) {
164 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
165 }
166 activeLayerChangeListeners.add(listener);
167 }
168
169 /**
170 * Adds a active/edit layer change listener. Fire a fake active-layer-changed-event right after adding
171 * the listener. The previous layers will be null. The listener is notified in the current thread.
172 * @param listener the listener.
173 */
174 public synchronized void addAndFireActiveLayerChangeListener(ActiveLayerChangeListener listener) {
175 addActiveLayerChangeListener(listener);
176 listener.activeOrEditLayerChanged(new ActiveLayerChangeEvent(this, null, null));
177 }
178
179 /**
180 * Removes an active/edit layer change listener.
181 * @param listener the listener.
182 */
183 public synchronized void removeActiveLayerChangeListener(ActiveLayerChangeListener listener) {
184 if (!activeLayerChangeListeners.contains(listener)) {
185 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
186 }
187 activeLayerChangeListeners.remove(listener);
188 }
189
190 /**
191 * Add a new {@link LayerAvailabilityListener}.
192 * @param listener The listener
193 * @since 10508
194 */
195 public synchronized void addLayerAvailabilityListener(LayerAvailabilityListener listener) {
196 if (!layerAvailabilityListeners.add(listener)) {
197 throw new IllegalArgumentException("Attempted to add listener that was already in list: " + listener);
198 }
199 }
200
201 /**
202 * Remove an {@link LayerAvailabilityListener}.
203 * @param listener The listener
204 * @since 10508
205 */
206 public synchronized void removeLayerAvailabilityListener(LayerAvailabilityListener listener) {
207 if (!layerAvailabilityListeners.remove(listener)) {
208 throw new IllegalArgumentException("Attempted to remove listener that was not in list: " + listener);
209 }
210 }
211
212 /**
213 * Set the active layer, unless the layer is read-only.
214 * If the layer is an OsmDataLayer, the edit layer is also changed.
215 * @param layer The active layer.
216 */
217 public void setActiveLayer(final Layer layer) {
218 // we force this on to the EDT Thread to make events fire from there.
219 // The synchronization lock needs to be held by the EDT.
220 if (layer instanceof OsmDataLayer && ((OsmDataLayer) layer).isReadOnly()) {
221 GuiHelper.runInEDT(() ->
222 JOptionPane.showMessageDialog(
223 MainApplication.parent,
224 tr("Trying to set a read only data layer as edit layer"),
225 tr("Warning"),
226 JOptionPane.WARNING_MESSAGE));
227 } else {
228 GuiHelper.runInEDTAndWaitWithException(() -> realSetActiveLayer(layer));
229 }
230 }
231
232 protected synchronized void realSetActiveLayer(final Layer layer) {
233 // to be called in EDT thread
234 checkContainsLayer(layer);
235 setActiveLayer(layer, false);
236 }
237
238 private void setActiveLayer(Layer layer, boolean forceEditLayerUpdate) {
239 ActiveLayerChangeEvent event = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
240 activeLayer = layer;
241 if (activeLayer instanceof OsmDataLayer) {
242 editLayer = (OsmDataLayer) activeLayer;
243 } else if (forceEditLayerUpdate) {
244 editLayer = null;
245 }
246 fireActiveLayerChange(event);
247 }
248
249 private void fireActiveLayerChange(ActiveLayerChangeEvent event) {
250 GuiHelper.assertCallFromEdt();
251 if (event.getPreviousActiveLayer() != activeLayer || event.getPreviousEditLayer() != editLayer) {
252 for (ActiveLayerChangeListener l : activeLayerChangeListeners) {
253 l.activeOrEditLayerChanged(event);
254 }
255 }
256 }
257
258 @Override
259 protected synchronized void realAddLayer(Layer layer, boolean initialZoom) {
260 if (getLayers().isEmpty()) {
261 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, true);
262 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
263 l.beforeFirstLayerAdded(e);
264 }
265 }
266 super.realAddLayer(layer, initialZoom);
267
268 // update the active layer automatically.
269 if (layer instanceof OsmDataLayer || activeLayer == null) {
270 setActiveLayer(layer);
271 }
272 }
273
274 @Override
275 protected Collection<Layer> realRemoveSingleLayer(Layer layer) {
276 if (layer == activeLayer || layer == editLayer) {
277 Layer nextActive = suggestNextActiveLayer(layer);
278 setActiveLayer(nextActive, true);
279 }
280
281 Collection<Layer> toDelete = super.realRemoveSingleLayer(layer);
282 if (getLayers().isEmpty()) {
283 LayerAvailabilityEvent e = new LayerAvailabilityEvent(this, false);
284 for (LayerAvailabilityListener l : layerAvailabilityListeners) {
285 l.afterLastLayerRemoved(e);
286 }
287 }
288 return toDelete;
289 }
290
291 /**
292 * Determines the next active data layer according to the following
293 * rules:
294 * <ul>
295 * <li>if there is at least one {@link OsmDataLayer} the first one
296 * becomes active</li>
297 * <li>otherwise, the top most layer of any type becomes active</li>
298 * </ul>
299 *
300 * @param except A layer to ignore.
301 * @return the next active data layer
302 */
303 private Layer suggestNextActiveLayer(Layer except) {
304 List<Layer> layersList = new ArrayList<>(getLayers());
305 layersList.remove(except);
306 // First look for data layer
307 for (Layer layer : layersList) {
308 if (layer instanceof OsmDataLayer) {
309 return layer;
310 }
311 }
312
313 // Then any layer
314 if (!layersList.isEmpty())
315 return layersList.get(0);
316
317 // and then give up
318 return null;
319 }
320
321 /**
322 * Replies the currently active layer
323 *
324 * @return the currently active layer (may be null)
325 */
326 public synchronized Layer getActiveLayer() {
327 if (activeLayer instanceof OsmDataLayer) {
328 if (!((OsmDataLayer) activeLayer).isReadOnly()) {
329 return activeLayer;
330 } else {
331 return null;
332 }
333 } else {
334 return activeLayer;
335 }
336 }
337
338 /**
339 * Replies the current edit layer, if present and not readOnly
340 *
341 * @return the current edit layer. May be null.
342 */
343 public synchronized OsmDataLayer getEditLayer() {
344 if (editLayer != null && !editLayer.isReadOnly())
345 return editLayer;
346 else
347 return null;
348 }
349
350 /**
351 * Gets the data set of the active edit layer.
352 * @return That data set, <code>null</code> if there is no edit layer.
353 */
354 public synchronized DataSet getEditDataSet() {
355 if (editLayer != null) {
356 return editLayer.data;
357 } else {
358 return null;
359 }
360 }
361
362 /**
363 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
364 * first, layer with the highest Z-Order last.
365 * <p>
366 * The active data layer is pulled above all adjacent data layers.
367 *
368 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
369 * first, layer with the highest Z-Order last.
370 */
371 public synchronized List<Layer> getVisibleLayersInZOrder() {
372 List<Layer> ret = new ArrayList<>();
373 // This is set while we delay the addition of the active layer.
374 boolean activeLayerDelayed = false;
375 List<Layer> layers = getLayers();
376 for (ListIterator<Layer> iterator = layers.listIterator(layers.size()); iterator.hasPrevious();) {
377 Layer l = iterator.previous();
378 if (!l.isVisible()) {
379 // ignored
380 } else if (l == activeLayer && l instanceof OsmDataLayer) {
381 // delay and add after the current block of OsmDataLayer
382 activeLayerDelayed = true;
383 } else {
384 if (activeLayerDelayed && !(l instanceof OsmDataLayer)) {
385 // add active layer before the current one.
386 ret.add(activeLayer);
387 activeLayerDelayed = false;
388 }
389 // Add this layer now
390 ret.add(l);
391 }
392 }
393 if (activeLayerDelayed) {
394 ret.add(activeLayer);
395 }
396 return ret;
397 }
398
399 /**
400 * Invalidates current edit layer, if any. Does nothing of there is no edit layer.
401 * @since 13150
402 */
403 public void invalidateEditLayer() {
404 if (editLayer != null) {
405 editLayer.invalidate();
406 }
407 }
408
409 @Override
410 protected synchronized void realResetState() {
411 // active and edit layer are unset automatically
412 super.realResetState();
413
414 activeLayerChangeListeners.clear();
415 layerAvailabilityListeners.clear();
416 }
417
418 /**
419 * Prepares an OsmDataLayer for upload. The layer to be uploaded is locked and
420 * if the layer to be uploaded is the current editLayer then editLayer is reset
421 * to null for disallowing any changes to the layer. An ActiveLayerChangeEvent
422 * is fired to notify the listeners
423 *
424 * @param layer The OsmDataLayer to be uploaded
425 */
426 public void prepareLayerForUpload(OsmDataLayer layer) {
427
428 GuiHelper.assertCallFromEdt();
429 layer.setReadOnly();
430
431 // Reset only the edit layer as empty
432 if (editLayer == layer) {
433 ActiveLayerChangeEvent activeLayerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
434 editLayer = null;
435 fireActiveLayerChange(activeLayerChangeEvent);
436 }
437 }
438
439 /**
440 * Post upload processing of the OsmDataLayer.
441 * If the current edit layer is empty this function sets the layer uploaded as the
442 * current editLayer. An ActiveLayerChangeEvent is fired to notify the listeners
443 *
444 * @param layer The OsmDataLayer uploaded
445 */
446 public void processLayerAfterUpload(OsmDataLayer layer) {
447 GuiHelper.assertCallFromEdt();
448 layer.unsetReadOnly();
449
450 // Set the layer as edit layer if the edit layer is empty.
451 if (editLayer == null) {
452 ActiveLayerChangeEvent layerChangeEvent = new ActiveLayerChangeEvent(this, editLayer, activeLayer);
453 editLayer = layer;
454 fireActiveLayerChange(layerChangeEvent);
455 }
456 }
457}
Note: See TracBrowser for help on using the repository browser.