Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java	(revision 24543)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryInfo.java	(revision 24544)
@@ -31,4 +31,5 @@
     ImageryType imageryType = ImageryType.WMS;
     double pixelPerDegree = 0.0;
+    int maxZoom = 0;
 
     public ImageryInfo(String name) {
@@ -66,5 +67,9 @@
         if(url != null && !url.isEmpty()) e2 = getFullURL();
         if(cookies != null && !cookies.isEmpty()) e3 = cookies;
-        if(pixelPerDegree != 0.0) e4 = String.valueOf(pixelPerDegree);
+        if(imageryType == ImageryType.WMS) {
+            if(pixelPerDegree != 0.0) e4 = String.valueOf(pixelPerDegree);
+        } else {
+            if(maxZoom != 0) e4 = String.valueOf(maxZoom);
+        }
         if(e4 != null && e3 == null) e3 = "";
         if(e3 != null && e2 == null) e2 = "";
@@ -83,5 +88,6 @@
         if(array.size() >= 2) setURL(array.get(1));
         if(array.size() >= 3) this.cookies=array.get(2);
-        if(array.size() >= 4) this.pixelPerDegree=Double.valueOf(array.get(3));
+        if(imageryType == ImageryType.WMS && array.size() >= 4) this.pixelPerDegree=Double.valueOf(array.get(3));
+        if(imageryType == ImageryType.TMS && array.size() >= 4) this.maxZoom=Integer.valueOf(array.get(3));
     }
 
@@ -148,4 +154,8 @@
     }
 
+    public int getMaxZoom() {
+        return this.maxZoom;
+    }
+
     public String getFullURL() {
         return imageryType.getUrlString() + ":" + url;
@@ -165,4 +175,6 @@
         if(pixelPerDegree != 0.0)
             res += " ("+pixelPerDegree+")";
+        else if(maxZoom != 0)
+            res += " (z"+maxZoom+")";
         return res;
     }
Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java	(revision 24543)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/ImageryPreferenceEditor.java	(revision 24544)
@@ -40,4 +40,5 @@
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
+import org.openstreetmap.josm.plugins.imagery.ImageryInfo.ImageryType;
 import org.openstreetmap.josm.plugins.imagery.tms.TMSPreferences;
 import org.openstreetmap.josm.plugins.imagery.wms.AddWMSLayerPanel;
@@ -73,5 +74,5 @@
             public String getToolTipText(MouseEvent e) {
                 java.awt.Point p = e.getPoint();
-                return (String) model.getValueAt(rowAtPoint(p), columnAtPoint(p));
+                return model.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
             }
         };
@@ -397,5 +398,6 @@
                 return info.getFullURL();
             case 2:
-                return info.pixelPerDegree == 0.0 ? "" : info.pixelPerDegree;
+                return (info.imageryType == ImageryType.WMS) ? (info.pixelPerDegree == 0.0 ? "" : info.pixelPerDegree)
+                                                             : (info.maxZoom == 0 ? "" : info.maxZoom);
             }
             return null;
@@ -410,4 +412,9 @@
             case 1:
                 info.setURL((String)o);
+            case 2:
+                if(info.imageryType == ImageryType.WMS)
+                    info.pixelPerDegree = Double.parseDouble((String) o);
+                else
+                    info.maxZoom = Integer.parseInt((String) o);
             }
         }
@@ -415,5 +422,5 @@
         @Override
         public boolean isCellEditable(int row, int column) {
-            return (column != 2);
+            return true;
         }
     }
Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java	(revision 24543)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSLayer.java	(revision 24544)
@@ -183,5 +183,9 @@
 
         if (info.getImageryType() == ImageryType.TMS) {
-            initTileSource(new TMSTileSource(info.getName(),info.getURL()));
+            if(isUrlWithPatterns(info.getURL())) {
+                initTileSource(new TemplatedTMSTileSource(info.getName(), info.getURL(), info.getMaxZoom()));
+            } else {
+                initTileSource(new TMSTileSource(info.getName(),info.getURL(), info.getMaxZoom()));
+            }
         } else if (info.getImageryType() == ImageryType.BING) {
             initTileSource(new BingAerialTileSource());
@@ -358,4 +362,8 @@
             }
         });
+    }
+
+    public static boolean isUrlWithPatterns(String url) {
+        return url != null && url.contains("{") && url.contains("}");
     }
 
Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java	(revision 24543)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSPreferences.java	(revision 24544)
@@ -59,6 +59,5 @@
             maxZoomLvl = PROP_MIN_ZOOM_LVL.get();
         }
-        if (ts != null && ts.getMaxZoom() < maxZoomLvl) {
-            System.err.println("decreasing maxZoomLvl to match tile source");
+        if (ts != null && ts.getMaxZoom() != 0) {
             maxZoomLvl = ts.getMaxZoom();
         }
Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java	(revision 24543)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TMSTileSource.java	(revision 24544)
@@ -4,7 +4,16 @@
 
 public class TMSTileSource extends OsmTileSource.AbstractOsmTileSource {
-    public TMSTileSource(String name, String url) {
+    private int maxZoom;
+
+    public TMSTileSource(String name, String url, int maxZoom) {
         super(name, url);
+        this.maxZoom = maxZoom;
     }
+
+    @Override
+    public int getMaxZoom() {
+        return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
+    }
+
     @Override
     public TileUpdate getTileUpdate() {
Index: /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java
===================================================================
--- /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java	(revision 24544)
+++ /applications/editors/josm/plugins/imagery/src/org/openstreetmap/josm/plugins/imagery/tms/TemplatedTMSTileSource.java	(revision 24544)
@@ -0,0 +1,30 @@
+package org.openstreetmap.josm.plugins.imagery.tms;
+
+import org.openstreetmap.gui.jmapviewer.OsmTileSource;
+
+public class TemplatedTMSTileSource extends OsmTileSource.AbstractOsmTileSource {
+    private int maxZoom;
+    
+    public TemplatedTMSTileSource(String name, String url, int maxZoom) {
+        super(name, url);
+        this.maxZoom = maxZoom;
+    }
+
+    public String getTileUrl(int zoom, int tilex, int tiley) {
+        return this.BASE_URL
+        .replaceAll("\\{zoom\\}", Integer.toString(zoom))
+        .replaceAll("\\{x\\}", Integer.toString(tilex))
+        .replaceAll("\\{y\\}", Integer.toString(tiley));
+        
+    }
+
+    @Override
+    public int getMaxZoom() {
+        return (maxZoom == 0) ? super.getMaxZoom() : maxZoom;
+    }
+
+    @Override
+    public TileUpdate getTileUpdate() {
+        return TileUpdate.IfNoneMatch;
+    }
+}
