Index: trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 3600)
+++ trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapBBoxChooser.java	(revision 3602)
@@ -8,5 +8,9 @@
 import java.awt.Rectangle;
 import java.awt.Toolkit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Vector;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.openstreetmap.gui.jmapviewer.Coordinate;
@@ -24,8 +28,65 @@
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.preferences.StringProperty;
 
 public class SlippyMapBBoxChooser extends JMapViewer implements BBoxChooser{
-    static private TileSource[] TILE_SOURCES = { new OsmTileSource.Mapnik(),
-        new OsmTileSource.TilesAtHome(), new OsmTileSource.CycleMap() };
+
+    public interface TileSourceProvider {
+        List<TileSource> getTileSources();
+    }
+
+    public static class RenamedSourceDecorator implements TileSource {
+
+        private final TileSource source;
+        private final String name;
+
+        public RenamedSourceDecorator(TileSource source, String name) {
+            this.source = source;
+            this.name = name;
+        }
+
+        @Override public String getName() {
+            return name;
+        }
+
+        @Override public int getMaxZoom() { return source.getMaxZoom(); }
+
+        @Override public int getMinZoom() { return source.getMinZoom(); }
+
+        @Override public int getTileSize() { return source.getTileSize(); }
+
+        @Override public String getTileType() { return source.getTileType(); }
+
+        @Override public TileUpdate getTileUpdate() { return source.getTileUpdate(); }
+
+        @Override public String getTileUrl(int zoom, int tilex, int tiley) { return source.getTileUrl(zoom, tilex, tiley); }
+
+
+    }
+
+    /**
+     * Plugins that wish to add custom tile sources to slippy map choose should call this method
+     * @param tileSourceProvider
+     */
+    public static void addTileSourceProvider(TileSourceProvider tileSourceProvider) {
+        providers.addIfAbsent(tileSourceProvider);
+    }
+
+    private static CopyOnWriteArrayList<TileSourceProvider> providers = new CopyOnWriteArrayList<TileSourceProvider>();
+
+    static {
+        addTileSourceProvider(new TileSourceProvider() {
+            @Override
+            public List<TileSource> getTileSources() {
+                return Arrays.<TileSource>asList(
+                        new RenamedSourceDecorator(new OsmTileSource.Mapnik(), "Mapnik"),
+                        new RenamedSourceDecorator(new OsmTileSource.TilesAtHome(), "Osmarender"),
+                        new RenamedSourceDecorator(new OsmTileSource.CycleMap(), "Cyclemap")
+                );
+            }
+        });
+    }
+
+    private static final StringProperty PROP_MAPSTYLE = new StringProperty("slippy_map_chooser.mapstyle", "Mapnik");
 
     // standard dimension
@@ -35,6 +96,6 @@
     private TileLoader uncachedLoader;
 
-    private SizeButton iSizeButton = new SizeButton();
-    private SourceButton iSourceButton = new SourceButton();
+    private final SizeButton iSizeButton = new SizeButton();
+    private final SourceButton iSourceButton;
     private Bounds bbox;
 
@@ -69,15 +130,24 @@
         setMaxTilesInMemory(Main.pref.getInteger("slippy_map_chooser.max_tiles", 1000));
 
-        String mapStyle = Main.pref.get("slippy_map_chooser.mapstyle", "mapnik");
-        if (mapStyle.equals("osmarender")) {
-            iSourceButton.setMapStyle(SourceButton.OSMARENDER);
-            this.setTileSource(TILE_SOURCES[1]);
-        } else if (mapStyle.equals("cyclemap")) {
-            iSourceButton.setMapStyle(SourceButton.CYCLEMAP);
-            this.setTileSource(TILE_SOURCES[2]);
-        } else {
-            if (!mapStyle.equals("mapnik")) {
-                Main.pref.put("slippy_map_chooser", "mapnik");
+        List<TileSource> tileSources = new ArrayList<TileSource>();
+        for (TileSourceProvider provider: providers) {
+            tileSources.addAll(provider.getTileSources());
+        }
+
+        iSourceButton = new SourceButton(tileSources);
+
+        String mapStyle = PROP_MAPSTYLE.get();
+        boolean foundSource = false;
+        for (TileSource source: tileSources) {
+            if (source.getName().equals(mapStyle)) {
+                this.setTileSource(source);
+                iSourceButton.setCurrentMap(source);
+                foundSource = true;
+                break;
             }
+        }
+        if (!foundSource) {
+            setTileSource(tileSources.get(0));
+            iSourceButton.setCurrentMap(tileSources.get(0));
         }
 
@@ -207,16 +277,8 @@
     }
 
-    public void toggleMapSource(int mapSource) {
+    public void toggleMapSource(TileSource tileSource) {
         this.tileController.setTileCache(new MemoryTileCache());
-        if (mapSource == SourceButton.MAPNIK) {
-            this.setTileSource(TILE_SOURCES[0]);
-            Main.pref.put("slippy_map_chooser.mapstyle", "mapnik");
-        } else if (mapSource == SourceButton.CYCLEMAP) {
-            this.setTileSource(TILE_SOURCES[2]);
-            Main.pref.put("slippy_map_chooser.mapstyle", "cyclemap");
-        } else {
-            this.setTileSource(TILE_SOURCES[1]);
-            Main.pref.put("slippy_map_chooser.mapstyle", "osmarender");
-        }
+        this.setTileSource(tileSource);
+        PROP_MAPSTYLE.put(tileSource.getName()); // TODO Is name really unique?
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapControler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapControler.java	(revision 3600)
+++ trunk/src/org/openstreetmap/josm/gui/bbox/SlippyMapControler.java	(revision 3602)
@@ -126,4 +126,5 @@
     }
 
+    @Override
     public void mouseDragged(MouseEvent e) {
         if ((e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
@@ -152,7 +153,6 @@
                 iSlippyMapChooser.repaint();
 
-            } else if (sourceButton == SourceButton.MAPNIK || sourceButton == SourceButton.OSMARENDER
-                    || sourceButton == SourceButton.CYCLEMAP) {
-                iSlippyMapChooser.toggleMapSource(sourceButton);
+            } else if (sourceButton != 0) {
+                iSlippyMapChooser.toggleMapSource(iSourceButton.hitToTileSource(sourceButton));
             } else {
                 if (e.getClickCount() == 1) {
@@ -168,4 +168,5 @@
     }
 
+    @Override
     public void mouseMoved(MouseEvent e) {
     }
Index: trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java	(revision 3600)
+++ trunk/src/org/openstreetmap/josm/gui/bbox/SourceButton.java	(revision 3602)
@@ -7,7 +7,9 @@
 import java.awt.Point;
 import java.awt.RenderingHints;
+import java.util.List;
 
 import javax.swing.ImageIcon;
 
+import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -20,5 +22,5 @@
     private int layerHeight;
 
-    private String[] sources = new String[] {"Mapnik", "Osmarender", "Cyclemap"};
+    private final TileSource[] sources;
 
     private ImageIcon enlargeImage;
@@ -27,12 +29,11 @@
     private boolean isEnlarged = false;
 
-    private int currentMap = MAPNIK;
+    private int currentMap;
 
     public static final int HIDE_OR_SHOW = 1;
-    public static final int MAPNIK = 2;
-    public static final int OSMARENDER = 3;
-    public static final int CYCLEMAP = 4;
 
-    public SourceButton() {
+    public SourceButton(List<TileSource> sources) {
+        this.sources = sources.toArray(new TileSource[sources.size()]);
+        this.currentMap = 2;
         enlargeImage = ImageProvider.get("layer-switcher-maximize.png");
         shrinkImage = ImageProvider.get("layer-switcher-minimize.png");
@@ -51,6 +52,6 @@
             g.setFont(g.getFont().deriveFont(Font.BOLD).deriveFont(15.0f));
             FontMetrics fm = g.getFontMetrics();
-            for (String source: sources) {
-                int width = fm.stringWidth(source);
+            for (TileSource source: sources) {
+                int width = fm.stringWidth(source.getName());
                 if (width > textWidth) {
                     textWidth = width;
@@ -68,5 +69,5 @@
                 g.setColor(Color.WHITE);
                 g.fillOval(barX + leftPadding, barY + topPadding + i * layerHeight + 6, radioButtonSize, radioButtonSize);
-                g.drawString(sources[i], barX + leftPadding + radioButtonSize + leftPadding, barY + topPadding + i * layerHeight + g.getFontMetrics().getHeight());
+                g.drawString(sources[i].getName(), barX + leftPadding + radioButtonSize + leftPadding, barY + topPadding + i * layerHeight + g.getFontMetrics().getHeight());
                 if (currentMap == i + 2) {
                     g.setColor(Color.BLACK);
@@ -111,9 +112,19 @@
     }
 
-    /**
-     * One of the constants OSMARENDER,MAPNIK or CYCLEMAP
-     */
-    public void setMapStyle (int style) {
-        currentMap = (style < 2 || style > 4) ? MAPNIK : style;
+    public TileSource hitToTileSource(int hit) {
+        if (hit >= 2 && hit < sources.length + 2)
+            return sources[hit - 2];
+        else
+            return null;
+    }
+
+    public void setCurrentMap(TileSource tileSource) {
+        for (int i=0; i<sources.length; i++) {
+            if (sources[i].equals(tileSource)) {
+                currentMap = i + 2;
+                return;
+            }
+        }
+        currentMap = 2;
     }
 }
