Index: trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- trunk/src/org/openstreetmap/josm/Main.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/Main.java	(revision 12634)
@@ -59,5 +59,4 @@
 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
-import org.openstreetmap.josm.gui.progress.ProgressMonitorExecutor;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.util.RedirectInputMap;
@@ -123,8 +122,9 @@
     /**
      * The worker thread slave. This is for executing all long and intensive
-     * calculations. The executed runnables are guaranteed to be executed separately
-     * and sequential.
-     */
-    public static final ExecutorService worker = new ProgressMonitorExecutor("main-worker-%d", Thread.NORM_PRIORITY);
+     * calculations. The executed runnables are guaranteed to be executed separately and sequential.
+     * @deprecated use {@link MainApplication#worker} instead
+     */
+    @Deprecated
+    public static final ExecutorService worker = MainApplication.worker;
 
     /**
@@ -855,5 +855,4 @@
     protected void shutdown() {
         if (!GraphicsEnvironment.isHeadless()) {
-            worker.shutdown();
             ImageProvider.shutdown(false);
             JCSCacheManager.shutdown();
@@ -867,5 +866,4 @@
         }
         if (!GraphicsEnvironment.isHeadless()) {
-            worker.shutdownNow();
             ImageProvider.shutdown(true);
         }
Index: trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/CloseChangesetAction.java	(revision 12634)
@@ -19,4 +19,5 @@
 import org.openstreetmap.josm.data.osm.UserInfo;
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.io.CloseChangesetDialog;
@@ -57,5 +58,5 @@
     @Override
     public void actionPerformed(ActionEvent e) {
-        Main.worker.submit(new DownloadOpenChangesetsTask());
+        MainApplication.worker.submit(new DownloadOpenChangesetsTask());
     }
 
@@ -80,5 +81,5 @@
         Collection<Changeset> changesetsToClose = dialog.getSelectedChangesets();
         CloseChangesetTask closeChangesetTask = new CloseChangesetTask(changesetsToClose);
-        Main.worker.submit(closeChangesetTask);
+        MainApplication.worker.submit(closeChangesetTask);
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/CreateMultipolygonAction.java	(revision 12634)
@@ -161,7 +161,7 @@
         if (multipolygonRelation != null) {
             if (!multipolygonRelation.isNew() && multipolygonRelation.isIncomplete()) {
-                Main.worker.submit(new DownloadRelationTask(Collections.singleton(multipolygonRelation), Main.getLayerManager().getEditLayer()));
+                MainApplication.worker.submit(new DownloadRelationTask(Collections.singleton(multipolygonRelation), Main.getLayerManager().getEditLayer()));
             } else if (multipolygonRelation.hasIncompleteMembers()) {
-                Main.worker.submit(new DownloadRelationMemberTask(multipolygonRelation,
+                MainApplication.worker.submit(new DownloadRelationMemberTask(multipolygonRelation,
                         DownloadSelectedIncompleteMembersAction.buildSetOfIncompleteMembers(Collections.singleton(multipolygonRelation)),
                         Main.getLayerManager().getEditLayer()));
@@ -169,5 +169,5 @@
         }
         // create/update multipolygon relation
-        Main.worker.submit(new CreateUpdateMultipolygonTask(selectedWays, multipolygonRelation));
+        MainApplication.worker.submit(new CreateUpdateMultipolygonTask(selectedWays, multipolygonRelation));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/DownloadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadAction.java	(revision 12634)
@@ -83,5 +83,5 @@
             task.setZoomAfterDownload(zoom && !dialog.isDownloadGpxData() && !dialog.isDownloadNotes());
             Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);
-            Main.worker.submit(new PostDownloadHandler(task, future));
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
             if (zoom) {
                 tasks.add(new Pair<>(task, future));
@@ -93,5 +93,5 @@
             task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadNotes());
             Future<?> future = task.download(dialog.isNewLayerRequired(), area, null);
-            Main.worker.submit(new PostDownloadHandler(task, future));
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
             if (zoom) {
                 tasks.add(new Pair<>(task, future));
@@ -103,5 +103,5 @@
             task.setZoomAfterDownload(zoom && !dialog.isDownloadOsmData() && !dialog.isDownloadGpxData());
             Future<?> future = task.download(false, area, null);
-            Main.worker.submit(new PostDownloadHandler(task, future));
+            MainApplication.worker.submit(new PostDownloadHandler(task, future));
             if (zoom) {
                 tasks.add(new Pair<>(task, future));
@@ -110,5 +110,5 @@
 
         if (zoom && tasks.size() > 1) {
-            Main.worker.submit(() -> {
+            MainApplication.worker.submit(() -> {
                 ProjectionBounds bounds = null;
                 // Wait for completion of download jobs
Index: trunk/src/org/openstreetmap/josm/actions/DownloadNotesInViewAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadNotesInViewAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadNotesInViewAction.java	(revision 12634)
@@ -48,5 +48,5 @@
         task.setZoomAfterDownload(false);
         Future<?> future = task.download(false, bounds, null);
-        Main.worker.submit(new PostDownloadHandler(task, future));
+        MainApplication.worker.submit(new PostDownloadHandler(task, future));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadOsmInViewAction.java	(revision 12634)
@@ -36,5 +36,5 @@
         task.setZoomAfterDownload(false);
         Future<?> future = task.download(bounds);
-        Main.worker.submit(new PostDownloadHandler(task, future));
+        MainApplication.worker.submit(new PostDownloadHandler(task, future));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadPrimitiveAction.java	(revision 12634)
@@ -11,4 +11,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.PrimitiveId;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.download.DownloadObjectDialog;
 import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
@@ -54,6 +55,6 @@
         final DownloadPrimitivesWithReferrersTask task =
                 new DownloadPrimitivesWithReferrersTask(newLayer, ids, downloadReferrers, full, null, null);
-        Main.worker.submit(task);
-        Main.worker.submit(() -> {
+        MainApplication.worker.submit(task);
+        MainApplication.worker.submit(() -> {
                 final List<PrimitiveId> downloaded = task.getDownloadedId();
                 if (downloaded != null) {
Index: trunk/src/org/openstreetmap/josm/actions/DownloadReferrersAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/DownloadReferrersAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/DownloadReferrersAction.java	(revision 12634)
@@ -12,4 +12,5 @@
 import org.openstreetmap.josm.actions.downloadtasks.DownloadReferrersTask;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.tools.Shortcut;
@@ -45,5 +46,5 @@
         if (children == null || children.isEmpty())
             return;
-        Main.worker.submit(new DownloadReferrersTask(targetLayer, children));
+        MainApplication.worker.submit(new DownloadReferrersTask(targetLayer, children));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/JosmAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/JosmAction.java	(revision 12634)
@@ -18,4 +18,5 @@
 import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
 import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
@@ -273,5 +274,5 @@
 
     protected static void waitFuture(final Future<?> future, final PleaseWaitProgressMonitor monitor) {
-        Main.worker.submit(() -> {
+        MainApplication.worker.submit(() -> {
                         try {
                             future.get();
Index: trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java	(revision 12634)
@@ -13,4 +13,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -52,5 +53,5 @@
             return null;
         final Object actionName = getValue(NAME);
-        return Main.worker.submit(() -> {
+        return MainApplication.worker.submit(() -> {
                 final long start = System.currentTimeMillis();
                 boolean layerMerged = false;
Index: trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/OpenFileAction.java	(revision 12634)
@@ -76,5 +76,5 @@
         OpenFileTask task = new OpenFileTask(Arrays.asList(files), fc.getFileFilter());
         task.setRecordHistory(true);
-        Main.worker.submit(task);
+        MainApplication.worker.submit(task);
     }
 
@@ -105,5 +105,5 @@
         OpenFileTask task = new OpenFileTask(fileList, null);
         task.setRecordHistory(recordHistory);
-        return Main.worker.submit(task);
+        return MainApplication.worker.submit(task);
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/OpenLocationAction.java	(revision 12634)
@@ -41,4 +41,5 @@
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
 import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
@@ -233,5 +234,5 @@
         for (final DownloadTask task : tasks) {
             try {
-                result.add(Main.worker.submit(new PostDownloadHandler(task, task.loadUrl(newLayer, url, monitor))));
+                result.add(MainApplication.worker.submit(new PostDownloadHandler(task, task.loadUrl(newLayer, url, monitor))));
             } catch (IllegalArgumentException e) {
                 Logging.error(e);
Index: trunk/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/OverpassDownloadAction.java	(revision 12634)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
 import org.openstreetmap.josm.gui.download.OverpassQueryList;
@@ -118,5 +119,5 @@
                 new OverpassDownloadReader(area, OverpassServerPreference.getOverpassServer(), overpassQuery),
                 dialog.isNewLayerRequired(), area, null);
-        Main.worker.submit(new PostDownloadHandler(task, future, errorReporter));
+        MainApplication.worker.submit(new PostDownloadHandler(task, future, errorReporter));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/SessionLoadAction.java	(revision 12634)
@@ -61,5 +61,5 @@
         File file = fc.getSelectedFile();
         boolean zip = Utils.hasExtension(file, "joz");
-        Main.worker.submit(new Loader(file, zip));
+        MainApplication.worker.submit(new Loader(file, zip));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/UpdateSelectionAction.java	(revision 12634)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.io.UpdatePrimitivesTask;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
@@ -59,5 +60,5 @@
      */
     public static void updatePrimitives(final Collection<OsmPrimitive> selection) {
-        Main.worker.submit(new UpdatePrimitivesTask(Main.getLayerManager().getEditLayer(), selection));
+        MainApplication.worker.submit(new UpdatePrimitivesTask(Main.getLayerManager().getEditLayer(), selection));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/UploadAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/UploadAction.java	(revision 12634)
@@ -246,5 +246,5 @@
         }
 
-        Main.worker.execute(
+        MainApplication.worker.execute(
                 new UploadPrimitivesTask(
                         UploadDialog.getUploadDialog().getUploadStrategySpecification(),
Index: trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java	(revision 12634)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.data.osm.visitor.Visitor;
 import org.openstreetmap.josm.gui.DefaultNameFormatter;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.io.UploadSelectionDialog;
@@ -160,5 +161,5 @@
             // processPostParentChecker()
             //
-            Main.worker.submit(new DeletedParentsChecker(layer, toUpload));
+            MainApplication.worker.submit(new DeletedParentsChecker(layer, toUpload));
         } else {
             processPostParentChecker(layer, toUpload);
Index: trunk/src/org/openstreetmap/josm/actions/ValidateAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/ValidateAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/ValidateAction.java	(revision 12634)
@@ -91,5 +91,5 @@
         }
 
-        Main.worker.submit(new ValidationTask(tests, selection, lastSelection));
+        MainApplication.worker.submit(new ValidationTask(tests, selection, lastSelection));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/AbstractChangesetDownloadTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/AbstractChangesetDownloadTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/AbstractChangesetDownloadTask.java	(revision 12634)
@@ -11,8 +11,8 @@
 import javax.swing.SwingUtilities;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.ChangesetCache;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -119,5 +119,5 @@
      *       }
      *    }
-     *    Main.worker.submit(runAfterTask);
+     *    MainApplication.worker.submit(runAfterTask);
      * </pre>
      *
@@ -125,10 +125,10 @@
      */
     public final Future<?> download() {
-        return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null;
+        return downloadTaskRunnable != null ? MainApplication.worker.submit(downloadTaskRunnable) : null;
     }
 
     @Override
     public final Future<?> loadUrl(boolean newLayer, String url, ProgressMonitor progressMonitor) {
-        return downloadTaskRunnable != null ? Main.worker.submit(downloadTaskRunnable) : null;
+        return downloadTaskRunnable != null ? MainApplication.worker.submit(downloadTaskRunnable) : null;
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTask.java	(revision 12634)
@@ -75,5 +75,5 @@
         // We need submit instead of execute so we can wait for it to finish and get the error
         // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -99,5 +99,5 @@
             // We need submit instead of execute so we can wait for it to finish and get the error
             // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
-            return Main.worker.submit(downloadTask);
+            return MainApplication.worker.submit(downloadTask);
 
         } else if (url.matches(PATTERN_TRACKPOINTS_BBOX)) {
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTask.java	(revision 12634)
@@ -60,5 +60,5 @@
         final String url = OsmApi.getOsmApi().getBaseUrl() + "notes/" + id;
         downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -66,5 +66,5 @@
     public Future<?> download(boolean newLayer, Bounds downloadArea, ProgressMonitor progressMonitor) {
         downloadTask = new DownloadBoundingBoxTask(new BoundingBoxDownloader(downloadArea), progressMonitor);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -76,5 +76,5 @@
             downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor);
         }
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeCompressedTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeCompressedTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeCompressedTask.java	(revision 12634)
@@ -2,10 +2,10 @@
 package org.openstreetmap.josm.actions.downloadtasks;
 
+import static org.openstreetmap.josm.tools.I18n.tr;
+
 import java.util.concurrent.Future;
 
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.OsmServerLocationReader;
@@ -50,5 +50,5 @@
         // Extract .osc.gz/bz/bz2 filename from URL to set the new layer name
         extractOsmFilename("https?://.*/(.*\\.osc.(gz|bz2?))", url);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 }
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java	(revision 12634)
@@ -77,5 +77,5 @@
         // Extract .osc filename from URL to set the new layer name
         extractOsmFilename("https?://.*/(.*\\.osc)", url);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -123,5 +123,5 @@
                 if (isCanceled()) return;
                 // Let's load all required history
-                Main.worker.submit(new HistoryLoaderAndListener(toLoad));
+                MainApplication.worker.submit(new HistoryLoaderAndListener(toLoad));
             } catch (RejectedExecutionException e) {
                 rememberException(e);
@@ -200,5 +200,5 @@
                 // Some primitives still need to be loaded
                 // Let's load all required history
-                Main.worker.submit(new HistoryLoaderAndListener(toLoadNext));
+                MainApplication.worker.submit(new HistoryLoaderAndListener(toLoadNext));
             }
         }
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmCompressedTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmCompressedTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmCompressedTask.java	(revision 12634)
@@ -6,7 +6,7 @@
 import java.util.concurrent.Future;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.OsmServerLocationReader;
@@ -61,5 +61,5 @@
         // Extract .osm.gz/bz/bz2/zip filename from URL to set the new layer name
         extractOsmFilename("https?://.*/(.*\\.osm.(gz|bz2?|zip))", url);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 }
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmIdTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmIdTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmIdTask.java	(revision 12634)
@@ -9,8 +9,8 @@
 import java.util.regex.Pattern;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
@@ -40,5 +40,5 @@
             final DownloadPrimitivesWithReferrersTask downloadTask = new DownloadPrimitivesWithReferrersTask(
                     newLayer, Collections.singletonList(primitiveId), true, true, null, null);
-            return Main.worker.submit(downloadTask);
+            return MainApplication.worker.submit(downloadTask);
         } else {
             throw new IllegalStateException("Failed to parse id from " + url);
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTask.java	(revision 12634)
@@ -116,5 +116,5 @@
      *       }
      *    }
-     *    Main.worker.submit(runAfterTask);
+     *    MainApplication.worker.submit(runAfterTask);
      * </pre>
      * @param reader the reader used to parse OSM data (see {@link OsmServerReader#parseOsm})
@@ -134,5 +134,5 @@
         // We need submit instead of execute so we can wait for it to finish and get the error
         // message if necessary. If no one calls getErrorMessage() it just behaves like execute.
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -160,5 +160,5 @@
         // Extract .osm filename from URL to set the new layer name
         extractOsmFilename("https?://.*/(.*\\.osm)", newUrl);
-        return Main.worker.submit(downloadTask);
+        return MainApplication.worker.submit(downloadTask);
     }
 
@@ -287,5 +287,5 @@
                 }
                 if (!primitivesToUpdate.isEmpty()) {
-                    Main.worker.submit(new UpdatePrimitivesTask(layer, primitivesToUpdate));
+                    MainApplication.worker.submit(new UpdatePrimitivesTask(layer, primitivesToUpdate));
                 }
                 layer.onPostDownloadFromServer();
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadSessionTask.java	(revision 12634)
@@ -12,4 +12,5 @@
 import org.openstreetmap.josm.actions.SessionLoadAction.Loader;
 import org.openstreetmap.josm.data.Bounds;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.tools.HttpClient;
@@ -47,5 +48,5 @@
                 URL u = new URL(url);
                 loader = new Loader(HttpClient.create(u).connect().getContent(), u.toURI(), url.endsWith(".joz"));
-                return Main.worker.submit(loader);
+                return MainApplication.worker.submit(loader);
             } catch (URISyntaxException | IOException e) {
                 Logging.error(e);
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTask.java	(revision 12634)
@@ -43,5 +43,5 @@
      *       }
      *    }
-     *    Main.worker.submit(runAfterTask);
+     *    MainApplication.worker.submit(runAfterTask);
      * </pre>
      *
Index: trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/downloadtasks/DownloadTaskList.java	(revision 12634)
@@ -29,4 +29,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.Notification;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -93,5 +94,5 @@
             }
         });
-        return Main.worker.submit(new PostDownloadProcessor(osmData));
+        return MainApplication.worker.submit(new PostDownloadProcessor(osmData));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java	(revision 12634)
@@ -603,5 +603,5 @@
                         // We need to do it like this as otherwise drawAction will see a double
                         // click and switch back to SelectMode
-                        Main.worker.execute(() -> map.selectDrawTool(true));
+                        MainApplication.worker.execute(() -> map.selectDrawTool(true));
                         return;
                     }
Index: trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/relation/DownloadMembersAction.java	(revision 12634)
@@ -35,5 +35,5 @@
     public void actionPerformed(ActionEvent e) {
         if (!isEnabled() || relations.isEmpty() || !MainApplication.isDisplayingMapView()) return;
-        Main.worker.submit(new DownloadRelationTask(relations, Main.getLayerManager().getEditLayer()));
+        MainApplication.worker.submit(new DownloadRelationTask(relations, Main.getLayerManager().getEditLayer()));
     }
 
Index: trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/relation/DownloadSelectedIncompleteMembersAction.java	(revision 12634)
@@ -51,5 +51,5 @@
     public void actionPerformed(ActionEvent e) {
         if (!isEnabled() || relations.isEmpty() || !MainApplication.isDisplayingMapView()) return;
-        Main.worker.submit(new DownloadRelationMemberTask(
+        MainApplication.worker.submit(new DownloadRelationMemberTask(
                 relations,
                 incompleteMembers,
Index: trunk/src/org/openstreetmap/josm/actions/upload/UploadNotesTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/upload/UploadNotesTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/actions/upload/UploadNotesTask.java	(revision 12634)
@@ -37,5 +37,5 @@
     public void uploadNotes(NoteData noteData, ProgressMonitor progressMonitor) {
         this.noteData = noteData;
-        Main.worker.submit(new UploadTask(tr("Uploading modified notes"), progressMonitor));
+        MainApplication.worker.submit(new UploadTask(tr("Uploading modified notes"), progressMonitor));
     }
 
Index: trunk/src/org/openstreetmap/josm/data/AutosaveTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/AutosaveTask.java	(revision 12633)
+++ 	(revision )
@@ -1,430 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data;
-
-import static org.openstreetmap.josm.tools.I18n.marktr;
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.management.ManagementFactory;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.regex.Pattern;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.actions.OpenFileAction.OpenFileTask;
-import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
-import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter;
-import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter.Listener;
-import org.openstreetmap.josm.data.preferences.BooleanProperty;
-import org.openstreetmap.josm.data.preferences.IntegerProperty;
-import org.openstreetmap.josm.gui.Notification;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
-import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
-import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.OsmExporter;
-import org.openstreetmap.josm.io.OsmImporter;
-import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
-
-/**
- * Saves data layers periodically so they can be recovered in case of a crash.
- *
- * There are 2 directories
- *  - autosave dir: copies of the currently open data layers are saved here every
- *      PROP_INTERVAL seconds. When a data layer is closed normally, the corresponding
- *      files are removed. If this dir is non-empty on start, JOSM assumes
- *      that it crashed last time.
- *  - deleted layers dir: "secondary archive" - when autosaved layers are restored
- *      they are copied to this directory. We cannot keep them in the autosave folder,
- *      but just deleting it would be dangerous: Maybe a feature inside the file
- *      caused JOSM to crash. If the data is valuable, the user can still try to
- *      open with another versions of JOSM or fix the problem manually.
- *
- *      The deleted layers dir keeps at most PROP_DELETED_LAYERS files.
- *
- * @since  3378 (creation)
- * @since 10386 (new LayerChangeListener interface)
- */
-public class AutosaveTask extends TimerTask implements LayerChangeListener, Listener {
-
-    private static final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'};
-    private static final String AUTOSAVE_DIR = "autosave";
-    private static final String DELETED_LAYERS_DIR = "autosave/deleted_layers";
-
-    /**
-     * If autosave is enabled
-     */
-    public static final BooleanProperty PROP_AUTOSAVE_ENABLED = new BooleanProperty("autosave.enabled", true);
-    /**
-     * The number of files to store per layer
-     */
-    public static final IntegerProperty PROP_FILES_PER_LAYER = new IntegerProperty("autosave.filesPerLayer", 1);
-    /**
-     * How many deleted layers should be stored
-     */
-    public static final IntegerProperty PROP_DELETED_LAYERS = new IntegerProperty("autosave.deletedLayersBackupCount", 5);
-    /**
-     * The autosave interval, in seconds
-     */
-    public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("autosave.interval", (int) TimeUnit.MINUTES.toSeconds(5));
-    /**
-     * The maximum number of autosave files to store
-     */
-    public static final IntegerProperty PROP_INDEX_LIMIT = new IntegerProperty("autosave.index-limit", 1000);
-    /**
-     * Defines if a notification should be displayed after each autosave
-     */
-    public static final BooleanProperty PROP_NOTIFICATION = new BooleanProperty("autosave.notification", false);
-
-    protected static final class AutosaveLayerInfo {
-        private final OsmDataLayer layer;
-        private String layerName;
-        private String layerFileName;
-        private final Deque<File> backupFiles = new LinkedList<>();
-
-        AutosaveLayerInfo(OsmDataLayer layer) {
-            this.layer = layer;
-        }
-    }
-
-    private final DataSetListenerAdapter datasetAdapter = new DataSetListenerAdapter(this);
-    private final Set<DataSet> changedDatasets = new HashSet<>();
-    private final List<AutosaveLayerInfo> layersInfo = new ArrayList<>();
-    private final Object layersLock = new Object();
-    private final Deque<File> deletedLayers = new LinkedList<>();
-
-    private final File autosaveDir = new File(Main.pref.getUserDataDirectory(), AUTOSAVE_DIR);
-    private final File deletedLayersDir = new File(Main.pref.getUserDataDirectory(), DELETED_LAYERS_DIR);
-
-    /**
-     * Replies the autosave directory.
-     * @return the autosave directory
-     * @since 10299
-     */
-    public final Path getAutosaveDir() {
-        return autosaveDir.toPath();
-    }
-
-    /**
-     * Starts the autosave background task.
-     */
-    public void schedule() {
-        if (PROP_INTERVAL.get() > 0) {
-
-            if (!autosaveDir.exists() && !autosaveDir.mkdirs()) {
-                Logging.warn(tr("Unable to create directory {0}, autosave will be disabled", autosaveDir.getAbsolutePath()));
-                return;
-            }
-            if (!deletedLayersDir.exists() && !deletedLayersDir.mkdirs()) {
-                Logging.warn(tr("Unable to create directory {0}, autosave will be disabled", deletedLayersDir.getAbsolutePath()));
-                return;
-            }
-
-            File[] files = deletedLayersDir.listFiles();
-            if (files != null) {
-                for (File f: files) {
-                    deletedLayers.add(f); // FIXME: sort by mtime
-                }
-            }
-
-            new Timer(true).schedule(this, TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(PROP_INTERVAL.get()));
-            Main.getLayerManager().addAndFireLayerChangeListener(this);
-        }
-    }
-
-    private static String getFileName(String layerName, int index) {
-        String result = layerName;
-        for (char illegalCharacter : ILLEGAL_CHARACTERS) {
-            result = result.replaceAll(Pattern.quote(String.valueOf(illegalCharacter)),
-                    '&' + String.valueOf((int) illegalCharacter) + ';');
-        }
-        if (index != 0) {
-            result = result + '_' + index;
-        }
-        return result;
-    }
-
-    private void setLayerFileName(AutosaveLayerInfo layer) {
-        int index = 0;
-        while (true) {
-            String filename = getFileName(layer.layer.getName(), index);
-            boolean foundTheSame = false;
-            for (AutosaveLayerInfo info: layersInfo) {
-                if (info != layer && filename.equals(info.layerFileName)) {
-                    foundTheSame = true;
-                    break;
-                }
-            }
-
-            if (!foundTheSame) {
-                layer.layerFileName = filename;
-                return;
-            }
-
-            index++;
-        }
-    }
-
-    protected File getNewLayerFile(AutosaveLayerInfo layer, Date now, int startIndex) {
-        int index = startIndex;
-        while (true) {
-            String filename = String.format("%1$s_%2$tY%2$tm%2$td_%2$tH%2$tM%2$tS%2$tL%3$s",
-                    layer.layerFileName, now, index == 0 ? "" : ('_' + Integer.toString(index)));
-            File result = new File(autosaveDir, filename + '.' + Main.pref.get("autosave.extension", "osm"));
-            try {
-                if (index > PROP_INDEX_LIMIT.get())
-                    throw new IOException("index limit exceeded");
-                if (result.createNewFile()) {
-                    createNewPidFile(autosaveDir, filename);
-                    return result;
-                } else {
-                    Logging.warn(tr("Unable to create file {0}, other filename will be used", result.getAbsolutePath()));
-                }
-            } catch (IOException e) {
-                Logging.log(Logging.LEVEL_ERROR, tr("IOError while creating file, autosave will be skipped: {0}", e.getMessage()), e);
-                return null;
-            }
-            index++;
-        }
-    }
-
-    private static void createNewPidFile(File autosaveDir, String filename) {
-        File pidFile = new File(autosaveDir, filename+".pid");
-        try (PrintStream ps = new PrintStream(pidFile, "UTF-8")) {
-            ps.println(ManagementFactory.getRuntimeMXBean().getName());
-        } catch (IOException | SecurityException t) {
-            Logging.error(t);
-        }
-    }
-
-    private void savelayer(AutosaveLayerInfo info) {
-        if (!info.layer.getName().equals(info.layerName)) {
-            setLayerFileName(info);
-            info.layerName = info.layer.getName();
-        }
-        if (changedDatasets.remove(info.layer.data)) {
-            File file = getNewLayerFile(info, new Date(), 0);
-            if (file != null) {
-                info.backupFiles.add(file);
-                new OsmExporter().exportData(file, info.layer, true /* no backup with appended ~ */);
-            }
-        }
-        while (info.backupFiles.size() > PROP_FILES_PER_LAYER.get()) {
-            File oldFile = info.backupFiles.remove();
-            if (Utils.deleteFile(oldFile, marktr("Unable to delete old backup file {0}"))) {
-                Utils.deleteFile(getPidFile(oldFile), marktr("Unable to delete old backup file {0}"));
-            }
-        }
-    }
-
-    @Override
-    public void run() {
-        synchronized (layersLock) {
-            try {
-                for (AutosaveLayerInfo info: layersInfo) {
-                    savelayer(info);
-                }
-                changedDatasets.clear();
-                if (PROP_NOTIFICATION.get() && !layersInfo.isEmpty()) {
-                    GuiHelper.runInEDT(this::displayNotification);
-                }
-            } catch (RuntimeException t) { // NOPMD
-                // Don't let exception stop time thread
-                Logging.error("Autosave failed:");
-                Logging.error(t);
-            }
-        }
-    }
-
-    protected void displayNotification() {
-        new Notification(tr("Your work has been saved automatically."))
-        .setDuration(Notification.TIME_SHORT)
-        .show();
-    }
-
-    @Override
-    public void layerOrderChanged(LayerOrderChangeEvent e) {
-        // Do nothing
-    }
-
-    private void registerNewlayer(OsmDataLayer layer) {
-        synchronized (layersLock) {
-            layer.data.addDataSetListener(datasetAdapter);
-            layersInfo.add(new AutosaveLayerInfo(layer));
-        }
-    }
-
-    @Override
-    public void layerAdded(LayerAddEvent e) {
-        if (e.getAddedLayer() instanceof OsmDataLayer) {
-            registerNewlayer((OsmDataLayer) e.getAddedLayer());
-        }
-    }
-
-    @Override
-    public void layerRemoving(LayerRemoveEvent e) {
-        if (e.getRemovedLayer() instanceof OsmDataLayer) {
-            synchronized (layersLock) {
-                OsmDataLayer osmLayer = (OsmDataLayer) e.getRemovedLayer();
-                osmLayer.data.removeDataSetListener(datasetAdapter);
-                Iterator<AutosaveLayerInfo> it = layersInfo.iterator();
-                while (it.hasNext()) {
-                    AutosaveLayerInfo info = it.next();
-                    if (info.layer == osmLayer) {
-
-                        savelayer(info);
-                        File lastFile = info.backupFiles.pollLast();
-                        if (lastFile != null) {
-                            moveToDeletedLayersFolder(lastFile);
-                        }
-                        for (File file: info.backupFiles) {
-                            if (Utils.deleteFile(file)) {
-                                Utils.deleteFile(getPidFile(file));
-                            }
-                        }
-
-                        it.remove();
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public void processDatasetEvent(AbstractDatasetChangedEvent event) {
-        changedDatasets.add(event.getDataset());
-    }
-
-    protected File getPidFile(File osmFile) {
-        return new File(autosaveDir, osmFile.getName().replaceFirst("[.][^.]+$", ".pid"));
-    }
-
-    /**
-     * Replies the list of .osm files still present in autosave dir, that are not currently managed by another instance of JOSM.
-     * These files are hence unsaved layers from an old instance of JOSM that crashed and may be recovered by this instance.
-     * @return The list of .osm files still present in autosave dir, that are not currently managed by another instance of JOSM
-     */
-    public List<File> getUnsavedLayersFiles() {
-        List<File> result = new ArrayList<>();
-        File[] files = autosaveDir.listFiles(OsmImporter.FILE_FILTER);
-        if (files == null)
-            return result;
-        for (File file: files) {
-            if (file.isFile()) {
-                boolean skipFile = false;
-                File pidFile = getPidFile(file);
-                if (pidFile.exists()) {
-                    try (BufferedReader reader = Files.newBufferedReader(pidFile.toPath(), StandardCharsets.UTF_8)) {
-                        String jvmId = reader.readLine();
-                        if (jvmId != null) {
-                            String pid = jvmId.split("@")[0];
-                            skipFile = jvmPerfDataFileExists(pid);
-                        }
-                    } catch (IOException | SecurityException t) {
-                        Logging.error(t);
-                    }
-                }
-                if (!skipFile) {
-                    result.add(file);
-                }
-            }
-        }
-        return result;
-    }
-
-    private static boolean jvmPerfDataFileExists(final String jvmId) {
-        File jvmDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "hsperfdata_" + System.getProperty("user.name"));
-        if (jvmDir.exists() && jvmDir.canRead()) {
-            File[] files = jvmDir.listFiles((FileFilter) file -> file.getName().equals(jvmId) && file.isFile());
-            return files != null && files.length == 1;
-        }
-        return false;
-    }
-
-    /**
-     * Recover the unsaved layers and open them asynchronously.
-     * @return A future that can be used to wait for the completion of this task.
-     */
-    public Future<?> recoverUnsavedLayers() {
-        List<File> files = getUnsavedLayersFiles();
-        final OpenFileTask openFileTsk = new OpenFileTask(files, null, tr("Restoring files"));
-        final Future<?> openFilesFuture = Main.worker.submit(openFileTsk);
-        return Main.worker.submit(() -> {
-            try {
-                // Wait for opened tasks to be generated.
-                openFilesFuture.get();
-                for (File f: openFileTsk.getSuccessfullyOpenedFiles()) {
-                    moveToDeletedLayersFolder(f);
-                }
-            } catch (InterruptedException | ExecutionException e) {
-                Logging.error(e);
-            }
-        });
-    }
-
-    /**
-     * Move file to the deleted layers directory.
-     * If moving does not work, it will try to delete the file directly.
-     * Afterwards, if the number of deleted layers gets larger than PROP_DELETED_LAYERS,
-     * some files in the deleted layers directory will be removed.
-     *
-     * @param f the file, usually from the autosave dir
-     */
-    private void moveToDeletedLayersFolder(File f) {
-        File backupFile = new File(deletedLayersDir, f.getName());
-        File pidFile = getPidFile(f);
-
-        if (backupFile.exists()) {
-            deletedLayers.remove(backupFile);
-            Utils.deleteFile(backupFile, marktr("Unable to delete old backup file {0}"));
-        }
-        if (f.renameTo(backupFile)) {
-            deletedLayers.add(backupFile);
-            Utils.deleteFile(pidFile);
-        } else {
-            Logging.warn(String.format("Could not move autosaved file %s to %s folder", f.getName(), deletedLayersDir.getName()));
-            // we cannot move to deleted folder, so just try to delete it directly
-            if (Utils.deleteFile(f, marktr("Unable to delete backup file {0}"))) {
-                Utils.deleteFile(pidFile, marktr("Unable to delete PID file {0}"));
-            }
-        }
-        while (deletedLayers.size() > PROP_DELETED_LAYERS.get()) {
-            File next = deletedLayers.remove();
-            if (next == null) {
-                break;
-            }
-            Utils.deleteFile(next, marktr("Unable to delete archived backup file {0}"));
-        }
-    }
-
-    /**
-     * Mark all unsaved layers as deleted. They are still preserved in the deleted layers folder.
-     */
-    public void discardUnsavedLayers() {
-        for (File f: getUnsavedLayersFiles()) {
-            moveToDeletedLayersFolder(f);
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/data/CustomConfigurator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/CustomConfigurator.java	(revision 12633)
+++ 	(revision )
@@ -1,1164 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.data;
-
-import static org.openstreetmap.josm.tools.I18n.tr;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.CharArrayReader;
-import java.io.CharArrayWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import javax.swing.JOptionPane;
-import javax.swing.SwingUtilities;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.preferences.ListListSetting;
-import org.openstreetmap.josm.data.preferences.ListSetting;
-import org.openstreetmap.josm.data.preferences.MapListSetting;
-import org.openstreetmap.josm.data.preferences.Setting;
-import org.openstreetmap.josm.data.preferences.StringSetting;
-import org.openstreetmap.josm.gui.io.DownloadFileTask;
-import org.openstreetmap.josm.plugins.PluginDownloadTask;
-import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
-import org.openstreetmap.josm.tools.LanguageInfo;
-import org.openstreetmap.josm.tools.Logging;
-import org.openstreetmap.josm.tools.Utils;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-import org.xml.sax.SAXException;
-
-/**
- * Class to process configuration changes stored in XML
- * can be used to modify preferences, store/delete files in .josm folders etc
- */
-public final class CustomConfigurator {
-
-    private static StringBuilder summary = new StringBuilder();
-
-    private CustomConfigurator() {
-        // Hide default constructor for utils classes
-    }
-
-    /**
-     * Log a formatted message.
-     * @param fmt format
-     * @param vars arguments
-     * @see String#format
-     */
-    public static void log(String fmt, Object... vars) {
-        summary.append(String.format(fmt, vars));
-    }
-
-    /**
-     * Log a message.
-     * @param s message to log
-     */
-    public static void log(String s) {
-        summary.append(s).append('\n');
-    }
-
-    /**
-     * Log an exception.
-     * @param e exception to log
-     * @param s message prefix
-     * @since 10469
-     */
-    public static void log(Exception e, String s) {
-        summary.append(s).append(' ').append(Logging.getErrorMessage(e)).append('\n');
-    }
-
-    /**
-     * Returns the log.
-     * @return the log
-     */
-    public static String getLog() {
-        return summary.toString();
-    }
-
-    /**
-     * Resets the log.
-     */
-    public static void resetLog() {
-        summary = new StringBuilder();
-    }
-
-    /**
-     * Read configuration script from XML file, modifying main preferences
-     * @param dir - directory
-     * @param fileName - XML file name
-     */
-    public static void readXML(String dir, String fileName) {
-        readXML(new File(dir, fileName));
-    }
-
-    /**
-     * Read configuration script from XML file, modifying given preferences object
-     * @param file - file to open for reading XML
-     * @param prefs - arbitrary Preferences object to modify by script
-     */
-    public static void readXML(final File file, final Preferences prefs) {
-        synchronized (CustomConfigurator.class) {
-            busy = true;
-        }
-        new XMLCommandProcessor(prefs).openAndReadXML(file);
-        synchronized (CustomConfigurator.class) {
-            CustomConfigurator.class.notifyAll();
-            busy = false;
-        }
-    }
-
-    /**
-     * Read configuration script from XML file, modifying main preferences
-     * @param file - file to open for reading XML
-     */
-    public static void readXML(File file) {
-        readXML(file, Main.pref);
-    }
-
-    /**
-     * Downloads file to one of JOSM standard folders
-     * @param address - URL to download
-     * @param path - file path relative to base where to put downloaded file
-     * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
-     */
-    public static void downloadFile(String address, String path, String base) {
-        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, false);
-    }
-
-    /**
-     * Downloads file to one of JOSM standard folders and unpack it as ZIP/JAR file
-     * @param address - URL to download
-     * @param path - file path relative to base where to put downloaded file
-     * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
-     */
-    public static void downloadAndUnpackFile(String address, String path, String base) {
-        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, true);
-    }
-
-    /**
-     * Downloads file to arbitrary folder
-     * @param address - URL to download
-     * @param path - file path relative to parentDir where to put downloaded file
-     * @param parentDir - folder where to put file
-     * @param mkdir - if true, non-existing directories will be created
-     * @param unzip - if true file wil be unzipped and deleted after download
-     */
-    public static void processDownloadOperation(String address, String path, String parentDir, boolean mkdir, boolean unzip) {
-        String dir = parentDir;
-        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
-            return; // some basic protection
-        }
-        File fOut = new File(dir, path);
-        DownloadFileTask downloadFileTask = new DownloadFileTask(Main.parent, address, fOut, mkdir, unzip);
-
-        Main.worker.submit(downloadFileTask);
-        log("Info: downloading file from %s to %s in background ", parentDir, fOut.getAbsolutePath());
-        if (unzip) log("and unpacking it"); else log("");
-
-    }
-
-    /**
-     * Simple function to show messageBox, may be used from JS API and from other code
-     * @param type - 'i','w','e','q','p' for Information, Warning, Error, Question, Message
-     * @param text - message to display, HTML allowed
-     */
-    public static void messageBox(String type, String text) {
-        char c = (type == null || type.isEmpty() ? "plain" : type).charAt(0);
-        switch (c) {
-            case 'i': JOptionPane.showMessageDialog(Main.parent, text, tr("Information"), JOptionPane.INFORMATION_MESSAGE); break;
-            case 'w': JOptionPane.showMessageDialog(Main.parent, text, tr("Warning"), JOptionPane.WARNING_MESSAGE); break;
-            case 'e': JOptionPane.showMessageDialog(Main.parent, text, tr("Error"), JOptionPane.ERROR_MESSAGE); break;
-            case 'q': JOptionPane.showMessageDialog(Main.parent, text, tr("Question"), JOptionPane.QUESTION_MESSAGE); break;
-            case 'p': JOptionPane.showMessageDialog(Main.parent, text, tr("Message"), JOptionPane.PLAIN_MESSAGE); break;
-            default: Logging.warn("Unsupported messageBox type: " + c);
-        }
-    }
-
-    /**
-     * Simple function for choose window, may be used from JS API and from other code
-     * @param text - message to show, HTML allowed
-     * @param opts -
-     * @return number of pressed button, -1 if cancelled
-     */
-    public static int askForOption(String text, String opts) {
-        if (!opts.isEmpty()) {
-            return JOptionPane.showOptionDialog(Main.parent, text, "Question",
-                    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, opts.split(";"), 0);
-        } else {
-            return JOptionPane.showOptionDialog(Main.parent, text, "Question",
-                    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, 2);
-        }
-    }
-
-    public static String askForText(String text) {
-        String s = JOptionPane.showInputDialog(Main.parent, text, tr("Enter text"), JOptionPane.QUESTION_MESSAGE);
-        return s != null ? s.trim() : null;
-    }
-
-    /**
-     * This function exports part of user preferences to specified file.
-     * Default values are not saved.
-     * @param filename - where to export
-     * @param append - if true, resulting file cause appending to exuisting preferences
-     * @param keys - which preferences keys you need to export ("imagery.entries", for example)
-     */
-    public static void exportPreferencesKeysToFile(String filename, boolean append, String... keys) {
-        Set<String> keySet = new HashSet<>();
-        Collections.addAll(keySet, keys);
-        exportPreferencesKeysToFile(filename, append, keySet);
-    }
-
-    /**
-     * This function exports part of user preferences to specified file.
-     * Default values are not saved.
-     * Preference keys matching specified pattern are saved
-     * @param fileName - where to export
-     * @param append - if true, resulting file cause appending to exuisting preferences
-     * @param pattern - Regexp pattern forh preferences keys you need to export (".*imagery.*", for example)
-     */
-    public static void exportPreferencesKeysByPatternToFile(String fileName, boolean append, String pattern) {
-        List<String> keySet = new ArrayList<>();
-        Map<String, Setting<?>> allSettings = Main.pref.getAllSettings();
-        for (String key: allSettings.keySet()) {
-            if (key.matches(pattern))
-                keySet.add(key);
-        }
-        exportPreferencesKeysToFile(fileName, append, keySet);
-    }
-
-    /**
-     * Export specified preferences keys to configuration file
-     * @param filename - name of file
-     * @param append - will the preferences be appended to existing ones when file is imported later.
-     * Elsewhere preferences from file will replace existing keys.
-     * @param keys - collection of preferences key names to save
-     */
-    public static void exportPreferencesKeysToFile(String filename, boolean append, Collection<String> keys) {
-        Element root = null;
-        Document document = null;
-        Document exportDocument = null;
-
-        try {
-            String toXML = Main.pref.toXML(true);
-            DocumentBuilder builder = Utils.newSafeDOMBuilder();
-            document = builder.parse(new ByteArrayInputStream(toXML.getBytes(StandardCharsets.UTF_8)));
-            exportDocument = builder.newDocument();
-            root = document.getDocumentElement();
-        } catch (SAXException | IOException | ParserConfigurationException ex) {
-            Logging.log(Logging.LEVEL_WARN, "Error getting preferences to save:", ex);
-        }
-        if (root == null || exportDocument == null)
-            return;
-        try {
-            Element newRoot = exportDocument.createElement("config");
-            exportDocument.appendChild(newRoot);
-
-            Element prefElem = exportDocument.createElement("preferences");
-            prefElem.setAttribute("operation", append ? "append" : "replace");
-            newRoot.appendChild(prefElem);
-
-            NodeList childNodes = root.getChildNodes();
-            int n = childNodes.getLength();
-            for (int i = 0; i < n; i++) {
-                Node item = childNodes.item(i);
-                if (item.getNodeType() == Node.ELEMENT_NODE) {
-                    String currentKey = ((Element) item).getAttribute("key");
-                    if (keys.contains(currentKey)) {
-                        Node imported = exportDocument.importNode(item, true);
-                        prefElem.appendChild(imported);
-                    }
-                }
-            }
-            File f = new File(filename);
-            Transformer ts = TransformerFactory.newInstance().newTransformer();
-            ts.setOutputProperty(OutputKeys.INDENT, "yes");
-            ts.transform(new DOMSource(exportDocument), new StreamResult(f.toURI().getPath()));
-        } catch (DOMException | TransformerFactoryConfigurationError | TransformerException ex) {
-            Logging.warn("Error saving preferences part:");
-            Logging.error(ex);
-        }
-    }
-
-    public static void deleteFile(String path, String base) {
-        String dir = getDirectoryByAbbr(base);
-        if (dir == null) {
-            log("Error: Can not find base, use base=cache, base=prefs or base=plugins attribute.");
-            return;
-        }
-        log("Delete file: %s\n", path);
-        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
-            return; // some basic protection
-        }
-        File fOut = new File(dir, path);
-        if (fOut.exists()) {
-            deleteFileOrDirectory(fOut);
-        }
-    }
-
-    public static void deleteFileOrDirectory(File f) {
-        if (f.isDirectory()) {
-            File[] files = f.listFiles();
-            if (files != null) {
-                for (File f1: files) {
-                    deleteFileOrDirectory(f1);
-                }
-            }
-        }
-        if (!Utils.deleteFile(f)) {
-            log("Warning: Can not delete file "+f.getPath());
-        }
-    }
-
-    private static boolean busy;
-
-    public static void pluginOperation(String install, String uninstall, String delete) {
-        final List<String> installList = new ArrayList<>();
-        final List<String> removeList = new ArrayList<>();
-        final List<String> deleteList = new ArrayList<>();
-        Collections.addAll(installList, install.toLowerCase(Locale.ENGLISH).split(";"));
-        Collections.addAll(removeList, uninstall.toLowerCase(Locale.ENGLISH).split(";"));
-        Collections.addAll(deleteList, delete.toLowerCase(Locale.ENGLISH).split(";"));
-        installList.remove("");
-        removeList.remove("");
-        deleteList.remove("");
-
-        if (!installList.isEmpty()) {
-            log("Plugins install: "+installList);
-        }
-        if (!removeList.isEmpty()) {
-            log("Plugins turn off: "+removeList);
-        }
-        if (!deleteList.isEmpty()) {
-            log("Plugins delete: "+deleteList);
-        }
-
-        final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
-        Runnable r = () -> {
-            if (task.isCanceled()) return;
-            synchronized (CustomConfigurator.class) {
-                try { // proceed only after all other tasks were finished
-                    while (busy) CustomConfigurator.class.wait();
-                } catch (InterruptedException ex) {
-                    Logging.log(Logging.LEVEL_WARN, "InterruptedException while reading local plugin information", ex);
-                    Thread.currentThread().interrupt();
-                }
-
-                SwingUtilities.invokeLater(() -> {
-                    List<PluginInformation> availablePlugins = task.getAvailablePlugins();
-                    List<PluginInformation> toInstallPlugins = new ArrayList<>();
-                    List<PluginInformation> toRemovePlugins = new ArrayList<>();
-                    List<PluginInformation> toDeletePlugins = new ArrayList<>();
-                    for (PluginInformation pi1: availablePlugins) {
-                        String name = pi1.name.toLowerCase(Locale.ENGLISH);
-                        if (installList.contains(name)) toInstallPlugins.add(pi1);
-                        if (removeList.contains(name)) toRemovePlugins.add(pi1);
-                        if (deleteList.contains(name)) toDeletePlugins.add(pi1);
-                    }
-                    if (!installList.isEmpty()) {
-                        PluginDownloadTask pluginDownloadTask =
-                                new PluginDownloadTask(Main.parent, toInstallPlugins, tr("Installing plugins"));
-                        Main.worker.submit(pluginDownloadTask);
-                    }
-                    Collection<String> pls = new ArrayList<>(Main.pref.getCollection("plugins"));
-                    for (PluginInformation pi2: toInstallPlugins) {
-                        if (!pls.contains(pi2.name)) {
-                            pls.add(pi2.name);
-                        }
-                    }
-                    for (PluginInformation pi3: toRemovePlugins) {
-                        pls.remove(pi3.name);
-                    }
-                    for (PluginInformation pi4: toDeletePlugins) {
-                        pls.remove(pi4.name);
-                        new File(Main.pref.getPluginsDirectory(), pi4.name+".jar").deleteOnExit();
-                    }
-                    Main.pref.putCollection("plugins", pls);
-                });
-            }
-        };
-        Main.worker.submit(task);
-        Main.worker.submit(r);
-    }
-
-    private static String getDirectoryByAbbr(String base) {
-        String dir;
-        if ("prefs".equals(base) || base.isEmpty()) {
-            dir = Main.pref.getPreferencesDirectory().getAbsolutePath();
-        } else if ("cache".equals(base)) {
-            dir = Main.pref.getCacheDirectory().getAbsolutePath();
-        } else if ("plugins".equals(base)) {
-            dir = Main.pref.getPluginsDirectory().getAbsolutePath();
-        } else {
-            dir = null;
-        }
-        return dir;
-    }
-
-    public static Preferences clonePreferences(Preferences pref) {
-        Preferences tmp = new Preferences();
-        tmp.settingsMap.putAll(pref.settingsMap);
-        tmp.defaultsMap.putAll(pref.defaultsMap);
-        tmp.colornames.putAll(pref.colornames);
-
-        return tmp;
-    }
-
-    public static class XMLCommandProcessor {
-
-        private Preferences mainPrefs;
-        private final Map<String, Element> tasksMap = new HashMap<>();
-
-        private boolean lastV; // last If condition result
-
-        private ScriptEngine engine;
-
-        public void openAndReadXML(File file) {
-            log("-- Reading custom preferences from " + file.getAbsolutePath() + " --");
-            try {
-                String fileDir = file.getParentFile().getAbsolutePath();
-                if (fileDir != null) engine.eval("scriptDir='"+normalizeDirName(fileDir) +"';");
-                try (InputStream is = new BufferedInputStream(new FileInputStream(file))) {
-                    openAndReadXML(is);
-                }
-            } catch (ScriptException | IOException | SecurityException ex) {
-                log(ex, "Error reading custom preferences:");
-            }
-        }
-
-        public void openAndReadXML(InputStream is) {
-            try {
-                Document document = Utils.parseSafeDOM(is);
-                synchronized (CustomConfigurator.class) {
-                    processXML(document);
-                }
-            } catch (SAXException | IOException | ParserConfigurationException ex) {
-                log(ex, "Error reading custom preferences:");
-            }
-            log("-- Reading complete --");
-        }
-
-        public XMLCommandProcessor(Preferences mainPrefs) {
-            try {
-                this.mainPrefs = mainPrefs;
-                resetLog();
-                engine = new ScriptEngineManager().getEngineByName("JavaScript");
-                engine.eval("API={}; API.pref={}; API.fragments={};");
-
-                engine.eval("homeDir='"+normalizeDirName(Main.pref.getPreferencesDirectory().getAbsolutePath()) +"';");
-                engine.eval("josmVersion="+Version.getInstance().getVersion()+';');
-                String className = CustomConfigurator.class.getName();
-                engine.eval("API.messageBox="+className+".messageBox");
-                engine.eval("API.askText=function(text) { return String("+className+".askForText(text));}");
-                engine.eval("API.askOption="+className+".askForOption");
-                engine.eval("API.downloadFile="+className+".downloadFile");
-                engine.eval("API.downloadAndUnpackFile="+className+".downloadAndUnpackFile");
-                engine.eval("API.deleteFile="+className+".deleteFile");
-                engine.eval("API.plugin ="+className+".pluginOperation");
-                engine.eval("API.pluginInstall = function(names) { "+className+".pluginOperation(names,'','');}");
-                engine.eval("API.pluginUninstall = function(names) { "+className+".pluginOperation('',names,'');}");
-                engine.eval("API.pluginDelete = function(names) { "+className+".pluginOperation('','',names);}");
-            } catch (ScriptException ex) {
-                log("Error: initializing script engine: "+ex.getMessage());
-                Logging.error(ex);
-            }
-        }
-
-        private void processXML(Document document) {
-            processXmlFragment(document.getDocumentElement());
-        }
-
-        private void processXmlFragment(Element root) {
-            NodeList childNodes = root.getChildNodes();
-            int nops = childNodes.getLength();
-            for (int i = 0; i < nops; i++) {
-                Node item = childNodes.item(i);
-                if (item.getNodeType() != Node.ELEMENT_NODE) continue;
-                String elementName = item.getNodeName();
-                Element elem = (Element) item;
-
-                switch(elementName) {
-                case "var":
-                    setVar(elem.getAttribute("name"), evalVars(elem.getAttribute("value")));
-                    break;
-                case "task":
-                    tasksMap.put(elem.getAttribute("name"), elem);
-                    break;
-                case "runtask":
-                    if (processRunTaskElement(elem)) return;
-                    break;
-                case "ask":
-                    processAskElement(elem);
-                    break;
-                case "if":
-                    processIfElement(elem);
-                    break;
-                case "else":
-                    processElseElement(elem);
-                    break;
-                case "break":
-                    return;
-                case "plugin":
-                    processPluginInstallElement(elem);
-                    break;
-                case "messagebox":
-                    processMsgBoxElement(elem);
-                    break;
-                case "preferences":
-                    processPreferencesElement(elem);
-                    break;
-                case "download":
-                    processDownloadElement(elem);
-                    break;
-                case "delete":
-                    processDeleteElement(elem);
-                    break;
-                case "script":
-                    processScriptElement(elem);
-                    break;
-                default:
-                    log("Error: Unknown element " + elementName);
-                }
-            }
-        }
-
-        private void processPreferencesElement(Element item) {
-            String oper = evalVars(item.getAttribute("operation"));
-            String id = evalVars(item.getAttribute("id"));
-
-            if ("delete-keys".equals(oper)) {
-                String pattern = evalVars(item.getAttribute("pattern"));
-                String key = evalVars(item.getAttribute("key"));
-                PreferencesUtils.deletePreferenceKey(key, mainPrefs);
-                PreferencesUtils.deletePreferenceKeyByPattern(pattern, mainPrefs);
-                return;
-            }
-
-            Preferences tmpPref = readPreferencesFromDOMElement(item);
-            PreferencesUtils.showPrefs(tmpPref);
-
-            if (!id.isEmpty()) {
-                try {
-                    String fragmentVar = "API.fragments['"+id+"']";
-                    engine.eval(fragmentVar+"={};");
-                    PreferencesUtils.loadPrefsToJS(engine, tmpPref, fragmentVar, false);
-                    // we store this fragment as API.fragments['id']
-                } catch (ScriptException ex) {
-                    log(ex, "Error: can not load preferences fragment:");
-                }
-            }
-
-            if ("replace".equals(oper)) {
-                log("Preferences replace: %d keys: %s\n",
-                   tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
-                PreferencesUtils.replacePreferences(tmpPref, mainPrefs);
-            } else if ("append".equals(oper)) {
-                log("Preferences append: %d keys: %s\n",
-                   tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
-                PreferencesUtils.appendPreferences(tmpPref, mainPrefs);
-            } else if ("delete-values".equals(oper)) {
-                PreferencesUtils.deletePreferenceValues(tmpPref, mainPrefs);
-            }
-        }
-
-         private void processDeleteElement(Element item) {
-            String path = evalVars(item.getAttribute("path"));
-            String base = evalVars(item.getAttribute("base"));
-            deleteFile(path, base);
-        }
-
-        private void processDownloadElement(Element item) {
-            String base = evalVars(item.getAttribute("base"));
-            String dir = getDirectoryByAbbr(base);
-            if (dir == null) {
-                log("Error: Can not find directory to place file, use base=cache, base=prefs or base=plugins attribute.");
-                return;
-            }
-
-            String path = evalVars(item.getAttribute("path"));
-            if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
-                return; // some basic protection
-            }
-
-            String address = evalVars(item.getAttribute("url"));
-            if (address.isEmpty() || path.isEmpty()) {
-                log("Error: Please specify url=\"where to get file\" and path=\"where to place it\"");
-                return;
-            }
-
-            String unzip = evalVars(item.getAttribute("unzip"));
-            String mkdir = evalVars(item.getAttribute("mkdir"));
-            processDownloadOperation(address, path, dir, "true".equals(mkdir), "true".equals(unzip));
-        }
-
-        private static void processPluginInstallElement(Element elem) {
-            String install = elem.getAttribute("install");
-            String uninstall = elem.getAttribute("remove");
-            String delete = elem.getAttribute("delete");
-            pluginOperation(install, uninstall, delete);
-        }
-
-        private void processMsgBoxElement(Element elem) {
-            String text = evalVars(elem.getAttribute("text"));
-            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
-            if (!locText.isEmpty()) text = locText;
-
-            String type = evalVars(elem.getAttribute("type"));
-            messageBox(type, text);
-        }
-
-        private void processAskElement(Element elem) {
-            String text = evalVars(elem.getAttribute("text"));
-            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
-            if (!locText.isEmpty()) text = locText;
-            String var = elem.getAttribute("var");
-            if (var.isEmpty()) var = "result";
-
-            String input = evalVars(elem.getAttribute("input"));
-            if ("true".equals(input)) {
-                setVar(var, askForText(text));
-            } else {
-                String opts = evalVars(elem.getAttribute("options"));
-                String locOpts = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".options"));
-                if (!locOpts.isEmpty()) opts = locOpts;
-                setVar(var, String.valueOf(askForOption(text, opts)));
-            }
-        }
-
-        public void setVar(String name, String value) {
-            try {
-                engine.eval(name+"='"+value+"';");
-            } catch (ScriptException ex) {
-                log(ex, String.format("Error: Can not assign variable: %s=%s :", name, value));
-            }
-        }
-
-        private void processIfElement(Element elem) {
-            String realValue = evalVars(elem.getAttribute("test"));
-            boolean v = false;
-            if ("true".equals(realValue) || "false".equals(realValue)) {
-                processXmlFragment(elem);
-                v = true;
-            } else {
-                log("Error: Illegal test expression in if: %s=%s\n", elem.getAttribute("test"), realValue);
-            }
-
-            lastV = v;
-        }
-
-        private void processElseElement(Element elem) {
-            if (!lastV) {
-                processXmlFragment(elem);
-            }
-        }
-
-        private boolean processRunTaskElement(Element elem) {
-            String taskName = elem.getAttribute("name");
-            Element task = tasksMap.get(taskName);
-            if (task != null) {
-                log("EXECUTING TASK "+taskName);
-                processXmlFragment(task); // process task recursively
-            } else {
-                log("Error: Can not execute task "+taskName);
-                return true;
-            }
-            return false;
-        }
-
-        private void processScriptElement(Element elem) {
-            String js = elem.getChildNodes().item(0).getTextContent();
-            log("Processing script...");
-            try {
-                PreferencesUtils.modifyPreferencesByScript(engine, mainPrefs, js);
-            } catch (ScriptException ex) {
-                messageBox("e", ex.getMessage());
-                log(ex, "JS error:");
-            }
-            log("Script finished");
-        }
-
-        /**
-         * substitute ${expression} = expression evaluated by JavaScript
-         * @param s string
-         * @return evaluation result
-         */
-        private String evalVars(String s) {
-            Matcher mr = Pattern.compile("\\$\\{([^\\}]*)\\}").matcher(s);
-            StringBuffer sb = new StringBuffer();
-            while (mr.find()) {
-                try {
-                    String result = engine.eval(mr.group(1)).toString();
-                    mr.appendReplacement(sb, result);
-                } catch (ScriptException ex) {
-                    log(ex, String.format("Error: Can not evaluate expression %s :", mr.group(1)));
-                }
-            }
-            mr.appendTail(sb);
-            return sb.toString();
-        }
-
-        private Preferences readPreferencesFromDOMElement(Element item) {
-            Preferences tmpPref = new Preferences();
-            try {
-                Transformer xformer = TransformerFactory.newInstance().newTransformer();
-                CharArrayWriter outputWriter = new CharArrayWriter(8192);
-                StreamResult out = new StreamResult(outputWriter);
-
-                xformer.transform(new DOMSource(item), out);
-
-                String fragmentWithReplacedVars = evalVars(outputWriter.toString());
-
-                CharArrayReader reader = new CharArrayReader(fragmentWithReplacedVars.toCharArray());
-                tmpPref.fromXML(reader);
-            } catch (TransformerException | XMLStreamException | IOException ex) {
-                log(ex, "Error: can not read XML fragment:");
-            }
-
-            return tmpPref;
-        }
-
-        private static String normalizeDirName(String dir) {
-            String s = dir.replace('\\', '/');
-            if (s.endsWith("/")) s = s.substring(0, s.length()-1);
-            return s;
-        }
-    }
-
-    /**
-     * Helper class to do specific Preferences operation - appending, replacing,
-     * deletion by key and by value
-     * Also contains functions that convert preferences object to JavaScript object and back
-     */
-    public static final class PreferencesUtils {
-
-        private PreferencesUtils() {
-            // Hide implicit public constructor for utility class
-        }
-
-        private static void replacePreferences(Preferences fragment, Preferences mainpref) {
-            for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
-                mainpref.putSetting(entry.getKey(), entry.getValue());
-            }
-        }
-
-        private static void appendPreferences(Preferences fragment, Preferences mainpref) {
-            for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
-                String key = entry.getKey();
-                if (entry.getValue() instanceof StringSetting) {
-                    mainpref.putSetting(key, entry.getValue());
-                } else if (entry.getValue() instanceof ListSetting) {
-                    ListSetting lSetting = (ListSetting) entry.getValue();
-                    Collection<String> newItems = getCollection(mainpref, key, true);
-                    if (newItems == null) continue;
-                    for (String item : lSetting.getValue()) {
-                        // add nonexisting elements to then list
-                        if (!newItems.contains(item)) {
-                            newItems.add(item);
-                        }
-                    }
-                    mainpref.putCollection(key, newItems);
-                } else if (entry.getValue() instanceof ListListSetting) {
-                    ListListSetting llSetting = (ListListSetting) entry.getValue();
-                    Collection<Collection<String>> newLists = getArray(mainpref, key, true);
-                    if (newLists == null) continue;
-
-                    for (Collection<String> list : llSetting.getValue()) {
-                        // add nonexisting list (equals comparison for lists is used implicitly)
-                        if (!newLists.contains(list)) {
-                            newLists.add(list);
-                        }
-                    }
-                    mainpref.putArray(key, newLists);
-                } else if (entry.getValue() instanceof MapListSetting) {
-                    MapListSetting mlSetting = (MapListSetting) entry.getValue();
-                    List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
-                    if (newMaps == null) continue;
-
-                    // get existing properties as list of maps
-
-                    for (Map<String, String> map : mlSetting.getValue()) {
-                        // add nonexisting map (equals comparison for maps is used implicitly)
-                        if (!newMaps.contains(map)) {
-                            newMaps.add(map);
-                        }
-                    }
-                    mainpref.putListOfStructs(entry.getKey(), newMaps);
-                }
-            }
-        }
-
-        /**
-         * Delete items from {@code mainpref} collections that match items from {@code fragment} collections.
-         * @param fragment preferences
-         * @param mainpref main preferences
-         */
-        private static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
-
-            for (Entry<String, Setting<?>> entry : fragment.settingsMap.entrySet()) {
-                String key = entry.getKey();
-                if (entry.getValue() instanceof StringSetting) {
-                    StringSetting sSetting = (StringSetting) entry.getValue();
-                    // if mentioned value found, delete it
-                    if (sSetting.equals(mainpref.settingsMap.get(key))) {
-                        mainpref.put(key, null);
-                    }
-                } else if (entry.getValue() instanceof ListSetting) {
-                    ListSetting lSetting = (ListSetting) entry.getValue();
-                    Collection<String> newItems = getCollection(mainpref, key, true);
-                    if (newItems == null) continue;
-
-                    // remove mentioned items from collection
-                    for (String item : lSetting.getValue()) {
-                        log("Deleting preferences: from list %s: %s\n", key, item);
-                        newItems.remove(item);
-                    }
-                    mainpref.putCollection(entry.getKey(), newItems);
-                } else if (entry.getValue() instanceof ListListSetting) {
-                    ListListSetting llSetting = (ListListSetting) entry.getValue();
-                    Collection<Collection<String>> newLists = getArray(mainpref, key, true);
-                    if (newLists == null) continue;
-
-                    // if items are found in one of lists, remove that list!
-                    Iterator<Collection<String>> listIterator = newLists.iterator();
-                    while (listIterator.hasNext()) {
-                        Collection<String> list = listIterator.next();
-                        for (Collection<String> removeList : llSetting.getValue()) {
-                            if (list.containsAll(removeList)) {
-                                // remove current list, because it matches search criteria
-                                log("Deleting preferences: list from lists %s: %s\n", key, list);
-                                listIterator.remove();
-                            }
-                        }
-                    }
-
-                    mainpref.putArray(key, newLists);
-                } else if (entry.getValue() instanceof MapListSetting) {
-                    MapListSetting mlSetting = (MapListSetting) entry.getValue();
-                    List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
-                    if (newMaps == null) continue;
-
-                    Iterator<Map<String, String>> mapIterator = newMaps.iterator();
-                    while (mapIterator.hasNext()) {
-                        Map<String, String> map = mapIterator.next();
-                        for (Map<String, String> removeMap : mlSetting.getValue()) {
-                            if (map.entrySet().containsAll(removeMap.entrySet())) {
-                                // the map contain all mentioned key-value pair, so it should be deleted from "maps"
-                                log("Deleting preferences: deleting map from maps %s: %s\n", key, map);
-                                mapIterator.remove();
-                            }
-                        }
-                    }
-                    mainpref.putListOfStructs(entry.getKey(), newMaps);
-                }
-            }
-        }
-
-    private static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
-        Map<String, Setting<?>> allSettings = pref.getAllSettings();
-        for (Entry<String, Setting<?>> entry : allSettings.entrySet()) {
-            String key = entry.getKey();
-            if (key.matches(pattern)) {
-                log("Deleting preferences: deleting key from preferences: " + key);
-                pref.putSetting(key, null);
-            }
-        }
-    }
-
-    private static void deletePreferenceKey(String key, Preferences pref) {
-        Map<String, Setting<?>> allSettings = pref.getAllSettings();
-        if (allSettings.containsKey(key)) {
-            log("Deleting preferences: deleting key from preferences: " + key);
-            pref.putSetting(key, null);
-        }
-    }
-
-    private static Collection<String> getCollection(Preferences mainpref, String key, boolean warnUnknownDefault) {
-        ListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListSetting.class);
-        ListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListSetting.class);
-        if (existing == null && defaults == null) {
-            if (warnUnknownDefault) defaultUnknownWarning(key);
-            return null;
-        }
-        if (existing != null)
-            return new ArrayList<>(existing.getValue());
-        else
-            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
-    }
-
-    private static Collection<Collection<String>> getArray(Preferences mainpref, String key, boolean warnUnknownDefault) {
-        ListListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListListSetting.class);
-        ListListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListListSetting.class);
-
-        if (existing == null && defaults == null) {
-            if (warnUnknownDefault) defaultUnknownWarning(key);
-            return null;
-        }
-        if (existing != null)
-            return new ArrayList<>(existing.getValue());
-        else
-            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
-    }
-
-    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault) {
-        MapListSetting existing = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
-        MapListSetting defaults = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
-
-        if (existing == null && defaults == null) {
-            if (warnUnknownDefault) defaultUnknownWarning(key);
-            return null;
-        }
-
-        if (existing != null)
-            return new ArrayList<>(existing.getValue());
-        else
-            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
-    }
-
-    private static void defaultUnknownWarning(String key) {
-        log("Warning: Unknown default value of %s , skipped\n", key);
-        JOptionPane.showMessageDialog(
-                Main.parent,
-                tr("<html>Settings file asks to append preferences to <b>{0}</b>,<br/> "+
-                        "but its default value is unknown at this moment.<br/> " +
-                        "Please activate corresponding function manually and retry importing.", key),
-                tr("Warning"),
-                JOptionPane.WARNING_MESSAGE);
-    }
-
-    private static void showPrefs(Preferences tmpPref) {
-        Logging.info("properties: " + tmpPref.settingsMap);
-    }
-
-    private static void modifyPreferencesByScript(ScriptEngine engine, Preferences tmpPref, String js) throws ScriptException {
-        loadPrefsToJS(engine, tmpPref, "API.pref", true);
-        engine.eval(js);
-        readPrefsFromJS(engine, tmpPref, "API.pref");
-    }
-
-    /**
-     * Convert JavaScript preferences object to preferences data structures
-     * @param engine - JS engine to put object
-     * @param tmpPref - preferences to fill from JS
-     * @param varInJS - JS variable name, where preferences are stored
-     * @throws ScriptException if the evaluation fails
-     */
-    public static void readPrefsFromJS(ScriptEngine engine, Preferences tmpPref, String varInJS) throws ScriptException {
-        String finish =
-            "stringMap = new java.util.TreeMap ;"+
-            "listMap =  new java.util.TreeMap ;"+
-            "listlistMap = new java.util.TreeMap ;"+
-            "listmapMap =  new java.util.TreeMap ;"+
-            "for (key in "+varInJS+") {"+
-            "  val = "+varInJS+"[key];"+
-            "  type = typeof val == 'string' ? 'string' : val.type;"+
-            "  if (type == 'string') {"+
-            "    stringMap.put(key, val);"+
-            "  } else if (type == 'list') {"+
-            "    l = new java.util.ArrayList;"+
-            "    for (i=0; i<val.length; i++) {"+
-            "      l.add(java.lang.String.valueOf(val[i]));"+
-            "    }"+
-            "    listMap.put(key, l);"+
-            "  } else if (type == 'listlist') {"+
-            "    l = new java.util.ArrayList;"+
-            "    for (i=0; i<val.length; i++) {"+
-            "      list=val[i];"+
-            "      jlist=new java.util.ArrayList;"+
-            "      for (j=0; j<list.length; j++) {"+
-            "         jlist.add(java.lang.String.valueOf(list[j]));"+
-            "      }"+
-            "      l.add(jlist);"+
-            "    }"+
-            "    listlistMap.put(key, l);"+
-            "  } else if (type == 'listmap') {"+
-            "    l = new java.util.ArrayList;"+
-            "    for (i=0; i<val.length; i++) {"+
-            "      map=val[i];"+
-            "      jmap=new java.util.TreeMap;"+
-            "      for (var key2 in map) {"+
-            "         jmap.put(key2,java.lang.String.valueOf(map[key2]));"+
-            "      }"+
-            "      l.add(jmap);"+
-            "    }"+
-            "    listmapMap.put(key, l);"+
-            "  }  else {" +
-            "   org.openstreetmap.josm.data.CustomConfigurator.log('Unknown type:'+val.type+ '- use list, listlist or listmap'); }"+
-            "  }";
-        engine.eval(finish);
-
-        @SuppressWarnings("unchecked")
-        Map<String, String> stringMap = (Map<String, String>) engine.get("stringMap");
-        @SuppressWarnings("unchecked")
-        Map<String, List<String>> listMap = (Map<String, List<String>>) engine.get("listMap");
-        @SuppressWarnings("unchecked")
-        Map<String, List<Collection<String>>> listlistMap = (Map<String, List<Collection<String>>>) engine.get("listlistMap");
-        @SuppressWarnings("unchecked")
-        Map<String, List<Map<String, String>>> listmapMap = (Map<String, List<Map<String, String>>>) engine.get("listmapMap");
-
-        tmpPref.settingsMap.clear();
-
-        Map<String, Setting<?>> tmp = new HashMap<>();
-        for (Entry<String, String> e : stringMap.entrySet()) {
-            tmp.put(e.getKey(), new StringSetting(e.getValue()));
-        }
-        for (Entry<String, List<String>> e : listMap.entrySet()) {
-            tmp.put(e.getKey(), new ListSetting(e.getValue()));
-        }
-
-        for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            List<List<String>> value = (List) e.getValue();
-            tmp.put(e.getKey(), new ListListSetting(value));
-        }
-        for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
-            tmp.put(e.getKey(), new MapListSetting(e.getValue()));
-        }
-        for (Entry<String, Setting<?>> e : tmp.entrySet()) {
-            if (e.getValue().equals(tmpPref.defaultsMap.get(e.getKey()))) continue;
-            tmpPref.settingsMap.put(e.getKey(), e.getValue());
-        }
-    }
-
-    /**
-     * Convert preferences data structures to JavaScript object
-     * @param engine - JS engine to put object
-     * @param tmpPref - preferences to convert
-     * @param whereToPutInJS - variable name to store preferences in JS
-     * @param includeDefaults - include known default values to JS objects
-     * @throws ScriptException if the evaluation fails
-     */
-    public static void loadPrefsToJS(ScriptEngine engine, Preferences tmpPref, String whereToPutInJS, boolean includeDefaults)
-            throws ScriptException {
-        Map<String, String> stringMap = new TreeMap<>();
-        Map<String, List<String>> listMap = new TreeMap<>();
-        Map<String, List<List<String>>> listlistMap = new TreeMap<>();
-        Map<String, List<Map<String, String>>> listmapMap = new TreeMap<>();
-
-        if (includeDefaults) {
-            for (Map.Entry<String, Setting<?>> e: tmpPref.defaultsMap.entrySet()) {
-                Setting<?> setting = e.getValue();
-                if (setting instanceof StringSetting) {
-                    stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
-                } else if (setting instanceof ListSetting) {
-                    listMap.put(e.getKey(), ((ListSetting) setting).getValue());
-                } else if (setting instanceof ListListSetting) {
-                    listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
-                } else if (setting instanceof MapListSetting) {
-                    listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
-                }
-            }
-        }
-        tmpPref.settingsMap.entrySet().removeIf(e -> e.getValue().getValue() == null);
-
-        for (Map.Entry<String, Setting<?>> e: tmpPref.settingsMap.entrySet()) {
-            Setting<?> setting = e.getValue();
-            if (setting instanceof StringSetting) {
-                stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
-            } else if (setting instanceof ListSetting) {
-                listMap.put(e.getKey(), ((ListSetting) setting).getValue());
-            } else if (setting instanceof ListListSetting) {
-                listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
-            } else if (setting instanceof MapListSetting) {
-                listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
-            }
-        }
-
-        engine.put("stringMap", stringMap);
-        engine.put("listMap", listMap);
-        engine.put("listlistMap", listlistMap);
-        engine.put("listmapMap", listmapMap);
-
-        String init =
-            "function getJSList( javaList ) {"+
-            " var jsList; var i; "+
-            " if (javaList == null) return null;"+
-            "jsList = [];"+
-            "  for (i = 0; i < javaList.size(); i++) {"+
-            "    jsList.push(String(list.get(i)));"+
-            "  }"+
-            "return jsList;"+
-            "}"+
-            "function getJSMap( javaMap ) {"+
-            " var jsMap; var it; var e; "+
-            " if (javaMap == null) return null;"+
-            " jsMap = {};"+
-            " for (it = javaMap.entrySet().iterator(); it.hasNext();) {"+
-            "    e = it.next();"+
-            "    jsMap[ String(e.getKey()) ] = String(e.getValue()); "+
-            "  }"+
-            "  return jsMap;"+
-            "}"+
-            "for (it = stringMap.entrySet().iterator(); it.hasNext();) {"+
-            "  e = it.next();"+
-            whereToPutInJS+"[String(e.getKey())] = String(e.getValue());"+
-            "}\n"+
-            "for (it = listMap.entrySet().iterator(); it.hasNext();) {"+
-            "  e = it.next();"+
-            "  list = e.getValue();"+
-            "  jslist = getJSList(list);"+
-            "  jslist.type = 'list';"+
-            whereToPutInJS+"[String(e.getKey())] = jslist;"+
-            "}\n"+
-            "for (it = listlistMap.entrySet().iterator(); it.hasNext(); ) {"+
-            "  e = it.next();"+
-            "  listlist = e.getValue();"+
-            "  jslistlist = [];"+
-            "  for (it2 = listlist.iterator(); it2.hasNext(); ) {"+
-            "    list = it2.next(); "+
-            "    jslistlist.push(getJSList(list));"+
-            "    }"+
-            "  jslistlist.type = 'listlist';"+
-            whereToPutInJS+"[String(e.getKey())] = jslistlist;"+
-            "}\n"+
-            "for (it = listmapMap.entrySet().iterator(); it.hasNext();) {"+
-            "  e = it.next();"+
-            "  listmap = e.getValue();"+
-            "  jslistmap = [];"+
-            "  for (it2 = listmap.iterator(); it2.hasNext();) {"+
-            "    map = it2.next();"+
-            "    jslistmap.push(getJSMap(map));"+
-            "    }"+
-            "  jslistmap.type = 'listmap';"+
-            whereToPutInJS+"[String(e.getKey())] = jslistmap;"+
-            "}\n";
-
-            // Execute conversion script
-            engine.eval(init);
-        }
-    }
-}
Index: trunk/src/org/openstreetmap/josm/data/Preferences.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/data/Preferences.java	(revision 12634)
@@ -235,4 +235,22 @@
 
     /**
+     * Constructs a new {@code Preferences}.
+     */
+    public Preferences() {
+        // Default constructor
+    }
+
+    /**
+     * Constructs a new {@code Preferences} from an existing instance.
+     * @param pref existing preferences to copy
+     * @since 12634
+     */
+    public Preferences(Preferences pref) {
+        settingsMap.putAll(pref.settingsMap);
+        defaultsMap.putAll(pref.defaultsMap);
+        colornames.putAll(pref.colornames);
+    }
+
+    /**
      * Adds a new preferences listener.
      * @param listener The listener to add
Index: trunk/src/org/openstreetmap/josm/data/PreferencesUtils.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/PreferencesUtils.java	(revision 12634)
+++ trunk/src/org/openstreetmap/josm/data/PreferencesUtils.java	(revision 12634)
@@ -0,0 +1,430 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.data;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import javax.swing.JOptionPane;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.preferences.ListListSetting;
+import org.openstreetmap.josm.data.preferences.ListSetting;
+import org.openstreetmap.josm.data.preferences.MapListSetting;
+import org.openstreetmap.josm.data.preferences.Setting;
+import org.openstreetmap.josm.data.preferences.StringSetting;
+import org.openstreetmap.josm.gui.io.CustomConfigurator;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Helper class to do specific Preferences operation - appending, replacing, deletion by key and by value
+ * Also contains functions that convert preferences object to JavaScript object and back
+ * @since 12634 (extracted from {@code CustomConfigurator})
+ */
+public final class PreferencesUtils {
+
+    private PreferencesUtils() {
+        // Hide implicit public constructor for utility class
+    }
+
+    public static void replacePreferences(Preferences fragment, Preferences mainpref) {
+        for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
+            mainpref.putSetting(entry.getKey(), entry.getValue());
+        }
+    }
+
+    public static void appendPreferences(Preferences fragment, Preferences mainpref) {
+        for (Entry<String, Setting<?>> entry: fragment.settingsMap.entrySet()) {
+            String key = entry.getKey();
+            if (entry.getValue() instanceof StringSetting) {
+                mainpref.putSetting(key, entry.getValue());
+            } else if (entry.getValue() instanceof ListSetting) {
+                ListSetting lSetting = (ListSetting) entry.getValue();
+                Collection<String> newItems = getCollection(mainpref, key, true);
+                if (newItems == null) continue;
+                for (String item : lSetting.getValue()) {
+                    // add nonexisting elements to then list
+                    if (!newItems.contains(item)) {
+                        newItems.add(item);
+                    }
+                }
+                mainpref.putCollection(key, newItems);
+            } else if (entry.getValue() instanceof ListListSetting) {
+                ListListSetting llSetting = (ListListSetting) entry.getValue();
+                Collection<Collection<String>> newLists = getArray(mainpref, key, true);
+                if (newLists == null) continue;
+
+                for (Collection<String> list : llSetting.getValue()) {
+                    // add nonexisting list (equals comparison for lists is used implicitly)
+                    if (!newLists.contains(list)) {
+                        newLists.add(list);
+                    }
+                }
+                mainpref.putArray(key, newLists);
+            } else if (entry.getValue() instanceof MapListSetting) {
+                MapListSetting mlSetting = (MapListSetting) entry.getValue();
+                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
+                if (newMaps == null) continue;
+
+                // get existing properties as list of maps
+
+                for (Map<String, String> map : mlSetting.getValue()) {
+                    // add nonexisting map (equals comparison for maps is used implicitly)
+                    if (!newMaps.contains(map)) {
+                        newMaps.add(map);
+                    }
+                }
+                mainpref.putListOfStructs(entry.getKey(), newMaps);
+            }
+        }
+    }
+
+    /**
+     * Delete items from {@code mainpref} collections that match items from {@code fragment} collections.
+     * @param fragment preferences
+     * @param mainpref main preferences
+     */
+    public static void deletePreferenceValues(Preferences fragment, Preferences mainpref) {
+
+        for (Entry<String, Setting<?>> entry : fragment.settingsMap.entrySet()) {
+            String key = entry.getKey();
+            if (entry.getValue() instanceof StringSetting) {
+                StringSetting sSetting = (StringSetting) entry.getValue();
+                // if mentioned value found, delete it
+                if (sSetting.equals(mainpref.settingsMap.get(key))) {
+                    mainpref.put(key, null);
+                }
+            } else if (entry.getValue() instanceof ListSetting) {
+                ListSetting lSetting = (ListSetting) entry.getValue();
+                Collection<String> newItems = getCollection(mainpref, key, true);
+                if (newItems == null) continue;
+
+                // remove mentioned items from collection
+                for (String item : lSetting.getValue()) {
+                    CustomConfigurator.log("Deleting preferences: from list %s: %s\n", key, item);
+                    newItems.remove(item);
+                }
+                mainpref.putCollection(entry.getKey(), newItems);
+            } else if (entry.getValue() instanceof ListListSetting) {
+                ListListSetting llSetting = (ListListSetting) entry.getValue();
+                Collection<Collection<String>> newLists = getArray(mainpref, key, true);
+                if (newLists == null) continue;
+
+                // if items are found in one of lists, remove that list!
+                Iterator<Collection<String>> listIterator = newLists.iterator();
+                while (listIterator.hasNext()) {
+                    Collection<String> list = listIterator.next();
+                    for (Collection<String> removeList : llSetting.getValue()) {
+                        if (list.containsAll(removeList)) {
+                            // remove current list, because it matches search criteria
+                            CustomConfigurator.log("Deleting preferences: list from lists %s: %s\n", key, list);
+                            listIterator.remove();
+                        }
+                    }
+                }
+
+                mainpref.putArray(key, newLists);
+            } else if (entry.getValue() instanceof MapListSetting) {
+                MapListSetting mlSetting = (MapListSetting) entry.getValue();
+                List<Map<String, String>> newMaps = getListOfStructs(mainpref, key, true);
+                if (newMaps == null) continue;
+
+                Iterator<Map<String, String>> mapIterator = newMaps.iterator();
+                while (mapIterator.hasNext()) {
+                    Map<String, String> map = mapIterator.next();
+                    for (Map<String, String> removeMap : mlSetting.getValue()) {
+                        if (map.entrySet().containsAll(removeMap.entrySet())) {
+                            // the map contain all mentioned key-value pair, so it should be deleted from "maps"
+                            CustomConfigurator.log("Deleting preferences: deleting map from maps %s: %s\n", key, map);
+                            mapIterator.remove();
+                        }
+                    }
+                }
+                mainpref.putListOfStructs(entry.getKey(), newMaps);
+            }
+        }
+    }
+
+    public static void deletePreferenceKeyByPattern(String pattern, Preferences pref) {
+        Map<String, Setting<?>> allSettings = pref.getAllSettings();
+        for (Entry<String, Setting<?>> entry : allSettings.entrySet()) {
+            String key = entry.getKey();
+            if (key.matches(pattern)) {
+                CustomConfigurator.log("Deleting preferences: deleting key from preferences: " + key);
+                pref.putSetting(key, null);
+            }
+        }
+    }
+
+    public static void deletePreferenceKey(String key, Preferences pref) {
+        Map<String, Setting<?>> allSettings = pref.getAllSettings();
+        if (allSettings.containsKey(key)) {
+            CustomConfigurator.log("Deleting preferences: deleting key from preferences: " + key);
+            pref.putSetting(key, null);
+        }
+    }
+
+    private static Collection<String> getCollection(Preferences mainpref, String key, boolean warnUnknownDefault) {
+        ListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListSetting.class);
+        ListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListSetting.class);
+        if (existing == null && defaults == null) {
+            if (warnUnknownDefault) defaultUnknownWarning(key);
+            return null;
+        }
+        if (existing != null)
+            return new ArrayList<>(existing.getValue());
+        else
+            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
+    }
+
+    private static Collection<Collection<String>> getArray(Preferences mainpref, String key, boolean warnUnknownDefault) {
+        ListListSetting existing = Utils.cast(mainpref.settingsMap.get(key), ListListSetting.class);
+        ListListSetting defaults = Utils.cast(mainpref.defaultsMap.get(key), ListListSetting.class);
+
+        if (existing == null && defaults == null) {
+            if (warnUnknownDefault) defaultUnknownWarning(key);
+            return null;
+        }
+        if (existing != null)
+            return new ArrayList<>(existing.getValue());
+        else
+            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
+    }
+
+    private static List<Map<String, String>> getListOfStructs(Preferences mainpref, String key, boolean warnUnknownDefault) {
+        MapListSetting existing = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
+        MapListSetting defaults = Utils.cast(mainpref.settingsMap.get(key), MapListSetting.class);
+
+        if (existing == null && defaults == null) {
+            if (warnUnknownDefault) defaultUnknownWarning(key);
+            return null;
+        }
+
+        if (existing != null)
+            return new ArrayList<>(existing.getValue());
+        else
+            return defaults.getValue() == null ? null : new ArrayList<>(defaults.getValue());
+    }
+
+    private static void defaultUnknownWarning(String key) {
+        CustomConfigurator.log("Warning: Unknown default value of %s , skipped\n", key);
+        JOptionPane.showMessageDialog(
+                Main.parent,
+                tr("<html>Settings file asks to append preferences to <b>{0}</b>,<br/> "+
+                        "but its default value is unknown at this moment.<br/> " +
+                        "Please activate corresponding function manually and retry importing.", key),
+                tr("Warning"),
+                JOptionPane.WARNING_MESSAGE);
+    }
+
+    public static void showPrefs(Preferences tmpPref) {
+        Logging.info("properties: " + tmpPref.settingsMap);
+    }
+
+    public static void modifyPreferencesByScript(ScriptEngine engine, Preferences tmpPref, String js) throws ScriptException {
+        loadPrefsToJS(engine, tmpPref, "API.pref", true);
+        engine.eval(js);
+        readPrefsFromJS(engine, tmpPref, "API.pref");
+    }
+
+    /**
+     * Convert JavaScript preferences object to preferences data structures
+     * @param engine - JS engine to put object
+     * @param tmpPref - preferences to fill from JS
+     * @param varInJS - JS variable name, where preferences are stored
+     * @throws ScriptException if the evaluation fails
+     */
+    public static void readPrefsFromJS(ScriptEngine engine, Preferences tmpPref, String varInJS) throws ScriptException {
+        String finish =
+            "stringMap = new java.util.TreeMap ;"+
+            "listMap =  new java.util.TreeMap ;"+
+            "listlistMap = new java.util.TreeMap ;"+
+            "listmapMap =  new java.util.TreeMap ;"+
+            "for (key in "+varInJS+") {"+
+            "  val = "+varInJS+"[key];"+
+            "  type = typeof val == 'string' ? 'string' : val.type;"+
+            "  if (type == 'string') {"+
+            "    stringMap.put(key, val);"+
+            "  } else if (type == 'list') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      l.add(java.lang.String.valueOf(val[i]));"+
+            "    }"+
+            "    listMap.put(key, l);"+
+            "  } else if (type == 'listlist') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      list=val[i];"+
+            "      jlist=new java.util.ArrayList;"+
+            "      for (j=0; j<list.length; j++) {"+
+            "         jlist.add(java.lang.String.valueOf(list[j]));"+
+            "      }"+
+            "      l.add(jlist);"+
+            "    }"+
+            "    listlistMap.put(key, l);"+
+            "  } else if (type == 'listmap') {"+
+            "    l = new java.util.ArrayList;"+
+            "    for (i=0; i<val.length; i++) {"+
+            "      map=val[i];"+
+            "      jmap=new java.util.TreeMap;"+
+            "      for (var key2 in map) {"+
+            "         jmap.put(key2,java.lang.String.valueOf(map[key2]));"+
+            "      }"+
+            "      l.add(jmap);"+
+            "    }"+
+            "    listmapMap.put(key, l);"+
+            "  }  else {" +
+            "   " + CustomConfigurator.class.getName() + ".log('Unknown type:'+val.type+ '- use list, listlist or listmap'); }"+
+            "  }";
+        engine.eval(finish);
+
+        @SuppressWarnings("unchecked")
+        Map<String, String> stringMap = (Map<String, String>) engine.get("stringMap");
+        @SuppressWarnings("unchecked")
+        Map<String, List<String>> listMap = (Map<String, List<String>>) engine.get("listMap");
+        @SuppressWarnings("unchecked")
+        Map<String, List<Collection<String>>> listlistMap = (Map<String, List<Collection<String>>>) engine.get("listlistMap");
+        @SuppressWarnings("unchecked")
+        Map<String, List<Map<String, String>>> listmapMap = (Map<String, List<Map<String, String>>>) engine.get("listmapMap");
+
+        tmpPref.settingsMap.clear();
+
+        Map<String, Setting<?>> tmp = new HashMap<>();
+        for (Entry<String, String> e : stringMap.entrySet()) {
+            tmp.put(e.getKey(), new StringSetting(e.getValue()));
+        }
+        for (Entry<String, List<String>> e : listMap.entrySet()) {
+            tmp.put(e.getKey(), new ListSetting(e.getValue()));
+        }
+
+        for (Entry<String, List<Collection<String>>> e : listlistMap.entrySet()) {
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            List<List<String>> value = (List) e.getValue();
+            tmp.put(e.getKey(), new ListListSetting(value));
+        }
+        for (Entry<String, List<Map<String, String>>> e : listmapMap.entrySet()) {
+            tmp.put(e.getKey(), new MapListSetting(e.getValue()));
+        }
+        for (Entry<String, Setting<?>> e : tmp.entrySet()) {
+            if (e.getValue().equals(tmpPref.defaultsMap.get(e.getKey()))) continue;
+            tmpPref.settingsMap.put(e.getKey(), e.getValue());
+        }
+    }
+
+    /**
+     * Convert preferences data structures to JavaScript object
+     * @param engine - JS engine to put object
+     * @param tmpPref - preferences to convert
+     * @param whereToPutInJS - variable name to store preferences in JS
+     * @param includeDefaults - include known default values to JS objects
+     * @throws ScriptException if the evaluation fails
+     */
+    public static void loadPrefsToJS(ScriptEngine engine, Preferences tmpPref, String whereToPutInJS, boolean includeDefaults)
+            throws ScriptException {
+        Map<String, String> stringMap = new TreeMap<>();
+        Map<String, List<String>> listMap = new TreeMap<>();
+        Map<String, List<List<String>>> listlistMap = new TreeMap<>();
+        Map<String, List<Map<String, String>>> listmapMap = new TreeMap<>();
+
+        if (includeDefaults) {
+            for (Map.Entry<String, Setting<?>> e: tmpPref.defaultsMap.entrySet()) {
+                Setting<?> setting = e.getValue();
+                if (setting instanceof StringSetting) {
+                    stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
+                } else if (setting instanceof ListSetting) {
+                    listMap.put(e.getKey(), ((ListSetting) setting).getValue());
+                } else if (setting instanceof ListListSetting) {
+                    listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
+                } else if (setting instanceof MapListSetting) {
+                    listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
+                }
+            }
+        }
+        tmpPref.settingsMap.entrySet().removeIf(e -> e.getValue().getValue() == null);
+
+        for (Map.Entry<String, Setting<?>> e: tmpPref.settingsMap.entrySet()) {
+            Setting<?> setting = e.getValue();
+            if (setting instanceof StringSetting) {
+                stringMap.put(e.getKey(), ((StringSetting) setting).getValue());
+            } else if (setting instanceof ListSetting) {
+                listMap.put(e.getKey(), ((ListSetting) setting).getValue());
+            } else if (setting instanceof ListListSetting) {
+                listlistMap.put(e.getKey(), ((ListListSetting) setting).getValue());
+            } else if (setting instanceof MapListSetting) {
+                listmapMap.put(e.getKey(), ((MapListSetting) setting).getValue());
+            }
+        }
+
+        engine.put("stringMap", stringMap);
+        engine.put("listMap", listMap);
+        engine.put("listlistMap", listlistMap);
+        engine.put("listmapMap", listmapMap);
+
+        String init =
+            "function getJSList( javaList ) {"+
+            " var jsList; var i; "+
+            " if (javaList == null) return null;"+
+            "jsList = [];"+
+            "  for (i = 0; i < javaList.size(); i++) {"+
+            "    jsList.push(String(list.get(i)));"+
+            "  }"+
+            "return jsList;"+
+            "}"+
+            "function getJSMap( javaMap ) {"+
+            " var jsMap; var it; var e; "+
+            " if (javaMap == null) return null;"+
+            " jsMap = {};"+
+            " for (it = javaMap.entrySet().iterator(); it.hasNext();) {"+
+            "    e = it.next();"+
+            "    jsMap[ String(e.getKey()) ] = String(e.getValue()); "+
+            "  }"+
+            "  return jsMap;"+
+            "}"+
+            "for (it = stringMap.entrySet().iterator(); it.hasNext();) {"+
+            "  e = it.next();"+
+            whereToPutInJS+"[String(e.getKey())] = String(e.getValue());"+
+            "}\n"+
+            "for (it = listMap.entrySet().iterator(); it.hasNext();) {"+
+            "  e = it.next();"+
+            "  list = e.getValue();"+
+            "  jslist = getJSList(list);"+
+            "  jslist.type = 'list';"+
+            whereToPutInJS+"[String(e.getKey())] = jslist;"+
+            "}\n"+
+            "for (it = listlistMap.entrySet().iterator(); it.hasNext(); ) {"+
+            "  e = it.next();"+
+            "  listlist = e.getValue();"+
+            "  jslistlist = [];"+
+            "  for (it2 = listlist.iterator(); it2.hasNext(); ) {"+
+            "    list = it2.next(); "+
+            "    jslistlist.push(getJSList(list));"+
+            "    }"+
+            "  jslistlist.type = 'listlist';"+
+            whereToPutInJS+"[String(e.getKey())] = jslistlist;"+
+            "}\n"+
+            "for (it = listmapMap.entrySet().iterator(); it.hasNext();) {"+
+            "  e = it.next();"+
+            "  listmap = e.getValue();"+
+            "  jslistmap = [];"+
+            "  for (it2 = listmap.iterator(); it2.hasNext();) {"+
+            "    map = it2.next();"+
+            "    jslistmap.push(getJSMap(map));"+
+            "    }"+
+            "  jslistmap.type = 'listmap';"+
+            whereToPutInJS+"[String(e.getKey())] = jslistmap;"+
+            "}\n";
+
+        // Execute conversion script
+        engine.eval(init);
+    }
+}
Index: trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java	(revision 12634)
@@ -16,4 +16,5 @@
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.ExecutorService;
 
 import org.openstreetmap.josm.Main;
@@ -33,4 +34,5 @@
 public class ImageryLayerInfo {
 
+    /** Unique instance */
     public static final ImageryLayerInfo instance = new ImageryLayerInfo();
     /** List of all usable layers */
@@ -61,8 +63,15 @@
     }
 
+    /**
+     * Constructs a new {@code ImageryLayerInfo} from an existing one.
+     * @param info info to copy
+     */
     public ImageryLayerInfo(ImageryLayerInfo info) {
         layers.addAll(info.layers);
     }
 
+    /**
+     * Clear the lists of layers.
+     */
     public void clear() {
         layers.clear();
@@ -88,5 +97,5 @@
             Collections.sort(layers);
         }
-        loadDefaults(false, true, fastFail);
+        loadDefaults(false, null, fastFail);
     }
 
@@ -95,18 +104,18 @@
      *
      * The data is downloaded from the JOSM website (or loaded from cache).
-     * Entries marked as "default" are added to the user selection, if not
-     * already present.
+     * Entries marked as "default" are added to the user selection, if not already present.
      *
      * @param clearCache if true, clear the cache and start a fresh download.
-     * @param quiet whether not the loading should be performed using a {@link PleaseWaitRunnable} in the background
+     * @param worker executor service which will perform the loading. If null, it should be performed using a {@link PleaseWaitRunnable} in the background
      * @param fastFail whether opening HTTP connections should fail fast, see {@link ImageryReader#setFastFail(boolean)}
-     */
-    public void loadDefaults(boolean clearCache, boolean quiet, boolean fastFail) {
+     * @since 12634
+     */
+    public void loadDefaults(boolean clearCache, ExecutorService worker, boolean fastFail) {
         final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail);
-        if (quiet) {
+        if (worker == null) {
             loader.realRun();
             loader.finish();
         } else {
-            Main.worker.execute(new DefaultEntryLoader(clearCache, fastFail));
+            worker.execute(loader);
         }
     }
@@ -327,12 +336,23 @@
     }
 
+    /**
+     * Add a new imagery entry.
+     * @param info imagery entry to add
+     */
     public void add(ImageryInfo info) {
         layers.add(info);
     }
 
+    /**
+     * Remove an imagery entry.
+     * @param info imagery entry to remove
+     */
     public void remove(ImageryInfo info) {
         layers.remove(info);
     }
 
+    /**
+     * Save the list of imagery entries to preferences.
+     */
     public void save() {
         List<ImageryPreferenceEntry> entries = new ArrayList<>();
Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 12634)
@@ -7,4 +7,5 @@
 import java.awt.BorderLayout;
 import java.awt.Dimension;
+import java.awt.GraphicsEnvironment;
 import java.awt.event.KeyEvent;
 import java.io.File;
@@ -36,4 +37,5 @@
 import java.util.TreeSet;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
@@ -59,7 +61,5 @@
 import org.openstreetmap.josm.actions.mapmode.DrawAction;
 import org.openstreetmap.josm.actions.search.SearchAction;
-import org.openstreetmap.josm.data.AutosaveTask;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.data.CustomConfigurator;
 import org.openstreetmap.josm.data.Version;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
@@ -68,4 +68,6 @@
 import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
+import org.openstreetmap.josm.gui.io.CustomConfigurator.XMLCommandProcessor;
+import org.openstreetmap.josm.gui.layer.AutosaveTask;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
@@ -73,4 +75,5 @@
 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
 import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
+import org.openstreetmap.josm.gui.progress.ProgressMonitorExecutor;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.gui.util.GuiHelper;
@@ -124,4 +127,11 @@
 
     /**
+     * The worker thread slave. This is for executing all long and intensive
+     * calculations. The executed runnables are guaranteed to be executed separately and sequential.
+     * @since 12634 (as a replacement to {@code Main.worker})
+     */
+    public static final ExecutorService worker = new ProgressMonitorExecutor("main-worker-%d", Thread.NORM_PRIORITY);
+
+    /**
      * Constructs a new {@code MainApplication} without a window.
      */
@@ -227,4 +237,7 @@
     @Override
     protected void shutdown() {
+        if (!GraphicsEnvironment.isHeadless()) {
+            worker.shutdown();
+        }
         if (mainFrame != null) {
             mainFrame.storeState();
@@ -234,4 +247,7 @@
         }
         super.shutdown();
+        if (!GraphicsEnvironment.isHeadless()) {
+            worker.shutdownNow();
+        }
     }
 
@@ -456,5 +472,5 @@
 
         if (args.hasOption(Option.LOAD_PREFERENCES)) {
-            CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
+            XMLCommandProcessor config = new XMLCommandProcessor(Main.pref);
             for (String i : args.get(Option.LOAD_PREFERENCES)) {
                 Logging.info("Reading preferences from " + i);
@@ -671,5 +687,5 @@
         Future<?> future = task.download(true, b, null);
         // ... and the continuation when the download is finished (this will wait for the download to finish)
-        return Collections.singletonList(Main.worker.submit(new PostDownloadHandler(task, future)));
+        return Collections.singletonList(MainApplication.worker.submit(new PostDownloadHandler(task, future)));
     }
 
@@ -693,5 +709,5 @@
         final Collection<String> selectionArguments = args.get(Option.SELECTION);
         if (!selectionArguments.isEmpty()) {
-            tasks.add(Main.worker.submit(() -> {
+            tasks.add(MainApplication.worker.submit(() -> {
                 for (String s : selectionArguments) {
                     SearchAction.search(s, SearchAction.SearchMode.add);
Index: trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/FilePaster.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/FilePaster.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/datatransfer/importers/FilePaster.java	(revision 12634)
@@ -10,7 +10,7 @@
 import javax.swing.TransferHandler.TransferSupport;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.OpenFileAction;
 import org.openstreetmap.josm.data.coor.EastNorth;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 
@@ -35,5 +35,5 @@
         OpenFileAction.OpenFileTask task = new OpenFileAction.OpenFileTask(files, null);
         task.setRecordHistory(true);
-        Main.worker.submit(task);
+        MainApplication.worker.submit(task);
         return true;
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java	(revision 12634)
@@ -44,4 +44,5 @@
 import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
 import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.SideButton;
 import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetCacheManager;
@@ -358,5 +359,5 @@
                 return;
             ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
-            Main.worker.submit(new PostDownloadHandler(task, task.download()));
+            MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
         }
 
@@ -393,5 +394,5 @@
             if (sel.isEmpty())
                 return;
-            Main.worker.submit(new CloseChangesetTask(sel));
+            MainApplication.worker.submit(new CloseChangesetTask(sel));
         }
 
@@ -515,5 +516,5 @@
             } else {
                 task = new ChangesetHeaderDownloadTask(toDownload);
-                future = Main.worker.submit(new PostDownloadHandler(task, task.download()));
+                future = MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
             }
 
@@ -543,5 +544,5 @@
                 GuiHelper.runInEDT(() -> launchChangesetManager(sel));
             };
-            Main.worker.submit(r);
+            MainApplication.worker.submit(r);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/MapPaintDialog.java	(revision 12634)
@@ -57,4 +57,5 @@
 import org.openstreetmap.josm.actions.PreferencesAction;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.SideButton;
@@ -417,5 +418,5 @@
             final int[] rows = tblStyles.getSelectedRows();
             MapPaintStyles.reloadStyles(rows);
-            Main.worker.submit(() -> SwingUtilities.invokeLater(() -> {
+            MainApplication.worker.submit(() -> SwingUtilities.invokeLater(() -> {
                 selectionModel.setValueIsAdjusting(true);
                 selectionModel.clearSelection();
@@ -461,5 +462,5 @@
             if (fc == null)
                 return;
-            Main.worker.submit(new SaveToFileTask(s, fc.getSelectedFile()));
+            MainApplication.worker.submit(new SaveToFileTask(s, fc.getSelectedFile()));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/ValidatorDialog.java	(revision 12634)
@@ -237,5 +237,5 @@
         //
         FixTask fixTask = new FixTask(errorsToFix);
-        Main.worker.submit(fixTask);
+        MainApplication.worker.submit(fixTask);
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java	(revision 12634)
@@ -54,4 +54,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.JosmUserIdentityManager;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.changeset.query.ChangesetQueryDialog;
 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
@@ -459,5 +460,5 @@
         @Override
         public void actionPerformed(ActionEvent e) {
-            Main.worker.submit(new CloseChangesetTask(model.getSelectedChangesets()));
+            MainApplication.worker.submit(new CloseChangesetTask(model.getSelectedChangesets()));
         }
 
@@ -568,5 +569,5 @@
             if (!GraphicsEnvironment.isHeadless()) {
                 actDownloadSelectedContent.actionPerformed(e);
-                Main.worker.submit(() -> {
+                MainApplication.worker.submit(() -> {
                     final List<PrimitiveId> primitiveIds = model.getSelectedChangesets().stream()
                             .map(Changeset::getContent)
@@ -754,6 +755,6 @@
      */
     public void runDownloadTask(final AbstractChangesetDownloadTask task) {
-        Main.worker.submit(new PostDownloadHandler(task, task.download()));
-        Main.worker.submit(() -> {
+        MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
+        MainApplication.worker.submit(() -> {
             if (task.isCanceled() || task.isFailed())
                 return;
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetContentPanel.java	(revision 12634)
@@ -43,4 +43,5 @@
 import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.history.HistoryBrowserDialogManager;
@@ -268,8 +269,8 @@
                     task.add(p);
                 }
-                Main.worker.submit(task);
-            }
-
-            Main.worker.submit(new ShowHistoryTask(primitives));
+                MainApplication.worker.submit(task);
+            }
+
+            MainApplication.worker.submit(new ShowHistoryTask(primitives));
         }
 
@@ -304,5 +305,5 @@
             final List<PrimitiveId> primitiveIds = model.getSelectedPrimitives().stream().map(HistoryOsmPrimitive::getPrimitiveId)
                     .collect(Collectors.toList());
-            Main.worker.submit(new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null));
+            MainApplication.worker.submit(new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDetailPanel.java	(revision 12634)
@@ -36,4 +36,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
@@ -316,5 +317,5 @@
                     Collections.singleton(currentChangeset.getId())
             );
-            Main.worker.submit(new PostDownloadHandler(task, task.download()));
+            MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetDiscussionPanel.java	(revision 12634)
@@ -24,4 +24,5 @@
 import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
 import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.io.OnlineResource;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -78,5 +79,5 @@
                     true /* include discussion */
             );
-            Main.worker.submit(new PostDownloadHandler(task, task.download()));
+            MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java	(revision 12634)
@@ -1172,5 +1172,5 @@
                 }
 
-                Main.worker.execute(() -> displayHelp(uris));
+                MainApplication.worker.execute(() -> displayHelp(uris));
             } catch (URISyntaxException e1) {
                 Logging.error(e1);
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(revision 12634)
@@ -227,5 +227,5 @@
 
         public void run() {
-            Main.worker.submit(new DownloadAllChildrenTask(getParentDialog(), (Relation) model.getRoot()));
+            MainApplication.worker.submit(new DownloadAllChildrenTask(getParentDialog(), (Relation) model.getRoot()));
         }
 
@@ -264,5 +264,5 @@
                 relations.add((Relation) aSelection.getLastPathComponent());
             }
-            Main.worker.submit(new DownloadRelationSetTask(getParentDialog(), relations));
+            MainApplication.worker.submit(new DownloadRelationSetTask(getParentDialog(), relations));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ParentRelationLoadingTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ParentRelationLoadingTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ParentRelationLoadingTask.java	(revision 12634)
@@ -48,5 +48,5 @@
  *
  *   // start the task
- *   Main.worker.submit(task);
+ *   MainApplication.worker.submit(task);
  * </pre>
  *
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/ReferringRelationsBrowser.java	(revision 12634)
@@ -22,6 +22,6 @@
 import javax.swing.event.ListSelectionListener;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
@@ -115,5 +115,5 @@
                     model.populate(task.getParents());
                 });
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/RelationTree.java	(revision 12634)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
@@ -95,5 +96,5 @@
                 return;
             // launch the download task
-            Main.worker.submit(new RelationLoader(getParentDialog(), parent, path));
+            MainApplication.worker.submit(new RelationLoader(getParentDialog(), parent, path));
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadIncompleteMembersAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadIncompleteMembersAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadIncompleteMembersAction.java	(revision 12634)
@@ -9,8 +9,9 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
+import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
 import org.openstreetmap.josm.gui.dialogs.relation.MemberTable;
 import org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel;
-import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.io.OnlineResource;
@@ -48,5 +49,5 @@
         if (!isEnabled())
             return;
-        Main.worker.submit(new DownloadRelationMemberTask(
+        MainApplication.worker.submit(new DownloadRelationMemberTask(
                 editor.getRelation(),
                 memberTableModel.getIncompleteMemberPrimitives(),
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadSelectedIncompleteMembersAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadSelectedIncompleteMembersAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/relation/actions/DownloadSelectedIncompleteMembersAction.java	(revision 12634)
@@ -8,4 +8,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
 import org.openstreetmap.josm.gui.dialogs.relation.IRelationEditor;
@@ -43,5 +44,5 @@
         if (!isEnabled())
             return;
-        Main.worker.submit(new DownloadRelationMemberTask(
+        MainApplication.worker.submit(new DownloadRelationMemberTask(
                 editor.getRelation(),
                 memberTableModel.getSelectedIncompleteMemberPrimitives(),
Index: trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/download/BookmarkList.java	(revision 12634)
@@ -33,4 +33,5 @@
 import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.JosmUserIdentityManager;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapViewState;
 import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetCacheManager;
@@ -334,5 +335,5 @@
                 final ChangesetQueryTask task = new ChangesetQueryTask(this, query);
                 ChangesetCacheManager.getInstance().runDownloadTask(task);
-                Main.worker.submit(() -> {
+                MainApplication.worker.submit(() -> {
                     if (task.isCanceled() || task.isFailed())
                         return;
Index: trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/download/PlaceSelection.java	(revision 12634)
@@ -47,4 +47,5 @@
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.util.GuiHelper;
@@ -190,5 +191,5 @@
             Main.pref.putCollection(HISTORY_KEY, cbSearchExpression.getHistory());
             NameQueryTask task = new NameQueryTask(cbSearchExpression.getText());
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialog.java	(revision 12634)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
 import org.openstreetmap.josm.data.osm.history.HistoryDataSetListener;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
 import org.openstreetmap.josm.gui.help.HelpUtil;
@@ -182,5 +183,5 @@
             HistoryLoadTask task = new HistoryLoadTask();
             task.add(browser.getHistory());
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/history/HistoryBrowserDialogManager.java	(revision 12634)
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.data.osm.history.History;
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
 import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
@@ -213,5 +214,5 @@
                 task.add(p);
             }
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
 
@@ -229,5 +230,5 @@
             }
         };
-        Main.worker.submit(r);
+        MainApplication.worker.submit(r);
     }
 }
Index: trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/history/HistoryLoadTask.java	(revision 12634)
@@ -43,5 +43,5 @@
  *      .add(aHistoryItem);
  *
- *   Main.worker.execute(task);
+ *   MainApplication.worker.execute(task);
  * </pre>
  */
Index: trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/history/NodeListViewer.java	(revision 12634)
@@ -29,4 +29,5 @@
 import org.openstreetmap.josm.data.osm.history.History;
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.util.AdjustmentSynchronizer;
@@ -302,7 +303,7 @@
         public void run() {
             if (HistoryDataSet.getInstance().getHistory(primitiveId) == null) {
-                Main.worker.submit(new HistoryLoadTask().add(primitiveId));
+                MainApplication.worker.submit(new HistoryLoadTask().add(primitiveId));
             }
-            Main.worker.submit(() -> {
+            MainApplication.worker.submit(() -> {
                 final History h = HistoryDataSet.getInstance().getHistory(primitiveId);
                 if (h == null)
Index: trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/io/AbstractUploadTask.java	(revision 12634)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
@@ -92,5 +93,5 @@
             throw new IllegalStateException(
                     tr("Failed to update primitive with id {0} because current edit layer does not include such a primitive", id));
-        Main.worker.execute(new UpdatePrimitivesTask(layer, Collections.singleton(p)));
+        MainApplication.worker.execute(new UpdatePrimitivesTask(layer, Collections.singleton(p)));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/io/ChangesetManagementPanel.java	(revision 12634)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.data.osm.Changeset;
 import org.openstreetmap.josm.data.osm.ChangesetCache;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
@@ -299,6 +300,5 @@
         @Override
         public void actionPerformed(ActionEvent e) {
-            DownloadOpenChangesetsTask task = new DownloadOpenChangesetsTask(ChangesetManagementPanel.this);
-            Main.worker.submit(task);
+            MainApplication.worker.submit(new DownloadOpenChangesetsTask(ChangesetManagementPanel.this));
         }
     }
@@ -319,6 +319,5 @@
             Changeset cs = (Changeset) cbOpenChangesets.getSelectedItem();
             if (cs == null) return;
-            CloseChangesetTask task = new CloseChangesetTask(Collections.singletonList(cs));
-            Main.worker.submit(task);
+            MainApplication.worker.submit(new CloseChangesetTask(Collections.singletonList(cs)));
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/io/CustomConfigurator.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/CustomConfigurator.java	(revision 12634)
+++ trunk/src/org/openstreetmap/josm/gui/io/CustomConfigurator.java	(revision 12634)
@@ -0,0 +1,748 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.io;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.PreferencesUtils;
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.data.preferences.Setting;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.plugins.PluginDownloadTask;
+import org.openstreetmap.josm.plugins.PluginInformation;
+import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
+import org.openstreetmap.josm.tools.LanguageInfo;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+/**
+ * Class to process configuration changes stored in XML
+ * can be used to modify preferences, store/delete files in .josm folders etc
+ */
+public final class CustomConfigurator {
+
+    private static StringBuilder summary = new StringBuilder();
+
+    private CustomConfigurator() {
+        // Hide default constructor for utils classes
+    }
+
+    /**
+     * Log a formatted message.
+     * @param fmt format
+     * @param vars arguments
+     * @see String#format
+     */
+    public static void log(String fmt, Object... vars) {
+        summary.append(String.format(fmt, vars));
+    }
+
+    /**
+     * Log a message.
+     * @param s message to log
+     */
+    public static void log(String s) {
+        summary.append(s).append('\n');
+    }
+
+    /**
+     * Log an exception.
+     * @param e exception to log
+     * @param s message prefix
+     * @since 10469
+     */
+    public static void log(Exception e, String s) {
+        summary.append(s).append(' ').append(Logging.getErrorMessage(e)).append('\n');
+    }
+
+    /**
+     * Returns the log.
+     * @return the log
+     */
+    public static String getLog() {
+        return summary.toString();
+    }
+
+    /**
+     * Resets the log.
+     */
+    public static void resetLog() {
+        summary = new StringBuilder();
+    }
+
+    /**
+     * Read configuration script from XML file, modifying main preferences
+     * @param dir - directory
+     * @param fileName - XML file name
+     */
+    public static void readXML(String dir, String fileName) {
+        readXML(new File(dir, fileName));
+    }
+
+    /**
+     * Read configuration script from XML file, modifying given preferences object
+     * @param file - file to open for reading XML
+     * @param prefs - arbitrary Preferences object to modify by script
+     */
+    public static void readXML(final File file, final Preferences prefs) {
+        synchronized (CustomConfigurator.class) {
+            busy = true;
+        }
+        new XMLCommandProcessor(prefs).openAndReadXML(file);
+        synchronized (CustomConfigurator.class) {
+            CustomConfigurator.class.notifyAll();
+            busy = false;
+        }
+    }
+
+    /**
+     * Read configuration script from XML file, modifying main preferences
+     * @param file - file to open for reading XML
+     */
+    public static void readXML(File file) {
+        readXML(file, Main.pref);
+    }
+
+    /**
+     * Downloads file to one of JOSM standard folders
+     * @param address - URL to download
+     * @param path - file path relative to base where to put downloaded file
+     * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
+     */
+    public static void downloadFile(String address, String path, String base) {
+        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, false);
+    }
+
+    /**
+     * Downloads file to one of JOSM standard folders and unpack it as ZIP/JAR file
+     * @param address - URL to download
+     * @param path - file path relative to base where to put downloaded file
+     * @param base - only "prefs", "cache" and "plugins" allowed for standard folders
+     */
+    public static void downloadAndUnpackFile(String address, String path, String base) {
+        processDownloadOperation(address, path, getDirectoryByAbbr(base), true, true);
+    }
+
+    /**
+     * Downloads file to arbitrary folder
+     * @param address - URL to download
+     * @param path - file path relative to parentDir where to put downloaded file
+     * @param parentDir - folder where to put file
+     * @param mkdir - if true, non-existing directories will be created
+     * @param unzip - if true file wil be unzipped and deleted after download
+     */
+    public static void processDownloadOperation(String address, String path, String parentDir, boolean mkdir, boolean unzip) {
+        String dir = parentDir;
+        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+            return; // some basic protection
+        }
+        File fOut = new File(dir, path);
+        DownloadFileTask downloadFileTask = new DownloadFileTask(Main.parent, address, fOut, mkdir, unzip);
+
+        MainApplication.worker.submit(downloadFileTask);
+        log("Info: downloading file from %s to %s in background ", parentDir, fOut.getAbsolutePath());
+        if (unzip) log("and unpacking it"); else log("");
+
+    }
+
+    /**
+     * Simple function to show messageBox, may be used from JS API and from other code
+     * @param type - 'i','w','e','q','p' for Information, Warning, Error, Question, Message
+     * @param text - message to display, HTML allowed
+     */
+    public static void messageBox(String type, String text) {
+        char c = (type == null || type.isEmpty() ? "plain" : type).charAt(0);
+        switch (c) {
+            case 'i': JOptionPane.showMessageDialog(Main.parent, text, tr("Information"), JOptionPane.INFORMATION_MESSAGE); break;
+            case 'w': JOptionPane.showMessageDialog(Main.parent, text, tr("Warning"), JOptionPane.WARNING_MESSAGE); break;
+            case 'e': JOptionPane.showMessageDialog(Main.parent, text, tr("Error"), JOptionPane.ERROR_MESSAGE); break;
+            case 'q': JOptionPane.showMessageDialog(Main.parent, text, tr("Question"), JOptionPane.QUESTION_MESSAGE); break;
+            case 'p': JOptionPane.showMessageDialog(Main.parent, text, tr("Message"), JOptionPane.PLAIN_MESSAGE); break;
+            default: Logging.warn("Unsupported messageBox type: " + c);
+        }
+    }
+
+    /**
+     * Simple function for choose window, may be used from JS API and from other code
+     * @param text - message to show, HTML allowed
+     * @param opts -
+     * @return number of pressed button, -1 if cancelled
+     */
+    public static int askForOption(String text, String opts) {
+        if (!opts.isEmpty()) {
+            return JOptionPane.showOptionDialog(Main.parent, text, "Question",
+                    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, opts.split(";"), 0);
+        } else {
+            return JOptionPane.showOptionDialog(Main.parent, text, "Question",
+                    JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, 2);
+        }
+    }
+
+    public static String askForText(String text) {
+        String s = JOptionPane.showInputDialog(Main.parent, text, tr("Enter text"), JOptionPane.QUESTION_MESSAGE);
+        return s != null ? s.trim() : null;
+    }
+
+    /**
+     * This function exports part of user preferences to specified file.
+     * Default values are not saved.
+     * @param filename - where to export
+     * @param append - if true, resulting file cause appending to exuisting preferences
+     * @param keys - which preferences keys you need to export ("imagery.entries", for example)
+     */
+    public static void exportPreferencesKeysToFile(String filename, boolean append, String... keys) {
+        Set<String> keySet = new HashSet<>();
+        Collections.addAll(keySet, keys);
+        exportPreferencesKeysToFile(filename, append, keySet);
+    }
+
+    /**
+     * This function exports part of user preferences to specified file.
+     * Default values are not saved.
+     * Preference keys matching specified pattern are saved
+     * @param fileName - where to export
+     * @param append - if true, resulting file cause appending to exuisting preferences
+     * @param pattern - Regexp pattern forh preferences keys you need to export (".*imagery.*", for example)
+     */
+    public static void exportPreferencesKeysByPatternToFile(String fileName, boolean append, String pattern) {
+        List<String> keySet = new ArrayList<>();
+        Map<String, Setting<?>> allSettings = Main.pref.getAllSettings();
+        for (String key: allSettings.keySet()) {
+            if (key.matches(pattern))
+                keySet.add(key);
+        }
+        exportPreferencesKeysToFile(fileName, append, keySet);
+    }
+
+    /**
+     * Export specified preferences keys to configuration file
+     * @param filename - name of file
+     * @param append - will the preferences be appended to existing ones when file is imported later.
+     * Elsewhere preferences from file will replace existing keys.
+     * @param keys - collection of preferences key names to save
+     */
+    public static void exportPreferencesKeysToFile(String filename, boolean append, Collection<String> keys) {
+        Element root = null;
+        Document document = null;
+        Document exportDocument = null;
+
+        try {
+            String toXML = Main.pref.toXML(true);
+            DocumentBuilder builder = Utils.newSafeDOMBuilder();
+            document = builder.parse(new ByteArrayInputStream(toXML.getBytes(StandardCharsets.UTF_8)));
+            exportDocument = builder.newDocument();
+            root = document.getDocumentElement();
+        } catch (SAXException | IOException | ParserConfigurationException ex) {
+            Logging.log(Logging.LEVEL_WARN, "Error getting preferences to save:", ex);
+        }
+        if (root == null || exportDocument == null)
+            return;
+        try {
+            Element newRoot = exportDocument.createElement("config");
+            exportDocument.appendChild(newRoot);
+
+            Element prefElem = exportDocument.createElement("preferences");
+            prefElem.setAttribute("operation", append ? "append" : "replace");
+            newRoot.appendChild(prefElem);
+
+            NodeList childNodes = root.getChildNodes();
+            int n = childNodes.getLength();
+            for (int i = 0; i < n; i++) {
+                Node item = childNodes.item(i);
+                if (item.getNodeType() == Node.ELEMENT_NODE) {
+                    String currentKey = ((Element) item).getAttribute("key");
+                    if (keys.contains(currentKey)) {
+                        Node imported = exportDocument.importNode(item, true);
+                        prefElem.appendChild(imported);
+                    }
+                }
+            }
+            File f = new File(filename);
+            Transformer ts = TransformerFactory.newInstance().newTransformer();
+            ts.setOutputProperty(OutputKeys.INDENT, "yes");
+            ts.transform(new DOMSource(exportDocument), new StreamResult(f.toURI().getPath()));
+        } catch (DOMException | TransformerFactoryConfigurationError | TransformerException ex) {
+            Logging.warn("Error saving preferences part:");
+            Logging.error(ex);
+        }
+    }
+
+    public static void deleteFile(String path, String base) {
+        String dir = getDirectoryByAbbr(base);
+        if (dir == null) {
+            log("Error: Can not find base, use base=cache, base=prefs or base=plugins attribute.");
+            return;
+        }
+        log("Delete file: %s\n", path);
+        if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+            return; // some basic protection
+        }
+        File fOut = new File(dir, path);
+        if (fOut.exists()) {
+            deleteFileOrDirectory(fOut);
+        }
+    }
+
+    public static void deleteFileOrDirectory(File f) {
+        if (f.isDirectory()) {
+            File[] files = f.listFiles();
+            if (files != null) {
+                for (File f1: files) {
+                    deleteFileOrDirectory(f1);
+                }
+            }
+        }
+        if (!Utils.deleteFile(f)) {
+            log("Warning: Can not delete file "+f.getPath());
+        }
+    }
+
+    private static boolean busy;
+
+    public static void pluginOperation(String install, String uninstall, String delete) {
+        final List<String> installList = new ArrayList<>();
+        final List<String> removeList = new ArrayList<>();
+        final List<String> deleteList = new ArrayList<>();
+        Collections.addAll(installList, install.toLowerCase(Locale.ENGLISH).split(";"));
+        Collections.addAll(removeList, uninstall.toLowerCase(Locale.ENGLISH).split(";"));
+        Collections.addAll(deleteList, delete.toLowerCase(Locale.ENGLISH).split(";"));
+        installList.remove("");
+        removeList.remove("");
+        deleteList.remove("");
+
+        if (!installList.isEmpty()) {
+            log("Plugins install: "+installList);
+        }
+        if (!removeList.isEmpty()) {
+            log("Plugins turn off: "+removeList);
+        }
+        if (!deleteList.isEmpty()) {
+            log("Plugins delete: "+deleteList);
+        }
+
+        final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
+        Runnable r = () -> {
+            if (task.isCanceled()) return;
+            synchronized (CustomConfigurator.class) {
+                try { // proceed only after all other tasks were finished
+                    while (busy) CustomConfigurator.class.wait();
+                } catch (InterruptedException ex) {
+                    Logging.log(Logging.LEVEL_WARN, "InterruptedException while reading local plugin information", ex);
+                    Thread.currentThread().interrupt();
+                }
+
+                SwingUtilities.invokeLater(() -> {
+                    List<PluginInformation> availablePlugins = task.getAvailablePlugins();
+                    List<PluginInformation> toInstallPlugins = new ArrayList<>();
+                    List<PluginInformation> toRemovePlugins = new ArrayList<>();
+                    List<PluginInformation> toDeletePlugins = new ArrayList<>();
+                    for (PluginInformation pi1: availablePlugins) {
+                        String name = pi1.name.toLowerCase(Locale.ENGLISH);
+                        if (installList.contains(name)) toInstallPlugins.add(pi1);
+                        if (removeList.contains(name)) toRemovePlugins.add(pi1);
+                        if (deleteList.contains(name)) toDeletePlugins.add(pi1);
+                    }
+                    if (!installList.isEmpty()) {
+                        PluginDownloadTask pluginDownloadTask =
+                                new PluginDownloadTask(Main.parent, toInstallPlugins, tr("Installing plugins"));
+                        MainApplication.worker.submit(pluginDownloadTask);
+                    }
+                    Collection<String> pls = new ArrayList<>(Main.pref.getCollection("plugins"));
+                    for (PluginInformation pi2: toInstallPlugins) {
+                        if (!pls.contains(pi2.name)) {
+                            pls.add(pi2.name);
+                        }
+                    }
+                    for (PluginInformation pi3: toRemovePlugins) {
+                        pls.remove(pi3.name);
+                    }
+                    for (PluginInformation pi4: toDeletePlugins) {
+                        pls.remove(pi4.name);
+                        new File(Main.pref.getPluginsDirectory(), pi4.name+".jar").deleteOnExit();
+                    }
+                    Main.pref.putCollection("plugins", pls);
+                });
+            }
+        };
+        MainApplication.worker.submit(task);
+        MainApplication.worker.submit(r);
+    }
+
+    private static String getDirectoryByAbbr(String base) {
+        String dir;
+        if ("prefs".equals(base) || base.isEmpty()) {
+            dir = Main.pref.getPreferencesDirectory().getAbsolutePath();
+        } else if ("cache".equals(base)) {
+            dir = Main.pref.getCacheDirectory().getAbsolutePath();
+        } else if ("plugins".equals(base)) {
+            dir = Main.pref.getPluginsDirectory().getAbsolutePath();
+        } else {
+            dir = null;
+        }
+        return dir;
+    }
+
+    public static class XMLCommandProcessor {
+
+        private Preferences mainPrefs;
+        private final Map<String, Element> tasksMap = new HashMap<>();
+
+        private boolean lastV; // last If condition result
+
+        private ScriptEngine engine;
+
+        public void openAndReadXML(File file) {
+            log("-- Reading custom preferences from " + file.getAbsolutePath() + " --");
+            try {
+                String fileDir = file.getParentFile().getAbsolutePath();
+                if (fileDir != null) engine.eval("scriptDir='"+normalizeDirName(fileDir) +"';");
+                try (InputStream is = new BufferedInputStream(new FileInputStream(file))) {
+                    openAndReadXML(is);
+                }
+            } catch (ScriptException | IOException | SecurityException ex) {
+                log(ex, "Error reading custom preferences:");
+            }
+        }
+
+        public void openAndReadXML(InputStream is) {
+            try {
+                Document document = Utils.parseSafeDOM(is);
+                synchronized (CustomConfigurator.class) {
+                    processXML(document);
+                }
+            } catch (SAXException | IOException | ParserConfigurationException ex) {
+                log(ex, "Error reading custom preferences:");
+            }
+            log("-- Reading complete --");
+        }
+
+        public XMLCommandProcessor(Preferences mainPrefs) {
+            try {
+                this.mainPrefs = mainPrefs;
+                resetLog();
+                engine = new ScriptEngineManager().getEngineByName("JavaScript");
+                engine.eval("API={}; API.pref={}; API.fragments={};");
+
+                engine.eval("homeDir='"+normalizeDirName(Main.pref.getPreferencesDirectory().getAbsolutePath()) +"';");
+                engine.eval("josmVersion="+Version.getInstance().getVersion()+';');
+                String className = CustomConfigurator.class.getName();
+                engine.eval("API.messageBox="+className+".messageBox");
+                engine.eval("API.askText=function(text) { return String("+className+".askForText(text));}");
+                engine.eval("API.askOption="+className+".askForOption");
+                engine.eval("API.downloadFile="+className+".downloadFile");
+                engine.eval("API.downloadAndUnpackFile="+className+".downloadAndUnpackFile");
+                engine.eval("API.deleteFile="+className+".deleteFile");
+                engine.eval("API.plugin ="+className+".pluginOperation");
+                engine.eval("API.pluginInstall = function(names) { "+className+".pluginOperation(names,'','');}");
+                engine.eval("API.pluginUninstall = function(names) { "+className+".pluginOperation('',names,'');}");
+                engine.eval("API.pluginDelete = function(names) { "+className+".pluginOperation('','',names);}");
+            } catch (ScriptException ex) {
+                log("Error: initializing script engine: "+ex.getMessage());
+                Logging.error(ex);
+            }
+        }
+
+        private void processXML(Document document) {
+            processXmlFragment(document.getDocumentElement());
+        }
+
+        private void processXmlFragment(Element root) {
+            NodeList childNodes = root.getChildNodes();
+            int nops = childNodes.getLength();
+            for (int i = 0; i < nops; i++) {
+                Node item = childNodes.item(i);
+                if (item.getNodeType() != Node.ELEMENT_NODE) continue;
+                String elementName = item.getNodeName();
+                Element elem = (Element) item;
+
+                switch(elementName) {
+                case "var":
+                    setVar(elem.getAttribute("name"), evalVars(elem.getAttribute("value")));
+                    break;
+                case "task":
+                    tasksMap.put(elem.getAttribute("name"), elem);
+                    break;
+                case "runtask":
+                    if (processRunTaskElement(elem)) return;
+                    break;
+                case "ask":
+                    processAskElement(elem);
+                    break;
+                case "if":
+                    processIfElement(elem);
+                    break;
+                case "else":
+                    processElseElement(elem);
+                    break;
+                case "break":
+                    return;
+                case "plugin":
+                    processPluginInstallElement(elem);
+                    break;
+                case "messagebox":
+                    processMsgBoxElement(elem);
+                    break;
+                case "preferences":
+                    processPreferencesElement(elem);
+                    break;
+                case "download":
+                    processDownloadElement(elem);
+                    break;
+                case "delete":
+                    processDeleteElement(elem);
+                    break;
+                case "script":
+                    processScriptElement(elem);
+                    break;
+                default:
+                    log("Error: Unknown element " + elementName);
+                }
+            }
+        }
+
+        private void processPreferencesElement(Element item) {
+            String oper = evalVars(item.getAttribute("operation"));
+            String id = evalVars(item.getAttribute("id"));
+
+            if ("delete-keys".equals(oper)) {
+                String pattern = evalVars(item.getAttribute("pattern"));
+                String key = evalVars(item.getAttribute("key"));
+                PreferencesUtils.deletePreferenceKey(key, mainPrefs);
+                PreferencesUtils.deletePreferenceKeyByPattern(pattern, mainPrefs);
+                return;
+            }
+
+            Preferences tmpPref = readPreferencesFromDOMElement(item);
+            PreferencesUtils.showPrefs(tmpPref);
+
+            if (!id.isEmpty()) {
+                try {
+                    String fragmentVar = "API.fragments['"+id+"']";
+                    engine.eval(fragmentVar+"={};");
+                    PreferencesUtils.loadPrefsToJS(engine, tmpPref, fragmentVar, false);
+                    // we store this fragment as API.fragments['id']
+                } catch (ScriptException ex) {
+                    log(ex, "Error: can not load preferences fragment:");
+                }
+            }
+
+            if ("replace".equals(oper)) {
+                log("Preferences replace: %d keys: %s\n",
+                   tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
+                PreferencesUtils.replacePreferences(tmpPref, mainPrefs);
+            } else if ("append".equals(oper)) {
+                log("Preferences append: %d keys: %s\n",
+                   tmpPref.getAllSettings().size(), tmpPref.getAllSettings().keySet().toString());
+                PreferencesUtils.appendPreferences(tmpPref, mainPrefs);
+            } else if ("delete-values".equals(oper)) {
+                PreferencesUtils.deletePreferenceValues(tmpPref, mainPrefs);
+            }
+        }
+
+         private void processDeleteElement(Element item) {
+            String path = evalVars(item.getAttribute("path"));
+            String base = evalVars(item.getAttribute("base"));
+            deleteFile(path, base);
+        }
+
+        private void processDownloadElement(Element item) {
+            String base = evalVars(item.getAttribute("base"));
+            String dir = getDirectoryByAbbr(base);
+            if (dir == null) {
+                log("Error: Can not find directory to place file, use base=cache, base=prefs or base=plugins attribute.");
+                return;
+            }
+
+            String path = evalVars(item.getAttribute("path"));
+            if (path.contains("..") || path.startsWith("/") || path.contains(":")) {
+                return; // some basic protection
+            }
+
+            String address = evalVars(item.getAttribute("url"));
+            if (address.isEmpty() || path.isEmpty()) {
+                log("Error: Please specify url=\"where to get file\" and path=\"where to place it\"");
+                return;
+            }
+
+            String unzip = evalVars(item.getAttribute("unzip"));
+            String mkdir = evalVars(item.getAttribute("mkdir"));
+            processDownloadOperation(address, path, dir, "true".equals(mkdir), "true".equals(unzip));
+        }
+
+        private static void processPluginInstallElement(Element elem) {
+            String install = elem.getAttribute("install");
+            String uninstall = elem.getAttribute("remove");
+            String delete = elem.getAttribute("delete");
+            pluginOperation(install, uninstall, delete);
+        }
+
+        private void processMsgBoxElement(Element elem) {
+            String text = evalVars(elem.getAttribute("text"));
+            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
+            if (!locText.isEmpty()) text = locText;
+
+            String type = evalVars(elem.getAttribute("type"));
+            messageBox(type, text);
+        }
+
+        private void processAskElement(Element elem) {
+            String text = evalVars(elem.getAttribute("text"));
+            String locText = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".text"));
+            if (!locText.isEmpty()) text = locText;
+            String var = elem.getAttribute("var");
+            if (var.isEmpty()) var = "result";
+
+            String input = evalVars(elem.getAttribute("input"));
+            if ("true".equals(input)) {
+                setVar(var, askForText(text));
+            } else {
+                String opts = evalVars(elem.getAttribute("options"));
+                String locOpts = evalVars(elem.getAttribute(LanguageInfo.getJOSMLocaleCode()+".options"));
+                if (!locOpts.isEmpty()) opts = locOpts;
+                setVar(var, String.valueOf(askForOption(text, opts)));
+            }
+        }
+
+        public void setVar(String name, String value) {
+            try {
+                engine.eval(name+"='"+value+"';");
+            } catch (ScriptException ex) {
+                log(ex, String.format("Error: Can not assign variable: %s=%s :", name, value));
+            }
+        }
+
+        private void processIfElement(Element elem) {
+            String realValue = evalVars(elem.getAttribute("test"));
+            boolean v = false;
+            if ("true".equals(realValue) || "false".equals(realValue)) {
+                processXmlFragment(elem);
+                v = true;
+            } else {
+                log("Error: Illegal test expression in if: %s=%s\n", elem.getAttribute("test"), realValue);
+            }
+
+            lastV = v;
+        }
+
+        private void processElseElement(Element elem) {
+            if (!lastV) {
+                processXmlFragment(elem);
+            }
+        }
+
+        private boolean processRunTaskElement(Element elem) {
+            String taskName = elem.getAttribute("name");
+            Element task = tasksMap.get(taskName);
+            if (task != null) {
+                log("EXECUTING TASK "+taskName);
+                processXmlFragment(task); // process task recursively
+            } else {
+                log("Error: Can not execute task "+taskName);
+                return true;
+            }
+            return false;
+        }
+
+        private void processScriptElement(Element elem) {
+            String js = elem.getChildNodes().item(0).getTextContent();
+            log("Processing script...");
+            try {
+                PreferencesUtils.modifyPreferencesByScript(engine, mainPrefs, js);
+            } catch (ScriptException ex) {
+                messageBox("e", ex.getMessage());
+                log(ex, "JS error:");
+            }
+            log("Script finished");
+        }
+
+        /**
+         * substitute ${expression} = expression evaluated by JavaScript
+         * @param s string
+         * @return evaluation result
+         */
+        private String evalVars(String s) {
+            Matcher mr = Pattern.compile("\\$\\{([^\\}]*)\\}").matcher(s);
+            StringBuffer sb = new StringBuffer();
+            while (mr.find()) {
+                try {
+                    String result = engine.eval(mr.group(1)).toString();
+                    mr.appendReplacement(sb, result);
+                } catch (ScriptException ex) {
+                    log(ex, String.format("Error: Can not evaluate expression %s :", mr.group(1)));
+                }
+            }
+            mr.appendTail(sb);
+            return sb.toString();
+        }
+
+        private Preferences readPreferencesFromDOMElement(Element item) {
+            Preferences tmpPref = new Preferences();
+            try {
+                Transformer xformer = TransformerFactory.newInstance().newTransformer();
+                CharArrayWriter outputWriter = new CharArrayWriter(8192);
+                StreamResult out = new StreamResult(outputWriter);
+
+                xformer.transform(new DOMSource(item), out);
+
+                String fragmentWithReplacedVars = evalVars(outputWriter.toString());
+
+                CharArrayReader reader = new CharArrayReader(fragmentWithReplacedVars.toCharArray());
+                tmpPref.fromXML(reader);
+            } catch (TransformerException | XMLStreamException | IOException ex) {
+                log(ex, "Error: can not read XML fragment:");
+            }
+
+            return tmpPref;
+        }
+
+        private static String normalizeDirName(String dir) {
+            String s = dir.replace('\\', '/');
+            if (s.endsWith("/")) s = s.substring(0, s.length()-1);
+            return s;
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/io/RecentlyOpenedFilesMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/io/RecentlyOpenedFilesMenu.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/io/RecentlyOpenedFilesMenu.java	(revision 12634)
@@ -19,4 +19,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.OpenFileAction.OpenFileTask;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.tools.ImageProvider;
 
@@ -85,5 +86,5 @@
             OpenFileTask task = new OpenFileTask(Collections.singletonList(new File(file)), null);
             task.setRecordHistory(true);
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(revision 12634)
+++ trunk/src/org/openstreetmap/josm/gui/layer/AutosaveTask.java	(revision 12634)
@@ -0,0 +1,430 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.layer;
+
+import static org.openstreetmap.josm.tools.I18n.marktr;
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.management.ManagementFactory;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.OpenFileAction.OpenFileTask;
+import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
+import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter;
+import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter.Listener;
+import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.gui.Notification;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
+import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.OsmExporter;
+import org.openstreetmap.josm.io.OsmImporter;
+import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
+
+/**
+ * Saves data layers periodically so they can be recovered in case of a crash.
+ *
+ * There are 2 directories
+ *  - autosave dir: copies of the currently open data layers are saved here every
+ *      PROP_INTERVAL seconds. When a data layer is closed normally, the corresponding
+ *      files are removed. If this dir is non-empty on start, JOSM assumes
+ *      that it crashed last time.
+ *  - deleted layers dir: "secondary archive" - when autosaved layers are restored
+ *      they are copied to this directory. We cannot keep them in the autosave folder,
+ *      but just deleting it would be dangerous: Maybe a feature inside the file
+ *      caused JOSM to crash. If the data is valuable, the user can still try to
+ *      open with another versions of JOSM or fix the problem manually.
+ *
+ *      The deleted layers dir keeps at most PROP_DELETED_LAYERS files.
+ *
+ * @since  3378 (creation)
+ * @since 10386 (new LayerChangeListener interface)
+ */
+public class AutosaveTask extends TimerTask implements LayerChangeListener, Listener {
+
+    private static final char[] ILLEGAL_CHARACTERS = {'/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':'};
+    private static final String AUTOSAVE_DIR = "autosave";
+    private static final String DELETED_LAYERS_DIR = "autosave/deleted_layers";
+
+    /**
+     * If autosave is enabled
+     */
+    public static final BooleanProperty PROP_AUTOSAVE_ENABLED = new BooleanProperty("autosave.enabled", true);
+    /**
+     * The number of files to store per layer
+     */
+    public static final IntegerProperty PROP_FILES_PER_LAYER = new IntegerProperty("autosave.filesPerLayer", 1);
+    /**
+     * How many deleted layers should be stored
+     */
+    public static final IntegerProperty PROP_DELETED_LAYERS = new IntegerProperty("autosave.deletedLayersBackupCount", 5);
+    /**
+     * The autosave interval, in seconds
+     */
+    public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("autosave.interval", (int) TimeUnit.MINUTES.toSeconds(5));
+    /**
+     * The maximum number of autosave files to store
+     */
+    public static final IntegerProperty PROP_INDEX_LIMIT = new IntegerProperty("autosave.index-limit", 1000);
+    /**
+     * Defines if a notification should be displayed after each autosave
+     */
+    public static final BooleanProperty PROP_NOTIFICATION = new BooleanProperty("autosave.notification", false);
+
+    protected static final class AutosaveLayerInfo {
+        private final OsmDataLayer layer;
+        private String layerName;
+        private String layerFileName;
+        private final Deque<File> backupFiles = new LinkedList<>();
+
+        AutosaveLayerInfo(OsmDataLayer layer) {
+            this.layer = layer;
+        }
+    }
+
+    private final DataSetListenerAdapter datasetAdapter = new DataSetListenerAdapter(this);
+    private final Set<DataSet> changedDatasets = new HashSet<>();
+    private final List<AutosaveLayerInfo> layersInfo = new ArrayList<>();
+    private final Object layersLock = new Object();
+    private final Deque<File> deletedLayers = new LinkedList<>();
+
+    private final File autosaveDir = new File(Main.pref.getUserDataDirectory(), AUTOSAVE_DIR);
+    private final File deletedLayersDir = new File(Main.pref.getUserDataDirectory(), DELETED_LAYERS_DIR);
+
+    /**
+     * Replies the autosave directory.
+     * @return the autosave directory
+     * @since 10299
+     */
+    public final Path getAutosaveDir() {
+        return autosaveDir.toPath();
+    }
+
+    /**
+     * Starts the autosave background task.
+     */
+    public void schedule() {
+        if (PROP_INTERVAL.get() > 0) {
+
+            if (!autosaveDir.exists() && !autosaveDir.mkdirs()) {
+                Logging.warn(tr("Unable to create directory {0}, autosave will be disabled", autosaveDir.getAbsolutePath()));
+                return;
+            }
+            if (!deletedLayersDir.exists() && !deletedLayersDir.mkdirs()) {
+                Logging.warn(tr("Unable to create directory {0}, autosave will be disabled", deletedLayersDir.getAbsolutePath()));
+                return;
+            }
+
+            File[] files = deletedLayersDir.listFiles();
+            if (files != null) {
+                for (File f: files) {
+                    deletedLayers.add(f); // FIXME: sort by mtime
+                }
+            }
+
+            new Timer(true).schedule(this, TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(PROP_INTERVAL.get()));
+            Main.getLayerManager().addAndFireLayerChangeListener(this);
+        }
+    }
+
+    private static String getFileName(String layerName, int index) {
+        String result = layerName;
+        for (char illegalCharacter : ILLEGAL_CHARACTERS) {
+            result = result.replaceAll(Pattern.quote(String.valueOf(illegalCharacter)),
+                    '&' + String.valueOf((int) illegalCharacter) + ';');
+        }
+        if (index != 0) {
+            result = result + '_' + index;
+        }
+        return result;
+    }
+
+    private void setLayerFileName(AutosaveLayerInfo layer) {
+        int index = 0;
+        while (true) {
+            String filename = getFileName(layer.layer.getName(), index);
+            boolean foundTheSame = false;
+            for (AutosaveLayerInfo info: layersInfo) {
+                if (info != layer && filename.equals(info.layerFileName)) {
+                    foundTheSame = true;
+                    break;
+                }
+            }
+
+            if (!foundTheSame) {
+                layer.layerFileName = filename;
+                return;
+            }
+
+            index++;
+        }
+    }
+
+    protected File getNewLayerFile(AutosaveLayerInfo layer, Date now, int startIndex) {
+        int index = startIndex;
+        while (true) {
+            String filename = String.format("%1$s_%2$tY%2$tm%2$td_%2$tH%2$tM%2$tS%2$tL%3$s",
+                    layer.layerFileName, now, index == 0 ? "" : ('_' + Integer.toString(index)));
+            File result = new File(autosaveDir, filename + '.' + Main.pref.get("autosave.extension", "osm"));
+            try {
+                if (index > PROP_INDEX_LIMIT.get())
+                    throw new IOException("index limit exceeded");
+                if (result.createNewFile()) {
+                    createNewPidFile(autosaveDir, filename);
+                    return result;
+                } else {
+                    Logging.warn(tr("Unable to create file {0}, other filename will be used", result.getAbsolutePath()));
+                }
+            } catch (IOException e) {
+                Logging.log(Logging.LEVEL_ERROR, tr("IOError while creating file, autosave will be skipped: {0}", e.getMessage()), e);
+                return null;
+            }
+            index++;
+        }
+    }
+
+    private static void createNewPidFile(File autosaveDir, String filename) {
+        File pidFile = new File(autosaveDir, filename+".pid");
+        try (PrintStream ps = new PrintStream(pidFile, "UTF-8")) {
+            ps.println(ManagementFactory.getRuntimeMXBean().getName());
+        } catch (IOException | SecurityException t) {
+            Logging.error(t);
+        }
+    }
+
+    private void savelayer(AutosaveLayerInfo info) {
+        if (!info.layer.getName().equals(info.layerName)) {
+            setLayerFileName(info);
+            info.layerName = info.layer.getName();
+        }
+        if (changedDatasets.remove(info.layer.data)) {
+            File file = getNewLayerFile(info, new Date(), 0);
+            if (file != null) {
+                info.backupFiles.add(file);
+                new OsmExporter().exportData(file, info.layer, true /* no backup with appended ~ */);
+            }
+        }
+        while (info.backupFiles.size() > PROP_FILES_PER_LAYER.get()) {
+            File oldFile = info.backupFiles.remove();
+            if (Utils.deleteFile(oldFile, marktr("Unable to delete old backup file {0}"))) {
+                Utils.deleteFile(getPidFile(oldFile), marktr("Unable to delete old backup file {0}"));
+            }
+        }
+    }
+
+    @Override
+    public void run() {
+        synchronized (layersLock) {
+            try {
+                for (AutosaveLayerInfo info: layersInfo) {
+                    savelayer(info);
+                }
+                changedDatasets.clear();
+                if (PROP_NOTIFICATION.get() && !layersInfo.isEmpty()) {
+                    GuiHelper.runInEDT(this::displayNotification);
+                }
+            } catch (RuntimeException t) { // NOPMD
+                // Don't let exception stop time thread
+                Logging.error("Autosave failed:");
+                Logging.error(t);
+            }
+        }
+    }
+
+    protected void displayNotification() {
+        new Notification(tr("Your work has been saved automatically."))
+        .setDuration(Notification.TIME_SHORT)
+        .show();
+    }
+
+    @Override
+    public void layerOrderChanged(LayerOrderChangeEvent e) {
+        // Do nothing
+    }
+
+    private void registerNewlayer(OsmDataLayer layer) {
+        synchronized (layersLock) {
+            layer.data.addDataSetListener(datasetAdapter);
+            layersInfo.add(new AutosaveLayerInfo(layer));
+        }
+    }
+
+    @Override
+    public void layerAdded(LayerAddEvent e) {
+        if (e.getAddedLayer() instanceof OsmDataLayer) {
+            registerNewlayer((OsmDataLayer) e.getAddedLayer());
+        }
+    }
+
+    @Override
+    public void layerRemoving(LayerRemoveEvent e) {
+        if (e.getRemovedLayer() instanceof OsmDataLayer) {
+            synchronized (layersLock) {
+                OsmDataLayer osmLayer = (OsmDataLayer) e.getRemovedLayer();
+                osmLayer.data.removeDataSetListener(datasetAdapter);
+                Iterator<AutosaveLayerInfo> it = layersInfo.iterator();
+                while (it.hasNext()) {
+                    AutosaveLayerInfo info = it.next();
+                    if (info.layer == osmLayer) {
+
+                        savelayer(info);
+                        File lastFile = info.backupFiles.pollLast();
+                        if (lastFile != null) {
+                            moveToDeletedLayersFolder(lastFile);
+                        }
+                        for (File file: info.backupFiles) {
+                            if (Utils.deleteFile(file)) {
+                                Utils.deleteFile(getPidFile(file));
+                            }
+                        }
+
+                        it.remove();
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void processDatasetEvent(AbstractDatasetChangedEvent event) {
+        changedDatasets.add(event.getDataset());
+    }
+
+    protected File getPidFile(File osmFile) {
+        return new File(autosaveDir, osmFile.getName().replaceFirst("[.][^.]+$", ".pid"));
+    }
+
+    /**
+     * Replies the list of .osm files still present in autosave dir, that are not currently managed by another instance of JOSM.
+     * These files are hence unsaved layers from an old instance of JOSM that crashed and may be recovered by this instance.
+     * @return The list of .osm files still present in autosave dir, that are not currently managed by another instance of JOSM
+     */
+    public List<File> getUnsavedLayersFiles() {
+        List<File> result = new ArrayList<>();
+        File[] files = autosaveDir.listFiles(OsmImporter.FILE_FILTER);
+        if (files == null)
+            return result;
+        for (File file: files) {
+            if (file.isFile()) {
+                boolean skipFile = false;
+                File pidFile = getPidFile(file);
+                if (pidFile.exists()) {
+                    try (BufferedReader reader = Files.newBufferedReader(pidFile.toPath(), StandardCharsets.UTF_8)) {
+                        String jvmId = reader.readLine();
+                        if (jvmId != null) {
+                            String pid = jvmId.split("@")[0];
+                            skipFile = jvmPerfDataFileExists(pid);
+                        }
+                    } catch (IOException | SecurityException t) {
+                        Logging.error(t);
+                    }
+                }
+                if (!skipFile) {
+                    result.add(file);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static boolean jvmPerfDataFileExists(final String jvmId) {
+        File jvmDir = new File(System.getProperty("java.io.tmpdir") + File.separator + "hsperfdata_" + System.getProperty("user.name"));
+        if (jvmDir.exists() && jvmDir.canRead()) {
+            File[] files = jvmDir.listFiles((FileFilter) file -> file.getName().equals(jvmId) && file.isFile());
+            return files != null && files.length == 1;
+        }
+        return false;
+    }
+
+    /**
+     * Recover the unsaved layers and open them asynchronously.
+     * @return A future that can be used to wait for the completion of this task.
+     */
+    public Future<?> recoverUnsavedLayers() {
+        List<File> files = getUnsavedLayersFiles();
+        final OpenFileTask openFileTsk = new OpenFileTask(files, null, tr("Restoring files"));
+        final Future<?> openFilesFuture = MainApplication.worker.submit(openFileTsk);
+        return MainApplication.worker.submit(() -> {
+            try {
+                // Wait for opened tasks to be generated.
+                openFilesFuture.get();
+                for (File f: openFileTsk.getSuccessfullyOpenedFiles()) {
+                    moveToDeletedLayersFolder(f);
+                }
+            } catch (InterruptedException | ExecutionException e) {
+                Logging.error(e);
+            }
+        });
+    }
+
+    /**
+     * Move file to the deleted layers directory.
+     * If moving does not work, it will try to delete the file directly.
+     * Afterwards, if the number of deleted layers gets larger than PROP_DELETED_LAYERS,
+     * some files in the deleted layers directory will be removed.
+     *
+     * @param f the file, usually from the autosave dir
+     */
+    private void moveToDeletedLayersFolder(File f) {
+        File backupFile = new File(deletedLayersDir, f.getName());
+        File pidFile = getPidFile(f);
+
+        if (backupFile.exists()) {
+            deletedLayers.remove(backupFile);
+            Utils.deleteFile(backupFile, marktr("Unable to delete old backup file {0}"));
+        }
+        if (f.renameTo(backupFile)) {
+            deletedLayers.add(backupFile);
+            Utils.deleteFile(pidFile);
+        } else {
+            Logging.warn(String.format("Could not move autosaved file %s to %s folder", f.getName(), deletedLayersDir.getName()));
+            // we cannot move to deleted folder, so just try to delete it directly
+            if (Utils.deleteFile(f, marktr("Unable to delete backup file {0}"))) {
+                Utils.deleteFile(pidFile, marktr("Unable to delete PID file {0}"));
+            }
+        }
+        while (deletedLayers.size() > PROP_DELETED_LAYERS.get()) {
+            File next = deletedLayers.remove();
+            if (next == null) {
+                break;
+            }
+            Utils.deleteFile(next, marktr("Unable to delete archived backup file {0}"));
+        }
+    }
+
+    /**
+     * Mark all unsaved layers as deleted. They are still preserved in the deleted layers folder.
+     */
+    public void discardUnsavedLayers() {
+        for (File f: getUnsavedLayersFiles()) {
+            moveToDeletedLayersFolder(f);
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java	(revision 12634)
@@ -316,5 +316,5 @@
 
     public static void create(Collection<File> files, GpxLayer gpxLayer) {
-        Main.worker.execute(new Loader(files, gpxLayer));
+        MainApplication.worker.execute(new Loader(files, gpxLayer));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadAlongTrackAction.java	(revision 12634)
@@ -8,5 +8,4 @@
 import java.awt.geom.Rectangle2D;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.DownloadAlongAction;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -15,4 +14,5 @@
 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.help.HelpUtil;
@@ -197,5 +197,5 @@
         PleaseWaitRunnable task = createTask();
         if (task != null) {
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackAction.java	(revision 12634)
@@ -21,4 +21,5 @@
 import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
 import org.openstreetmap.josm.data.gpx.WayPoint;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
@@ -109,5 +110,5 @@
         PrecacheWmsTask task = createTask();
         if (task != null) {
-            Main.worker.execute(task);
+            MainApplication.worker.execute(task);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java	(revision 12634)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Tag;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -349,5 +350,5 @@
             toReload.add(data.get(i));
         }
-        Main.worker.submit(new MapPaintStyleLoader(toReload));
+        MainApplication.worker.submit(new MapPaintStyleLoader(toReload));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/StyleSetting.java	(revision 12634)
@@ -11,4 +11,5 @@
 
 import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -70,5 +71,5 @@
                 public void actionPerformed(ActionEvent e) {
                     setValue(item.isSelected());
-                    Main.worker.submit(new MapPaintStyles.MapPaintStyleLoader(Arrays.asList(parentStyle)));
+                    MainApplication.worker.submit(new MapPaintStyles.MapPaintStyleLoader(Arrays.asList(parentStyle)));
                 }
             };
Index: trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/oauth/OAuthAuthorizationWizard.java	(revision 12634)
@@ -36,5 +36,4 @@
 
 import org.openstreetmap.josm.Main;
-import org.openstreetmap.josm.data.CustomConfigurator;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.oauth.OAuthParameters;
@@ -306,5 +305,5 @@
     public void initFromPreferences() {
         // Copy current JOSM preferences to update API url with the one used in this wizard
-        Preferences copyPref = CustomConfigurator.clonePreferences(Main.pref);
+        Preferences copyPref = new Preferences(Main.pref);
         copyPref.put("osm-server.url", apiUrl);
         pnlFullyAutomaticAuthorisationUI.initFromPreferences(copyPref);
Index: trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java	(revision 12634)
@@ -35,4 +35,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.preferences.advanced.AdvancedPreference;
 import org.openstreetmap.josm.gui.preferences.audio.AudioPreference;
@@ -418,6 +419,6 @@
                 // if we have to launch a plugin download task we do it asynchronously, followed
                 // by the remaining "save preferences" activites run on the Swing EDT.
-                Main.worker.submit(task);
-                Main.worker.submit(() -> SwingUtilities.invokeLater(continuation));
+                MainApplication.worker.submit(task);
+                MainApplication.worker.submit(() -> SwingUtilities.invokeLater(continuation));
             } else {
                 // no need for asynchronous activities. Simply run the remaining "save preference"
Index: trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/SourceEditor.java	(revision 12634)
@@ -79,4 +79,5 @@
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.util.FileFilterAllFiles;
@@ -533,5 +534,5 @@
      */
     protected void reloadAvailableSources(String url, List<SourceProvider> sourceProviders) {
-        Main.worker.submit(new SourceLoader(url, sourceProviders));
+        MainApplication.worker.submit(new SourceLoader(url, sourceProviders));
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java	(revision 12634)
@@ -38,9 +38,9 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.DiskAccessAction;
-import org.openstreetmap.josm.data.CustomConfigurator;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.preferences.Setting;
 import org.openstreetmap.josm.data.preferences.StringSetting;
 import org.openstreetmap.josm.gui.dialogs.LogShowDialog;
+import org.openstreetmap.josm.gui.io.CustomConfigurator;
 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
 import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
@@ -258,5 +258,5 @@
             return;
 
-        Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
+        Preferences tmpPrefs = new Preferences(Main.pref);
 
         StringBuilder log = new StringBuilder();
@@ -408,5 +408,5 @@
         @Override
         public void actionPerformed(ActionEvent ae) {
-            Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
+            Preferences tmpPrefs = new Preferences(Main.pref);
             CustomConfigurator.readXML(file, tmpPrefs);
             readPreferences(tmpPrefs);
Index: trunk/src/org/openstreetmap/josm/gui/preferences/advanced/ExportProfileAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/advanced/ExportProfileAction.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/advanced/ExportProfileAction.java	(revision 12634)
@@ -19,7 +19,7 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.DiskAccessAction;
-import org.openstreetmap.josm.data.CustomConfigurator;
 import org.openstreetmap.josm.data.Preferences;
 import org.openstreetmap.josm.data.preferences.Setting;
+import org.openstreetmap.josm.gui.io.CustomConfigurator;
 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser;
 import org.openstreetmap.josm.tools.Utils;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/imagery/CacheContentsPanel.java	(revision 12634)
@@ -27,6 +27,6 @@
 import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
 import org.apache.commons.jcs.engine.stats.behavior.IStats;
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.layer.WMSLayer;
@@ -51,5 +51,5 @@
     public CacheContentsPanel() {
         super(new GridBagLayout());
-        Main.worker.submit(() -> {
+        MainApplication.worker.submit(() -> {
             addToPanel(TMSLayer.getCache(), "TMS");
             addToPanel(WMSLayer.getCache(), "WMS");
Index: trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreference.java	(revision 12634)
@@ -61,4 +61,5 @@
 import org.openstreetmap.josm.data.imagery.Shape;
 import org.openstreetmap.josm.data.preferences.ColorProperty;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.download.DownloadDialog;
 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
@@ -657,5 +658,5 @@
             @Override
             public void actionPerformed(ActionEvent evt) {
-                layerInfo.loadDefaults(true, false, false);
+                layerInfo.loadDefaults(true, MainApplication.worker, false);
                 defaultModel.fireTableDataChanged();
                 defaultTable.getSelectionModel().clearSelection();
Index: trunk/src/org/openstreetmap/josm/gui/preferences/map/BackupPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/map/BackupPreference.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/map/BackupPreference.java	(revision 12634)
@@ -15,6 +15,6 @@
 import javax.swing.JSeparator;
 
-import org.openstreetmap.josm.data.AutosaveTask;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.gui.layer.AutosaveTask;
 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
Index: trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java	(revision 12634)
@@ -47,4 +47,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
@@ -328,6 +329,6 @@
             }
         };
-        Main.worker.submit(task);
-        Main.worker.submit(r);
+        MainApplication.worker.submit(task);
+        MainApplication.worker.submit(r);
     }
 
@@ -362,6 +363,6 @@
                 }
             };
-            Main.worker.submit(task);
-            Main.worker.submit(continuation);
+            MainApplication.worker.submit(task);
+            MainApplication.worker.submit(continuation);
         }
     }
@@ -436,10 +437,10 @@
                 }
                 pluginDownloadTask.setPluginsToDownload(toUpdate);
-                Main.worker.submit(pluginDownloadTask);
-                Main.worker.submit(pluginDownloadContinuation);
+                MainApplication.worker.submit(pluginDownloadTask);
+                MainApplication.worker.submit(pluginDownloadContinuation);
             };
 
-            Main.worker.submit(pluginInfoDownloadTask);
-            Main.worker.submit(pluginInfoDownloadContinuation);
+            MainApplication.worker.submit(pluginInfoDownloadTask);
+            MainApplication.worker.submit(pluginInfoDownloadContinuation);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/server/OAuthAuthenticationPreferencesPanel.java	(revision 12634)
@@ -26,4 +26,5 @@
 import org.openstreetmap.josm.data.oauth.OAuthParameters;
 import org.openstreetmap.josm.data.oauth.OAuthToken;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.oauth.AdvancedOAuthPropertiesPanel;
 import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
@@ -315,5 +316,5 @@
                     OAuthAuthenticationPreferencesPanel.this,
                     apiUrl,
-                    Main.worker);
+                    MainApplication.worker);
             try {
                 wizard.showDialog();
@@ -364,5 +365,5 @@
                     token
             );
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
         }
     }
Index: trunk/src/org/openstreetmap/josm/gui/preferences/server/OsmApiUrlInputPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/preferences/server/OsmApiUrlInputPanel.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/preferences/server/OsmApiUrlInputPanel.java	(revision 12634)
@@ -29,4 +29,5 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.preferences.CollectionProperty;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.help.HelpUtil;
 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator;
@@ -186,5 +187,5 @@
             final String url = getStrippedApiUrl();
             final ApiUrlTestTask task = new ApiUrlTestTask(OsmApiUrlInputPanel.this, url);
-            Main.worker.submit(task);
+            MainApplication.worker.submit(task);
             Runnable r = () -> {
                 if (task.isCanceled())
@@ -203,5 +204,5 @@
                 SwingUtilities.invokeLater(r1);
             };
-            Main.worker.submit(r);
+            MainApplication.worker.submit(r);
         }
 
Index: trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java	(revision 12634)
@@ -50,4 +50,5 @@
 import org.openstreetmap.josm.data.preferences.StrokeProperty;
 import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -93,5 +94,5 @@
      */
     public static void executeByMainWorkerInEDT(final Runnable task) {
-        Main.worker.submit(() -> runInEDTAndWait(task));
+        MainApplication.worker.submit(() -> runInEDTAndWait(task));
     }
 
Index: trunk/src/org/openstreetmap/josm/io/FileWatcher.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/FileWatcher.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/FileWatcher.java	(revision 12634)
@@ -14,6 +14,6 @@
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.Executors;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.validation.OsmValidator;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
@@ -24,4 +24,5 @@
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -140,5 +141,6 @@
                     if (style != null) {
                         Logging.info("Map style "+style.getDisplayString()+" has been modified. Reloading style...");
-                        Main.worker.submit(new MapPaintStyleLoader(Collections.singleton(style)));
+                        Executors.newSingleThreadExecutor(Utils.newThreadFactory("mapstyle-reload-%d", Thread.NORM_PRIORITY)).submit(
+                                new MapPaintStyleLoader(Collections.singleton(style)));
                     } else if (rule != null) {
                         Logging.info("Validator rule "+rule.getDisplayString()+" has been modified. Reloading rule...");
Index: trunk/src/org/openstreetmap/josm/io/OsmConnection.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/OsmConnection.java	(revision 12634)
@@ -121,6 +121,6 @@
             }
             final Runnable authTask = new FutureTask<>(() -> {
-                // Concerning Utils.newDirectExecutor: Main.worker cannot be used since this connection is already
-                // executed via Main.worker. The OAuth connections would block otherwise.
+                // Concerning Utils.newDirectExecutor: Main worker cannot be used since this connection is already
+                // executed via main worker. The OAuth connections would block otherwise.
                 final OAuthAuthorizationWizard wizard = new OAuthAuthorizationWizard(
                         Main.parent, apiUrl.toExternalForm(), Utils.newDirectExecutor());
Index: trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadAndZoomHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadAndZoomHandler.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadAndZoomHandler.java	(revision 12634)
@@ -145,5 +145,5 @@
                         Future<?> future = osmTask.download(newLayer, new Bounds(minlat, minlon, maxlat, maxlon),
                                 null /* let the task manage the progress monitor */);
-                        Main.worker.submit(new PostDownloadHandler(osmTask, future));
+                        MainApplication.worker.submit(new PostDownloadHandler(osmTask, future));
                     }
                 }
@@ -196,5 +196,5 @@
             try {
                 final SearchCompiler.Match search = SearchCompiler.compile(args.get("search"));
-                Main.worker.submit(() -> {
+                MainApplication.worker.submit(() -> {
                     final DataSet ds = Main.getLayerManager().getEditDataSet();
                     final Collection<OsmPrimitive> filteredPrimitives = SubclassFilteredCollection.filter(ds.allPrimitives(), search);
@@ -214,5 +214,5 @@
         // add changeset tags after download if necessary
         if (args.containsKey("changeset_comment") || args.containsKey("changeset_source")) {
-            Main.worker.submit(() -> {
+            MainApplication.worker.submit(() -> {
                 if (Main.getLayerManager().getEditDataSet() != null) {
                     if (args.containsKey("changeset_comment")) {
Index: trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadDataHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadDataHandler.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadDataHandler.java	(revision 12634)
@@ -7,7 +7,7 @@
 import java.nio.charset.StandardCharsets;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
 import org.openstreetmap.josm.data.osm.DataSet;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.OsmReader;
@@ -40,5 +40,5 @@
     @Override
     protected void handleRequest() throws RequestHandlerErrorException {
-        Main.worker.submit(new LoadDataTask(isLoadInNewLayer(), dataSet, args.get("layer_name")));
+        MainApplication.worker.submit(new LoadDataTask(isLoadInNewLayer(), dataSet, args.get("layer_name")));
     }
 
Index: trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java	(revision 12634)
@@ -14,4 +14,5 @@
 import org.openstreetmap.josm.data.osm.PrimitiveId;
 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
 import org.openstreetmap.josm.gui.util.GuiHelper;
@@ -68,6 +69,6 @@
             final DownloadPrimitivesWithReferrersTask task = new DownloadPrimitivesWithReferrersTask(
                     newLayer, ps, referrers, relationMembers, args.get("layer_name"), null);
-            Main.worker.submit(task);
-            Main.worker.submit(() -> {
+            MainApplication.worker.submit(task);
+            MainApplication.worker.submit(() -> {
                 final List<PrimitiveId> downloaded = task.getDownloadedId();
                 final DataSet ds = Main.getLayerManager().getEditDataSet();
Index: trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java
===================================================================
--- trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/io/session/SessionImporter.java	(revision 12634)
@@ -7,7 +7,7 @@
 import java.io.IOException;
 
-import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.actions.ExtensionFileFilter;
 import org.openstreetmap.josm.actions.SessionLoadAction.Loader;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.FileImporter;
@@ -37,5 +37,5 @@
     public void importData(File file, ProgressMonitor progressMonitor) throws IOException, IllegalDataException {
         boolean zip = Utils.hasExtension(file, "joz");
-        Main.worker.submit(new Loader(file, zip));
+        MainApplication.worker.submit(new Loader(file, zip));
     }
 }
Index: trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java
===================================================================
--- trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/plugins/PluginHandler.java	(revision 12634)
@@ -57,4 +57,5 @@
 import org.openstreetmap.josm.gui.HelpAwareOptionPane;
 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
+import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.download.DownloadSelection;
 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
@@ -590,8 +591,8 @@
         final ReadRemotePluginInformationTask pluginInfoDownloadTask = new ReadRemotePluginInformationTask(
                 Main.pref.getOnlinePluginSites());
-        Main.worker.submit(pluginInfoDownloadTask);
+        MainApplication.worker.submit(pluginInfoDownloadTask);
 
         // Continuation
-        Main.worker.submit(() -> {
+        MainApplication.worker.submit(() -> {
             // Build list of plugins to download
             Set<PluginInformation> toDownload = new HashSet<>(pluginInfoDownloadTask.getAvailablePlugins());
@@ -601,6 +602,6 @@
                 // download plugins
                 final PluginDownloadTask task = new PluginDownloadTask(parent, toDownload, tr("Download plugins"));
-                Main.worker.submit(task);
-                Main.worker.submit(() -> {
+                MainApplication.worker.submit(task);
+                MainApplication.worker.submit(() -> {
                     // restart if some plugins have been downloaded
                     if (!task.getDownloadedPlugins().isEmpty()) {
Index: trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(revision 12633)
+++ trunk/src/org/openstreetmap/josm/tools/PlatformHookOsx.java	(revision 12634)
@@ -17,4 +17,5 @@
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executors;
 
 import javax.swing.UIManager;
@@ -148,5 +149,6 @@
                     Object oFiles = args[0].getClass().getMethod("getFiles").invoke(args[0]);
                     if (oFiles instanceof List) {
-                        Main.worker.submit(new OpenFileTask((List<File>) oFiles, null) {
+                        Executors.newSingleThreadExecutor(Utils.newThreadFactory("openFiles-%d", Thread.NORM_PRIORITY)).submit(
+                                new OpenFileTask((List<File>) oFiles, null) {
                             @Override
                             protected void realRun() throws SAXException, IOException, OsmTransferException {
