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        Main.platform.afterPrefStartupHook();
370
371        FontsManager.initialize();
372
373        handleSpecialLanguages();
374
375        final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
376        Main.parent = mainFrame;
377
378        if (args.containsKey(Option.LOAD_PREFERENCES)) {
379            CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
380            for (String i : args.get(Option.LOAD_PREFERENCES)) {
381                info("Reading preferences from " + i);
382                try (InputStream is = Utils.openURL(new URL(i))) {
383                    config.openAndReadXML(is);
384                } catch (Exception ex) {
385                    throw new RuntimeException(ex);
386                }
387            }
388        }
389
390        if (args.containsKey(Option.SET)) {
391            for (String i : args.get(Option.SET)) {
392                String[] kv = i.split("=", 2);
393                Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
394            }
395        }
396
397        DefaultAuthenticator.createInstance();
398        Authenticator.setDefault(DefaultAuthenticator.getInstance());
399        DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
400        ProxySelector.setDefault(proxySelector);
401        OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
402
403        final SplashScreen splash = new SplashScreen();
404        final ProgressMonitor monitor = splash.getProgressMonitor();
405        monitor.beginTask(tr("Initializing"));
406        splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
407        Main.setInitStatusListener(new InitStatusListener() {
408
409            @Override
410            public void updateStatus(String event) {
411                monitor.indeterminateSubTask(event);
412            }
413        });
414
415        Collection<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false));
416        if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
417            monitor.subTask(tr("Updating plugins"));
418            pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
419        }
420
421        monitor.indeterminateSubTask(tr("Installing updated plugins"));
422        PluginHandler.installDownloadedPlugins(true);
423
424        monitor.indeterminateSubTask(tr("Loading early plugins"));
425        PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false));
426
427        monitor.indeterminateSubTask(tr("Setting defaults"));
428        preConstructorInit(args);
429
430        monitor.indeterminateSubTask(tr("Creating main GUI"));
431        final Main main = new MainApplication(mainFrame);
432
433        monitor.indeterminateSubTask(tr("Loading plugins"));
434        PluginHandler.loadLatePlugins(splash, pluginsToLoad,  monitor.createSubTaskMonitor(1, false));
435        toolbar.refreshToolbarControl();
436
437        // Wait for splash disappearance (fix #9714)
438        GuiHelper.runInEDTAndWait(new Runnable() {
439            @Override
440            public void run() {
441                splash.setVisible(false);
442                splash.dispose();
443                mainFrame.setVisible(true);
444                main.gettingStarted.requestFocusInWindow();
445            }
446        });
447
448        Main.MasterWindowListener.setup();
449
450        boolean maximized = Main.pref.getBoolean("gui.maximized", false);
451        if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
452            if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
453                Main.windowState = JFrame.MAXIMIZED_BOTH;
454                mainFrame.setExtendedState(Main.windowState);
455            } else {
456                Main.debug("Main window: maximizing not supported");
457            }
458        }
459        if (main.menu.fullscreenToggleAction != null) {
460            main.menu.fullscreenToggleAction.initial();
461        }
462
463        SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
464
465        if (Main.isPlatformWindows()) {
466            try {
467                // Check for insecure certificates to remove.
468                // This is Windows-dependant code but it can't go to preStartupHook (need i18n) neither startupHook (need to be called before remote control)
469                PlatformHookWindows.removeInsecureCertificates();
470            } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
471                error(e);
472            }
473        }
474
475        if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
476            RemoteControl.start();
477        }
478
479        if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
480            MessageNotifier.start();
481        }
482
483        if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
484            // 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
485            info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
486            RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
487        }
488    }
489
490    private static void handleSpecialLanguages() {
491        // Use special font for Khmer script, as the default Java font do not display these characters
492        if ("km".equals(Main.pref.get("language"))) {
493            Collection<String> fonts = Arrays.asList(
494                    GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
495            for (String f : new String[]{"Khmer UI", "DaunPenh", "MoolBoran"}) {
496                if (fonts.contains(f)) {
497                    GuiHelper.setUIFont(f);
498                    break;
499                }
500            }
501        }
502    }
503
504    private static void processOffline(Map<Option, Collection<String>> args) {
505        if (args.containsKey(Option.OFFLINE)) {
506            for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) {
507                try {
508                    Main.setOffline(OnlineResource.valueOf(s.toUpperCase()));
509                } catch (IllegalArgumentException e) {
510                    Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.",
511                            s.toUpperCase(), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values())));
512                    System.exit(1);
513                    return;
514                }
515            }
516            Set<OnlineResource> offline = Main.getOfflineResources();
517            if (!offline.isEmpty()) {
518                Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
519                        "JOSM is running in offline mode. These resources will not be available: {0}",
520                        offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
521            }
522        }
523    }
524
525    private static class GuiFinalizationWorker implements Runnable {
526
527        private final Map<Option, Collection<String>> args;
528        private final DefaultProxySelector proxySelector;
529
530        public GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
531            this.args = args;
532            this.proxySelector = proxySelector;
533        }
534
535        @Override
536        public void run() {
537
538            // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
539            if (!handleProxyErrors()) {
540                handleNetworkErrors();
541            }
542
543            // Restore autosave layers after crash and start autosave thread
544            handleAutosave();
545
546            // Handle command line instructions
547            postConstructorProcessCmdLine(args);
548
549            // Show download dialog if autostart is enabled
550            DownloadDialog.autostartIfNeeded();
551        }
552
553        private void handleAutosave() {
554            if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
555                AutosaveTask autosaveTask = new AutosaveTask();
556                List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
557                if (!unsavedLayerFiles.isEmpty()) {
558                    ExtendedDialog dialog = new ExtendedDialog(
559                            Main.parent,
560                            tr("Unsaved osm data"),
561                            new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
562                            );
563                    dialog.setContent(
564                            trn("JOSM found {0} unsaved osm data layer. ",
565                                    "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
566                                    tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
567                    dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"});
568                    int selection = dialog.showDialog().getValue();
569                    if (selection == 1) {
570                        autosaveTask.recoverUnsavedLayers();
571                    } else if (selection == 3) {
572                        autosaveTask.discardUnsavedLayers();
573                    }
574                }
575                autosaveTask.schedule();
576            }
577        }
578
579        private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
580            if (hasErrors) {
581                ExtendedDialog ed = new ExtendedDialog(
582                        Main.parent, title,
583                        new String[]{tr("Change proxy settings"), tr("Cancel")});
584                ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2);
585                ed.setMinimumSize(new Dimension(460, 260));
586                ed.setIcon(JOptionPane.WARNING_MESSAGE);
587                ed.setContent(message);
588
589                if (ed.showDialog().getValue() == 1) {
590                    PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
591                }
592            }
593            return hasErrors;
594        }
595
596        private boolean handleProxyErrors() {
597            return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
598                    tr("JOSM tried to access the following resources:<br>" +
599                            "{0}" +
600                            "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
601                            "{1}" +
602                            "Would you like to change your proxy settings now?",
603                            Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
604                            Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
605                    ));
606        }
607
608        private boolean handleNetworkErrors() {
609            boolean condition = !NETWORK_ERRORS.isEmpty();
610            if (condition) {
611                Set<String> errors = new TreeSet<>();
612                for (Throwable t : NETWORK_ERRORS.values()) {
613                    errors.add(t.toString());
614                }
615                return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
616                        tr("JOSM tried to access the following resources:<br>" +
617                                "{0}" +
618                                "but <b>failed</b> to do so, because of the following network errors:<br>" +
619                                "{1}" +
620                                "It may be due to a missing proxy configuration.<br>" +
621                                "Would you like to change your proxy settings now?",
622                                Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()),
623                                Utils.joinAsHtmlUnorderedList(errors)
624                        ));
625            }
626            return false;
627        }
628    }
629}