diff --git a/src/org/openstreetmap/josm/actions/JosmAction.java b/src/org/openstreetmap/josm/actions/JosmAction.java
index e111b87..d227894 100644
--- a/src/org/openstreetmap/josm/actions/JosmAction.java
+++ b/src/org/openstreetmap/josm/actions/JosmAction.java
@@ -148,6 +148,13 @@ abstract public class JosmAction extends AbstractAction implements Destroyable {
         DataSet.addSelectionListener(selectionChangeAdapter);
         initEnabledState();
     }
+    protected void uninstallAdapters() {
+        if(layerChangeAdapter != null)
+            MapView.removeLayerChangeListener(layerChangeAdapter);
+        if(selectionChangeAdapter != null) {
+            DataSet.removeSelectionListener(selectionChangeAdapter);
+        }
+    }
 
     /**
      * Override in subclasses to init the enabled state of an action when it is
diff --git a/src/org/openstreetmap/josm/actions/ReloadPluginsAction.java b/src/org/openstreetmap/josm/actions/ReloadPluginsAction.java
new file mode 100644
index 0000000..b5cb91b
--- /dev/null
+++ b/src/org/openstreetmap/josm/actions/ReloadPluginsAction.java
@@ -0,0 +1,23 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.actions;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.tools.Shortcut;
+
+public class ReloadPluginsAction extends JosmAction {
+
+    public ReloadPluginsAction() {
+        super(tr("Reload plugins"), null, tr("Reload plugins"), Shortcut.registerShortcut("system:reload-plugins",
+                tr("Reload plugins"), KeyEvent.VK_F5, Shortcut.GROUP_DIRECT, Shortcut.SHIFT_DEFAULT), true);
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent e) {
+        PluginHandler.reloadPlugins();
+    }
+}
diff --git a/src/org/openstreetmap/josm/gui/MainMenu.java b/src/org/openstreetmap/josm/gui/MainMenu.java
index b17cb98..a86c584 100644
--- a/src/org/openstreetmap/josm/gui/MainMenu.java
+++ b/src/org/openstreetmap/josm/gui/MainMenu.java
@@ -52,6 +52,7 @@ import org.openstreetmap.josm.actions.NewAction;
 import org.openstreetmap.josm.actions.OpenFileAction;
 import org.openstreetmap.josm.actions.OpenLocationAction;
 import org.openstreetmap.josm.actions.OrthogonalizeAction;
+import org.openstreetmap.josm.actions.ReloadPluginsAction;
 import org.openstreetmap.josm.actions.OrthogonalizeAction.Undo;
 import org.openstreetmap.josm.actions.PasteAction;
 import org.openstreetmap.josm.actions.PasteTagsAction;
@@ -359,6 +360,7 @@ public class MainMenu extends JMenuBar {
         current.setAccelerator(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1,
                 Shortcut.GROUP_DIRECT).getKeyStroke());
         add(helpMenu, about);
+        add(helpMenu, new ReloadPluginsAction());
 
         new PresetsMenuEnabler(presetsMenu).refreshEnabled();
     }
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index 96bddae..189e9b9 100644
--- a/src/org/openstreetmap/josm/gui/MapFrame.java
+++ b/src/org/openstreetmap/josm/gui/MapFrame.java
@@ -281,6 +281,15 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
         return button;
     }
 
+    // Hopefully this is enough. TODO: Should we call .validate()?
+    public void removeToggleDialog(final ToggleDialog dlg) {
+        if (dialogsPanel.initialized) {
+            dialogsPanel.remove(dlg);
+        }
+        allDialogs.remove(dlg);
+        toolBarToggle.remove(dlg);
+    }
+
     public void addMapMode(IconToggleButton b) {
         toolBarActions.add(b);
         toolGroup.add(b);
@@ -290,6 +299,12 @@ public class MapFrame extends JPanel implements Destroyable, LayerChangeListener
             throw new IllegalArgumentException("MapMode action must be subclass of MapMode");
     }
 
+    public void removeMapMode(IconToggleButton mode) {
+        toolBarActions.remove(mode);
+        toolGroup.remove(mode);
+        mapModes.remove(mode.getAction());
+    }
+
     /**
      * Fires an property changed event "visible".
      */
diff --git a/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java b/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
index 7c576fc..66f52df 100644
--- a/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
+++ b/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
@@ -279,6 +279,8 @@ public class PreferenceTabbedPane extends JTabbedPane implements MouseWheelListe
         settingsFactory.add(new RemoteControlPreference.Factory());
         settingsFactory.add(new ImageryPreference.Factory());
 
+        // FIXME: Can we really be sure that all plugins is loaded? This does only work because java are lazy with class loading and this class is referenced late...
+        // Possible to make PluginHandler create a wrapper factory
         PluginHandler.getPreferenceSetting(settingsFactory);
 
         // always the last: advanced tab
diff --git a/src/org/openstreetmap/josm/plugins/Plugin.java b/src/org/openstreetmap/josm/plugins/Plugin.java
index 0ef60ac..058da9c 100644
--- a/src/org/openstreetmap/josm/plugins/Plugin.java
+++ b/src/org/openstreetmap/josm/plugins/Plugin.java
@@ -72,6 +72,10 @@ public abstract class Plugin {
         this.info = info;
     }
 
+    public void preReloadCleanup() {
+        System.out.println("no cleanup for this class");
+    }
+
     /**
      * @return The directory for the plugin to store all kind of stuff.
      */
diff --git a/src/org/openstreetmap/josm/plugins/PluginHandler.java b/src/org/openstreetmap/josm/plugins/PluginHandler.java
index db94edf..c45708f 100644
--- a/src/org/openstreetmap/josm/plugins/PluginHandler.java
+++ b/src/org/openstreetmap/josm/plugins/PluginHandler.java
@@ -13,6 +13,7 @@ import java.awt.Window;
 import java.awt.event.ActionEvent;
 import java.io.File;
 import java.io.FilenameFilter;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.ArrayList;
@@ -54,10 +55,11 @@ import org.openstreetmap.josm.gui.JMultilineLabel;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.download.DownloadSelection;
 import org.openstreetmap.josm.gui.help.HelpUtil;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
+import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -429,13 +431,9 @@ public class PluginHandler {
     }
 
     /**
-     * Creates a class loader for loading plugin code.
-     *
-     * @param plugins the collection of plugins which are going to be loaded with this
-     * class loader
-     * @return the class loader
+     * Helper to extract the jar paths of the plugins
      */
-    public static ClassLoader createClassLoader(Collection<PluginInformation> plugins) {
+    private static URL[] createJarURLs(Collection<PluginInformation> plugins) {
         // iterate all plugins and collect all libraries of all plugins:
         List<URL> allPluginLibraries = new LinkedList<URL>();
         File pluginDir = Main.pref.getPluginsDirectory();
@@ -451,11 +449,140 @@ public class PluginHandler {
 
         // create a classloader for all plugins:
         URL[] jarUrls = new URL[allPluginLibraries.size()];
-        jarUrls = allPluginLibraries.toArray(jarUrls);
+        return allPluginLibraries.toArray(jarUrls);
+    }
+    /**
+     * Creates a class loader for loading plugin code.
+     *
+     * @param plugins the collection of plugins which are going to be loaded with this
+     * class loader
+     * @return the class loader
+     */
+    public static ClassLoader createClassLoader(Collection<PluginInformation> plugins) {
+        URL[] jarUrls = createJarURLs(plugins);
         URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
         return pluginClassLoader;
     }
 
+    private static String getPackagePartOfClassName(String name) {
+        for (int i = name.length() - 1; i >= 0; i--) {
+            if (name.charAt(i) == '.')
+                return name.substring(0, i);
+        }
+        return "";
+    }
+
+    private static boolean isSubPackageOf(String sub, String parent) {
+        return sub.startsWith(parent);
+    }
+
+    /**
+     * Creates a class loader that will reload the supplied plugins
+     *
+     * The plugins should not define classes in packages above the package of the 'Plugin-Class' (defined in MANIFEST)
+     * ie. all classes must belong to a package matching: <plugin-class-package>.*
+     */
+    public static ClassLoader createClassReloader(Collection<PluginInformation> plugins) {
+        final Set<String> packageList = new HashSet<String>();
+        for (PluginInformation pi : plugins) {
+            packageList.add(getPackagePartOfClassName(pi.className));
+        }
+
+        ClassLoader deceiving = new ClassLoader(Main.class.getClassLoader()) {
+            // Overriding loadClass/1 does not seem to work ...
+            // Are the 'fullClassName' guaranteed to be full?
+            @Override
+            protected Class<?> loadClass(String fullClassName, boolean resolve) throws ClassNotFoundException {
+                String packageOfClass = getPackagePartOfClassName(fullClassName);
+                boolean hide = false;
+                for(String pkg : packageList) {
+                    if(isSubPackageOf(packageOfClass, pkg)) {
+                        hide = true;
+                        break;
+                    }
+                }
+                if(hide) {
+                    System.out.println(fullClassName);
+                    return null;
+                } else {
+                    // is it possible that super.loadClass/2 calls this.loadClass/2 again, causing infinite recursion?
+                    return super.loadClass(fullClassName, resolve);
+                }
+            }
+        };
+        URL[] jarUrls = createJarURLs(plugins);
+        return new URLClassLoader(jarUrls, deceiving) {
+            // make it easier to identify the current class loader in heapdumps
+            public long time = System.currentTimeMillis();
+            @Override public String toString() {
+                return "Reloading CL";
+            }
+        };
+    }
+
+    // TODO: MANIFEST property instead? This is simpler for everyone though?
+    private static boolean supportReload(PluginProxy pluginProxy) {
+        try {
+            Method m = pluginProxy.plugin.getClass().getMethod("preReloadCleanup");
+            return !m.getDeclaringClass().equals(Plugin.class);
+        } catch (NoSuchMethodException e) {
+            return false;
+        }
+    }
+
+    // FIXME: fucked if p1 depends on p2 and only p2 is reloadable
+    // Should be OK to let 'sources' keep the old class loader since the new class loader is prepended?
+    //   Somewhat complicated if we should filter out the reloaded jars from the old loader...
+    // TODO: This does not check preconditions for the plugins, assuming they are fulfilled as the plugins are already loaded. This is of course not 100% correct.
+    // TODO: add progressmonitor support
+    // TODO: do we need to respect the loadearly/late flags?
+    // TODO: seems to be quick enough, but is it problematic to put in a worker thread?
+    // TODO: use 'Plugin-Date' manifest value if defined to determine if plugin need reload
+    /**
+     * Reloads all loaded plugins implementing 'preReloadCleanup'
+     */
+    public static void reloadPlugins() {
+        //        ProgressMonitor monitor = null;
+        //        if (monitor == null) {
+        //            monitor = NullProgressMonitor.INSTANCE;
+        //        }
+        //        loadLocallyAvailablePluginInformation(null); // can't be run from the gui thread
+        List<PluginInformation> reloadablePlugins = new ArrayList<PluginInformation>();
+        for (Iterator<PluginProxy> iter = pluginList.iterator(); iter.hasNext();) {
+            PluginProxy pp = iter.next();
+            if (supportReload(pp)) {
+                pp.preReloadCleanup();
+                reloadablePlugins.add(pp.getPluginInformation());
+                iter.remove();
+            }
+        }
+        if (reloadablePlugins.isEmpty())
+            return;
+        // safest not to have a to-be-reloaded map mode active
+        if (Main.map != null) {
+            Main.map.selectSelectTool(false);
+        }
+        //        try {
+        //            List<PluginInformation> plugins = PluginHandler.buildListOfPluginsToLoad(null,monitor.createSubTaskMonitor(1, false));
+        //            monitor.beginTask(tr("Loading plugins ..."));
+        //            monitor.subTask(tr("Checking plugin preconditions..."));
+        //            Collection<PluginInformation> toLoad = preproccessPluginList(null, plugins);
+
+        ClassLoader cl = createClassReloader(reloadablePlugins);
+
+        List<PluginProxy> nonReloadablePlugins = new ArrayList<PluginProxy>(pluginList);
+        loadPluginsNoCheck(null, reloadablePlugins, null, cl); // adds the plugins to pluginList
+        for (PluginProxy plugin : pluginList) {
+            if (!nonReloadablePlugins.contains(plugin)) { // only notify reloaded plugins
+                plugin.mapFrameInitialized(Main.map, Main.map);
+            }
+        }
+        //        PluginHandler.notifyMapFrameChanged(Main.map, Main.map);
+        //        } finally {
+        //            monitor.finishTask();
+        //        }
+    }
+
     /**
      * Loads and instantiates the plugin described by <code>plugin</code> using
      * the class loader <code>pluginClassLoader</code>.
@@ -493,46 +620,59 @@ public class PluginHandler {
      * @param plugins the list of plugins
      * @param monitor the progress monitor. Defaults to {@see NullProgressMonitor#INSTANCE} if null.
      */
-    public static void loadPlugins(Window parent,Collection<PluginInformation> plugins, ProgressMonitor monitor) {
+    public static void loadPlugins(Window parent, Collection<PluginInformation> plugins, ProgressMonitor monitor) {
         if (monitor == null) {
             monitor = NullProgressMonitor.INSTANCE;
         }
         try {
             monitor.beginTask(tr("Loading plugins ..."));
             monitor.subTask(tr("Checking plugin preconditions..."));
-            List<PluginInformation> toLoad = new LinkedList<PluginInformation>();
-            for (PluginInformation pi: plugins) {
-                if (checkLoadPreconditions(parent, plugins, pi)) {
-                    toLoad.add(pi);
-                }
-            }
-            // sort the plugins according to their "staging" equivalence class. The
-            // lower the value of "stage" the earlier the plugin should be loaded.
-            //
-            Collections.sort(
-                    toLoad,
-                    new Comparator<PluginInformation>() {
-                        public int compare(PluginInformation o1, PluginInformation o2) {
-                            if (o1.stage < o2.stage) return -1;
-                            if (o1.stage == o2.stage) return 0;
-                            return 1;
-                        }
-                    }
-            );
+            Collection<PluginInformation> toLoad = preproccessPluginList(parent, plugins);
             if (toLoad.isEmpty())
                 return;
+            loadPluginsNoCheck(parent, toLoad, monitor, PluginHandler.createClassLoader(toLoad));
+        } finally {
+            monitor.finishTask();
+        }
+    }
 
-            ClassLoader pluginClassLoader = createClassLoader(toLoad);
-            sources.add(0, pluginClassLoader);
-            monitor.setTicksCount(toLoad.size());
-            for (PluginInformation info : toLoad) {
+    public static void loadPluginsNoCheck(Window parent, Collection<PluginInformation> plugins,
+            ProgressMonitor monitor, ClassLoader classLoader) {
+        sources.add(0, classLoader);
+        if (monitor != null)
+            monitor.setTicksCount(plugins.size());
+        for (PluginInformation info : plugins) {
+            if (monitor != null)
                 monitor.setExtraText(tr("Loading plugin ''{0}''...", info.name));
-                loadPlugin(parent, info, pluginClassLoader);
+            else
+                System.out.println(tr("Loading plugin ''{0}''...", info.name));
+            loadPlugin(parent, info, classLoader);
+            if (monitor != null)
                 monitor.worked(1);
+        }
+    }
+
+    private static Collection<PluginInformation> preproccessPluginList(Window parent,
+            Collection<PluginInformation> plugins) {
+        List<PluginInformation> toLoad = new LinkedList<PluginInformation>();
+        for (PluginInformation pi : plugins) {
+            if (checkLoadPreconditions(parent, plugins, pi)) {
+                toLoad.add(pi);
             }
-        } finally {
-            monitor.finishTask();
         }
+        // sort the plugins according to their "staging" equivalence class. The
+        // lower the value of "stage" the earlier the plugin should be loaded.
+        //
+        Collections.sort(toLoad, new Comparator<PluginInformation>() {
+            public int compare(PluginInformation o1, PluginInformation o2) {
+                if (o1.stage < o2.stage)
+                    return -1;
+                if (o1.stage == o2.stage)
+                    return 0;
+                return 1;
+            }
+        });
+        return toLoad;
     }
 
     /**
@@ -838,18 +978,59 @@ public class PluginHandler {
         return null;
     }
 
+    // FIXME: add mechanism to remove DowloadSections
     public static void addDownloadSelection(List<DownloadSelection> downloadSelections) {
         for (PluginProxy p : pluginList) {
             p.addDownloadSelection(downloadSelections);
         }
     }
 
-    public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings) {
-        for (PluginProxy plugin : pluginList) {
-            settings.add(new PluginPreferenceFactory(plugin));
+    // FIXME: Quick hack to control the preference dialog (The preferences system could use a refactoring IMO)
+    // Relies on internal behavior in at least PreferenceTabbedPane. Adding functionality there is probably better
+    // long-term.
+    //
+    // The hack fixes two issues (in reality they are the same issue):
+    // Before, PreferenceTabbedPane would keep a reference to all plugins in its PreferenceSettingFactory list,
+    // causing stray references to reloaded plugins.
+    // Secondary, the preferences dialog will now actually work for reloaded plugins.
+    private static class JointPluginPreferenceSettingsFactory implements PreferenceSettingFactory {
+        @Override
+        public PreferenceSetting createPreferenceSetting() {
+            return new PreferenceSetting() {
+                private List<PreferenceSetting> settings = new ArrayList<PreferenceSetting>();
+
+                { // "Constructor"
+                    for (PluginProxy plugin : pluginList) {
+                        // Delay setting creation so we don't depend on static initializer magic(?)
+                        PreferenceSetting setting = plugin.getPreferenceSetting();
+                        if (setting != null)
+                            settings.add(setting);
+                    }
+                }
+
+                @Override
+                public boolean ok() {
+                    for (PreferenceSetting s : settings) {
+                        s.ok();
+                    }
+                    return false;
+                }
+
+                @Override
+                public void addGui(PreferenceTabbedPane gui) {
+                    for (PreferenceSetting s : settings) {
+                        s.addGui(gui);
+                    }
+                }
+            };
         }
     }
 
+    // This is only called once from the static initializer in PreferenceTabbedPane
+    public static void getPreferenceSetting(Collection<PreferenceSettingFactory> settings) {
+        settings.add(new JointPluginPreferenceSettingsFactory());
+    }
+
     /**
      * Installs downloaded plugins. Moves files with the suffix ".jar.new" to the corresponding
      * ".jar" files.
diff --git a/src/org/openstreetmap/josm/plugins/PluginProxy.java b/src/org/openstreetmap/josm/plugins/PluginProxy.java
index 9d2e094..8388c79 100644
--- a/src/org/openstreetmap/josm/plugins/PluginProxy.java
+++ b/src/org/openstreetmap/josm/plugins/PluginProxy.java
@@ -1,6 +1,7 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.plugins;
 
+import java.lang.reflect.Method;
 import java.util.List;
 
 import org.openstreetmap.josm.gui.MapFrame;
@@ -33,6 +34,16 @@ public class PluginProxy extends Plugin {
         }
     }
 
+    @Override public void preReloadCleanup() {
+        try {
+            Method m = plugin.getClass().getMethod("preReloadCleanup");
+            m.invoke(plugin);
+        } catch (NoSuchMethodException e) {
+        } catch (Exception e) {
+            BugReportExceptionHandler.handleException(new PluginException(this, getPluginInformation().name, e));
+        }
+    }
+
     @Override public PreferenceSetting getPreferenceSetting() {
         try {
             return (PreferenceSetting)plugin.getClass().getMethod("getPreferenceSetting").invoke(plugin);
