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 DefaultAuthenticator.createInstance(); 404 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 405 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault()); 406 ProxySelector.setDefault(proxySelector); 407 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 408 409 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(new Callable<SplashScreen>() { 410 @Override 411 public SplashScreen call() { 412 return new SplashScreen(); 413 } 414 }); 415 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor(); 416 monitor.beginTask(tr("Initializing")); 417 GuiHelper.runInEDT(new Runnable() { 418 @Override 419 public void run() { 420 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)); 421 } 422 }); 423 Main.setInitStatusListener(new InitStatusListener() { 424 425 @Override 426 public Object updateStatus(String event) { 427 monitor.beginTask(event); 428 return event; 429 } 430 431 @Override 432 public void finish(Object status) { 433 if (status instanceof String) { 434 monitor.finishTask((String) status); 435 } 436 } 437 }); 438 439 Collection<PluginInformation> pluginsToLoad = null; 440 441 442 if (!skipLoadingPlugins) { 443 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false)); 444 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 445 monitor.subTask(tr("Updating plugins")); 446 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false); 447 } 448 449 monitor.indeterminateSubTask(tr("Installing updated plugins")); 450 PluginHandler.installDownloadedPlugins(true); 451 452 monitor.indeterminateSubTask(tr("Loading early plugins")); 453 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 454 } 455 456 monitor.indeterminateSubTask(tr("Setting defaults")); 457 preConstructorInit(args); 458 459 monitor.indeterminateSubTask(tr("Creating main GUI")); 460 final Main main = new MainApplication(mainFrame); 461 462 if (!skipLoadingPlugins) { 463 monitor.indeterminateSubTask(tr("Loading plugins")); 464 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 465 toolbar.refreshToolbarControl(); 466 } 467 468 // Wait for splash disappearance (fix #9714) 469 GuiHelper.runInEDTAndWait(new Runnable() { 470 @Override 471 public void run() { 472 splash.setVisible(false); 473 splash.dispose(); 474 mainFrame.setVisible(true); 475 main.gettingStarted.requestFocusInWindow(); 476 } 477 }); 478 479 Main.MasterWindowListener.setup(); 480 481 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 482 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) { 483 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 484 Main.windowState = JFrame.MAXIMIZED_BOTH; 485 mainFrame.setExtendedState(Main.windowState); 486 } else { 487 Main.debug("Main window: maximizing not supported"); 488 } 489 } 490 if (main.menu.fullscreenToggleAction != null) { 491 main.menu.fullscreenToggleAction.initial(); 492 } 493 494 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 495 496 if (Main.isPlatformWindows()) { 497 try { 498 // Check for insecure certificates to remove. 499 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) 500 // neither startupHook (need to be called before remote control) 501 PlatformHookWindows.removeInsecureCertificates(); 502 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 503 error(e); 504 } 505 } 506 507 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 508 RemoteControl.start(); 509 } 510 511 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 512 MessageNotifier.start(); 513 } 514 515 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 516 // Repaint manager is registered so late for a reason - there is lots of violation during startup process 517 // but they don't seem to break anything and are difficult to fix 518 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 519 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 520 } 521 } 522 523 private static void processOffline(Map<Option, Collection<String>> args) { 524 if (args.containsKey(Option.OFFLINE)) { 525 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) { 526 try { 527 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 528 } catch (IllegalArgumentException e) { 529 Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.", 530 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values()))); 531 System.exit(1); 532 return; 533 } 534 } 535 Set<OnlineResource> offline = Main.getOfflineResources(); 536 if (!offline.isEmpty()) { 537 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 538 "JOSM is running in offline mode. These resources will not be available: {0}", 539 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 540 } 541 } 542 } 543 544 /** 545 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation, 546 * disabling or enabling IPV6 may only be done with next start. 547 */ 548 private static void checkIPv6() { 549 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) { 550 new Thread(new Runnable() { /* this may take some time (DNS, Connect) */ 551 public void run() { 552 boolean hasv6 = false; 553 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false); 554 try { 555 /* Use the check result from last run of the software, as after the test, value 556 changes have no effect anymore */ 557 if (wasv6) { 558 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 559 } 560 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) { 561 if (a instanceof Inet6Address) { 562 if (a.isReachable(1000)) { 563 /* be sure it REALLY works */ 564 Socket s = new Socket(); 565 s.connect(new InetSocketAddress(a, 80), 1000); 566 s.close(); 567 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 568 if (!wasv6) { 569 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart.")); 570 } else { 571 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4.")); 572 } 573 hasv6 = true; 574 } 575 break; /* we're done */ 576 } 577 } 578 } catch (IOException | SecurityException e) { 579 if (Main.isDebugEnabled()) { 580 Main.debug("Exception while checking IPv6 connectivity: "+e); 581 } 582 } 583 if (wasv6 && !hasv6) { 584 Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart.")); 585 Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart! 586 new RestartAction().actionPerformed(null); 587 } 588 Main.pref.put("validated.ipv6", hasv6); 589 } 590 }, "IPv6-checker").start(); 591 } 592 } 593 594 private static class GuiFinalizationWorker implements Runnable { 595 596 private final Map<Option, Collection<String>> args; 597 private final DefaultProxySelector proxySelector; 598 599 GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) { 600 this.args = args; 601 this.proxySelector = proxySelector; 602 } 603 604 @Override 605 public void run() { 606 607 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly 608 if (!handleProxyErrors()) { 609 handleNetworkErrors(); 610 } 611 612 // Restore autosave layers after crash and start autosave thread 613 handleAutosave(); 614 615 // Handle command line instructions 616 postConstructorProcessCmdLine(args); 617 618 // Show download dialog if autostart is enabled 619 DownloadDialog.autostartIfNeeded(); 620 } 621 622 private static void handleAutosave() { 623 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 624 AutosaveTask autosaveTask = new AutosaveTask(); 625 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 626 if (!unsavedLayerFiles.isEmpty()) { 627 ExtendedDialog dialog = new ExtendedDialog( 628 Main.parent, 629 tr("Unsaved osm data"), 630 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 631 ); 632 dialog.setContent( 633 trn("JOSM found {0} unsaved osm data layer. ", 634 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 635 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 636 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 637 int selection = dialog.showDialog().getValue(); 638 if (selection == 1) { 639 autosaveTask.recoverUnsavedLayers(); 640 } else if (selection == 3) { 641 autosaveTask.discardUnsavedLayers(); 642 } 643 } 644 autosaveTask.schedule(); 645 } 646 } 647 648 private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) { 649 if (hasErrors) { 650 ExtendedDialog ed = new ExtendedDialog( 651 Main.parent, title, 652 new String[]{tr("Change proxy settings"), tr("Cancel")}); 653 ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2); 654 ed.setMinimumSize(new Dimension(460, 260)); 655 ed.setIcon(JOptionPane.WARNING_MESSAGE); 656 ed.setContent(message); 657 658 if (ed.showDialog().getValue() == 1) { 659 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run(); 660 } 661 } 662 return hasErrors; 663 } 664 665 private boolean handleProxyErrors() { 666 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"), 667 tr("JOSM tried to access the following resources:<br>" + 668 "{0}" + 669 "but <b>failed</b> to do so, because of the following proxy errors:<br>" + 670 "{1}" + 671 "Would you like to change your proxy settings now?", 672 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()), 673 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages()) 674 )); 675 } 676 677 private boolean handleNetworkErrors() { 678 boolean condition = !NETWORK_ERRORS.isEmpty(); 679 if (condition) { 680 Set<String> errors = new TreeSet<>(); 681 for (Throwable t : NETWORK_ERRORS.values()) { 682 errors.add(t.toString()); 683 } 684 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"), 685 tr("JOSM tried to access the following resources:<br>" + 686 "{0}" + 687 "but <b>failed</b> to do so, because of the following network errors:<br>" + 688 "{1}" + 689 "It may be due to a missing proxy configuration.<br>" + 690 "Would you like to change your proxy settings now?", 691 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()), 692 Utils.joinAsHtmlUnorderedList(errors) 693 )); 694 } 695 return false; 696 } 697 } 698}