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

Last change on this file since 7392 was 7383, checked in by bastiK, 10 years ago

applied #10301 - extend display of maxspeed nodes (patch by Klumbumbus)
Include Droid Sans font in the JOSM binary distribution.
This unifies the font rendering on different platforms and allows geometric constructions with text (as demonstrated for maxspeed). Both regular and bold style are available.

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