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

Last change on this file since 12655 was 12647, checked in by Don-vip, 7 years ago

see #15182 - remove unused method

  • Property svn:eol-style set to native
File size: 48.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;
6
7import java.awt.BorderLayout;
8import java.awt.Dimension;
9import java.awt.GraphicsEnvironment;
10import java.awt.event.KeyEvent;
11import java.io.File;
12import java.io.IOException;
13import java.io.InputStream;
14import java.net.Authenticator;
15import java.net.Inet6Address;
16import java.net.InetAddress;
17import java.net.ProxySelector;
18import java.net.URL;
19import java.security.AllPermission;
20import java.security.CodeSource;
21import java.security.GeneralSecurityException;
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.Arrays;
30import java.util.Collection;
31import java.util.Collections;
32import java.util.List;
33import java.util.Locale;
34import java.util.Map;
35import java.util.Optional;
36import java.util.Set;
37import java.util.TreeSet;
38import java.util.concurrent.Callable;
39import java.util.concurrent.ExecutorService;
40import java.util.concurrent.Future;
41import java.util.logging.Level;
42import java.util.stream.Collectors;
43import java.util.stream.Stream;
44
45import javax.net.ssl.SSLSocketFactory;
46import javax.swing.Action;
47import javax.swing.InputMap;
48import javax.swing.JComponent;
49import javax.swing.JOptionPane;
50import javax.swing.KeyStroke;
51import javax.swing.LookAndFeel;
52import javax.swing.RepaintManager;
53import javax.swing.SwingUtilities;
54import javax.swing.UIManager;
55import javax.swing.UnsupportedLookAndFeelException;
56
57import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
58import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
59import org.openstreetmap.josm.Main;
60import org.openstreetmap.josm.actions.JosmAction;
61import org.openstreetmap.josm.actions.OpenFileAction;
62import org.openstreetmap.josm.actions.PreferencesAction;
63import org.openstreetmap.josm.actions.RestartAction;
64import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
65import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
66import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
67import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
68import org.openstreetmap.josm.actions.mapmode.DrawAction;
69import org.openstreetmap.josm.actions.search.SearchAction;
70import org.openstreetmap.josm.data.Bounds;
71import org.openstreetmap.josm.data.UndoRedoHandler;
72import org.openstreetmap.josm.data.Version;
73import org.openstreetmap.josm.data.osm.DataSet;
74import org.openstreetmap.josm.data.osm.OsmPrimitive;
75import org.openstreetmap.josm.data.validation.OsmValidator;
76import org.openstreetmap.josm.gui.ProgramArguments.Option;
77import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor;
78import org.openstreetmap.josm.gui.download.DownloadDialog;
79import org.openstreetmap.josm.gui.io.CustomConfigurator.XMLCommandProcessor;
80import org.openstreetmap.josm.gui.io.SaveLayersDialog;
81import org.openstreetmap.josm.gui.layer.AutosaveTask;
82import org.openstreetmap.josm.gui.layer.MainLayerManager;
83import org.openstreetmap.josm.gui.layer.OsmDataLayer.CommandQueueListener;
84import org.openstreetmap.josm.gui.layer.TMSLayer;
85import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
86import org.openstreetmap.josm.gui.preferences.display.LafPreference;
87import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
88import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
89import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
90import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
91import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
92import org.openstreetmap.josm.gui.progress.ProgressMonitorExecutor;
93import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
94import org.openstreetmap.josm.gui.util.GuiHelper;
95import org.openstreetmap.josm.gui.util.RedirectInputMap;
96import org.openstreetmap.josm.io.CertificateAmendment;
97import org.openstreetmap.josm.io.DefaultProxySelector;
98import org.openstreetmap.josm.io.MessageNotifier;
99import org.openstreetmap.josm.io.OnlineResource;
100import org.openstreetmap.josm.io.OsmApi;
101import org.openstreetmap.josm.io.OsmApiInitializationException;
102import org.openstreetmap.josm.io.OsmTransferCanceledException;
103import org.openstreetmap.josm.io.auth.CredentialsManager;
104import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
105import org.openstreetmap.josm.io.protocols.data.Handler;
106import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
107import org.openstreetmap.josm.plugins.PluginHandler;
108import org.openstreetmap.josm.plugins.PluginInformation;
109import org.openstreetmap.josm.tools.FontsManager;
110import org.openstreetmap.josm.tools.HttpClient;
111import org.openstreetmap.josm.tools.I18n;
112import org.openstreetmap.josm.tools.ImageProvider;
113import org.openstreetmap.josm.tools.Logging;
114import org.openstreetmap.josm.tools.OpenBrowser;
115import org.openstreetmap.josm.tools.OsmUrlToBounds;
116import org.openstreetmap.josm.tools.OverpassTurboQueryWizard;
117import org.openstreetmap.josm.tools.PlatformHookWindows;
118import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
119import org.openstreetmap.josm.tools.Shortcut;
120import org.openstreetmap.josm.tools.Territories;
121import org.openstreetmap.josm.tools.Utils;
122import org.openstreetmap.josm.tools.WindowGeometry;
123import org.openstreetmap.josm.tools.bugreport.BugReport;
124import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
125
126/**
127 * Main window class application.
128 *
129 * @author imi
130 */
131public class MainApplication extends Main {
132
133 /**
134 * Command-line arguments used to run the application.
135 */
136 private static final List<String> COMMAND_LINE_ARGS = new ArrayList<>();
137
138 /**
139 * The main menu bar at top of screen.
140 */
141 static MainMenu menu;
142
143 /**
144 * The main panel, required to be static for {@link MapFrameListener} handling.
145 */
146 static MainPanel mainPanel;
147
148 /**
149 * The private content pane of {@link MainFrame}, required to be static for shortcut handling.
150 */
151 static JComponent contentPanePrivate;
152
153 /**
154 * The MapFrame.
155 */
156 static MapFrame map;
157
158 /**
159 * The toolbar preference control to register new actions.
160 */
161 static volatile ToolbarPreferences toolbar;
162
163 private final MainFrame mainFrame;
164
165 /**
166 * The worker thread slave. This is for executing all long and intensive
167 * calculations. The executed runnables are guaranteed to be executed separately and sequential.
168 * @since 12634 (as a replacement to {@code Main.worker})
169 */
170 public static final ExecutorService worker = new ProgressMonitorExecutor("main-worker-%d", Thread.NORM_PRIORITY);
171
172 /**
173 * Provides access to the layers displayed in the main view.
174 */
175 private static final MainLayerManager layerManager = new MainLayerManager();
176
177 /**
178 * The commands undo/redo handler.
179 * @since 12641 (as a replacement to {@code Main.main.undoRedo})
180 */
181 public static final UndoRedoHandler undoRedo = new UndoRedoHandler(); // Must be declared after layerManager
182
183 /**
184 * Listener that sets the enabled state of undo/redo menu entries.
185 */
186 private final CommandQueueListener redoUndoListener = (queueSize, redoSize) -> {
187 menu.undo.setEnabled(queueSize > 0);
188 menu.redo.setEnabled(redoSize > 0);
189 };
190
191 /**
192 * Constructs a new {@code MainApplication} without a window.
193 */
194 public MainApplication() {
195 this(null);
196 }
197
198 /**
199 * Constructs a main frame, ready sized and operating. Does not display the frame.
200 * @param mainFrame The main JFrame of the application
201 * @since 10340
202 */
203 public MainApplication(MainFrame mainFrame) {
204 this.mainFrame = mainFrame;
205 }
206
207 @Override
208 protected List<InitializationTask> beforeInitializationTasks() {
209 return Arrays.asList(
210 new InitializationTask(tr("Starting file watcher"), fileWatcher::start),
211 new InitializationTask(tr("Executing platform startup hook"), platform::startupHook),
212 new InitializationTask(tr("Building main menu"), this::initializeMainWindow),
213 new InitializationTask(tr("Updating user interface"), () -> {
214 undoRedo.addCommandQueueListener(redoUndoListener);
215 // creating toolbar
216 GuiHelper.runInEDTAndWait(() -> contentPanePrivate.add(toolbar.control, BorderLayout.NORTH));
217 // help shortcut
218 registerActionShortcut(menu.help, Shortcut.registerShortcut("system:help", tr("Help"),
219 KeyEvent.VK_F1, Shortcut.DIRECT));
220 }),
221 // This needs to be done before RightAndLefthandTraffic::initialize is called
222 new InitializationTask(tr("Initializing internal boundaries data"), Territories::initialize)
223 );
224 }
225
226 @Override
227 protected Collection<InitializationTask> parallelInitializationTasks() {
228 return Arrays.asList(
229 new InitializationTask(tr("Initializing OSM API"), () -> {
230 // We try to establish an API connection early, so that any API
231 // capabilities are already known to the editor instance. However
232 // if it goes wrong that's not critical at this stage.
233 try {
234 OsmApi.getOsmApi().initialize(null, true);
235 } catch (OsmTransferCanceledException | OsmApiInitializationException e) {
236 Logging.warn(Logging.getErrorMessage(Utils.getRootCause(e)));
237 }
238 }),
239 new InitializationTask(tr("Initializing internal traffic data"), RightAndLefthandTraffic::initialize),
240 new InitializationTask(tr("Initializing validator"), OsmValidator::initialize),
241 new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize),
242 new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize),
243 new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)
244 );
245 }
246
247 @Override
248 protected List<Callable<?>> asynchronousCallableTasks() {
249 return Arrays.asList(
250 OverpassTurboQueryWizard::getInstance
251 );
252 }
253
254 @Override
255 protected List<Runnable> asynchronousRunnableTasks() {
256 return Arrays.asList(
257 TMSLayer::getCache,
258 OsmValidator::initializeTests
259 );
260 }
261
262 @Override
263 protected List<InitializationTask> afterInitializationTasks() {
264 return Arrays.asList(
265 new InitializationTask(tr("Updating user interface"), () -> GuiHelper.runInEDTAndWait(() -> {
266 // hooks for the jmapviewer component
267 FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl);
268 FeatureAdapter.registerTranslationAdapter(I18n::tr);
269 FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger());
270 // UI update
271 toolbar.refreshToolbarControl();
272 toolbar.control.updateUI();
273 contentPanePrivate.updateUI();
274 }))
275 );
276 }
277
278 /**
279 * Called once at startup to initialize the main window content.
280 * Should set {@link #menu} and {@link #mainPanel}
281 */
282 @SuppressWarnings("deprecation")
283 protected void initializeMainWindow() {
284 if (mainFrame != null) {
285 mainPanel = mainFrame.getPanel();
286 panel = mainPanel;
287 mainFrame.initialize();
288 menu = mainFrame.getMenu();
289 super.menu = menu;
290 } else {
291 // required for running some tests.
292 mainPanel = new MainPanel(layerManager);
293 panel = mainPanel;
294 menu = new MainMenu();
295 super.menu = menu;
296 }
297 mainPanel.addMapFrameListener((o, n) -> redoUndoListener.commandChanged(0, 0));
298 mainPanel.reAddListeners();
299 }
300
301 @Override
302 protected void shutdown() {
303 if (!GraphicsEnvironment.isHeadless()) {
304 worker.shutdown();
305 }
306 if (mainFrame != null) {
307 mainFrame.storeState();
308 }
309 if (map != null) {
310 map.rememberToggleDialogWidth();
311 }
312 // Remove all layers because somebody may rely on layerRemoved events (like AutosaveTask)
313 layerManager.resetState();
314 super.shutdown();
315 if (!GraphicsEnvironment.isHeadless()) {
316 worker.shutdownNow();
317 }
318 }
319
320 @Override
321 protected Bounds getRealBounds() {
322 return isDisplayingMapView() ? map.mapView.getRealBounds() : null;
323 }
324
325 @Override
326 protected void restoreOldBounds(Bounds oldBounds) {
327 if (isDisplayingMapView()) {
328 map.mapView.zoomTo(oldBounds);
329 }
330 }
331
332 /**
333 * Replies the current selected primitives, from a end-user point of view.
334 * It is not always technically the same collection of primitives than {@link DataSet#getSelected()}.
335 * Indeed, if the user is currently in drawing mode, only the way currently being drawn is returned,
336 * see {@link DrawAction#getInProgressSelection()}.
337 *
338 * @return The current selected primitives, from a end-user point of view. Can be {@code null}.
339 * @since 6546
340 */
341 @Override
342 public Collection<OsmPrimitive> getInProgressSelection() {
343 if (map != null && map.mapMode instanceof DrawAction) {
344 return ((DrawAction) map.mapMode).getInProgressSelection();
345 } else {
346 DataSet ds = layerManager.getEditDataSet();
347 if (ds == null) return null;
348 return ds.getSelected();
349 }
350 }
351
352 /**
353 * Returns the command-line arguments used to run the application.
354 * @return the command-line arguments used to run the application
355 * @since 11650
356 */
357 public static List<String> getCommandLineArgs() {
358 return Collections.unmodifiableList(COMMAND_LINE_ARGS);
359 }
360
361 /**
362 * Returns the main layer manager that is used by the map view.
363 * @return The layer manager. The value returned will never change.
364 * @since 12636 (as a replacement to {@code Main.getLayerManager()})
365 */
366 @SuppressWarnings("deprecation")
367 public static MainLayerManager getLayerManager() {
368 return layerManager;
369 }
370
371 /**
372 * Returns the MapFrame.
373 * <p>
374 * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
375 * @return the MapFrame
376 * @see MainPanel
377 * @since 12630 (as a replacement to {@code Main.map})
378 */
379 public static MapFrame getMap() {
380 return map;
381 }
382
383 /**
384 * Returns the main panel.
385 * @return the main panel
386 * @since 12642 (as a replacement to {@code Main.main.panel})
387 */
388 public static MainPanel getMainPanel() {
389 return mainPanel;
390 }
391
392 /**
393 * Returns the main menu, at top of screen.
394 * @return the main menu
395 * @since 12643 (as a replacement to {@code MainApplication.getMenu()})
396 */
397 public static MainMenu getMenu() {
398 return menu;
399 }
400
401 /**
402 * Returns the toolbar preference control to register new actions.
403 * @return the toolbar preference control
404 * @since 12637 (as a replacement to {@code Main.toolbar})
405 */
406 public static ToolbarPreferences getToolbar() {
407 return toolbar;
408 }
409
410 /**
411 * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if
412 * it only shows the MOTD panel.
413 * <p>
414 * You do not need this when accessing the layer manager. The layer manager will be empty if no map view is shown.
415 *
416 * @return <code>true</code> if JOSM currently displays a map view
417 * @since 12630 (as a replacement to {@code Main.isDisplayingMapView()})
418 */
419 @SuppressWarnings("deprecation")
420 public static boolean isDisplayingMapView() {
421 return map != null && map.mapView != null;
422 }
423
424 /**
425 * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
426 * If there are some unsaved data layers, asks first for user confirmation.
427 * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
428 * @param exitCode The return code
429 * @param reason the reason for exiting
430 * @return {@code true} if JOSM has been closed, {@code false} if the user has cancelled the operation.
431 * @since 12636 (specialized version of {@link Main#exitJosm})
432 */
433 public static boolean exitJosm(boolean exit, int exitCode, SaveLayersDialog.Reason reason) {
434 final boolean proceed = Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() ->
435 SaveLayersDialog.saveUnsavedModifications(layerManager.getLayers(),
436 reason != null ? reason : SaveLayersDialog.Reason.EXIT)));
437 if (proceed) {
438 return Main.exitJosm(exit, exitCode);
439 }
440 return false;
441 }
442
443 public static void redirectToMainContentPane(JComponent source) {
444 RedirectInputMap.redirect(source, contentPanePrivate);
445 }
446
447 /**
448 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
449 * <p>
450 * It will fire an initial mapFrameInitialized event when the MapFrame is present.
451 * Otherwise will only fire when the MapFrame is created or destroyed.
452 * @param listener The MapFrameListener
453 * @return {@code true} if the listeners collection changed as a result of the call
454 * @see #addMapFrameListener
455 * @since 12639 (as a replacement to {@code Main.addAndFireMapFrameListener})
456 */
457 @SuppressWarnings("deprecation")
458 public static boolean addAndFireMapFrameListener(MapFrameListener listener) {
459 return mainPanel != null && mainPanel.addAndFireMapFrameListener(listener);
460 }
461
462 /**
463 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
464 * @param listener The MapFrameListener
465 * @return {@code true} if the listeners collection changed as a result of the call
466 * @see #addAndFireMapFrameListener
467 * @since 12639 (as a replacement to {@code Main.addMapFrameListener})
468 */
469 @SuppressWarnings("deprecation")
470 public static boolean addMapFrameListener(MapFrameListener listener) {
471 return mainPanel != null && mainPanel.addMapFrameListener(listener);
472 }
473
474 /**
475 * Unregisters the given {@code MapFrameListener} from MapFrame changes
476 * @param listener The MapFrameListener
477 * @return {@code true} if the listeners collection changed as a result of the call
478 * @since 12639 (as a replacement to {@code Main.removeMapFrameListener})
479 */
480 @SuppressWarnings("deprecation")
481 public static boolean removeMapFrameListener(MapFrameListener listener) {
482 return mainPanel != null && mainPanel.removeMapFrameListener(listener);
483 }
484
485 /**
486 * Registers a {@code JosmAction} and its shortcut.
487 * @param action action defining its own shortcut
488 * @since 12639 (as a replacement to {@code Main.registerActionShortcut})
489 */
490 @SuppressWarnings("deprecation")
491 public static void registerActionShortcut(JosmAction action) {
492 registerActionShortcut(action, action.getShortcut());
493 }
494
495 /**
496 * Registers an action and its shortcut.
497 * @param action action to register
498 * @param shortcut shortcut to associate to {@code action}
499 * @since 12639 (as a replacement to {@code Main.registerActionShortcut})
500 */
501 @SuppressWarnings("deprecation")
502 public static void registerActionShortcut(Action action, Shortcut shortcut) {
503 KeyStroke keyStroke = shortcut.getKeyStroke();
504 if (keyStroke == null)
505 return;
506
507 InputMap inputMap = contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
508 Object existing = inputMap.get(keyStroke);
509 if (existing != null && !existing.equals(action)) {
510 Logging.info(String.format("Keystroke %s is already assigned to %s, will be overridden by %s", keyStroke, existing, action));
511 }
512 inputMap.put(keyStroke, action);
513
514 contentPanePrivate.getActionMap().put(action, action);
515 }
516
517 /**
518 * Unregisters a shortcut.
519 * @param shortcut shortcut to unregister
520 * @since 12639 (as a replacement to {@code Main.unregisterShortcut})
521 */
522 @SuppressWarnings("deprecation")
523 public static void unregisterShortcut(Shortcut shortcut) {
524 contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(shortcut.getKeyStroke());
525 }
526
527 /**
528 * Unregisters a {@code JosmAction} and its shortcut.
529 * @param action action to unregister
530 * @since 12639 (as a replacement to {@code Main.unregisterActionShortcut})
531 */
532 @SuppressWarnings("deprecation")
533 public static void unregisterActionShortcut(JosmAction action) {
534 unregisterActionShortcut(action, action.getShortcut());
535 }
536
537 /**
538 * Unregisters an action and its shortcut.
539 * @param action action to unregister
540 * @param shortcut shortcut to unregister
541 * @since 12639 (as a replacement to {@code Main.unregisterActionShortcut})
542 */
543 @SuppressWarnings("deprecation")
544 public static void unregisterActionShortcut(Action action, Shortcut shortcut) {
545 unregisterShortcut(shortcut);
546 contentPanePrivate.getActionMap().remove(action);
547 }
548
549 /**
550 * Replies the registered action for the given shortcut
551 * @param shortcut The shortcut to look for
552 * @return the registered action for the given shortcut
553 * @since 12639 (as a replacement to {@code Main.getRegisteredActionShortcut})
554 */
555 @SuppressWarnings("deprecation")
556 public static Action getRegisteredActionShortcut(Shortcut shortcut) {
557 KeyStroke keyStroke = shortcut.getKeyStroke();
558 if (keyStroke == null)
559 return null;
560 Object action = contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(keyStroke);
561 if (action instanceof Action)
562 return (Action) action;
563 return null;
564 }
565
566 /**
567 * Displays help on the console
568 * @since 2748
569 */
570 public static void showHelp() {
571 // TODO: put in a platformHook for system that have no console by default
572 System.out.println(getHelp());
573 }
574
575 static String getHelp() {
576 return tr("Java OpenStreetMap Editor")+" ["
577 +Version.getInstance().getAgentString()+"]\n\n"+
578 tr("usage")+":\n"+
579 "\tjava -jar josm.jar <options>...\n\n"+
580 tr("options")+":\n"+
581 "\t--help|-h "+tr("Show this help")+'\n'+
582 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+
583 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+
584 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+
585 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+
586 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+
587 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+
588 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+
589 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+
590 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+
591 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+
592 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+
593 "\t--language=<language> "+tr("Set the language")+"\n\n"+
594 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+
595 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+
596 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+
597 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+
598 tr("options provided as Java system properties")+":\n"+
599 align("\t-Djosm.dir.name=JOSM") + tr("Change the JOSM directory name") + "\n\n" +
600 align("\t-Djosm.pref=" + tr("/PATH/TO/JOSM/PREF ")) + tr("Set the preferences directory") + "\n" +
601 align("\t") + tr("Default: {0}", platform.getDefaultPrefDirectory()) + "\n\n" +
602 align("\t-Djosm.userdata=" + tr("/PATH/TO/JOSM/USERDATA")) + tr("Set the user data directory") + "\n" +
603 align("\t") + tr("Default: {0}", platform.getDefaultUserDataDirectory()) + "\n\n" +
604 align("\t-Djosm.cache=" + tr("/PATH/TO/JOSM/CACHE ")) + tr("Set the cache directory") + "\n" +
605 align("\t") + tr("Default: {0}", platform.getDefaultCacheDirectory()) + "\n\n" +
606 align("\t-Djosm.home=" + tr("/PATH/TO/JOSM/HOMEDIR ")) +
607 tr("Set the preferences+data+cache directory (cache directory will be josm.home/cache)")+"\n\n"+
608 tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+
609 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
610 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
611 "\t-Xmx...m\n\n"+
612 tr("examples")+":\n"+
613 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
614 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+'\n'+
615 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
616 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
617 "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+
618 "\tjava -Djosm.dir.name=josm_dev -jar josm.jar\n"+
619 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
620 "\tjava -Xmx1024m -jar josm.jar\n\n"+
621 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+'\n'+
622 tr("Make sure you load some data if you use --selection.")+'\n';
623 }
624
625 private static String align(String str) {
626 return str + Stream.generate(() -> " ").limit(Math.max(0, 43 - str.length())).collect(Collectors.joining(""));
627 }
628
629 /**
630 * Main application Startup
631 * @param argArray Command-line arguments
632 */
633 @SuppressWarnings("deprecation")
634 public static void main(final String[] argArray) {
635 I18n.init();
636
637 ProgramArguments args = null;
638 // construct argument table
639 try {
640 args = new ProgramArguments(argArray);
641 } catch (IllegalArgumentException e) {
642 System.err.println(e.getMessage());
643 System.exit(1);
644 return;
645 }
646
647 Level logLevel = args.getLogLevel();
648 Logging.setLogLevel(logLevel);
649 if (!args.showVersion() && !args.showHelp()) {
650 Logging.info(tr("Log level is at {0} ({1}, {2})", logLevel.getLocalizedName(), logLevel.getName(), logLevel.intValue()));
651 }
652
653 Optional<String> language = args.getSingle(Option.LANGUAGE);
654 I18n.set(language.orElse(null));
655
656 Policy.setPolicy(new Policy() {
657 // Permissions for plug-ins loaded when josm is started via webstart
658 private PermissionCollection pc;
659
660 {
661 pc = new Permissions();
662 pc.add(new AllPermission());
663 }
664
665 @Override
666 public PermissionCollection getPermissions(CodeSource codesource) {
667 return pc;
668 }
669 });
670
671 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
672
673 // initialize the platform hook, and
674 Main.determinePlatformHook();
675 // call the really early hook before we do anything else
676 Main.platform.preStartupHook();
677
678 if (args.showVersion()) {
679 System.out.println(Version.getInstance().getAgentString());
680 return;
681 } else if (args.showHelp()) {
682 showHelp();
683 return;
684 }
685
686 COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray));
687
688 boolean skipLoadingPlugins = args.hasOption(Option.SKIP_PLUGINS);
689 if (skipLoadingPlugins) {
690 Logging.info(tr("Plugin loading skipped"));
691 }
692
693 if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) {
694 // Enable debug in OAuth signpost via system preference, but only at trace level
695 Utils.updateSystemProperty("debug", "true");
696 Logging.info(tr("Enabled detailed debug level (trace)"));
697 }
698
699 Main.pref.init(args.hasOption(Option.RESET_PREFERENCES));
700
701 args.getPreferencesToSet().forEach(Main.pref::put);
702
703 if (!language.isPresent()) {
704 I18n.set(Main.pref.get("language", null));
705 }
706 Main.pref.updateSystemProperties();
707
708 checkIPv6();
709
710 processOffline(args);
711
712 Main.platform.afterPrefStartupHook();
713
714 FontsManager.initialize();
715
716 GuiHelper.setupLanguageFonts();
717
718 Handler.install();
719
720 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry",
721 args.getSingle(Option.GEOMETRY).orElse(null),
722 !args.hasOption(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false));
723 final MainFrame mainFrame = new MainFrame(geometry);
724 if (mainFrame.getContentPane() instanceof JComponent) {
725 contentPanePrivate = (JComponent) mainFrame.getContentPane();
726 }
727 mainPanel = mainFrame.getPanel();
728 Main.parent = mainFrame;
729
730 if (args.hasOption(Option.LOAD_PREFERENCES)) {
731 XMLCommandProcessor config = new XMLCommandProcessor(Main.pref);
732 for (String i : args.get(Option.LOAD_PREFERENCES)) {
733 Logging.info("Reading preferences from " + i);
734 try (InputStream is = openStream(new URL(i))) {
735 config.openAndReadXML(is);
736 } catch (IOException ex) {
737 throw BugReport.intercept(ex).put("file", i);
738 }
739 }
740 }
741
742 try {
743 CertificateAmendment.addMissingCertificates();
744 } catch (IOException | GeneralSecurityException ex) {
745 Logging.warn(ex);
746 Logging.warn(Logging.getErrorMessage(Utils.getRootCause(ex)));
747 }
748 Authenticator.setDefault(DefaultAuthenticator.getInstance());
749 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
750 ProxySelector.setDefault(proxySelector);
751 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
752
753 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(SplashScreen::new);
754 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor();
755 monitor.beginTask(tr("Initializing"));
756 GuiHelper.runInEDT(() -> splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)));
757 Main.setInitStatusListener(new InitStatusListener() {
758
759 @Override
760 public Object updateStatus(String event) {
761 monitor.beginTask(event);
762 return event;
763 }
764
765 @Override
766 public void finish(Object status) {
767 if (status instanceof String) {
768 monitor.finishTask((String) status);
769 }
770 }
771 });
772
773 Collection<PluginInformation> pluginsToLoad = null;
774
775 if (!skipLoadingPlugins) {
776 pluginsToLoad = updateAndLoadEarlyPlugins(splash, monitor);
777 }
778
779 monitor.indeterminateSubTask(tr("Setting defaults"));
780 setupUIManager();
781 toolbar = new ToolbarPreferences();
782 Main.toolbar = toolbar;
783 ProjectionPreference.setProjection();
784 GuiHelper.translateJavaInternalMessages();
785 preConstructorInit();
786
787 monitor.indeterminateSubTask(tr("Creating main GUI"));
788 final Main main = new MainApplication(mainFrame);
789 main.initialize();
790
791 if (!skipLoadingPlugins) {
792 loadLatePlugins(splash, monitor, pluginsToLoad);
793 }
794
795 // Wait for splash disappearance (fix #9714)
796 GuiHelper.runInEDTAndWait(() -> {
797 splash.setVisible(false);
798 splash.dispose();
799 mainFrame.setVisible(true);
800 });
801
802 boolean maximized = Main.pref.getBoolean("gui.maximized", false);
803 if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) {
804 mainFrame.setMaximized(true);
805 }
806 if (main.menu.fullscreenToggleAction != null) {
807 main.menu.fullscreenToggleAction.initial();
808 }
809
810 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
811
812 if (Main.isPlatformWindows()) {
813 try {
814 // Check for insecure certificates to remove.
815 // This is Windows-dependant code but it can't go to preStartupHook (need i18n)
816 // neither startupHook (need to be called before remote control)
817 PlatformHookWindows.removeInsecureCertificates();
818 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
819 Logging.error(e);
820 }
821 }
822
823 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
824 RemoteControl.start();
825 }
826
827 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
828 MessageNotifier.start();
829 }
830
831 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
832 // Repaint manager is registered so late for a reason - there is lots of violation during startup process
833 // but they don't seem to break anything and are difficult to fix
834 Logging.info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
835 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
836 }
837 }
838
839 static void setupUIManager() {
840 String defaultlaf = platform.getDefaultStyle();
841 String laf = LafPreference.LAF.get();
842 try {
843 UIManager.setLookAndFeel(laf);
844 } catch (final NoClassDefFoundError | ClassNotFoundException e) {
845 // Try to find look and feel in plugin classloaders
846 Logging.trace(e);
847 Class<?> klass = null;
848 for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
849 try {
850 klass = cl.loadClass(laf);
851 break;
852 } catch (ClassNotFoundException ex) {
853 Logging.trace(ex);
854 }
855 }
856 if (klass != null && LookAndFeel.class.isAssignableFrom(klass)) {
857 try {
858 UIManager.setLookAndFeel((LookAndFeel) klass.getConstructor().newInstance());
859 } catch (ReflectiveOperationException ex) {
860 Logging.log(Logging.LEVEL_WARN, "Cannot set Look and Feel: " + laf + ": "+ex.getMessage(), ex);
861 } catch (UnsupportedLookAndFeelException ex) {
862 Logging.info("Look and Feel not supported: " + laf);
863 LafPreference.LAF.put(defaultlaf);
864 Logging.trace(ex);
865 }
866 } else {
867 Logging.info("Look and Feel not found: " + laf);
868 LafPreference.LAF.put(defaultlaf);
869 }
870 } catch (UnsupportedLookAndFeelException e) {
871 Logging.info("Look and Feel not supported: " + laf);
872 LafPreference.LAF.put(defaultlaf);
873 Logging.trace(e);
874 } catch (InstantiationException | IllegalAccessException e) {
875 Logging.error(e);
876 }
877
878 UIManager.put("OptionPane.okIcon", ImageProvider.get("ok"));
879 UIManager.put("OptionPane.yesIcon", UIManager.get("OptionPane.okIcon"));
880 UIManager.put("OptionPane.cancelIcon", ImageProvider.get("cancel"));
881 UIManager.put("OptionPane.noIcon", UIManager.get("OptionPane.cancelIcon"));
882 // Ensures caret color is the same than text foreground color, see #12257
883 // See http://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/synth/doc-files/componentProperties.html
884 for (String p : Arrays.asList(
885 "EditorPane", "FormattedTextField", "PasswordField", "TextArea", "TextField", "TextPane")) {
886 UIManager.put(p+".caretForeground", UIManager.getColor(p+".foreground"));
887 }
888 }
889
890 private static InputStream openStream(URL url) throws IOException {
891 if ("file".equals(url.getProtocol())) {
892 return url.openStream();
893 } else {
894 return HttpClient.create(url).connect().getContent();
895 }
896 }
897
898 static Collection<PluginInformation> updateAndLoadEarlyPlugins(SplashScreen splash, SplashProgressMonitor monitor) {
899 Collection<PluginInformation> pluginsToLoad;
900 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false));
901 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
902 monitor.subTask(tr("Updating plugins"));
903 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
904 }
905
906 monitor.indeterminateSubTask(tr("Installing updated plugins"));
907 PluginHandler.installDownloadedPlugins(true);
908
909 monitor.indeterminateSubTask(tr("Loading early plugins"));
910 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false));
911 return pluginsToLoad;
912 }
913
914 static void loadLatePlugins(SplashScreen splash, SplashProgressMonitor monitor, Collection<PluginInformation> pluginsToLoad) {
915 monitor.indeterminateSubTask(tr("Loading plugins"));
916 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false));
917 GuiHelper.runInEDTAndWait(() -> toolbar.refreshToolbarControl());
918 }
919
920 private static void processOffline(ProgramArguments args) {
921 for (String offlineNames : args.get(Option.OFFLINE)) {
922 for (String s : offlineNames.split(",")) {
923 try {
924 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH)));
925 } catch (IllegalArgumentException e) {
926 Logging.log(Logging.LEVEL_ERROR,
927 tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.",
928 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values())), e);
929 System.exit(1);
930 return;
931 }
932 }
933 }
934 Set<OnlineResource> offline = Main.getOfflineResources();
935 if (!offline.isEmpty()) {
936 Logging.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
937 "JOSM is running in offline mode. These resources will not be available: {0}",
938 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
939 }
940 }
941
942 /**
943 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
944 * disabling or enabling IPV6 may only be done with next start.
945 */
946 private static void checkIPv6() {
947 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) {
948 new Thread((Runnable) () -> { /* this may take some time (DNS, Connect) */
949 boolean hasv6 = false;
950 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false);
951 try {
952 /* Use the check result from last run of the software, as after the test, value
953 changes have no effect anymore */
954 if (wasv6) {
955 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
956 }
957 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
958 if (a instanceof Inet6Address) {
959 if (a.isReachable(1000)) {
960 /* be sure it REALLY works */
961 SSLSocketFactory.getDefault().createSocket(a, 443).close();
962 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
963 if (!wasv6) {
964 Logging.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
965 } else {
966 Logging.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
967 }
968 hasv6 = true;
969 }
970 break; /* we're done */
971 }
972 }
973 } catch (IOException | SecurityException e) {
974 Logging.debug("Exception while checking IPv6 connectivity: {0}", e);
975 Logging.trace(e);
976 }
977 if (wasv6 && !hasv6) {
978 Logging.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
979 Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart!
980 try {
981 RestartAction.restartJOSM();
982 } catch (IOException e) {
983 Logging.error(e);
984 }
985 }
986 Main.pref.put("validated.ipv6", hasv6);
987 }, "IPv6-checker").start();
988 }
989 }
990
991 /**
992 * Download area specified as Bounds value.
993 * @param rawGps Flag to download raw GPS tracks
994 * @param b The bounds value
995 * @return the complete download task (including post-download handler)
996 */
997 static List<Future<?>> downloadFromParamBounds(final boolean rawGps, Bounds b) {
998 DownloadTask task = rawGps ? new DownloadGpsTask() : new DownloadOsmTask();
999 // asynchronously launch the download task ...
1000 Future<?> future = task.download(true, b, null);
1001 // ... and the continuation when the download is finished (this will wait for the download to finish)
1002 return Collections.singletonList(MainApplication.worker.submit(new PostDownloadHandler(task, future)));
1003 }
1004
1005 /**
1006 * Handle command line instructions after GUI has been initialized.
1007 * @param args program arguments
1008 * @return the list of submitted tasks
1009 */
1010 static List<Future<?>> postConstructorProcessCmdLine(ProgramArguments args) {
1011 List<Future<?>> tasks = new ArrayList<>();
1012 List<File> fileList = new ArrayList<>();
1013 for (String s : args.get(Option.DOWNLOAD)) {
1014 tasks.addAll(DownloadParamType.paramType(s).download(s, fileList));
1015 }
1016 if (!fileList.isEmpty()) {
1017 tasks.add(OpenFileAction.openFiles(fileList, true));
1018 }
1019 for (String s : args.get(Option.DOWNLOADGPS)) {
1020 tasks.addAll(DownloadParamType.paramType(s).downloadGps(s));
1021 }
1022 final Collection<String> selectionArguments = args.get(Option.SELECTION);
1023 if (!selectionArguments.isEmpty()) {
1024 tasks.add(MainApplication.worker.submit(() -> {
1025 for (String s : selectionArguments) {
1026 SearchAction.search(s, SearchAction.SearchMode.add);
1027 }
1028 }));
1029 }
1030 return tasks;
1031 }
1032
1033 private static class GuiFinalizationWorker implements Runnable {
1034
1035 private final ProgramArguments args;
1036 private final DefaultProxySelector proxySelector;
1037
1038 GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) {
1039 this.args = args;
1040 this.proxySelector = proxySelector;
1041 }
1042
1043 @Override
1044 public void run() {
1045
1046 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
1047 if (!handleProxyErrors()) {
1048 handleNetworkErrors();
1049 }
1050
1051 // Restore autosave layers after crash and start autosave thread
1052 handleAutosave();
1053
1054 // Handle command line instructions
1055 postConstructorProcessCmdLine(args);
1056
1057 // Show download dialog if autostart is enabled
1058 DownloadDialog.autostartIfNeeded();
1059 }
1060
1061 private static void handleAutosave() {
1062 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
1063 AutosaveTask autosaveTask = new AutosaveTask();
1064 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
1065 if (!unsavedLayerFiles.isEmpty()) {
1066 ExtendedDialog dialog = new ExtendedDialog(
1067 Main.parent,
1068 tr("Unsaved osm data"),
1069 tr("Restore"), tr("Cancel"), tr("Discard")
1070 );
1071 dialog.setContent(
1072 trn("JOSM found {0} unsaved osm data layer. ",
1073 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
1074 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
1075 dialog.setButtonIcons("ok", "cancel", "dialogs/delete");
1076 int selection = dialog.showDialog().getValue();
1077 if (selection == 1) {
1078 autosaveTask.recoverUnsavedLayers();
1079 } else if (selection == 3) {
1080 autosaveTask.discardUnsavedLayers();
1081 }
1082 }
1083 autosaveTask.schedule();
1084 }
1085 }
1086
1087 private static boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
1088 if (hasErrors) {
1089 ExtendedDialog ed = new ExtendedDialog(
1090 Main.parent, title,
1091 tr("Change proxy settings"), tr("Cancel"));
1092 ed.setButtonIcons("dialogs/settings", "cancel").setCancelButton(2);
1093 ed.setMinimumSize(new Dimension(460, 260));
1094 ed.setIcon(JOptionPane.WARNING_MESSAGE);
1095 ed.setContent(message);
1096
1097 if (ed.showDialog().getValue() == 1) {
1098 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
1099 }
1100 }
1101 return hasErrors;
1102 }
1103
1104 private boolean handleProxyErrors() {
1105 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
1106 tr("JOSM tried to access the following resources:<br>" +
1107 "{0}" +
1108 "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
1109 "{1}" +
1110 "Would you like to change your proxy settings now?",
1111 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
1112 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
1113 ));
1114 }
1115
1116 private static boolean handleNetworkErrors() {
1117 Map<String, Throwable> networkErrors = Main.getNetworkErrors();
1118 boolean condition = !networkErrors.isEmpty();
1119 if (condition) {
1120 Set<String> errors = new TreeSet<>();
1121 for (Throwable t : networkErrors.values()) {
1122 errors.add(t.toString());
1123 }
1124 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
1125 tr("JOSM tried to access the following resources:<br>" +
1126 "{0}" +
1127 "but <b>failed</b> to do so, because of the following network errors:<br>" +
1128 "{1}" +
1129 "It may be due to a missing proxy configuration.<br>" +
1130 "Would you like to change your proxy settings now?",
1131 Utils.joinAsHtmlUnorderedList(networkErrors.keySet()),
1132 Utils.joinAsHtmlUnorderedList(errors)
1133 ));
1134 }
1135 return false;
1136 }
1137 }
1138}
Note: See TracBrowser for help on using the repository browser.