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.Image; 010import java.awt.Toolkit; 011import java.awt.event.WindowAdapter; 012import java.awt.event.WindowEvent; 013import java.io.File; 014import java.net.Authenticator; 015import java.net.ProxySelector; 016import java.net.URL; 017import java.security.AllPermission; 018import java.security.CodeSource; 019import java.security.PermissionCollection; 020import java.security.Permissions; 021import java.security.Policy; 022import java.util.ArrayList; 023import java.util.Collection; 024import java.util.HashMap; 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Map; 028 029import javax.swing.JFrame; 030import javax.swing.RepaintManager; 031import javax.swing.SwingUtilities; 032 033import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager; 034import org.openstreetmap.josm.Main; 035import org.openstreetmap.josm.data.AutosaveTask; 036import org.openstreetmap.josm.data.CustomConfigurator; 037import org.openstreetmap.josm.data.Preferences; 038import org.openstreetmap.josm.data.Version; 039import org.openstreetmap.josm.gui.download.DownloadDialog; 040import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 041import org.openstreetmap.josm.gui.progress.ProgressMonitor; 042import org.openstreetmap.josm.gui.util.GuiHelper; 043import org.openstreetmap.josm.io.DefaultProxySelector; 044import org.openstreetmap.josm.io.MessageNotifier; 045import org.openstreetmap.josm.io.auth.CredentialsManager; 046import org.openstreetmap.josm.io.auth.DefaultAuthenticator; 047import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 048import org.openstreetmap.josm.plugins.PluginHandler; 049import org.openstreetmap.josm.plugins.PluginInformation; 050import org.openstreetmap.josm.tools.BugReportExceptionHandler; 051import org.openstreetmap.josm.tools.I18n; 052import org.openstreetmap.josm.tools.ImageProvider; 053import org.openstreetmap.josm.tools.OsmUrlToBounds; 054import org.openstreetmap.josm.tools.Utils; 055 056/** 057 * Main window class application. 058 * 059 * @author imi 060 */ 061public class MainApplication extends Main { 062 /** 063 * Allow subclassing (see JOSM.java) 064 */ 065 public MainApplication() {} 066 067 /** 068 * Constructs a main frame, ready sized and operating. Does not display the frame. 069 * @param mainFrame The main JFrame of the application 070 */ 071 public MainApplication(JFrame mainFrame) { 072 addListener(); 073 mainFrame.setContentPane(contentPanePrivate); 074 mainFrame.setJMenuBar(menu); 075 geometry.applySafe(mainFrame); 076 LinkedList<Image> l = new LinkedList<Image>(); 077 l.add(ImageProvider.get("logo_16x16x32").getImage()); 078 l.add(ImageProvider.get("logo_16x16x8").getImage()); 079 l.add(ImageProvider.get("logo_32x32x32").getImage()); 080 l.add(ImageProvider.get("logo_32x32x8").getImage()); 081 l.add(ImageProvider.get("logo_48x48x32").getImage()); 082 l.add(ImageProvider.get("logo_48x48x8").getImage()); 083 l.add(ImageProvider.get("logo").getImage()); 084 mainFrame.setIconImages(l); 085 mainFrame.addWindowListener(new WindowAdapter(){ 086 @Override public void windowClosing(final WindowEvent arg0) { 087 Main.exitJosm(true, 0); 088 } 089 }); 090 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 091 } 092 093 /** 094 * Displays help on the console 095 * @since 2748 096 */ 097 public static void showHelp() { 098 // TODO: put in a platformHook for system that have no console by default 099 System.out.println(tr("Java OpenStreetMap Editor")+" [" 100 +Version.getInstance().getAgentString()+"]\n\n"+ 101 tr("usage")+":\n"+ 102 "\tjava -jar josm.jar <options>...\n\n"+ 103 tr("options")+":\n"+ 104 "\t--help|-h "+tr("Show this help")+"\n"+ 105 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+"\n"+ 106 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+"\n"+ 107 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+"\n"+ 108 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+"\n"+ 109 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+"\n"+ 110 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+"\n"+ 111 "\t--selection=<searchstring> "+tr("Select with the given search")+"\n"+ 112 "\t--[no-]maximize "+tr("Launch in maximized mode")+"\n"+ 113 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 114 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+ 115 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+ 116 "\t--language=<language> "+tr("Set the language")+"\n\n"+ 117 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+ 118 tr("options provided as Java system properties")+":\n"+ 119 "\t-Djosm.home="+tr("/PATH/TO/JOSM/FOLDER/ ")+tr("Change the folder for all user settings")+"\n\n"+ 120 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" + 121 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+ 122 "\t-Xmx...m\n\n"+ 123 tr("examples")+":\n"+ 124 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+ 125 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+"\n"+ 126 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 127 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+ 128 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+ 129 "\tjava -Xmx1024m -jar josm.jar\n\n"+ 130 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+"\n"+ 131 tr("Make sure you load some data if you use --selection.")+"\n" 132 ); 133 } 134 135 /** 136 * JOSM command line options. 137 * @see <a href="http://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a> 138 * @since 5279 139 */ 140 public enum Option { 141 /** --help|-h Show this help */ 142 HELP(false), 143 /** --version Displays the JOSM version and exits */ 144 VERSION(false), 145 /** --language=<language> Set the language */ 146 LANGUAGE(true), 147 /** --reset-preferences Reset the preferences to default */ 148 RESET_PREFERENCES(false), 149 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */ 150 LOAD_PREFERENCES(true), 151 /** --set=<key>=<value> Set preference key to value */ 152 SET(true), 153 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */ 154 GEOMETRY(true), 155 /** --no-maximize Do not launch in maximized mode */ 156 NO_MAXIMIZE(false), 157 /** --maximize Launch in maximized mode */ 158 MAXIMIZE(false), 159 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br> 160 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br> 161 * --download=<filename> Open a file (any file type that can be opened with File/Open) */ 162 DOWNLOAD(true), 163 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br> 164 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */ 165 DOWNLOADGPS(true), 166 /** --selection=<searchstring> Select with the given search */ 167 SELECTION(true); 168 169 private String name; 170 private boolean requiresArgument; 171 172 private Option(boolean requiresArgument) { 173 this.name = name().toLowerCase().replace("_", "-"); 174 this.requiresArgument = requiresArgument; 175 } 176 177 /** 178 * Replies the option name 179 * @return The option name, in lowercase 180 */ 181 public String getName() { 182 return name; 183 } 184 185 /** 186 * Determines if this option requires an argument. 187 * @return {@code true} if this option requires an argument, {@code false} otherwise 188 */ 189 public boolean requiresArgument() { 190 return requiresArgument; 191 } 192 193 public static Map<Option, Collection<String>> fromStringMap(Map<String, Collection<String>> opts) { 194 Map<Option, Collection<String>> res = new HashMap<Option, Collection<String>>(); 195 for (Map.Entry<String, Collection<String>> e : opts.entrySet()) { 196 Option o = Option.valueOf(e.getKey().toUpperCase().replace("-", "_")); 197 if (o != null) { 198 res.put(o, e.getValue()); 199 } 200 } 201 return res; 202 } 203 } 204 205 private static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) { 206 207 List<LongOpt> los = new ArrayList<LongOpt>(); 208 for (Option o : Option.values()) { 209 los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0)); 210 } 211 212 Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()])); 213 214 Map<Option, Collection<String>> argMap = new HashMap<Option, Collection<String>>(); 215 216 int c; 217 while ((c = g.getopt()) != -1 ) { 218 Option opt = null; 219 switch (c) { 220 case 'h': 221 opt = Option.HELP; 222 break; 223 case 'v': 224 opt = Option.VERSION; 225 break; 226 case 0: 227 opt = Option.values()[g.getLongind()]; 228 break; 229 } 230 if (opt != null) { 231 Collection<String> values = argMap.get(opt); 232 if (values == null) { 233 values = new ArrayList<String>(); 234 argMap.put(opt, values); 235 } 236 values.add(g.getOptarg()); 237 } else 238 throw new IllegalArgumentException(); 239 } 240 // positional arguments are a shortcut for the --download ... option 241 for (int i = g.getOptind(); i < args.length; ++i) { 242 Collection<String> values = argMap.get(Option.DOWNLOAD); 243 if (values == null) { 244 values = new ArrayList<String>(); 245 argMap.put(Option.DOWNLOAD, values); 246 } 247 values.add(args[i]); 248 } 249 250 return argMap; 251 } 252 253 /** 254 * Main application Startup 255 * @param argArray Command-line arguments 256 */ 257 public static void main(final String[] argArray) { 258 I18n.init(); 259 Main.checkJava6(); 260 261 // construct argument table 262 Map<Option, Collection<String>> args = null; 263 try { 264 args = buildCommandLineArgumentMap(argArray); 265 } catch (IllegalArgumentException e) { 266 System.exit(1); 267 } 268 269 final boolean languageGiven = args.containsKey(Option.LANGUAGE); 270 271 if (languageGiven) { 272 I18n.set(args.get(Option.LANGUAGE).iterator().next()); 273 } 274 275 initApplicationPreferences(); 276 277 Policy.setPolicy(new Policy() { 278 // Permissions for plug-ins loaded when josm is started via webstart 279 private PermissionCollection pc; 280 281 { 282 pc = new Permissions(); 283 pc.add(new AllPermission()); 284 } 285 286 @Override 287 public void refresh() { } 288 289 @Override 290 public PermissionCollection getPermissions(CodeSource codesource) { 291 return pc; 292 } 293 }); 294 295 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 296 // http://stuffthathappens.com/blog/2007/10/15/one-more-note-on-uncaught-exception-handlers/ 297 System.setProperty("sun.awt.exception.handler", BugReportExceptionHandler.class.getName()); 298 299 // initialize the platform hook, and 300 Main.determinePlatformHook(); 301 // call the really early hook before we do anything else 302 Main.platform.preStartupHook(); 303 304 Main.commandLineArgs = Utils.copyArray(argArray); 305 306 if (args.containsKey(Option.VERSION)) { 307 System.out.println(Version.getInstance().getAgentString()); 308 System.exit(0); 309 } 310 311 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES)); 312 313 if (!languageGiven) { 314 I18n.set(Main.pref.get("language", null)); 315 } 316 Main.pref.updateSystemProperties(); 317 318 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor")); 319 Main.parent = mainFrame; 320 321 if (args.containsKey(Option.LOAD_PREFERENCES)) { 322 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 323 for (String i : args.get(Option.LOAD_PREFERENCES)) { 324 info("Reading preferences from " + i); 325 try { 326 config.openAndReadXML(Utils.openURL(new URL(i))); 327 } catch (Exception ex) { 328 throw new RuntimeException(ex); 329 } 330 } 331 } 332 333 if (args.containsKey(Option.SET)) { 334 for (String i : args.get(Option.SET)) { 335 String[] kv = i.split("=", 2); 336 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]); 337 } 338 } 339 340 DefaultAuthenticator.createInstance(); 341 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 342 ProxySelector.setDefault(new DefaultProxySelector(ProxySelector.getDefault())); 343 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 344 345 // asking for help? show help and exit 346 if (args.containsKey(Option.HELP)) { 347 showHelp(); 348 System.exit(0); 349 } 350 351 final SplashScreen splash = new SplashScreen(); 352 final ProgressMonitor monitor = splash.getProgressMonitor(); 353 monitor.beginTask(tr("Initializing")); 354 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)); 355 Main.setInitStatusListener(new InitStatusListener() { 356 357 @Override 358 public void updateStatus(String event) { 359 monitor.indeterminateSubTask(event); 360 } 361 }); 362 363 List<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false)); 364 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 365 monitor.subTask(tr("Updating plugins")); 366 pluginsToLoad = PluginHandler.updatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 367 } 368 369 monitor.indeterminateSubTask(tr("Installing updated plugins")); 370 PluginHandler.installDownloadedPlugins(true); 371 372 monitor.indeterminateSubTask(tr("Loading early plugins")); 373 PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 374 375 monitor.indeterminateSubTask(tr("Setting defaults")); 376 preConstructorInit(args); 377 378 monitor.indeterminateSubTask(tr("Creating main GUI")); 379 final Main main = new MainApplication(mainFrame); 380 381 monitor.indeterminateSubTask(tr("Loading plugins")); 382 PluginHandler.loadLatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 383 toolbar.refreshToolbarControl(); 384 385 GuiHelper.runInEDT(new Runnable() { 386 @Override 387 public void run() { 388 splash.setVisible(false); 389 splash.dispose(); 390 mainFrame.setVisible(true); 391 } 392 }); 393 394 Main.MasterWindowListener.setup(); 395 396 boolean maximized = Boolean.parseBoolean(Main.pref.get("gui.maximized")); 397 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) { 398 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 399 Main.windowState = JFrame.MAXIMIZED_BOTH; 400 mainFrame.setExtendedState(Main.windowState); 401 } else { 402 Main.debug("Main window: maximizing not supported"); 403 } 404 } 405 if (main.menu.fullscreenToggleAction != null) { 406 main.menu.fullscreenToggleAction.initial(); 407 } 408 409 final Map<Option, Collection<String>> args_final = args; 410 411 SwingUtilities.invokeLater(new Runnable() { 412 @Override 413 public void run() { 414 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 415 AutosaveTask autosaveTask = new AutosaveTask(); 416 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 417 if (!unsavedLayerFiles.isEmpty()) { 418 ExtendedDialog dialog = new ExtendedDialog( 419 Main.parent, 420 tr("Unsaved osm data"), 421 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 422 ); 423 dialog.setContent( 424 trn("JOSM found {0} unsaved osm data layer. ", 425 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 426 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 427 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 428 int selection = dialog.showDialog().getValue(); 429 if (selection == 1) { 430 autosaveTask.recoverUnsavedLayers(); 431 } else if (selection == 3) { 432 autosaveTask.dicardUnsavedLayers(); 433 } 434 } 435 autosaveTask.schedule(); 436 } 437 438 postConstructorProcessCmdLine(args_final); 439 440 DownloadDialog.autostartIfNeeded(); 441 } 442 }); 443 444 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 445 RemoteControl.start(); 446 } 447 448 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 449 MessageNotifier.start(); 450 } 451 452 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 453 // 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 454 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 455 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 456 } 457 } 458}