Index: trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java	(revision 3329)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java	(revision 3330)
@@ -13,4 +13,6 @@
 
 import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
@@ -105,7 +107,5 @@
             String localversion = formatPluginLocalVersion(model.getPluginInformation(pi.getName()));
 
-            final JCheckBox cbPlugin = new JCheckBox(
-                    tr("{0}: Version {1} (local: {2})", pi.getName(), remoteversion, localversion)
-            );
+            final JCheckBox cbPlugin = new JCheckBox();
             cbPlugin.setSelected(selected);
             cbPlugin.setToolTipText(formatCheckboxTooltipText(pi));
@@ -115,8 +115,19 @@
                 }
             });
+            JLabel lblPlugin = new JLabel(
+                    tr("{0}: Version {1} (local: {2})", pi.getName(), remoteversion, localversion),
+                    pi.getScaledIcon(),
+                    SwingConstants.LEFT);
+
+            gbc.gridx = 0;
             gbc.gridy = ++row;
             gbc.insets = new Insets(5,5,0,5);
             gbc.weighty = 0.0;
+            gbc.weightx = 0.0;
             add(cbPlugin, gbc);
+
+            gbc.gridx = 1;
+            gbc.weightx = 1.0;
+            add(lblPlugin, gbc);
 
             HtmlPanel description = new HtmlPanel();
@@ -130,4 +141,5 @@
             });
 
+            gbc.gridx = 1;
             gbc.gridy = ++row;
             gbc.insets = new Insets(3,25,5,5);
Index: trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java	(revision 3329)
+++ trunk/src/org/openstreetmap/josm/plugins/PluginInformation.java	(revision 3330)
@@ -4,4 +4,5 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.awt.Image;
 import java.io.File;
 import java.io.FileInputStream;
@@ -21,7 +22,9 @@
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
+import javax.swing.ImageIcon;
 
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.LanguageInfo;
 
@@ -47,4 +50,6 @@
     public String localversion = null;
     public String downloadlink = null;
+    public String iconPath;
+    public ImageIcon icon;
     public List<URL> libraries = new LinkedList<URL>();
     public final Map<String, String> attr = new TreeMap<String, String>();
@@ -135,4 +140,6 @@
         this.version = other.version;
         this.downloadlink = other.downloadlink;
+        this.icon = other.icon;
+        this.iconPath = other.iconPath;
         this.libraries = other.libraries;
         this.attr.clear();
@@ -166,4 +173,9 @@
         catch(NumberFormatException e) {}
         author = attr.getValue("Author");
+        iconPath = attr.getValue("Plugin-Icon");
+        if (iconPath != null) {
+            // extract icon from the plugin jar file
+            icon = ImageProvider.getIfAvailable(null, null, null, iconPath, file);
+        }
         if(oldcheck && mainversion > Version.getInstance().getVersion())
         {
@@ -413,3 +425,8 @@
     }
 
+    public ImageIcon getScaledIcon() {
+        if (icon == null)
+            return null;
+        return new ImageIcon(icon.getImage().getScaledInstance(24, 24, Image.SCALE_SMOOTH));
+    }
 }
Index: trunk/src/org/openstreetmap/josm/plugins/ReadLocalPluginInformationTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/ReadLocalPluginInformationTask.java	(revision 3329)
+++ trunk/src/org/openstreetmap/josm/plugins/ReadLocalPluginInformationTask.java	(revision 3330)
@@ -14,8 +14,8 @@
 import java.util.Map;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.xml.sax.SAXException;
 
@@ -66,4 +66,7 @@
         } else {
             availablePlugins.get(info.getName()).localversion = info.version;
+            if (info.icon != null) {
+                availablePlugins.get(info.getName()).icon = info.icon;
+            }
         }
     }
@@ -89,4 +92,29 @@
                 System.err.println(tr("Warning: Failed to scan file ''{0}'' for plugin information. Skipping.", fname));
                 e.printStackTrace();
+            }
+            monitor.worked(1);
+        }
+    }
+
+    protected void scanIconCacheFiles(ProgressMonitor monitor, File pluginsDirectory) {
+        System.err.println("scanIconCacheFiles");
+        File[] siteCacheFiles = pluginsDirectory.listFiles(
+                new FilenameFilter() {
+                    public boolean accept(File dir, String name) {
+                        return name.matches("^([0-9]+-)?site.*plugin-icons\\.zip$");
+                    }
+                }
+        );
+        if (siteCacheFiles == null || siteCacheFiles.length == 0)
+            return;
+        monitor.subTask(tr("Processing plugin site cache icon files..."));
+        monitor.setTicksCount(siteCacheFiles.length);
+        for (File f: siteCacheFiles) {
+            String fname = f.getName();
+            monitor.setCustomText(tr("Processing file ''{0}''", fname));
+            for (PluginInformation pi : availablePlugins.values()) {
+                if (pi.icon == null && pi.iconPath != null) {
+                    pi.icon = ImageProvider.getIfAvailable(null, null, null, pi.name+".jar/"+pi.iconPath, f);
+                }
             }
             monitor.worked(1);
@@ -130,4 +158,5 @@
             monitor.beginTask("");
             scanSiteCacheFiles(monitor, pluginsDirectory);
+            scanIconCacheFiles(monitor, pluginsDirectory);
             scanPluginFiles(monitor, pluginsDirectory);
         } finally {
Index: trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 3329)
+++ trunk/src/org/openstreetmap/josm/plugins/ReadRemotePluginInformationTask.java	(revision 3330)
@@ -12,4 +12,5 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
+import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
@@ -32,4 +33,5 @@
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.OsmTransferException;
+import org.openstreetmap.josm.tools.ImageProvider;
 import org.xml.sax.SAXException;
 
@@ -46,4 +48,6 @@
     private List<PluginInformation> availablePlugins;
 
+    protected enum CacheType {PLUGIN_LIST, ICON_LIST};
+
     protected void init(Collection<String> sites){
         this.sites = sites;
@@ -90,10 +94,12 @@
 
     /**
-     * Creates the file name for the cached plugin list.
+     * Creates the file name for the cached plugin list and the icon cache
+     * file.
      * 
      * @param site the name of the site
+     * @param type icon cache or plugin list cache
      * @return the file name for the cache file
      */
-    protected File createSiteCacheFile(File pluginDir, String site) {
+    protected File createSiteCacheFile(File pluginDir, String site, CacheType type) {
         String name;
         try {
@@ -114,5 +120,12 @@
                 }
             }
-            sb.append(".txt");
+            switch (type) {
+                case PLUGIN_LIST:
+                    sb.append(".txt");
+                    break;
+                case ICON_LIST:
+                    sb.append("-icons.zip");
+                    break;
+            }
             name = sb.toString();
         } catch(MalformedURLException e) {
@@ -175,4 +188,62 @@
 
     /**
+     * Downloads the icon archive from a remote location
+     *
+     * @param site the site URL
+     * @param monitor a progress monitor
+     */
+    protected void downloadPluginIcons(String site, File destFile, ProgressMonitor monitor) {
+        InputStream in = null;
+        OutputStream out = null;
+        System.err.println("icons site: "+site);
+        try {
+            monitor.beginTask("");
+            monitor.indeterminateSubTask(tr("Downloading plugin list from ''{0}''", site));
+
+            URL url = new URL(site);
+            synchronized(this) {
+                connection = (HttpURLConnection)url.openConnection();
+                connection.setRequestProperty("Cache-Control", "no-cache");
+                connection.setRequestProperty("User-Agent",Version.getInstance().getAgentString());
+                connection.setRequestProperty("Host", url.getHost());
+            }
+            in = connection.getInputStream();
+            out = new FileOutputStream(destFile);
+            byte[] buffer = new byte[8192];
+            for (int read = in.read(buffer); read != -1; read = in.read(buffer)) {
+                out.write(buffer, 0, read);
+            }
+            out.close();
+            in.close();
+        } catch(MalformedURLException e) {
+            if (canceled) return;
+            e.printStackTrace();
+            return;
+        } catch(IOException e) {
+            if (canceled) return;
+            e.printStackTrace();
+            return;
+        } finally {
+            synchronized(this) {
+                if (connection != null) {
+                    connection.disconnect();
+                }
+                connection = null;
+            }
+            if (in != null) {
+                try {
+                    in.close();
+                } catch(IOException e){/* ignore */}
+            }
+            monitor.finishTask();
+        }
+        for (PluginInformation pi : availablePlugins) {
+            if (pi.icon == null && pi.iconPath != null) {
+                pi.icon = ImageProvider.getIfAvailable(null, null, null, pi.name+".jar/"+pi.iconPath, destFile);
+            }
+        }
+    }
+
+    /**
      * Writes the list of plugins to a cache file
      * 
@@ -189,5 +260,5 @@
                 }
             }
-            File cacheFile = createSiteCacheFile(pluginDir, site);
+            File cacheFile = createSiteCacheFile(pluginDir, site, CacheType.PLUGIN_LIST);
             getProgressMonitor().subTask(tr("Writing plugin list to local cache ''{0}''", cacheFile.toString()));
             writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(cacheFile), "utf-8"));
@@ -249,4 +320,6 @@
         getProgressMonitor().setTicksCount(sites.size() * 3);
         File pluginDir = Main.pref.getPluginsDirectory();
+
+        // collect old cache files and remove if no longer in use
         List<File> siteCacheFiles = new LinkedList<File>();
         for (String location : PluginInformation.getPluginLocations()) {
@@ -254,5 +327,6 @@
                 new FilenameFilter() {
                     public boolean accept(File dir, String name) {
-                        return name.matches("^([0-9]+-)?site.*\\.txt$");
+                        return name.matches("^([0-9]+-)?site.*\\.txt$") ||
+                               name.matches("^([0-9]+-)?site.*-icons\\.zip$");
                     }
                 }
@@ -266,5 +340,6 @@
             String list = downloadPluginList(site, getProgressMonitor().createSubTaskMonitor(0, false));
             if (canceled) return;
-            siteCacheFiles.remove(createSiteCacheFile(pluginDir, site));
+            siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.PLUGIN_LIST));
+            siteCacheFiles.remove(createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST));
             if(list != null)
             {
@@ -278,4 +353,5 @@
                 if (canceled) return;
             }
+            downloadPluginIcons(site+"-icons.zip", createSiteCacheFile(pluginDir, site, CacheType.ICON_LIST), getProgressMonitor().createSubTaskMonitor(0, false));
         }
         for (File file: siteCacheFiles) /* remove old stuff or whole update process is broken */
