Ticket #13001: patch-main-add-map-content.patch

File patch-main-add-map-content.patch, 43.3 KB (added by michael2402, 8 years ago)
  • src/org/openstreetmap/josm/Main.java

    diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
    index 99dd688..81fc031 100644
    a b import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;  
    6161import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
    6262import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
    6363import org.openstreetmap.josm.actions.mapmode.DrawAction;
    64 import org.openstreetmap.josm.actions.mapmode.MapMode;
    6564import org.openstreetmap.josm.actions.search.SearchAction;
    6665import org.openstreetmap.josm.data.Bounds;
    6766import org.openstreetmap.josm.data.Preferences;
    import org.openstreetmap.josm.data.projection.ProjectionChangeListener;  
    7978import org.openstreetmap.josm.data.validation.OsmValidator;
    8079import org.openstreetmap.josm.gui.GettingStarted;
    8180import org.openstreetmap.josm.gui.MainApplication.Option;
     81import org.openstreetmap.josm.gui.MainFrame;
    8282import org.openstreetmap.josm.gui.MainMenu;
     83import org.openstreetmap.josm.gui.MainPanel;
    8384import org.openstreetmap.josm.gui.MapFrame;
    8485import org.openstreetmap.josm.gui.MapFrameListener;
    85 import org.openstreetmap.josm.gui.MapView;
    86 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
    8786import org.openstreetmap.josm.gui.help.HelpUtil;
    8887import org.openstreetmap.josm.gui.io.SaveLayersDialog;
    8988import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    9089import org.openstreetmap.josm.gui.layer.Layer;
    91 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
    92 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
    93 import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
    94 import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
    9590import org.openstreetmap.josm.gui.layer.MainLayerManager;
    9691import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    9792import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
    public abstract class Main {  
    194189     * The MapFrame. Use {@link Main#setMapFrame} to set or clear it.
    195190     * <p>
    196191     * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
     192     *
     193     * @see MainPanel
    197194     */
    198195    public static MapFrame map;
    199196
    public abstract class Main {  
    235232
    236233    /**
    237234     * The MOTD Layer.
     235     * @deprecated Do not access this. It will be removed soon. You should not need to access the GettingStarted panel.
    238236     */
    239     public final GettingStarted gettingStarted = new GettingStarted();
    240 
    241     private static final Collection<MapFrameListener> mapFrameListeners = new ArrayList<>();
     237    @Deprecated
     238    public final GettingStarted gettingStarted = mainPanel.getGettingStarted();
    242239
    243240    protected static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>();
    244241
    public abstract class Main {  
    253250     */
    254251    public static int logLevel = 3;
    255252
     253    /**
     254     * The real main panel. Thies field may be removed any time and made private to {@link MainFrame}
     255     * @see #panel
     256     */
     257    protected static final MainPanel mainPanel = new MainPanel(getLayerManager());
     258
    256259    private static void rememberWarnErrorMsg(String msg) {
    257260        // Only remember first line of message
    258261        int idx = msg.indexOf('\n');
    public abstract class Main {  
    509512
    510513    /**
    511514     * Set or clear (if passed <code>null</code>) the map.
     515     * <p>
     516     * To be removed any time
    512517     * @param map The map to set {@link Main#map} to. Can be null.
     518     * @deprecated This is done automatically by {@link MainPanel}
    513519     */
     520    @Deprecated
    514521    public final void setMapFrame(final MapFrame map) {
    515         MapFrame old = Main.map;
    516         panel.setVisible(false);
    517         panel.removeAll();
    518         if (map != null) {
    519             map.fillPanel(panel);
    520         } else {
    521             old.destroy();
    522             panel.add(gettingStarted, BorderLayout.CENTER);
    523         }
    524         panel.setVisible(true);
    525         redoUndoListener.commandChanged(0, 0);
    526 
    527         Main.map = map;
    528 
    529         // Notify map frame listeners, mostly plugins.
    530         if ((map == null) == (old == null)) {
    531             Main.warn("Replacing the map frame. This is not expected by some plugins and should not happen.");
    532         }
    533         for (MapFrameListener listener : mapFrameListeners) {
    534             MapView.fireDeprecatedListenerOnAdd = true;
    535             listener.mapFrameInitialized(old, map);
    536             MapView.fireDeprecatedListenerOnAdd = false;
    537         }
    538         if (map == null && currentProgressMonitor != null) {
    539             currentProgressMonitor.showForegroundDialog();
    540         }
     522        Main.warn("setMapFrame call was ignored.");
    541523    }
    542524
    543525    /**
    544526     * Remove the specified layer from the map. If it is the last layer,
    545527     * remove the map as well.
     528     * <p>
     529     * To be removed end of 2016
    546530     * @param layer The layer to remove
     531     * @deprecated You can remove the layer using {@link #getLayerManager()}
    547532     */
     533    @Deprecated
    548534    public final synchronized void removeLayer(final Layer layer) {
    549535        if (map != null) {
    550536            getLayerManager().removeLayer(layer);
    551             if (isDisplayingMapView() && getLayerManager().getLayers().isEmpty()) {
    552                 setMapFrame(null);
    553             }
    554537        }
    555538    }
    556539
    public abstract class Main {  
    574557     */
    575558    public Main() {
    576559        main = this;
    577         getLayerManager().addLayerChangeListener(new LayerChangeListener() {
     560        mainPanel.addAndFireMapFrameListener(new MapFrameListener() {
    578561            @Override
    579             public void layerAdded(LayerAddEvent e) {
    580                 Layer layer = e.getAddedLayer();
    581                 if (map == null) {
    582                     Main.main.createMapFrame(layer, null);
    583                     Main.map.setVisible(true);
    584                 }
    585                 ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
    586                 if (viewProjectionBounds != null) {
    587                     Main.map.mapView.scheduleZoomTo(new ViewportData(viewProjectionBounds));
    588                 }
     562            public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
     563                redoUndoListener.commandChanged(0, 0);
    589564            }
    590 
    591             @Override
    592             public void layerRemoving(LayerRemoveEvent e) {
    593                 // empty
    594             }
    595 
    596             @Override
    597             public void layerOrderChanged(LayerOrderChangeEvent e) {
    598                 //empty
    599             }
    600 
    601565        });
    602566    }
    603567
    public abstract class Main {  
    797761     * Add a new layer to the map.
    798762     *
    799763     * If no map exists, create one.
     764     * <p>
     765     * To be removed end of 2016
    800766     *
    801767     * @param layer the layer
    802768     *
    803769     * @see #addLayer(Layer, ProjectionBounds)
    804770     * @see #addLayer(Layer, ViewportData)
     771     * @deprecated You can add the layer to the layer manager: {@link #getLayerManager()}
    805772     */
     773    @Deprecated
    806774    public final void addLayer(final Layer layer) {
    807         addLayer(layer, layer.getViewProjectionBounds());
     775        addLayer(layer, (ViewportData) null);
    808776    }
    809777
    810778    /**
    public abstract class Main {  
    830798     */
    831799    public final void addLayer(Layer layer, ViewportData viewport) {
    832800        getLayerManager().addLayer(layer);
    833         if (viewport != null) {
     801        if (viewport != null && Main.map.mapView != null) {
     802            // MapView may be null in headless mode here.
    834803            Main.map.mapView.scheduleZoomTo(viewport);
    835804        }
    836805    }
    837806
    838807    /**
    839808     * Creates the map frame. Call only in EDT Thread.
     809     * <p>
     810     * To be removed any time
    840811     * @param firstLayer The first layer that was added.
    841812     * @param viewportData The initial viewport. Can be <code>null</code> to be automatically computed.
     813     * @deprecated Not supported. MainPanel does this automatically.
    842814     */
     815    @Deprecated
    843816    public synchronized void createMapFrame(Layer firstLayer, ViewportData viewportData) {
    844817        GuiHelper.assertCallFromEdt();
    845         MapFrame mapFrame = new MapFrame(contentPanePrivate, viewportData);
    846         setMapFrame(mapFrame);
    847         if (firstLayer != null) {
    848             mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), firstLayer);
    849         }
    850         mapFrame.initializeDialogsPane();
    851         // bootstrapping problem: make sure the layer list dialog is going to
    852         // listen to change events of the very first layer
    853         //
    854         if (firstLayer != null) {
    855             firstLayer.addPropertyChangeListener(LayerListDialog.getInstance().getModel());
    856         }
     818        Main.error("createMapFrame() not supported any more.");
    857819    }
    858820
    859821    /**
    860822     * Replies <code>true</code> if there is an edit layer
     823     * <p>
     824     * To be removed end of 2016
    861825     *
    862826     * @return <code>true</code> if there is an edit layer
     827     * @deprecated You can get the edit layer using the layer manager and then check if it is not null: {@link #getLayerManager()}
    863828     */
     829    @Deprecated
    864830    public boolean hasEditLayer() {
    865831        if (getEditLayer() == null) return false;
    866832        return true;
    public abstract class Main {  
    868834
    869835    /**
    870836     * Replies the current edit layer
     837     * <p>
     838     * To be removed end of 2016
    871839     *
    872840     * @return the current edit layer. <code>null</code>, if no current edit layer exists
     841     * @deprecated You can get the edit layer using the layer manager: {@link #getLayerManager()}
    873842     */
     843    @Deprecated
    874844    public OsmDataLayer getEditLayer() {
    875         if (!isDisplayingMapView()) return null;
    876845        return getLayerManager().getEditLayer();
    877846    }
    878847
    879848    /**
    880849     * Replies the current data set.
     850     * <p>
     851     * To be removed end of 2016
    881852     *
    882853     * @return the current data set. <code>null</code>, if no current data set exists
     854     * @deprecated You can get the data set using the layer manager: {@link #getLayerManager()}
    883855     */
     856    @Deprecated
    884857    public DataSet getCurrentDataSet() {
    885         if (!hasEditLayer()) return null;
    886         return getEditLayer().data;
     858        return getLayerManager().getEditDataSet();
    887859    }
    888860
    889861    /**
    public abstract class Main {  
    907879
    908880    /**
    909881     * Returns the currently active  layer
     882     * <p>
     883     * To be removed end of 2016
    910884     *
    911885     * @return the currently active layer. <code>null</code>, if currently no active layer exists
     886     * @deprecated You can get the layer using the layer manager: {@link #getLayerManager()}
    912887     */
     888    @Deprecated
    913889    public Layer getActiveLayer() {
    914         if (!isDisplayingMapView()) return null;
    915890        return getLayerManager().getActiveLayer();
    916891    }
    917892
    public abstract class Main {  
    976951    /**
    977952     * Global panel.
    978953     */
    979     public static final JPanel panel = new JPanel(new BorderLayout());
     954    public static final JPanel panel = mainPanel;
    980955
    981956    private final CommandQueueListener redoUndoListener = new CommandQueueListener() {
    982957        @Override
    public abstract class Main {  
    11621137            map.rememberToggleDialogWidth();
    11631138        }
    11641139        // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
    1165         if (Main.isDisplayingMapView()) {
    1166             Collection<Layer> layers = new ArrayList<>(getLayerManager().getLayers());
    1167             for (Layer l: layers) {
    1168                 Main.main.removeLayer(l);
    1169             }
    1170         }
     1140        getLayerManager().resetState();
    11711141        try {
    11721142            pref.saveDefaults();
    11731143        } catch (IOException ex) {
    public abstract class Main {  
    16531623     * @return {@code true} if the listeners collection changed as a result of the call
    16541624     */
    16551625    public static boolean addMapFrameListener(MapFrameListener listener, boolean fireWhenMapViewPresent) {
    1656         boolean changed = listener != null && mapFrameListeners.add(listener);
    1657         if (fireWhenMapViewPresent && changed && map != null) {
    1658             listener.mapFrameInitialized(null, map);
     1626        if (fireWhenMapViewPresent) {
     1627            return mainPanel.addAndFireMapFrameListener(listener);
     1628        } else {
     1629            return mainPanel.addMapFrameListener(listener);
    16591630        }
    1660         return changed;
    16611631    }
    16621632
    16631633    /**
    public abstract class Main {  
    16671637     * @since 5957
    16681638     */
    16691639    public static boolean addMapFrameListener(MapFrameListener listener) {
    1670         return addMapFrameListener(listener, false);
     1640        return mainPanel.addMapFrameListener(listener);
    16711641    }
    16721642
    16731643    /**
    public abstract class Main {  
    16771647     * @since 5957
    16781648     */
    16791649    public static boolean removeMapFrameListener(MapFrameListener listener) {
    1680         return listener != null && mapFrameListeners.remove(listener);
     1650        return mainPanel.removeMapFrameListener(listener);
    16811651    }
    16821652
    16831653    /**
  • src/org/openstreetmap/josm/actions/SessionLoadAction.java

    diff --git a/src/org/openstreetmap/josm/actions/SessionLoadAction.java b/src/org/openstreetmap/josm/actions/SessionLoadAction.java
    index cd390f2..6d2b2ee 100644
    a b public class SessionLoadAction extends DiskAccessAction {  
    124124
    125125        private void addLayers() {
    126126            if (layers != null && !layers.isEmpty()) {
    127                 Layer firstLayer = layers.get(0);
    128127                boolean noMap = Main.map == null;
    129                 if (noMap) {
    130                     Main.main.createMapFrame(firstLayer, viewport);
    131                 }
    132128                for (Layer l : layers) {
    133129                    if (canceled)
    134130                        return;
    135                     Main.main.addLayer(l, (ViewportData) null);
     131                    Main.getLayerManager().addLayer(l);
    136132                }
    137133                if (active != null) {
    138134                    Main.getLayerManager().setActiveLayer(active);
    139135                }
    140136                if (noMap) {
    141                     Main.map.setVisible(true);
     137                    Main.map.mapView.scheduleZoomTo(viewport);
    142138                }
    143139            }
    144140        }
  • src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java

    diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
    index 024b189..33d467a 100644
    a b import java.util.regex.Pattern;  
    1212import org.openstreetmap.josm.Main;
    1313import org.openstreetmap.josm.data.Bounds;
    1414import org.openstreetmap.josm.data.Bounds.ParseMethod;
     15import org.openstreetmap.josm.data.ViewportData;
    1516import org.openstreetmap.josm.data.gpx.GpxData;
    1617import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    1718import org.openstreetmap.josm.gui.layer.GpxLayer;
    public class DownloadGpsTask extends AbstractDownloadTask<GpxData> {  
    140141        private <L extends Layer> L addOrMergeLayer(L layer, L mergeLayer) {
    141142            if (layer == null) return null;
    142143            if (newLayer || mergeLayer == null) {
    143                 if (Main.main != null) {
    144                     Main.main.addLayer(layer);
    145                 }
     144                Main.getLayerManager().addLayer(layer);
    146145                return layer;
    147146            } else {
    148147                mergeLayer.mergeFrom(layer);
     148                mergeLayer.invalidate();
    149149                if (Main.map != null) {
    150                     Main.map.repaint();
     150                    Main.map.mapView.scheduleZoomTo(new ViewportData(layer.getViewProjectionBounds()));
    151151                }
    152152                return mergeLayer;
    153153            }
  • src/org/openstreetmap/josm/gui/MainApplication.java

    diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java
    index 5727fa5..58ebfc2 100644
    a b public class MainApplication extends Main {  
    316316
    317317        initApplicationPreferences();
    318318
     319        // Can only be called after preferences are initialized.
     320        // We can move this to MainPanel constructor as soon as noone depends on Main#panel any more.
     321        GuiHelper.runInEDTAndWait(new Runnable() {
     322            @Override
     323            public void run() {
     324                mainPanel.updateContent();
     325            }
     326        });
     327
    319328        Policy.setPolicy(new Policy() {
    320329            // Permissions for plug-ins loaded when josm is started via webstart
    321330            private PermissionCollection pc;
    public class MainApplication extends Main {  
    398407        WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry",
    399408                args.containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null,
    400409                !args.containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));
    401         final MainFrame mainFrame = new MainFrame(contentPanePrivate, geometry);
     410        final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry);
    402411        Main.parent = mainFrame;
    403412
    404413        if (args.containsKey(Option.LOAD_PREFERENCES)) {
  • src/org/openstreetmap/josm/gui/MainFrame.java

    diff --git a/src/org/openstreetmap/josm/gui/MainFrame.java b/src/org/openstreetmap/josm/gui/MainFrame.java
    index 75ac489..7b5a654 100644
    a b import org.openstreetmap.josm.tools.WindowGeometry;  
    3939 * @since 10340
    4040 */
    4141public class MainFrame extends JFrame {
    42     protected transient WindowGeometry geometry;
    43     protected int windowState = JFrame.NORMAL;
    44     private MainMenu menu;
    45 
    4642    private final transient LayerStateChangeListener updateTitleOnLayerStateChange = new LayerStateChangeListener() {
    4743        @Override
    4844        public void uploadDiscouragedChanged(OsmDataLayer layer, boolean newValue) {
    public class MainFrame extends JFrame {  
    5147    };
    5248
    5349    private final transient PropertyChangeListener updateTitleOnSaveChange = new PropertyChangeListener() {
    54 
    5550        @Override
    5651        public void propertyChange(PropertyChangeEvent evt) {
    5752            if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
    public class MainFrame extends JFrame {  
    6257        }
    6358    };
    6459
     60    protected transient WindowGeometry geometry;
     61    protected int windowState = JFrame.NORMAL;
     62    private MainMenu menu;
     63
    6564    /**
    6665     * Create a new main window.
    6766     */
    6867    public MainFrame() {
    69         this(new JPanel(), new WindowGeometry(new Rectangle(10, 10, 500, 500)));
     68        this(new JPanel(), new MainPanel(Main.getLayerManager()), new WindowGeometry(new Rectangle(10, 10, 500, 500)));
    7069    }
    7170
    7271    /**
    73      * Create a new main window.
     72     * Create a new main window. The parameters will be removed in the future.
    7473     * @param contentPanePrivate The content
     74     * @param mainPanel The main panel.
    7575     * @param geometry The inital geometry to use.
    7676     */
    77     public MainFrame(Container contentPanePrivate, WindowGeometry geometry) {
     77    public MainFrame(Container contentPanePrivate, MainPanel mainPanel, WindowGeometry geometry) {
    7878        super();
    7979        this.geometry = geometry;
    8080        setContentPane(contentPanePrivate);
    public class MainFrame extends JFrame {  
    119119        refreshTitle();
    120120
    121121        getContentPane().add(Main.panel, BorderLayout.CENTER);
    122         Main.panel.add(Main.main.gettingStarted, BorderLayout.CENTER);
    123122        menu.initialize();
    124123    }
    125124
  • new file src/org/openstreetmap/josm/gui/MainPanel.java

    diff --git a/src/org/openstreetmap/josm/gui/MainPanel.java b/src/org/openstreetmap/josm/gui/MainPanel.java
    new file mode 100644
    index 0000000..c81f3e4
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui;
     3
     4import java.awt.BorderLayout;
     5import java.util.List;
     6import java.util.concurrent.CopyOnWriteArrayList;
     7
     8import javax.swing.JPanel;
     9import javax.swing.SwingUtilities;
     10
     11import org.openstreetmap.josm.Main;
     12import org.openstreetmap.josm.actions.mapmode.MapMode;
     13import org.openstreetmap.josm.gui.layer.Layer;
     14import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
     15import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
     16import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
     17import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
     18import org.openstreetmap.josm.gui.layer.MainLayerManager;
     19import org.openstreetmap.josm.gui.util.GuiHelper;
     20
     21/**
     22 * This is the content panel inside the {@link MainFrame}. It displays the content the user is working with.
     23 * <p>
     24 * If there is no active layer, there is no content displayed. As soon as there are active layers, the {@link MapFrame} is displayed.
     25 *
     26 * @author Michael Zangl
     27 * @since xxx
     28 */
     29public class MainPanel extends JPanel {
     30    private MapFrame map;
     31    // Needs to be lazy because we need to wait for preferences to set up.
     32    private GettingStarted gettingStarted;
     33    private final CopyOnWriteArrayList<MapFrameListener> mapFrameListeners = new CopyOnWriteArrayList<>();
     34    private final transient MainLayerManager layerManager;
     35
     36    /**
     37     * Create a new main panel
     38     * @param layerManager The layer manager to use to display the content.
     39     */
     40    public MainPanel(MainLayerManager layerManager) {
     41        super(new BorderLayout());
     42        this.layerManager = layerManager;
     43        reAddListeners();
     44    }
     45
     46    /**
     47     * Update the content of this {@link MainFrame} to either display the map or display the welcome screen.
     48     */
     49    protected void updateContent() {
     50        GuiHelper.assertCallFromEdt();
     51        MapFrame old = map;
     52        boolean showMap = !layerManager.getLayers().isEmpty();
     53        if (old != null && showMap) {
     54            // no state change
     55            return;
     56        }
     57
     58        // remove old content
     59        setVisible(false);
     60        removeAll();
     61        if (old != null) {
     62            old.destroy();
     63        }
     64
     65        // create new content
     66        if (showMap) {
     67            map = createNewMapFrame();
     68        } else {
     69            map = null;
     70                    Main.map = map;
     71            add(getGettingStarted(), BorderLayout.CENTER);
     72        }
     73        setVisible(true);
     74
     75        if (old == null && !showMap) {
     76            // listeners may not be able to handle this...
     77            return;
     78        }
     79
     80        // Notify map frame listeners, mostly plugins.
     81        for (MapFrameListener listener : mapFrameListeners) {
     82            MapView.fireDeprecatedListenerOnAdd = true;
     83            listener.mapFrameInitialized(old, map);
     84            MapView.fireDeprecatedListenerOnAdd = false;
     85        }
     86        if (map == null && Main.currentProgressMonitor != null) {
     87            Main.currentProgressMonitor.showForegroundDialog();
     88        }
     89    }
     90
     91    private MapFrame createNewMapFrame() {
     92        MapFrame mapFrame = new MapFrame(null, null);
     93        // Required by many components.
     94        Main.map = mapFrame;
     95
     96        mapFrame.fillPanel(this);
     97
     98        //TODO: Move this to some better place
     99        List<Layer> layers = Main.getLayerManager().getLayers();
     100        if (!layers.isEmpty()) {
     101            mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), layers.get(0));
     102        }
     103        mapFrame.initializeDialogsPane();
     104        mapFrame.setVisible(true);
     105        return mapFrame;
     106    }
     107
     108    /**
     109     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
     110     * <p>
     111     * It will fire an initial mapFrameInitialized event
     112     * when the MapFrame is present. Otherwise will only fire when the MapFrame is created
     113     * or destroyed.
     114     * @param listener The MapFrameListener
     115     * @return {@code true} if the listeners collection changed as a result of the call.
     116     * @since xxx
     117     */
     118    public boolean addAndFireMapFrameListener(MapFrameListener listener) {
     119        boolean changed = addMapFrameListener(listener);
     120        if (changed && map != null) {
     121            listener.mapFrameInitialized(null, map);
     122        }
     123        return changed;
     124    }
     125
     126    /**
     127     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
     128     * @param listener The MapFrameListener
     129     * @return {@code true} if the listeners collection changed as a result of the call
     130     * @since xxx
     131     */
     132    public boolean addMapFrameListener(MapFrameListener listener) {
     133        return listener != null && mapFrameListeners.add(listener);
     134    }
     135
     136    /**
     137     * Unregisters the given {@code MapFrameListener} from MapFrame changes
     138     * @param listener The MapFrameListener
     139     * @return {@code true} if the listeners collection changed as a result of the call
     140     * @since xxx
     141     */
     142    public boolean removeMapFrameListener(MapFrameListener listener) {
     143        return listener != null && mapFrameListeners.remove(listener);
     144    }
     145
     146    /**
     147     * Gets the {@link GettingStarted} panel.
     148     * @return The panel.
     149     */
     150    public GettingStarted getGettingStarted() {
     151        if (gettingStarted == null) {
     152            gettingStarted = new GettingStarted();
     153        }
     154        return gettingStarted;
     155    }
     156
     157    /**
     158     * Re-adds the layer listeners. Never call this in production, only needed for testing.
     159     */
     160    public void reAddListeners() {
     161        layerManager.addLayerChangeListener(new LayerChangeListener() {
     162            @Override
     163            public void layerAdded(LayerAddEvent e) {
     164                updateContent();
     165            }
     166
     167            @Override
     168            public void layerRemoving(final LayerRemoveEvent e) {
     169                // Delay main.map removal until after all listeners are finished.
     170                // Some components rely on this and e.g. get the MapView that way.
     171                SwingUtilities.invokeLater(new Runnable() {
     172                    @Override
     173                    public void run() {
     174                        updateContent();
     175                    }
     176                });
     177            }
     178
     179            @Override
     180            public void layerOrderChanged(LayerOrderChangeEvent e) {
     181                // ignored
     182            }
     183        });
     184    }
     185}
  • src/org/openstreetmap/josm/gui/MapFrame.java

    diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
    index b0a4335..e3ab5cc 100644
    a b public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi  
    187187
    188188    /**
    189189     * Constructs a new {@code MapFrame}.
    190      * @param contentPane The content pane used to register shortcuts in its
    191      * {@link javax.swing.InputMap} and {@link javax.swing.ActionMap}
     190     * @param contentPane Ignored. Main content pane is used.
    192191     * @param viewportData the initial viewport of the map. Can be null, then
    193192     * the viewport is derived from the layer data.
    194193     */
  • src/org/openstreetmap/josm/gui/MapMover.java

    diff --git a/src/org/openstreetmap/josm/gui/MapMover.java b/src/org/openstreetmap/josm/gui/MapMover.java
    index 377580f..d745f1b 100644
    a b import java.awt.event.KeyEvent;  
    1010import java.awt.event.MouseAdapter;
    1111import java.awt.event.MouseEvent;
    1212import java.awt.event.MouseWheelEvent;
     13import java.util.ArrayList;
    1314
    1415import javax.swing.AbstractAction;
    15 import javax.swing.ActionMap;
    16 import javax.swing.InputMap;
    17 import javax.swing.JComponent;
    1816import javax.swing.JPanel;
    19 import javax.swing.KeyStroke;
    2017
    2118import org.openstreetmap.gui.jmapviewer.JMapViewer;
    2219import org.openstreetmap.josm.Main;
    import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;  
    2623import org.openstreetmap.josm.data.coor.EastNorth;
    2724import org.openstreetmap.josm.data.preferences.BooleanProperty;
    2825import org.openstreetmap.josm.tools.Destroyable;
     26import org.openstreetmap.josm.tools.Pair;
    2927import org.openstreetmap.josm.tools.Shortcut;
    3028
    3129/**
    public class MapMover extends MouseAdapter implements Destroyable {  
    6361        private final String action;
    6462
    6563        ZoomerAction(String action) {
     64            this(action, "MapMover.Zoomer." + action);
     65        }
     66
     67        public ZoomerAction(String action, String name) {
    6668            this.action = action;
     69            putValue(NAME, name);
    6770        }
    6871
    6972        @Override
    public class MapMover extends MouseAdapter implements Destroyable {  
    106109     * The map to move around.
    107110     */
    108111    private final NavigatableComponent nc;
    109     private final JPanel contentPane;
    110112
    111113    private boolean movementInPlace;
    112114
     115    private final ArrayList<Pair<ZoomerAction, Shortcut>> registeredShortcuts = new ArrayList<>();
     116
    113117    /**
    114118     * Constructs a new {@code MapMover}.
    115119     * @param navComp the navigatable component
    116      * @param contentPane the content pane
     120     * @param contentPane Ignored. The main action map is used.
    117121     */
    118122    public MapMover(NavigatableComponent navComp, JPanel contentPane) {
    119123        this.nc = navComp;
    120         this.contentPane = contentPane;
    121124        nc.addMouseListener(this);
    122125        nc.addMouseMotionListener(this);
    123126        nc.addMouseWheelListener(this);
    124127
    125         if (contentPane != null) {
    126             // CHECKSTYLE.OFF: LineLength
    127             contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    128                 Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL).getKeyStroke(),
    129                 "MapMover.Zoomer.right");
    130             contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right"));
    131 
    132             contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    133                 Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL).getKeyStroke(),
    134                 "MapMover.Zoomer.left");
    135             contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left"));
    136 
    137             contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    138                 Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL).getKeyStroke(),
    139                 "MapMover.Zoomer.up");
    140             contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up"));
    141 
    142             contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    143                 Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL).getKeyStroke(),
    144                 "MapMover.Zoomer.down");
    145             contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down"));
    146             // CHECKSTYLE.ON: LineLength
    147 
    148             // see #10592 - Disable these alternate shortcuts on OS X because of conflict with system shortcut
    149             if (!Main.isPlatformOsx()) {
    150                 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    151                     Shortcut.registerShortcut("view:zoominalternate",
    152                             tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL).getKeyStroke(),
    153                     "MapMover.Zoomer.in");
    154                 contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(","));
    155 
    156                 contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
    157                     Shortcut.registerShortcut("view:zoomoutalternate",
    158                             tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL).getKeyStroke(),
    159                     "MapMover.Zoomer.out");
    160                 contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction("."));
    161             }
     128        registerActionShortcut(new ZoomerAction("right"),
     129                Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL));
     130
     131        registerActionShortcut(new ZoomerAction("left"),
     132                Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL));
     133
     134        registerActionShortcut(new ZoomerAction("up"),
     135                Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL));
     136        registerActionShortcut(new ZoomerAction("down"),
     137                Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL));
     138
     139        // see #10592 - Disable these alternate shortcuts on OS X because of conflict with system shortcut
     140        if (!Main.isPlatformOsx()) {
     141            registerActionShortcut(new ZoomerAction(",", "MapMover.Zoomer.in"),
     142                    Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL));
     143
     144            registerActionShortcut(new ZoomerAction(".", "MapMover.Zoomer.out"),
     145                    Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL));
    162146        }
    163147    }
    164148
     149    private void registerActionShortcut(ZoomerAction action, Shortcut shortcut) {
     150        Main.registerActionShortcut(action, shortcut);
     151        registeredShortcuts.add(new Pair<>(action, shortcut));
     152    }
     153
    165154    /**
    166155     * If the right (and only the right) mouse button is pressed, move the map.
    167156     */
    public class MapMover extends MouseAdapter implements Destroyable {  
    269258
    270259    @Override
    271260    public void destroy() {
    272         if (this.contentPane != null) {
    273             InputMap inputMap = contentPane.getInputMap();
    274             KeyStroke[] inputKeys = inputMap.keys();
    275             if (inputKeys != null) {
    276                 for (KeyStroke key : inputKeys) {
    277                     Object binding = inputMap.get(key);
    278                     if (binding instanceof String && ((String) binding).startsWith("MapMover.")) {
    279                         inputMap.remove(key);
    280                     }
    281                 }
    282             }
    283             ActionMap actionMap = contentPane.getActionMap();
    284             Object[] actionsKeys = actionMap.keys();
    285             if (actionsKeys != null) {
    286                 for (Object key : actionsKeys) {
    287                     if (key instanceof String && ((String) key).startsWith("MapMover.")) {
    288                         actionMap.remove(key);
    289                     }
    290                 }
    291             }
     261        for (Pair<ZoomerAction, Shortcut> shortcut : registeredShortcuts) {
     262            Main.unregisterActionShortcut(shortcut.a, shortcut.b);
    292263        }
    293264    }
    294265}
  • src/org/openstreetmap/josm/gui/MapView.java

    diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
    index 1415463..03166a0 100644
    a b import java.util.Set;  
    3131import java.util.concurrent.CopyOnWriteArrayList;
    3232
    3333import javax.swing.AbstractButton;
    34 import javax.swing.ActionMap;
    35 import javax.swing.InputMap;
    3634import javax.swing.JComponent;
    3735import javax.swing.JPanel;
    3836
    import org.openstreetmap.josm.actions.mapmode.MapMode;  
    4139import org.openstreetmap.josm.data.Bounds;
    4240import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
    4341import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
     42import org.openstreetmap.josm.data.ProjectionBounds;
    4443import org.openstreetmap.josm.data.SelectionChangedListener;
    4544import org.openstreetmap.josm.data.ViewportData;
    4645import org.openstreetmap.josm.data.coor.EastNorth;
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    494493    /**
    495494     * Constructs a new {@code MapView}.
    496495     * @param layerManager The layers to display.
    497      * @param contentPane The content pane used to register shortcuts in its
    498      * {@link InputMap} and {@link ActionMap}
     496     * @param contentPane Ignored. Main content pane is used.
    499497     * @param viewportData the initial viewport of the map. Can be null, then
    500498     * the viewport is derived from the layer data.
    501499     * @since 10279
    LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {  
    599597            playHeadMarker = PlayHeadMarker.create();
    600598        }
    601599
     600        ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
     601        if (viewProjectionBounds != null) {
     602            scheduleZoomTo(new ViewportData(viewProjectionBounds));
     603        }
     604
    602605        layer.addPropertyChangeListener(this);
    603606        Main.addProjectionChangeListener(layer);
    604607        invalidatedListener.addTo(layer);
  • src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java

    diff --git a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
    index f2ef962..c7b9ade 100644
    a b public class LayerListDialog extends ToggleDialog {  
    330330    @Override
    331331    public void showNotify() {
    332332        layerManager.addActiveLayerChangeListener(activateLayerAction);
    333         layerManager.addLayerChangeListener(model);
     333        layerManager.addLayerChangeListener(model, true);
    334334        layerManager.addAndFireActiveLayerChangeListener(model);
    335335        model.populate();
    336336    }
    337337
    338338    @Override
    339339    public void hideNotify() {
    340         layerManager.removeLayerChangeListener(model);
     340        layerManager.removeLayerChangeListener(model, true);
    341341        layerManager.removeActiveLayerChangeListener(model);
    342342        layerManager.removeActiveLayerChangeListener(activateLayerAction);
    343343    }
  • src/org/openstreetmap/josm/gui/layer/LayerManager.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/LayerManager.java b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
    index 16fbfd4..4484702 100644
    a b public class LayerManager {  
    8989     */
    9090    public static class LayerRemoveEvent extends LayerManagerEvent {
    9191        private final Layer removedLayer;
     92        private final boolean lastLayer;
    9293
    9394        LayerRemoveEvent(LayerManager source, Layer removedLayer) {
    9495            super(source);
    9596            this.removedLayer = removedLayer;
     97            this.lastLayer = source.getLayers().size() == 1;
    9698        }
    9799
    98100        /**
    public class LayerManager {  
    102104        public Layer getRemovedLayer() {
    103105            return removedLayer;
    104106        }
     107
     108        /**
     109         * Check if the layer that was removed is the last layer in the list.
     110         * @return <code>true</code> if this was the last layer.
     111         */
     112        public boolean isLastLayer() {
     113            return lastLayer;
     114        }
    105115    }
    106116
    107117    /**
    public class LayerManager {  
    346356            l.layerOrderChanged(e);
    347357        }
    348358    }
     359
     360    /**
     361     * Reset all layer manager state. This includes removing all layers and then unregistering all listeners
     362     */
     363    public void resetState() {
     364        // some layer remove listeners remove other layers.
     365        while (!getLayers().isEmpty()) {
     366            removeLayer(getLayers().get(0));
     367        }
     368
     369        layerChangeListeners.clear();
     370    }
    349371}
  • src/org/openstreetmap/josm/gui/layer/MainLayerManager.java

    diff --git a/src/org/openstreetmap/josm/gui/layer/MainLayerManager.java b/src/org/openstreetmap/josm/gui/layer/MainLayerManager.java
    index cf7418a..47856fe 100644
    a b public class MainLayerManager extends LayerManager {  
    312312        }
    313313        return ret;
    314314    }
     315
     316    @Override
     317    public void resetState() {
     318        // active and edit layer are unset automatically
     319        super.resetState();
     320
     321        activeLayerChangeListeners.clear();
     322    }
    315323}
  • src/org/openstreetmap/josm/io/NoteImporter.java

    diff --git a/src/org/openstreetmap/josm/io/NoteImporter.java b/src/org/openstreetmap/josm/io/NoteImporter.java
    index da40198..af84702 100644
    a b import org.openstreetmap.josm.actions.ExtensionFileFilter;  
    1313import org.openstreetmap.josm.data.notes.Note;
    1414import org.openstreetmap.josm.gui.layer.NoteLayer;
    1515import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    16 import org.openstreetmap.josm.gui.util.GuiHelper;
    1716import org.xml.sax.SAXException;
    1817
    1918/**
    public class NoteImporter extends FileImporter {  
    3736        }
    3837        try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
    3938            final NoteLayer layer = loadLayer(is, file, file.getName(), progressMonitor);
    40             if (Main.map == null || !Main.getLayerManager().containsLayer(layer)) {
    41                 GuiHelper.runInEDT(new Runnable() {
    42                     @Override
    43                     public void run() {
    44                         Main.main.addLayer(layer);
    45                     }
    46                 });
     39            if (!Main.getLayerManager().containsLayer(layer)) {
     40                Main.getLayerManager().addLayer(layer);
    4741            }
    4842        } catch (SAXException e) {
    4943            Main.error("error opening up notes file");
  • test/unit/org/openstreetmap/josm/io/NoteImporterTest.java

    diff --git a/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java b/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java
    index 8e0c9a1..90f0905 100644
    a b import static org.junit.Assert.assertTrue;  
    66
    77import java.io.File;
    88
     9import org.junit.Rule;
    910import org.junit.Test;
    1011import org.openstreetmap.josm.Main;
    1112import org.openstreetmap.josm.TestUtils;
    12 import org.openstreetmap.josm.gui.layer.Layer;
     13import org.openstreetmap.josm.testutils.JOSMTestRules;
    1314
    1415/**
    1516 * Unit tests of {@link NoteImporter} class.
    import org.openstreetmap.josm.gui.layer.Layer;  
    1718public class NoteImporterTest {
    1819
    1920    /**
     21     * Use the test rules to remove any layers and reset state.
     22     */
     23    @Rule
     24    public final JOSMTestRules rules = new JOSMTestRules();
     25
     26    /**
    2027     * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12531">Bug #12531</a>.
    2128     */
    2229    @Test
    2330    public void testTicket12531() {
    24         if (Main.map != null) {
    25             for (Layer l: Main.getLayerManager().getLayers()) {
    26                 Main.getLayerManager().removeLayer(l);
    27             }
    28             Main.main.setMapFrame(null);
    29         }
     31        Main.getLayerManager().resetState();
    3032        assertNull(Main.map);
    3133        assertTrue(new NoteImporter().importDataHandleExceptions(
    3234                new File(TestUtils.getRegressionDataFile(12531, "notes.osn")), null));
  • test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java

    diff --git a/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java b/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
    index 2531d0e..39dae30 100644
    a b import org.junit.runners.model.InitializationError;  
    1212import org.junit.runners.model.Statement;
    1313import org.openstreetmap.josm.Main;
    1414import org.openstreetmap.josm.data.projection.Projections;
    15 import org.openstreetmap.josm.gui.layer.MainLayerManager;
    1615import org.openstreetmap.josm.gui.util.GuiHelper;
    1716import org.openstreetmap.josm.io.OsmApi;
    1817import org.openstreetmap.josm.io.OsmApiInitializationException;
    public class JOSMTestRules implements TestRule {  
    222221            }
    223222        });
    224223        // Remove all layers
    225         MainLayerManager lm = Main.getLayerManager();
    226         while (!lm.getLayers().isEmpty()) {
    227             lm.removeLayer(lm.getLayers().get(0));
    228         }
     224        Main.getLayerManager().resetState();
    229225
    230226        // TODO: Remove global listeners and other global state.
    231227        Main.pref = null;