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