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

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

fix #13237 - Should not duplicate layer names in the changeset source

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