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

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

see #8465 - switch core to Java 7

  • Property svn:eol-style set to native
File size: 23.8 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 // https://stackoverflow.com/q/75218/2257172
307 // To be replaced with official API when switching to Java 7: https://bugs.openjdk.java.net/browse/JDK-4714232
308 Preferences.updateSystemProperty("sun.awt.exception.handler", BugReportExceptionHandler.class.getName());
309
310 // initialize the platform hook, and
311 Main.determinePlatformHook();
312 // call the really early hook before we do anything else
313 Main.platform.preStartupHook();
314
315 Main.commandLineArgs = Utils.copyArray(argArray);
316
317 if (args.containsKey(Option.VERSION)) {
318 System.out.println(Version.getInstance().getAgentString());
319 System.exit(0);
320 }
321
322 if (args.containsKey(Option.DEBUG)) {
323 // Enable JOSM debug level
324 logLevel = 4;
325 // Enable debug in OAuth signpost
326 Preferences.updateSystemProperty("debug", "true");
327 Main.debug(tr("Print debugging messages to console"));
328 }
329
330 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
331
332 if (!languageGiven) {
333 I18n.set(Main.pref.get("language", null));
334 }
335 Main.pref.updateSystemProperties();
336
337 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
338 Main.parent = mainFrame;
339
340 if (args.containsKey(Option.LOAD_PREFERENCES)) {
341 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
342 for (String i : args.get(Option.LOAD_PREFERENCES)) {
343 info("Reading preferences from " + i);
344 try {
345 config.openAndReadXML(Utils.openURL(new URL(i)));
346 } catch (Exception ex) {
347 throw new RuntimeException(ex);
348 }
349 }
350 }
351
352 if (args.containsKey(Option.SET)) {
353 for (String i : args.get(Option.SET)) {
354 String[] kv = i.split("=", 2);
355 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
356 }
357 }
358
359 DefaultAuthenticator.createInstance();
360 Authenticator.setDefault(DefaultAuthenticator.getInstance());
361 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
362 ProxySelector.setDefault(proxySelector);
363 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
364
365 // asking for help? show help and exit
366 if (args.containsKey(Option.HELP)) {
367 showHelp();
368 System.exit(0);
369 }
370
371 final SplashScreen splash = new SplashScreen();
372 final ProgressMonitor monitor = splash.getProgressMonitor();
373 monitor.beginTask(tr("Initializing"));
374 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
375 Main.setInitStatusListener(new InitStatusListener() {
376
377 @Override
378 public void updateStatus(String event) {
379 monitor.indeterminateSubTask(event);
380 }
381 });
382
383 Collection<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false));
384 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
385 monitor.subTask(tr("Updating plugins"));
386 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
387 }
388
389 monitor.indeterminateSubTask(tr("Installing updated plugins"));
390 PluginHandler.installDownloadedPlugins(true);
391
392 monitor.indeterminateSubTask(tr("Loading early plugins"));
393 PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
394
395 monitor.indeterminateSubTask(tr("Setting defaults"));
396 preConstructorInit(args);
397
398 monitor.indeterminateSubTask(tr("Creating main GUI"));
399 final Main main = new MainApplication(mainFrame);
400
401 monitor.indeterminateSubTask(tr("Loading plugins"));
402 PluginHandler.loadLatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
403 toolbar.refreshToolbarControl();
404
405 // Wait for splash disappearance (fix #9714)
406 GuiHelper.runInEDTAndWait(new Runnable() {
407 @Override
408 public void run() {
409 splash.setVisible(false);
410 splash.dispose();
411 mainFrame.setVisible(true);
412 }
413 });
414
415 Main.MasterWindowListener.setup();
416
417 boolean maximized = Main.pref.getBoolean("gui.maximized", false);
418 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
419 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
420 Main.windowState = JFrame.MAXIMIZED_BOTH;
421 mainFrame.setExtendedState(Main.windowState);
422 } else {
423 Main.debug("Main window: maximizing not supported");
424 }
425 }
426 if (main.menu.fullscreenToggleAction != null) {
427 main.menu.fullscreenToggleAction.initial();
428 }
429
430 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
431
432 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
433 RemoteControl.start();
434 }
435
436 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
437 MessageNotifier.start();
438 }
439
440 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
441 // 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
442 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
443 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
444 }
445 }
446
447 private static class GuiFinalizationWorker implements Runnable {
448
449 private final Map<Option, Collection<String>> args;
450 private final DefaultProxySelector proxySelector;
451
452 public GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
453 this.args = args;
454 this.proxySelector = proxySelector;
455 }
456
457 @Override
458 public void run() {
459
460 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
461 if (!handleProxyErrors()) {
462 handleNetworkErrors();
463 }
464
465 // Restore autosave layers after crash and start autosave thread
466 handleAutosave();
467
468 // Handle command line instructions
469 postConstructorProcessCmdLine(args);
470
471 // Show download dialog if autostart is enabled
472 DownloadDialog.autostartIfNeeded();
473 }
474
475 private void handleAutosave() {
476 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
477 AutosaveTask autosaveTask = new AutosaveTask();
478 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
479 if (!unsavedLayerFiles.isEmpty()) {
480 ExtendedDialog dialog = new ExtendedDialog(
481 Main.parent,
482 tr("Unsaved osm data"),
483 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
484 );
485 dialog.setContent(
486 trn("JOSM found {0} unsaved osm data layer. ",
487 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
488 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
489 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"});
490 int selection = dialog.showDialog().getValue();
491 if (selection == 1) {
492 autosaveTask.recoverUnsavedLayers();
493 } else if (selection == 3) {
494 autosaveTask.discardUnsavedLayers();
495 }
496 }
497 autosaveTask.schedule();
498 }
499 }
500
501 private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
502 if (hasErrors) {
503 ExtendedDialog ed = new ExtendedDialog(
504 Main.parent, title,
505 new String[]{tr("Change proxy settings"), tr("Cancel")});
506 ed.setButtonIcons(new String[]{"dialogs/settings.png", "cancel.png"}).setCancelButton(2);
507 ed.setMinimumSize(new Dimension(460, 260));
508 ed.setIcon(JOptionPane.WARNING_MESSAGE);
509 ed.setContent(message);
510
511 if (ed.showDialog().getValue() == 1) {
512 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
513 }
514 }
515 return hasErrors;
516 }
517
518 private boolean handleProxyErrors() {
519 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
520 tr("JOSM tried to access the following resources:<br>" +
521 "{0}" +
522 "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
523 "{1}" +
524 "Would you like to change your proxy settings now?",
525 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
526 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
527 ));
528 }
529
530 private boolean handleNetworkErrors() {
531 boolean condition = !NETWORK_ERRORS.isEmpty();
532 if (condition) {
533 Set<String> errors = new TreeSet<String>();
534 for (Throwable t : NETWORK_ERRORS.values()) {
535 errors.add(t.toString());
536 }
537 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
538 tr("JOSM tried to access the following resources:<br>" +
539 "{0}" +
540 "but <b>failed</b> to do so, because of the following network errors:<br>" +
541 "{1}" +
542 "It may result of a missing proxy configuration.<br>" +
543 "Would you like to change your proxy settings now?",
544 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()),
545 Utils.joinAsHtmlUnorderedList(errors)
546 ));
547 }
548 return false;
549 }
550 }
551}
Note: See TracBrowser for help on using the repository browser.