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