Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 4186)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java	(revision 4188)
@@ -4,4 +4,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.openstreetmap.josm.io.OsmApi;
@@ -38,4 +40,6 @@
     double pixelPerDegree = 0.0;
     int maxZoom = 0;
+    int defaultMaxZoom = 0;
+    int defaultMinZoom = 0;
 
     public ImageryInfo(String name) {
@@ -135,4 +139,5 @@
         this.cookies=i.cookies;
         this.imageryType=i.imageryType;
+        this.defaultMaxZoom=i.defaultMaxZoom;
         this.pixelPerDegree=i.pixelPerDegree;
         this.eulaAcceptanceRequired = null;
@@ -169,7 +174,15 @@
         
         for (ImageryType type : ImageryType.values()) {
-            if (url.startsWith(type.getUrlString() + ":")) {
-                this.url = url.substring(type.getUrlString().length() + 1);
+            Matcher m = Pattern.compile(type.getUrlString()+"(?:\\[(?:(\\d+),)?(\\d+)\\])?:(.*)").matcher(url);
+            if(m.matches()) {
+                this.url = m.group(3);
                 this.imageryType = type;
+                if(m.group(2) != null) {
+                    defaultMaxZoom = Integer.valueOf(m.group(2));
+                    maxZoom = defaultMaxZoom;
+                }
+                if(m.group(1) != null) {
+                    defaultMinZoom = Integer.valueOf(m.group(1));
+                }
                 return;
             }
@@ -205,6 +218,11 @@
     }
 
+    public int getMinZoom() {
+        return this.defaultMinZoom;
+    }
+
     public String getFullUrl() {
-        return imageryType.getUrlString() + ":" + url;
+        return imageryType.getUrlString() + (defaultMaxZoom != 0
+            ? "["+(defaultMinZoom != 0 ? defaultMinZoom+",":"")+defaultMaxZoom+"]" : "") + ":" + url;
     }
 
@@ -223,5 +241,5 @@
         if(pixelPerDegree != 0.0) {
             res += " ("+pixelPerDegree+")";
-        } else if(maxZoom != 0) {
+        } else if(maxZoom != 0 && maxZoom != defaultMaxZoom) {
             res += " (z"+maxZoom+")";
         }
Index: trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4186)
+++ trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 4188)
@@ -92,5 +92,5 @@
     public static final IntegerProperty PROP_MIN_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".min_zoom_lvl", DEFAULT_MIN_ZOOM);
     public static final IntegerProperty PROP_MAX_ZOOM_LVL = new IntegerProperty(PREFERENCE_PREFIX + ".max_zoom_lvl", DEFAULT_MAX_ZOOM);
-    public static final BooleanProperty PROP_DRAW_DEBUG = new BooleanProperty(PREFERENCE_PREFIX + ".draw_debug", false);
+    //public static final BooleanProperty PROP_DRAW_DEBUG = new BooleanProperty(PREFERENCE_PREFIX + ".draw_debug", false);
     public static final BooleanProperty PROP_ADD_TO_SLIPPYMAP_CHOOSER = new BooleanProperty(PREFERENCE_PREFIX + ".add_to_slippymap_chooser", true);
     public static final StringProperty PROP_TILECACHE_DIR;
@@ -105,9 +105,5 @@
     }
 
-    boolean debug = false;
-    void out(String s)
-    {
-        Main.debug(s);
-    }
+    /*boolean debug = false;*/
 
     protected MemoryTileCache tileCache;
@@ -131,7 +127,7 @@
         Main.map.repaint(100);
         tileRequestsOutstanding.remove(tile);
-        if (debug) {
-            out("tileLoadingFinished() tile: " + tile + " success: " + success);
-        }
+        /*if (debug) {
+            Main.debug("tileLoadingFinished() tile: " + tile + " success: " + success);
+        }*/
     }
     @Override
@@ -142,7 +138,7 @@
     void clearTileCache()
     {
-        if (debug) {
-            out("clearing tile storage");
-        }
+        /*if (debug) {
+            Main.debug("clearing tile storage");
+        }*/
         tileCache = new MemoryTileCache();
         tileCache.setCacheSize(200);
@@ -185,9 +181,9 @@
     {
         if(maxZoomLvl > MAX_ZOOM) {
-            System.err.println("MaxZoomLvl shouldnt be more than 30! Setting to 30.");
+            /*Main.debug("Max. zoom level should not be more than 30! Setting to 30.");*/
             maxZoomLvl = MAX_ZOOM;
         }
         if(maxZoomLvl < PROP_MIN_ZOOM_LVL.get()) {
-            System.err.println("maxZoomLvl shouldnt be more than minZoomLvl! Setting to minZoomLvl.");
+            /*Main.debug("Max. zoom level should not be more than min. zoom level! Setting to min.");*/
             maxZoomLvl = PROP_MIN_ZOOM_LVL.get();
         }
@@ -211,13 +207,13 @@
     {
         if(minZoomLvl < MIN_ZOOM) {
-            System.err.println("minZoomLvl shouldnt be lees than "+MIN_ZOOM+"! Setting to that.");
+            /*Main.debug("Min. zoom level should not be less than "+MIN_ZOOM+"! Setting to that.");*/
             minZoomLvl = MIN_ZOOM;
         }
         if(minZoomLvl > PROP_MAX_ZOOM_LVL.get()) {
-            System.err.println("minZoomLvl shouldnt be more than maxZoomLvl! Setting to maxZoomLvl.");
+            /*Main.debug("Min. zoom level should not be more than Max. zoom level! Setting to Max.");*/
             minZoomLvl = getMaxZoomLvl(ts);
         }
         if (ts != null && ts.getMinZoom() > minZoomLvl) {
-            System.err.println("increasomg minZoomLvl to match tile source");
+            /*Main.debug("Increasing min. zoom level to match tile source");*/
             minZoomLvl = ts.getMinZoom();
         }
@@ -238,7 +234,7 @@
         if (info.getImageryType() == ImageryType.TMS) {
             if(ImageryInfo.isUrlWithPatterns(info.getUrl()))
-                return new TemplatedTMSTileSource(info.getName(), info.getUrl(), info.getMaxZoom());
+                return new TemplatedTMSTileSource(info.getName(), info.getUrl(), info.getMinZoom(), info.getMaxZoom());
             else
-                return new TMSTileSource(info.getName(),info.getUrl(), info.getMaxZoom());
+                return new TMSTileSource(info.getName(),info.getUrl(), info.getMinZoom(), info.getMaxZoom());
         } else if (info.getImageryType() == ImageryType.BING)
             return new BingAerialTileSource();
@@ -254,9 +250,9 @@
         if(requireAttr) {
             attrImage = tileSource.getAttributionImage();
-            if(attrImage == null) {
-                System.out.println("Attribution image was null.");
+            /*if(attrImage == null) {
+                Main.debug("Attribution image was null.");
             } else {
-                System.out.println("Got an attribution image " + attrImage.getHeight(this) + "x" + attrImage.getWidth(this));
-            }
+                Main.debug("Got an attribution image " + attrImage.getHeight(this) + "x" + attrImage.getWidth(this));
+            }*/
 
             attrTermsUrl = tileSource.getTermsOfUseURL();
@@ -374,5 +370,5 @@
             @Override
             public void actionPerformed(ActionEvent ae) {
-                out("info tile: " + clickedTile);
+                //Main.debug("info tile: " + clickedTile);
                 if (clickedTile != null) {
                     showMetadataTile = clickedTile;
@@ -438,7 +434,7 @@
                     @Override
                     public void actionPerformed(ActionEvent ae) {
-                        System.out.print("flushing all tiles...");
+                        //Main.debug("flushing all tiles...");
                         clearTileCache();
-                        System.out.println("done");
+                        //Main.debug("done");
                     }
                 }));
@@ -508,7 +504,7 @@
     void zoomChanged()
     {
-        if (debug) {
-            out("zoomChanged(): " + currentZoomLevel);
-        }
+        /*if (debug) {
+            Main.debug("zoomChanged(): " + currentZoomLevel);
+        }*/
         needRedraw = true;
         jobDispatcher.cancelOutstandingJobs();
@@ -537,7 +533,7 @@
     {
         boolean zia = currentZoomLevel < this.getMaxZoomLvl();
-        if (debug) {
-            out("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() );
-        }
+        /*if (debug) {
+            Main.debug("zoomIncreaseAllowed(): " + zia + " " + currentZoomLevel + " vs. " + this.getMaxZoomLvl() );
+        }*/
         return zia;
     }
@@ -546,11 +542,11 @@
         if (zoomIncreaseAllowed()) {
             currentZoomLevel++;
-            if (debug) {
-                out("increasing zoom level to: " + currentZoomLevel);
-            }
+            /*if (debug) {
+                Main.debug("increasing zoom level to: " + currentZoomLevel);
+            }*/
             zoomChanged();
         } else {
-            System.err.println("current zoom lvl ("+currentZoomLevel+") couldnt be increased. "+
-                    "MaxZoomLvl ("+this.getMaxZoomLvl()+") reached.");
+            Main.warn("Current zoom level ("+currentZoomLevel+") could not be increased. "+
+                    "Max.zZoom Level "+this.getMaxZoomLvl()+" reached.");
             return false;
         }
@@ -580,11 +576,11 @@
         int minZoom = this.getMinZoomLvl();
         if (zoomDecreaseAllowed()) {
-            if (debug) {
-                out("decreasing zoom level to: " + currentZoomLevel);
-            }
+            /*if (debug) {
+                Main.debug("decreasing zoom level to: " + currentZoomLevel);
+            }*/
             currentZoomLevel--;
             zoomChanged();
         } else {
-            System.err.println("current zoom lvl couldnt be decreased. MinZoomLvl("+minZoom+") reached.");
+            /*Main.debug("Current zoom level could not be decreased. Min. zoom level "+minZoom+" reached.");*/
             return false;
         }
@@ -671,7 +667,7 @@
         boolean done = ((infoflags & (ERROR | FRAMEBITS | ALLBITS)) != 0);
         needRedraw = true;
-        if (debug) {
-            out("imageUpdate() done: " + done + " calling repaint");
-        }
+        /*if (debug) {
+            Main.debug("imageUpdate() done: " + done + " calling repaint");
+        }*/
         Main.map.repaint(done ? 0 : 100);
         return !done;
@@ -730,7 +726,7 @@
         if (border != null) {
             target = source.intersection(border);
-            if (debug) {
-                out("source: " + source + "\nborder: " + border + "\nintersection: " + target);
-            }
+            /*if (debug) {
+                Main.debug("source: " + source + "\nborder: " + border + "\nintersection: " + target);
+            }*/
         }
 
@@ -757,7 +753,7 @@
         int img_y_end   = img_y_offset + (int)(target.getHeight() * imageYScaling);
 
-        if (debug) {
-            out("drawing image into target rect: " + target);
-        }
+        /*if (debug) {
+            Main.debug("drawing image into target rect: " + target);
+        }*/
         g.drawImage(sourceImg,
                 target.x, target.y,
@@ -792,7 +788,7 @@
             Image img = getLoadedTileImage(tile);
             if (img == null || tile.hasError()) {
-                if (debug) {
-                    out("missed tile: " + tile);
-                }
+                /*if (debug) {
+                    Main.debug("missed tile: " + tile);
+                }*/
                 missedTiles.add(tile);
                 continue;
@@ -822,5 +818,5 @@
         int texty = p.y + 2 + fontHeight;
 
-        if (PROP_DRAW_DEBUG.get()) {
+        /*if (PROP_DRAW_DEBUG.get()) {
             myDrawString(g, "x=" + t.getXtile() + " y=" + t.getYtile() + " z=" + zoom + "", p.x + 2, texty);
             texty += 1 + fontHeight;
@@ -829,5 +825,5 @@
                 texty += 1 + fontHeight;
             }
-        }// end of if draw debug
+        }*/// end of if draw debug
 
         if (tile == showMetadataTile) {
@@ -847,8 +843,8 @@
 
         String tileStatus = tile.getStatus();
-        if (!tile.isLoaded() && PROP_DRAW_DEBUG.get()) {
+        /*if (!tile.isLoaded() && PROP_DRAW_DEBUG.get()) {
             myDrawString(g, tr("image " + tileStatus), p.x + 2, texty);
             texty += 1 + fontHeight;
-        }
+        }*/
 
         if (tile.hasError()) {
@@ -857,5 +853,5 @@
         }
 
-        int xCursor = -1;
+        /*int xCursor = -1;
         int yCursor = -1;
         if (PROP_DRAW_DEBUG.get()) {
@@ -880,5 +876,5 @@
                 xCursor = t.getXtile();
             }
-        }
+        }*/
     }
 
@@ -1007,8 +1003,8 @@
                 }
             }
-            if (debug)
+            /*if (debug)
                 if (nr_queued > 0) {
-                    out("queued to load: " + nr_queued + "/" + tiles.size() + " tiles at zoom: " + zoom);
-                }
+                    Main.debug("queued to load: " + nr_queued + "/" + tiles.size() + " tiles at zoom: " + zoom);
+                }*/
         }
     }
@@ -1084,5 +1080,5 @@
 
         if (botRight.east() == 0.0 || botRight.north() == 0) {
-            Main.debug("still initializing??");
+            /*Main.debug("still initializing??");*/
             // probably still initializing
             return;
@@ -1143,5 +1139,5 @@
         // Too many tiles... refuse to download
         if (!ts.tooLarge()) {
-            //out("size: " + ts.size() + " spanned: " + ts.tilesSpanned());
+            //Main.debug("size: " + ts.size() + " spanned: " + ts.tilesSpanned());
             ts.loadAllTiles(false);
         }
@@ -1184,7 +1180,7 @@
             missedTiles = newlyMissedTiles;
         }
-        if (debug && missedTiles.size() > 0) {
-            out("still missed "+missedTiles.size()+" in the end");
-        }
+        /*if (debug && missedTiles.size() > 0) {
+            Main.debug("still missed "+missedTiles.size()+" in the end");
+        }*/
         g.setColor(Color.red);
         g.setFont(InfoFont);
@@ -1251,10 +1247,10 @@
             myDrawString(g, tr("No tiles at this zoom level"), 120, 120);
         }
-        if (debug) {
+        /*if (debug) {
             myDrawString(g, tr("Current zoom: {0}", currentZoomLevel), 50, 140);
             myDrawString(g, tr("Display zoom: {0}", displayZoomLevel), 50, 155);
             myDrawString(g, tr("Pixel scale: {0}", getScaleFactor(currentZoomLevel)), 50, 170);
             myDrawString(g, tr("Best zoom: {0}", Math.log(getScaleFactor(1))/Math.log(2)/2+1), 50, 185);
-        }
+        }*/
     }// end of paint method
 
@@ -1264,7 +1260,7 @@
      */
     Tile getTileForPixelpos(int px, int py) {
-        if (debug) {
-            out("getTileForPixelpos("+px+", "+py+")");
-        }
+        /*if (debug) {
+            Main.debug("getTileForPixelpos("+px+", "+py+")");
+        }*/
         MapView mv = Main.map.mapView;
         Point clicked = new Point(px, py);
@@ -1282,7 +1278,7 @@
             Rectangle r = new Rectangle(pixelPos(t1));
             r.add(pixelPos(t2));
-            if (debug) {
-                out("r: " + r + " clicked: " + clicked);
-            }
+            /*if (debug) {
+                Main.debug("r: " + r + " clicked: " + clicked);
+            }*/
             if (!r.contains(clicked)) {
                 continue;
@@ -1293,6 +1289,6 @@
         if (clickedTile == null)
             return null;
-        System.out.println("clicked on tile: " + clickedTile.getXtile() + " " + clickedTile.getYtile() +
-                " currentZoomLevel: " + currentZoomLevel);
+        /*Main.debug("Clicked on tile: " + clickedTile.getXtile() + " " + clickedTile.getYtile() +
+                " currentZoomLevel: " + currentZoomLevel);*/
         return clickedTile;
     }
Index: trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java	(revision 4186)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/AddWMSLayerPanel.java	(revision 4188)
@@ -3,4 +3,5 @@
 
 import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trc;
 
 import java.awt.Component;
@@ -79,4 +80,5 @@
     private boolean previouslyShownUnsupportedCrsError = false;
     private JTextArea tmsURL;
+    private JTextField tmsZoom;
 
     public AddWMSLayerPanel() {
@@ -200,4 +202,13 @@
                 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
         tmsView.add(tmsUrlScrollPane, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
+        tmsView.add(new JLabel(trc("layer", "Zoom")), GBC.std().insets(0,0,5,0));
+        tmsZoom = new JTextField(3);
+        tmsZoom.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyReleased(KeyEvent e) {
+                resultingLayerField.setText(buildTMSUrl());
+            }
+        });
+        tmsView.add(tmsZoom, GBC.eop().insets(5,0,0,0).fill(GridBagConstraints.HORIZONTAL));
         tabbedPane.addTab(tr("TMS"), tmsView);
 
@@ -229,5 +240,9 @@
 
     private String buildTMSUrl() {
-        StringBuilder a = new StringBuilder("tms:");
+        StringBuilder a = new StringBuilder("tms");
+        String z = sanitize(tmsZoom.getText());
+        if(!z.isEmpty())
+            a.append("["+z+"]");
+        a.append(":");
         a.append(sanitize(tmsURL.getText()));
         return a.toString();
