Index: applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java
===================================================================
--- applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java	(revision 18354)
+++ applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapLayer.java	(revision 18355)
@@ -63,5 +63,5 @@
 
     protected MemoryTileCache tileCache;
-    protected TileSource tileSource = new OsmTileSource.Mapnik();
+    protected TileSource tileSource;
     protected TileLoader tileLoader;
     JobDispatcher jobDispatcher = JobDispatcher.getInstance();
@@ -81,7 +81,8 @@
         return tileCache;
     }
-    void clearTileStorage()
-    {
-        out("clearing tile storage");
+    void clearTileCache()
+    {
+        if (debug)
+            out("clearing tile storage");
         tileCache = new MemoryTileCache();
         tileCache.setCacheSize(2000);
@@ -109,4 +110,25 @@
     }
 
+    void newTileStorage()
+    {
+        int origZoom = currentZoomLevel;
+        tileSource = SlippyMapPreferences.getMapSource();
+        // The minimum should also take care of integer parsing
+        // errors which would leave us with a zoom of -1 otherwise
+        if (tileSource.getMaxZoom() < currentZoomLevel)
+            currentZoomLevel = tileSource.getMaxZoom();
+        if (tileSource.getMinZoom() > currentZoomLevel)
+            currentZoomLevel = tileSource.getMinZoom();
+        if (currentZoomLevel != origZoom) {
+            out("changed currentZoomLevel loading new tile store from " + origZoom + " to " + currentZoomLevel);
+            out("tileSource.getMinZoom(): " + tileSource.getMinZoom());
+            out("tileSource.getMaxZoom(): " + tileSource.getMaxZoom());
+            SlippyMapPreferences.setLastZoom(currentZoomLevel);
+        }
+        clearTileCache();
+        //tileLoader = new OsmTileLoader(this);
+        tileLoader = new OsmFileCacheTileLoader(this);
+    }
+
     @SuppressWarnings("serial")
     public SlippyMapLayer() {
@@ -117,9 +139,5 @@
 
         currentZoomLevel = SlippyMapPreferences.getLastZoom();
-        if (currentZoomLevel <= 0)
-            currentZoomLevel = SlippyMapPreferences.getMinZoomLvl();
-        clearTileStorage();
-        //tileLoader = new OsmTileLoader(this);
-        tileLoader = new OsmFileCacheTileLoader(this);
+        newTileStorage();
 
         tileOptionMenu = new JPopupMenu();
@@ -168,5 +186,5 @@
                 tr("Load All Tiles")) {
             public void actionPerformed(ActionEvent ae) {
-                loadAllTiles();
+                loadAllTiles(true);
                 redraw();
             }
@@ -191,8 +209,20 @@
 
         tileOptionMenu.add(new JMenuItem(
+                new AbstractAction(tr("Snap to tile size")) {
+                    public void actionPerformed(ActionEvent ae) {
+                        double new_factor = Math.sqrt(lastImageScale);
+                        if (debug)
+                            out("tile snap: scale was: " + lastImageScale + ", new factor: " + new_factor);
+                        Main.map.mapView.zoomToFactor(new_factor);
+                        redraw();
+                    }
+                }));
+        // end of adding menu commands
+
+        tileOptionMenu.add(new JMenuItem(
                 new AbstractAction(tr("Flush Tile Cache")) {
                     public void actionPerformed(ActionEvent ae) {
                         System.out.print("flushing all tiles...");
-                        clearTileStorage();
+                        clearTileCache();
                         System.out.println("done");
                     }
@@ -238,4 +268,21 @@
         SlippyMapPreferences.setLastZoom(currentZoomLevel);
     }
+
+    int getMaxZoomLvl()
+    {
+        int ret = SlippyMapPreferences.getMaxZoomLvl();
+        if (tileSource.getMaxZoom() < ret)
+            ret = tileSource.getMaxZoom();
+        return ret;
+    }
+
+    int getMinZoomLvl()
+    {
+        int ret = SlippyMapPreferences.getMinZoomLvl();
+        if (tileSource.getMinZoom() > ret)
+            ret = tileSource.getMinZoom();
+        return ret;
+    }
+
     /**
      * Zoom in, go closer to map.
@@ -245,7 +292,7 @@
     public boolean zoomIncreaseAllowed()
     {
-        boolean zia = currentZoomLevel < SlippyMapPreferences.getMaxZoomLvl();
+        boolean zia = currentZoomLevel < this.getMaxZoomLvl();
         if (debug)
-            out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + SlippyMapPreferences.getMaxZoomLvl() );
+            out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() );
         return zia;
     }
@@ -260,5 +307,5 @@
         } else {
             System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
-                             "MaxZoomLvl ("+SlippyMapPreferences.getMaxZoomLvl()+") reached.");
+                             "MaxZoomLvl ("+this.getMaxZoomLvl()+") reached.");
             return false;
         }
@@ -273,8 +320,8 @@
     public boolean zoomDecreaseAllowed()
     {
-        return currentZoomLevel > SlippyMapPreferences.getMinZoomLvl();
+        return currentZoomLevel > this.getMinZoomLvl();
     }
     public boolean decreaseZoomLevel() {
-        int minZoom = SlippyMapPreferences.getMinZoomLvl();
+        int minZoom = this.getMinZoomLvl();
         lastImageScale = null;
         if (zoomDecreaseAllowed()) {
@@ -315,4 +362,6 @@
     {
         if (tile == null)
+            return false;
+        if (tile.hasError())
             return false;
         if (tile.isLoaded())
@@ -328,5 +377,5 @@
     }
 
-    void loadAllTiles() {
+    void loadAllTiles(boolean force) {
         MapView mv = Main.map.mapView;
         LatLon topLeft = mv.getLatLon(0, 0);
@@ -341,5 +390,5 @@
             return;
         }
-        ts.loadAllTiles();
+        ts.loadAllTiles(force);
     }
 
@@ -422,4 +471,9 @@
         LatLon topLeft  = tileLatLon(topLeftTile);
         LatLon botRight = tileLatLon(botRightTile);
+
+        if (!autoZoomEnabled())
+            return 0;
+        if (!SlippyMapPreferences.getAutoloadTiles())
+            return 0;
 
         /*
@@ -437,6 +491,6 @@
         for (int zoomOff : otherZooms) {
             int zoom = currentZoomLevel + zoomOff;
-            if ((zoom < SlippyMapPreferences.getMinZoomLvl()) ||
-                (zoom > SlippyMapPreferences.getMaxZoomLvl())) {
+            if ((zoom < this.getMinZoomLvl()) ||
+                (zoom > this.getMaxZoomLvl())) {
                 continue;
             }
@@ -612,7 +666,10 @@
             return ret;
         }
-        void loadAllTiles()
+        void loadAllTiles(boolean force)
         {
             List<Tile> tiles = this.allTiles(true);
+            boolean autoload = SlippyMapPreferences.getAutoloadTiles();
+            if (!autoload && !force)
+               return;
             int nr_queued = 0;
             for (Tile t : tiles) {
@@ -697,5 +754,5 @@
             return;
 
-        ts.loadAllTiles();
+        ts.loadAllTiles(false);
 
         int fontHeight = g.getFontMetrics().getHeight();
@@ -736,6 +793,6 @@
                         out("autozoom increase: scale: " + lastImageScale);
                     increaseZoomLevel();
+                    this.paint(oldg, mv);
                 }
-                this.paint(oldg, mv);
             // If each source image pixel is being squished into > 0.32
             // of a drawn pixels, zoom out.
@@ -745,6 +802,6 @@
                         out("autozoom decrease: scale: " + lastImageScale);
                     decreaseZoomLevel();
+                    this.paint(oldg, mv);
                 }
-                this.paint(oldg, mv);
             }
         }
@@ -767,5 +824,5 @@
         TileSet ts = new TileSet(topLeft, botRight, z);
 
-        ts.loadAllTiles(); // make sure there are tile objects for all tiles
+        ts.loadAllTiles(false); // make sure there are tile objects for all tiles
         Tile clickedTile = null;
         Point p1 = null, p2 = null;
@@ -866,4 +923,7 @@
                 autoZoomPopup.setSelected(SlippyMapPreferences.getAutozoom());
             }
+            if (key.equals(SlippyMapPreferences.PREFERENCE_TILE_SOURCE)) {
+                newTileStorage();
+            }
             redraw();
         }
Index: applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java
===================================================================
--- applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java	(revision 18354)
+++ applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferenceSetting.java	(revision 18355)
@@ -10,4 +10,9 @@
 import javax.swing.JSlider;
 import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import java.util.Collection;
+
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
 
 import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
@@ -29,16 +34,19 @@
     private JCheckBox autozoomActive = new JCheckBox(tr("autozoom"));
     private JCheckBox autoloadTiles = new JCheckBox(tr("autoload tiles"));
-    private JSpinner maxZoomLvl = new JSpinner();
+    private JSpinner maxZoomLvl;
+    private JSpinner minZoomLvl = new JSpinner();
     private JSlider fadeBackground = new JSlider(0, 100);
     
     public void addGui(PreferenceDialog gui)
     {
+        minZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MIN_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
+        maxZoomLvl = new JSpinner(new SpinnerNumberModel(SlippyMapPreferences.DEFAULT_MAX_ZOOM, SlippyMapPreferences.MIN_ZOOM, SlippyMapPreferences.MAX_ZOOM, 1));
         //String description = tr("A plugin that adds to JOSM new layer. This layer could render external tiles.");
         JPanel slippymapTab = gui.createPreferenceTab("slippymap.png", tr("SlippyMap"), tr("Settings for the SlippyMap plugin."));
-        String[] allMapUrls = SlippyMapPreferences.getAllMapUrls();
-        tileSourceCombo = new JComboBox(allMapUrls);
-        tileSourceCombo.setEditable(true);
-        String source = SlippyMapPreferences.getMapUrl();
-        tileSourceCombo.setSelectedItem(source);
+        Collection<TileSource> allSources = SlippyMapPreferences.getAllMapSources();
+        //Collection<String> allSources = SlippyMapPreferences.getAllMapNames();
+        tileSourceCombo = new JComboBox(allSources.toArray());
+        //tileSourceCombo.setEditable(true);
+        tileSourceCombo.setSelectedItem(SlippyMapPreferences.getMapSource());
         slippymapTab.add(new JLabel(tr("Tile Sources")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std());
@@ -53,8 +61,12 @@
         slippymapTab.add(autoloadTiles, GBC.eol().fill(GBC.HORIZONTAL));
         
+        slippymapTab.add(new JLabel(tr("Min zoom lvl: ")), GBC.std());
+        slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
+        slippymapTab.add(this.minZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
+        
         slippymapTab.add(new JLabel(tr("Max zoom lvl: ")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
         slippymapTab.add(this.maxZoomLvl, GBC.eol().fill(GBC.HORIZONTAL));
-        
+
         slippymapTab.add(new JLabel(tr("Fade background: ")), GBC.std());
         slippymapTab.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL));
@@ -75,4 +87,5 @@
      * 	<li>autoload - {@link #autoloadTiles} - {@link SlippyMapPreferences#getAutoloadTiles()}</li>
      * 	<li>maxZoomLvl - {@link #maxZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
+     * 	<li>minZoomLvl - {@link #minZoomLvl} - {@link SlippyMapPreferences#getMaxZoomLvl()}</li>
      * </ul>
      * </p>
@@ -82,4 +95,5 @@
         this.autoloadTiles.setSelected(SlippyMapPreferences.getAutoloadTiles());
         this.maxZoomLvl.setValue(SlippyMapPreferences.getMaxZoomLvl());
+        this.minZoomLvl.setValue(SlippyMapPreferences.getMinZoomLvl());
         this.fadeBackground.setValue(Math.round(SlippyMapPreferences.getFadeBackground()*100f));
     }
@@ -95,8 +109,9 @@
     public boolean ok()
     {
-        SlippyMapPreferences.setMapUrl(this.tileSourceCombo.getSelectedItem().toString());
+        SlippyMapPreferences.setMapSource((TileSource)this.tileSourceCombo.getSelectedItem());
         SlippyMapPreferences.setAutozoom(this.autozoomActive.isSelected());
         SlippyMapPreferences.setAutoloadTiles(this.autoloadTiles.isSelected());
         SlippyMapPreferences.setMaxZoomLvl((Integer)this.maxZoomLvl.getValue());
+        SlippyMapPreferences.setMinZoomLvl((Integer)this.minZoomLvl.getValue());
         SlippyMapPreferences.setFadeBackground(this.fadeBackground.getValue()/100f);
         //restart isn't required
Index: applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java
===================================================================
--- applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java	(revision 18354)
+++ applications/editors/josm/plugins/slippymap/src/org/openstreetmap/josm/plugins/slippymap/SlippyMapPreferences.java	(revision 18355)
@@ -2,11 +2,17 @@
 
 import org.openstreetmap.josm.Main;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import org.openstreetmap.gui.jmapviewer.*;
+import org.openstreetmap.gui.jmapviewer.interfaces.*;
 
 /**
  * Preferences for Slippy Map Tiles
- * 
+ *
  * @author Hakan Tandogan <hakan@gurkensalat.com>
  * @author LuVar <lubomir.varga@freemap.sk>
- * 
+ *
  */
 public class SlippyMapPreferences
@@ -14,5 +20,6 @@
     public static final String PREFERENCE_PREFIX   = "slippymap";
 
-    public static final String PREFERENCE_TILE_URL = PREFERENCE_PREFIX + ".tile_url";
+    public static final String PREFERENCE_TILE_CUSTOM_SOURCE = PREFERENCE_PREFIX + ".custom_tile_source_";
+    public static final String PREFERENCE_TILE_SOURCE = PREFERENCE_PREFIX + ".tile_source";
     public static final String PREFERENCE_AUTOZOOM = PREFERENCE_PREFIX + ".autozoom";
     public static final String PREFERENCE_AUTOLOADTILES = PREFERENCE_PREFIX + ".autoload_tiles";
@@ -22,22 +29,32 @@
     public static final String PREFERENCE_FADE_BACKGROUND = PREFERENCE_PREFIX + ".fade_background";
     public static final String PREFERENCE_DRAW_DEBUG = PREFERENCE_PREFIX + ".draw_debug";
-    
-    public static String getMapUrl()
-    {
-        String url = Main.pref.get(PREFERENCE_TILE_URL);
-
-        if (url == null || "".equals(url))
-        {
-            url = "http://tah.openstreetmap.org/Tiles/tile"; // t@h
-            Main.pref.put(PREFERENCE_TILE_URL, url);
-        }
-
-        return url;
-    }
-    
-    public static void setMapUrl(String mapUrl) {
-    	Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_URL, mapUrl);
-    }
-    
+
+    public static final int MAX_ZOOM = 30;
+    public static final int MIN_ZOOM = 2;
+    public static final int DEFAULT_MAX_ZOOM = 20;
+    public static final int DEFAULT_MIN_ZOOM = 2;
+
+    public static TileSource getMapSource()
+    {
+        String name = Main.pref.get(PREFERENCE_TILE_SOURCE);
+        List<TileSource> sources = SlippyMapPreferences.getAllMapSources();
+        TileSource source = sources.get(0);
+        if (name == null || "".equals(name)) {
+            name = source.getName();
+            Main.pref.put(PREFERENCE_TILE_SOURCE, name);
+        }
+        for (TileSource s : sources) {
+            if (!name.equals(s.getName()))
+                continue;
+            source = s;
+            break;
+        }
+        return source;
+    }
+
+    public static void setMapSource(TileSource source) {
+    	Main.pref.put(SlippyMapPreferences.PREFERENCE_TILE_SOURCE, source.getName());
+    }
+
     public static boolean getAutozoom()
     {
@@ -52,13 +69,13 @@
         return Boolean.parseBoolean(autozoom);
     }
-    
+
     public static void setAutozoom(boolean autozoom) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOZOOM, autozoom);
     }
-    
+
     public static void setDrawDebug(boolean drawDebug) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_DRAW_DEBUG, drawDebug);
     }
-    
+
     public static void setLastZoom(int zoom) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_LAST_ZOOM, ""+zoom);
@@ -86,5 +103,5 @@
         return Boolean.parseBoolean(drawDebug);
     }
-    
+
     public static boolean getAutoloadTiles()
     {
@@ -99,5 +116,5 @@
         return Boolean.parseBoolean(autoloadTiles);
     }
-    
+
     public static void setFadeBackground(float fadeBackground) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_FADE_BACKGROUND, fadeBackground + "");
@@ -105,5 +122,5 @@
 
     /**
-     * 
+     *
      * @return	number between 0 and 1, inclusive
      */
@@ -116,5 +133,5 @@
             Main.pref.put(PREFERENCE_FADE_BACKGROUND, fadeBackground);
         }
-        
+
         float parsed;
         try {
@@ -135,79 +152,56 @@
         return parsed;
     }
-    
+
     public static void setAutoloadTiles(boolean autoloadTiles) {
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_AUTOLOADTILES, autoloadTiles);
     }
-    
+
+    private static int getIntPref(String prefName, int def)
+    {
+        int pref;
+        try {
+        	//Should we use Main.pref.getInteger(str)?
+        	pref = Main.pref.getInteger(prefName, def);
+        } catch (Exception ex) {
+            String str = Main.pref.get(prefName);
+            Main.pref.put(prefName, null);
+        	throw new RuntimeException("Problem while converting string to int. "
+                                       + "Converting value of preferences "
+                                       + prefName + ". Value=\"" + str
+                                       + "\". Should be an integer. Error: "
+                                       + ex.getMessage(), ex);
+        }
+        return pref;
+    }
+
+    static int checkMaxZoomLvl(int maxZoomLvl)
+    {
+    	if(maxZoomLvl > MAX_ZOOM) {
+    		System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
+    		maxZoomLvl = MAX_ZOOM;
+    	}
+    	if(maxZoomLvl < SlippyMapPreferences.__getMinZoomLvl()) {
+    		System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
+    		maxZoomLvl = SlippyMapPreferences.__getMinZoomLvl();
+    	}
+        return maxZoomLvl;
+    }
+
     public static int getMaxZoomLvl()
     {
-        String maxZoomLvl = Main.pref.get(PREFERENCE_MAX_ZOOM_LVL);
-
-        if (maxZoomLvl == null || "".equals(maxZoomLvl))
-        {
-        	maxZoomLvl = "17";
-            Main.pref.put(PREFERENCE_MAX_ZOOM_LVL, maxZoomLvl);
-        }
-
-        int navrat;
-        try {
-        	navrat = Integer.parseInt(maxZoomLvl);
-        } catch (Exception ex) {
-        	throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MAX_ZOOM_LVL + ". Value=\"" + maxZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
-        }
-        if(navrat > 30) {
-    		System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
-    		navrat = 30;
-    	}
-        //if(navrat < SlippyMapPreferences.getMinZoomLvl()) {
-    	//	System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
-    	//	navrat = SlippyMapPreferences.getMinZoomLvl();
-    	//}
-        return navrat;
-    }
-    
+        int maxZoomLvl = getIntPref(PREFERENCE_MAX_ZOOM_LVL, DEFAULT_MAX_ZOOM);
+        return checkMaxZoomLvl(maxZoomLvl);
+    }
+
     public static void setMaxZoomLvl(int maxZoomLvl) {
-    	if(maxZoomLvl > 30) {
-    		System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
-    		maxZoomLvl = 30;
-    	}
-    	if(maxZoomLvl < SlippyMapPreferences.getMinZoomLvl()) {
-    		System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
-    		maxZoomLvl = SlippyMapPreferences.getMinZoomLvl();
-    	}
+        maxZoomLvl = checkMaxZoomLvl(maxZoomLvl);
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_MAX_ZOOM_LVL, "" + maxZoomLvl);
     }
-    
-    public static int getMinZoomLvl()
-    {
-        String minZoomLvl = Main.pref.get(PREFERENCE_MIN_ZOOM_LVL);
-
-        if (minZoomLvl == null || "".equals(minZoomLvl))
-        {
-        	minZoomLvl = "" + (SlippyMapPreferences.getMaxZoomLvl() - 4);
-            Main.pref.put(PREFERENCE_MIN_ZOOM_LVL, minZoomLvl);
-        }
-
-        int navrat;
-        try {
-        	navrat = Integer.parseInt(minZoomLvl);
-        } catch (Exception ex) {
-        	throw new RuntimeException("Problem while converting string to int. Converting value of prefetrences " + PREFERENCE_MIN_ZOOM_LVL + ". Value=\"" + minZoomLvl + "\". Should be an integer. Error: " + ex.getMessage(), ex);
-        }
-        if(navrat < 2) {
-    		System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
-    		navrat = 2;
-    	}
-        //if(navrat > SlippyMapPreferences.getMaxZoomLvl()) {
-    	//	System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
-    	//	navrat = SlippyMapPreferences.getMaxZoomLvl();
-    	//}
-        return navrat;
-    }
-    
-    public static void setMinZoomLvl(int minZoomLvl) {
-    	if(minZoomLvl < 2) {
-    		System.err.println("minZoomLvl shouldnt be lees than 2! Setting to 2.");
-    		minZoomLvl = 2;
+
+    static int checkMinZoomLvl(int minZoomLvl)
+    {
+        if(minZoomLvl < MIN_ZOOM) {
+    		System.err.println("minZoomLvl shouldnt be lees than "+MIN_ZOOM+"! Setting to that.");
+    		minZoomLvl = MIN_ZOOM;
     	}
     	if(minZoomLvl > SlippyMapPreferences.getMaxZoomLvl()) {
@@ -215,19 +209,118 @@
     		minZoomLvl = SlippyMapPreferences.getMaxZoomLvl();
     	}
+        return minZoomLvl;
+    }
+
+    private static int __getMinZoomLvl()
+    {
+        // We can use this internally
+        return getIntPref(PREFERENCE_MIN_ZOOM_LVL, DEFAULT_MIN_ZOOM);
+    }
+    public static int getMinZoomLvl()
+    {
+        return checkMinZoomLvl(__getMinZoomLvl());
+    }
+
+    public static void setMinZoomLvl(int minZoomLvl) {
+        minZoomLvl = checkMinZoomLvl(minZoomLvl);
     	Main.pref.put(SlippyMapPreferences.PREFERENCE_MIN_ZOOM_LVL, "" + minZoomLvl);
     }
-    
-    public static String[] getAllMapUrls()
-    {
-        String[] defaultTileSources = new String[]
-        {
-                "http://tah.openstreetmap.org/Tiles/tile", // t@h
-                "http://tah.openstreetmap.org/Tiles/maplint", // maplint
-                "http://tile.openstreetmap.org", // mapnik
-                "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
-                "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
-                "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
-        };
-        return defaultTileSources;
+
+    public static class Coastline extends OsmTileSource.AbstractOsmTileSource {
+        public Coastline() {
+            super("Coastline", "http://hypercube.telascience.org/tiles/1.0.0/coastline");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+    public static class FreeMapySk extends OsmTileSource.AbstractOsmTileSource {
+        public FreeMapySk() {
+            super("freemapy.sk", "http://www.freemap.sk/layers/allinone/?");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+    public static class FreeMapySkPokus extends OsmTileSource.AbstractOsmTileSource {
+        public FreeMapySkPokus() {
+            super("freemapy.sk pokus 2", "http://www.freemap.sk/layers/tiles/?");
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+
+    public static class Custom extends OsmTileSource.AbstractOsmTileSource {
+        public Custom(String name, String url) {
+            super(name, url);
+        }
+        public Custom(String name, String url, String extension) {
+            super(name, url);
+            this.extension = extension;
+        }
+        String extension;
+        @Override
+        public String getExtension() {
+            if (extension == null)
+                return super.getExtension();
+            return extension;
+        }
+        public TileUpdate getTileUpdate() {
+            return TileUpdate.IfNoneMatch;
+        }
+    }
+
+    public static List<TileSource> getCustomSources()
+    {
+        List<TileSource> ret = new ArrayList<TileSource>();
+        Map<String, String> customSources = Main.pref.getAllPrefix(PREFERENCE_TILE_CUSTOM_SOURCE);
+        for (String key : customSources.keySet()) {
+            String short_key = key.replaceFirst(PREFERENCE_TILE_CUSTOM_SOURCE, "");
+            // slippymap.custom_tile_source_1.name=OOC layer
+            // slippymap.custom_tile_source_1.url=http://a.ooc.openstreetmap.org/npe
+            // slippymap.custom_tile_source_1.ext=png
+
+            if (!(short_key.endsWith("name")))
+                continue;
+            String url_key = short_key.replaceFirst("name","url");
+            String ext_key = short_key.replaceFirst("name","ext");
+            String name = customSources.get(key);
+            String url = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + url_key);
+            String ext = customSources.get(PREFERENCE_TILE_CUSTOM_SOURCE + ext_key);
+            // ext may be null, but that's OK
+            System.out.println("found new tile source: '" +name+"' url:'"+url+"'"+"' ext:'"+ext+"'");
+            ret.add(new Custom(name, url, ext));
+        }
+        return ret;
+    }
+
+    public static ArrayList<TileSource> sources = null;
+    public static List<TileSource> getAllMapSources()
+    {
+        if (sources != null)
+            return sources;
+        sources = new ArrayList<TileSource>();
+        // first here is the default if the user does not set one
+        sources.add(new OsmTileSource.Mapnik());
+        sources.add(new OsmTileSource.CycleMap());
+        sources.add(new OsmTileSource.TilesAtHome());
+        sources.add(new Coastline());
+        sources.add(new FreeMapySkPokus());
+        sources.add(new FreeMapySk());
+        sources.addAll(getCustomSources());
+        // Probably need to either add these or let users add them somehow
+        //      "http://hypercube.telascience.org/tiles/1.0.0/coastline", // coastline
+        //      "http://www.freemap.sk/layers/allinone/?", //freemapy.sk
+        //      "http://www.freemap.sk/layers/tiles/?", //freemapy.sk pokus 2
+        return sources;
+    }
+
+    public static TileSource getSourceNamed(String name)
+    {
+        for (TileSource s : getAllMapSources())
+            if (s.getName().equals(name))
+                return s;
+        return null;
     }
 }
