Index: /trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/Main.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/Main.java	(revision 1326)
@@ -12,16 +12,8 @@
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.SortedMap;
 import java.util.StringTokenizer;
-import java.util.TreeMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -34,5 +26,4 @@
 import javax.swing.UIManager;
 
-import org.openstreetmap.josm.actions.AboutAction;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
@@ -57,6 +48,5 @@
 import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
 import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
-import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
@@ -102,8 +92,4 @@
      */
     public static MapFrame map;
-    /**
-     * All installed and loaded plugins (resp. their main classes)
-     */
-    public final static Collection<PluginProxy> plugins = new LinkedList<PluginProxy>();
     /**
      * The dialog that gets displayed during background task execution.
@@ -164,20 +150,5 @@
         redoUndoListener.commandChanged(0,0);
 
-        for (PluginProxy plugin : plugins)
-            plugin.mapFrameInitialized(old, map);
-    }
-
-    /**
-     * Set the layer menu (changed when active layer changes).
-     */
-    public final void setLayerMenu(Component[] entries) {
-        //if (entries == null || entries.length == 0)
-            //menu.layerMenu.setVisible(false);
-        //else {
-            //menu.layerMenu.removeAll();
-            //for (Component c : entries)
-                //menu.layerMenu.add(c);
-            //menu.layerMenu.setVisible(true);
-        //}
+        PluginHandler.setMapFrame(old, map);
     }
 
@@ -197,5 +168,5 @@
         this(null);
     }
-    
+
     public Main(SplashScreen splash) {
         main = this;
@@ -205,5 +176,5 @@
         if(splash != null) splash.setStatus(tr("Download \"Message of the day\""));
         panel.add(new GettingStarted(), BorderLayout.CENTER);
-        
+
         if(splash != null) splash.setStatus(tr("Creating main GUI"));
         menu = new MainMenu();
@@ -214,5 +185,7 @@
         contentPane.add(toolbar.control, BorderLayout.NORTH);
 
-        contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, Shortcut.GROUP_DIRECT).getKeyStroke(), "Help");
+        contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
+        .put(Shortcut.registerShortcut("system:help", tr("Help"),
+        KeyEvent.VK_F1, Shortcut.GROUP_DIRECT).getKeyStroke(), "Help");
         contentPane.getActionMap().put("Help", menu.help);
 
@@ -224,122 +197,4 @@
         toolbar.control.updateUI();
         contentPane.updateUI();
-    }
-
-    /**
-     * Load all plugins specified in preferences. If the parameter is
-     * <code>true</code>, all early plugins are loaded (before constructor).
-     */
-    public static void loadPlugins(boolean early) {
-        List<String> plugins = new LinkedList<String>();
-        Collection<String> cp = Main.pref.getCollection("plugins", null);
-        if (cp != null)
-            plugins.addAll(cp);
-        if (System.getProperty("josm.plugins") != null)
-            plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
-
-        String [] oldplugins = new String[] {"mappaint", "unglueplugin",
-        "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
-        "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat"};
-        for (String p : oldplugins) {
-            if (plugins.contains(p)) {
-                plugins.remove(p);
-                Main.pref.removeFromCollection("plugins", p);
-                System.out.println(tr("Warning - loading of {0} plugin was requested. This plugin is no longer required.", p));
-            }
-        }
-
-        if (plugins.isEmpty())
-            return;
-
-        SortedMap<Integer, Collection<PluginInformation>> p = new TreeMap<Integer, Collection<PluginInformation>>();
-        for (String pluginName : plugins) {
-            PluginInformation info = PluginInformation.findPlugin(pluginName);
-            if (info != null) {
-                if (info.early != early)
-                    continue;
-                if (info.mainversion != null) {
-                    int requiredJOSMVersion = 0;
-                    try {
-                        requiredJOSMVersion = Integer.parseInt(info.mainversion);
-                    } catch(NumberFormatException e) {
-                        e.printStackTrace();
-                    }
-                    if (requiredJOSMVersion > AboutAction.getVersionNumber()) {
-                        JOptionPane.showMessageDialog(Main.parent, tr("Plugin requires JOSM update: {0}.", pluginName));
-                        continue;
-                    }
-                }
-                if (!p.containsKey(info.stage))
-                    p.put(info.stage, new LinkedList<PluginInformation>());
-                p.get(info.stage).add(info);
-            } else {
-                if (early)
-                    System.out.println("Plugin not found: "+pluginName); // do not translate
-                else
-                    JOptionPane.showMessageDialog(Main.parent, tr("Plugin not found: {0}.", pluginName));
-            }
-        }
-
-        if (!early) {
-            long tim = System.currentTimeMillis();
-            long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
-            Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 30);
-            long d = (tim - last)/(24*60*60*1000l);
-            if ((last <= 0) || (maxTime <= 0)) {
-                Main.pref.put("pluginmanager.lastupdate",Long.toString(tim));
-            } else if (d > maxTime) {
-                JOptionPane.showMessageDialog(Main.parent,
-                   "<html>" +
-                   tr("Last plugin update more than {0} days ago.", d) +
-                   "<br><em>" +
-                   tr("(You can change the number of days after which this warning appears<br>by setting the config option 'pluginmanager.warntime'.)") +
-                   "</html>");
-            }
-        }
-
-        // iterate all plugins and collect all libraries of all plugins:
-        List<URL> allPluginLibraries = new ArrayList<URL>();
-        for (Collection<PluginInformation> c : p.values())
-            for (PluginInformation info : c)
-                allPluginLibraries.addAll(info.libraries);
-        // create a classloader for all plugins:
-        URL[] jarUrls = new URL[allPluginLibraries.size()];
-        jarUrls = allPluginLibraries.toArray(jarUrls);
-        URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
-        ImageProvider.sources.add(0, pluginClassLoader);
-
-        for (Collection<PluginInformation> c : p.values()) {
-            for (PluginInformation info : c) {
-                try {
-                    Class<?> klass = info.loadClass(pluginClassLoader);
-                    if (klass != null) {
-                        System.out.println("loading "+info.name);
-                        Main.plugins.add(info.load(klass));
-                    }
-                } catch (Throwable e) {
-                    e.printStackTrace();
-                    boolean remove = true;
-                    if (early)
-                        System.out.println("Could not load plugin: "+info.name+" - deleted from preferences"); // do not translate
-                    else {
-                        int answer = JOptionPane.showConfirmDialog(Main.parent,
-                            tr("Could not load plugin {0}. Delete from preferences?", info.name,
-                            JOptionPane.YES_NO_OPTION));
-                        if (answer != JOptionPane.OK_OPTION) {
-                            remove = false;
-                        }
-                    }
-                    if (remove) {
-                        plugins.remove(info.name);
-                        String plist = null;
-                        for (String pn : plugins) {
-                            if (plist==null) plist=""; else plist=plist+",";
-                            plist=plist+pn;
-                        }
-                        Main.pref.put("plugins", plist);
-                    }
-                }
-            }
-        }
     }
 
@@ -370,5 +225,4 @@
      */
     public static final JPanel contentPane = new JPanel(new BorderLayout());
-
 
     ///////////////////////////////////////////////////////////////////////////
Index: /trunk/src/org/openstreetmap/josm/actions/AboutAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/AboutAction.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/actions/AboutAction.java	(revision 1326)
@@ -15,12 +15,8 @@
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.Map.Entry;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.swing.AbstractAction;
 import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.JButton;
 import javax.swing.JLabel;
 import javax.swing.JOptionPane;
@@ -29,8 +25,7 @@
 import javax.swing.JTabbedPane;
 import javax.swing.JTextArea;
-import javax.swing.UIManager;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -145,42 +140,10 @@
         about.addTab(tr("Revision"), createScrollPane(revision));
         about.addTab(tr("Contribution"), createScrollPane(contribution));
-
-        JPanel pluginTab = new JPanel(new GridBagLayout());
-        for (final PluginProxy p : Main.plugins) {
-            String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
-            pluginTab.add(new JLabel(name), GBC.std());
-            pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
-            pluginTab.add(new JButton(new AbstractAction(tr("Information")){
-                public void actionPerformed(ActionEvent event) {
-                    StringBuilder b = new StringBuilder();
-                    for (Entry<String,String> e : p.info.attr.entrySet()) {
-                        b.append(e.getKey());
-                        b.append(": ");
-                        b.append(e.getValue());
-                        b.append("\n");
-                    }
-                    JTextArea a = new JTextArea(10,40);
-                    a.setEditable(false);
-                    a.setText(b.toString());
-                    JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a));
-                }
-            }), GBC.eol());
-
-            JTextArea description = new JTextArea((p.info.description==null? tr("no description available"):p.info.description));
-            description.setEditable(false);
-            description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC));
-            description.setLineWrap(true);
-            description.setWrapStyleWord(true);
-            description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
-            description.setBackground(UIManager.getColor("Panel.background"));
-            
-            pluginTab.add(description, GBC.eop().fill(GBC.HORIZONTAL));
-        }
-        about.addTab(tr("Plugins"), new JScrollPane(pluginTab));
+        about.addTab(tr("Plugins"), new JScrollPane(PluginHandler.getInfoPanel()));
 
         about.setPreferredSize(new Dimension(500,300));
 
         JOptionPane.showMessageDialog(Main.parent, about, tr("About JOSM..."),
-                JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo"));
+        JOptionPane.INFORMATION_MESSAGE, ImageProvider.get("logo"));
     }
 
Index: /trunk/src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 1326)
@@ -1,4 +1,6 @@
 // License: GPL. Copyright 2007 by Immanuel Scholz and others
 package org.openstreetmap.josm.data;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
 
 import java.awt.Color;
@@ -22,4 +24,6 @@
 import java.util.regex.Pattern;
 
+import javax.swing.JOptionPane;
+
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.AboutAction;
@@ -298,8 +302,37 @@
             properties.put(line.substring(0,i), line.substring(i+1));
         }
-        if (!errLines.isEmpty()) {
-            throw new IOException("Malformed config file at lines " + errLines);
-        }
+        if (!errLines.isEmpty())
+            throw new IOException(tr("Malformed config file at lines {0}", errLines));
         setSystemProperties();
+    }
+
+    public void init(Boolean reset)
+    {
+        // get the preferences.
+        File prefDir = getPreferencesDirFile();
+        if (prefDir.exists()) {
+            if(!prefDir.isDirectory()) {
+                JOptionPane.showMessageDialog(null, tr("Cannot open preferences directory: {0}",Main.pref.getPreferencesDir()));
+                return;
+            }
+        }
+        else
+            prefDir.mkdirs();
+
+        if (!new File(getPreferencesDir()+"preferences").exists())
+            resetToDefault();
+
+        try {
+            if (reset)
+                resetToDefault();
+            else
+                load();
+        } catch (final IOException e1) {
+            e1.printStackTrace();
+            String backup = getPreferencesDir() + "preferences.bak";
+            JOptionPane.showMessageDialog(null, tr("Preferences file had errors. Making backup of old one to {0}.", backup));
+            new File(getPreferencesDir() + "preferences").renameTo(new File(backup));
+            save();
+        }
     }
 
@@ -448,5 +481,5 @@
             /* handle space separated stuff - remove in future */
             else if(s.indexOf(' ') >= 0)
-                return Arrays.asList(s.split(","));
+                return Arrays.asList(s.split(" "));
             else
                 return Arrays.asList(s.split(";"));
Index: /trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 1326)
@@ -6,5 +6,4 @@
 import static org.openstreetmap.josm.tools.I18n.i18n;
 import static org.openstreetmap.josm.tools.I18n.tr;
-
 
 import java.awt.EventQueue;
@@ -24,8 +23,7 @@
 
 import javax.swing.JFrame;
-import javax.swing.JOptionPane;
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.plugins.PluginDownloader;
+import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -67,14 +65,7 @@
      */
     public static void main(final String[] argArray) {
-        /////////////////////////////////////////////////////////////////////////
-        //                        TO ALL TRANSLATORS
-        /////////////////////////////////////////////////////////////////////////
-        // Do not translate the early strings below until the locale is set up.
-        // (By the eager loaded plugins)
-        //
-        // These strings cannot be translated. That's life. Really. Sorry.
-        //
-        //                                                                 Imi.
-        /////////////////////////////////////////////////////////////////////////
+        /* try initial language settings, may be changed later again */
+        try { i18n = I18nFactory.getI18n(MainApplication.class); }
+        catch (MissingResourceException ex) { Locale.setDefault(Locale.ENGLISH);}
 
         Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
@@ -101,4 +92,38 @@
         }
 
+        Main.pref.init(args.containsKey("reset-preferences"));
+
+        String localeName = null; // The locale to use
+
+        //Check if passed as parameter
+        if(args.containsKey("language"))
+            localeName = (String)(args.get("language").toArray()[0]);
+
+        if (localeName == null)
+            localeName = Main.pref.get("language", null);
+
+        if (localeName != null) {
+            Locale l;
+            Locale d = Locale.getDefault();
+            if(localeName.equals("he")) localeName = "iw_IL";
+            int i = localeName.indexOf('_');
+            if (i > 0) {
+                l = new Locale(localeName.substring(0, i), localeName.substring(i + 1));
+            } else {
+                l = new Locale(localeName);
+            }
+            try {
+                Locale.setDefault(l);
+                i18n = I18nFactory.getI18n(MainApplication.class);
+            } catch (MissingResourceException ex) {
+                if(!l.getLanguage().equals("en"))
+                {
+                    System.out.println(tr("Unable to find translation for the locale {0}. Reverting to {1}.",
+                    l.getDisplayName(), d.getDisplayName()));
+                    Locale.setDefault(d);
+                }
+            }
+        }
+
         if (argList.contains("--help") || argList.contains("-?") || argList.contains("-h")) {
             // TODO: put in a platformHook for system that have no console by default
@@ -122,5 +147,4 @@
                     "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
                     "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n\n"+
-
                     tr("Parameters are read in the order they are specified, so make sure you load\n"+
                     "some data before --selection")+"\n\n"+
@@ -129,77 +153,11 @@
         }
 
-        // get the preferences.
-        final File prefDir = new File(Main.pref.getPreferencesDir());
-        if (prefDir.exists()) {
-            if(!prefDir.isDirectory()) {
-                JOptionPane.showMessageDialog(null, tr("Cannot open preferences directory: {0}",Main.pref.getPreferencesDir()));
-                return;
-            }
-        }
-        else
-            prefDir.mkdirs();
-
-        if (!new File(Main.pref.getPreferencesDir()+"preferences").exists()) {
-            Main.pref.resetToDefault();
-        }
-
-        try {
-            if (args.containsKey("reset-preferences")) {
-                Main.pref.resetToDefault();
-            } else {
-                Main.pref.load();
-            }
-        } catch (final IOException e1) {
-            e1.printStackTrace();
-            String backup = Main.pref.getPreferencesDir() + "preferences.bak";
-            JOptionPane.showMessageDialog(null, tr("Preferences file had errors. Making backup of old one to {0}.", backup));
-            new File(Main.pref.getPreferencesDir() + "preferences").renameTo(new File(backup));
-            Main.pref.save();
-        }
-
-        String localeName = null; //The locale to use
-
-        //Check if passed as parameter
-        if(args.containsKey("language"))
-            localeName = (String)(args.get("language").toArray()[0]);
-
-        if (localeName == null) {
-            localeName = Main.pref.get("language", null);
-        }
-
-        if (localeName != null) {
-            if(localeName.equals("he")) localeName = "iw_IL";
-            Locale l;
-            int i = localeName.indexOf('_');
-            if (i > 0) {
-                l = new Locale(localeName.substring(0, i), localeName.substring(i + 1));
-            } else {
-                l = new Locale(localeName);
-            }
-            Locale.setDefault(l);
-        }
-        try {
-            i18n = I18nFactory.getI18n(MainApplication.class);
-        } catch (MissingResourceException ex) {
-            if(!Locale.getDefault().getLanguage().equals("en"))
-            {
-                System.out.println("Unable to find translation for the locale: "
-                + Locale.getDefault().getDisplayName() + " reverting to English.");
-                Locale.setDefault(Locale.ENGLISH);
-            }
-        }
-
         SplashScreen splash = new SplashScreen(Main.pref.getBoolean("draw.splashscreen", true));
 
         splash.setStatus(tr("Activating updated plugins"));
-        if (!PluginDownloader.moveUpdatedPlugins()) {
-            JOptionPane.showMessageDialog(null,
-                    tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
-                    tr("Plugins"), JOptionPane.ERROR_MESSAGE);
-        }
+        PluginHandler.earlyCleanup();
 
-        // load the early plugins
         splash.setStatus(tr("Loading early plugins"));
-        Main.loadPlugins(true);
+        PluginHandler.loadPlugins(true);
 
         splash.setStatus(tr("Setting defaults"));
@@ -210,5 +168,5 @@
         final Main main = new MainApplication(mainFrame, splash);
         splash.setStatus(tr("Loading plugins"));
-        Main.loadPlugins(false);
+        PluginHandler.loadPlugins(false);
         toolbar.refreshToolbarControl();
 
Index: /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java	(revision 1326)
@@ -30,5 +30,5 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.gui.MapView;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginHandler;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.OsmUrlToBounds;
@@ -107,7 +107,5 @@
 
         // add selections from plugins
-        for (PluginProxy p : Main.plugins) {
-            p.addDownloadSelection(downloadSelections);
-        }
+        PluginHandler.addDownloadSelection(downloadSelections);
 
         // now everybody may add their tab to the tabbed pane
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java	(revision 1326)
@@ -3,35 +3,16 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
-import static org.openstreetmap.josm.tools.I18n.trn;
 
 import java.awt.Dimension;
-import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
-import java.awt.Insets;
 import java.awt.Rectangle;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.Map.Entry;
 
 import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
 import javax.swing.DefaultListModel;
 import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JEditorPane;
 import javax.swing.JLabel;
 import javax.swing.JList;
@@ -40,56 +21,17 @@
 import javax.swing.JScrollPane;
 import javax.swing.Scrollable;
-import javax.swing.UIManager;
-import javax.swing.event.HyperlinkEvent;
-import javax.swing.event.HyperlinkListener;
-import javax.swing.event.HyperlinkEvent.EventType;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.plugins.PluginDownloader;
-import org.openstreetmap.josm.plugins.PluginException;
-import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginSelection;
 import org.openstreetmap.josm.tools.GBC;
-import org.openstreetmap.josm.tools.OpenBrowser;
-import org.openstreetmap.josm.tools.XmlObjectParser.Uniform;
 
 public class PluginPreference implements PreferenceSetting {
 
-    /**
-     * Only the plugin name, its jar location and the description.
-     * In other words, this is the minimal requirement the plugin preference page
-     * needs to show the plugin as available
-     *
-     * @author imi
-     */
-    public static class PluginDescription implements Comparable<Object> {
-        // Note: All the following need to be public instance variables of
-        // type String.  (Plugin description XMLs from the server are parsed
-        // with tools.XmlObjectParser, which uses reflection to access them.)
-        public String name;
-        public String description;
-        public String resource;
-        public String version;
-        public PluginDescription(String name, String description, String resource, String version) {
-            this.name = name;
-            this.description = description;
-            this.resource = resource;
-            this.version = version;
-        }
-        public PluginDescription() {
-        }
-        public int compareTo(Object n) {
-            if(n instanceof PluginDescription)
-                return name.compareToIgnoreCase(((PluginDescription)n).name);
-            return -1;
-        }
-    }
-
-    private Map<String, Boolean> pluginMap;
-    private Map<String, PluginDescription> availablePlugins;
     private JPanel plugin;
     private JPanel pluginPanel = new NoHorizontalScrollPanel(new GridBagLayout());
     private PreferenceDialog gui;
     private JScrollPane pluginPane;
+    private PluginSelection selection = new PluginSelection();
 
     public void addGui(final PreferenceDialog gui) {
@@ -103,12 +45,5 @@
         morePlugins.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent e) {
-                int count = PluginDownloader.downloadDescription();
-                if (count > 0)
-                    JOptionPane.showMessageDialog(Main.parent,
-                        trn("Downloaded plugin information from {0} site",
-                            "Downloaded plugin information from {0} sites", count, count));
-                else
-                    JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found."));
-                refreshPluginPanel(gui);
+                selection.updateDescription(pluginPanel);
             }
         });
@@ -118,6 +53,5 @@
         update.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent e) {
-                update();
-                refreshPluginPanel(gui);
+                selection.update(pluginPanel);
             }
         });
@@ -132,5 +66,5 @@
         plugin.add(configureSites, GBC.std());
 
-        refreshPluginPanel(gui);
+        selection.drawPanel(pluginPanel);
     }
 
@@ -174,225 +108,14 @@
         if (answer != JOptionPane.OK_OPTION)
             return;
-        StringBuilder b = new StringBuilder();
-        for (int i = 0; i < model.getSize(); ++i) {
-            b.append(model.getElementAt(i));
-            if (i < model.getSize()-1)
-                b.append(" ");
-        }
-        Main.pref.put("pluginmanager.sites", b.toString());
-    }
-
-    private void update() {
-        // refresh description
-        int num = PluginDownloader.downloadDescription();
-        Boolean done = false;
-        refreshPluginPanel(gui);
-
-        Set<PluginDescription> toUpdate = new HashSet<PluginDescription>();
-        StringBuilder toUpdateStr = new StringBuilder();
-        for (PluginProxy proxy : Main.plugins) {
-            PluginDescription description = availablePlugins.get(proxy.info.name);
-            if (description != null && (description.version == null || description.version.equals("")) ?
-            (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) {
-                toUpdate.add(description);
-                toUpdateStr.append(description.name+"\n");
-            }
-        }
-        if (toUpdate.isEmpty()) {
-            JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date."));
-            done = true;
-        } else {
-            int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}",
-            toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION);
-            if (answer == JOptionPane.OK_OPTION) {
-                PluginDownloader.update(toUpdate);
-                done = true;
-            }
-        }
-        if (done && num >= 1)
-            Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
-    }
-
-    private void refreshPluginPanel(final PreferenceDialog gui) {
-        availablePlugins = getAvailablePlugins();
-        Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null);
-
-        if (pluginMap == null)
-            pluginMap = new HashMap<String, Boolean>();
-        else
-            // Keep the map in bounds; possibly slightly pointless.
-            for (final String pname : pluginMap.keySet())
-                if (availablePlugins.get(pname) == null) pluginMap.remove(pname);
-
-        pluginPanel.removeAll();
-
-        GridBagConstraints gbc = new GridBagConstraints();
-        gbc.gridx = 0;
-        gbc.anchor = GridBagConstraints.NORTHWEST;
-
-        int row = 0;
-        for (final PluginDescription plugin : availablePlugins.values()) {
-            boolean enabled = (enabledPlugins != null) && enabledPlugins.contains(plugin.name);
-            if (pluginMap.get(plugin.name) == null)
-                pluginMap.put(plugin.name, enabled);
-
-            String remoteversion = plugin.version;
-            if ((remoteversion == null) || remoteversion.equals(""))
-                remoteversion = tr("unknown");
-
-            String localversion;
-            PluginInformation p = PluginInformation.findPlugin(plugin.name);
-            if (p != null) {
-                if (p.version != null && !p.version.equals(""))
-                    localversion = p.version;
-                else
-                    localversion = tr("unknown");
-                localversion = " (" + localversion + ")";
-            } else
-                localversion = "";
-
-            final JCheckBox pluginCheck = new JCheckBox(
-                    tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion),
-                    pluginMap.get(plugin.name));
-            gbc.gridy = row++;
-            gbc.insets = new Insets(5,5,0,5);
-            gbc.weighty = 0.1;
-            gbc.fill = GridBagConstraints.NONE;
-            pluginPanel.add(pluginCheck, gbc);
-
-            pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM"));
-
-            JEditorPane description = new JEditorPane();
-            description.setContentType("text/html");
-            description.setEditable(false);
-            description.setText("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>");
-            description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
-            description.setBackground(UIManager.getColor("Panel.background"));
-            description.addHyperlinkListener(new HyperlinkListener() {
-                public void hyperlinkUpdate(HyperlinkEvent e) {
-                    if(e.getEventType() == EventType.ACTIVATED) {
-                        OpenBrowser.displayUrl(e.getURL().toString());
-                    }
-                }
-            });
-
-            gbc.gridy = row++;
-            gbc.insets = new Insets(3,5,5,5);
-            gbc.weighty = 0.9;
-            gbc.weightx = 1.0;
-            gbc.anchor = GridBagConstraints.WEST;
-            gbc.fill = GridBagConstraints.HORIZONTAL;
-            pluginPanel.add(description, gbc);
-
-            pluginCheck.addActionListener(new ActionListener(){
-                public void actionPerformed(ActionEvent e) {
-                    // if user enabled a plugin, it is not loaded but found somewhere on disk: offer to delete jar
-                    if (pluginCheck.isSelected()) {
-                        PluginInformation plinfo = PluginInformation.findPlugin(plugin.name);
-                        if ((PluginInformation.getLoaded(plugin.name) == null) && (plinfo != null)) {
-                            try {
-                                int answer = JOptionPane.showConfirmDialog(Main.parent,
-                                    tr("Plugin archive already available. Do you want to download the current version by deleting existing archive?\n\n{0}",
-                                    plinfo.file.getCanonicalPath()), tr("Plugin already exists"), JOptionPane.OK_CANCEL_OPTION);
-                                if (answer == JOptionPane.OK_OPTION) {
-                                    if (!plinfo.file.delete()) {
-                                        JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", plinfo.file.getCanonicalPath()));
-                                    }
-                                }
-                            } catch (IOException e1) {
-                                e1.printStackTrace();
-                                JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", e1.getMessage()));
-                            }
-                        }
-                    }
-                    pluginMap.put(plugin.name, pluginCheck.isSelected());
-                }
-            });
-        }
-        plugin.updateUI();
-    }
-
-    private Map<String, PluginDescription> getAvailablePlugins() {
-        SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){
-            public int compare(String o1, String o2) {
-                return o1.compareToIgnoreCase(o2);
-            }
-        });
-        for (String location : PluginInformation.getPluginLocations()) {
-            File[] pluginFiles = new File(location).listFiles();
-            if (pluginFiles != null) {
-                Arrays.sort(pluginFiles);
-                for (File f : pluginFiles) {
-                    if (!f.isFile())
-                        continue;
-                    if (f.getName().endsWith(".jar")) {
-                        try {
-                            PluginInformation info = new PluginInformation(f);
-                            if (!availablePlugins.containsKey(info.name))
-                                availablePlugins.put(info.name, new PluginDescription(
-                                    info.name,
-                                    info.description,
-                                    PluginInformation.fileToURL(f).toString(),
-                                    info.version));
-                        } catch (PluginException x) {
-                        }
-                    } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) {
-                        try {
-                            Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class);
-                            for (PluginDescription pd : parser)
-                                if (!availablePlugins.containsKey(pd.name))
-                                    availablePlugins.put(pd.name, pd);
-                        } catch (Exception e) {
-                            e.printStackTrace();
-                            JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName()));
-                        }
-                    }
-                }
-            }
-        }
-        for (PluginProxy proxy : Main.plugins)
-            if (!availablePlugins.containsKey(proxy.info.name))
-                availablePlugins.put(proxy.info.name, new PluginDescription(
-                        proxy.info.name,
-                        proxy.info.description,
-                        proxy.info.file == null ? null :
-                            PluginInformation.fileToURL(proxy.info.file).toString(),
-                        proxy.info.version));
-        return availablePlugins;
+        Collection<String> sites = new LinkedList<String>();
+        for (int i = 0; i < model.getSize(); ++i)
+            sites.add((String)model.getElementAt(i));
+        PluginDownloader.setSites(sites);
     }
 
     public boolean ok() {
-        Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>();
-        String msg = "";
-        for (Entry<String, Boolean> entry : pluginMap.entrySet()) {
-            if (entry.getValue() && PluginInformation.findPlugin(entry.getKey()) == null) {
-                toDownload.add(availablePlugins.get(entry.getKey()));
-                msg += entry.getKey() + "\n";
-            }
-        }
-        if (!toDownload.isEmpty()) {
-            int answer = JOptionPane.showConfirmDialog(Main.parent,
-                    tr("Download the following plugins?\n\n{0}", msg),
-                    tr("Download missing plugins"),
-                    JOptionPane.YES_NO_OPTION);
-            if (answer != JOptionPane.OK_OPTION)
-                for (PluginDescription pd : toDownload)
-                    pluginMap.put(pd.name, false);
-            else
-                for (PluginDescription pd : toDownload)
-                    if (!PluginDownloader.downloadPlugin(pd))
-                        pluginMap.put(pd.name, false);
+        return selection.finish();
+    }
 
-        }
-        LinkedList<String> plugins = new LinkedList<String>();
-        for (Map.Entry<String, Boolean> d : pluginMap.entrySet()) {
-            if (d.getValue())
-                plugins.add(d.getKey());
-        }
-
-        Collections.sort(plugins);
-        return Main.pref.putCollection("plugins", plugins);
-    }
-    
     class NoHorizontalScrollPanel extends JPanel implements Scrollable {
         public NoHorizontalScrollPanel(GridBagLayout gridBagLayout) {
Index: /trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java	(revision 1326)
@@ -22,5 +22,6 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginHandler;
+import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 import org.openstreetmap.josm.tools.GBC;
 import org.openstreetmap.josm.tools.I18n;
@@ -112,4 +113,7 @@
             } catch (SecurityException e) {
                 it.remove();
+            } catch (Throwable e) {
+                /* allow to change most settings even if e.g. a plugin fails */
+                BugReportExceptionHandler.handleException(e);
             }
         }
@@ -133,9 +137,5 @@
         settings.add(new ShortcutPreference());
 
-        for (PluginProxy plugin : Main.plugins) {
-            PreferenceSetting p = plugin.getPreferenceSetting();
-            if (p != null)
-                settings.add(p);
-        }
+        PluginHandler.getPreferenceSetting(settings);
 
         // always the last: advanced tab
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginDescription.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginDescription.java	(revision 1326)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginDescription.java	(revision 1326)
@@ -0,0 +1,32 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.plugins;
+
+/**
+* Only the plugin name, its jar location and the description.
+* In other words, this is the minimal requirement the plugin preference page
+* needs to show the plugin as available
+*
+* @author imi
+*/
+public class PluginDescription implements Comparable<Object> {
+    // Note: All the following need to be public instance variables of
+    // type String.  (Plugin description XMLs from the server are parsed
+    // with tools.XmlObjectParser, which uses reflection to access them.)
+    public String name;
+    public String description;
+    public String resource;
+    public String version;
+    public PluginDescription(String name, String description, String resource, String version) {
+        this.name = name;
+        this.description = description;
+        this.resource = resource;
+        this.version = version;
+    }
+    public PluginDescription() {
+    }
+    public int compareTo(Object n) {
+        if(n instanceof PluginDescription)
+            return name.compareToIgnoreCase(((PluginDescription)n).name);
+        return -1;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginDownloader.java	(revision 1326)
@@ -29,5 +29,4 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
-import org.openstreetmap.josm.gui.preferences.PluginPreference.PluginDescription;
 import org.xml.sax.SAXException;
 
@@ -76,4 +75,7 @@
     public static Collection<String> getSites() {
         return Main.pref.getCollection("pluginmanager.sites", Arrays.asList(pluginSites));
+    }
+    public static void setSites(Collection<String> c) {
+        Main.pref.putCollection("pluginmanager.sites", c);
     }
 
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 1326)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 1326)
@@ -0,0 +1,324 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.plugins;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.AboutAction;
+import org.openstreetmap.josm.gui.MapFrame;
+import org.openstreetmap.josm.gui.download.DownloadSelection;
+import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
+import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.ImageProvider;
+
+public class PluginHandler {
+    /**
+     * All installed and loaded plugins (resp. their main classes)
+     */
+    public final static Collection<PluginProxy> pluginList = new LinkedList<PluginProxy>();
+    /**
+     * Load all plugins specified in preferences. If the parameter is
+     * <code>true</code>, all early plugins are loaded (before constructor).
+     */
+    public static void loadPlugins(boolean early) {
+        List<String> plugins = new LinkedList<String>();
+        Collection<String> cp = Main.pref.getCollection("plugins", null);
+        if (cp != null)
+            plugins.addAll(cp);
+        if (System.getProperty("josm.plugins") != null)
+            plugins.addAll(Arrays.asList(System.getProperty("josm.plugins").split(",")));
+
+        String [] oldplugins = new String[] {"mappaint", "unglueplugin",
+        "lang-de", "lang-en_GB", "lang-fr", "lang-it", "lang-pl", "lang-ro",
+        "lang-ru", "ewmsplugin", "ywms", "tways-0.2", "geotagged", "landsat"};
+        for (String p : oldplugins) {
+            if (plugins.contains(p)) {
+                plugins.remove(p);
+                Main.pref.removeFromCollection("plugins", p);
+                JOptionPane.showMessageDialog(Main.parent, tr("Warning - loading of {0} plugin was requested. This plugin is no longer required.", p));
+            }
+        }
+
+        if (plugins.isEmpty())
+            return;
+
+        SortedMap<Integer, Collection<PluginInformation>> p = new TreeMap<Integer, Collection<PluginInformation>>();
+        for (String pluginName : plugins) {
+            PluginInformation info = PluginInformation.findPlugin(pluginName);
+            if (info != null) {
+                if (info.early != early)
+                    continue;
+                if (info.mainversion != null) {
+                    int requiredJOSMVersion = 0;
+                    try {
+                        requiredJOSMVersion = Integer.parseInt(info.mainversion);
+                    } catch(NumberFormatException e) {
+                        e.printStackTrace();
+                    }
+                    if (requiredJOSMVersion > AboutAction.getVersionNumber()) {
+                        JOptionPane.showMessageDialog(Main.parent, tr("Plugin requires JOSM update: {0}.", pluginName));
+                        continue;
+                    }
+                }
+                if(info.requires != null)
+                {
+                    String warn = null;
+                    for(String n : info.requires.split(";"))
+                    {
+                        if(!plugins.contains(n))
+                        { warn = n; break; }
+                    }
+                    if(warn != null)
+                    {
+                        JOptionPane.showMessageDialog(Main.parent,
+                        tr("Plugin {0} is required by plugin {1} but was not found.",
+                        warn, pluginName));
+                        continue;
+                    }
+                }
+                if (!p.containsKey(info.stage))
+                    p.put(info.stage, new LinkedList<PluginInformation>());
+                p.get(info.stage).add(info);
+            } else {
+                JOptionPane.showMessageDialog(Main.parent, tr("Plugin not found: {0}.", pluginName));
+            }
+        }
+
+        if (!early) {
+            long tim = System.currentTimeMillis();
+            long last = Main.pref.getLong("pluginmanager.lastupdate", 0);
+            Integer maxTime = Main.pref.getInteger("pluginmanager.warntime", 30);
+            long d = (tim - last)/(24*60*60*1000l);
+            if ((last <= 0) || (maxTime <= 0)) {
+                Main.pref.put("pluginmanager.lastupdate",Long.toString(tim));
+            } else if (d > maxTime) {
+                JOptionPane.showMessageDialog(Main.parent,
+                   "<html>" +
+                   tr("Last plugin update more than {0} days ago.", d) +
+                   "<br><em>" +
+                   tr("(You can change the number of days after which this warning appears<br>by setting the config option 'pluginmanager.warntime'.)") +
+                   "</html>");
+            }
+        }
+
+        // iterate all plugins and collect all libraries of all plugins:
+        List<URL> allPluginLibraries = new ArrayList<URL>();
+        for (Collection<PluginInformation> c : p.values())
+            for (PluginInformation info : c)
+                allPluginLibraries.addAll(info.libraries);
+        // create a classloader for all plugins:
+        URL[] jarUrls = new URL[allPluginLibraries.size()];
+        jarUrls = allPluginLibraries.toArray(jarUrls);
+        URLClassLoader pluginClassLoader = new URLClassLoader(jarUrls, Main.class.getClassLoader());
+        ImageProvider.sources.add(0, pluginClassLoader);
+
+        for (Collection<PluginInformation> c : p.values()) {
+            for (PluginInformation info : c) {
+                try {
+                    Class<?> klass = info.loadClass(pluginClassLoader);
+                    if (klass != null) {
+                        System.out.println("loading "+info.name);
+                        pluginList.add(info.load(klass));
+                    }
+                } catch (Throwable e) {
+                    e.printStackTrace();
+                    if(JOptionPane.showConfirmDialog(Main.parent,
+                    tr("Could not load plugin {0}. Delete from preferences?",
+                    info.name), tr("Disable plugin"),
+                    JOptionPane.YES_NO_OPTION) == JOptionPane.OK_OPTION)
+                    {
+                        plugins.remove(info.name);
+                        Main.pref.removeFromCollection("plugins", info.name);
+                    }
+                }
+            }
+        }
+    }
+    public static void setMapFrame(MapFrame old, MapFrame map) {
+        for (PluginProxy plugin : pluginList)
+            plugin.mapFrameInitialized(old, map);
+    }
+
+    public static Object getPlugin(String name) {
+        for (PluginProxy plugin : pluginList)
+            if(plugin.info.name.equals(name))
+                return plugin.plugin;
+        return null;
+    }
+
+    public static void addDownloadSelection(List<DownloadSelection> downloadSelections)
+    {
+        for (PluginProxy p : pluginList)
+            p.addDownloadSelection(downloadSelections);
+    }
+    public static void getPreferenceSetting(Collection<PreferenceSetting> settings)
+    {
+        for (PluginProxy plugin : pluginList) {
+            PreferenceSetting p = plugin.getPreferenceSetting();
+            if (p != null)
+                settings.add(p);
+        }
+    }
+
+    public static void earlyCleanup()
+    {
+        if (!PluginDownloader.moveUpdatedPlugins()) {
+            JOptionPane.showMessageDialog(null,
+                    tr("Activating the updated plugins failed. Check if JOSM has the permission to overwrite the existing ones."),
+                    tr("Plugins"), JOptionPane.ERROR_MESSAGE);
+        }
+    }
+    public static Boolean checkException(Throwable e)
+    {
+        PluginProxy plugin = null;
+
+        // Check for an explicit problem when calling a plugin function
+        if (e instanceof PluginException)
+            plugin = ((PluginException)e).plugin;
+
+        if (plugin == null)
+        {
+            String name = null;
+            /**
+            * Analyze the stack of the argument and find a name of a plugin, if
+            * some known problem pattern has been found.
+            *
+            * Note: This heuristic is not meant as discrimination against specific
+            * plugins, but only to stop the flood of similar bug reports about plugins.
+            * Of course, plugin writers are free to install their own version of
+            * an exception handler with their email address listed to receive
+            * bug reports ;-).
+            */
+            for (StackTraceElement element : e.getStackTrace()) {
+                String c = element.getClassName();
+
+                if (c.contains("wmsplugin.") || c.contains(".WMSLayer"))
+                    name = "wmsplugin";
+                if (c.contains("livegps."))
+                    name = "livegps";
+                if (c.startsWith("UtilsPlugin."))
+                    name = "UtilsPlugin";
+
+                if (c.startsWith("org.openstreetmap.josm.plugins.")) {
+                    String p = c.substring("org.openstreetmap.josm.plugins.".length());
+                    if (p.indexOf('.') != -1 && p.matches("[a-z].*")) {
+                        name = p.substring(0,p.indexOf('.'));
+                    }
+                }
+                if(name != null)
+                  break;
+            }
+            for (PluginProxy p : pluginList)
+            {
+                if (p.info.name.equals(name))
+                {
+                    plugin = p;
+                    break;
+                }
+            }
+        }
+
+        if (plugin != null) {
+            int answer = JOptionPane.showConfirmDialog(
+                    Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.",
+                    plugin.info.name) + "\n"+ (plugin.info.author != null ?
+                    tr("According to the information within the plugin, the author is {0}.",
+                    plugin.info.author) : "") + "\n" +
+                    tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" +
+                    tr("Should the plugin be disabled?"),
+                    tr("Disable plugin"),
+                    JOptionPane.YES_NO_OPTION);
+            if (answer == JOptionPane.OK_OPTION) {
+                LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(",")));
+                if (plugins.contains(plugin.info.name)) {
+                    while (plugins.remove(plugin.info.name)) {}
+                    String p = "";
+                    for (String s : plugins)
+                        p += ","+s;
+                    if (p.length() > 0)
+                        p = p.substring(1);
+                    Main.pref.put("plugins", p);
+                    JOptionPane.showMessageDialog(Main.parent,
+                    tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin."));
+                } else {
+                    JOptionPane.showMessageDialog(Main.parent,
+                    tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem."));
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+    public static String getBugReportText()
+    {
+        String text = "";
+        String pl = Main.pref.get("plugins");
+        if(pl != null && pl.length() != 0)
+            text += "Plugins: "+pl+"\n";
+        for (final PluginProxy pp : pluginList) {
+            text += "Plugin " + pp.info.name + (pp.info.version != null && !pp.info.version.equals("") ? " Version: "+pp.info.version+"\n" : "\n");
+        }
+        return text;
+    }
+    public static JPanel getInfoPanel()
+    {
+        JPanel pluginTab = new JPanel(new GridBagLayout());
+        for (final PluginProxy p : pluginList) {
+            String name = p.info.name + (p.info.version != null && !p.info.version.equals("") ? " Version: "+p.info.version : "");
+            pluginTab.add(new JLabel(name), GBC.std());
+            pluginTab.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
+            pluginTab.add(new JButton(new AbstractAction(tr("Information")){
+                public void actionPerformed(ActionEvent event) {
+                    StringBuilder b = new StringBuilder();
+                    for (Entry<String,String> e : p.info.attr.entrySet()) {
+                        b.append(e.getKey());
+                        b.append(": ");
+                        b.append(e.getValue());
+                        b.append("\n");
+                    }
+                    JTextArea a = new JTextArea(10,40);
+                    a.setEditable(false);
+                    a.setText(b.toString());
+                    JOptionPane.showMessageDialog(Main.parent, new JScrollPane(a));
+                }
+            }), GBC.eol());
+
+            JTextArea description = new JTextArea((p.info.description==null? tr("no description available"):p.info.description));
+            description.setEditable(false);
+            description.setFont(new JLabel().getFont().deriveFont(Font.ITALIC));
+            description.setLineWrap(true);
+            description.setWrapStyleWord(true);
+            description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
+            description.setBackground(UIManager.getColor("Panel.background"));
+
+            pluginTab.add(description, GBC.eop().fill(GBC.HORIZONTAL));
+        }
+        return pluginTab;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java	(revision 1326)
@@ -34,4 +34,5 @@
     public final String mainversion;
     public final String className;
+    public final String requires;
     public final String description;
     public final boolean early;
@@ -77,4 +78,5 @@
                 Attributes attr = manifest.getMainAttributes();
                 className = attr.getValue("Plugin-Class");
+                requires = attr.getValue("Plugin-Requires");
                 description = attr.getValue("Plugin-Description");
                 early = Boolean.parseBoolean(attr.getValue("Plugin-Early"));
@@ -107,4 +109,5 @@
                 early = false;
                 stage = 50;
+                requires = null;
                 version = null;
                 author = null;
@@ -215,21 +218,4 @@
         return all;
     }
-
-
-    /**
-     * Return information about a loaded plugin.
-     *
-     * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if
-     * the plugin requested is not loaded yet.
-     *
-     * @return The PluginInformation to a specific plugin, but only if the plugin is loaded.
-     * If it is not loaded, <code>null</code> is returned.
-     */
-    public static PluginInformation getLoaded(String pluginName) {
-        for (PluginProxy p : Main.plugins)
-            if (p.info.name.equals(pluginName))
-                return p.info;
-        return null;
-    }
 }
 
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginProxy.java	(revision 1326)
@@ -7,5 +7,5 @@
 import org.openstreetmap.josm.gui.download.DownloadSelection;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
-
+import org.openstreetmap.josm.tools.BugReportExceptionHandler;
 
 /**
@@ -31,5 +31,5 @@
         } catch (NoSuchMethodException e) {
         } catch (Exception e) {
-            throw new PluginException(this, info.name, e);
+            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e));
         }
     }
@@ -41,6 +41,7 @@
             return null;
         } catch (Exception e) {
-            throw new PluginException(this, info.name, e);
+            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e));
         }
+        return null;
     }
 
@@ -51,5 +52,5 @@
             // ignore
         } catch (Exception e) {
-            throw new PluginException(this, info.name, e);
+            BugReportExceptionHandler.handleException(new PluginException(this, info.name, e));
         }
     }
Index: /trunk/src/org/openstreetmap/josm/plugins/PluginSelection.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/plugins/PluginSelection.java	(revision 1326)
+++ /trunk/src/org/openstreetmap/josm/plugins/PluginSelection.java	(revision 1326)
@@ -0,0 +1,289 @@
+//License: GPL. Copyright 2007 by Immanuel Scholz and others
+package org.openstreetmap.josm.plugins;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+import javax.swing.AbstractAction;
+import javax.swing.BorderFactory;
+import javax.swing.JCheckBox;
+import javax.swing.JEditorPane;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.event.HyperlinkEvent.EventType;
+import javax.swing.UIManager;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.tools.OpenBrowser;
+import org.openstreetmap.josm.tools.XmlObjectParser.Uniform;
+
+public class PluginSelection {
+
+    private Map<String, Boolean> pluginMap;
+    private Map<String, PluginDescription> availablePlugins;
+
+    public void updateDescription(JPanel pluginPanel) {
+        int count = PluginDownloader.downloadDescription();
+        if (count > 0)
+            JOptionPane.showMessageDialog(Main.parent,
+                trn("Downloaded plugin information from {0} site",
+                    "Downloaded plugin information from {0} sites", count, count));
+        else
+            JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found."));
+        drawPanel(pluginPanel);
+    }
+
+    public void update(JPanel pluginPanel) {
+        // refresh description
+        int num = PluginDownloader.downloadDescription();
+        Boolean done = false;
+        drawPanel(pluginPanel);
+
+        Set<PluginDescription> toUpdate = new HashSet<PluginDescription>();
+        StringBuilder toUpdateStr = new StringBuilder();
+        for (PluginProxy proxy : PluginHandler.pluginList) {
+            PluginDescription description = availablePlugins.get(proxy.info.name);
+            if (description != null && (description.version == null || description.version.equals("")) ?
+            (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) {
+                toUpdate.add(description);
+                toUpdateStr.append(description.name+"\n");
+            }
+        }
+        if (toUpdate.isEmpty()) {
+            JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date."));
+            done = true;
+        } else {
+            int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}",
+            toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION);
+            if (answer == JOptionPane.OK_OPTION) {
+                PluginDownloader.update(toUpdate);
+                done = true;
+            }
+        }
+        if (done && num >= 1)
+            Main.pref.put("pluginmanager.lastupdate", Long.toString(System.currentTimeMillis()));
+        drawPanel(pluginPanel);
+    }
+
+    public Boolean finish() {
+        Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>();
+        String msg = "";
+        for (Entry<String, Boolean> entry : pluginMap.entrySet()) {
+            if (entry.getValue() && PluginInformation.findPlugin(entry.getKey()) == null) {
+                toDownload.add(availablePlugins.get(entry.getKey()));
+                msg += entry.getKey() + "\n";
+            }
+        }
+        if (!toDownload.isEmpty()) {
+            int answer = JOptionPane.showConfirmDialog(Main.parent,
+                    tr("Download the following plugins?\n\n{0}", msg),
+                    tr("Download missing plugins"),
+                    JOptionPane.YES_NO_OPTION);
+            if (answer != JOptionPane.OK_OPTION)
+                for (PluginDescription pd : toDownload)
+                    pluginMap.put(pd.name, false);
+            else
+                for (PluginDescription pd : toDownload)
+                    if (!PluginDownloader.downloadPlugin(pd))
+                        pluginMap.put(pd.name, false);
+
+        }
+        LinkedList<String> plugins = new LinkedList<String>();
+        for (Map.Entry<String, Boolean> d : pluginMap.entrySet()) {
+            if (d.getValue())
+                plugins.add(d.getKey());
+        }
+
+        Collections.sort(plugins);
+        return Main.pref.putCollection("plugins", plugins);
+    }
+
+    /* return true when plugin list changed */
+    public void drawPanel(JPanel pluginPanel) {
+        availablePlugins = getAvailablePlugins();
+        Collection<String> enabledPlugins = Main.pref.getCollection("plugins", null);
+
+        if (pluginMap == null)
+            pluginMap = new HashMap<String, Boolean>();
+        else
+            // Keep the map in bounds; possibly slightly pointless.
+            for (final String pname : pluginMap.keySet())
+                if (availablePlugins.get(pname) == null) pluginMap.remove(pname);
+
+        pluginPanel.removeAll();
+
+        GridBagConstraints gbc = new GridBagConstraints();
+        gbc.gridx = 0;
+        gbc.anchor = GridBagConstraints.NORTHWEST;
+
+        int row = 0;
+        for (final PluginDescription plugin : availablePlugins.values()) {
+            boolean enabled = (enabledPlugins != null) && enabledPlugins.contains(plugin.name);
+            if (pluginMap.get(plugin.name) == null)
+                pluginMap.put(plugin.name, enabled);
+
+            String remoteversion = plugin.version;
+            if ((remoteversion == null) || remoteversion.equals(""))
+                remoteversion = tr("unknown");
+
+            String localversion;
+            PluginInformation p = PluginInformation.findPlugin(plugin.name);
+            if (p != null) {
+                if (p.version != null && !p.version.equals(""))
+                    localversion = p.version;
+                else
+                    localversion = tr("unknown");
+                localversion = " (" + localversion + ")";
+            } else
+                localversion = "";
+
+            final JCheckBox pluginCheck = new JCheckBox(
+                    tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion),
+                    pluginMap.get(plugin.name));
+            gbc.gridy = row++;
+            gbc.insets = new Insets(5,5,0,5);
+            gbc.weighty = 0.1;
+            gbc.fill = GridBagConstraints.NONE;
+            pluginPanel.add(pluginCheck, gbc);
+
+            pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM"));
+
+            JEditorPane description = new JEditorPane();
+            description.setContentType("text/html");
+            description.setEditable(false);
+            description.setText("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>");
+            description.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
+            description.setBackground(UIManager.getColor("Panel.background"));
+            description.addHyperlinkListener(new HyperlinkListener() {
+                public void hyperlinkUpdate(HyperlinkEvent e) {
+                    if(e.getEventType() == EventType.ACTIVATED) {
+                        OpenBrowser.displayUrl(e.getURL().toString());
+                    }
+                }
+            });
+
+            gbc.gridy = row++;
+            gbc.insets = new Insets(3,5,5,5);
+            gbc.weighty = 0.9;
+            gbc.weightx = 1.0;
+            gbc.anchor = GridBagConstraints.WEST;
+            gbc.fill = GridBagConstraints.HORIZONTAL;
+            pluginPanel.add(description, gbc);
+
+            pluginCheck.addActionListener(new ActionListener(){
+                public void actionPerformed(ActionEvent e) {
+                    // if user enabled a plugin, it is not loaded but found somewhere on disk: offer to delete jar
+                    if (pluginCheck.isSelected()) {
+                        PluginInformation plinfo = PluginInformation.findPlugin(plugin.name);
+                        if ((getLoaded(plugin.name) == null) && (plinfo != null)) {
+                            try {
+                                int answer = JOptionPane.showConfirmDialog(Main.parent,
+                                    tr("Plugin archive already available. Do you want to download the current version by deleting existing archive?\n\n{0}",
+                                    plinfo.file.getCanonicalPath()), tr("Plugin already exists"), JOptionPane.OK_CANCEL_OPTION);
+                                if (answer == JOptionPane.OK_OPTION) {
+                                    if (!plinfo.file.delete()) {
+                                        JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", plinfo.file.getCanonicalPath()));
+                                    }
+                                }
+                            } catch (IOException e1) {
+                                e1.printStackTrace();
+                                JOptionPane.showMessageDialog(Main.parent, tr("Error deleting plugin file: {0}", e1.getMessage()));
+                            }
+                        }
+                    }
+                    pluginMap.put(plugin.name, pluginCheck.isSelected());
+                }
+            });
+        }
+        pluginPanel.updateUI();
+    }
+
+    /**
+     * Return information about a loaded plugin.
+     *
+     * Note that if you call this in your plugins bootstrap, you may get <code>null</code> if
+     * the plugin requested is not loaded yet.
+     *
+     * @return The PluginInformation to a specific plugin, but only if the plugin is loaded.
+     * If it is not loaded, <code>null</code> is returned.
+     */
+    private static PluginInformation getLoaded(String pluginName) {
+        for (PluginProxy p : PluginHandler.pluginList)
+            if (p.info.name.equals(pluginName))
+                return p.info;
+        return null;
+    }
+
+    private Map<String, PluginDescription> getAvailablePlugins() {
+        SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){
+            public int compare(String o1, String o2) {
+                return o1.compareToIgnoreCase(o2);
+            }
+        });
+        for (String location : PluginInformation.getPluginLocations()) {
+            File[] pluginFiles = new File(location).listFiles();
+            if (pluginFiles != null) {
+                Arrays.sort(pluginFiles);
+                for (File f : pluginFiles) {
+                    if (!f.isFile())
+                        continue;
+                    if (f.getName().endsWith(".jar")) {
+                        try {
+                            PluginInformation info = new PluginInformation(f);
+                            if (!availablePlugins.containsKey(info.name))
+                                availablePlugins.put(info.name, new PluginDescription(
+                                    info.name,
+                                    info.description,
+                                    PluginInformation.fileToURL(f).toString(),
+                                    info.version));
+                        } catch (PluginException x) {
+                        }
+                    } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) {
+                        try {
+                            Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class);
+                            for (PluginDescription pd : parser)
+                                if (!availablePlugins.containsKey(pd.name))
+                                    availablePlugins.put(pd.name, pd);
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                            JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName()));
+                        }
+                    }
+                }
+            }
+        }
+        for (PluginProxy proxy : PluginHandler.pluginList)
+            if (!availablePlugins.containsKey(proxy.info.name))
+                availablePlugins.put(proxy.info.name, new PluginDescription(
+                        proxy.info.name,
+                        proxy.info.description,
+                        proxy.info.file == null ? null :
+                            PluginInformation.fileToURL(proxy.info.file).toString(),
+                        proxy.info.version));
+        return availablePlugins;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java	(revision 1325)
+++ /trunk/src/org/openstreetmap/josm/tools/BugReportExceptionHandler.java	(revision 1326)
@@ -30,6 +30,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.AboutAction;
-import org.openstreetmap.josm.plugins.PluginException;
-import org.openstreetmap.josm.plugins.PluginProxy;
+import org.openstreetmap.josm.plugins.PluginHandler;
 
 /**
@@ -41,4 +40,7 @@
 
     public void uncaughtException(Thread t, Throwable e) {
+        handleException(e);
+    }
+    public static void handleException(Throwable e) {
         e.printStackTrace();
         if (Main.parent != null) {
@@ -52,42 +54,6 @@
             }
 
-            PluginProxy plugin = null;
-
-            // Check for an explicit problem when calling a plugin function
-            if (e instanceof PluginException)
-                plugin = ((PluginException)e).plugin;
-
-            if (plugin == null)
-                plugin = guessPlugin(e);
-
-            if (plugin != null) {
-                int answer = JOptionPane.showConfirmDialog(
-                        Main.parent, tr("An unexpected exception occurred that may have come from the ''{0}'' plugin.",
-                        plugin.info.name) + "\n"+ (plugin.info.author != null ?
-                        tr("According to the information within the plugin, the author is {0}.",
-                        plugin.info.author) : "") + "\n" +
-                        tr("Try updating to the newest version of this plugin before reporting a bug.") + "\n" +
-                        tr("Should the plugin be disabled?"),
-                        tr("Disable plugin"),
-                        JOptionPane.YES_NO_OPTION);
-                if (answer == JOptionPane.OK_OPTION) {
-                    LinkedList<String> plugins = new LinkedList<String>(Arrays.asList(Main.pref.get("plugins").split(",")));
-                    if (plugins.contains(plugin.info.name)) {
-                        while (plugins.remove(plugin.info.name)) {}
-                        String p = "";
-                        for (String s : plugins)
-                            p += ","+s;
-                        if (p.length() > 0)
-                            p = p.substring(1);
-                        Main.pref.put("plugins", p);
-                        JOptionPane.showMessageDialog(Main.parent,
-                        tr("The plugin has been removed from the configuration. Please restart JOSM to unload the plugin."));
-                    } else {
-                        JOptionPane.showMessageDialog(Main.parent,
-                        tr("The plugin could not be removed. Please tell the people you got JOSM from about the problem."));
-                    }
-                    return;
-                }
-            }
+            if(PluginHandler.checkException(e))
+                return;
 
             Object[] options = new String[]{tr("Do nothing"), tr("Report Bug")};
@@ -103,11 +69,6 @@
 
                     String text = AboutAction.getTextBlock();
-                    String pl = Main.pref.get("plugins");
                     text += "Java version: " + System.getProperty("java.version")+"\n";
-                    if(pl != null && pl.length() != 0)
-                        text += "Plugins: "+pl+"\n";
-                    for (final PluginProxy pp : Main.plugins) {
-                        text += "Plugin " + pp.info.name + (pp.info.version != null && !pp.info.version.equals("") ? " Version: "+pp.info.version+"\n" : "\n");
-                    }
+                    text += PluginHandler.getBugReportText();
                     text += "\n" + stack.getBuffer().toString();
 
@@ -137,43 +98,3 @@
         }
     }
-
-    private PluginProxy guessPlugin(Throwable e) {
-        String name = guessPluginName(e);
-        for (PluginProxy p : Main.plugins)
-            if (p.info.name.equals(name))
-                return p;
-        return null;
-    }
-
-    /**
-     * Analyze the stack of the argument and return a name of a plugin, if
-     * some known problem pattern has been found or <code>null</code>, if
-     * the stack does not contain plugin-code.
-     *
-     * Note: This heuristic is not meant as discrimination against specific
-     * plugins, but only to stop the flood of similar bug reports about plugins.
-     * Of course, plugin writers are free to install their own version of
-     * an exception handler with their email address listed to receive
-     * bug reports ;-).
-     */
-    private String guessPluginName(Throwable e) {
-        for (StackTraceElement element : e.getStackTrace()) {
-            String c = element.getClassName();
-
-            if (c.contains("wmsplugin.") || c.contains(".WMSLayer"))
-                return "wmsplugin";
-            if (c.contains("livegps."))
-                return "livegps";
-            if (c.startsWith("UtilsPlugin."))
-                return "UtilsPlugin";
-
-            if (c.startsWith("org.openstreetmap.josm.plugins.")) {
-                String p = c.substring("org.openstreetmap.josm.plugins.".length());
-                if (p.indexOf('.') != -1 && p.matches("[a-z].*")) {
-                    return p.substring(0,p.indexOf('.'));
-                }
-            }
-        }
-        return null;
-    }
 }
