diff --git a/src/org/openstreetmap/josm/Main.java b/src/org/openstreetmap/josm/Main.java
index 99dd688..81fc031 100644
--- a/src/org/openstreetmap/josm/Main.java
+++ b/src/org/openstreetmap/josm/Main.java
@@ -61,7 +61,6 @@ import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
 import org.openstreetmap.josm.actions.mapmode.DrawAction;
-import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.actions.search.SearchAction;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Preferences;
@@ -79,19 +78,15 @@ import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
 import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.gui.GettingStarted;
 import org.openstreetmap.josm.gui.MainApplication.Option;
+import org.openstreetmap.josm.gui.MainFrame;
 import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.MainPanel;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.MapFrameListener;
-import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.io.SaveLayersDialog;
 import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
 import org.openstreetmap.josm.gui.layer.Layer;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
 import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
@@ -194,6 +189,8 @@ public abstract class Main {
      * The MapFrame. Use {@link Main#setMapFrame} to set or clear it.
      * <p>
      * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
+     *
+     * @see MainPanel
      */
     public static MapFrame map;
 
@@ -235,10 +232,10 @@ public abstract class Main {
 
     /**
      * The MOTD Layer.
+     * @deprecated Do not access this. It will be removed soon. You should not need to access the GettingStarted panel.
      */
-    public final GettingStarted gettingStarted = new GettingStarted();
-
-    private static final Collection<MapFrameListener> mapFrameListeners = new ArrayList<>();
+    @Deprecated
+    public final GettingStarted gettingStarted = mainPanel.getGettingStarted();
 
     protected static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>();
 
@@ -253,6 +250,12 @@ public abstract class Main {
      */
     public static int logLevel = 3;
 
+    /**
+     * The real main panel. Thies field may be removed any time and made private to {@link MainFrame}
+     * @see #panel
+     */
+    protected static final MainPanel mainPanel = new MainPanel(getLayerManager());
+
     private static void rememberWarnErrorMsg(String msg) {
         // Only remember first line of message
         int idx = msg.indexOf('\n');
@@ -509,48 +512,28 @@ public abstract class Main {
 
     /**
      * Set or clear (if passed <code>null</code>) the map.
+     * <p>
+     * To be removed any time
      * @param map The map to set {@link Main#map} to. Can be null.
+     * @deprecated This is done automatically by {@link MainPanel}
      */
+    @Deprecated
     public final void setMapFrame(final MapFrame map) {
-        MapFrame old = Main.map;
-        panel.setVisible(false);
-        panel.removeAll();
-        if (map != null) {
-            map.fillPanel(panel);
-        } else {
-            old.destroy();
-            panel.add(gettingStarted, BorderLayout.CENTER);
-        }
-        panel.setVisible(true);
-        redoUndoListener.commandChanged(0, 0);
-
-        Main.map = map;
-
-        // Notify map frame listeners, mostly plugins.
-        if ((map == null) == (old == null)) {
-            Main.warn("Replacing the map frame. This is not expected by some plugins and should not happen.");
-        }
-        for (MapFrameListener listener : mapFrameListeners) {
-            MapView.fireDeprecatedListenerOnAdd = true;
-            listener.mapFrameInitialized(old, map);
-            MapView.fireDeprecatedListenerOnAdd = false;
-        }
-        if (map == null && currentProgressMonitor != null) {
-            currentProgressMonitor.showForegroundDialog();
-        }
+        Main.warn("setMapFrame call was ignored.");
     }
 
     /**
      * Remove the specified layer from the map. If it is the last layer,
      * remove the map as well.
+     * <p>
+     * To be removed end of 2016
      * @param layer The layer to remove
+     * @deprecated You can remove the layer using {@link #getLayerManager()}
      */
+    @Deprecated
     public final synchronized void removeLayer(final Layer layer) {
         if (map != null) {
             getLayerManager().removeLayer(layer);
-            if (isDisplayingMapView() && getLayerManager().getLayers().isEmpty()) {
-                setMapFrame(null);
-            }
         }
     }
 
@@ -574,30 +557,11 @@ public abstract class Main {
      */
     public Main() {
         main = this;
-        getLayerManager().addLayerChangeListener(new LayerChangeListener() {
+        mainPanel.addAndFireMapFrameListener(new MapFrameListener() {
             @Override
-            public void layerAdded(LayerAddEvent e) {
-                Layer layer = e.getAddedLayer();
-                if (map == null) {
-                    Main.main.createMapFrame(layer, null);
-                    Main.map.setVisible(true);
-                }
-                ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
-                if (viewProjectionBounds != null) {
-                    Main.map.mapView.scheduleZoomTo(new ViewportData(viewProjectionBounds));
-                }
+            public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
+                redoUndoListener.commandChanged(0, 0);
             }
-
-            @Override
-            public void layerRemoving(LayerRemoveEvent e) {
-                // empty
-            }
-
-            @Override
-            public void layerOrderChanged(LayerOrderChangeEvent e) {
-                //empty
-            }
-
         });
     }
 
@@ -797,14 +761,18 @@ public abstract class Main {
      * Add a new layer to the map.
      *
      * If no map exists, create one.
+     * <p>
+     * To be removed end of 2016
      *
      * @param layer the layer
      *
      * @see #addLayer(Layer, ProjectionBounds)
      * @see #addLayer(Layer, ViewportData)
+     * @deprecated You can add the layer to the layer manager: {@link #getLayerManager()}
      */
+    @Deprecated
     public final void addLayer(final Layer layer) {
-        addLayer(layer, layer.getViewProjectionBounds());
+        addLayer(layer, (ViewportData) null);
     }
 
     /**
@@ -830,37 +798,35 @@ public abstract class Main {
      */
     public final void addLayer(Layer layer, ViewportData viewport) {
         getLayerManager().addLayer(layer);
-        if (viewport != null) {
+        if (viewport != null && Main.map.mapView != null) {
+            // MapView may be null in headless mode here.
             Main.map.mapView.scheduleZoomTo(viewport);
         }
     }
 
     /**
      * Creates the map frame. Call only in EDT Thread.
+     * <p>
+     * To be removed any time
      * @param firstLayer The first layer that was added.
      * @param viewportData The initial viewport. Can be <code>null</code> to be automatically computed.
+     * @deprecated Not supported. MainPanel does this automatically.
      */
+    @Deprecated
     public synchronized void createMapFrame(Layer firstLayer, ViewportData viewportData) {
         GuiHelper.assertCallFromEdt();
-        MapFrame mapFrame = new MapFrame(contentPanePrivate, viewportData);
-        setMapFrame(mapFrame);
-        if (firstLayer != null) {
-            mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), firstLayer);
-        }
-        mapFrame.initializeDialogsPane();
-        // bootstrapping problem: make sure the layer list dialog is going to
-        // listen to change events of the very first layer
-        //
-        if (firstLayer != null) {
-            firstLayer.addPropertyChangeListener(LayerListDialog.getInstance().getModel());
-        }
+        Main.error("createMapFrame() not supported any more.");
     }
 
     /**
      * Replies <code>true</code> if there is an edit layer
+     * <p>
+     * To be removed end of 2016
      *
      * @return <code>true</code> if there is an edit layer
+     * @deprecated You can get the edit layer using the layer manager and then check if it is not null: {@link #getLayerManager()}
      */
+    @Deprecated
     public boolean hasEditLayer() {
         if (getEditLayer() == null) return false;
         return true;
@@ -868,22 +834,28 @@ public abstract class Main {
 
     /**
      * Replies the current edit layer
+     * <p>
+     * To be removed end of 2016
      *
      * @return the current edit layer. <code>null</code>, if no current edit layer exists
+     * @deprecated You can get the edit layer using the layer manager: {@link #getLayerManager()}
      */
+    @Deprecated
     public OsmDataLayer getEditLayer() {
-        if (!isDisplayingMapView()) return null;
         return getLayerManager().getEditLayer();
     }
 
     /**
      * Replies the current data set.
+     * <p>
+     * To be removed end of 2016
      *
      * @return the current data set. <code>null</code>, if no current data set exists
+     * @deprecated You can get the data set using the layer manager: {@link #getLayerManager()}
      */
+    @Deprecated
     public DataSet getCurrentDataSet() {
-        if (!hasEditLayer()) return null;
-        return getEditLayer().data;
+        return getLayerManager().getEditDataSet();
     }
 
     /**
@@ -907,11 +879,14 @@ public abstract class Main {
 
     /**
      * Returns the currently active  layer
+     * <p>
+     * To be removed end of 2016
      *
      * @return the currently active layer. <code>null</code>, if currently no active layer exists
+     * @deprecated You can get the layer using the layer manager: {@link #getLayerManager()}
      */
+    @Deprecated
     public Layer getActiveLayer() {
-        if (!isDisplayingMapView()) return null;
         return getLayerManager().getActiveLayer();
     }
 
@@ -976,7 +951,7 @@ public abstract class Main {
     /**
      * Global panel.
      */
-    public static final JPanel panel = new JPanel(new BorderLayout());
+    public static final JPanel panel = mainPanel;
 
     private final CommandQueueListener redoUndoListener = new CommandQueueListener() {
         @Override
@@ -1162,12 +1137,7 @@ public abstract class Main {
             map.rememberToggleDialogWidth();
         }
         // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
-        if (Main.isDisplayingMapView()) {
-            Collection<Layer> layers = new ArrayList<>(getLayerManager().getLayers());
-            for (Layer l: layers) {
-                Main.main.removeLayer(l);
-            }
-        }
+        getLayerManager().resetState();
         try {
             pref.saveDefaults();
         } catch (IOException ex) {
@@ -1653,11 +1623,11 @@ public abstract class Main {
      * @return {@code true} if the listeners collection changed as a result of the call
      */
     public static boolean addMapFrameListener(MapFrameListener listener, boolean fireWhenMapViewPresent) {
-        boolean changed = listener != null && mapFrameListeners.add(listener);
-        if (fireWhenMapViewPresent && changed && map != null) {
-            listener.mapFrameInitialized(null, map);
+        if (fireWhenMapViewPresent) {
+            return mainPanel.addAndFireMapFrameListener(listener);
+        } else {
+            return mainPanel.addMapFrameListener(listener);
         }
-        return changed;
     }
 
     /**
@@ -1667,7 +1637,7 @@ public abstract class Main {
      * @since 5957
      */
     public static boolean addMapFrameListener(MapFrameListener listener) {
-        return addMapFrameListener(listener, false);
+        return mainPanel.addMapFrameListener(listener);
     }
 
     /**
@@ -1677,7 +1647,7 @@ public abstract class Main {
      * @since 5957
      */
     public static boolean removeMapFrameListener(MapFrameListener listener) {
-        return listener != null && mapFrameListeners.remove(listener);
+        return mainPanel.removeMapFrameListener(listener);
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/actions/SessionLoadAction.java b/src/org/openstreetmap/josm/actions/SessionLoadAction.java
index cd390f2..6d2b2ee 100644
--- a/src/org/openstreetmap/josm/actions/SessionLoadAction.java
+++ b/src/org/openstreetmap/josm/actions/SessionLoadAction.java
@@ -124,21 +124,17 @@ public class SessionLoadAction extends DiskAccessAction {
 
         private void addLayers() {
             if (layers != null && !layers.isEmpty()) {
-                Layer firstLayer = layers.get(0);
                 boolean noMap = Main.map == null;
-                if (noMap) {
-                    Main.main.createMapFrame(firstLayer, viewport);
-                }
                 for (Layer l : layers) {
                     if (canceled)
                         return;
-                    Main.main.addLayer(l, (ViewportData) null);
+                    Main.getLayerManager().addLayer(l);
                 }
                 if (active != null) {
                     Main.getLayerManager().setActiveLayer(active);
                 }
                 if (noMap) {
-                    Main.map.setVisible(true);
+                    Main.map.mapView.scheduleZoomTo(viewport);
                 }
             }
         }
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/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
+++ b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
@@ -12,6 +12,7 @@ import java.util.regex.Pattern;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Bounds.ParseMethod;
+import org.openstreetmap.josm.data.ViewportData;
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.GpxLayer;
@@ -140,14 +141,13 @@ public class DownloadGpsTask extends AbstractDownloadTask<GpxData> {
         private <L extends Layer> L addOrMergeLayer(L layer, L mergeLayer) {
             if (layer == null) return null;
             if (newLayer || mergeLayer == null) {
-                if (Main.main != null) {
-                    Main.main.addLayer(layer);
-                }
+                Main.getLayerManager().addLayer(layer);
                 return layer;
             } else {
                 mergeLayer.mergeFrom(layer);
+                mergeLayer.invalidate();
                 if (Main.map != null) {
-                    Main.map.repaint();
+                    Main.map.mapView.scheduleZoomTo(new ViewportData(layer.getViewProjectionBounds()));
                 }
                 return mergeLayer;
             }
diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java
index 5727fa5..58ebfc2 100644
--- a/src/org/openstreetmap/josm/gui/MainApplication.java
+++ b/src/org/openstreetmap/josm/gui/MainApplication.java
@@ -316,6 +316,15 @@ public class MainApplication extends Main {
 
         initApplicationPreferences();
 
+        // Can only be called after preferences are initialized.
+        // We can move this to MainPanel constructor as soon as noone depends on Main#panel any more.
+        GuiHelper.runInEDTAndWait(new Runnable() {
+            @Override
+            public void run() {
+                mainPanel.updateContent();
+            }
+        });
+
         Policy.setPolicy(new Policy() {
             // Permissions for plug-ins loaded when josm is started via webstart
             private PermissionCollection pc;
@@ -398,7 +407,7 @@ public class MainApplication extends Main {
         WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry",
                 args.containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null,
                 !args.containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));
-        final MainFrame mainFrame = new MainFrame(contentPanePrivate, geometry);
+        final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry);
         Main.parent = mainFrame;
 
         if (args.containsKey(Option.LOAD_PREFERENCES)) {
diff --git a/src/org/openstreetmap/josm/gui/MainFrame.java b/src/org/openstreetmap/josm/gui/MainFrame.java
index 75ac489..7b5a654 100644
--- a/src/org/openstreetmap/josm/gui/MainFrame.java
+++ b/src/org/openstreetmap/josm/gui/MainFrame.java
@@ -39,10 +39,6 @@ import org.openstreetmap.josm.tools.WindowGeometry;
  * @since 10340
  */
 public class MainFrame extends JFrame {
-    protected transient WindowGeometry geometry;
-    protected int windowState = JFrame.NORMAL;
-    private MainMenu menu;
-
     private final transient LayerStateChangeListener updateTitleOnLayerStateChange = new LayerStateChangeListener() {
         @Override
         public void uploadDiscouragedChanged(OsmDataLayer layer, boolean newValue) {
@@ -51,7 +47,6 @@ public class MainFrame extends JFrame {
     };
 
     private final transient PropertyChangeListener updateTitleOnSaveChange = new PropertyChangeListener() {
-
         @Override
         public void propertyChange(PropertyChangeEvent evt) {
             if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP)
@@ -62,19 +57,24 @@ public class MainFrame extends JFrame {
         }
     };
 
+    protected transient WindowGeometry geometry;
+    protected int windowState = JFrame.NORMAL;
+    private MainMenu menu;
+
     /**
      * Create a new main window.
      */
     public MainFrame() {
-        this(new JPanel(), new WindowGeometry(new Rectangle(10, 10, 500, 500)));
+        this(new JPanel(), new MainPanel(Main.getLayerManager()), new WindowGeometry(new Rectangle(10, 10, 500, 500)));
     }
 
     /**
-     * Create a new main window.
+     * Create a new main window. The parameters will be removed in the future.
      * @param contentPanePrivate The content
+     * @param mainPanel The main panel.
      * @param geometry The inital geometry to use.
      */
-    public MainFrame(Container contentPanePrivate, WindowGeometry geometry) {
+    public MainFrame(Container contentPanePrivate, MainPanel mainPanel, WindowGeometry geometry) {
         super();
         this.geometry = geometry;
         setContentPane(contentPanePrivate);
@@ -119,7 +119,6 @@ public class MainFrame extends JFrame {
         refreshTitle();
 
         getContentPane().add(Main.panel, BorderLayout.CENTER);
-        Main.panel.add(Main.main.gettingStarted, BorderLayout.CENTER);
         menu.initialize();
     }
 
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
--- /dev/null
+++ b/src/org/openstreetmap/josm/gui/MainPanel.java
@@ -0,0 +1,185 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui;
+
+import java.awt.BorderLayout;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.mapmode.MapMode;
+import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.layer.MainLayerManager;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+
+/**
+ * This is the content panel inside the {@link MainFrame}. It displays the content the user is working with.
+ * <p>
+ * If there is no active layer, there is no content displayed. As soon as there are active layers, the {@link MapFrame} is displayed.
+ *
+ * @author Michael Zangl
+ * @since xxx
+ */
+public class MainPanel extends JPanel {
+    private MapFrame map;
+    // Needs to be lazy because we need to wait for preferences to set up.
+    private GettingStarted gettingStarted;
+    private final CopyOnWriteArrayList<MapFrameListener> mapFrameListeners = new CopyOnWriteArrayList<>();
+    private final transient MainLayerManager layerManager;
+
+    /**
+     * Create a new main panel
+     * @param layerManager The layer manager to use to display the content.
+     */
+    public MainPanel(MainLayerManager layerManager) {
+        super(new BorderLayout());
+        this.layerManager = layerManager;
+        reAddListeners();
+    }
+
+    /**
+     * Update the content of this {@link MainFrame} to either display the map or display the welcome screen.
+     */
+    protected void updateContent() {
+        GuiHelper.assertCallFromEdt();
+        MapFrame old = map;
+        boolean showMap = !layerManager.getLayers().isEmpty();
+        if (old != null && showMap) {
+            // no state change
+            return;
+        }
+
+        // remove old content
+        setVisible(false);
+        removeAll();
+        if (old != null) {
+            old.destroy();
+        }
+
+        // create new content
+        if (showMap) {
+            map = createNewMapFrame();
+        } else {
+            map = null;
+                    Main.map = map;
+            add(getGettingStarted(), BorderLayout.CENTER);
+        }
+        setVisible(true);
+
+        if (old == null && !showMap) {
+            // listeners may not be able to handle this...
+            return;
+        }
+
+        // Notify map frame listeners, mostly plugins.
+        for (MapFrameListener listener : mapFrameListeners) {
+            MapView.fireDeprecatedListenerOnAdd = true;
+            listener.mapFrameInitialized(old, map);
+            MapView.fireDeprecatedListenerOnAdd = false;
+        }
+        if (map == null && Main.currentProgressMonitor != null) {
+            Main.currentProgressMonitor.showForegroundDialog();
+        }
+    }
+
+    private MapFrame createNewMapFrame() {
+        MapFrame mapFrame = new MapFrame(null, null);
+        // Required by many components.
+        Main.map = mapFrame;
+
+        mapFrame.fillPanel(this);
+
+        //TODO: Move this to some better place
+        List<Layer> layers = Main.getLayerManager().getLayers();
+        if (!layers.isEmpty()) {
+            mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), layers.get(0));
+        }
+        mapFrame.initializeDialogsPane();
+        mapFrame.setVisible(true);
+        return mapFrame;
+    }
+
+    /**
+     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
+     * <p>
+     * It will fire an initial mapFrameInitialized event
+     * when the MapFrame is present. Otherwise will only fire when the MapFrame is created
+     * or destroyed.
+     * @param listener The MapFrameListener
+     * @return {@code true} if the listeners collection changed as a result of the call.
+     * @since xxx
+     */
+    public boolean addAndFireMapFrameListener(MapFrameListener listener) {
+        boolean changed = addMapFrameListener(listener);
+        if (changed && map != null) {
+            listener.mapFrameInitialized(null, map);
+        }
+        return changed;
+    }
+
+    /**
+     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
+     * @param listener The MapFrameListener
+     * @return {@code true} if the listeners collection changed as a result of the call
+     * @since xxx
+     */
+    public boolean addMapFrameListener(MapFrameListener listener) {
+        return listener != null && mapFrameListeners.add(listener);
+    }
+
+    /**
+     * Unregisters the given {@code MapFrameListener} from MapFrame changes
+     * @param listener The MapFrameListener
+     * @return {@code true} if the listeners collection changed as a result of the call
+     * @since xxx
+     */
+    public boolean removeMapFrameListener(MapFrameListener listener) {
+        return listener != null && mapFrameListeners.remove(listener);
+    }
+
+    /**
+     * Gets the {@link GettingStarted} panel.
+     * @return The panel.
+     */
+    public GettingStarted getGettingStarted() {
+        if (gettingStarted == null) {
+            gettingStarted = new GettingStarted();
+        }
+        return gettingStarted;
+    }
+
+    /**
+     * Re-adds the layer listeners. Never call this in production, only needed for testing.
+     */
+    public void reAddListeners() {
+        layerManager.addLayerChangeListener(new LayerChangeListener() {
+            @Override
+            public void layerAdded(LayerAddEvent e) {
+                updateContent();
+            }
+
+            @Override
+            public void layerRemoving(final LayerRemoveEvent e) {
+                // Delay main.map removal until after all listeners are finished.
+                // Some components rely on this and e.g. get the MapView that way.
+                SwingUtilities.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        updateContent();
+                    }
+                });
+            }
+
+            @Override
+            public void layerOrderChanged(LayerOrderChangeEvent e) {
+                // ignored
+            }
+        });
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index b0a4335..e3ab5cc 100644
--- a/src/org/openstreetmap/josm/gui/MapFrame.java
+++ b/src/org/openstreetmap/josm/gui/MapFrame.java
@@ -187,8 +187,7 @@ public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi
 
     /**
      * Constructs a new {@code MapFrame}.
-     * @param contentPane The content pane used to register shortcuts in its
-     * {@link javax.swing.InputMap} and {@link javax.swing.ActionMap}
+     * @param contentPane Ignored. Main content pane is used.
      * @param viewportData the initial viewport of the map. Can be null, then
      * the viewport is derived from the layer data.
      */
diff --git a/src/org/openstreetmap/josm/gui/MapMover.java b/src/org/openstreetmap/josm/gui/MapMover.java
index 377580f..d745f1b 100644
--- a/src/org/openstreetmap/josm/gui/MapMover.java
+++ b/src/org/openstreetmap/josm/gui/MapMover.java
@@ -10,13 +10,10 @@ import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseWheelEvent;
+import java.util.ArrayList;
 
 import javax.swing.AbstractAction;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
-import javax.swing.JComponent;
 import javax.swing.JPanel;
-import javax.swing.KeyStroke;
 
 import org.openstreetmap.gui.jmapviewer.JMapViewer;
 import org.openstreetmap.josm.Main;
@@ -26,6 +23,7 @@ import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
 import org.openstreetmap.josm.tools.Destroyable;
+import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.Shortcut;
 
 /**
@@ -63,7 +61,12 @@ public class MapMover extends MouseAdapter implements Destroyable {
         private final String action;
 
         ZoomerAction(String action) {
+            this(action, "MapMover.Zoomer." + action);
+        }
+
+        public ZoomerAction(String action, String name) {
             this.action = action;
+            putValue(NAME, name);
         }
 
         @Override
@@ -106,62 +109,48 @@ public class MapMover extends MouseAdapter implements Destroyable {
      * The map to move around.
      */
     private final NavigatableComponent nc;
-    private final JPanel contentPane;
 
     private boolean movementInPlace;
 
+    private final ArrayList<Pair<ZoomerAction, Shortcut>> registeredShortcuts = new ArrayList<>();
+
     /**
      * Constructs a new {@code MapMover}.
      * @param navComp the navigatable component
-     * @param contentPane the content pane
+     * @param contentPane Ignored. The main action map is used.
      */
     public MapMover(NavigatableComponent navComp, JPanel contentPane) {
         this.nc = navComp;
-        this.contentPane = contentPane;
         nc.addMouseListener(this);
         nc.addMouseMotionListener(this);
         nc.addMouseWheelListener(this);
 
-        if (contentPane != null) {
-            // CHECKSTYLE.OFF: LineLength
-            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL).getKeyStroke(),
-                "MapMover.Zoomer.right");
-            contentPane.getActionMap().put("MapMover.Zoomer.right", new ZoomerAction("right"));
-
-            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL).getKeyStroke(),
-                "MapMover.Zoomer.left");
-            contentPane.getActionMap().put("MapMover.Zoomer.left", new ZoomerAction("left"));
-
-            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL).getKeyStroke(),
-                "MapMover.Zoomer.up");
-            contentPane.getActionMap().put("MapMover.Zoomer.up", new ZoomerAction("up"));
-
-            contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL).getKeyStroke(),
-                "MapMover.Zoomer.down");
-            contentPane.getActionMap().put("MapMover.Zoomer.down", new ZoomerAction("down"));
-            // CHECKSTYLE.ON: LineLength
-
-            // see #10592 - Disable these alternate shortcuts on OS X because of conflict with system shortcut
-            if (!Main.isPlatformOsx()) {
-                contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                    Shortcut.registerShortcut("view:zoominalternate",
-                            tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL).getKeyStroke(),
-                    "MapMover.Zoomer.in");
-                contentPane.getActionMap().put("MapMover.Zoomer.in", new ZoomerAction(","));
-
-                contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
-                    Shortcut.registerShortcut("view:zoomoutalternate",
-                            tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL).getKeyStroke(),
-                    "MapMover.Zoomer.out");
-                contentPane.getActionMap().put("MapMover.Zoomer.out", new ZoomerAction("."));
-            }
+        registerActionShortcut(new ZoomerAction("right"),
+                Shortcut.registerShortcut("system:movefocusright", tr("Map: {0}", tr("Move right")), KeyEvent.VK_RIGHT, Shortcut.CTRL));
+
+        registerActionShortcut(new ZoomerAction("left"),
+                Shortcut.registerShortcut("system:movefocusleft", tr("Map: {0}", tr("Move left")), KeyEvent.VK_LEFT, Shortcut.CTRL));
+
+        registerActionShortcut(new ZoomerAction("up"),
+                Shortcut.registerShortcut("system:movefocusup", tr("Map: {0}", tr("Move up")), KeyEvent.VK_UP, Shortcut.CTRL));
+        registerActionShortcut(new ZoomerAction("down"),
+                Shortcut.registerShortcut("system:movefocusdown", tr("Map: {0}", tr("Move down")), KeyEvent.VK_DOWN, Shortcut.CTRL));
+
+        // see #10592 - Disable these alternate shortcuts on OS X because of conflict with system shortcut
+        if (!Main.isPlatformOsx()) {
+            registerActionShortcut(new ZoomerAction(",", "MapMover.Zoomer.in"),
+                    Shortcut.registerShortcut("view:zoominalternate", tr("Map: {0}", tr("Zoom in")), KeyEvent.VK_COMMA, Shortcut.CTRL));
+
+            registerActionShortcut(new ZoomerAction(".", "MapMover.Zoomer.out"),
+                    Shortcut.registerShortcut("view:zoomoutalternate", tr("Map: {0}", tr("Zoom out")), KeyEvent.VK_PERIOD, Shortcut.CTRL));
         }
     }
 
+    private void registerActionShortcut(ZoomerAction action, Shortcut shortcut) {
+        Main.registerActionShortcut(action, shortcut);
+        registeredShortcuts.add(new Pair<>(action, shortcut));
+    }
+
     /**
      * If the right (and only the right) mouse button is pressed, move the map.
      */
@@ -269,26 +258,8 @@ public class MapMover extends MouseAdapter implements Destroyable {
 
     @Override
     public void destroy() {
-        if (this.contentPane != null) {
-            InputMap inputMap = contentPane.getInputMap();
-            KeyStroke[] inputKeys = inputMap.keys();
-            if (inputKeys != null) {
-                for (KeyStroke key : inputKeys) {
-                    Object binding = inputMap.get(key);
-                    if (binding instanceof String && ((String) binding).startsWith("MapMover.")) {
-                        inputMap.remove(key);
-                    }
-                }
-            }
-            ActionMap actionMap = contentPane.getActionMap();
-            Object[] actionsKeys = actionMap.keys();
-            if (actionsKeys != null) {
-                for (Object key : actionsKeys) {
-                    if (key instanceof String && ((String) key).startsWith("MapMover.")) {
-                        actionMap.remove(key);
-                    }
-                }
-            }
+        for (Pair<ZoomerAction, Shortcut> shortcut : registeredShortcuts) {
+            Main.unregisterActionShortcut(shortcut.a, shortcut.b);
         }
     }
 }
diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index 1415463..03166a0 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -31,8 +31,6 @@ import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.swing.AbstractButton;
-import javax.swing.ActionMap;
-import javax.swing.InputMap;
 import javax.swing.JComponent;
 import javax.swing.JPanel;
 
@@ -41,6 +39,7 @@ import org.openstreetmap.josm.actions.mapmode.MapMode;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
+import org.openstreetmap.josm.data.ProjectionBounds;
 import org.openstreetmap.josm.data.SelectionChangedListener;
 import org.openstreetmap.josm.data.ViewportData;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -494,8 +493,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
     /**
      * Constructs a new {@code MapView}.
      * @param layerManager The layers to display.
-     * @param contentPane The content pane used to register shortcuts in its
-     * {@link InputMap} and {@link ActionMap}
+     * @param contentPane Ignored. Main content pane is used.
      * @param viewportData the initial viewport of the map. Can be null, then
      * the viewport is derived from the layer data.
      * @since 10279
@@ -599,6 +597,11 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
             playHeadMarker = PlayHeadMarker.create();
         }
 
+        ProjectionBounds viewProjectionBounds = layer.getViewProjectionBounds();
+        if (viewProjectionBounds != null) {
+            scheduleZoomTo(new ViewportData(viewProjectionBounds));
+        }
+
         layer.addPropertyChangeListener(this);
         Main.addProjectionChangeListener(layer);
         invalidatedListener.addTo(layer);
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/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
+++ b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
@@ -330,14 +330,14 @@ public class LayerListDialog extends ToggleDialog {
     @Override
     public void showNotify() {
         layerManager.addActiveLayerChangeListener(activateLayerAction);
-        layerManager.addLayerChangeListener(model);
+        layerManager.addLayerChangeListener(model, true);
         layerManager.addAndFireActiveLayerChangeListener(model);
         model.populate();
     }
 
     @Override
     public void hideNotify() {
-        layerManager.removeLayerChangeListener(model);
+        layerManager.removeLayerChangeListener(model, true);
         layerManager.removeActiveLayerChangeListener(model);
         layerManager.removeActiveLayerChangeListener(activateLayerAction);
     }
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/src/org/openstreetmap/josm/gui/layer/LayerManager.java
+++ b/src/org/openstreetmap/josm/gui/layer/LayerManager.java
@@ -89,10 +89,12 @@ public class LayerManager {
      */
     public static class LayerRemoveEvent extends LayerManagerEvent {
         private final Layer removedLayer;
+        private final boolean lastLayer;
 
         LayerRemoveEvent(LayerManager source, Layer removedLayer) {
             super(source);
             this.removedLayer = removedLayer;
+            this.lastLayer = source.getLayers().size() == 1;
         }
 
         /**
@@ -102,6 +104,14 @@ public class LayerManager {
         public Layer getRemovedLayer() {
             return removedLayer;
         }
+
+        /**
+         * Check if the layer that was removed is the last layer in the list.
+         * @return <code>true</code> if this was the last layer.
+         */
+        public boolean isLastLayer() {
+            return lastLayer;
+        }
     }
 
     /**
@@ -346,4 +356,16 @@ public class LayerManager {
             l.layerOrderChanged(e);
         }
     }
+
+    /**
+     * Reset all layer manager state. This includes removing all layers and then unregistering all listeners
+     */
+    public void resetState() {
+        // some layer remove listeners remove other layers.
+        while (!getLayers().isEmpty()) {
+            removeLayer(getLayers().get(0));
+        }
+
+        layerChangeListeners.clear();
+    }
 }
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/src/org/openstreetmap/josm/gui/layer/MainLayerManager.java
+++ b/src/org/openstreetmap/josm/gui/layer/MainLayerManager.java
@@ -312,4 +312,12 @@ public class MainLayerManager extends LayerManager {
         }
         return ret;
     }
+
+    @Override
+    public void resetState() {
+        // active and edit layer are unset automatically
+        super.resetState();
+
+        activeLayerChangeListeners.clear();
+    }
 }
diff --git a/src/org/openstreetmap/josm/io/NoteImporter.java b/src/org/openstreetmap/josm/io/NoteImporter.java
index da40198..af84702 100644
--- a/src/org/openstreetmap/josm/io/NoteImporter.java
+++ b/src/org/openstreetmap/josm/io/NoteImporter.java
@@ -13,7 +13,6 @@ import org.openstreetmap.josm.actions.ExtensionFileFilter;
 import org.openstreetmap.josm.data.notes.Note;
 import org.openstreetmap.josm.gui.layer.NoteLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.xml.sax.SAXException;
 
 /**
@@ -37,13 +36,8 @@ public class NoteImporter extends FileImporter {
         }
         try (InputStream is = Compression.getUncompressedFileInputStream(file)) {
             final NoteLayer layer = loadLayer(is, file, file.getName(), progressMonitor);
-            if (Main.map == null || !Main.getLayerManager().containsLayer(layer)) {
-                GuiHelper.runInEDT(new Runnable() {
-                    @Override
-                    public void run() {
-                        Main.main.addLayer(layer);
-                    }
-                });
+            if (!Main.getLayerManager().containsLayer(layer)) {
+                Main.getLayerManager().addLayer(layer);
             }
         } catch (SAXException e) {
             Main.error("error opening up notes file");
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/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java
+++ b/test/unit/org/openstreetmap/josm/io/NoteImporterTest.java
@@ -6,10 +6,11 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.gui.layer.Layer;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
 
 /**
  * Unit tests of {@link NoteImporter} class.
@@ -17,16 +18,17 @@ import org.openstreetmap.josm.gui.layer.Layer;
 public class NoteImporterTest {
 
     /**
+     * Use the test rules to remove any layers and reset state.
+     */
+    @Rule
+    public final JOSMTestRules rules = new JOSMTestRules();
+
+    /**
      * Non-regression test for <a href="https://josm.openstreetmap.de/ticket/12531">Bug #12531</a>.
      */
     @Test
     public void testTicket12531() {
-        if (Main.map != null) {
-            for (Layer l: Main.getLayerManager().getLayers()) {
-                Main.getLayerManager().removeLayer(l);
-            }
-            Main.main.setMapFrame(null);
-        }
+        Main.getLayerManager().resetState();
         assertNull(Main.map);
         assertTrue(new NoteImporter().importDataHandleExceptions(
                 new File(TestUtils.getRegressionDataFile(12531, "notes.osn")), null));
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/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
+++ b/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
@@ -12,7 +12,6 @@ import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.projection.Projections;
-import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiInitializationException;
@@ -222,10 +221,7 @@ public class JOSMTestRules implements TestRule {
             }
         });
         // Remove all layers
-        MainLayerManager lm = Main.getLayerManager();
-        while (!lm.getLayers().isEmpty()) {
-            lm.removeLayer(lm.getLayers().get(0));
-        }
+        Main.getLayerManager().resetState();
 
         // TODO: Remove global listeners and other global state.
         Main.pref = null;
