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

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

fix #13175 - Use invalidation event instead of isChanged() (patch by michael2402) - gsoc-core

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