source: josm/trunk/src/org/openstreetmap/josm/gui/MainApplication.java@ 7002

Last change on this file since 7002 was 7002, checked in by Don-vip, 10 years ago

see #8465 - sun.awt.exception.handler hack removed in Java 7

  • Property svn:eol-style set to native
File size: 23.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6import gnu.getopt.Getopt;
7import gnu.getopt.LongOpt;
8
9import java.awt.Dimension;
10import java.awt.Image;
11import java.awt.Toolkit;
12import java.awt.event.WindowAdapter;
13import java.awt.event.WindowEvent;
14import java.io.File;
15import java.net.Authenticator;
16import java.net.ProxySelector;
17import java.net.URL;
18import java.security.AllPermission;
19import java.security.CodeSource;
20import java.security.PermissionCollection;
21import java.security.Permissions;
22import java.security.Policy;
23import java.util.ArrayList;
24import java.util.Collection;
25import java.util.HashMap;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import java.util.TreeSet;
31
32import javax.swing.JFrame;
33import javax.swing.JOptionPane;
34import javax.swing.RepaintManager;
35import javax.swing.SwingUtilities;
36
37import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
38import org.openstreetmap.josm.Main;
39import org.openstreetmap.josm.actions.PreferencesAction;
40import org.openstreetmap.josm.data.AutosaveTask;
41import org.openstreetmap.josm.data.CustomConfigurator;
42import org.openstreetmap.josm.data.Preferences;
43import org.openstreetmap.josm.data.Version;
44import org.openstreetmap.josm.gui.download.DownloadDialog;
45import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
46import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
47import org.openstreetmap.josm.gui.progress.ProgressMonitor;
48import org.openstreetmap.josm.gui.util.GuiHelper;
49import org.openstreetmap.josm.io.DefaultProxySelector;
50import org.openstreetmap.josm.io.MessageNotifier;
51import org.openstreetmap.josm.io.auth.CredentialsManager;
52import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
53import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
54import org.openstreetmap.josm.plugins.PluginHandler;
55import org.openstreetmap.josm.plugins.PluginInformation;
56import org.openstreetmap.josm.tools.BugReportExceptionHandler;
57import org.openstreetmap.josm.tools.I18n;
58import org.openstreetmap.josm.tools.ImageProvider;
59import org.openstreetmap.josm.tools.OsmUrlToBounds;
60import org.openstreetmap.josm.tools.Utils;
61
62/**
63 * Main window class application.
64 *
65 * @author imi
66 */
67public class MainApplication extends Main {
68 /**
69 * Allow subclassing (see JOSM.java)
70 */
71 public MainApplication() {}
72
73 /**
74 * Constructs a main frame, ready sized and operating. Does not display the frame.
75 * @param mainFrame The main JFrame of the application
76 */
77 public MainApplication(JFrame mainFrame) {
78 addListener();
79 mainFrame.setContentPane(contentPanePrivate);
80 mainFrame.setJMenuBar(menu);
81 geometry.applySafe(mainFrame);
82 LinkedList<Image> l = new LinkedList<Image>();
83 l.add(ImageProvider.get("logo_16x16x32").getImage());
84 l.add(ImageProvider.get("logo_16x16x8").getImage());
85 l.add(ImageProvider.get("logo_32x32x32").getImage());
86 l.add(ImageProvider.get("logo_32x32x8").getImage());
87 l.add(ImageProvider.get("logo_48x48x32").getImage());
88 l.add(ImageProvider.get("logo_48x48x8").getImage());
89 l.add(ImageProvider.get("logo").getImage());
90 mainFrame.setIconImages(l);
91 mainFrame.addWindowListener(new WindowAdapter(){
92 @Override
93 public void windowClosing(final WindowEvent arg0) {
94 Main.exitJosm(true, 0);
95 }
96 });
97 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
98 }
99
100 /**
101 * Displays help on the console
102 * @since 2748
103 */
104 public static void showHelp() {
105 // TODO: put in a platformHook for system that have no console by default
106 System.out.println(tr("Java OpenStreetMap Editor")+" ["
107 +Version.getInstance().getAgentString()+"]\n\n"+
108 tr("usage")+":\n"+
109 "\tjava -jar josm.jar <options>...\n\n"+
110 tr("options")+":\n"+
111 "\t--help|-h "+tr("Show this help")+"\n"+
112 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+"\n"+
113 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+"\n"+
114 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+"\n"+
115 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+"\n"+
116 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+"\n"+
117 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+"\n"+
118 "\t--selection=<searchstring> "+tr("Select with the given search")+"\n"+
119 "\t--[no-]maximize "+tr("Launch in maximized mode")+"\n"+
120 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+
121 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+
122 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+
123 "\t--language=<language> "+tr("Set the language")+"\n\n"+
124 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+
125 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+
126 tr("options provided as Java system properties")+":\n"+
127 "\t-Djosm.home="+tr("/PATH/TO/JOSM/FOLDER/ ")+tr("Change the folder for all user settings")+"\n\n"+
128 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
129 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
130 "\t-Xmx...m\n\n"+
131 tr("examples")+":\n"+
132 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
133 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+"\n"+
134 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
135 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
136 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
137 "\tjava -Xmx1024m -jar josm.jar\n\n"+
138 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+"\n"+
139 tr("Make sure you load some data if you use --selection.")+"\n"
140 );
141 }
142
143 /**
144 * JOSM command line options.
145 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>
146 * @since 5279
147 */
148 public enum Option {
149 /** --help|-h Show this help */
150 HELP(false),
151 /** --version Displays the JOSM version and exits */
152 VERSION(false),
153 /** --debug Print debugging messages to console */
154 DEBUG(false),
155 /** --language=&lt;language&gt; Set the language */
156 LANGUAGE(true),
157 /** --reset-preferences Reset the preferences to default */
158 RESET_PREFERENCES(false),
159 /** --load-preferences=&lt;url-to-xml&gt; Changes preferences according to the XML file */
160 LOAD_PREFERENCES(true),
161 /** --set=&lt;key&gt;=&lt;value&gt; Set preference key to value */
162 SET(true),
163 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */
164 GEOMETRY(true),
165 /** --no-maximize Do not launch in maximized mode */
166 NO_MAXIMIZE(false),
167 /** --maximize Launch in maximized mode */
168 MAXIMIZE(false),
169 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br>
170 * --download=&lt;URL&gt; Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
171 * --download=&lt;filename&gt; Open a file (any file type that can be opened with File/Open) */
172 DOWNLOAD(true),
173 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br>
174 * --downloadgps=&lt;URL&gt; Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
175 DOWNLOADGPS(true),
176 /** --selection=&lt;searchstring&gt; Select with the given search */
177 SELECTION(true);
178
179 private String name;
180 private boolean requiresArgument;
181
182 private Option(boolean requiresArgument) {
183 this.name = name().toLowerCase().replace("_", "-");
184 this.requiresArgument = requiresArgument;
185 }
186
187 /**
188 * Replies the option name
189 * @return The option name, in lowercase
190 */
191 public String getName() {
192 return name;
193 }
194
195 /**
196 * Determines if this option requires an argument.
197 * @return {@code true} if this option requires an argument, {@code false} otherwise
198 */
199 public boolean requiresArgument() {
200 return requiresArgument;
201 }
202
203 public static Map<Option, Collection<String>> fromStringMap(Map<String, Collection<String>> opts) {
204 Map<Option, Collection<String>> res = new HashMap<Option, Collection<String>>();
205 for (Map.Entry<String, Collection<String>> e : opts.entrySet()) {
206 Option o = Option.valueOf(e.getKey().toUpperCase().replace("-", "_"));
207 if (o != null) {
208 res.put(o, e.getValue());
209 }
210 }
211 return res;
212 }
213 }
214
215 private static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) {
216
217 List<LongOpt> los = new ArrayList<LongOpt>();
218 for (Option o : Option.values()) {
219 los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));
220 }
221
222 Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()]));
223
224 Map<Option, Collection<String>> argMap = new HashMap<Option, Collection<String>>();
225
226 int c;
227 while ((c = g.getopt()) != -1 ) {
228 Option opt = null;
229 switch (c) {
230 case 'h':
231 opt = Option.HELP;
232 break;
233 case 'v':
234 opt = Option.VERSION;
235 break;
236 case 0:
237 opt = Option.values()[g.getLongind()];
238 break;
239 }
240 if (opt != null) {
241 Collection<String> values = argMap.get(opt);
242 if (values == null) {
243 values = new ArrayList<String>();
244 argMap.put(opt, values);
245 }
246 values.add(g.getOptarg());
247 } else
248 throw new IllegalArgumentException();
249 }
250 // positional arguments are a shortcut for the --download ... option
251 for (int i = g.getOptind(); i < args.length; ++i) {
252 Collection<String> values = argMap.get(Option.DOWNLOAD);
253 if (values == null) {
254 values = new ArrayList<String>();
255 argMap.put(Option.DOWNLOAD, values);
256 }
257 values.add(args[i]);
258 }
259
260 return argMap;
261 }
262
263 /**
264 * Main application Startup
265 * @param argArray Command-line arguments
266 */
267 public static void main(final String[] argArray) {
268 I18n.init();
269 Main.checkJavaVersion();
270
271 // construct argument table
272 Map<Option, Collection<String>> args = null;
273 try {
274 args = buildCommandLineArgumentMap(argArray);
275 } catch (IllegalArgumentException e) {
276 System.exit(1);
277 }
278
279 final boolean languageGiven = args.containsKey(Option.LANGUAGE);
280
281 if (languageGiven) {
282 I18n.set(args.get(Option.LANGUAGE).iterator().next());
283 }
284
285 initApplicationPreferences();
286
287 Policy.setPolicy(new Policy() {
288 // Permissions for plug-ins loaded when josm is started via webstart
289 private PermissionCollection pc;
290
291 {
292 pc = new Permissions();
293 pc.add(new AllPermission());
294 }
295
296 @Override
297 public void refresh() { }
298
299 @Override
300 public PermissionCollection getPermissions(CodeSource codesource) {
301 return pc;
302 }
303 });
304
305 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
306
307 // initialize the platform hook, and
308 Main.determinePlatformHook();
309 // call the really early hook before we do anything else
310 Main.platform.preStartupHook();
311
312 Main.commandLineArgs = Utils.copyArray(argArray);
313
314 if (args.containsKey(Option.VERSION)) {
315 System.out.println(Version.getInstance().getAgentString());
316 System.exit(0);
317 }
318
319 if (args.containsKey(Option.DEBUG)) {
320 // Enable JOSM debug level
321 logLevel = 4;
322 // Enable debug in OAuth signpost
323 Preferences.updateSystemProperty("debug", "true");
324 Main.debug(tr("Print debugging messages to console"));
325 }
326
327 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
328
329 if (!languageGiven) {
330 I18n.set(Main.pref.get("language", null));
331 }
332 Main.pref.updateSystemProperties();
333
334 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
335 Main.parent = mainFrame;
336
337 if (args.containsKey(Option.LOAD_PREFERENCES)) {
338 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
339 for (String i : args.get(Option.LOAD_PREFERENCES)) {
340 info("Reading preferences from " + i);
341 try {
342 config.openAndReadXML(Utils.openURL(new URL(i)));
343 } catch (Exception ex) {
344 throw new RuntimeException(ex);
345 }
346 }
347 }
348
349 if (args.containsKey(Option.SET)) {
350 for (String i : args.get(Option.SET)) {
351 String[] kv = i.split("=", 2);
352 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
353 }
354 }
355
356 DefaultAuthenticator.createInstance();
357 Authenticator.setDefault(DefaultAuthenticator.getInstance());
358 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
359 ProxySelector.setDefault(proxySelector);
360 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
361
362 // asking for help? show help and exit
363 if (args.containsKey(Option.HELP)) {
364 showHelp();
365 System.exit(0);
366 }
367
368 final SplashScreen splash = new SplashScreen();
369 final ProgressMonitor monitor = splash.getProgressMonitor();
370 monitor.beginTask(tr("Initializing"));
371 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
372 Main.setInitStatusListener(new InitStatusListener() {
373
374 @Override
375 public void updateStatus(String event) {
376 monitor.indeterminateSubTask(event);
377 }
378 });
379
380 Collection<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false));
381 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
382 monitor.subTask(tr("Updating plugins"));
383 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
384 }
385
386 monitor.indeterminateSubTask(tr("Installing updated plugins"));
387 PluginHandler.installDownloadedPlugins(true);
388
389 monitor.indeterminateSubTask(tr("Loading early plugins"));
390 PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
391
392 monitor.indeterminateSubTask(tr("Setting defaults"));
393 preConstructorInit(args);
394
395 monitor.indeterminateSubTask(tr("Creating main GUI"));
396 final Main main = new MainApplication(mainFrame);
397
398 monitor.indeterminateSubTask(tr("Loading plugins"));
399 PluginHandler.loadLatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
400 toolbar.refreshToolbarControl();
401
402 // Wait for splash disappearance (fix #9714)
403 GuiHelper.runInEDTAndWait(new Runnable() {
404 @Override
405 public void run() {
406 splash.setVisible(false);
407 splash.dispose();
408 mainFrame.setVisible(true);
409 }
410 });
411
412 Main.MasterWindowListener.setup();
413
414 boolean maximized = Main.pref.getBoolean("gui.maximized", false);
415 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
416 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
417 Main.windowState = JFrame.MAXIMIZED_BOTH;
418 mainFrame.setExtendedState(Main.windowState);
419 } else {
420 Main.debug("Main window: maximizing not supported");
421 }
422 }
423 if (main.menu.fullscreenToggleAction != null) {
424 main.menu.fullscreenToggleAction.initial();
425 }
426
427 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
428
429 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
430 RemoteControl.start();
431 }
432
433 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
434 MessageNotifier.start();
435 }
436
437 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
438 // 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
439 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
440 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
441 }
442 }
443
444 private static class GuiFinalizationWorker implements Runnable {
445
446 private final Map<Option, Collection<String>> args;
447 private final DefaultProxySelector proxySelector;
448
449 public GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
450 this.args = args;
451 this.proxySelector = proxySelector;
452 }
453
454 @Override
455 public void run() {
456
457 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
458 if (!handleProxyErrors()) {
459 handleNetworkErrors();
460 }
461
462 // Restore autosave layers after crash and start autosave thread
463 handleAutosave();
464
465 // Handle command line instructions
466 postConstructorProcessCmdLine(args);
467
468 // Show download dialog if autostart is enabled
469 DownloadDialog.autostartIfNeeded();
470 }
471
472 private void handleAutosave() {
473 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
474 AutosaveTask autosaveTask = new AutosaveTask();
475 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
476 if (!unsavedLayerFiles.isEmpty()) {
477 ExtendedDialog dialog = new ExtendedDialog(
478 Main.parent,
479 tr("Unsaved osm data"),
480 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
481 );
482 dialog.setContent(
483 trn("JOSM found {0} unsaved osm data layer. ",
484 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
485 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
486 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"});
487 int selection = dialog.showDialog().getValue();
488 if (selection == 1) {
489 autosaveTask.recoverUnsavedLayers();
490 } else if (selection == 3) {
491 autosaveTask.discardUnsavedLayers();
492 }
493 }
494 autosaveTask.schedule();
495 }
496 }
497
498 private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
499 if (hasErrors) {
500 ExtendedDialog ed = new ExtendedDialog(
501 Main.parent, title,
502 new String[]{tr("Change proxy settings"), tr("Cancel")});
503 ed.setButtonIcons(new String[]{"dialogs/settings.png", "cancel.png"}).setCancelButton(2);
504 ed.setMinimumSize(new Dimension(460, 260));
505 ed.setIcon(JOptionPane.WARNING_MESSAGE);
506 ed.setContent(message);
507
508 if (ed.showDialog().getValue() == 1) {
509 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
510 }
511 }
512 return hasErrors;
513 }
514
515 private boolean handleProxyErrors() {
516 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
517 tr("JOSM tried to access the following resources:<br>" +
518 "{0}" +
519 "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
520 "{1}" +
521 "Would you like to change your proxy settings now?",
522 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
523 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
524 ));
525 }
526
527 private boolean handleNetworkErrors() {
528 boolean condition = !NETWORK_ERRORS.isEmpty();
529 if (condition) {
530 Set<String> errors = new TreeSet<String>();
531 for (Throwable t : NETWORK_ERRORS.values()) {
532 errors.add(t.toString());
533 }
534 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
535 tr("JOSM tried to access the following resources:<br>" +
536 "{0}" +
537 "but <b>failed</b> to do so, because of the following network errors:<br>" +
538 "{1}" +
539 "It may result of a missing proxy configuration.<br>" +
540 "Would you like to change your proxy settings now?",
541 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()),
542 Utils.joinAsHtmlUnorderedList(errors)
543 ));
544 }
545 return false;
546 }
547 }
548}
Note: See TracBrowser for help on using the repository browser.