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

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

sonar - squid:S1226 - Method parameters, caught exceptions and foreach variables should not be reassigned

  • Property svn:eol-style set to native
File size: 49.4 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;
79
80/**
81 * This is a component used in the {@link MapFrame} for browsing the map. It use is to
82 * provide the MapMode's enough capabilities to operate.<br><br>
83 *
84 * {@code MapView} holds meta-data about the data set currently displayed, as scale level,
85 * center point viewed, what scrolling mode or editing mode is selected or with
86 * what projection the map is viewed etc..<br><br>
87 *
88 * {@code MapView} is able to administrate several layers.
89 *
90 * @author imi
91 */
92public class MapView extends NavigatableComponent
93implements PropertyChangeListener, PreferenceChangedListener,
94LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
95 /**
96 * Interface to notify listeners of a layer change.
97 * <p>
98 * To be removed: end of 2016.
99 * @deprecated Use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener} instead.
100 * @author imi
101 */
102 @Deprecated
103 public interface LayerChangeListener {
104
105 /**
106 * Notifies this listener that the active layer has changed.
107 * @param oldLayer The previous active layer
108 * @param newLayer The new activer layer
109 */
110 void activeLayerChange(Layer oldLayer, Layer newLayer);
111
112 /**
113 * Notifies this listener that a layer has been added.
114 * @param newLayer The new added layer
115 */
116 void layerAdded(Layer newLayer);
117
118 /**
119 * Notifies this listener that a layer has been removed.
120 * @param oldLayer The old removed layer
121 */
122 void layerRemoved(Layer oldLayer);
123 }
124
125 /**
126 * An interface that needs to be implemented in order to listen for changes to the active edit layer.
127 * <p>
128 * To be removed: end of 2016.
129 * @deprecated Use {@link ActiveLayerChangeListener} instead.
130 */
131 @Deprecated
132 @FunctionalInterface
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
151 private final Set<MapViewPaintable> invalidatedLayers = Collections.newSetFromMap(new IdentityHashMap<MapViewPaintable, Boolean>());
152
153 @Override
154 public void paintableInvalidated(PaintableInvalidationEvent event) {
155 invalidate(event.getLayer());
156 }
157
158 /**
159 * Invalidate contents and repaint map view
160 * @param mapViewPaintable invalidated layer
161 */
162 public synchronized void invalidate(MapViewPaintable mapViewPaintable) {
163 ignoreRepaint = true;
164 invalidatedLayers.add(mapViewPaintable);
165 repaint();
166 }
167
168 /**
169 * Temporary until all {@link MapViewPaintable}s support this.
170 * @param p The paintable.
171 */
172 public synchronized void addTo(MapViewPaintable p) {
173 if (p instanceof AbstractMapViewPaintable) {
174 ((AbstractMapViewPaintable) p).addInvalidationListener(this);
175 }
176 }
177
178 /**
179 * Temporary until all {@link MapViewPaintable}s support this.
180 * @param p The paintable.
181 */
182 public synchronized void removeFrom(MapViewPaintable p) {
183 if (p instanceof AbstractMapViewPaintable) {
184 ((AbstractMapViewPaintable) p).removeInvalidationListener(this);
185 }
186 invalidatedLayers.remove(p);
187 }
188
189 /**
190 * Attempts to trace repaints that did not originate from this listener. Good to find missed {@link MapView#repaint()}s in code.
191 */
192 protected synchronized void traceRandomRepaint() {
193 if (!ignoreRepaint) {
194 System.err.println("Repaint:");
195 Thread.dumpStack();
196 }
197 ignoreRepaint = false;
198 }
199
200 /**
201 * Retrieves a set of all layers that have been marked as invalid since the last call to this method.
202 * @return The layers
203 */
204 protected synchronized Set<MapViewPaintable> collectInvalidatedLayers() {
205 Set<MapViewPaintable> layers = Collections.newSetFromMap(new IdentityHashMap<MapViewPaintable, Boolean>());
206 layers.addAll(invalidatedLayers);
207 invalidatedLayers.clear();
208 return layers;
209 }
210 }
211
212 /**
213 * This class is an adapter for the old layer change interface.
214 * @author Michael Zangl
215 * @since 10271
216 * @deprecated New implementations should use {@link org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener}
217 */
218 @Deprecated
219 protected static class LayerChangeAdapter implements ActiveLayerChangeListener, LayerManager.LayerChangeListener {
220
221 private final LayerChangeListener wrapped;
222 private boolean receiveOneInitialFire;
223
224 public LayerChangeAdapter(LayerChangeListener wrapped) {
225 this.wrapped = wrapped;
226 }
227
228 public LayerChangeAdapter(LayerChangeListener wrapped, boolean initialFire) {
229 this(wrapped);
230 this.receiveOneInitialFire = initialFire;
231 }
232
233 @Override
234 public void layerAdded(LayerAddEvent e) {
235 wrapped.layerAdded(e.getAddedLayer());
236 }
237
238 @Override
239 public void layerRemoving(LayerRemoveEvent e) {
240 wrapped.layerRemoved(e.getRemovedLayer());
241 }
242
243 @Override
244 public void layerOrderChanged(LayerOrderChangeEvent e) {
245 // not in old API
246 }
247
248 @Override
249 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
250 Layer oldActive = receiveOneInitialFire ? null : e.getPreviousActiveLayer();
251 Layer newActive = e.getSource().getActiveLayer();
252 if (oldActive != newActive) {
253 wrapped.activeLayerChange(oldActive, newActive);
254 }
255 receiveOneInitialFire = false;
256 }
257
258 @Override
259 public int hashCode() {
260 final int prime = 31;
261 int result = 1;
262 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
263 return result;
264 }
265
266 @Override
267 public boolean equals(Object obj) {
268 if (this == obj)
269 return true;
270 if (obj == null)
271 return false;
272 if (getClass() != obj.getClass())
273 return false;
274 LayerChangeAdapter other = (LayerChangeAdapter) obj;
275 if (wrapped == null) {
276 if (other.wrapped != null)
277 return false;
278 } else if (!wrapped.equals(other.wrapped))
279 return false;
280 return true;
281 }
282
283 @Override
284 public String toString() {
285 return "LayerChangeAdapter [wrapped=" + wrapped + ']';
286 }
287 }
288
289 /**
290 * This class is an adapter for the old layer change interface.
291 * @author Michael Zangl
292 * @since 10271
293 * @deprecated New implementations should use {@link org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener}
294 */
295 @Deprecated
296 protected static class EditLayerChangeAdapter implements ActiveLayerChangeListener {
297
298 private final EditLayerChangeListener wrapped;
299
300 public EditLayerChangeAdapter(EditLayerChangeListener wrapped) {
301 this.wrapped = wrapped;
302 }
303
304 @Override
305 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
306 OsmDataLayer oldLayer = e.getPreviousEditLayer();
307 OsmDataLayer newLayer = e.getSource().getEditLayer();
308 if (oldLayer != newLayer) {
309 wrapped.editLayerChanged(oldLayer, newLayer);
310 }
311 }
312
313 @Override
314 public int hashCode() {
315 final int prime = 31;
316 int result = 1;
317 result = prime * result + ((wrapped == null) ? 0 : wrapped.hashCode());
318 return result;
319 }
320
321 @Override
322 public boolean equals(Object obj) {
323 if (this == obj)
324 return true;
325 if (obj == null)
326 return false;
327 if (getClass() != obj.getClass())
328 return false;
329 EditLayerChangeAdapter other = (EditLayerChangeAdapter) obj;
330 if (wrapped == null) {
331 if (other.wrapped != null)
332 return false;
333 } else if (!wrapped.equals(other.wrapped))
334 return false;
335 return true;
336 }
337
338 @Override
339 public String toString() {
340 return "EditLayerChangeAdapter [wrapped=" + wrapped + ']';
341 }
342 }
343
344 /**
345 * A layer painter that issues a warning when being called.
346 * @author Michael Zangl
347 * @since 10474
348 */
349 private static class WarningLayerPainter implements LayerPainter {
350 boolean warningPrinted;
351 private final Layer layer;
352
353 WarningLayerPainter(Layer layer) {
354 this.layer = layer;
355 }
356
357 @Override
358 public void paint(MapViewGraphics graphics) {
359 if (!warningPrinted) {
360 Main.debug("A layer triggered a repaint while being added: " + layer);
361 warningPrinted = true;
362 }
363 }
364
365 @Override
366 public void detachFromMapView(MapViewEvent event) {
367 // ignored
368 }
369 }
370
371 /**
372 * Removes a layer change listener
373 * <p>
374 * To be removed: end of 2016.
375 *
376 * @param listener the listener. Ignored if null or not registered.
377 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
378 */
379 @Deprecated
380 public static void removeLayerChangeListener(LayerChangeListener listener) {
381 LayerChangeAdapter adapter = new LayerChangeAdapter(listener);
382 try {
383 Main.getLayerManager().removeLayerChangeListener(adapter);
384 } catch (IllegalArgumentException e) {
385 // Ignored in old implementation
386 Main.debug(e);
387 }
388 try {
389 Main.getLayerManager().removeActiveLayerChangeListener(adapter);
390 } catch (IllegalArgumentException e) {
391 // Ignored in old implementation
392 Main.debug(e);
393 }
394 }
395
396 /**
397 * Removes an edit layer change listener
398 * <p>
399 * To be removed: end of 2016.
400 *
401 * @param listener the listener. Ignored if null or not registered.
402 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
403 */
404 @Deprecated
405 public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
406 try {
407 Main.getLayerManager().removeActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
408 } catch (IllegalArgumentException e) {
409 // Ignored in old implementation
410 Main.debug(e);
411 }
412 }
413
414 /**
415 * Adds a layer change listener
416 * <p>
417 * To be removed: end of 2016.
418 *
419 * @param listener the listener. Ignored if null or already registered.
420 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
421 */
422 @Deprecated
423 public static void addLayerChangeListener(LayerChangeListener listener) {
424 if (fireDeprecatedListenerOnAdd) {
425 Main.warn("Plugin seems to be adding listener during mapFrameInitialized(): " + BugReport.getCallingMethod(2)
426 + ". Layer listeners should be set on plugin load.");
427 }
428 addLayerChangeListener(listener, fireDeprecatedListenerOnAdd);
429 }
430
431 /**
432 * Adds a layer change listener
433 * <p>
434 * To be removed: end of 2016.
435 *
436 * @param listener the listener. Ignored if null or already registered.
437 * @param initialFire fire an active-layer-changed-event right after adding
438 * the listener in case there is a layer present (should be)
439 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
440 */
441 @Deprecated
442 public static void addLayerChangeListener(LayerChangeListener listener, boolean initialFire) {
443 if (listener != null) {
444 boolean doInitialFire = initialFire && (Main.isDisplayingMapView() || fireDeprecatedListenerOnAdd);
445
446 LayerChangeAdapter adapter = new LayerChangeAdapter(listener, doInitialFire);
447 Main.getLayerManager().addLayerChangeListener(adapter, doInitialFire);
448 if (doInitialFire) {
449 Main.getLayerManager().addAndFireActiveLayerChangeListener(adapter);
450 } else {
451 Main.getLayerManager().addActiveLayerChangeListener(adapter);
452 }
453 adapter.receiveOneInitialFire = false;
454 }
455 }
456
457 /**
458 * Adds an edit layer change listener
459 * <p>
460 * To be removed: end of 2016.
461 *
462 * @param listener the listener. Ignored if null or already registered.
463 * @param initialFire fire an edit-layer-changed-event right after adding
464 * the listener in case there is an edit layer present
465 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
466 */
467 @Deprecated
468 public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
469 if (listener != null) {
470 boolean doFire = initialFire && Main.isDisplayingMapView() && Main.getLayerManager().getEditLayer() != null;
471 if (doFire) {
472 Main.getLayerManager().addAndFireActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
473 } else {
474 Main.getLayerManager().addActiveLayerChangeListener(new EditLayerChangeAdapter(listener));
475 }
476 }
477 }
478
479 /**
480 * Adds an edit layer change listener
481 * <p>
482 * To be removed: end of 2016.
483 *
484 * @param listener the listener. Ignored if null or already registered.
485 * @deprecated You should register the listener on {@link Main#getLayerManager()} instead.
486 */
487 @Deprecated
488 public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
489 addEditLayerChangeListener(listener, false);
490 }
491
492
493 /**
494 * Temporary. To be removed as soon as the {@link LayerChangeListener}s are removed.
495 * <p>
496 * Some plugins add their listeners in {@link Main#setMapFrame(MapFrame)}. This method is now called just after the first layer was added to
497 * 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
498 * to that listener when it is added to immitate the old behaviour. You should not access it from anywhere else.
499 */
500 public static boolean fireDeprecatedListenerOnAdd;
501
502 public boolean viewportFollowing;
503
504 /**
505 * A list of all layers currently loaded. If we support multiple map views, this list may be different for each of them.
506 */
507 private final MainLayerManager layerManager;
508
509 /**
510 * The play head marker: there is only one of these so it isn't in any specific layer
511 */
512 public transient PlayHeadMarker playHeadMarker;
513
514 /**
515 * The last event performed by mouse.
516 */
517 public MouseEvent lastMEvent = new MouseEvent(this, 0, 0, 0, 0, 0, 0, false); // In case somebody reads it before first mouse move
518
519 /**
520 * Temporary layers (selection rectangle, etc.) that are never cached and
521 * drawn on top of regular layers.
522 * Access must be synchronized.
523 */
524 private final transient Set<MapViewPaintable> temporaryLayers = new LinkedHashSet<>();
525
526 private transient BufferedImage nonChangedLayersBuffer;
527 private transient BufferedImage offscreenBuffer;
528 // Layers that wasn't changed since last paint
529 private final transient List<Layer> nonChangedLayers = new ArrayList<>();
530 private int lastViewID;
531 private boolean paintPreferencesChanged = true;
532 private Rectangle lastClipBounds = new Rectangle();
533 private transient MapMover mapMover;
534
535 /**
536 * The listener that listens to invalidations of all layers.
537 */
538 private final LayerInvalidatedListener invalidatedListener = new LayerInvalidatedListener();
539
540 /**
541 * This is a map of all Layers that have been added to this view.
542 */
543 private final HashMap<Layer, LayerPainter> registeredLayers = new HashMap<>();
544
545 /**
546 * Constructs a new {@code MapView}.
547 * @param layerManager The layers to display.
548 * @param contentPane Ignored. Main content pane is used.
549 * @param viewportData the initial viewport of the map. Can be null, then
550 * the viewport is derived from the layer data.
551 * @since 10279
552 */
553 public MapView(MainLayerManager layerManager, final JPanel contentPane, final ViewportData viewportData) {
554 this.layerManager = layerManager;
555 initialViewport = viewportData;
556 layerManager.addLayerChangeListener(this, true);
557 layerManager.addActiveLayerChangeListener(this);
558 Main.pref.addPreferenceChangeListener(this);
559
560 addComponentListener(new ComponentAdapter() {
561 @Override
562 public void componentResized(ComponentEvent e) {
563 removeComponentListener(this);
564
565 mapMover = new MapMover(MapView.this, contentPane);
566 }
567 });
568
569 // listend to selection changes to redraw the map
570 DataSet.addSelectionListener(repaintSelectionChangedListener);
571
572 //store the last mouse action
573 this.addMouseMotionListener(new MouseMotionListener() {
574 @Override
575 public void mouseDragged(MouseEvent e) {
576 mouseMoved(e);
577 }
578
579 @Override
580 public void mouseMoved(MouseEvent e) {
581 lastMEvent = e;
582 }
583 });
584 this.addMouseListener(new MouseAdapter() {
585 @Override
586 public void mousePressed(MouseEvent me) {
587 // focus the MapView component when mouse is pressed inside it
588 requestFocus();
589 }
590 });
591
592 if (Shortcut.findShortcut(KeyEvent.VK_TAB, 0) != null) {
593 setFocusTraversalKeysEnabled(false);
594 }
595
596 for (JComponent c : getMapNavigationComponents(this)) {
597 add(c);
598 }
599 setTransferHandler(new OsmTransferHandler());
600 }
601
602 /**
603 * Adds the map navigation components to a
604 * @param forMapView The map view to get the components for.
605 * @return A list containing the correctly positioned map navigation components.
606 */
607 public static List<? extends JComponent> getMapNavigationComponents(MapView forMapView) {
608 MapSlider zoomSlider = new MapSlider(forMapView);
609 Dimension size = zoomSlider.getPreferredSize();
610 zoomSlider.setSize(size);
611 zoomSlider.setLocation(3, 0);
612 zoomSlider.setFocusTraversalKeysEnabled(Shortcut.findShortcut(KeyEvent.VK_TAB, 0) == null);
613
614 MapScaler scaler = new MapScaler(forMapView);
615 scaler.setPreferredLineLength(size.width - 10);
616 scaler.setSize(scaler.getPreferredSize());
617 scaler.setLocation(3, size.height);
618
619 return Arrays.asList(zoomSlider, scaler);
620 }
621
622 // remebered geometry of the component
623 private Dimension oldSize;
624 private Point oldLoc;
625
626 /**
627 * Call this method to keep map position on screen during next repaint
628 */
629 public void rememberLastPositionOnScreen() {
630 oldSize = getSize();
631 oldLoc = getLocationOnScreen();
632 }
633
634 /**
635 * Add a layer to the current MapView.
636 * <p>
637 * To be removed: end of 2016.
638 * @param layer The layer to add
639 * @deprecated Use {@link Main#getLayerManager()}.addLayer() instead.
640 */
641 @Deprecated
642 public void addLayer(Layer layer) {
643 layerManager.addLayer(layer);
644 }
645
646 @Override
647 public void layerAdded(LayerAddEvent e) {
648 try {
649 Layer layer = e.getAddedLayer();
650 registeredLayers.put(layer, new WarningLayerPainter(layer));
651 // Layers may trigger a redraw during this call if they open dialogs.
652 LayerPainter painter = layer.attachToMapView(new MapViewEvent(this, false));
653 if (!registeredLayers.containsKey(layer)) {
654 // The layer may have removed itself during attachToMapView()
655 Main.warn("Layer was removed during attachToMapView()");
656 } else {
657 registeredLayers.put(layer, painter);
658
659 ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
660 if (viewProjectionBounds != null) {
661 scheduleZoomTo(new ViewportData(viewProjectionBounds));
662 }
663
664 layer.addPropertyChangeListener(this);
665 Main.addProjectionChangeListener(layer);
666 invalidatedListener.addTo(layer);
667 AudioPlayer.reset();
668
669 repaint();
670 }
671 } catch (RuntimeException t) {
672 throw BugReport.intercept(t).put("layer", e.getAddedLayer());
673 }
674 }
675
676 /**
677 * Returns current data set. To be removed: end of 2016.
678 * @deprecated Use {@link #getLayerManager()}.getEditDataSet() instead.
679 */
680 @Override
681 @Deprecated
682 protected DataSet getCurrentDataSet() {
683 return layerManager.getEditDataSet();
684 }
685
686 /**
687 * Replies true if the active data layer (edit layer) is drawable.
688 *
689 * @return true if the active data layer (edit layer) is drawable, false otherwise
690 */
691 public boolean isActiveLayerDrawable() {
692 return layerManager.getEditLayer() != null;
693 }
694
695 /**
696 * Replies true if the active data layer (edit layer) is visible.
697 *
698 * @return true if the active data layer (edit layer) is visible, false otherwise
699 */
700 public boolean isActiveLayerVisible() {
701 OsmDataLayer e = layerManager.getEditLayer();
702 return e != null && e.isVisible();
703 }
704
705 /**
706 * Determines the next active data layer according to the following rules:
707 * <ul>
708 * <li>if there is at least one {@link OsmDataLayer} the first one
709 * becomes active</li>
710 * <li>otherwise, the top most layer of any type becomes active</li>
711 * </ul>
712 * To be removed: end of 2016.
713 * @param layersList lit of layers
714 *
715 * @return the next active data layer
716 * @deprecated now handled by {@link MainLayerManager}
717 */
718 @Deprecated
719 protected Layer determineNextActiveLayer(List<Layer> layersList) {
720 // First look for data layer
721 for (Layer layer:layersList) {
722 if (layer instanceof OsmDataLayer)
723 return layer;
724 }
725
726 // Then any layer
727 if (!layersList.isEmpty())
728 return layersList.get(0);
729
730 // and then give up
731 return null;
732 }
733
734 /**
735 * Remove the layer from the mapview. If the layer was in the list before,
736 * an LayerChange event is fired.
737 * <p>
738 * To be removed: end of 2016.
739 * @param layer The layer to remove
740 * @deprecated Use {@link Main#getLayerManager()}.removeLayer() instead.
741 */
742 @Deprecated
743 public void removeLayer(Layer layer) {
744 layerManager.removeLayer(layer);
745 }
746
747 @Override
748 public void layerRemoving(LayerRemoveEvent e) {
749 Layer layer = e.getRemovedLayer();
750
751 LayerPainter painter = registeredLayers.remove(layer);
752 if (painter == null) {
753 Main.error("The painter for layer " + layer + " was not registered.");
754 return;
755 }
756 painter.detachFromMapView(new MapViewEvent(this, false));
757 Main.removeProjectionChangeListener(layer);
758 layer.removePropertyChangeListener(this);
759 invalidatedListener.removeFrom(layer);
760 layer.destroy();
761 AudioPlayer.reset();
762
763 repaint();
764 }
765
766 private boolean virtualNodesEnabled;
767
768 public void setVirtualNodesEnabled(boolean enabled) {
769 if (virtualNodesEnabled != enabled) {
770 virtualNodesEnabled = enabled;
771 repaint();
772 }
773 }
774
775 /**
776 * Checks if virtual nodes should be drawn. Default is <code>false</code>
777 * @return The virtual nodes property.
778 * @see Rendering#render(DataSet, boolean, Bounds)
779 */
780 public boolean isVirtualNodesEnabled() {
781 return virtualNodesEnabled;
782 }
783
784 /**
785 * Moves the layer to the given new position. No event is fired, but repaints
786 * according to the new Z-Order of the layers.
787 *
788 * @param layer The layer to move
789 * @param pos The new position of the layer
790 */
791 public void moveLayer(Layer layer, int pos) {
792 layerManager.moveLayer(layer, pos);
793 }
794
795 @Override
796 public void layerOrderChanged(LayerOrderChangeEvent e) {
797 AudioPlayer.reset();
798 repaint();
799 }
800
801 /**
802 * Gets the index of the layer in the layer list.
803 * <p>
804 * To be removed: end of 2016.
805 * @param layer The layer to search for.
806 * @return The index in the list.
807 * @throws IllegalArgumentException if that layer does not belong to this view.
808 * @deprecated Access the layer list using {@link Main#getLayerManager()} instead.
809 */
810 @Deprecated
811 public int getLayerPos(Layer layer) {
812 int curLayerPos = layerManager.getLayers().indexOf(layer);
813 if (curLayerPos == -1)
814 throw new IllegalArgumentException(tr("Layer not in list."));
815 return curLayerPos;
816 }
817
818 /**
819 * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
820 * first, layer with the highest Z-Order last.
821 * <p>
822 * The active data layer is pulled above all adjacent data layers.
823 * <p>
824 * To be removed: end of 2016.
825 *
826 * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
827 * first, layer with the highest Z-Order last.
828 * @deprecated Access the layer list using {@link Main#getLayerManager()} instead.
829 */
830 @Deprecated
831 public List<Layer> getVisibleLayersInZOrder() {
832 return layerManager.getVisibleLayersInZOrder();
833 }
834
835 private void paintLayer(Layer layer, Graphics2D g, Bounds box) {
836 try {
837 LayerPainter painter = registeredLayers.get(layer);
838 if (painter == null) {
839 throw new IllegalArgumentException("Cannot paint layer, it is not registered.");
840 }
841 MapViewRectangle clipBounds = getState().getViewArea(g.getClipBounds());
842 MapViewGraphics paintGraphics = new MapViewGraphics(this, g, clipBounds);
843
844 if (layer.getOpacity() < 1) {
845 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) layer.getOpacity()));
846 }
847 painter.paint(paintGraphics);
848 g.setPaintMode();
849 } catch (RuntimeException t) {
850 BugReport.intercept(t).put("layer", layer).put("bounds", box).warn();
851 }
852 }
853
854 /**
855 * Draw the component.
856 */
857 @Override
858 public void paint(Graphics g) {
859 try {
860 if (!prepareToDraw()) {
861 return;
862 }
863 } catch (RuntimeException e) {
864 BugReport.intercept(e).put("center", this::getCenter).warn();
865 return;
866 }
867
868 List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
869
870 int nonChangedLayersCount = 0;
871 Set<MapViewPaintable> invalidated = invalidatedListener.collectInvalidatedLayers();
872 for (Layer l: visibleLayers) {
873 if (l.isChanged() || invalidated.contains(l)) {
874 break;
875 } else {
876 nonChangedLayersCount++;
877 }
878 }
879
880 boolean canUseBuffer;
881
882 synchronized (this) {
883 canUseBuffer = !paintPreferencesChanged;
884 paintPreferencesChanged = false;
885 }
886 canUseBuffer = canUseBuffer && nonChangedLayers.size() <= nonChangedLayersCount &&
887 lastViewID == getViewID() && lastClipBounds.contains(g.getClipBounds());
888 if (canUseBuffer) {
889 for (int i = 0; i < nonChangedLayers.size(); i++) {
890 if (visibleLayers.get(i) != nonChangedLayers.get(i)) {
891 canUseBuffer = false;
892 break;
893 }
894 }
895 }
896
897 if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() || offscreenBuffer.getHeight() != getHeight()) {
898 offscreenBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
899 }
900
901 Graphics2D tempG = offscreenBuffer.createGraphics();
902 tempG.setClip(g.getClip());
903 Bounds box = getLatLonBounds(g.getClipBounds());
904
905 if (!canUseBuffer || nonChangedLayersBuffer == null) {
906 if (null == nonChangedLayersBuffer
907 || nonChangedLayersBuffer.getWidth() != getWidth() || nonChangedLayersBuffer.getHeight() != getHeight()) {
908 nonChangedLayersBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
909 }
910 Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
911 g2.setClip(g.getClip());
912 g2.setColor(PaintColors.getBackgroundColor());
913 g2.fillRect(0, 0, getWidth(), getHeight());
914
915 for (int i = 0; i < nonChangedLayersCount; i++) {
916 paintLayer(visibleLayers.get(i), g2, box);
917 }
918 } else {
919 // Maybe there were more unchanged layers then last time - draw them to buffer
920 if (nonChangedLayers.size() != nonChangedLayersCount) {
921 Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
922 g2.setClip(g.getClip());
923 for (int i = nonChangedLayers.size(); i < nonChangedLayersCount; i++) {
924 paintLayer(visibleLayers.get(i), g2, box);
925 }
926 }
927 }
928
929 nonChangedLayers.clear();
930 for (int i = 0; i < nonChangedLayersCount; i++) {
931 nonChangedLayers.add(visibleLayers.get(i));
932 }
933 lastViewID = getViewID();
934 lastClipBounds = g.getClipBounds();
935
936 tempG.drawImage(nonChangedLayersBuffer, 0, 0, null);
937
938 for (int i = nonChangedLayersCount; i < visibleLayers.size(); i++) {
939 paintLayer(visibleLayers.get(i), tempG, box);
940 }
941
942 try {
943 drawTemporaryLayers(tempG, box);
944 } catch (RuntimeException e) {
945 BugReport.intercept(e).put("temporaryLayers", temporaryLayers).warn();
946 }
947
948 // draw world borders
949 try {
950 drawWorldBorders(tempG);
951 } catch (RuntimeException e) {
952 // getProjection() needs to be inside lambda to catch errors.
953 BugReport.intercept(e).put("bounds", () -> getProjection().getWorldBoundsLatLon()).warn();
954 }
955
956 if (Main.isDisplayingMapView() && Main.map.filterDialog != null) {
957 Main.map.filterDialog.drawOSDText(tempG);
958 }
959
960 if (playHeadMarker != null) {
961 playHeadMarker.paint(tempG, this);
962 }
963
964 try {
965 g.drawImage(offscreenBuffer, 0, 0, null);
966 } catch (ClassCastException e) {
967 // See #11002 and duplicate tickets. On Linux with Java >= 8 Many users face this error here:
968 //
969 // java.lang.ClassCastException: sun.awt.image.BufImgSurfaceData cannot be cast to sun.java2d.xr.XRSurfaceData
970 // at sun.java2d.xr.XRPMBlitLoops.cacheToTmpSurface(XRPMBlitLoops.java:145)
971 // at sun.java2d.xr.XrSwToPMBlit.Blit(XRPMBlitLoops.java:353)
972 // at sun.java2d.pipe.DrawImage.blitSurfaceData(DrawImage.java:959)
973 // at sun.java2d.pipe.DrawImage.renderImageCopy(DrawImage.java:577)
974 // at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:67)
975 // at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:1014)
976 // at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:186)
977 // at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3318)
978 // at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:3296)
979 // at org.openstreetmap.josm.gui.MapView.paint(MapView.java:834)
980 //
981 // It seems to be this JDK bug, but Oracle does not seem to be fixing it:
982 // https://bugs.openjdk.java.net/browse/JDK-7172749
983 //
984 // According to bug reports it can happen for a variety of reasons such as:
985 // - long period of time
986 // - change of screen resolution
987 // - addition/removal of a secondary monitor
988 //
989 // But the application seems to work fine after, so let's just log the error
990 Main.error(e);
991 }
992 super.paint(g);
993 }
994
995 private void drawTemporaryLayers(Graphics2D tempG, Bounds box) {
996 synchronized (temporaryLayers) {
997 for (MapViewPaintable mvp : temporaryLayers) {
998 try {
999 mvp.paint(tempG, this, box);
1000 } catch (RuntimeException e) {
1001 throw BugReport.intercept(e).put("mvp", mvp);
1002 }
1003 }
1004 }
1005 }
1006
1007 private void drawWorldBorders(Graphics2D tempG) {
1008 tempG.setColor(Color.WHITE);
1009 Bounds b = getProjection().getWorldBoundsLatLon();
1010
1011 int w = getWidth();
1012 int h = getHeight();
1013
1014 // Work around OpenJDK having problems when drawing out of bounds
1015 final Area border = getState().getArea(b);
1016 // Make the viewport 1px larger in every direction to prevent an
1017 // additional 1px border when zooming in
1018 final Area viewport = new Area(new Rectangle(-1, -1, w + 2, h + 2));
1019 border.intersect(viewport);
1020 tempG.draw(border);
1021 }
1022
1023 /**
1024 * Sets up the viewport to prepare for drawing the view.
1025 * @return <code>true</code> if the view can be drawn, <code>false</code> otherwise.
1026 */
1027 public boolean prepareToDraw() {
1028 updateLocationState();
1029 if (initialViewport != null) {
1030 zoomTo(initialViewport);
1031 initialViewport = null;
1032 }
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 invalidatedListener.invalidate(l);
1242 }
1243 }
1244 }
1245
1246 /**
1247 * Sets the title of the JOSM main window, adding a star if there are dirty layers.
1248 * @see Main#parent
1249 * @deprecated Replaced by {@link MainFrame#refreshTitle()}. The {@link MainFrame} should handle this by itself.
1250 */
1251 @Deprecated
1252 protected void refreshTitle() {
1253 if (Main.parent != null) {
1254 ((MainFrame) Main.parent).refreshTitle();
1255 }
1256 }
1257
1258 @Override
1259 public void preferenceChanged(PreferenceChangeEvent e) {
1260 synchronized (this) {
1261 paintPreferencesChanged = true;
1262 }
1263 }
1264
1265 private final transient SelectionChangedListener repaintSelectionChangedListener = newSelection -> repaint();
1266
1267 /**
1268 * Destroy this map view panel. Should be called once when it is not needed any more.
1269 */
1270 public void destroy() {
1271 layerManager.removeLayerChangeListener(this, true);
1272 layerManager.removeActiveLayerChangeListener(this);
1273 Main.pref.removePreferenceChangeListener(this);
1274 DataSet.removeSelectionListener(repaintSelectionChangedListener);
1275 MultipolygonCache.getInstance().clear(this);
1276 if (mapMover != null) {
1277 mapMover.destroy();
1278 }
1279 nonChangedLayers.clear();
1280 synchronized (temporaryLayers) {
1281 temporaryLayers.clear();
1282 }
1283 nonChangedLayersBuffer = null;
1284 offscreenBuffer = null;
1285 }
1286
1287 /**
1288 * Get a string representation of all layers suitable for the {@code source} changeset tag.
1289 * @return A String of sources separated by ';'
1290 */
1291 public String getLayerInformationForSourceTag() {
1292 final Set<String> layerInfo = new TreeSet<>();
1293 if (!layerManager.getLayersOfType(GpxLayer.class).isEmpty()) {
1294 // no i18n for international values
1295 layerInfo.add("survey");
1296 }
1297 for (final GeoImageLayer i : layerManager.getLayersOfType(GeoImageLayer.class)) {
1298 if (i.isVisible()) {
1299 layerInfo.add(i.getName());
1300 }
1301 }
1302 for (final ImageryLayer i : layerManager.getLayersOfType(ImageryLayer.class)) {
1303 if (i.isVisible()) {
1304 layerInfo.add(ImageryInfo.ImageryType.BING.equals(i.getInfo().getImageryType()) ? "Bing" : i.getName());
1305 }
1306 }
1307 return Utils.join("; ", layerInfo);
1308 }
1309
1310 /**
1311 * This is a listener that gets informed whenever repaint is called for this MapView.
1312 * <p>
1313 * This is the only safe method to find changes to the map view, since many components call MapView.repaint() directly.
1314 * @author Michael Zangl
1315 * @since 10600 (functional interface)
1316 */
1317 @FunctionalInterface
1318 public interface RepaintListener {
1319 /**
1320 * Called when any repaint method is called (using default arguments if required).
1321 * @param tm see {@link JComponent#repaint(long, int, int, int, int)}
1322 * @param x see {@link JComponent#repaint(long, int, int, int, int)}
1323 * @param y see {@link JComponent#repaint(long, int, int, int, int)}
1324 * @param width see {@link JComponent#repaint(long, int, int, int, int)}
1325 * @param height see {@link JComponent#repaint(long, int, int, int, int)}
1326 */
1327 void repaint(long tm, int x, int y, int width, int height);
1328 }
1329
1330 private final transient CopyOnWriteArrayList<RepaintListener> repaintListeners = new CopyOnWriteArrayList<>();
1331
1332 /**
1333 * Adds a listener that gets informed whenever repaint() is called for this class.
1334 * @param l The listener.
1335 */
1336 public void addRepaintListener(RepaintListener l) {
1337 repaintListeners.add(l);
1338 }
1339
1340 /**
1341 * Removes a registered repaint listener.
1342 * @param l The listener.
1343 */
1344 public void removeRepaintListener(RepaintListener l) {
1345 repaintListeners.remove(l);
1346 }
1347
1348 @Override
1349 public void repaint(long tm, int x, int y, int width, int height) {
1350 // This is the main repaint method, all other methods are convenience methods and simply call this method.
1351 // This is just an observation, not a must, but seems to be true for all implementations I found so far.
1352 if (repaintListeners != null) {
1353 // Might get called early in super constructor
1354 for (RepaintListener l : repaintListeners) {
1355 l.repaint(tm, x, y, width, height);
1356 }
1357 }
1358 super.repaint(tm, x, y, width, height);
1359 }
1360
1361 @Override
1362 public void repaint() {
1363 if (Main.isTraceEnabled()) {
1364 invalidatedListener.traceRandomRepaint();
1365 }
1366 super.repaint();
1367 }
1368
1369 /**
1370 * Returns the layer manager.
1371 * @return the layer manager
1372 * @since 10282
1373 */
1374 public final MainLayerManager getLayerManager() {
1375 return layerManager;
1376 }
1377
1378 /**
1379 * Schedule a zoom to the given position on the next redraw.
1380 * Temporary, may be removed without warning.
1381 * @param viewportData the viewport to zoom to
1382 * @since 10394
1383 */
1384 public void scheduleZoomTo(ViewportData viewportData) {
1385 initialViewport = viewportData;
1386 }
1387}
Note: See TracBrowser for help on using the repository browser.