001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005import static org.openstreetmap.josm.tools.I18n.trn;
006import gnu.getopt.Getopt;
007import gnu.getopt.LongOpt;
008
009import java.awt.Dimension;
010import java.awt.GraphicsEnvironment;
011import java.awt.Image;
012import java.awt.Toolkit;
013import java.awt.event.WindowAdapter;
014import java.awt.event.WindowEvent;
015import java.io.File;
016import java.io.IOException;
017import java.io.InputStream;
018import java.net.Authenticator;
019import java.net.ProxySelector;
020import java.net.URL;
021import java.security.AllPermission;
022import java.security.CodeSource;
023import java.security.KeyStoreException;
024import java.security.NoSuchAlgorithmException;
025import java.security.PermissionCollection;
026import java.security.Permissions;
027import java.security.Policy;
028import java.security.cert.CertificateException;
029import java.util.ArrayList;
030import java.util.Arrays;
031import java.util.Collection;
032import java.util.HashMap;
033import java.util.LinkedList;
034import java.util.List;
035import java.util.Map;
036import java.util.Set;
037import java.util.TreeSet;
038
039import javax.swing.JFrame;
040import javax.swing.JOptionPane;
041import javax.swing.RepaintManager;
042import javax.swing.SwingUtilities;
043
044import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
045import org.openstreetmap.josm.Main;
046import org.openstreetmap.josm.actions.PreferencesAction;
047import org.openstreetmap.josm.data.AutosaveTask;
048import org.openstreetmap.josm.data.CustomConfigurator;
049import org.openstreetmap.josm.data.Version;
050import org.openstreetmap.josm.gui.download.DownloadDialog;
051import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
052import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
053import org.openstreetmap.josm.gui.progress.ProgressMonitor;
054import org.openstreetmap.josm.gui.util.GuiHelper;
055import org.openstreetmap.josm.io.DefaultProxySelector;
056import org.openstreetmap.josm.io.MessageNotifier;
057import org.openstreetmap.josm.io.OnlineResource;
058import org.openstreetmap.josm.io.auth.CredentialsManager;
059import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
060import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
061import org.openstreetmap.josm.plugins.PluginHandler;
062import org.openstreetmap.josm.plugins.PluginInformation;
063import org.openstreetmap.josm.tools.BugReportExceptionHandler;
064import org.openstreetmap.josm.tools.FontsManager;
065import org.openstreetmap.josm.tools.I18n;
066import org.openstreetmap.josm.tools.ImageProvider;
067import org.openstreetmap.josm.tools.OsmUrlToBounds;
068import org.openstreetmap.josm.tools.PlatformHookWindows;
069import org.openstreetmap.josm.tools.Utils;
070
071/**
072 * Main window class application.
073 *
074 * @author imi
075 */
076public class MainApplication extends Main {
077    /**
078     * Allow subclassing (see JOSM.java)
079     */
080    public MainApplication() {}
081
082    /**
083     * Constructs a main frame, ready sized and operating. Does not display the frame.
084     * @param mainFrame The main JFrame of the application
085     */
086    public MainApplication(JFrame mainFrame) {
087        addListener();
088        mainFrame.setContentPane(contentPanePrivate);
089        mainFrame.setJMenuBar(menu);
090        geometry.applySafe(mainFrame);
091        List<Image> l = new LinkedList<>();
092        l.add(ImageProvider.get("logo_16x16x32").getImage());
093        l.add(ImageProvider.get("logo_16x16x8").getImage());
094        l.add(ImageProvider.get("logo_32x32x32").getImage());
095        l.add(ImageProvider.get("logo_32x32x8").getImage());
096        l.add(ImageProvider.get("logo_48x48x32").getImage());
097        l.add(ImageProvider.get("logo_48x48x8").getImage());
098        l.add(ImageProvider.get("logo").getImage());
099        mainFrame.setIconImages(l);
100        mainFrame.addWindowListener(new WindowAdapter(){
101            @Override
102            public void windowClosing(final WindowEvent arg0) {
103                Main.exitJosm(true, 0);
104            }
105        });
106        mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
107    }
108
109    /**
110     * Displays help on the console
111     * @since 2748
112     */
113    public static void showHelp() {
114        // TODO: put in a platformHook for system that have no console by default
115        System.out.println(tr("Java OpenStreetMap Editor")+" ["
116                +Version.getInstance().getAgentString()+"]\n\n"+
117                tr("usage")+":\n"+
118                "\tjava -jar josm.jar <options>...\n\n"+
119                tr("options")+":\n"+
120                "\t--help|-h                                 "+tr("Show this help")+"\n"+
121                "\t--geometry=widthxheight(+|-)x(+|-)y       "+tr("Standard unix geometry argument")+"\n"+
122                "\t[--download=]minlat,minlon,maxlat,maxlon  "+tr("Download the bounding box")+"\n"+
123                "\t[--download=]<URL>                        "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+"\n"+
124                "\t[--download=]<filename>                   "+tr("Open a file (any file type that can be opened with File/Open)")+"\n"+
125                "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+"\n"+
126                "\t--downloadgps=<URL>                       "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+"\n"+
127                "\t--selection=<searchstring>                "+tr("Select with the given search")+"\n"+
128                "\t--[no-]maximize                           "+tr("Launch in maximized mode")+"\n"+
129                "\t--reset-preferences                       "+tr("Reset the preferences to default")+"\n\n"+
130                "\t--load-preferences=<url-to-xml>           "+tr("Changes preferences according to the XML file")+"\n\n"+
131                "\t--set=<key>=<value>                       "+tr("Set preference key to value")+"\n\n"+
132                "\t--language=<language>                     "+tr("Set the language")+"\n\n"+
133                "\t--version                                 "+tr("Displays the JOSM version and exits")+"\n\n"+
134                "\t--debug                                   "+tr("Print debugging messages to console")+"\n\n"+
135                "\t--offline=<osm_api|josm_website|all>      "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+
136                tr("options provided as Java system properties")+":\n"+
137                "\t-Djosm.pref="    +tr("/PATH/TO/JOSM/PREF    ")+tr("Set the preferences directory")+"\n\n"+
138                "\t-Djosm.userdata="+tr("/PATH/TO/JOSM/USERDATA")+tr("Set the user data directory")+"\n\n"+
139                "\t-Djosm.cache="   +tr("/PATH/TO/JOSM/CACHE   ")+tr("Set the cache directory")+"\n\n"+
140                "\t-Djosm.home="    +tr("/PATH/TO/JOSM/HOMEDIR ")+
141                tr("Relocate all 3 directories to homedir. Cache directory will be in homedir/cache")+"\n\n"+
142                tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+
143                tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
144                        "      Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
145                        "\t-Xmx...m\n\n"+
146                        tr("examples")+":\n"+
147                        "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
148                        "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+"\n"+
149                        "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
150                        "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
151                        "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+
152                        "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
153                        "\tjava -Xmx1024m -jar josm.jar\n\n"+
154                        tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+"\n"+
155                        tr("Make sure you load some data if you use --selection.")+"\n"
156                );
157    }
158
159    /**
160     * JOSM command line options.
161     * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>
162     * @since 5279
163     */
164    public enum Option {
165        /** --help|-h                                  Show this help */
166        HELP(false),
167        /** --version                                  Displays the JOSM version and exits */
168        VERSION(false),
169        /** --debug                                    Print debugging messages to console */
170        DEBUG(false),
171        /** --trace                                    Print detailed debugging messages to console */
172        TRACE(false),
173        /** --language=&lt;language&gt;                Set the language */
174        LANGUAGE(true),
175        /** --reset-preferences                        Reset the preferences to default */
176        RESET_PREFERENCES(false),
177        /** --load-preferences=&lt;url-to-xml&gt;      Changes preferences according to the XML file */
178        LOAD_PREFERENCES(true),
179        /** --set=&lt;key&gt;=&lt;value&gt;            Set preference key to value */
180        SET(true),
181        /** --geometry=widthxheight(+|-)x(+|-)y        Standard unix geometry argument */
182        GEOMETRY(true),
183        /** --no-maximize                              Do not launch in maximized mode */
184        NO_MAXIMIZE(false),
185        /** --maximize                                 Launch in maximized mode */
186        MAXIMIZE(false),
187        /** --download=minlat,minlon,maxlat,maxlon     Download the bounding box <br>
188         *  --download=&lt;URL&gt;                     Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
189         *  --download=&lt;filename&gt;                Open a file (any file type that can be opened with File/Open) */
190        DOWNLOAD(true),
191        /** --downloadgps=minlat,minlon,maxlat,maxlon  Download the bounding box as raw GPS <br>
192         *  --downloadgps=&lt;URL&gt;                  Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
193        DOWNLOADGPS(true),
194        /** --selection=&lt;searchstring&gt;           Select with the given search */
195        SELECTION(true),
196        /** --offline=&lt;osm_api|josm_website|all&gt; Disable access to the given resource(s), delimited by comma */
197        OFFLINE(true);
198
199        private String name;
200        private boolean requiresArgument;
201
202        private Option(boolean requiresArgument) {
203            this.name = name().toLowerCase().replace("_", "-");
204            this.requiresArgument = requiresArgument;
205        }
206
207        /**
208         * Replies the option name
209         * @return The option name, in lowercase
210         */
211        public String getName() {
212            return name;
213        }
214
215        /**
216         * Determines if this option requires an argument.
217         * @return {@code true} if this option requires an argument, {@code false} otherwise
218         */
219        public boolean requiresArgument() {
220            return requiresArgument;
221        }
222
223        public static Map<Option, Collection<String>> fromStringMap(Map<String, Collection<String>> opts) {
224            Map<Option, Collection<String>> res = new HashMap<>();
225            for (Map.Entry<String, Collection<String>> e : opts.entrySet()) {
226                Option o = Option.valueOf(e.getKey().toUpperCase().replace("-", "_"));
227                if (o != null) {
228                    res.put(o, e.getValue());
229                }
230            }
231            return res;
232        }
233    }
234
235    private static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) {
236
237        List<LongOpt> los = new ArrayList<>();
238        for (Option o : Option.values()) {
239            los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));
240        }
241
242        Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()]));
243
244        Map<Option, Collection<String>> argMap = new HashMap<>();
245
246        int c;
247        while ((c = g.getopt()) != -1 ) {
248            Option opt = null;
249            switch (c) {
250                case 'h':
251                    opt = Option.HELP;
252                    break;
253                case 'v':
254                    opt = Option.VERSION;
255                    break;
256                case 0:
257                    opt = Option.values()[g.getLongind()];
258                    break;
259            }
260            if (opt != null) {
261                Collection<String> values = argMap.get(opt);
262                if (values == null) {
263                    values = new ArrayList<>();
264                    argMap.put(opt, values);
265                }
266                values.add(g.getOptarg());
267            } else
268                throw new IllegalArgumentException("Invalid option: "+c);
269        }
270        // positional arguments are a shortcut for the --download ... option
271        for (int i = g.getOptind(); i < args.length; ++i) {
272            Collection<String> values = argMap.get(Option.DOWNLOAD);
273            if (values == null) {
274                values = new ArrayList<>();
275                argMap.put(Option.DOWNLOAD, values);
276            }
277            values.add(args[i]);
278        }
279
280        return argMap;
281    }
282
283    /**
284     * Main application Startup
285     * @param argArray Command-line arguments
286     */
287    public static void main(final String[] argArray) {
288        I18n.init();
289        Main.checkJavaVersion();
290
291        // construct argument table
292        Map<Option, Collection<String>> args = null;
293        try {
294            args = buildCommandLineArgumentMap(argArray);
295        } catch (IllegalArgumentException e) {
296            System.exit(1);
297            return;
298        }
299
300        final boolean languageGiven = args.containsKey(Option.LANGUAGE);
301
302        if (languageGiven) {
303            I18n.set(args.get(Option.LANGUAGE).iterator().next());
304        }
305
306        initApplicationPreferences();
307
308        Policy.setPolicy(new Policy() {
309            // Permissions for plug-ins loaded when josm is started via webstart
310            private PermissionCollection pc;
311
312            {
313                pc = new Permissions();
314                pc.add(new AllPermission());
315            }
316
317            @Override
318            public void refresh() { }
319
320            @Override
321            public PermissionCollection getPermissions(CodeSource codesource) {
322                return pc;
323            }
324        });
325
326        Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
327
328        // initialize the platform hook, and
329        Main.determinePlatformHook();
330        // call the really early hook before we do anything else
331        Main.platform.preStartupHook();
332
333        Main.commandLineArgs = Utils.copyArray(argArray);
334
335        if (args.containsKey(Option.VERSION)) {
336            System.out.println(Version.getInstance().getAgentString());
337            System.exit(0);
338        }
339
340        if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) {
341            // Enable JOSM debug level
342            logLevel = 4;
343            Main.info(tr("Printing debugging messages to console"));
344        }
345
346        if (args.containsKey(Option.TRACE)) {
347            // Enable JOSM debug level
348            logLevel = 5;
349            // Enable debug in OAuth signpost via system preference, but only at trace level
350            Utils.updateSystemProperty("debug", "true");
351            Main.info(tr("Enabled detailed debug level (trace)"));
352        }
353
354        Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
355
356        if (!languageGiven) {
357            I18n.set(Main.pref.get("language", null));
358        }
359        Main.pref.updateSystemProperties();
360
361        // asking for help? show help and exit
362        if (args.containsKey(Option.HELP)) {
363            showHelp();
364            System.exit(0);
365        }
366
367        processOffline(args);
368
369        FontsManager.initialize();
370
371        handleSpecialLanguages();
372
373        final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
374        Main.parent = mainFrame;
375
376        if (args.containsKey(Option.LOAD_PREFERENCES)) {
377            CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
378            for (String i : args.get(Option.LOAD_PREFERENCES)) {
379                info("Reading preferences from " + i);
380                try (InputStream is = Utils.openURL(new URL(i))) {
381                    config.openAndReadXML(is);
382                } catch (Exception ex) {
383                    throw new RuntimeException(ex);
384                }
385            }
386        }
387
388        if (args.containsKey(Option.SET)) {
389            for (String i : args.get(Option.SET)) {
390                String[] kv = i.split("=", 2);
391                Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
392            }
393        }
394
395        DefaultAuthenticator.createInstance();
396        Authenticator.setDefault(DefaultAuthenticator.getInstance());
397        DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
398        ProxySelector.setDefault(proxySelector);
399        OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
400
401        final SplashScreen splash = new SplashScreen();
402        final ProgressMonitor monitor = splash.getProgressMonitor();
403        monitor.beginTask(tr("Initializing"));
404        splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
405        Main.setInitStatusListener(new InitStatusListener() {
406
407            @Override
408            public void updateStatus(String event) {
409                monitor.indeterminateSubTask(event);
410            }
411        });
412
413        Collection<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false));
414        if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
415            monitor.subTask(tr("Updating plugins"));
416            pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
417        }
418
419        monitor.indeterminateSubTask(tr("Installing updated plugins"));
420        PluginHandler.installDownloadedPlugins(true);
421
422        monitor.indeterminateSubTask(tr("Loading early plugins"));
423        PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
424
425        monitor.indeterminateSubTask(tr("Setting defaults"));
426        preConstructorInit(args);
427
428        monitor.indeterminateSubTask(tr("Creating main GUI"));
429        final Main main = new MainApplication(mainFrame);
430
431        monitor.indeterminateSubTask(tr("Loading plugins"));
432        PluginHandler.loadLatePlugins(splash,pluginsToLoad,  monitor.createSubTaskMonitor(1, false));
433        toolbar.refreshToolbarControl();
434
435        // Wait for splash disappearance (fix #9714)
436        GuiHelper.runInEDTAndWait(new Runnable() {
437            @Override
438            public void run() {
439                splash.setVisible(false);
440                splash.dispose();
441                mainFrame.setVisible(true);
442            }
443        });
444
445        Main.MasterWindowListener.setup();
446
447        boolean maximized = Main.pref.getBoolean("gui.maximized", false);
448        if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
449            if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
450                Main.windowState = JFrame.MAXIMIZED_BOTH;
451                mainFrame.setExtendedState(Main.windowState);
452            } else {
453                Main.debug("Main window: maximizing not supported");
454            }
455        }
456        if (main.menu.fullscreenToggleAction != null) {
457            main.menu.fullscreenToggleAction.initial();
458        }
459
460        SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
461
462        if (Main.isPlatformWindows()) {
463            try {
464                // Check for insecure certificates to remove.
465                // This is Windows-dependant code but it can't go to preStartupHook (need i18n) neither startupHook (need to be called before remote control)
466                PlatformHookWindows.removeInsecureCertificates();
467            } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
468                error(e);
469            }
470        }
471
472        if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
473            RemoteControl.start();
474        }
475
476        if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
477            MessageNotifier.start();
478        }
479
480        if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
481            // Repaint manager is registered so late for a reason - there is lots of violation during startup process but they don't seem to break anything and are difficult to fix
482            info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
483            RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
484        }
485    }
486
487    private static void handleSpecialLanguages() {
488        // Use special font for Khmer script, as the default Java font do not display these characters
489        if ("km".equals(Main.pref.get("language"))) {
490            Collection<String> fonts = Arrays.asList(
491                    GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
492            for (String f : new String[]{"Khmer UI", "DaunPenh", "MoolBoran"}) {
493                if (fonts.contains(f)) {
494                    GuiHelper.setUIFont(f);
495                    break;
496                }
497            }
498        }
499    }
500
501    private static void processOffline(Map<Option, Collection<String>> args) {
502        if (args.containsKey(Option.OFFLINE)) {
503            for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {
504                try {
505                    Main.setOffline(OnlineResource.valueOf(s.toUpperCase()));
506                } catch (IllegalArgumentException e) {
507                    Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.",
508                            s.toUpperCase(), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values())));
509                    System.exit(1);
510                    return;
511                }
512            }
513            Set<OnlineResource> offline = Main.getOfflineResources();
514            if (!offline.isEmpty()) {
515                Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
516                        "JOSM is running in offline mode. These resources will not be available: {0}",
517                        offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
518            }
519        }
520    }
521
522    private static class GuiFinalizationWorker implements Runnable {
523
524        private final Map<Option, Collection<String>> args;
525        private final DefaultProxySelector proxySelector;
526
527        public GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
528            this.args = args;
529            this.proxySelector = proxySelector;
530        }
531
532        @Override
533        public void run() {
534
535            // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
536            if (!handleProxyErrors()) {
537                handleNetworkErrors();
538            }
539
540            // Restore autosave layers after crash and start autosave thread
541            handleAutosave();
542
543            // Handle command line instructions
544            postConstructorProcessCmdLine(args);
545
546            // Show download dialog if autostart is enabled
547            DownloadDialog.autostartIfNeeded();
548        }
549
550        private void handleAutosave() {
551            if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
552                AutosaveTask autosaveTask = new AutosaveTask();
553                List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
554                if (!unsavedLayerFiles.isEmpty()) {
555                    ExtendedDialog dialog = new ExtendedDialog(
556                            Main.parent,
557                            tr("Unsaved osm data"),
558                            new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
559                            );
560                    dialog.setContent(
561                            trn("JOSM found {0} unsaved osm data layer. ",
562                                    "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
563                                    tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
564                    dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"});
565                    int selection = dialog.showDialog().getValue();
566                    if (selection == 1) {
567                        autosaveTask.recoverUnsavedLayers();
568                    } else if (selection == 3) {
569                        autosaveTask.discardUnsavedLayers();
570                    }
571                }
572                autosaveTask.schedule();
573            }
574        }
575
576        private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
577            if (hasErrors) {
578                ExtendedDialog ed = new ExtendedDialog(
579                        Main.parent, title,
580                        new String[]{tr("Change proxy settings"), tr("Cancel")});
581                ed.setButtonIcons(new String[]{"dialogs/settings.png", "cancel.png"}).setCancelButton(2);
582                ed.setMinimumSize(new Dimension(460, 260));
583                ed.setIcon(JOptionPane.WARNING_MESSAGE);
584                ed.setContent(message);
585
586                if (ed.showDialog().getValue() == 1) {
587                    PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
588                }
589            }
590            return hasErrors;
591        }
592
593        private boolean handleProxyErrors() {
594            return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
595                    tr("JOSM tried to access the following resources:<br>" +
596                            "{0}" +
597                            "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
598                            "{1}" +
599                            "Would you like to change your proxy settings now?",
600                            Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
601                            Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
602                    ));
603        }
604
605        private boolean handleNetworkErrors() {
606            boolean condition = !NETWORK_ERRORS.isEmpty();
607            if (condition) {
608                Set<String> errors = new TreeSet<>();
609                for (Throwable t : NETWORK_ERRORS.values()) {
610                    errors.add(t.toString());
611                }
612                return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
613                        tr("JOSM tried to access the following resources:<br>" +
614                                "{0}" +
615                                "but <b>failed</b> to do so, because of the following network errors:<br>" +
616                                "{1}" +
617                                "It may be due to a missing proxy configuration.<br>" +
618                                "Would you like to change your proxy settings now?",
619                                Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()),
620                                Utils.joinAsHtmlUnorderedList(errors)
621                        ));
622            }
623            return false;
624        }
625    }
626}