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; 006 007import java.awt.Dimension; 008import java.awt.Image; 009import java.awt.Toolkit; 010import java.awt.event.WindowAdapter; 011import java.awt.event.WindowEvent; 012import java.io.File; 013import java.io.IOException; 014import java.io.InputStream; 015import java.net.Authenticator; 016import java.net.Inet6Address; 017import java.net.InetAddress; 018import java.net.InetSocketAddress; 019import java.net.ProxySelector; 020import java.net.Socket; 021import java.net.URL; 022import java.security.AllPermission; 023import java.security.CodeSource; 024import java.security.KeyStoreException; 025import java.security.NoSuchAlgorithmException; 026import java.security.PermissionCollection; 027import java.security.Permissions; 028import java.security.Policy; 029import java.security.cert.CertificateException; 030import java.util.ArrayList; 031import java.util.Arrays; 032import java.util.Collection; 033import java.util.EnumMap; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.Locale; 037import java.util.Map; 038import java.util.Set; 039import java.util.TreeSet; 040import java.util.concurrent.Callable; 041 042import javax.swing.JFrame; 043import javax.swing.JOptionPane; 044import javax.swing.RepaintManager; 045import javax.swing.SwingUtilities; 046 047import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager; 048import org.openstreetmap.josm.Main; 049import org.openstreetmap.josm.actions.PreferencesAction; 050import org.openstreetmap.josm.data.AutosaveTask; 051import org.openstreetmap.josm.data.CustomConfigurator; 052import org.openstreetmap.josm.data.Version; 053import org.openstreetmap.josm.gui.download.DownloadDialog; 054import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 055import org.openstreetmap.josm.gui.preferences.server.ProxyPreference; 056import org.openstreetmap.josm.gui.util.GuiHelper; 057import org.openstreetmap.josm.io.DefaultProxySelector; 058import org.openstreetmap.josm.io.MessageNotifier; 059import org.openstreetmap.josm.io.OnlineResource; 060import org.openstreetmap.josm.io.auth.CredentialsManager; 061import org.openstreetmap.josm.io.auth.DefaultAuthenticator; 062import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 063import org.openstreetmap.josm.plugins.PluginHandler; 064import org.openstreetmap.josm.plugins.PluginInformation; 065import org.openstreetmap.josm.tools.BugReportExceptionHandler; 066import org.openstreetmap.josm.tools.FontsManager; 067import org.openstreetmap.josm.tools.I18n; 068import org.openstreetmap.josm.tools.ImageProvider; 069import org.openstreetmap.josm.tools.OsmUrlToBounds; 070import org.openstreetmap.josm.tools.PlatformHookWindows; 071import org.openstreetmap.josm.tools.Utils; 072 073import gnu.getopt.Getopt; 074import gnu.getopt.LongOpt; 075 076/** 077 * Main window class application. 078 * 079 * @author imi 080 */ 081public class MainApplication extends Main { 082 083 /** 084 * Constructs a new {@code MainApplication}. 085 */ 086 public MainApplication() { 087 // Allow subclassing (see JOSM.java) 088 } 089 090 /** 091 * Constructs a main frame, ready sized and operating. Does not display the frame. 092 * @param mainFrame The main JFrame of the application 093 */ 094 public MainApplication(JFrame mainFrame) { 095 addListener(); 096 mainFrame.setContentPane(contentPanePrivate); 097 mainFrame.setJMenuBar(menu); 098 geometry.applySafe(mainFrame); 099 List<Image> l = new LinkedList<>(); 100 l.add(ImageProvider.get("logo_16x16x32").getImage()); 101 l.add(ImageProvider.get("logo_16x16x8").getImage()); 102 l.add(ImageProvider.get("logo_32x32x32").getImage()); 103 l.add(ImageProvider.get("logo_32x32x8").getImage()); 104 l.add(ImageProvider.get("logo_48x48x32").getImage()); 105 l.add(ImageProvider.get("logo_48x48x8").getImage()); 106 l.add(ImageProvider.get("logo").getImage()); 107 mainFrame.setIconImages(l); 108 mainFrame.addWindowListener(new WindowAdapter() { 109 @Override 110 public void windowClosing(final WindowEvent arg0) { 111 Main.exitJosm(true, 0); 112 } 113 }); 114 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 115 } 116 117 /** 118 * Displays help on the console 119 * @since 2748 120 */ 121 public static void showHelp() { 122 // TODO: put in a platformHook for system that have no console by default 123 System.out.println(tr("Java OpenStreetMap Editor")+" [" 124 +Version.getInstance().getAgentString()+"]\n\n"+ 125 tr("usage")+":\n"+ 126 "\tjava -jar josm.jar <options>...\n\n"+ 127 tr("options")+":\n"+ 128 "\t--help|-h "+tr("Show this help")+'\n'+ 129 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+ 130 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+ 131 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+ 132 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+ 133 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+ 134 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+ 135 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+ 136 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+ 137 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 138 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+ 139 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+ 140 "\t--language=<language> "+tr("Set the language")+"\n\n"+ 141 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+ 142 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+ 143 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+ 144 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+ 145 tr("options provided as Java system properties")+":\n"+ 146 "\t-Djosm.pref=" +tr("/PATH/TO/JOSM/PREF ")+tr("Set the preferences directory")+"\n\n"+ 147 "\t-Djosm.userdata="+tr("/PATH/TO/JOSM/USERDATA")+tr("Set the user data directory")+"\n\n"+ 148 "\t-Djosm.cache=" +tr("/PATH/TO/JOSM/CACHE ")+tr("Set the cache directory")+"\n\n"+ 149 "\t-Djosm.home=" +tr("/PATH/TO/JOSM/HOMEDIR ")+ 150 tr("Relocate all 3 directories to homedir. Cache directory will be in homedir/cache")+"\n\n"+ 151 tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+ 152 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" + 153 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+ 154 "\t-Xmx...m\n\n"+ 155 tr("examples")+":\n"+ 156 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+ 157 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+'\n'+ 158 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 159 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+ 160 "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+ 161 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+ 162 "\tjava -Xmx1024m -jar josm.jar\n\n"+ 163 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+'\n'+ 164 tr("Make sure you load some data if you use --selection.")+'\n' 165 ); 166 } 167 168 /** 169 * JOSM command line options. 170 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a> 171 * @since 5279 172 */ 173 public enum Option { 174 /** --help|-h Show this help */ 175 HELP(false), 176 /** --version Displays the JOSM version and exits */ 177 VERSION(false), 178 /** --debug Print debugging messages to console */ 179 DEBUG(false), 180 /** --trace Print detailed debugging messages to console */ 181 TRACE(false), 182 /** --language=<language> Set the language */ 183 LANGUAGE(true), 184 /** --reset-preferences Reset the preferences to default */ 185 RESET_PREFERENCES(false), 186 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */ 187 LOAD_PREFERENCES(true), 188 /** --set=<key>=<value> Set preference key to value */ 189 SET(true), 190 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */ 191 GEOMETRY(true), 192 /** --no-maximize Do not launch in maximized mode */ 193 NO_MAXIMIZE(false), 194 /** --maximize Launch in maximized mode */ 195 MAXIMIZE(false), 196 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br> 197 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br> 198 * --download=<filename> Open a file (any file type that can be opened with File/Open) */ 199 DOWNLOAD(true), 200 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br> 201 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */ 202 DOWNLOADGPS(true), 203 /** --selection=<searchstring> Select with the given search */ 204 SELECTION(true), 205 /** --offline=<osm_api|josm_website|all> Disable access to the given resource(s), delimited by comma */ 206 OFFLINE(true), 207 /** --skip-plugins */ 208 SKIP_PLUGINS(false); 209 210 private final String name; 211 private final boolean requiresArg; 212 213 Option(boolean requiresArgument) { 214 this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-'); 215 this.requiresArg = requiresArgument; 216 } 217 218 /** 219 * Replies the option name 220 * @return The option name, in lowercase 221 */ 222 public String getName() { 223 return name; 224 } 225 226 /** 227 * Determines if this option requires an argument. 228 * @return {@code true} if this option requires an argument, {@code false} otherwise 229 */ 230 public boolean requiresArgument() { 231 return requiresArg; 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 EnumMap<>(Option.class); 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 PermissionCollection getPermissions(CodeSource codesource) { 319 return pc; 320 } 321 }); 322 323 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 324 325 // initialize the platform hook, and 326 Main.determinePlatformHook(); 327 // call the really early hook before we do anything else 328 Main.platform.preStartupHook(); 329 330 Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray)); 331 332 if (args.containsKey(Option.VERSION)) { 333 System.out.println(Version.getInstance().getAgentString()); 334 System.exit(0); 335 } 336 337 if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) { 338 // Enable JOSM debug level 339 logLevel = 4; 340 Main.info(tr("Printing debugging messages to console")); 341 } 342 343 boolean skipLoadingPlugins = false; 344 if (args.containsKey(Option.SKIP_PLUGINS)) { 345 skipLoadingPlugins = true; 346 Main.info(tr("Plugin loading skipped")); 347 } 348 349 if (args.containsKey(Option.TRACE)) { 350 // Enable JOSM debug level 351 logLevel = 5; 352 // Enable debug in OAuth signpost via system preference, but only at trace level 353 Utils.updateSystemProperty("debug", "true"); 354 Main.info(tr("Enabled detailed debug level (trace)")); 355 } 356 357 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES)); 358 359 if (args.containsKey(Option.SET)) { 360 for (String i : args.get(Option.SET)) { 361 String[] kv = i.split("=", 2); 362 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]); 363 } 364 } 365 366 if (!languageGiven) { 367 I18n.set(Main.pref.get("language", null)); 368 } 369 Main.pref.updateSystemProperties(); 370 371 checkIPv6(); 372 373 // asking for help? show help and exit 374 if (args.containsKey(Option.HELP)) { 375 showHelp(); 376 System.exit(0); 377 } 378 379 processOffline(args); 380 381 Main.platform.afterPrefStartupHook(); 382 383 FontsManager.initialize(); 384 385 I18n.setupLanguageFonts(); 386 387 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor")); 388 Main.parent = mainFrame; 389 390 if (args.containsKey(Option.LOAD_PREFERENCES)) { 391 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 392 for (String i : args.get(Option.LOAD_PREFERENCES)) { 393 info("Reading preferences from " + i); 394 try (InputStream is = Utils.openURL(new URL(i))) { 395 config.openAndReadXML(is); 396 } catch (Exception ex) { 397 throw new RuntimeException(ex); 398 } 399 } 400 } 401 402 DefaultAuthenticator.createInstance(); 403 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 404 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault()); 405 ProxySelector.setDefault(proxySelector); 406 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 407 408 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(new Callable<SplashScreen>() { 409 @Override 410 public SplashScreen call() { 411 return new SplashScreen(); 412 } 413 }); 414 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor(); 415 monitor.beginTask(tr("Initializing")); 416 GuiHelper.runInEDT(new Runnable() { 417 @Override 418 public void run() { 419 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)); 420 } 421 }); 422 Main.setInitStatusListener(new InitStatusListener() { 423 424 @Override 425 public Object updateStatus(String event) { 426 monitor.beginTask(event); 427 return event; 428 } 429 430 @Override 431 public void finish(Object status) { 432 if (status instanceof String) { 433 monitor.finishTask((String) status); 434 } 435 } 436 }); 437 438 Collection<PluginInformation> pluginsToLoad = null; 439 440 441 if (!skipLoadingPlugins) { 442 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false)); 443 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 444 monitor.subTask(tr("Updating plugins")); 445 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false); 446 } 447 448 monitor.indeterminateSubTask(tr("Installing updated plugins")); 449 PluginHandler.installDownloadedPlugins(true); 450 451 monitor.indeterminateSubTask(tr("Loading early plugins")); 452 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 453 } 454 455 monitor.indeterminateSubTask(tr("Setting defaults")); 456 preConstructorInit(args); 457 458 monitor.indeterminateSubTask(tr("Creating main GUI")); 459 final Main main = new MainApplication(mainFrame); 460 461 if (!skipLoadingPlugins) { 462 monitor.indeterminateSubTask(tr("Loading plugins")); 463 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 464 toolbar.refreshToolbarControl(); 465 } 466 467 // Wait for splash disappearance (fix #9714) 468 GuiHelper.runInEDTAndWait(new Runnable() { 469 @Override 470 public void run() { 471 splash.setVisible(false); 472 splash.dispose(); 473 mainFrame.setVisible(true); 474 main.gettingStarted.requestFocusInWindow(); 475 } 476 }); 477 478 Main.MasterWindowListener.setup(); 479 480 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 481 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) { 482 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 483 Main.windowState = JFrame.MAXIMIZED_BOTH; 484 mainFrame.setExtendedState(Main.windowState); 485 } else { 486 Main.debug("Main window: maximizing not supported"); 487 } 488 } 489 if (main.menu.fullscreenToggleAction != null) { 490 main.menu.fullscreenToggleAction.initial(); 491 } 492 493 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 494 495 if (Main.isPlatformWindows()) { 496 try { 497 // Check for insecure certificates to remove. 498 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) 499 // neither startupHook (need to be called before remote control) 500 PlatformHookWindows.removeInsecureCertificates(); 501 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 502 error(e); 503 } 504 } 505 506 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 507 RemoteControl.start(); 508 } 509 510 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 511 MessageNotifier.start(); 512 } 513 514 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 515 // Repaint manager is registered so late for a reason - there is lots of violation during startup process 516 // but they don't seem to break anything and are difficult to fix 517 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 518 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 519 } 520 } 521 522 private static void processOffline(Map<Option, Collection<String>> args) { 523 if (args.containsKey(Option.OFFLINE)) { 524 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) { 525 try { 526 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 527 } catch (IllegalArgumentException e) { 528 Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.", 529 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values()))); 530 System.exit(1); 531 return; 532 } 533 } 534 Set<OnlineResource> offline = Main.getOfflineResources(); 535 if (!offline.isEmpty()) { 536 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 537 "JOSM is running in offline mode. These resources will not be available: {0}", 538 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 539 } 540 } 541 } 542 543 /** 544 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation, 545 * disabling or enabling IPV6 may only be done with next start. 546 */ 547 private static void checkIPv6() { 548 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) { 549 new Thread(new Runnable() { /* this may take some time (DNS, Connect) */ 550 public void run() { 551 boolean hasv6 = false; 552 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false); 553 try { 554 /* Use the check result from last run of the software, as after the test, value 555 changes have no effect anymore */ 556 if (wasv6) { 557 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 558 } 559 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) { 560 if (a instanceof Inet6Address) { 561 if (a.isReachable(1000)) { 562 /* be sure it REALLY works */ 563 Socket s = new Socket(); 564 s.connect(new InetSocketAddress(a, 80), 1000); 565 s.close(); 566 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 567 if (!wasv6) { 568 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart.")); 569 } else { 570 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4.")); 571 } 572 hasv6 = true; 573 } 574 break; /* we're done */ 575 } 576 } 577 } catch (IOException | SecurityException e) { 578 if (Main.isDebugEnabled()) { 579 Main.debug("Exception while checking IPv6 connectivity: "+e); 580 } 581 } 582 if (wasv6 && !hasv6) { 583 Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart.")); 584 } 585 Main.pref.put("validated.ipv6", hasv6); 586 } 587 }, "IPv6-checker").start(); 588 } 589 } 590 591 private static class GuiFinalizationWorker implements Runnable { 592 593 private final Map<Option, Collection<String>> args; 594 private final DefaultProxySelector proxySelector; 595 596 GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) { 597 this.args = args; 598 this.proxySelector = proxySelector; 599 } 600 601 @Override 602 public void run() { 603 604 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly 605 if (!handleProxyErrors()) { 606 handleNetworkErrors(); 607 } 608 609 // Restore autosave layers after crash and start autosave thread 610 handleAutosave(); 611 612 // Handle command line instructions 613 postConstructorProcessCmdLine(args); 614 615 // Show download dialog if autostart is enabled 616 DownloadDialog.autostartIfNeeded(); 617 } 618 619 private static void handleAutosave() { 620 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 621 AutosaveTask autosaveTask = new AutosaveTask(); 622 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 623 if (!unsavedLayerFiles.isEmpty()) { 624 ExtendedDialog dialog = new ExtendedDialog( 625 Main.parent, 626 tr("Unsaved osm data"), 627 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 628 ); 629 dialog.setContent( 630 trn("JOSM found {0} unsaved osm data layer. ", 631 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 632 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 633 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 634 int selection = dialog.showDialog().getValue(); 635 if (selection == 1) { 636 autosaveTask.recoverUnsavedLayers(); 637 } else if (selection == 3) { 638 autosaveTask.discardUnsavedLayers(); 639 } 640 } 641 autosaveTask.schedule(); 642 } 643 } 644 645 private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) { 646 if (hasErrors) { 647 ExtendedDialog ed = new ExtendedDialog( 648 Main.parent, title, 649 new String[]{tr("Change proxy settings"), tr("Cancel")}); 650 ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2); 651 ed.setMinimumSize(new Dimension(460, 260)); 652 ed.setIcon(JOptionPane.WARNING_MESSAGE); 653 ed.setContent(message); 654 655 if (ed.showDialog().getValue() == 1) { 656 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run(); 657 } 658 } 659 return hasErrors; 660 } 661 662 private boolean handleProxyErrors() { 663 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"), 664 tr("JOSM tried to access the following resources:<br>" + 665 "{0}" + 666 "but <b>failed</b> to do so, because of the following proxy errors:<br>" + 667 "{1}" + 668 "Would you like to change your proxy settings now?", 669 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()), 670 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages()) 671 )); 672 } 673 674 private boolean handleNetworkErrors() { 675 boolean condition = !NETWORK_ERRORS.isEmpty(); 676 if (condition) { 677 Set<String> errors = new TreeSet<>(); 678 for (Throwable t : NETWORK_ERRORS.values()) { 679 errors.add(t.toString()); 680 } 681 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"), 682 tr("JOSM tried to access the following resources:<br>" + 683 "{0}" + 684 "but <b>failed</b> to do so, because of the following network errors:<br>" + 685 "{1}" + 686 "It may be due to a missing proxy configuration.<br>" + 687 "Would you like to change your proxy settings now?", 688 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()), 689 Utils.joinAsHtmlUnorderedList(errors) 690 )); 691 } 692 return false; 693 } 694 } 695}