root/trunk/src/org/openstreetmap/josm/gui/MapView.java @ 3125

Revision 3125, 26.7 KB (checked in by jttt, 6 months ago)

Fix #4723 Error on load, on zoom

  • Property svn:eol-style set to native
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui;
4
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Color;
8import java.awt.Graphics;
9import java.awt.Graphics2D;
10import java.awt.Point;
11import java.awt.Rectangle;
12import java.awt.event.ComponentAdapter;
13import java.awt.event.ComponentEvent;
14import java.awt.event.MouseEvent;
15import java.awt.event.MouseMotionListener;
16import java.awt.geom.Area;
17import java.awt.geom.GeneralPath;
18import java.awt.image.BufferedImage;
19import java.beans.PropertyChangeEvent;
20import java.beans.PropertyChangeListener;
21import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Comparator;
25import java.util.Enumeration;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.concurrent.CopyOnWriteArrayList;
29
30import javax.swing.AbstractButton;
31import javax.swing.JComponent;
32import javax.swing.JOptionPane;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.AutoScaleAction;
36import org.openstreetmap.josm.actions.JosmAction;
37import org.openstreetmap.josm.actions.MoveAction;
38import org.openstreetmap.josm.actions.mapmode.MapMode;
39import org.openstreetmap.josm.data.Bounds;
40import org.openstreetmap.josm.data.SelectionChangedListener;
41import org.openstreetmap.josm.data.coor.LatLon;
42import org.openstreetmap.josm.data.osm.DataSet;
43import org.openstreetmap.josm.data.osm.DataSource;
44import org.openstreetmap.josm.data.osm.OsmPrimitive;
45import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
46import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
47import org.openstreetmap.josm.gui.layer.GpxLayer;
48import org.openstreetmap.josm.gui.layer.Layer;
49import org.openstreetmap.josm.gui.layer.MapViewPaintable;
50import org.openstreetmap.josm.gui.layer.OsmDataLayer;
51import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
52import org.openstreetmap.josm.gui.layer.markerlayer.PlayHeadMarker;
53import org.openstreetmap.josm.tools.AudioPlayer;
54
55/**
56 * This is a component used in the MapFrame for browsing the map. It use is to
57 * provide the MapMode's enough capabilities to operate.
58 *
59 * MapView hold meta-data about the data set currently displayed, as scale level,
60 * center point viewed, what scrolling mode or editing mode is selected or with
61 * what projection the map is viewed etc..
62 *
63 * MapView is able to administrate several layers.
64 *
65 * @author imi
66 */
67public class MapView extends NavigatableComponent implements PropertyChangeListener {
68
69    /**
70     * Interface to notify listeners of the change of the active layer.
71     * @author imi
72     */
73    public interface LayerChangeListener {
74        void activeLayerChange(Layer oldLayer, Layer newLayer);
75        void layerAdded(Layer newLayer);
76        void layerRemoved(Layer oldLayer);
77    }
78
79    public interface EditLayerChangeListener {
80        void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer);
81    }
82
83    /**
84     * the layer listeners
85     */
86    private static final CopyOnWriteArrayList<MapView.LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<MapView.LayerChangeListener>();
87    private static final CopyOnWriteArrayList<EditLayerChangeListener> editLayerChangeListeners = new CopyOnWriteArrayList<EditLayerChangeListener>();
88
89    /**
90     * Removes a layer change listener
91     *
92     * @param listener the listener. Ignored if null or already registered.
93     */
94    public static void removeLayerChangeListener(MapView.LayerChangeListener listener) {
95        layerChangeListeners.remove(listener);
96    }
97
98    public static void removeEditLayerChangeListener(EditLayerChangeListener listener) {
99        editLayerChangeListeners.remove(listener);
100    }
101
102    /**
103     * Adds a layer change listener
104     *
105     * @param listener the listener. Ignored if null or already registered.
106     */
107    public static void addLayerChangeListener(MapView.LayerChangeListener listener) {
108        if (listener != null) {
109            layerChangeListeners.addIfAbsent(listener);
110        }
111    }
112
113    /**
114     * Adds a edit layer change listener
115     *
116     * @param listener the listener. Ignored if null or already registered.
117     * @param initialFire Fire an edit-layer-changed-event right after adding the listener.
118     */
119    public static void addEditLayerChangeListener(EditLayerChangeListener listener, boolean initialFire) {
120        addEditLayerChangeListener(listener);
121        if (initialFire) {
122            if (Main.map != null && Main.map.mapView != null && Main.map.mapView.getEditLayer() != null) {
123                fireEditLayerChanged(null, Main.map.mapView.getEditLayer());
124            }
125        }
126    }
127
128    public static void addEditLayerChangeListener(EditLayerChangeListener listener) {
129        if (listener != null) {
130            editLayerChangeListeners.addIfAbsent(listener);
131        }
132    }
133
134    protected static void fireActiveLayerChanged(Layer oldLayer, Layer newLayer) {
135        for (LayerChangeListener l : layerChangeListeners) {
136            l.activeLayerChange(oldLayer, newLayer);
137        }
138    }
139
140    protected static void fireLayerAdded(Layer newLayer) {
141        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
142            l.layerAdded(newLayer);
143        }
144    }
145
146    protected static void fireLayerRemoved(Layer layer) {
147        for (MapView.LayerChangeListener l : MapView.layerChangeListeners) {
148            l.layerRemoved(layer);
149        }
150    }
151
152    protected static void fireEditLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
153        for (EditLayerChangeListener l : editLayerChangeListeners) {
154            l.editLayerChanged(oldLayer, newLayer);
155        }
156    }
157
158    /**
159     * A list of all layers currently loaded.
160     */
161    private final List<Layer> layers = new ArrayList<Layer>();
162    /**
163     * The play head marker: there is only one of these so it isn't in any specific layer
164     */
165    public PlayHeadMarker playHeadMarker = null;
166
167    /**
168     * The layer from the layers list that is currently active.
169     */
170    private Layer activeLayer;
171
172    private OsmDataLayer editLayer;
173
174    /**
175     * The last event performed by mouse.
176     */
177    public MouseEvent lastMEvent;
178
179    private LinkedList<MapViewPaintable> temporaryLayers = new LinkedList<MapViewPaintable>();
180
181    private BufferedImage offscreenBuffer;
182    // Layers that wasn't changed since last paint
183    private final List<Layer> nonChangedLayers = new ArrayList<Layer>();
184    private int lastViewID;
185
186    public MapView() {
187        addComponentListener(new ComponentAdapter(){
188            @Override public void componentResized(ComponentEvent e) {
189                removeComponentListener(this);
190
191                MapSlider zoomSlider = new MapSlider(MapView.this);
192                add(zoomSlider);
193                zoomSlider.setBounds(3, 0, 114, 30);
194
195                MapScaler scaler = new MapScaler(MapView.this);
196                add(scaler);
197                scaler.setLocation(10,30);
198
199                OsmDataLayer layer = getEditLayer();
200                if (layer != null) {
201                    if (!zoomToDataSetBoundingBox(layer.data)) {
202                        // no bounding box defined
203                        new AutoScaleAction("data").actionPerformed(null);
204                    }
205                } else {
206                    new AutoScaleAction("layer").actionPerformed(null);
207                }
208
209                new MapMover(MapView.this, Main.contentPane);
210                JosmAction mv;
211                mv = new MoveAction(MoveAction.Direction.UP);
212                if (mv.getShortcut() != null) {
213                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "UP");
214                    Main.contentPane.getActionMap().put("UP", mv);
215                }
216                mv = new MoveAction(MoveAction.Direction.DOWN);
217                if (mv.getShortcut() != null) {
218                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "DOWN");
219                    Main.contentPane.getActionMap().put("DOWN", mv);
220                }
221                mv = new MoveAction(MoveAction.Direction.LEFT);
222                if (mv.getShortcut() != null) {
223                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "LEFT");
224                    Main.contentPane.getActionMap().put("LEFT", mv);
225                }
226                mv = new MoveAction(MoveAction.Direction.RIGHT);
227                if (mv.getShortcut() != null) {
228                    Main.contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(mv.getShortcut().getKeyStroke(), "RIGHT");
229                    Main.contentPane.getActionMap().put("RIGHT", mv);
230                }
231            }
232        });
233
234        // listend to selection changes to redraw the map
235        DataSet.selListeners.add(new SelectionChangedListener(){
236            public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
237                repaint();
238            }
239        });
240
241        //store the last mouse action
242        this.addMouseMotionListener(new MouseMotionListener() {
243            public void mouseDragged(MouseEvent e) {
244                mouseMoved(e);
245            }
246            public void mouseMoved(MouseEvent e) {
247                lastMEvent = e;
248            }
249        });
250    }
251
252    /**
253     * Adds a GPX layer. A GPX layer is added below the lowest data layer.
254     *
255     * @param layer the GPX layer
256     */
257    protected void addGpxLayer(GpxLayer layer) {
258        if (layers.isEmpty()) {
259            layers.add(layer);
260            return;
261        }
262        for (int i=layers.size()-1; i>= 0; i--) {
263            if (layers.get(i) instanceof OsmDataLayer) {
264                if (i == layers.size()-1) {
265                    layers.add(layer);
266                } else {
267                    layers.add(i+1, layer);
268                }
269                return;
270            }
271        }
272        layers.add(layer);
273    }
274
275    /**
276     * Add a layer to the current MapView. The layer will be added at topmost
277     * position.
278     */
279    public void addLayer(Layer layer) {
280        if (layer instanceof MarkerLayer && playHeadMarker == null) {
281            playHeadMarker = PlayHeadMarker.create();
282        }
283
284        if (layer instanceof GpxLayer) {
285            addGpxLayer((GpxLayer)layer);
286        } else if (layer.isBackgroundLayer() || layers.isEmpty()) {
287            layers.add(layer);
288        } else {
289            layers.add(0, layer);
290        }
291        fireLayerAdded(layer);
292        if (layer instanceof OsmDataLayer || activeLayer == null) {
293            // autoselect the new layer
294            setActiveLayer(layer);
295        }
296        layer.addPropertyChangeListener(this);
297        AudioPlayer.reset();
298        repaint();
299    }
300
301    @Override
302    protected DataSet getCurrentDataSet() {
303        if (editLayer != null)
304            return editLayer.data;
305        else
306            return null;
307    }
308
309    /**
310     * Replies true if the active layer is drawable.
311     *
312     * @return true if the active layer is drawable, false otherwise
313     */
314    public boolean isActiveLayerDrawable() {
315        return editLayer != null;
316    }
317
318    /**
319     * Replies true if the active layer is visible.
320     *
321     * @return true if the active layer is visible, false otherwise
322     */
323    public boolean isActiveLayerVisible() {
324        return isActiveLayerDrawable() && editLayer.isVisible();
325    }
326
327    /**
328     * Determines the next active data layer according to the following
329     * rules:
330     * <ul>
331     *   <li>if there is at least one {@see OsmDataLayer} the first one
332     *     becomes active</li>
333     *   <li>otherwise, the top most layer of any type becomes active</li>
334     * </ul>
335     *
336     * @return the next active data layer
337     */
338    protected Layer determineNextActiveLayer(List<Layer> layersList) {
339        // First look for data layer
340        for (Layer layer:layersList) {
341            if (layer instanceof OsmDataLayer)
342                return layer;
343        }
344
345        // Then any layer
346        if (!layersList.isEmpty())
347            return layersList.get(0);
348
349        // and then give up
350        return null;
351
352    }
353
354    /**
355     * Remove the layer from the mapview. If the layer was in the list before,
356     * an LayerChange event is fired.
357     */
358    public void removeLayer(Layer layer) {
359        List<Layer> layersList = new ArrayList<Layer>(layers);
360
361        if (!layersList.remove(layer))
362            return;
363
364        setEditLayer(layersList);
365
366        if (layer == activeLayer) {
367            setActiveLayer(determineNextActiveLayer(layersList), false);
368        }
369
370        layers.remove(layer);
371        fireLayerRemoved(layer);
372        layer.removePropertyChangeListener(this);
373        layer.destroy();
374        AudioPlayer.reset();
375        repaint();
376    }
377
378    private boolean virtualNodesEnabled = false;
379
380    public void setVirtualNodesEnabled(boolean enabled) {
381        if(virtualNodesEnabled != enabled) {
382            virtualNodesEnabled = enabled;
383            repaint();
384        }
385    }
386    public boolean isVirtualNodesEnabled() {
387        return virtualNodesEnabled;
388    }
389
390    /**
391     * Moves the layer to the given new position. No event is fired, but repaints
392     * according to the new Z-Order of the layers.
393     *
394     * @param layer     The layer to move
395     * @param pos       The new position of the layer
396     */
397    public void moveLayer(Layer layer, int pos) {
398        int curLayerPos = layers.indexOf(layer);
399        if (curLayerPos == -1)
400            throw new IllegalArgumentException(tr("Layer not in list."));
401        if (pos == curLayerPos)
402            return; // already in place.
403        layers.remove(curLayerPos);
404        if (pos >= layers.size()) {
405            layers.add(layer);
406        } else {
407            layers.add(pos, layer);
408        }
409        setEditLayer(layers);
410        AudioPlayer.reset();
411        repaint();
412    }
413
414    public int getLayerPos(Layer layer) {
415        int curLayerPos = layers.indexOf(layer);
416        if (curLayerPos == -1)
417            throw new IllegalArgumentException(tr("Layer not in list."));
418        return curLayerPos;
419    }
420
421    /**
422     * Creates a list of the visible layers in Z-Order, the layer with the lowest Z-Order
423     * first, layer with the highest Z-Order last.
424     *
425     * @return a list of the visible in Z-Order, the layer with the lowest Z-Order
426     * first, layer with the highest Z-Order last.
427     */
428    protected List<Layer> getVisibleLayersInZOrder() {
429        ArrayList<Layer> ret = new ArrayList<Layer>();
430        for (Layer l: layers) {
431            if (l.isVisible()) {
432                ret.add(l);
433            }
434        }
435        // sort according to position in the list of layers, with one exception:
436        // an active data layer always becomes a higher Z-Order than all other
437        // data layers
438        //
439        Collections.sort(
440                ret,
441                new Comparator<Layer>() {
442                    public int compare(Layer l1, Layer l2) {
443                        if (l1 instanceof OsmDataLayer && l2 instanceof OsmDataLayer) {
444                            if (l1 == getActiveLayer()) return -1;
445                            if (l2 == getActiveLayer()) return 1;
446                            return Integer.valueOf(layers.indexOf(l1)).compareTo(layers.indexOf(l2));
447                        } else
448                            return Integer.valueOf(layers.indexOf(l1)).compareTo(layers.indexOf(l2));
449                    }
450                }
451        );
452        Collections.reverse(ret);
453        return ret;
454    }
455
456    /**
457     * Draw the component.
458     */
459    @Override public void paint(Graphics g) {
460        if (center == null)
461            return; // no data loaded yet.
462
463        List<Layer> visibleLayers = getVisibleLayersInZOrder();
464
465        int nonChangedLayersCount = 0;
466        for (Layer l: visibleLayers) {
467            if (l.isChanged()) {
468                break;
469            } else {
470                nonChangedLayersCount++;
471            }
472        }
473
474        boolean canUseBuffer = nonChangedLayers.size() <= nonChangedLayersCount && lastViewID == getViewID();
475        if (canUseBuffer) {
476            for (int i=0; i<nonChangedLayers.size(); i++) {
477                if (visibleLayers.get(i) != nonChangedLayers.get(i)) {
478                    canUseBuffer = false;
479                    break;
480                }
481            }
482        }
483
484        Graphics2D tempG = (Graphics2D) g;
485        Bounds box = getLatLonBounds(g.getClipBounds());
486
487        if (!canUseBuffer || offscreenBuffer == null) {
488            if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() || offscreenBuffer.getHeight() != getHeight()) {
489                offscreenBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
490            }
491            Graphics2D g2 = offscreenBuffer.createGraphics();
492            g2.setClip(g.getClip());
493            g2.setColor(PaintColors.BACKGROUND.get());
494            g2.fillRect(0, 0, getWidth(), getHeight());
495
496            for (int i=0; i<nonChangedLayersCount; i++) {
497                visibleLayers.get(i).paint(g2, this, box);
498            }
499        } else {
500            // Maybe there were more unchanged layers then last time - draw them to buffer
501            if (nonChangedLayers.size() != nonChangedLayersCount) {
502                Graphics2D g2 = offscreenBuffer.createGraphics();
503                g2.setClip(g.getClip());
504                for (int i=nonChangedLayers.size(); i<nonChangedLayersCount; i++) {
505                    visibleLayers.get(i).paint(g2, this, box);
506                }
507            }
508        }
509
510        nonChangedLayers.clear();
511        for (int i=0; i<nonChangedLayersCount; i++) {
512            nonChangedLayers.add(visibleLayers.get(i));
513        }
514        lastViewID = getViewID();
515
516        tempG.drawImage(offscreenBuffer, 0, 0, null);
517
518        for (int i=nonChangedLayersCount; i<visibleLayers.size(); i++) {
519            visibleLayers.get(i).paint(tempG, this, box);
520        }
521
522        for (MapViewPaintable mvp : temporaryLayers) {
523            mvp.paint(tempG, this, box);
524        }
525
526        // draw world borders
527        tempG.setColor(Color.WHITE);
528        Bounds b = getProjection().getWorldBoundsLatLon();
529        double lat = b.getMin().lat();
530        double lon = b.getMin().lon();
531
532        Point p = getPoint(b.getMin());
533
534        GeneralPath path = new GeneralPath();
535
536        path.moveTo(p.x, p.y);
537        double max = b.getMax().lat();
538        for(; lat <= max; lat += 1.0)
539        {
540            p = getPoint(new LatLon(lat >= max ? max : lat, lon));
541            path.lineTo(p.x, p.y);
542        }
543        lat = max; max = b.getMax().lon();
544        for(; lon <= max; lon += 1.0)
545        {
546            p = getPoint(new LatLon(lat, lon >= max ? max : lon));
547            path.lineTo(p.x, p.y);
548        }
549        lon = max; max = b.getMin().lat();
550        for(; lat >= max; lat -= 1.0)
551        {
552            p = getPoint(new LatLon(lat <= max ? max : lat, lon));
553            path.lineTo(p.x, p.y);
554        }
555        lat = max; max = b.getMin().lon();
556        for(; lon >= max; lon -= 1.0)
557        {
558            p = getPoint(new LatLon(lat, lon <= max ? max : lon));
559            path.lineTo(p.x, p.y);
560        }
561
562        int w = getWidth();
563        int h = getHeight();
564
565        // Work around OpenJDK having problems when drawing out of bounds
566        final Area border = new Area(path);
567        // Make the viewport 1px larger in every direction to prevent an
568        // additional 1px border when zooming in
569        final Area viewport = new Area(new Rectangle(-1, -1, w + 2, h + 2));
570        border.intersect(viewport);
571        tempG.draw(border);
572
573        if (playHeadMarker != null) {
574            playHeadMarker.paint(tempG, this);
575        }
576
577        super.paint(g);
578    }
579
580    /**
581     * Set the new dimension to the view.
582     */
583    public void recalculateCenterScale(BoundingXYVisitor box) {
584        if (box == null) {
585            box = new BoundingXYVisitor();
586        }
587        if (box.getBounds() == null) {
588            box.visit(getProjection().getWorldBoundsLatLon());
589        }
590        if (!box.hasExtend()) {
591            box.enlargeBoundingBox();
592        }
593
594        zoomTo(box.getBounds());
595    }
596
597    /**
598     * @return An unmodifiable collection of all layers
599     */
600    public Collection<Layer> getAllLayers() {
601        return Collections.unmodifiableCollection(layers);
602    }
603
604    /**
605     * @return An unmodifiable ordered list of all layers
606     */
607    public List<Layer> getAllLayersAsList() {
608        return Collections.unmodifiableList(layers);
609    }
610
611    /**
612     * Replies an unmodifiable list of layers of a certain type.
613     *
614     * Example:
615     * <pre>
616     *     List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class);
617     * </pre>
618     *
619     * @return an unmodifiable list of layers of a certain type.
620     */
621    public <T> List<T>  getLayersOfType(Class<T> ofType) {
622        ArrayList<T> ret = new ArrayList<T>();
623        for (Layer layer : getAllLayersAsList()) {
624            if (ofType.isInstance(layer)) {
625                ret.add(ofType.cast(layer));
626            }
627        }
628        return ret;
629    }
630
631    /**
632     * Replies the number of layers managed by this mav view
633     *
634     * @return the number of layers managed by this mav view
635     */
636    public int getNumLayers() {
637        return layers.size();
638    }
639
640    /**
641     * Replies true if there is at least one layer in this map view
642     *
643     * @return true if there is at least one layer in this map view
644     */
645    public boolean hasLayers() {
646        return getNumLayers() > 0;
647    }
648
649    private void setEditLayer(List<Layer> layersList) {
650        OsmDataLayer newEditLayer = layersList.contains(editLayer)?editLayer:null;
651        OsmDataLayer oldEditLayer = editLayer;
652
653        // Find new edit layer
654        if (activeLayer != editLayer || !layersList.contains(editLayer)) {
655            if (activeLayer instanceof OsmDataLayer && layersList.contains(activeLayer)) {
656                newEditLayer = (OsmDataLayer) activeLayer;
657            } else {
658                for (Layer layer:layersList) {
659                    if (layer instanceof OsmDataLayer) {
660                        newEditLayer = (OsmDataLayer) layer;
661                        break;
662                    }
663                }
664            }
665        }
666
667        // Set new edit layer
668        if (newEditLayer != editLayer) {
669            if (newEditLayer == null) {
670                getCurrentDataSet().setSelected();
671            }
672
673            editLayer = newEditLayer;
674            fireEditLayerChanged(oldEditLayer, newEditLayer);
675            refreshTitle();
676        }
677
678    }
679
680    /**
681     * Sets the active layer to <code>layer</code>. If <code>layer</code> is an instance
682     * of {@see OsmDataLayer} also sets {@see #editLayer} to <code>layer</code>.
683     *
684     * @param layer the layer to be activate; must be one of the layers in the list of layers
685     * @exception IllegalArgumentException thrown if layer is not in the lis of layers
686     */
687    public void setActiveLayer(Layer layer) {
688        setActiveLayer(layer, true);
689    }
690
691    private void setActiveLayer(Layer layer, boolean setEditLayer) {
692        if (layer != null && !layers.contains(layer))
693            throw new IllegalArgumentException(tr("Layer ''{0}'' must be in list of layers", layer.toString()));
694
695        if (layer == activeLayer)
696            return;
697
698        Layer old = activeLayer;
699        activeLayer = layer;
700        if (setEditLayer) {
701            setEditLayer(layers);
702        }
703        fireActiveLayerChanged(old, layer);
704
705        /* This only makes the buttons look disabled. Disabling the actions as well requires
706         * the user to re-select the tool after i.e. moving a layer. While testing I found
707         * that I switch layers and actions at the same time and it was annoying to mind the
708         * order. This way it works as visual clue for new users */
709        for (Enumeration<AbstractButton> e = Main.map.toolGroup.getElements() ; e.hasMoreElements() ;) {
710            AbstractButton x=e.nextElement();
711            x.setEnabled(((MapMode)x.getAction()).layerIsSupported(layer));
712        }
713        AudioPlayer.reset();
714        repaint();
715    }
716
717    /**
718     * Replies the currently active layer
719     *
720     * @return the currently active layer (may be null)
721     */
722    public Layer getActiveLayer() {
723        return activeLayer;
724    }
725
726    /**
727     * Replies the current edit layer, if any
728     *
729     * @return the current edit layer. May be null.
730     */
731    public OsmDataLayer getEditLayer() {
732        return editLayer;
733    }
734
735    /**
736     * replies true if the list of layers managed by this map view contain layer
737     *
738     * @param layer the layer
739     * @return true if the list of layers managed by this map view contain layer
740     */
741    public boolean hasLayer(Layer layer) {
742        return layers.contains(layer);
743    }
744
745    /**
746     * Tries to zoom to the download boundingbox[es] of the current edit layer
747     * (aka {@link OsmDataLayer}). If the edit layer has multiple download bounding
748     * boxes it zooms to a large virtual bounding box containing all smaller ones.
749     *
750     * @return <code>true</code> if a zoom operation has been performed
751     */
752    public boolean zoomToDataSetBoundingBox(DataSet ds) {
753        // In case we already have an existing data layer ...
754        OsmDataLayer layer= getEditLayer();
755        if (layer == null)
756            return false;
757        Collection<DataSource> dataSources = ds.dataSources;
758        // ... with bounding box[es] of data loaded from OSM or a file...
759        BoundingXYVisitor bbox = new BoundingXYVisitor();
760        for (DataSource source : dataSources) {
761            bbox.visit(source.bounds);
762        }
763        if (bbox.hasExtend()) {
764            // ... we zoom to it's bounding box
765            recalculateCenterScale(bbox);
766            return true;
767        }
768        return false;
769    }
770
771    public boolean addTemporaryLayer(MapViewPaintable mvp) {
772        if (temporaryLayers.contains(mvp)) return false;
773        return temporaryLayers.add(mvp);
774    }
775
776    public boolean removeTemporaryLayer(MapViewPaintable mvp) {
777        return temporaryLayers.remove(mvp);
778    }
779
780    public void propertyChange(PropertyChangeEvent evt) {
781        if (evt.getPropertyName().equals(Layer.VISIBLE_PROP)) {
782            repaint();
783        } else if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
784                || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) {
785            OsmDataLayer layer = (OsmDataLayer)evt.getSource();
786            if (layer == getEditLayer()) {
787                refreshTitle();
788            }
789        }
790    }
791
792    protected void refreshTitle() {
793        boolean dirty = editLayer != null && (editLayer.requiresSaveToFile() || editLayer.requiresUploadToServer());
794        if (dirty) {
795            JOptionPane.getFrameForComponent(Main.parent).setTitle("* " + tr("Java OpenStreetMap Editor"));
796        } else {
797            JOptionPane.getFrameForComponent(Main.parent).setTitle(tr("Java OpenStreetMap Editor"));
798        }
799    }
800
801}
Note: See TracBrowser for help on using the browser.