source: josm/trunk/src/org/openstreetmap/josm/gui/MapView.java@ 10596

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

see #11390, see #13120, see #13190 - sonar - squid:S1612 - Lambdas should be replaced with method references

  • Property svn:eol-style set to native
File size: 49.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.AlphaComposite;
7import java.awt.Color;
8import java.awt.Dimension;
9import java.awt.Graphics;
10import java.awt.Graphics2D;
11import java.awt.Point;
12import java.awt.Rectangle;
13import java.awt.event.ComponentAdapter;
14import java.awt.event.ComponentEvent;
15import java.awt.event.KeyEvent;
16import java.awt.event.MouseAdapter;
17import java.awt.event.MouseEvent;
18import java.awt.event.MouseMotionListener;
19import java.awt.geom.Area;
20import java.awt.geom.GeneralPath;
21import java.awt.image.BufferedImage;
22import java.beans.PropertyChangeEvent;
23import java.beans.PropertyChangeListener;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.LinkedHashSet;
30import java.util.List;
31import java.util.Set;
32import java.util.concurrent.CopyOnWriteArrayList;
33
34import javax.swing.AbstractButton;
35import javax.swing.JComponent;
36import javax.swing.JPanel;
37
38import org.openstreetmap.josm.Main;
39import org.openstreetmap.josm.actions.mapmode.MapMode;
40import org.openstreetmap.josm.data.Bounds;
41import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
42import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
43import org.openstreetmap.josm.data.ProjectionBounds;
44import org.openstreetmap.josm.data.SelectionChangedListener;
45import org.openstreetmap.josm.data.ViewportData;
46import org.openstreetmap.josm.data.coor.EastNorth;
47import org.openstreetmap.josm.data.coor.LatLon;
48import org.openstreetmap.josm.data.imagery.ImageryInfo;
49import org.openstreetmap.josm.data.osm.DataSet;
50import org.openstreetmap.josm.data.osm.OsmPrimitive;
51import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
52import org.openstreetmap.josm.data.osm.visitor.paint.Rendering;
53import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache;
54import org.openstreetmap.josm.gui.MapViewState.MapViewRectangle;
55import org.openstreetmap.josm.gui.layer.AbstractMapViewPaintable;
56import org.openstreetmap.josm.gui.layer.GpxLayer;
57import org.openstreetmap.josm.gui.layer.ImageryLayer;
58import org.openstreetmap.josm.gui.layer.Layer;
59import org.openstreetmap.josm.gui.layer.LayerManager;
60import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
61import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
62import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
63import org.openstreetmap.josm.gui.layer.MainLayerManager;
64import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
65import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
66import org.openstreetmap.josm.gui.layer.MapViewGraphics;
67import org.openstreetmap.josm.gui.layer.MapViewPaintable;
68import org.openstreetmap.josm.gui.layer.MapViewPaintable.LayerPainter;
69import org.openstreetmap.josm.gui.layer.MapViewPaintable.MapViewEvent;
70import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationEvent;
71import org.openstreetmap.josm.gui.layer.MapViewPaintable.PaintableInvalidationListener;
72import org.openstreetmap.josm.gui.layer.OsmDataLayer;
73import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
74import org.openstreetmap.josm.gui.layer.markerlayer.PlayHeadMarker;
75import org.openstreetmap.josm.tools.AudioPlayer;
76import org.openstreetmap.josm.tools.Shortcut;
77import org.openstreetmap.josm.tools.Utils;
78import org.openstreetmap.josm.tools.bugreport.BugReport;
79import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
80
81/**
82 * This is a component used in the {@link MapFrame} for browsing the map. It use is to
83 * provide the MapMode's enough capabilities to operate.<br><br>
84 *
85 * {@code MapView} holds meta-data about the data set currently displayed, as scale level,
86 * center point viewed, what scrolling mode or editing mode is selected or with
87 * what projection the map is viewed etc..<br><br>
88 *
89 * {@code MapView} is able to administrate several layers.
90 *
91 * @author imi
92 */
93public class MapView extends NavigatableComponent
94implements PropertyChangeListener, PreferenceChangedListener,
95LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
96 /**
97 * Interface to notify listeners of a layer change.
98 * <p>
99 * To be removed: end of 2016.
100 * @deprecated Use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener} instead.
101 * @author imi
102 */
103 @Deprecated
104 public interface LayerChangeListener {
105
106 /**
107 * Notifies this listener that the active layer has changed.
108 * @param oldLayer The previous active layer
109 * @param newLayer The new activer layer
110 */
111 void activeLayerChange(Layer oldLayer, Layer newLayer);
112
113 /**
114 * Notifies this listener that a layer has been added.
115 * @param newLayer The new added layer
116 */
117 void layerAdded(Layer newLayer);
118
119 /**
120 * Notifies this listener that a layer has been removed.
121 * @param oldLayer The old removed layer
122 */
123 void layerRemoved(Layer oldLayer);
124 }
125
126 /**
127 * An interface that needs to be implemented in order to listen for changes to the active edit layer.
128 * <p>
129 * To be removed: end of 2016.
130 * @deprecated Use {@link ActiveLayerChangeListener} instead.
131 */
132 @Deprecated
133 public interface EditLayerChangeListener {
134
135 /**
136 * Called after the active edit layer was changed.
137 * @param oldLayer The old edit layer
138 * @param newLayer The current (new) edit layer
139 */
140 void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer);
141 }
142
143 /**
144 * An invalidation listener that simply calls repaint() for now.
145 * @author Michael Zangl
146 * @since 10271
147 */
148 private class LayerInvalidatedListener implements PaintableInvalidationListener {
149 private boolean ignoreRepaint;
150 @Override
151 public void paintablInvalidated(PaintableInvalidationEvent event) {
152 ignoreRepaint = true;
153 repaint();
154 }
155
156 /**
157 * Temporary until all {@link MapViewPaintable}s support this.
158 * @param p The paintable.
159 */
160 public void addTo(MapViewPaintable p) {
161 if (p instanceof AbstractMapViewPaintable) {
162 ((AbstractMapViewPaintable) p).addInvalidationListener(this);
163 }
164 }
165
166 /**
167 * Temporary until all {@link MapViewPaintable}s support this.
168 * @param p The paintable.
169 */
170 public void removeFrom(MapViewPaintable p) {
171 if (p instanceof AbstractMapViewPaintable) {
172 ((AbstractMapViewPaintable) p).removeInvalidationListener(this);
173 }
174 }
175
176 /**
177 * Attempts to trace repaints that did not originate from this listener. Good to find missed {@link MapView#repaint()}s in code.
178 */
179 protected synchronized void traceRandomRepaint() {
180 if (!ignoreRepaint) {
181 System.err.println("Repaint:");
182 Thread.dumpStack();
183 }
184 ignoreRepaint = false;
185 }
186 }
187
188 /**
189 * This class is an adapter for the old layer change interface.
190 * <p>
191 * New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener}
192 * @author Michael Zangl
193 * @since 10271
194 */
195 protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener {
196
197 private final LayerChangeListener wrapped;
198 private boolean receiveOneInitialFire;
199
200 public LayerChangeAdapter(LayerChangeListener wrapped) {
201 this.wrapped = wrapped;
202 }
203
204 public LayerChangeAdapter(LayerChangeListener wrapped, boolean initialFire) {
205 this(wrapped);
206 this.receiveOneInitialFire = initialFire;
207 }
208
209 @Override
210 public void layerAdded(LayerAddEvent e) {
211 wrapped.layerAdded(e.getAddedLayer());
212 }
213
214 @Override
215 public void layerRemoving(LayerRemoveEvent e) {
216 wrapped.layerRemoved(e.getRemovedLayer());
217 }
218
219 @Override
220 public void layerOrderChanged(LayerOrderChangeEvent e) {
221 // not in old API
222 }
223
224 @Override
225 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
226 Layer oldActive = receiveOneInitialFire ? null : e.getPreviousActiveLayer();
227 Layer newActive = e.getSource().getActiveLayer();
228 if (oldActive != newActive) {
229 wrapped.activeLayerChange(oldActive, newActive);
230 }
231 receiveOneInitialFire = false;
232 }
233
234 @Override
235 public int hashCode() {
236 final int prime = 31;
237 int result = 1;
238 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
239 return result;
240 }
241
242 @Override
243 public boolean equals(Object obj) {
244 if (this == obj)
245 return true;
246 if (obj == null)
247 return false;
248 if (getClass() != obj.getClass())
249 return false;
250 LayerChangeAdapter other = (LayerChangeAdapter) obj;
251 if (wrapped == null) {
252 if (other.wrapped != null)
253 return false;
254 } else if (!wrapped.equals(other.wrapped))
255 return false;
256 return true;
257 }
258
259 @Override
260 public String toString() {
261 return "LayerChangeAdapter [wrapped=" + wrapped + ']';
262 }
263 }
264
265 /**
266 * This class is an adapter for the old layer change interface.
267 * <p>
268 * New implementations should use {@link org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener}
269 * @author Michael Zangl
270 * @since 10271
271 */
272 protected static class EditLayerChangeAdapter implements ActiveLayerChangeListener {
273
274 private final EditLayerChangeListener wrapped;
275
276 public EditLayerChangeAdapter(EditLayerChangeListener wrapped) {
277 this.wrapped = wrapped;
278 }
279
280 @Override
281 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
282 OsmDataLayer oldLayer = e.getPreviousEditLayer();
283 OsmDataLayer newLayer = e.getSource().getEditLayer();
284 if (oldLayer != newLayer) {
285 wrapped.editLayerChanged(oldLayer, newLayer);
286 }
287 }
288
289 @Override
290 public int hashCode() {
291 final int prime = 31;
292 int result = 1;
293 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
294 return result;
295 }
296
297 @Override
298 public boolean equals(Object obj) {
299 if (this == obj)
300 return true;
301 if (obj == null)
302 return false;
303 if (getClass() != obj.getClass())
304 return false;
305 EditLayerChangeAdapter other = (EditLayerChangeAdapter) obj;
306 if (wrapped == null) {
307 if (other.wrapped != null)
308 return false;
309 } else if (!wrapped.equals(other.wrapped))
310 return false;
311 return true;
312 }
313
314 @Override
315 public String toString() {
316 return "EditLayerChangeAdapter [wrapped=" + wrapped + ']';
317 }
318 }
319
320 /**
321 * A layer painter that issues a warning when being called.
322 * @author Michael Zangl
323 * @since 10474
324 */
325 private static class WarningLayerPainter implements LayerPainter {
326 boolean warningPrinted = false;
327 private Layer layer;
328
329 WarningLayerPainter(Layer layer) {
330 this.layer = layer;
331 }
332
333 @Override
334 public void paint(MapViewGraphics graphics) {
335 if (!warningPrinted) {
336 Main.debug("A layer triggered a repaint while being added: " + layer);
337 warningPrinted = true;
338 }
339 }
340
341 @Override
342 public void detachFromMapView(MapViewEvent event) {
343 // ignored
344 }
345 }
346
347 /**
348 * Removes a layer change listener
349 * <p>
350 * To be removed: end of 2016.
351 *
352 * @param listener the listener. Ignored if null or not registered.
353 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
354 */
355 @Deprecated
356 public static void removeLayerChangeListener(LayerChangeListener listener) {
357 LayerChangeAdapter adapter = new LayerChangeAdapter(listener);
358 try {
359 Main.getLayerManager().removeLayerChangeListener(adapter);
360 } catch (IllegalArgumentException e) {
361 // Ignored in old implementation
362 if (Main.isDebugEnabled()) {
363 Main.debug(e.getMessage());
364 }
365 }
366 try {
367 Main.getLayerManager().removeActiveLayerChangeListener(adapter);
368 } catch (IllegalArgumentException e) {
369 // Ignored in old implementation
370 if (Main.isDebugEnabled()) {
371 Main.debug(e.getMessage());
372 }
373 }
374 }
375
376 /**
377 * Removes an edit layer change listener
378 * <p>
379 * To be removed: end of 2016.
380 *
381 * @param listener the listener. Ignored if null or not registered.
382 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
383 */
384 @Deprecated
385 public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
386 try {
387 Main.getLayerManager().removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
388 } catch (IllegalArgumentException e) {
389 // Ignored in old implementation
390 if (Main.isDebugEnabled()) {
391 Main.debug(e.getMessage());
392 }
393 }
394 }
395
396 /**
397 * Adds a layer change listener
398 * <p>
399 * To be removed: end of 2016.
400 *
401 * @param listener the listener. Ignored if null or already registered.
402 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
403 */
404 @Deprecated
405 public static void addLayerChangeListener(LayerChangeListener listener) {
406 if (fireDeprecatedListenerOnAdd) {
407 Main.warn("Plugin seems to be adding listener during mapFrameInitialized(): " + BugReport.getCallingMethod(2)
408 + ". Layer listeners should be set on plugin load.");
409 }
410 addLayerChangeListener(listener, fireDeprecatedListenerOnAdd);
411 }
412
413 /**
414 * Adds a layer change listener
415 * <p>
416 * To be removed: end of 2016.
417 *
418 * @param listener the listener. Ignored if null or already registered.
419 * @param initialFire fire an active-layer-changed-event right after adding
420 * the listener in case there is a layer present (should be)
421 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
422 */
423 @Deprecated
424 public static void addLayerChangeListener(LayerChangeListener listener, boolean initialFire) {
425 if (listener != null) {
426 initialFire = initialFire && (Main.isDisplayingMapView() || fireDeprecatedListenerOnAdd);
427
428 LayerChangeAdapter adapter = new LayerChangeAdapter(listener, initialFire);
429 Main.getLayerManager().addLayerChangeListener(adapter, initialFire);
430 if (initialFire) {
431 Main.getLayerManager().addAndFireActiveLayerChangeListener(adapter);
432 } else {
433 Main.getLayerManager().addActiveLayerChangeListener(adapter);
434 }
435 adapter.receiveOneInitialFire = false;
436 }
437 }
438
439 /**
440 * Adds an edit layer change listener
441 * <p>
442 * To be removed: end of 2016.
443 *
444 * @param listener the listener. Ignored if null or already registered.
445 * @param initialFire fire an edit-layer-changed-event right after adding
446 * the listener in case there is an edit layer present
447 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
448 */
449 @Deprecated
450 public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
451 if (listener != null) {
452 boolean doFire = initialFire && Main.isDisplayingMapView() && Main.getLayerManager().getEditLayer() != null;
453 if (doFire) {
454 Main.getLayerManager().addAndFireActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
455 } else {
456 Main.getLayerManager().addActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
457 }
458 }
459 }
460
461 /**
462 * Adds an edit layer change listener
463 * <p>
464 * To be removed: end of 2016.
465 *
466 * @param listener the listener. Ignored if null or already registered.
467 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
468 */
469 @Deprecated
470 public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
471 addEditLayerChangeListener(listener, false);
472 }
473
474
475 /**
476 * Temporary. To be removed as soon as the {@link LayerChangeListener}s are removed.
477 * <p>
478 * Some plugins add their listeners in {@link Main#setMapFrame(MapFrame)}. This method is now called just after the first layer was added to
479 * the layer manager. So that listener would not receive the addition of the first layer. As long as this field is set, we fake an add call
480 * to that listener when it is added to immitate the old behaviour. You should not access it from anywhere else.
481 */
482 public static boolean fireDeprecatedListenerOnAdd;
483
484 public boolean viewportFollowing;
485
486 /**
487 * A list of all layers currently loaded. If we support multiple map views, this list may be different for each of them.
488 */
489 private final MainLayerManager layerManager;
490
491 /**
492 * The play head marker: there is only one of these so it isn't in any specific layer
493 */
494 public transient PlayHeadMarker playHeadMarker;
495
496 /**
497 * The last event performed by mouse.
498 */
499 public MouseEvent lastMEvent = new MouseEvent(this, 0, 0, 0, 0, 0, 0, false); // In case somebody reads it before first mouse move
500
501 /**
502 * Temporary layers (selection rectangle, etc.) that are never cached and
503 * drawn on top of regular layers.
504 * Access must be synchronized.
505 */
506 private final transient Set<MapViewPaintable> temporaryLayers = new LinkedHashSet<>();
507
508 private transient BufferedImage nonChangedLayersBuffer;
509 private transient BufferedImage offscreenBuffer;
510 // Layers that wasn't changed since last paint
511 private final transient List<Layer> nonChangedLayers = new ArrayList<>();
512 private transient Layer changedLayer;
513 private int lastViewID;
514 private boolean paintPreferencesChanged = true;
515 private Rectangle lastClipBounds = new Rectangle();
516 private transient MapMover mapMover;
517
518 /**
519 * The listener that listens to invalidations of all layers.
520 */
521 private final LayerInvalidatedListener invalidatedListener = new LayerInvalidatedListener();
522
523 /**
524 * This is a map of all Layers that have been added to this view.
525 */
526 private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
527
528 /**
529 * Constructs a new {@code MapView}.
530 * @param layerManager The layers to display.
531 * @param contentPane Ignored. Main content pane is used.
532 * @param viewportData the initial viewport of the map. Can be null, then
533 * the viewport is derived from the layer data.
534 * @since 10279
535 */
536 public MapView(MainLayerManager layerManager, final JPanel contentPane, final ViewportData viewportData) {
537 this.layerManager = layerManager;
538 initialViewport = viewportData;
539 layerManager.addLayerChangeListener(this, true);
540 layerManager.addActiveLayerChangeListener(this);
541 Main.pref.addPreferenceChangeListener(this);
542
543 addComponentListener(new ComponentAdapter() {
544 @Override
545 public void componentResized(ComponentEvent e) {
546 removeComponentListener(this);
547
548 mapMover = new MapMover(MapView.this, contentPane);
549 }
550 });
551
552 // listend to selection changes to redraw the map
553 DataSet.addSelectionListener(repaintSelectionChangedListener);
554
555 //store the last mouse action
556 this.addMouseMotionListener(new MouseMotionListener() {
557 @Override
558 public void mouseDragged(MouseEvent e) {
559 mouseMoved(e);
560 }
561
562 @Override
563 public void mouseMoved(MouseEvent e) {
564 lastMEvent = e;
565 }
566 });
567 this.addMouseListener(new MouseAdapter() {
568 @Override
569 public void mousePressed(MouseEvent me) {
570 // focus the MapView component when mouse is pressed inside it
571 requestFocus();
572 }
573 });
574
575 if (Shortcut.findShortcut(KeyEvent.VK_TAB, 0) != null) {
576 setFocusTraversalKeysEnabled(false);
577 }
578
579 for (JComponent c : getMapNavigationComponents(MapView.this)) {
580 add(c);
581 }
582 }
583
584 /**
585 * Adds the map navigation components to a
586 * @param forMapView The map view to get the components for.
587 * @return A list containing the correctly positioned map navigation components.
588 */
589 public static List<? extends JComponent> getMapNavigationComponents(MapView forMapView) {
590 MapSlider zoomSlider = new MapSlider(forMapView);
591 Dimension size = zoomSlider.getPreferredSize();
592 zoomSlider.setSize(size);
593 zoomSlider.setLocation(3, 0);
594 zoomSlider.setFocusTraversalKeysEnabled(Shortcut.findShortcut(KeyEvent.VK_TAB, 0) == null);
595
596 MapScaler scaler = new MapScaler(forMapView);
597 scaler.setPreferredLineLength(size.width - 10);
598 scaler.setSize(scaler.getPreferredSize());
599 scaler.setLocation(3, size.height);
600
601 return Arrays.asList(zoomSlider, scaler);
602 }
603
604 // remebered geometry of the component
605 private Dimension oldSize;
606 private Point oldLoc;
607
608 /**
609 * Call this method to keep map position on screen during next repaint
610 */
611 public void rememberLastPositionOnScreen() {
612 oldSize = getSize();
613 oldLoc = getLocationOnScreen();
614 }
615
616 /**
617 * Add a layer to the current MapView.
618 * <p>
619 * To be removed: end of 2016.
620 * @param layer The layer to add
621 * @deprecated Use {@link Main#getLayerManager()}.addLayer() instead.
622 */
623 @Deprecated
624 public void addLayer(Layer layer) {
625 layerManager.addLayer(layer);
626 }
627
628 @Override
629 public void layerAdded(LayerAddEvent e) {
630 try {
631 Layer layer = e.getAddedLayer();
632 registeredLayers.put(layer, new WarningLayerPainter(layer));
633 // Layers may trigger a redraw during this call if they open dialogs.
634 LayerPainter painter = layer.attachToMapView(new MapViewEvent(this, false));
635 if (!registeredLayers.containsKey(layer)) {
636 // The layer may have removed itself during attachToMapView()
637 Main.warn("Layer was removed during attachToMapView()");
638 } else {
639 registeredLayers.put(layer, painter);
640
641 ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
642 if (viewProjectionBounds != null) {
643 scheduleZoomTo(new ViewportData(viewProjectionBounds));
644 }
645
646 layer.addPropertyChangeListener(this);
647 Main.addProjectionChangeListener(layer);
648 invalidatedListener.addTo(layer);
649 AudioPlayer.reset();
650
651 repaint();
652 }
653 } catch (RuntimeException t) {
654 throw BugReport.intercept(t).put("layer", e.getAddedLayer());
655 }
656 }
657
658 /**
659 * Returns current data set. To be removed: end of 2016.
660 * @deprecated Use {@link #getLayerManager()}.getEditDataSet() instead.
661 */
662 @Override
663 @Deprecated
664 protected DataSet getCurrentDataSet() {
665 return layerManager.getEditDataSet();
666 }
667
668 /**
669 * Replies true if the active data layer (edit layer) is drawable.
670 *
671 * @return true if the active data layer (edit layer) is drawable, false otherwise
672 */
673 public boolean isActiveLayerDrawable() {
674 return getEditLayer() != null;
675 }
676
677 /**
678 * Replies true if the active data layer (edit layer) is visible.
679 *
680 * @return true if the active data layer (edit layer) is visible, false otherwise
681 */
682 public boolean isActiveLayerVisible() {
683 OsmDataLayer e = getEditLayer();
684 return e != null && e.isVisible();
685 }
686
687 /**
688 * Determines the next active data layer according to the following rules:
689 * <ul>
690 * <li>if there is at least one {@link OsmDataLayer} the first one
691 * becomes active</li>
692 * <li>otherwise, the top most layer of any type becomes active</li>
693 * </ul>
694 * To be removed: end of 2016.
695 * @param layersList lit of layers
696 *
697 * @return the next active data layer
698 * @deprecated now handled by {@link MainLayerManager}
699 */
700 @Deprecated
701 protected Layer determineNextActiveLayer(List<Layer> layersList) {
702 // First look for data layer
703 for (Layer layer:layersList) {
704 if (layer instanceof OsmDataLayer)
705 return layer;
706 }
707
708 // Then any layer
709 if (!layersList.isEmpty())
710 return layersList.get(0);
711
712 // and then give up
713 return null;
714 }
715
716 /**
717 * Remove the layer from the mapview. If the layer was in the list before,
718 * an LayerChange event is fired.
719 * <p>
720 * To be removed: end of 2016.
721 * @param layer The layer to remove
722 * @deprecated Use {@link Main#getLayerManager()}.removeLayer() instead.
723 */
724 @Deprecated
725 public void removeLayer(Layer layer) {
726 layerManager.removeLayer(layer);
727 }
728
729 @Override
730 public void layerRemoving(LayerRemoveEvent e) {
731 Layer layer = e.getRemovedLayer();
732
733 LayerPainter painter = registeredLayers.remove(layer);
734 if (painter == null) {
735 Main.error("The painter for layer " + layer + " was not registered.");
736 return;
737 }
738 painter.detachFromMapView(new MapViewEvent(this, false));
739 Main.removeProjectionChangeListener(layer);
740 layer.removePropertyChangeListener(this);
741 invalidatedListener.removeFrom(layer);
742 layer.destroy();
743 AudioPlayer.reset();
744
745 repaint();
746 }
747
748 private boolean virtualNodesEnabled;
749
750 public void setVirtualNodesEnabled(boolean enabled) {
751 if (virtualNodesEnabled != enabled) {
752 virtualNodesEnabled = enabled;
753 repaint();
754 }
755 }
756
757 /**
758 * Checks if virtual nodes should be drawn. Default is <code>false</code>
759 * @return The virtual nodes property.
760 * @see Rendering#render(DataSet, boolean, Bounds)
761 */
762 public boolean isVirtualNodesEnabled() {
763 return virtualNodesEnabled;
764 }
765
766 /**
767 * Moves the layer to the given new position. No event is fired, but repaints
768 * according to the new Z-Order of the layers.
769 *
770 * @param layer The layer to move
771 * @param pos The new position of the layer
772 */
773 public void moveLayer(Layer layer, int pos) {
774 layerManager.moveLayer(layer, pos);
775 }
776
777 @Override
778 public void layerOrderChanged(LayerOrderChangeEvent e) {
779 AudioPlayer.reset();
780 repaint();
781 }
782
783 /**
784 * Gets the index of the layer in the layer list.
785 * <p>
786 * To be removed: end of 2016.
787 * @param layer The layer to search for.
788 * @return The index in the list.
789 * @throws IllegalArgumentException if that layer does not belong to this view.
790 * @deprecated Access the layer list using {@link Main#getLayerManager()} instead.
791 */
792 @Deprecated
793 public int getLayerPos(Layer layer) {
794 int curLayerPos = layerManager.getLayers().indexOf(layer);
795 if (curLayerPos == -1)
796 throw new IllegalArgumentException(tr("Layer not in list."));
797 return curLayerPos;
798 }
799
800 /**
801 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
802 * first, layer with the highest Z-Order last.
803 * <p>
804 * The active data layer is pulled above all adjacent data layers.
805 * <p>
806 * To be removed: end of 2016.
807 *
808 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
809 * first, layer with the highest Z-Order last.
810 * @deprecated Access the layer list using {@link Main#getLayerManager()} instead.
811 */
812 @Deprecated
813 public List<Layer> getVisibleLayersInZOrder() {
814 return layerManager.getVisibleLayersInZOrder();
815 }
816
817 private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
818 try {
819 LayerPainter painter = registeredLayers.get(layer);
820 if (painter == null) {
821 throw new IllegalArgumentException("Cannot paint layer, it is not registered.");
822 }
823 MapViewRectangle clipBounds = getState().getViewArea(g.getClipBounds());
824 MapViewGraphics paintGraphics = new MapViewGraphics(this, g, clipBounds);
825
826 if (layer.getOpacity() < 1) {
827 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
828 }
829 painter.paint(paintGraphics);
830 g.setPaintMode();
831 } catch (RuntimeException t) {
832 //TODO: only display.
833 throw BugReport.intercept(t).put("layer", layer).put("bounds", box);
834 }
835 }
836
837 /**
838 * Draw the component.
839 */
840 @Override
841 public void paint(Graphics g) {
842 if (!prepareToDraw()) {
843 return;
844 }
845
846 List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
847
848 int nonChangedLayersCount = 0;
849 for (Layer l: visibleLayers) {
850 if (l.isChanged() || l == changedLayer) {
851 break;
852 } else {
853 nonChangedLayersCount++;
854 }
855 }
856
857 boolean canUseBuffer;
858
859 synchronized (this) {
860 canUseBuffer = !paintPreferencesChanged;
861 paintPreferencesChanged = false;
862 }
863 canUseBuffer = canUseBuffer && nonChangedLayers.size() <= nonChangedLayersCount &&
864 lastViewID == getViewID() && lastClipBounds.contains(g.getClipBounds());
865 if (canUseBuffer) {
866 for (int i = 0; i < nonChangedLayers.size(); i++) {
867 if (visibleLayers.get(i) != nonChangedLayers.get(i)) {
868 canUseBuffer = false;
869 break;
870 }
871 }
872 }
873
874 if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() || offscreenBuffer.getHeight() != getHeight()) {
875 offscreenBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
876 }
877
878 Graphics2D tempG = offscreenBuffer.createGraphics();
879 tempG.setClip(g.getClip());
880 Bounds box = getLatLonBounds(g.getClipBounds());
881
882 if (!canUseBuffer || nonChangedLayersBuffer == null) {
883 if (null == nonChangedLayersBuffer
884 || nonChangedLayersBuffer.getWidth() != getWidth() || nonChangedLayersBuffer.getHeight() != getHeight()) {
885 nonChangedLayersBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
886 }
887 Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
888 g2.setClip(g.getClip());
889 g2.setColor(PaintColors.getBackgroundColor());
890 g2.fillRect(0, 0, getWidth(), getHeight());
891
892 for (int i = 0; i < nonChangedLayersCount; i++) {
893 paintLayer(visibleLayers.get(i), g2, box);
894 }
895 } else {
896 // Maybe there were more unchanged layers then last time - draw them to buffer
897 if (nonChangedLayers.size() != nonChangedLayersCount) {
898 Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
899 g2.setClip(g.getClip());
900 for (int i = nonChangedLayers.size(); i < nonChangedLayersCount; i++) {
901 paintLayer(visibleLayers.get(i), g2, box);
902 }
903 }
904 }
905
906 nonChangedLayers.clear();
907 changedLayer = null;
908 for (int i = 0; i < nonChangedLayersCount; i++) {
909 nonChangedLayers.add(visibleLayers.get(i));
910 }
911 lastViewID = getViewID();
912 lastClipBounds = g.getClipBounds();
913
914 tempG.drawImage(nonChangedLayersBuffer, 0, 0, null);
915
916 for (int i = nonChangedLayersCount; i < visibleLayers.size(); i++) {
917 paintLayer(visibleLayers.get(i), tempG, box);
918 }
919
920 synchronized (temporaryLayers) {
921 for (MapViewPaintable mvp : temporaryLayers) {
922 try {
923 mvp.paint(tempG, this, box);
924 } catch (RuntimeException e) {
925 throw BugReport.intercept(e).put("mvp", mvp);
926 }
927 }
928 }
929
930 // draw world borders
931 try {
932 drawWorldBorders(tempG);
933 } catch (RuntimeException e) {
934 throw BugReport.intercept(e).put("bounds", getProjection()::getWorldBoundsLatLon);
935 }
936
937 if (Main.isDisplayingMapView() && Main.map.filterDialog != null) {
938 Main.map.filterDialog.drawOSDText(tempG);
939 }
940
941 if (playHeadMarker != null) {
942 playHeadMarker.paint(tempG, this);
943 }
944
945 try {
946 g.drawImage(offscreenBuffer, 0, 0, null);
947 } catch (ClassCastException e) {
948 // See #11002 and duplicate tickets. On Linux with Java >= 8 Many users face this error here:
949 //
950 // java.lang.ClassCastException: sun.awt.image.BufImgSurfaceData cannot be cast to sun.java2d.xr.XRSurfaceData
951 // at sun.java2d.xr.XRPMBlitLoops.cacheToTmpSurface(XRPMBlitLoops.java:145)
952 // at sun.java2d.xr.XrSwToPMBlit.Blit(XRPMBlitLoops.java:353)
953 // at sun.java2d.pipe.DrawImage.blitSurfaceData(DrawImage.java:959)
954 // at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:577)
955 // at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:67)
956 // at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:1014)
957 // at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:186)
958 // at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3318)
959 // at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3296)
960 // at org.openstreetmap.josm.gui.MapView.paint(MapView.java:834)
961 //
962 // It seems to be this JDK bug, but Oracle does not seem to be fixing it:
963 // https://bugs.openjdk.java.net/browse/JDK-7172749
964 //
965 // According to bug reports it can happen for a variety of reasons such as:
966 // - long period of time
967 // - change of screen resolution
968 // - addition/removal of a secondary monitor
969 //
970 // But the application seems to work fine after, so let's just log the error
971 Main.error(e);
972 }
973 super.paint(g);
974 }
975
976 private void drawWorldBorders(Graphics2D tempG) {
977 tempG.setColor(Color.WHITE);
978 Bounds b = getProjection().getWorldBoundsLatLon();
979 double lat = b.getMinLat();
980 double lon = b.getMinLon();
981
982 Point p = getPoint(b.getMin());
983
984 GeneralPath path = new GeneralPath();
985
986 double d = 1.0;
987 path.moveTo(p.x, p.y);
988 double max = b.getMax().lat();
989 for (; lat <= max; lat += d) {
990 p = getPoint(new LatLon(lat >= max ? max : lat, lon));
991 path.lineTo(p.x, p.y);
992 }
993 lat = max; max = b.getMax().lon();
994 for (; lon <= max; lon += d) {
995 p = getPoint(new LatLon(lat, lon >= max ? max : lon));
996 path.lineTo(p.x, p.y);
997 }
998 lon = max; max = b.getMinLat();
999 for (; lat >= max; lat -= d) {
1000 p = getPoint(new LatLon(lat <= max ? max : lat, lon));
1001 path.lineTo(p.x, p.y);
1002 }
1003 lat = max; max = b.getMinLon();
1004 for (; lon >= max; lon -= d) {
1005 p = getPoint(new LatLon(lat, lon <= max ? max : lon));
1006 path.lineTo(p.x, p.y);
1007 }
1008
1009 int w = getWidth();
1010 int h = getHeight();
1011
1012 // Work around OpenJDK having problems when drawing out of bounds
1013 final Area border = new Area(path);
1014 // Make the viewport 1px larger in every direction to prevent an
1015 // additional 1px border when zooming in
1016 final Area viewport = new Area(new Rectangle(-1, -1, w + 2, h + 2));
1017 border.intersect(viewport);
1018 tempG.draw(border);
1019 }
1020
1021 /**
1022 * Sets up the viewport to prepare for drawing the view.
1023 * @return <code>true</code> if the view can be drawn, <code>false</code> otherwise.
1024 */
1025 public boolean prepareToDraw() {
1026 updateLocationState();
1027 if (initialViewport != null) {
1028 zoomTo(initialViewport);
1029 initialViewport = null;
1030 }
1031 if (BugReportExceptionHandler.exceptionHandlingInProgress())
1032 return false;
1033
1034 if (getCenter() == null)
1035 return false; // no data loaded yet.
1036
1037 // if the position was remembered, we need to adjust center once before repainting
1038 if (oldLoc != null && oldSize != null) {
1039 Point l1 = getLocationOnScreen();
1040 final EastNorth newCenter = new EastNorth(
1041 getCenter().getX()+ (l1.x-oldLoc.x - (oldSize.width-getWidth())/2.0)*getScale(),
1042 getCenter().getY()+ (oldLoc.y-l1.y + (oldSize.height-getHeight())/2.0)*getScale()
1043 );
1044 oldLoc = null; oldSize = null;
1045 zoomTo(newCenter);
1046 }
1047
1048 return true;
1049 }
1050
1051 /**
1052 * Returns all layers. To be removed: end of 2016.
1053 *
1054 * @return An unmodifiable collection of all layers
1055 * @deprecated Use {@link LayerManager#getLayers()} instead.
1056 */
1057 @Deprecated
1058 public Collection<Layer> getAllLayers() {
1059 return layerManager.getLayers();
1060 }
1061
1062 /**
1063 * Returns all layers as list. To be removed: end of 2016.
1064 *
1065 * @return An unmodifiable ordered list of all layers
1066 * @deprecated Use {@link LayerManager#getLayers()} instead.
1067 */
1068 @Deprecated
1069 public List<Layer> getAllLayersAsList() {
1070 return layerManager.getLayers();
1071 }
1072
1073 /**
1074 * Replies an unmodifiable list of layers of a certain type. To be removed: end of 2016.
1075 *
1076 * Example:
1077 * <pre>
1078 * List&lt;WMSLayer&gt; wmsLayers = getLayersOfType(WMSLayer.class);
1079 * </pre>
1080 *
1081 * @param <T> layer type
1082 *
1083 * @param ofType The layer type.
1084 * @return an unmodifiable list of layers of a certain type.
1085 * @deprecated Use {@link LayerManager#getLayersOfType(Class)} instead.
1086 */
1087 @Deprecated
1088 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) {
1089 return layerManager.getLayersOfType(ofType);
1090 }
1091
1092 /**
1093 * Replies the number of layers managed by this map view. To be removed: end of 2016.
1094 * <p>
1095 *
1096 * @return the number of layers managed by this map view
1097 * @deprecated Use {@link Main#getLayerManager()}.getLayers().size() instead.
1098 */
1099 @Deprecated
1100 public int getNumLayers() {
1101 return getAllLayers().size();
1102 }
1103
1104 /**
1105 * Replies true if there is at least one layer in this map view
1106 * <p>
1107 *
1108 * @return true if there is at least one layer in this map view
1109 * @deprecated Use !{@link Main#getLayerManager()}.getLayers().isEmpty() instead.
1110 */
1111 @Deprecated
1112 public boolean hasLayers() {
1113 return getNumLayers() > 0;
1114 }
1115
1116 /**
1117 * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance
1118 * of {@link OsmDataLayer} also sets editLayer to <code>layer</code>.
1119 * <p>
1120 *
1121 * @param layer the layer to be activate; must be one of the layers in the list of layers
1122 * @throws IllegalArgumentException if layer is not in the list of layers
1123 * @deprecated Use !{@link Main#getLayerManager()}.setActiveLayer() instead.
1124 */
1125 @Deprecated
1126 public void setActiveLayer(Layer layer) {
1127 layerManager.setActiveLayer(layer);
1128 }
1129
1130 /**
1131 * Replies the currently active layer
1132 * <p>
1133 *
1134 * @return the currently active layer (may be null)
1135 * @deprecated Use !{@link Main#getLayerManager()}.getActiveLayer() instead.
1136 */
1137 @Deprecated
1138 public Layer getActiveLayer() {
1139 return layerManager.getActiveLayer();
1140 }
1141
1142 @Override
1143 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
1144 if (Main.map != null) {
1145 /* This only makes the buttons look disabled. Disabling the actions as well requires
1146 * the user to re-select the tool after i.e. moving a layer. While testing I found
1147 * that I switch layers and actions at the same time and it was annoying to mind the
1148 * order. This way it works as visual clue for new users */
1149 // FIXME: This does not belong here.
1150 for (final AbstractButton b: Main.map.allMapModeButtons) {
1151 MapMode mode = (MapMode) b.getAction();
1152 final boolean activeLayerSupported = mode.layerIsSupported(layerManager.getActiveLayer());
1153 if (activeLayerSupported) {
1154 Main.registerActionShortcut(mode, mode.getShortcut()); //fix #6876
1155 } else {
1156 Main.unregisterShortcut(mode.getShortcut());
1157 }
1158 b.setEnabled(activeLayerSupported);
1159 }
1160 }
1161 AudioPlayer.reset();
1162 repaint();
1163 }
1164
1165 /**
1166 * Replies the current edit layer, if any
1167 * <p>
1168 *
1169 * @return the current edit layer. May be null.
1170 * @deprecated Use !{@link Main#getLayerManager()}.getEditLayer() instead. To be made private: end of 2016.
1171 */
1172 @Deprecated
1173 public OsmDataLayer getEditLayer() {
1174 return layerManager.getEditLayer();
1175 }
1176
1177 /**
1178 * replies true if the list of layers managed by this map view contain layer
1179 * <p>
1180 *
1181 * @param layer the layer
1182 * @return true if the list of layers managed by this map view contain layer
1183 * @deprecated Use !{@link Main#getLayerManager()}.containsLayer() instead.
1184 */
1185 @Deprecated
1186 public boolean hasLayer(Layer layer) {
1187 return layerManager.containsLayer(layer);
1188 }
1189
1190 /**
1191 * Adds a new temporary layer.
1192 * <p>
1193 * A temporary layer is a layer that is painted above all normal layers. Layers are painted in the order they are added.
1194 *
1195 * @param mvp The layer to paint.
1196 * @return <code>true</code> if the layer was added.
1197 */
1198 public boolean addTemporaryLayer(MapViewPaintable mvp) {
1199 synchronized (temporaryLayers) {
1200 boolean added = temporaryLayers.add(mvp);
1201 if (added) {
1202 invalidatedListener.addTo(mvp);
1203 }
1204 return added;
1205 }
1206 }
1207
1208 /**
1209 * Removes a layer previously added as temporary layer.
1210 * @param mvp The layer to remove.
1211 * @return <code>true</code> if that layer was removed.
1212 */
1213 public boolean removeTemporaryLayer(MapViewPaintable mvp) {
1214 synchronized (temporaryLayers) {
1215 boolean removed = temporaryLayers.remove(mvp);
1216 if (removed) {
1217 invalidatedListener.removeFrom(mvp);
1218 }
1219 return removed;
1220 }
1221 }
1222
1223 /**
1224 * Gets a list of temporary layers.
1225 * @return The layers in the order they are added.
1226 */
1227 public List<MapViewPaintable> getTemporaryLayers() {
1228 synchronized (temporaryLayers) {
1229 return Collections.unmodifiableList(new ArrayList<>(temporaryLayers));
1230 }
1231 }
1232
1233 @Override
1234 public void propertyChange(PropertyChangeEvent evt) {
1235 if (evt.getPropertyName().equals(Layer.VISIBLE_PROP)) {
1236 repaint();
1237 } else if (evt.getPropertyName().equals(Layer.OPACITY_PROP) ||
1238 evt.getPropertyName().equals(Layer.FILTER_STATE_PROP)) {
1239 Layer l = (Layer) evt.getSource();
1240 if (l.isVisible()) {
1241 changedLayer = l;
1242 repaint();
1243 }
1244 }
1245 }
1246
1247 /**
1248 * Sets the title of the JOSM main window, adding a star if there are dirty layers.
1249 * @see Main#parent
1250 * @deprecated Replaced by {@link MainFrame#refreshTitle()}. The {@link MainFrame} should handle this by itself.
1251 */
1252 @Deprecated
1253 protected void refreshTitle() {
1254 if (Main.parent != null) {
1255 ((MainFrame) Main.parent).refreshTitle();
1256 }
1257 }
1258
1259 @Override
1260 public void preferenceChanged(PreferenceChangeEvent e) {
1261 synchronized (this) {
1262 paintPreferencesChanged = true;
1263 }
1264 }
1265
1266 private final transient SelectionChangedListener repaintSelectionChangedListener = new SelectionChangedListener() {
1267 @Override
1268 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
1269 repaint();
1270 }
1271 };
1272
1273 /**
1274 * Destroy this map view panel. Should be called once when it is not needed any more.
1275 */
1276 public void destroy() {
1277 layerManager.removeLayerChangeListener(this, true);
1278 layerManager.removeActiveLayerChangeListener(this);
1279 Main.pref.removePreferenceChangeListener(this);
1280 DataSet.removeSelectionListener(repaintSelectionChangedListener);
1281 MultipolygonCache.getInstance().clear(this);
1282 if (mapMover != null) {
1283 mapMover.destroy();
1284 }
1285 nonChangedLayers.clear();
1286 synchronized (temporaryLayers) {
1287 temporaryLayers.clear();
1288 }
1289 nonChangedLayersBuffer = null;
1290 }
1291
1292 /**
1293 * Get a string representation of all layers suitable for the {@code source} changeset tag.
1294 * @return A String of sources separated by ';'
1295 */
1296 public String getLayerInformationForSourceTag() {
1297 final Collection<String> layerInfo = new ArrayList<>();
1298 if (!getLayersOfType(GpxLayer.class).isEmpty()) {
1299 // no i18n for international values
1300 layerInfo.add("survey");
1301 }
1302 for (final GeoImageLayer i : getLayersOfType(GeoImageLayer.class)) {
1303 if (i.isVisible()) {
1304 layerInfo.add(i.getName());
1305 }
1306 }
1307 for (final ImageryLayer i : getLayersOfType(ImageryLayer.class)) {
1308 if (i.isVisible()) {
1309 layerInfo.add(ImageryInfo.ImageryType.BING.equals(i.getInfo().getImageryType()) ? "Bing" : i.getName());
1310 }
1311 }
1312 return Utils.join("; ", layerInfo);
1313 }
1314
1315 /**
1316 * This is a listener that gets informed whenever repaint is called for this MapView.
1317 * <p>
1318 * This is the only safe method to find changes to the map view, since many components call MapView.repaint() directly.
1319 * @author Michael Zangl
1320 */
1321 public interface RepaintListener {
1322 /**
1323 * Called when any repaint method is called (using default arguments if required).
1324 * @param tm see {@link JComponent#repaint(long, int, int, int, int)}
1325 * @param x see {@link JComponent#repaint(long, int, int, int, int)}
1326 * @param y see {@link JComponent#repaint(long, int, int, int, int)}
1327 * @param width see {@link JComponent#repaint(long, int, int, int, int)}
1328 * @param height see {@link JComponent#repaint(long, int, int, int, int)}
1329 */
1330 void repaint(long tm, int x, int y, int width, int height);
1331 }
1332
1333 private final transient CopyOnWriteArrayList<RepaintListener> repaintListeners = new CopyOnWriteArrayList<>();
1334
1335 /**
1336 * Adds a listener that gets informed whenever repaint() is called for this class.
1337 * @param l The listener.
1338 */
1339 public void addRepaintListener(RepaintListener l) {
1340 repaintListeners.add(l);
1341 }
1342
1343 /**
1344 * Removes a registered repaint listener.
1345 * @param l The listener.
1346 */
1347 public void removeRepaintListener(RepaintListener l) {
1348 repaintListeners.remove(l);
1349 }
1350
1351 @Override
1352 public void repaint(long tm, int x, int y, int width, int height) {
1353 // This is the main repaint method, all other methods are convenience methods and simply call this method.
1354 // This is just an observation, not a must, but seems to be true for all implementations I found so far.
1355 if (repaintListeners != null) {
1356 // Might get called early in super constructor
1357 for (RepaintListener l : repaintListeners) {
1358 l.repaint(tm, x, y, width, height);
1359 }
1360 }
1361 super.repaint(tm, x, y, width, height);
1362 }
1363
1364 @Override
1365 public void repaint() {
1366 if (Main.isTraceEnabled()) {
1367 invalidatedListener.traceRandomRepaint();
1368 }
1369 super.repaint();
1370 }
1371
1372 /**
1373 * Returns the layer manager.
1374 * @return the layer manager
1375 * @since 10282
1376 */
1377 public final MainLayerManager getLayerManager() {
1378 return layerManager;
1379 }
1380
1381 /**
1382 * Schedule a zoom to the given position on the next redraw.
1383 * Temporary, may be removed without warning.
1384 * @param viewportData the viewport to zoom to
1385 * @since 10394
1386 */
1387 public void scheduleZoomTo(ViewportData viewportData) {
1388 initialViewport = viewportData;
1389 }
1390}
Note: See TracBrowser for help on using the repository browser.