Index: src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 17635)
+++ src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(working copy)
@@ -38,7 +38,6 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.Logging;
@@ -79,7 +78,10 @@
 
     protected boolean recurseDownRelations;
     private boolean recurseDownAppended = true;
+    private boolean hideSubTaskDetails;
 
+    private ExecutorService exec;
+
     /**
      * Constructs a {@code MultiFetchServerObjectReader}.
      */
@@ -301,18 +303,32 @@
      * @throws OsmTransferException if an error occurs while communicating with the API server
      */
     protected void fetchPrimitives(Set<Long> ids, OsmPrimitiveType type, ProgressMonitor progressMonitor) throws OsmTransferException {
+        int n = ids.size();
+        if (n == 0)
+            return;
         String msg;
         final String baseUrl = getBaseUrl();
         switch (type) {
-            // CHECKSTYLE.OFF: SingleSpaceSeparator
-            case NODE:     msg = tr("Fetching a package of nodes from ''{0}''",     baseUrl); break;
-            case WAY:      msg = tr("Fetching a package of ways from ''{0}''",      baseUrl); break;
-            case RELATION: msg = tr("Fetching a package of relations from ''{0}''", baseUrl); break;
-            // CHECKSTYLE.ON: SingleSpaceSeparator
-            default: throw new AssertionError();
+        case NODE:
+            msg = trn("Fetching {0} node from ''{1}''", "Fetching {0} nodes from ''{1}''", n, n, baseUrl);
+            break;
+        case WAY:
+            msg = trn("Fetching {0} way from ''{1}''", "Fetching {0} ways from ''{1}''", n, n, baseUrl);
+            break;
+        case RELATION:
+            msg = trn("Fetching {0} relation from ''{1}''", "Fetching {0} relations from ''{1}''", n, n, baseUrl);
+            break;
+        default:
+            throw new AssertionError();
         }
+        progressMonitor.setTicks(0);
         progressMonitor.setTicksCount(ids.size());
-        progressMonitor.setTicks(0);
+
+        hideSubTaskDetails = Config.getPref().getBoolean("download.multi-fetch.hide-monitor-details", false);
+        if (hideSubTaskDetails)
+            progressMonitor.subTask(msg);
+        else
+            progressMonitor.setCustomText(msg);
         // The complete set containing all primitives to fetch
         Set<Long> toFetch = new HashSet<>(ids);
         // Build a list of fetchers that will  download smaller sets containing only MAX_IDS_PER_REQUEST (200) primitives each.
@@ -319,7 +335,7 @@
         // we will run up to MAX_DOWNLOAD_THREADS concurrent fetchers.
         int threadsNumber = Config.getPref().getInt("osm.download.threads", OsmApi.MAX_DOWNLOAD_THREADS);
         threadsNumber = Utils.clamp(threadsNumber, 1, OsmApi.MAX_DOWNLOAD_THREADS);
-        final ExecutorService exec = Executors.newFixedThreadPool(
+        exec = Executors.newFixedThreadPool(
                 threadsNumber, Utils.newThreadFactory(getClass() + "-%d", Thread.NORM_PRIORITY));
         CompletionService<FetchResult> ecs = new ExecutorCompletionService<>(exec);
         List<Future<FetchResult>> jobs = new ArrayList<>();
@@ -328,14 +344,13 @@
         }
         // Run the fetchers
         for (int i = 0; i < jobs.size() && !isCanceled(); i++) {
-            progressMonitor.subTask(msg + "... " + progressMonitor.getTicks() + '/' + progressMonitor.getTicksCount());
             try {
                 FetchResult result = ecs.take().get();
                 if (result.rc404 != null) {
                     List<Long> toSplit = new ArrayList<>(result.rc404);
-                    int n = toSplit.size() / 2;
-                    jobs.add(ecs.submit(new Fetcher(type, new HashSet<>(toSplit.subList(0, n)), progressMonitor)));
-                    jobs.add(ecs.submit(new Fetcher(type, new HashSet<>(toSplit.subList(n, toSplit.size())), progressMonitor)));
+                    int num = toSplit.size() / 2;
+                    jobs.add(ecs.submit(new Fetcher(type, new HashSet<>(toSplit.subList(0, num)), progressMonitor)));
+                    jobs.add(ecs.submit(new Fetcher(type, new HashSet<>(toSplit.subList(num, toSplit.size())), progressMonitor)));
                 }
                 if (result.missingPrimitives != null) {
                     missingPrimitives.addAll(result.missingPrimitives);
@@ -357,6 +372,7 @@
                 job.cancel(true);
             }
         }
+        exec = null;
     }
 
     /**
@@ -618,11 +634,11 @@
                 throws OsmTransferException {
             String request = buildRequestString(type, pkg);
             FetchResult result = null;
-            try (InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE)) {
+            try (InputStream in = getInputStream(request, progressMonitor.createSubTaskMonitor(1, hideSubTaskDetails))) {
                 if (in == null) return null;
-                progressMonitor.subTask(tr("Downloading OSM data..."));
                 try {
-                    result = new FetchResult(OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(pkg.size(), false)), null);
+                    result = new FetchResult(OsmReader.parseDataSet(in,
+                            progressMonitor.createSubTaskMonitor(pkg.size(), hideSubTaskDetails)), null);
                 } catch (IllegalDataException e) {
                     throw new OsmTransferException(e);
                 }
@@ -634,7 +650,7 @@
         }
 
         /**
-         * invokes a Multi Get for a single id and a given {@link OsmPrimitiveType}.
+         * invokes a Get for a single id and a given {@link OsmPrimitiveType}.
          * The retrieved primitive is merged to {@link #outputDataSet}.
          *
          * @param type The primitive type. Must be one of {@link OsmPrimitiveType#NODE NODE}, {@link OsmPrimitiveType#WAY WAY},
@@ -647,11 +663,10 @@
         protected DataSet singleGetId(OsmPrimitiveType type, long id, ProgressMonitor progressMonitor) throws OsmTransferException {
             String request = buildRequestString(type, Collections.singleton(id));
             DataSet result = null;
-            try (InputStream in = getInputStream(request, NullProgressMonitor.INSTANCE)) {
+            try (InputStream in = getInputStream(request, progressMonitor.createSubTaskMonitor(1, hideSubTaskDetails))) {
                 if (in == null) return null;
-                progressMonitor.subTask(tr("Downloading OSM data..."));
                 try {
-                    result = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, false));
+                    result = OsmReader.parseDataSet(in, progressMonitor.createSubTaskMonitor(1, hideSubTaskDetails));
                 } catch (IllegalDataException e) {
                     throw new OsmTransferException(e);
                 }
@@ -705,4 +720,11 @@
             return result;
         }
     }
+
+    @Override
+    public void cancel() {
+        super.cancel();
+        if (exec != null)
+            exec.shutdownNow();
+    }
 }
