Index: /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8396)
+++ /trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java	(revision 8397)
@@ -63,4 +63,8 @@
 
     public static class LIFOQueue extends LinkedBlockingDeque<Runnable> {
+        public LIFOQueue() {
+            super();
+        }
+
         public LIFOQueue(int capacity) {
             super(capacity);
@@ -91,5 +95,5 @@
      * and performance (we do want to have something to offer to worker threads before tasks will be resubmitted by class consumer)
      */
-    private static Executor DOWNLOAD_JOB_DISPATCHER = new ThreadPoolExecutor(
+    private static Executor DEFAULT_DOWNLOAD_JOB_DISPATCHER = new ThreadPoolExecutor(
             2, // we have a small queue, so threads will be quickly started (threads are started only, when queue is full)
             THREAD_LIMIT.get().intValue(), // do not this number of threads
@@ -114,4 +118,5 @@
     private int readTimeout;
     private Map<String, String> headers;
+    private Executor downloadJobExecutor;
 
     /**
@@ -120,8 +125,10 @@
      * @param readTimeout
      * @param connectTimeout
+     * @param downloadJobExecutor
      */
     public JCSCachedTileLoaderJob(ICacheAccess<K,V> cache,
             int connectTimeout, int readTimeout,
-            Map<String, String> headers) {
+            Map<String, String> headers,
+            Executor downloadJobExecutor) {
 
         this.cache = cache;
@@ -130,4 +137,18 @@
         this.readTimeout = readTimeout;
         this.headers = headers;
+        this.downloadJobExecutor = downloadJobExecutor;
+    }
+
+    /**
+     * @param cache cache instance that we will work on
+     * @param headers
+     * @param readTimeout
+     * @param connectTimeout
+     */
+    public JCSCachedTileLoaderJob(ICacheAccess<K, V> cache,
+            int connectTimeout, int readTimeout,
+            Map<String, String> headers) {
+        this(cache, connectTimeout, readTimeout,
+                headers, DEFAULT_DOWNLOAD_JOB_DISPATCHER);
     }
 
@@ -240,7 +261,6 @@
      */
     protected Executor getDownloadExecutor() {
-        return DOWNLOAD_JOB_DISPATCHER;
-    }
-
+        return downloadJobExecutor;
+    }
 
     public void run() {
@@ -330,4 +350,5 @@
                 return true;
             }
+
             HttpURLConnection urlConn = getURLConnection();
 
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java	(revision 8396)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java	(revision 8397)
@@ -4,4 +4,6 @@
 import java.io.IOException;
 import java.util.Map;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.jcs.access.behavior.ICacheAccess;
@@ -15,4 +17,5 @@
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
 import org.openstreetmap.josm.data.cache.JCSCacheManager;
+import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob.LIFOQueue;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
 
@@ -30,8 +33,33 @@
     private Map<String, String> headers;
     private TileLoaderListener listener;
-    public static final String PREFERENCE_PREFIX   = "imagery.tms.cache.";
+    private static final String PREFERENCE_PREFIX   = "imagery.tms.cache.";
     // average tile size is about 20kb
     public static final IntegerProperty MAX_OBJECTS_ON_DISK = new IntegerProperty(PREFERENCE_PREFIX + "max_objects_disk", 25000); // 25000 is around 500MB under this assumptions
 
+    /**
+     * overrides the THREAD_LIMIT in superclass, as we want to have separate limit and pool for TMS
+     */
+    public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobs", 25);
+
+    /**
+     * separate from JCS thread pool for TMS loader, so we can have different thread pools for default JCS
+     * and for TMS imagery
+     */
+    private static ThreadPoolExecutor DEFAULT_DOWNLOAD_JOB_DISPATCHER = getThreadPoolExecutor();
+
+    private static ThreadPoolExecutor getThreadPoolExecutor() {
+        return new ThreadPoolExecutor(
+                THREAD_LIMIT.get().intValue(), // keep the thread number constant
+                THREAD_LIMIT.get().intValue(), // do not this number of threads
+                30, // keepalive for thread
+                TimeUnit.SECONDS,
+                // make queue of LIFO type - so recently requested tiles will be loaded first (assuming that these are which user is waiting to see)
+                new LIFOQueue()
+                    /* keep the queue size fairly small, we do not want to
+                     download a lot of tiles, that user is not seeing anyway */
+                );
+    }
+
+    private ThreadPoolExecutor downloadExecutor = DEFAULT_DOWNLOAD_JOB_DISPATCHER;
 
     /**
@@ -58,5 +86,6 @@
     @Override
     public TileJob createTileLoaderJob(Tile tile) {
-        return new TMSCachedTileLoaderJob(listener, tile, cache, connectTimeout, readTimeout, headers);
+        return new TMSCachedTileLoaderJob(listener, tile, cache,
+                connectTimeout, readTimeout, headers, downloadExecutor);
     }
 
@@ -86,6 +115,43 @@
     }
 
+    /**
+     * @return cache statistics as string
+     */
     public String getStats() {
         return cache.getStats();
     }
+
+    /**
+     * Sets the download executor for this tile loader factory. Enables to use different queuing method
+     * for this factory.
+     * @param downloadExecutor
+     */
+    public void setDownloadExecutor(ThreadPoolExecutor downloadExecutor) {
+        this.downloadExecutor = downloadExecutor;
+    }
+
+    /**
+     * @return Executor that handles the jobs for this tile loader
+     */
+    public ThreadPoolExecutor getDownloadExecutor() {
+        return downloadExecutor;
+    }
+
+    /**
+     * cancels all outstanding tasks in the queue. This rollbacks the state of the tiles in the queue
+     * to loading = false / loaded = false
+     */
+    public void cancelOutstandingTasks() {
+        for(Runnable elem: downloadExecutor.getQueue()) {
+            if (elem instanceof TMSCachedTileLoaderJob) {
+                TMSCachedTileLoaderJob loaderJob = (TMSCachedTileLoaderJob) elem;
+                if (downloadExecutor.remove(loaderJob)) {
+                    Tile t = loaderJob.getTile();
+                    t.finishLoading();
+                    t.setLoaded(false);
+                }
+            }
+        }
+    }
+
 }
Index: /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8396)
+++ /trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java	(revision 8397)
@@ -15,6 +15,4 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Semaphore;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -99,33 +97,8 @@
 
     /**
-     * overrides the THREAD_LIMIT in superclass, as we want to have separate limit and pool for TMS
-     */
-    public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobs", 25);
-
-    /**
-     * separate from JCS thread pool for TMS loader, so we can have different thread pools for default JCS
-     * and for TMS imagery
-     */
-    private static ThreadPoolExecutor DOWNLOAD_JOB_DISPATCHER = getThreadPoolExecutor();
-
-    private static ThreadPoolExecutor getThreadPoolExecutor() {
-        return new ThreadPoolExecutor(
-                THREAD_LIMIT.get().intValue(), // keep the thread number constant
-                THREAD_LIMIT.get().intValue(), // do not this number of threads
-                30, // keepalive for thread
-                TimeUnit.SECONDS,
-                // make queue of LIFO type - so recently requested tiles will be loaded first (assuming that these are which user is waiting to see)
-                new LIFOQueue(5)
-                    /* keep the queue size fairly small, we do not want to
-                     download a lot of tiles, that user is not seeing anyway */
-                );
-    }
-
-    /**
      * Reconfigures download dispatcher using current values of THREAD_LIMIT and HOST_LIMIT
      */
     public static final void reconfigureDownloadDispatcher() {
         HOST_LIMITS = new ConcurrentHashMap<>();
-        DOWNLOAD_JOB_DISPATCHER = getThreadPoolExecutor();
     }
 
@@ -139,7 +112,9 @@
      * @param headers to be sent together with request
      */
-    public TMSCachedTileLoaderJob(TileLoaderListener listener, Tile tile, ICacheAccess<String, BufferedImageCacheEntry> cache, int connectTimeout, int readTimeout,
-            Map<String, String> headers) {
-        super(cache, connectTimeout, readTimeout, headers);
+    public TMSCachedTileLoaderJob(TileLoaderListener listener, Tile tile,
+            ICacheAccess<String, BufferedImageCacheEntry> cache,
+            int connectTimeout, int readTimeout, Map<String, String> headers,
+            Executor downloadExecutor) {
+        super(cache, connectTimeout, readTimeout, headers, downloadExecutor);
         this.tile = tile;
         if (listener != null) {
@@ -230,9 +205,4 @@
         }
         return false;
-    }
-
-    @Override
-    protected Executor getDownloadExecutor() {
-        return DOWNLOAD_JOB_DISPATCHER;
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 8396)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java	(revision 8397)
@@ -70,4 +70,5 @@
 import org.openstreetmap.josm.gui.MapView;
 import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
+import org.openstreetmap.josm.gui.NavigatableComponent.ZoomChangeListener;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
@@ -91,5 +92,5 @@
  *
  */
-public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderListener {
+public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderListener, ZoomChangeListener {
     public static final String PREFERENCE_PREFIX   = "imagery.tms";
 
@@ -104,6 +105,6 @@
     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_ADD_TO_SLIPPYMAP_CHOOSER = new BooleanProperty(PREFERENCE_PREFIX + ".add_to_slippymap_chooser", true);
+    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;
 
@@ -118,6 +119,20 @@
     }
 
+    /**
+     * Interface for creating TileLoaders, ie. classes responsible for loading tiles on map
+     *
+     */
     public interface TileLoaderFactory {
+        /**
+         * @param listener object that will be notified, when tile has finished loading
+         * @return TileLoader that will notify the listener
+         */
         TileLoader makeTileLoader(TileLoaderListener listener);
+
+        /**
+         * @param listener object that will be notified, when tile has finished loading
+         * @param headers HTTP headers that should be sent by TileLoader to tile server
+         * @return TileLoader that will notify the listener
+         */
         TileLoader makeTileLoader(TileLoaderListener listener, Map<String, String> headers);
     }
@@ -189,12 +204,12 @@
      * method.
      *
-     * @param monitor
-     * @see OsmFileCacheTileLoader#clearCache(org.openstreetmap.gui.jmapviewer.interfaces.TileSource, org.openstreetmap.gui.jmapviewer.interfaces.TileClearController)
-     */
-    void clearTileCache(ProgressMonitor monitor) {
+     * @param monitor not used in this implementation - as cache clear is instaneus
+     */
+    public void clearTileCache(ProgressMonitor monitor) {
         tileCache.clear();
         if (tileLoader instanceof CachedTileLoader) {
             ((CachedTileLoader)tileLoader).clearCache(tileSource);
         }
+        redraw();
     }
 
@@ -225,10 +240,10 @@
      * @see MapFrame#repaint()
      */
-    void redraw() {
+    protected void redraw() {
         needRedraw = true;
         Main.map.repaint();
     }
 
-    static int checkMaxZoomLvl(int maxZoomLvl, TileSource ts) {
+    protected static int checkMaxZoomLvl(int maxZoomLvl, TileSource ts) {
         if(maxZoomLvl > MAX_ZOOM) {
             maxZoomLvl = MAX_ZOOM;
@@ -248,6 +263,6 @@
 
     public static void setMaxZoomLvl(int maxZoomLvl) {
-        maxZoomLvl = checkMaxZoomLvl(maxZoomLvl, null);
-        PROP_MAX_ZOOM_LVL.put(maxZoomLvl);
+        Integer newMaxZoom = Integer.valueOf(checkMaxZoomLvl(maxZoomLvl, null));
+        PROP_MAX_ZOOM_LVL.put(newMaxZoom);
     }
 
@@ -342,8 +357,6 @@
             return t;
         } else if (info.getImageryType() == ImageryType.BING) {
-            //return new CachedAttributionBingAerialTileSource(info.getId());
             return new CachedAttributionBingAerialTileSource(info);
         } else if (info.getImageryType() == ImageryType.SCANEX) {
-            //return new ScanexTileSource(info.getName(), info.getUrl(), info.getId(), info.getMaxZoom());
             return new ScanexTileSource(info);
         }
@@ -385,5 +398,4 @@
         }
 
-        // FIXME: tileCache = new MemoryTileCache();
         tileLoader = loaderFactory.makeTileLoader(this, headers);
         if (tileLoader instanceof TMSCachedTileLoader) {
@@ -464,4 +476,6 @@
             throw new IllegalStateException("Cannot create TMSLayer with non-TMS ImageryInfo");
         initTileSource(source);
+
+        MapView.addZoomChangeListener(this);
     }
 
@@ -538,14 +552,13 @@
         }));
 
-        /* FIXME
         tileOptionMenu.add(new JMenuItem(new AbstractAction(
                 tr("Request Update")) {
             public void actionPerformed(ActionEvent ae) {
                 if (clickedTile != null) {
-                    clickedTile.requestUpdate();
-                    redraw();
-                }
-            }
-        }));*/
+                    clickedTile.setLoaded(false);
+                    tileLoader.createTileLoaderJob(clickedTile).submit();
+                }
+            }
+        }));
 
         tileOptionMenu.add(new JMenuItem(new AbstractAction(
@@ -590,6 +603,6 @@
             @Override
             public void actionPerformed(ActionEvent ae) {
-                double new_factor = Math.sqrt(getScaleFactor(currentZoomLevel));
-                Main.map.mapView.zoomToFactor(new_factor);
+                double newFactor = Math.sqrt(getScaleFactor(currentZoomLevel));
+                Main.map.mapView.zoomToFactor(newFactor);
                 redraw();
             }
@@ -647,4 +660,5 @@
                 if (oldLayer == TMSLayer.this) {
                     Main.map.mapView.removeMouseListener(adapter);
+                    MapView.removeZoomChangeListener(TMSLayer.this);
                     MapView.removeLayerChangeListener(this);
                 }
@@ -653,12 +667,20 @@
     }
 
-    void zoomChanged() {
+    /**
+     * This fires every time the user changes the zoom, but also (due to ZoomChangeListener) - on all
+     * changes to visible map (panning/zooming)
+     */
+    @Override
+    public void zoomChanged() {
         if (Main.isDebugEnabled()) {
             Main.debug("zoomChanged(): " + currentZoomLevel);
         }
         needRedraw = true;
-    }
-
-    int getMaxZoomLvl() {
+        if (tileLoader instanceof TMSCachedTileLoader) {
+            ((TMSCachedTileLoader) tileLoader).cancelOutstandingTasks();
+        }
+    }
+
+    protected int getMaxZoomLvl() {
         if (info.getMaxZoom() != 0)
             return checkMaxZoomLvl(info.getMaxZoom(), tileSource);
@@ -667,5 +689,5 @@
     }
 
-    int getMinZoomLvl() {
+    protected int getMinZoomLvl() {
         return getMinZoomLvl(tileSource);
     }
@@ -674,5 +696,5 @@
      * Zoom in, go closer to map.
      *
-     * @return    true, if zoom increasing was successfull, false othervise
+     * @return    true, if zoom increasing was successful, false otherwise
      */
     public boolean zoomIncreaseAllowed() {
@@ -742,5 +764,5 @@
      * into the tileCache.
      */
-    Tile tempCornerTile(Tile t) {
+    private Tile tempCornerTile(Tile t) {
         int x = t.getXtile() + 1;
         int y = t.getYtile() + 1;
@@ -752,5 +774,5 @@
     }
 
-    Tile getOrCreateTile(int x, int y, int zoom) {
+    private Tile getOrCreateTile(int x, int y, int zoom) {
         Tile tile = getTile(x, y, zoom);
         if (tile == null) {
@@ -766,5 +788,5 @@
      * already in the cache.
      */
-    Tile getTile(int x, int y, int zoom) {
+    private Tile getTile(int x, int y, int zoom) {
         int max = 1 << zoom;
         if (x < 0 || x >= max || y < 0 || y >= max)
@@ -773,5 +795,5 @@
     }
 
-    boolean loadTile(Tile tile, boolean force) {
+    private boolean loadTile(Tile tile, boolean force) {
         if (tile == null)
             return false;
@@ -784,5 +806,5 @@
     }
 
-    void loadAllTiles(boolean force) {
+    private void loadAllTiles(boolean force) {
         MapView mv = Main.map.mapView;
         EastNorth topLeft = mv.getEastNorth(0, 0);
@@ -800,5 +822,5 @@
     }
 
-    void loadAllErrorTiles(boolean force) {
+    private void loadAllErrorTiles(boolean force) {
         MapView mv = Main.map.mapView;
         EastNorth topLeft = mv.getEastNorth(0, 0);
@@ -821,5 +843,5 @@
     }
 
-    boolean imageLoaded(Image i) {
+    private boolean imageLoaded(Image i) {
         if (i == null)
             return false;
@@ -837,5 +859,5 @@
      * @return  the image of the tile or null.
      */
-    Image getLoadedTileImage(Tile tile) {
+    private Image getLoadedTileImage(Tile tile) {
         if (!tile.isLoaded())
             return null;
@@ -846,5 +868,5 @@
     }
 
-    LatLon tileLatLon(Tile t) {
+    private LatLon tileLatLon(Tile t) {
         int zoom = t.getZoom();
         return new LatLon(tileSource.tileYToLat(t.getYtile(), zoom),
@@ -852,5 +874,5 @@
     }
 
-    Rectangle tileToRect(Tile t1) {
+    private Rectangle tileToRect(Tile t1) {
         /*
          * We need to get a box in which to draw, so advance by one tile in
@@ -870,5 +892,5 @@
     // 'border' is the screen cordinates that need to be drawn.
     //  We must not draw outside of it.
-    void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border) {
+    private void drawImageInside(Graphics g, Image sourceImg, Rectangle source, Rectangle border) {
         Rectangle target = source;
 
@@ -931,5 +953,5 @@
     // drawn currently.  If drawing the displayZoomLevel,
     // border is null and we draw the entire tile set.
-    List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
+    private List<Tile> paintTileImages(Graphics g, TileSet ts, int zoom, Tile border) {
         if (zoom <= 0) return Collections.emptyList();
         Rectangle borderRect = null;
@@ -962,5 +984,5 @@
     }
 
-    void myDrawString(Graphics g, String text, int x, int y) {
+    private void myDrawString(Graphics g, String text, int x, int y) {
         Color oldColor = g.getColor();
         g.setColor(Color.black);
@@ -970,5 +992,5 @@
     }
 
-    void paintTileText(TileSet ts, Tile tile, Graphics g, MapView mv, int zoom, Tile t) {
+    private void paintTileText(TileSet ts, Tile tile, Graphics g, MapView mv, int zoom, Tile t) {
         int fontHeight = g.getFontMetrics().getHeight();
         if (tile == null)
@@ -1066,5 +1088,5 @@
          * Create a TileSet by EastNorth bbox taking a layer shift in account
          */
-        TileSet(EastNorth topLeft, EastNorth botRight, int zoom) {
+        private TileSet(EastNorth topLeft, EastNorth botRight, int zoom) {
             this(getShiftedLatLon(topLeft), getShiftedLatLon(botRight),zoom);
         }
@@ -1073,5 +1095,5 @@
          * Create a TileSet by known LatLon bbox without layer shift correction
          */
-        TileSet(LatLon topLeft, LatLon botRight, int zoom) {
+        private TileSet(LatLon topLeft, LatLon botRight, int zoom) {
             this.zoom = zoom;
             if (zoom == 0)
@@ -1107,21 +1129,21 @@
         }
 
-        boolean tooSmall() {
+        private boolean tooSmall() {
             return this.tilesSpanned() < 2.1;
         }
 
-        boolean tooLarge() {
+        private boolean tooLarge() {
             return this.tilesSpanned() > 10;
         }
 
-        boolean insane() {
+        private boolean insane() {
             return this.tilesSpanned() > 100;
         }
 
-        double tilesSpanned() {
+        private double tilesSpanned() {
             return Math.sqrt(1.0 * this.size());
         }
 
-        int size() {
+        private int size() {
             int x_span = x1 - x0 + 1;
             int y_span = y1 - y0 + 1;
@@ -1133,9 +1155,9 @@
          * already in the tileCache.
          */
-        List<Tile> allExistingTiles() {
+        private List<Tile> allExistingTiles() {
             return this.__allTiles(false);
         }
 
-        List<Tile> allTilesCreate() {
+        private List<Tile> allTilesCreate() {
             return this.__allTiles(true);
         }
@@ -1171,5 +1193,5 @@
         }
 
-        void loadAllTiles(boolean force) {
+        private void loadAllTiles(boolean force) {
             if (!autoLoad && !force)
                 return;
@@ -1179,5 +1201,5 @@
         }
 
-        void loadAllErrorTiles(boolean force) {
+        private void loadAllErrorTiles(boolean force) {
             if (!autoLoad && !force)
                 return;
@@ -1418,5 +1440,5 @@
      * user right-clicks on the map.
      */
-    Tile getTileForPixelpos(int px, int py) {
+    private Tile getTileForPixelpos(int px, int py) {
         if (Main.isDebugEnabled()) {
             Main.debug("getTileForPixelpos("+px+", "+py+")");
