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

Last change on this file since 14180 was 14177, checked in by Don-vip, 6 years ago

see #16666 - add workaround against ATK wrapper failure on Debian, Ubuntu and Mint

  • Property svn:eol-style set to native
File size: 64.0 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 static org.openstreetmap.josm.tools.Utils.getSystemProperty;
7
8import java.awt.AWTError;
9import java.awt.Container;
10import java.awt.Dimension;
11import java.awt.Font;
12import java.awt.GraphicsEnvironment;
13import java.awt.GridBagLayout;
14import java.awt.Toolkit;
15import java.io.File;
16import java.io.IOException;
17import java.io.InputStream;
18import java.lang.reflect.Field;
19import java.net.Authenticator;
20import java.net.Inet6Address;
21import java.net.InetAddress;
22import java.net.ProxySelector;
23import java.net.URL;
24import java.nio.file.InvalidPathException;
25import java.nio.file.Paths;
26import java.security.AllPermission;
27import java.security.CodeSource;
28import java.security.GeneralSecurityException;
29import java.security.KeyStoreException;
30import java.security.NoSuchAlgorithmException;
31import java.security.PermissionCollection;
32import java.security.Permissions;
33import java.security.Policy;
34import java.security.cert.CertificateException;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Collection;
38import java.util.Collections;
39import java.util.List;
40import java.util.Locale;
41import java.util.Map;
42import java.util.Objects;
43import java.util.Optional;
44import java.util.ResourceBundle;
45import java.util.Set;
46import java.util.TreeSet;
47import java.util.concurrent.ExecutorService;
48import java.util.concurrent.Executors;
49import java.util.concurrent.Future;
50import java.util.logging.Level;
51import java.util.stream.Collectors;
52import java.util.stream.Stream;
53
54import javax.net.ssl.SSLSocketFactory;
55import javax.swing.Action;
56import javax.swing.InputMap;
57import javax.swing.JComponent;
58import javax.swing.JLabel;
59import javax.swing.JOptionPane;
60import javax.swing.JPanel;
61import javax.swing.KeyStroke;
62import javax.swing.LookAndFeel;
63import javax.swing.RepaintManager;
64import javax.swing.SwingUtilities;
65import javax.swing.UIManager;
66import javax.swing.UnsupportedLookAndFeelException;
67
68import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
69import org.openstreetmap.josm.actions.DeleteAction;
70import org.openstreetmap.josm.actions.JosmAction;
71import org.openstreetmap.josm.actions.OpenFileAction;
72import org.openstreetmap.josm.actions.OpenFileAction.OpenFileTask;
73import org.openstreetmap.josm.actions.PreferencesAction;
74import org.openstreetmap.josm.actions.RestartAction;
75import org.openstreetmap.josm.actions.downloadtasks.DownloadGpsTask;
76import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
77import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
78import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
79import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
80import org.openstreetmap.josm.actions.search.SearchAction;
81import org.openstreetmap.josm.cli.CLIModule;
82import org.openstreetmap.josm.command.DeleteCommand;
83import org.openstreetmap.josm.command.SplitWayCommand;
84import org.openstreetmap.josm.data.Bounds;
85import org.openstreetmap.josm.data.Preferences;
86import org.openstreetmap.josm.data.UndoRedoHandler;
87import org.openstreetmap.josm.data.UndoRedoHandler.CommandQueueListener;
88import org.openstreetmap.josm.data.Version;
89import org.openstreetmap.josm.data.oauth.OAuthAccessTokenHolder;
90import org.openstreetmap.josm.data.osm.DataSet;
91import org.openstreetmap.josm.data.osm.IPrimitive;
92import org.openstreetmap.josm.data.osm.OsmDataManager;
93import org.openstreetmap.josm.data.osm.OsmPrimitive;
94import org.openstreetmap.josm.data.osm.UserInfo;
95import org.openstreetmap.josm.data.osm.search.SearchMode;
96import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
97import org.openstreetmap.josm.data.preferences.JosmUrls;
98import org.openstreetmap.josm.data.preferences.sources.SourceType;
99import org.openstreetmap.josm.data.projection.ProjectionBoundsProvider;
100import org.openstreetmap.josm.data.projection.ProjectionCLI;
101import org.openstreetmap.josm.data.projection.ProjectionRegistry;
102import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileSource;
103import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper;
104import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource;
105import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
106import org.openstreetmap.josm.gui.ProgramArguments.Option;
107import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor;
108import org.openstreetmap.josm.gui.bugreport.BugReportDialog;
109import org.openstreetmap.josm.gui.bugreport.DefaultBugReportSendingHandler;
110import org.openstreetmap.josm.gui.download.DownloadDialog;
111import org.openstreetmap.josm.gui.io.CredentialDialog;
112import org.openstreetmap.josm.gui.io.CustomConfigurator.XMLCommandProcessor;
113import org.openstreetmap.josm.gui.io.SaveLayersDialog;
114import org.openstreetmap.josm.gui.layer.AutosaveTask;
115import org.openstreetmap.josm.gui.layer.Layer;
116import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
117import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
118import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
119import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
120import org.openstreetmap.josm.gui.layer.MainLayerManager;
121import org.openstreetmap.josm.gui.layer.OsmDataLayer;
122import org.openstreetmap.josm.gui.mappaint.RenderingCLI;
123import org.openstreetmap.josm.gui.mappaint.loader.MapPaintStyleLoader;
124import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
125import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
126import org.openstreetmap.josm.gui.preferences.display.LafPreference;
127import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
128import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
129import org.openstreetmap.josm.gui.progress.swing.ProgressMonitorExecutor;
130import org.openstreetmap.josm.gui.util.GuiHelper;
131import org.openstreetmap.josm.gui.util.RedirectInputMap;
132import org.openstreetmap.josm.gui.util.WindowGeometry;
133import org.openstreetmap.josm.gui.widgets.UrlLabel;
134import org.openstreetmap.josm.io.CachedFile;
135import org.openstreetmap.josm.io.CertificateAmendment;
136import org.openstreetmap.josm.io.DefaultProxySelector;
137import org.openstreetmap.josm.io.FileWatcher;
138import org.openstreetmap.josm.io.MessageNotifier;
139import org.openstreetmap.josm.io.NetworkManager;
140import org.openstreetmap.josm.io.OnlineResource;
141import org.openstreetmap.josm.io.OsmConnection;
142import org.openstreetmap.josm.io.OsmTransferException;
143import org.openstreetmap.josm.io.auth.AbstractCredentialsAgent;
144import org.openstreetmap.josm.io.auth.CredentialsManager;
145import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
146import org.openstreetmap.josm.io.protocols.data.Handler;
147import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
148import org.openstreetmap.josm.plugins.PluginHandler;
149import org.openstreetmap.josm.plugins.PluginInformation;
150import org.openstreetmap.josm.spi.lifecycle.InitStatusListener;
151import org.openstreetmap.josm.spi.lifecycle.Lifecycle;
152import org.openstreetmap.josm.spi.preferences.Config;
153import org.openstreetmap.josm.tools.FontsManager;
154import org.openstreetmap.josm.tools.GBC;
155import org.openstreetmap.josm.tools.I18n;
156import org.openstreetmap.josm.tools.ImageProvider;
157import org.openstreetmap.josm.tools.JosmRuntimeException;
158import org.openstreetmap.josm.tools.Logging;
159import org.openstreetmap.josm.tools.OsmUrlToBounds;
160import org.openstreetmap.josm.tools.PlatformHook.NativeOsCallback;
161import org.openstreetmap.josm.tools.PlatformHookWindows;
162import org.openstreetmap.josm.tools.PlatformManager;
163import org.openstreetmap.josm.tools.Shortcut;
164import org.openstreetmap.josm.tools.Utils;
165import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler;
166import org.openstreetmap.josm.tools.bugreport.BugReportQueue;
167import org.openstreetmap.josm.tools.bugreport.BugReportSender;
168import org.xml.sax.SAXException;
169
170/**
171 * Main window class application.
172 *
173 * @author imi
174 */
175@SuppressWarnings("deprecation")
176public class MainApplication extends org.openstreetmap.josm.Main {
177
178 /**
179 * Command-line arguments used to run the application.
180 */
181 private static volatile List<String> commandLineArgs;
182
183 /**
184 * The main menu bar at top of screen.
185 */
186 static MainMenu menu;
187
188 /**
189 * The main panel, required to be static for {@link MapFrameListener} handling.
190 */
191 static MainPanel mainPanel;
192
193 /**
194 * The private content pane of {@link MainFrame}, required to be static for shortcut handling.
195 */
196 static JComponent contentPanePrivate;
197
198 /**
199 * The MapFrame.
200 */
201 static MapFrame map;
202
203 /**
204 * The toolbar preference control to register new actions.
205 */
206 static volatile ToolbarPreferences toolbar;
207
208 private static MainFrame mainFrame;
209
210 /**
211 * The worker thread slave. This is for executing all long and intensive
212 * calculations. The executed runnables are guaranteed to be executed separately and sequential.
213 * @since 12634 (as a replacement to {@code Main.worker})
214 */
215 public static final ExecutorService worker = new ProgressMonitorExecutor("main-worker-%d", Thread.NORM_PRIORITY);
216
217 /**
218 * Provides access to the layers displayed in the main view.
219 */
220 private static final MainLayerManager layerManager = new MainLayerManager();
221
222 /**
223 * The commands undo/redo handler.
224 * @since 12641
225 * @deprecated Use {@link UndoRedoHandler#getInstance}
226 */
227 @Deprecated
228 public static final UndoRedoHandler undoRedo = UndoRedoHandler.getInstance();
229
230 private static final LayerChangeListener undoRedoCleaner = new LayerChangeListener() {
231 @Override
232 public void layerRemoving(LayerRemoveEvent e) {
233 Layer layer = e.getRemovedLayer();
234 if (layer instanceof OsmDataLayer) {
235 UndoRedoHandler.getInstance().clean(((OsmDataLayer) layer).getDataSet());
236 }
237 }
238
239 @Override
240 public void layerOrderChanged(LayerOrderChangeEvent e) {
241 // Do nothing
242 }
243
244 @Override
245 public void layerAdded(LayerAddEvent e) {
246 // Do nothing
247 }
248 };
249
250 private static final ProjectionBoundsProvider mainBoundsProvider = new ProjectionBoundsProvider() {
251 @Override
252 public Bounds getRealBounds() {
253 return isDisplayingMapView() ? map.mapView.getRealBounds() : null;
254 }
255
256 @Override
257 public void restoreOldBounds(Bounds oldBounds) {
258 if (isDisplayingMapView()) {
259 map.mapView.zoomTo(oldBounds);
260 }
261 }
262 };
263
264 private static final List<CLIModule> cliModules = new ArrayList<>();
265
266 /**
267 * Default JOSM command line interface.
268 * <p>
269 * Runs JOSM and performs some action, depending on the options and positional
270 * arguments.
271 */
272 public static final CLIModule JOSM_CLI_MODULE = new CLIModule() {
273 @Override
274 public String getActionKeyword() {
275 return "runjosm";
276 }
277
278 @Override
279 public void processArguments(String[] argArray) {
280 ProgramArguments args = null;
281 // construct argument table
282 try {
283 args = new ProgramArguments(argArray);
284 } catch (IllegalArgumentException e) {
285 System.err.println(e.getMessage());
286 System.exit(1);
287 }
288 mainJOSM(args);
289 }
290 };
291
292 /**
293 * Listener that sets the enabled state of undo/redo menu entries.
294 */
295 final CommandQueueListener redoUndoListener = (queueSize, redoSize) -> {
296 menu.undo.setEnabled(queueSize > 0);
297 menu.redo.setEnabled(redoSize > 0);
298 };
299
300 /**
301 * Source of NTV2 shift files: Download from JOSM website.
302 * @since 12777
303 */
304 public static final NTV2GridShiftFileSource JOSM_WEBSITE_NTV2_SOURCE = gridFileName -> {
305 String location = Config.getUrls().getJOSMWebsite() + "/proj/" + gridFileName;
306 // Try to load grid file
307 @SuppressWarnings("resource")
308 CachedFile cf = new CachedFile(location);
309 try {
310 return cf.getInputStream();
311 } catch (IOException ex) {
312 Logging.warn(ex);
313 return null;
314 }
315 };
316
317 static {
318 registerCLIModule(JOSM_CLI_MODULE);
319 registerCLIModule(ProjectionCLI.INSTANCE);
320 registerCLIModule(RenderingCLI.INSTANCE);
321 }
322
323 /**
324 * Register a command line interface module.
325 * @param module the module
326 * @since 12886
327 */
328 public static void registerCLIModule(CLIModule module) {
329 cliModules.add(module);
330 }
331
332 /**
333 * Constructs a new {@code MainApplication} without a window.
334 */
335 public MainApplication() {
336 this(null);
337 }
338
339 /**
340 * Constructs a main frame, ready sized and operating. Does not display the frame.
341 * @param mainFrame The main JFrame of the application
342 * @since 10340
343 */
344 public MainApplication(MainFrame mainFrame) {
345 MainApplication.mainFrame = mainFrame;
346 getLayerManager().addLayerChangeListener(undoRedoCleaner);
347 ProjectionRegistry.setboundsProvider(mainBoundsProvider);
348 Lifecycle.setShutdownSequence(new MainTermination());
349 }
350
351 /**
352 * Asks user to update its version of Java.
353 * @param updVersion target update version
354 * @param url download URL
355 * @param major true for a migration towards a major version of Java (8:9), false otherwise
356 * @param eolDate the EOL/expiration date
357 * @since 12270
358 */
359 public static void askUpdateJava(String updVersion, String url, String eolDate, boolean major) {
360 ExtendedDialog ed = new ExtendedDialog(
361 mainFrame,
362 tr("Outdated Java version"),
363 tr("OK"), tr("Update Java"), tr("Cancel"));
364 // Check if the dialog has not already been permanently hidden by user
365 if (!ed.toggleEnable("askUpdateJava"+updVersion).toggleCheckState()) {
366 ed.setButtonIcons("ok", "java", "cancel").setCancelButton(3);
367 ed.setMinimumSize(new Dimension(480, 300));
368 ed.setIcon(JOptionPane.WARNING_MESSAGE);
369 StringBuilder content = new StringBuilder(tr("You are running version {0} of Java.",
370 "<b>"+getSystemProperty("java.version")+"</b>")).append("<br><br>");
371 if ("Sun Microsystems Inc.".equals(getSystemProperty("java.vendor")) && !PlatformManager.getPlatform().isOpenJDK()) {
372 content.append("<b>").append(tr("This version is no longer supported by {0} since {1} and is not recommended for use.",
373 "Oracle", eolDate)).append("</b><br><br>");
374 }
375 content.append("<b>")
376 .append(major ?
377 tr("JOSM will soon stop working with this version; we highly recommend you to update to Java {0}.", updVersion) :
378 tr("You may face critical Java bugs; we highly recommend you to update to Java {0}.", updVersion))
379 .append("</b><br><br>")
380 .append(tr("Would you like to update now ?"));
381 ed.setContent(content.toString());
382
383 if (ed.showDialog().getValue() == 2) {
384 try {
385 PlatformManager.getPlatform().openUrl(url);
386 } catch (IOException e) {
387 Logging.warn(e);
388 }
389 }
390 }
391 }
392
393 /**
394 * Called once at startup to initialize the main window content.
395 * Should set {@link #menu} and {@link #mainPanel}
396 */
397 protected void initializeMainWindow() {
398 if (mainFrame != null) {
399 mainPanel = mainFrame.getPanel();
400 mainFrame.initialize();
401 menu = mainFrame.getMenu();
402 } else {
403 // required for running some tests.
404 mainPanel = new MainPanel(layerManager);
405 menu = new MainMenu();
406 }
407 mainPanel.addMapFrameListener((o, n) -> redoUndoListener.commandChanged(0, 0));
408 mainPanel.reAddListeners();
409 }
410
411 /**
412 * Returns the JOSM main frame.
413 * @return the JOSM main frame
414 * @since 14140
415 */
416 public static MainFrame getMainFrame() {
417 return mainFrame;
418 }
419
420 /**
421 * @deprecated Use {@code OsmDataManager.getInstance().getInProgressSelection()}
422 */
423 @Deprecated
424 @Override
425 public Collection<OsmPrimitive> getInProgressSelection() {
426 return OsmDataManager.getInstance().getInProgressSelection();
427 }
428
429 /**
430 * @deprecated Use {@code OsmDataManager.getInstance().getInProgressSelection()}
431 */
432 @Deprecated
433 @Override
434 public Collection<? extends IPrimitive> getInProgressISelection() {
435 return OsmDataManager.getInstance().getInProgressSelection();
436 }
437
438 /**
439 * @deprecated Use {@code OsmDataManager.getInstance().getEditDataSet()}
440 */
441 @Deprecated
442 @Override
443 public DataSet getEditDataSet() {
444 return OsmDataManager.getInstance().getEditDataSet();
445 }
446
447 /**
448 * @deprecated Use {@code OsmDataManager.getInstance().getActiveDataSet()}
449 */
450 @Deprecated
451 @Override
452 public DataSet getActiveDataSet() {
453 return OsmDataManager.getInstance().getActiveDataSet();
454 }
455
456 /**
457 * @deprecated Use {@code OsmDataManager.getInstance().setActiveDataSet}
458 */
459 @Deprecated
460 @Override
461 public void setActiveDataSet(DataSet ds) {
462 OsmDataManager.getInstance().setActiveDataSet(ds);
463 }
464
465 /**
466 * @deprecated Use {@code OsmDataManager.getInstance().containsDataSet}
467 */
468 @Deprecated
469 @Override
470 public boolean containsDataSet(DataSet ds) {
471 return getLayerManager().getLayersOfType(OsmDataLayer.class).stream().anyMatch(l -> l.data.equals(ds));
472 }
473
474 /**
475 * Returns the command-line arguments used to run the application.
476 * @return the command-line arguments used to run the application
477 * @since 11650
478 */
479 public static List<String> getCommandLineArgs() {
480 return Collections.unmodifiableList(commandLineArgs);
481 }
482
483 /**
484 * Returns the main layer manager that is used by the map view.
485 * @return The layer manager. The value returned will never change.
486 * @since 12636 (as a replacement to {@code Main.getLayerManager()})
487 */
488 public static MainLayerManager getLayerManager() {
489 return layerManager;
490 }
491
492 /**
493 * Returns the MapFrame.
494 * <p>
495 * There should be no need to access this to access any map data. Use {@link #layerManager} instead.
496 * @return the MapFrame
497 * @see MainPanel
498 * @since 12630
499 */
500 public static MapFrame getMap() {
501 return map;
502 }
503
504 /**
505 * Returns the main panel.
506 * @return the main panel
507 * @since 12642
508 */
509 public static MainPanel getMainPanel() {
510 return mainPanel;
511 }
512
513 /**
514 * Returns the main menu, at top of screen.
515 * @return the main menu
516 * @since 12643 (as a replacement to {@code MainApplication.getMenu()})
517 */
518 public static MainMenu getMenu() {
519 return menu;
520 }
521
522 /**
523 * Returns the toolbar preference control to register new actions.
524 * @return the toolbar preference control
525 * @since 12637
526 */
527 public static ToolbarPreferences getToolbar() {
528 return toolbar;
529 }
530
531 /**
532 * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if
533 * it only shows the MOTD panel.
534 * <p>
535 * You do not need this when accessing the layer manager. The layer manager will be empty if no map view is shown.
536 *
537 * @return <code>true</code> if JOSM currently displays a map view
538 * @since 12630 (as a replacement to {@code Main.isDisplayingMapView()})
539 */
540 public static boolean isDisplayingMapView() {
541 return map != null && map.mapView != null;
542 }
543
544 /**
545 * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
546 * If there are some unsaved data layers, asks first for user confirmation.
547 * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
548 * @param exitCode The return code
549 * @param reason the reason for exiting
550 * @return {@code true} if JOSM has been closed, {@code false} if the user has cancelled the operation.
551 * @since 12636 (specialized version of {@link Lifecycle#exitJosm})
552 */
553 public static boolean exitJosm(boolean exit, int exitCode, SaveLayersDialog.Reason reason) {
554 final boolean proceed = Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() ->
555 SaveLayersDialog.saveUnsavedModifications(layerManager.getLayers(),
556 reason != null ? reason : SaveLayersDialog.Reason.EXIT)));
557 if (proceed) {
558 return Lifecycle.exitJosm(exit, exitCode);
559 }
560 return false;
561 }
562
563 /**
564 * Redirects the key inputs from {@code source} to main content pane.
565 * @param source source component from which key inputs are redirected
566 */
567 public static void redirectToMainContentPane(JComponent source) {
568 RedirectInputMap.redirect(source, contentPanePrivate);
569 }
570
571 /**
572 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
573 * <p>
574 * It will fire an initial mapFrameInitialized event when the MapFrame is present.
575 * Otherwise will only fire when the MapFrame is created or destroyed.
576 * @param listener The MapFrameListener
577 * @return {@code true} if the listeners collection changed as a result of the call
578 * @see #addMapFrameListener
579 * @since 12639 (as a replacement to {@code Main.addAndFireMapFrameListener})
580 */
581 public static boolean addAndFireMapFrameListener(MapFrameListener listener) {
582 return mainPanel != null && mainPanel.addAndFireMapFrameListener(listener);
583 }
584
585 /**
586 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
587 * @param listener The MapFrameListener
588 * @return {@code true} if the listeners collection changed as a result of the call
589 * @see #addAndFireMapFrameListener
590 * @since 12639 (as a replacement to {@code Main.addMapFrameListener})
591 */
592 public static boolean addMapFrameListener(MapFrameListener listener) {
593 return mainPanel != null && mainPanel.addMapFrameListener(listener);
594 }
595
596 /**
597 * Unregisters the given {@code MapFrameListener} from MapFrame changes
598 * @param listener The MapFrameListener
599 * @return {@code true} if the listeners collection changed as a result of the call
600 * @since 12639 (as a replacement to {@code Main.removeMapFrameListener})
601 */
602 public static boolean removeMapFrameListener(MapFrameListener listener) {
603 return mainPanel != null && mainPanel.removeMapFrameListener(listener);
604 }
605
606 /**
607 * Registers a {@code JosmAction} and its shortcut.
608 * @param action action defining its own shortcut
609 * @since 12639 (as a replacement to {@code Main.registerActionShortcut})
610 */
611 public static void registerActionShortcut(JosmAction action) {
612 registerActionShortcut(action, action.getShortcut());
613 }
614
615 /**
616 * Registers an action and its shortcut.
617 * @param action action to register
618 * @param shortcut shortcut to associate to {@code action}
619 * @since 12639 (as a replacement to {@code Main.registerActionShortcut})
620 */
621 public static void registerActionShortcut(Action action, Shortcut shortcut) {
622 KeyStroke keyStroke = shortcut.getKeyStroke();
623 if (keyStroke == null)
624 return;
625
626 InputMap inputMap = contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
627 Object existing = inputMap.get(keyStroke);
628 if (existing != null && !existing.equals(action)) {
629 Logging.info(String.format("Keystroke %s is already assigned to %s, will be overridden by %s", keyStroke, existing, action));
630 }
631 inputMap.put(keyStroke, action);
632
633 contentPanePrivate.getActionMap().put(action, action);
634 }
635
636 /**
637 * Unregisters a shortcut.
638 * @param shortcut shortcut to unregister
639 * @since 12639 (as a replacement to {@code Main.unregisterShortcut})
640 */
641 public static void unregisterShortcut(Shortcut shortcut) {
642 contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(shortcut.getKeyStroke());
643 }
644
645 /**
646 * Unregisters a {@code JosmAction} and its shortcut.
647 * @param action action to unregister
648 * @since 12639 (as a replacement to {@code Main.unregisterActionShortcut})
649 */
650 public static void unregisterActionShortcut(JosmAction action) {
651 unregisterActionShortcut(action, action.getShortcut());
652 }
653
654 /**
655 * Unregisters an action and its shortcut.
656 * @param action action to unregister
657 * @param shortcut shortcut to unregister
658 * @since 12639 (as a replacement to {@code Main.unregisterActionShortcut})
659 */
660 public static void unregisterActionShortcut(Action action, Shortcut shortcut) {
661 unregisterShortcut(shortcut);
662 contentPanePrivate.getActionMap().remove(action);
663 }
664
665 /**
666 * Replies the registered action for the given shortcut
667 * @param shortcut The shortcut to look for
668 * @return the registered action for the given shortcut
669 * @since 12639 (as a replacement to {@code Main.getRegisteredActionShortcut})
670 */
671 public static Action getRegisteredActionShortcut(Shortcut shortcut) {
672 KeyStroke keyStroke = shortcut.getKeyStroke();
673 if (keyStroke == null)
674 return null;
675 Object action = contentPanePrivate.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(keyStroke);
676 if (action instanceof Action)
677 return (Action) action;
678 return null;
679 }
680
681 /**
682 * Displays help on the console
683 * @since 2748
684 */
685 public static void showHelp() {
686 // TODO: put in a platformHook for system that have no console by default
687 System.out.println(getHelp());
688 }
689
690 static String getHelp() {
691 return tr("Java OpenStreetMap Editor")+" ["
692 +Version.getInstance().getAgentString()+"]\n\n"+
693 tr("usage")+":\n"+
694 "\tjava -jar josm.jar [<command>] <options>...\n\n"+
695 tr("commands")+":\n"+
696 "\trunjosm "+tr("launch JOSM (default, performed when no command is specified)")+'\n'+
697 "\trender "+tr("render data and save the result to an image file")+'\n'+
698 "\tproject "+tr("convert coordinates from one coordinate reference system to another")+"\n\n"+
699 tr("For details on the {0} and {1} commands, run them with the {2} option.", "render", "project", "--help")+'\n'+
700 tr("The remainder of this help page documents the {0} command.", "runjosm")+"\n\n"+
701 tr("options")+":\n"+
702 "\t--help|-h "+tr("Show this help")+'\n'+
703 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+
704 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+
705 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+
706 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+
707 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+
708 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+
709 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+
710 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+
711 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+
712 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+
713 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+
714 "\t--language=<language> "+tr("Set the language")+"\n\n"+
715 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+
716 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+
717 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+
718 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+
719 tr("options provided as Java system properties")+":\n"+
720 align("\t-Djosm.dir.name=JOSM") + tr("Change the JOSM directory name") + "\n\n" +
721 align("\t-Djosm.pref=" + tr("/PATH/TO/JOSM/PREF ")) + tr("Set the preferences directory") + "\n" +
722 align("\t") + tr("Default: {0}", PlatformManager.getPlatform().getDefaultPrefDirectory()) + "\n\n" +
723 align("\t-Djosm.userdata=" + tr("/PATH/TO/JOSM/USERDATA")) + tr("Set the user data directory") + "\n" +
724 align("\t") + tr("Default: {0}", PlatformManager.getPlatform().getDefaultUserDataDirectory()) + "\n\n" +
725 align("\t-Djosm.cache=" + tr("/PATH/TO/JOSM/CACHE ")) + tr("Set the cache directory") + "\n" +
726 align("\t") + tr("Default: {0}", PlatformManager.getPlatform().getDefaultCacheDirectory()) + "\n\n" +
727 align("\t-Djosm.home=" + tr("/PATH/TO/JOSM/HOMEDIR ")) +
728 tr("Set the preferences+data+cache directory (cache directory will be josm.home/cache)")+"\n\n"+
729 tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+
730 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
731 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
732 "\t-Xmx...m\n\n"+
733 tr("examples")+":\n"+
734 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
735 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+'\n'+
736 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
737 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
738 "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+
739 "\tjava -Djosm.dir.name=josm_dev -jar josm.jar\n"+
740 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
741 "\tjava -Xmx1024m -jar josm.jar\n\n"+
742 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+'\n'+
743 tr("Make sure you load some data if you use --selection.")+'\n';
744 }
745
746 private static String align(String str) {
747 return str + Stream.generate(() -> " ").limit(Math.max(0, 43 - str.length())).collect(Collectors.joining(""));
748 }
749
750 /**
751 * Main application Startup
752 * @param argArray Command-line arguments
753 */
754 public static void main(final String[] argArray) {
755 I18n.init();
756 commandLineArgs = Arrays.asList(Arrays.copyOf(argArray, argArray.length));
757
758 if (argArray.length > 0) {
759 String moduleStr = argArray[0];
760 for (CLIModule module : cliModules) {
761 if (Objects.equals(moduleStr, module.getActionKeyword())) {
762 String[] argArrayCdr = Arrays.copyOfRange(argArray, 1, argArray.length);
763 module.processArguments(argArrayCdr);
764 return;
765 }
766 }
767 }
768 // no module specified, use default (josm)
769 JOSM_CLI_MODULE.processArguments(argArray);
770 }
771
772 /**
773 * Main method to run the JOSM GUI.
774 * @param args program arguments
775 */
776 public static void mainJOSM(ProgramArguments args) {
777
778 if (!GraphicsEnvironment.isHeadless()) {
779 BugReportQueue.getInstance().setBugReportHandler(BugReportDialog::showFor);
780 BugReportSender.setBugReportSendingHandler(new DefaultBugReportSendingHandler());
781 }
782
783 Level logLevel = args.getLogLevel();
784 Logging.setLogLevel(logLevel);
785 if (!args.showVersion() && !args.showHelp()) {
786 Logging.info(tr("Log level is at {0} ({1}, {2})", logLevel.getLocalizedName(), logLevel.getName(), logLevel.intValue()));
787 }
788
789 Optional<String> language = args.getSingle(Option.LANGUAGE);
790 I18n.set(language.orElse(null));
791
792 try {
793 Policy.setPolicy(new Policy() {
794 // Permissions for plug-ins loaded when josm is started via webstart
795 private PermissionCollection pc;
796
797 {
798 pc = new Permissions();
799 pc.add(new AllPermission());
800 }
801
802 @Override
803 public PermissionCollection getPermissions(CodeSource codesource) {
804 return pc;
805 }
806 });
807 } catch (SecurityException e) {
808 Logging.log(Logging.LEVEL_ERROR, "Unable to set permissions", e);
809 }
810
811 try {
812 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
813 } catch (SecurityException e) {
814 Logging.log(Logging.LEVEL_ERROR, "Unable to set uncaught exception handler", e);
815 }
816
817 // initialize the platform hook, and
818 PlatformManager.getPlatform().setNativeOsCallback(new DefaultNativeOsCallback());
819 // call the really early hook before we do anything else
820 PlatformManager.getPlatform().preStartupHook();
821
822 Preferences prefs = Preferences.main();
823 Config.setPreferencesInstance(prefs);
824 Config.setBaseDirectoriesProvider(JosmBaseDirectories.getInstance());
825 Config.setUrlsProvider(JosmUrls.getInstance());
826
827 if (args.showVersion()) {
828 System.out.println(Version.getInstance().getAgentString());
829 return;
830 } else if (args.showHelp()) {
831 showHelp();
832 return;
833 }
834
835 boolean skipLoadingPlugins = args.hasOption(Option.SKIP_PLUGINS);
836 if (skipLoadingPlugins) {
837 Logging.info(tr("Plugin loading skipped"));
838 }
839
840 if (Logging.isLoggingEnabled(Logging.LEVEL_TRACE)) {
841 // Enable debug in OAuth signpost via system preference, but only at trace level
842 Utils.updateSystemProperty("debug", "true");
843 Logging.info(tr("Enabled detailed debug level (trace)"));
844 }
845
846 try {
847 Preferences.main().init(args.hasOption(Option.RESET_PREFERENCES));
848 } catch (SecurityException e) {
849 Logging.log(Logging.LEVEL_ERROR, "Unable to initialize preferences", e);
850 }
851
852 args.getPreferencesToSet().forEach(prefs::put);
853
854 if (!language.isPresent()) {
855 I18n.set(Config.getPref().get("language", null));
856 }
857 updateSystemProperties();
858 Preferences.main().addPreferenceChangeListener(e -> updateSystemProperties());
859
860 checkIPv6();
861
862 processOffline(args);
863
864 PlatformManager.getPlatform().afterPrefStartupHook();
865
866 applyWorkarounds();
867
868 FontsManager.initialize();
869
870 GuiHelper.setupLanguageFonts();
871
872 Handler.install();
873
874 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry",
875 args.getSingle(Option.GEOMETRY).orElse(null),
876 !args.hasOption(Option.NO_MAXIMIZE) && Config.getPref().getBoolean("gui.maximized", false));
877 final MainFrame mainFrame = createMainFrame(geometry);
878 final Container contentPane = mainFrame.getContentPane();
879 if (contentPane instanceof JComponent) {
880 contentPanePrivate = (JComponent) contentPane;
881 }
882 mainPanel = mainFrame.getPanel();
883 org.openstreetmap.josm.Main.parent = mainFrame;
884
885 if (args.hasOption(Option.LOAD_PREFERENCES)) {
886 XMLCommandProcessor config = new XMLCommandProcessor(prefs);
887 for (String i : args.get(Option.LOAD_PREFERENCES)) {
888 try {
889 URL url = i.contains(":/") ? new URL(i) : Paths.get(i).toUri().toURL();
890 Logging.info("Reading preferences from " + url);
891 try (InputStream is = Utils.openStream(url)) {
892 config.openAndReadXML(is);
893 }
894 } catch (IOException | InvalidPathException ex) {
895 Logging.error(ex);
896 return;
897 }
898 }
899 }
900
901 try {
902 CertificateAmendment.addMissingCertificates();
903 } catch (IOException | GeneralSecurityException ex) {
904 Logging.warn(ex);
905 Logging.warn(Logging.getErrorMessage(Utils.getRootCause(ex)));
906 }
907 try {
908 Authenticator.setDefault(DefaultAuthenticator.getInstance());
909 } catch (SecurityException e) {
910 Logging.log(Logging.LEVEL_ERROR, "Unable to set default authenticator", e);
911 }
912 DefaultProxySelector proxySelector = null;
913 try {
914 proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
915 } catch (SecurityException e) {
916 Logging.log(Logging.LEVEL_ERROR, "Unable to get default proxy selector", e);
917 }
918 try {
919 if (proxySelector != null) {
920 ProxySelector.setDefault(proxySelector);
921 }
922 } catch (SecurityException e) {
923 Logging.log(Logging.LEVEL_ERROR, "Unable to set default proxy selector", e);
924 }
925 OAuthAccessTokenHolder.getInstance().init(CredentialsManager.getInstance());
926
927 setupCallbacks();
928
929 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(SplashScreen::new);
930 // splash can be null sometimes on Linux, in this case try to load JOSM silently
931 final SplashProgressMonitor monitor = splash != null ? splash.getProgressMonitor() : new SplashProgressMonitor(null, e -> {
932 if (e != null) {
933 Logging.debug(e.toString());
934 }
935 });
936 monitor.beginTask(tr("Initializing"));
937 if (splash != null) {
938 GuiHelper.runInEDT(() -> splash.setVisible(Config.getPref().getBoolean("draw.splashscreen", true)));
939 }
940 Lifecycle.setInitStatusListener(new InitStatusListener() {
941
942 @Override
943 public Object updateStatus(String event) {
944 monitor.beginTask(event);
945 return event;
946 }
947
948 @Override
949 public void finish(Object status) {
950 if (status instanceof String) {
951 monitor.finishTask((String) status);
952 }
953 }
954 });
955
956 Collection<PluginInformation> pluginsToLoad = null;
957
958 if (!skipLoadingPlugins) {
959 pluginsToLoad = updateAndLoadEarlyPlugins(splash, monitor);
960 }
961
962 monitor.indeterminateSubTask(tr("Setting defaults"));
963 setupUIManager();
964 toolbar = new ToolbarPreferences();
965 ProjectionPreference.setProjection();
966 setupNadGridSources();
967 GuiHelper.translateJavaInternalMessages();
968
969 monitor.indeterminateSubTask(tr("Creating main GUI"));
970 Lifecycle.initialize(new MainInitialization(new MainApplication(mainFrame)));
971
972 if (!skipLoadingPlugins) {
973 loadLatePlugins(splash, monitor, pluginsToLoad);
974 }
975
976 // Wait for splash disappearance (fix #9714)
977 GuiHelper.runInEDTAndWait(() -> {
978 if (splash != null) {
979 splash.setVisible(false);
980 splash.dispose();
981 }
982 mainFrame.setVisible(true);
983 });
984
985 boolean maximized = Config.getPref().getBoolean("gui.maximized", false);
986 if ((!args.hasOption(Option.NO_MAXIMIZE) && maximized) || args.hasOption(Option.MAXIMIZE)) {
987 mainFrame.setMaximized(true);
988 }
989 if (menu.fullscreenToggleAction != null) {
990 menu.fullscreenToggleAction.initial();
991 }
992
993 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
994
995 if (PlatformManager.isPlatformWindows()) {
996 try {
997 // Check for insecure certificates to remove.
998 // This is Windows-dependant code but it can't go to preStartupHook (need i18n)
999 // neither startupHook (need to be called before remote control)
1000 PlatformHookWindows.removeInsecureCertificates();
1001 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
1002 Logging.error(e);
1003 }
1004 }
1005
1006 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
1007 RemoteControl.start();
1008 }
1009
1010 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
1011 MessageNotifier.start();
1012 }
1013
1014 if (Config.getPref().getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
1015 // Repaint manager is registered so late for a reason - there is lots of violation during startup process
1016 // but they don't seem to break anything and are difficult to fix
1017 Logging.info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
1018 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
1019 }
1020 }
1021
1022 private static MainFrame createMainFrame(WindowGeometry geometry) {
1023 try {
1024 return new MainFrame(geometry);
1025 } catch (AWTError e) {
1026 // #12022 #16666 On Debian, Ubuntu and Linux Mint the first AWT toolkit access can fail because of ATK wrapper
1027 // Good news: the error happens after the toolkit initialization so we can just try again and it will work
1028 Logging.error(e);
1029 return new MainFrame(geometry);
1030 }
1031 }
1032
1033 /**
1034 * Updates system properties with the current values in the preferences.
1035 */
1036 private static void updateSystemProperties() {
1037 if ("true".equals(Config.getPref().get("prefer.ipv6", "auto"))
1038 && !"true".equals(Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"))) {
1039 // never set this to false, only true!
1040 Logging.info(tr("Try enabling IPv6 network, prefering IPv6 over IPv4 (only works on early startup)."));
1041 }
1042 Utils.updateSystemProperty("http.agent", Version.getInstance().getAgentString());
1043 Utils.updateSystemProperty("user.language", Config.getPref().get("language"));
1044 // Workaround to fix a Java bug. This ugly hack comes from Sun bug database: https://bugs.openjdk.java.net/browse/JDK-6292739
1045 // Force AWT toolkit to update its internal preferences (fix #6345).
1046 // Does not work anymore with Java 9, to remove with Java 9 migration
1047 if (Utils.getJavaVersion() < 9 && !GraphicsEnvironment.isHeadless()) {
1048 try {
1049 Field field = Toolkit.class.getDeclaredField("resources");
1050 Utils.setObjectsAccessible(field);
1051 field.set(null, ResourceBundle.getBundle("sun.awt.resources.awt"));
1052 } catch (ReflectiveOperationException | RuntimeException e) { // NOPMD
1053 // Catch RuntimeException in order to catch InaccessibleObjectException, new in Java 9
1054 Logging.log(Logging.LEVEL_WARN, null, e);
1055 }
1056 }
1057 // Possibility to disable SNI (not by default) in case of misconfigured https servers
1058 // See #9875 + http://stackoverflow.com/a/14884941/2257172
1059 // then https://josm.openstreetmap.de/ticket/12152#comment:5 for details
1060 if (Config.getPref().getBoolean("jdk.tls.disableSNIExtension", false)) {
1061 Utils.updateSystemProperty("jsse.enableSNIExtension", "false");
1062 }
1063 }
1064
1065 /**
1066 * Setup the sources for NTV2 grid shift files for projection support.
1067 * @since 12795
1068 */
1069 public static void setupNadGridSources() {
1070 NTV2GridShiftFileWrapper.registerNTV2GridShiftFileSource(
1071 NTV2GridShiftFileWrapper.NTV2_SOURCE_PRIORITY_LOCAL,
1072 NTV2Proj4DirGridShiftFileSource.getInstance());
1073 NTV2GridShiftFileWrapper.registerNTV2GridShiftFileSource(
1074 NTV2GridShiftFileWrapper.NTV2_SOURCE_PRIORITY_DOWNLOAD,
1075 JOSM_WEBSITE_NTV2_SOURCE);
1076 }
1077
1078 static void applyWorkarounds() {
1079 // Workaround for JDK-8180379: crash on Windows 10 1703 with Windows L&F and java < 8u141 / 9+172
1080 // To remove during Java 9 migration
1081 if (getSystemProperty("os.name").toLowerCase(Locale.ENGLISH).contains("windows 10") &&
1082 PlatformManager.getPlatform().getDefaultStyle().equals(LafPreference.LAF.get())) {
1083 try {
1084 String build = PlatformHookWindows.getCurrentBuild();
1085 if (build != null) {
1086 final int currentBuild = Integer.parseInt(build);
1087 final int javaVersion = Utils.getJavaVersion();
1088 final int javaUpdate = Utils.getJavaUpdate();
1089 final int javaBuild = Utils.getJavaBuild();
1090 // See https://technet.microsoft.com/en-us/windows/release-info.aspx
1091 if (currentBuild >= 15_063 && ((javaVersion == 8 && javaUpdate < 141)
1092 || (javaVersion == 9 && javaUpdate == 0 && javaBuild < 173))) {
1093 // Workaround from https://bugs.openjdk.java.net/browse/JDK-8179014
1094 UIManager.put("FileChooser.useSystemExtensionHiding", Boolean.FALSE);
1095 }
1096 }
1097 } catch (NumberFormatException | ReflectiveOperationException | JosmRuntimeException e) {
1098 Logging.error(e);
1099 } catch (ExceptionInInitializerError e) {
1100 Logging.log(Logging.LEVEL_ERROR, null, e);
1101 }
1102 }
1103 }
1104
1105 static void setupCallbacks() {
1106 OsmConnection.setOAuthAccessTokenFetcher(OAuthAuthorizationWizard::obtainAccessToken);
1107 AbstractCredentialsAgent.setCredentialsProvider(CredentialDialog::promptCredentials);
1108 MessageNotifier.setNotifierCallback(MainApplication::notifyNewMessages);
1109 DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
1110 SplitWayCommand.setWarningNotifier(msg -> new Notification(msg).setIcon(JOptionPane.WARNING_MESSAGE).show());
1111 FileWatcher.registerLoader(SourceType.MAP_PAINT_STYLE, MapPaintStyleLoader::reloadStyle);
1112 FileWatcher.registerLoader(SourceType.TAGCHECKER_RULE, MapCSSTagChecker::reloadRule);
1113 OsmUrlToBounds.setMapSizeSupplier(() -> {
1114 if (isDisplayingMapView()) {
1115 MapView mapView = getMap().mapView;
1116 return new Dimension(mapView.getWidth(), mapView.getHeight());
1117 } else {
1118 return GuiHelper.getScreenSize();
1119 }
1120 });
1121 }
1122
1123 static void setupUIManager() {
1124 String defaultlaf = PlatformManager.getPlatform().getDefaultStyle();
1125 String laf = LafPreference.LAF.get();
1126 try {
1127 UIManager.setLookAndFeel(laf);
1128 } catch (final NoClassDefFoundError | ClassNotFoundException e) {
1129 // Try to find look and feel in plugin classloaders
1130 Logging.trace(e);
1131 Class<?> klass = null;
1132 for (ClassLoader cl : PluginHandler.getResourceClassLoaders()) {
1133 try {
1134 klass = cl.loadClass(laf);
1135 break;
1136 } catch (ClassNotFoundException ex) {
1137 Logging.trace(ex);
1138 }
1139 }
1140 if (klass != null && LookAndFeel.class.isAssignableFrom(klass)) {
1141 try {
1142 UIManager.setLookAndFeel((LookAndFeel) klass.getConstructor().newInstance());
1143 } catch (ReflectiveOperationException ex) {
1144 Logging.log(Logging.LEVEL_WARN, "Cannot set Look and Feel: " + laf + ": "+ex.getMessage(), ex);
1145 } catch (UnsupportedLookAndFeelException ex) {
1146 Logging.info("Look and Feel not supported: " + laf);
1147 LafPreference.LAF.put(defaultlaf);
1148 Logging.trace(ex);
1149 }
1150 } else {
1151 Logging.info("Look and Feel not found: " + laf);
1152 LafPreference.LAF.put(defaultlaf);
1153 }
1154 } catch (UnsupportedLookAndFeelException e) {
1155 Logging.info("Look and Feel not supported: " + laf);
1156 LafPreference.LAF.put(defaultlaf);
1157 Logging.trace(e);
1158 } catch (InstantiationException | IllegalAccessException e) {
1159 Logging.error(e);
1160 }
1161
1162 UIManager.put("OptionPane.okIcon", ImageProvider.getIfAvailable("ok"));
1163 UIManager.put("OptionPane.yesIcon", UIManager.get("OptionPane.okIcon"));
1164 UIManager.put("OptionPane.cancelIcon", ImageProvider.getIfAvailable("cancel"));
1165 UIManager.put("OptionPane.noIcon", UIManager.get("OptionPane.cancelIcon"));
1166 // Ensures caret color is the same than text foreground color, see #12257
1167 // See http://docs.oracle.com/javase/8/docs/api/javax/swing/plaf/synth/doc-files/componentProperties.html
1168 for (String p : Arrays.asList(
1169 "EditorPane", "FormattedTextField", "PasswordField", "TextArea", "TextField", "TextPane")) {
1170 UIManager.put(p+".caretForeground", UIManager.getColor(p+".foreground"));
1171 }
1172
1173 double menuFontFactor = Config.getPref().getDouble("gui.scale.menu.font", 1.0);
1174 if (menuFontFactor != 1.0) {
1175 for (String key : Arrays.asList(
1176 "Menu.font", "MenuItem.font", "CheckBoxMenuItem.font", "RadioButtonMenuItem.font", "MenuItem.acceleratorFont")) {
1177 Font font = UIManager.getFont(key);
1178 if (font != null) {
1179 UIManager.put(key, font.deriveFont(font.getSize2D() * (float) menuFontFactor));
1180 }
1181 }
1182 }
1183 }
1184
1185 static Collection<PluginInformation> updateAndLoadEarlyPlugins(SplashScreen splash, SplashProgressMonitor monitor) {
1186 Collection<PluginInformation> pluginsToLoad;
1187 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false));
1188 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
1189 monitor.subTask(tr("Updating plugins"));
1190 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
1191 }
1192
1193 monitor.indeterminateSubTask(tr("Installing updated plugins"));
1194 try {
1195 PluginHandler.installDownloadedPlugins(pluginsToLoad, true);
1196 } catch (SecurityException e) {
1197 Logging.log(Logging.LEVEL_ERROR, "Unable to install plugins", e);
1198 }
1199
1200 monitor.indeterminateSubTask(tr("Loading early plugins"));
1201 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false));
1202 return pluginsToLoad;
1203 }
1204
1205 static void loadLatePlugins(SplashScreen splash, SplashProgressMonitor monitor, Collection<PluginInformation> pluginsToLoad) {
1206 monitor.indeterminateSubTask(tr("Loading plugins"));
1207 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false));
1208 GuiHelper.runInEDTAndWait(() -> toolbar.refreshToolbarControl());
1209 }
1210
1211 private static void processOffline(ProgramArguments args) {
1212 for (String offlineNames : args.get(Option.OFFLINE)) {
1213 for (String s : offlineNames.split(",")) {
1214 try {
1215 NetworkManager.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH)));
1216 } catch (IllegalArgumentException e) {
1217 Logging.log(Logging.LEVEL_ERROR,
1218 tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.",
1219 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values())), e);
1220 System.exit(1);
1221 return;
1222 }
1223 }
1224 }
1225 Set<OnlineResource> offline = NetworkManager.getOfflineResources();
1226 if (!offline.isEmpty()) {
1227 Logging.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}",
1228 "JOSM is running in offline mode. These resources will not be available: {0}",
1229 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray())));
1230 }
1231 }
1232
1233 /**
1234 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation,
1235 * disabling or enabling IPV6 may only be done with next start.
1236 */
1237 private static void checkIPv6() {
1238 if ("auto".equals(Config.getPref().get("prefer.ipv6", "auto"))) {
1239 new Thread((Runnable) () -> { /* this may take some time (DNS, Connect) */
1240 boolean hasv6 = false;
1241 boolean wasv6 = Config.getPref().getBoolean("validated.ipv6", false);
1242 try {
1243 /* Use the check result from last run of the software, as after the test, value
1244 changes have no effect anymore */
1245 if (wasv6) {
1246 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
1247 }
1248 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) {
1249 if (a instanceof Inet6Address) {
1250 if (a.isReachable(1000)) {
1251 /* be sure it REALLY works */
1252 SSLSocketFactory.getDefault().createSocket(a, 443).close();
1253 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true");
1254 if (!wasv6) {
1255 Logging.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart."));
1256 } else {
1257 Logging.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4."));
1258 }
1259 hasv6 = true;
1260 }
1261 break; /* we're done */
1262 }
1263 }
1264 } catch (IOException | SecurityException e) {
1265 Logging.debug("Exception while checking IPv6 connectivity: {0}", e);
1266 Logging.trace(e);
1267 }
1268 if (wasv6 && !hasv6) {
1269 Logging.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart."));
1270 Config.getPref().putBoolean("validated.ipv6", hasv6); // be sure it is stored before the restart!
1271 try {
1272 RestartAction.restartJOSM();
1273 } catch (IOException e) {
1274 Logging.error(e);
1275 }
1276 }
1277 Config.getPref().putBoolean("validated.ipv6", hasv6);
1278 }, "IPv6-checker").start();
1279 }
1280 }
1281
1282 /**
1283 * Download area specified as Bounds value.
1284 * @param rawGps Flag to download raw GPS tracks
1285 * @param b The bounds value
1286 * @return the complete download task (including post-download handler)
1287 */
1288 static List<Future<?>> downloadFromParamBounds(final boolean rawGps, Bounds b) {
1289 DownloadTask task = rawGps ? new DownloadGpsTask() : new DownloadOsmTask();
1290 // asynchronously launch the download task ...
1291 Future<?> future = task.download(new DownloadParams().withNewLayer(true), b, null);
1292 // ... and the continuation when the download is finished (this will wait for the download to finish)
1293 return Collections.singletonList(MainApplication.worker.submit(new PostDownloadHandler(task, future)));
1294 }
1295
1296 /**
1297 * Handle command line instructions after GUI has been initialized.
1298 * @param args program arguments
1299 * @return the list of submitted tasks
1300 */
1301 static List<Future<?>> postConstructorProcessCmdLine(ProgramArguments args) {
1302 List<Future<?>> tasks = new ArrayList<>();
1303 List<File> fileList = new ArrayList<>();
1304 for (String s : args.get(Option.DOWNLOAD)) {
1305 tasks.addAll(DownloadParamType.paramType(s).download(s, fileList));
1306 }
1307 if (!fileList.isEmpty()) {
1308 tasks.add(OpenFileAction.openFiles(fileList, true));
1309 }
1310 for (String s : args.get(Option.DOWNLOADGPS)) {
1311 tasks.addAll(DownloadParamType.paramType(s).downloadGps(s));
1312 }
1313 final Collection<String> selectionArguments = args.get(Option.SELECTION);
1314 if (!selectionArguments.isEmpty()) {
1315 tasks.add(MainApplication.worker.submit(() -> {
1316 for (String s : selectionArguments) {
1317 SearchAction.search(s, SearchMode.add);
1318 }
1319 }));
1320 }
1321 return tasks;
1322 }
1323
1324 private static class GuiFinalizationWorker implements Runnable {
1325
1326 private final ProgramArguments args;
1327 private final DefaultProxySelector proxySelector;
1328
1329 GuiFinalizationWorker(ProgramArguments args, DefaultProxySelector proxySelector) {
1330 this.args = args;
1331 this.proxySelector = proxySelector;
1332 }
1333
1334 @Override
1335 public void run() {
1336
1337 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
1338 if (!handleProxyErrors()) {
1339 handleNetworkErrors();
1340 }
1341
1342 // Restore autosave layers after crash and start autosave thread
1343 handleAutosave();
1344
1345 // Handle command line instructions
1346 postConstructorProcessCmdLine(args);
1347
1348 // Show download dialog if autostart is enabled
1349 DownloadDialog.autostartIfNeeded();
1350 }
1351
1352 private static void handleAutosave() {
1353 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
1354 AutosaveTask autosaveTask = new AutosaveTask();
1355 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
1356 if (!unsavedLayerFiles.isEmpty()) {
1357 ExtendedDialog dialog = new ExtendedDialog(
1358 mainFrame,
1359 tr("Unsaved osm data"),
1360 tr("Restore"), tr("Cancel"), tr("Discard")
1361 );
1362 dialog.setContent(
1363 trn("JOSM found {0} unsaved osm data layer. ",
1364 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
1365 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
1366 dialog.setButtonIcons("ok", "cancel", "dialogs/delete");
1367 int selection = dialog.showDialog().getValue();
1368 if (selection == 1) {
1369 autosaveTask.recoverUnsavedLayers();
1370 } else if (selection == 3) {
1371 autosaveTask.discardUnsavedLayers();
1372 }
1373 }
1374 try {
1375 autosaveTask.schedule();
1376 } catch (SecurityException e) {
1377 Logging.log(Logging.LEVEL_ERROR, "Unable to schedule autosave!", e);
1378 }
1379 }
1380 }
1381
1382 private static boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
1383 if (hasErrors) {
1384 ExtendedDialog ed = new ExtendedDialog(
1385 mainFrame, title,
1386 tr("Change proxy settings"), tr("Cancel"));
1387 ed.setButtonIcons("dialogs/settings", "cancel").setCancelButton(2);
1388 ed.setMinimumSize(new Dimension(460, 260));
1389 ed.setIcon(JOptionPane.WARNING_MESSAGE);
1390 ed.setContent(message);
1391
1392 if (ed.showDialog().getValue() == 1) {
1393 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
1394 }
1395 }
1396 return hasErrors;
1397 }
1398
1399 private boolean handleProxyErrors() {
1400 return proxySelector != null &&
1401 handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
1402 tr("JOSM tried to access the following resources:<br>" +
1403 "{0}" +
1404 "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
1405 "{1}" +
1406 "Would you like to change your proxy settings now?",
1407 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
1408 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
1409 ));
1410 }
1411
1412 private static boolean handleNetworkErrors() {
1413 Map<String, Throwable> networkErrors = NetworkManager.getNetworkErrors();
1414 boolean condition = !networkErrors.isEmpty();
1415 if (condition) {
1416 Set<String> errors = new TreeSet<>();
1417 for (Throwable t : networkErrors.values()) {
1418 errors.add(t.toString());
1419 }
1420 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
1421 tr("JOSM tried to access the following resources:<br>" +
1422 "{0}" +
1423 "but <b>failed</b> to do so, because of the following network errors:<br>" +
1424 "{1}" +
1425 "It may be due to a missing proxy configuration.<br>" +
1426 "Would you like to change your proxy settings now?",
1427 Utils.joinAsHtmlUnorderedList(networkErrors.keySet()),
1428 Utils.joinAsHtmlUnorderedList(errors)
1429 ));
1430 }
1431 return false;
1432 }
1433 }
1434
1435 private static class DefaultNativeOsCallback implements NativeOsCallback {
1436 @Override
1437 public void openFiles(List<File> files) {
1438 Executors.newSingleThreadExecutor(Utils.newThreadFactory("openFiles-%d", Thread.NORM_PRIORITY)).submit(
1439 new OpenFileTask(files, null) {
1440 @Override
1441 protected void realRun() throws SAXException, IOException, OsmTransferException {
1442 // Wait for JOSM startup is advanced enough to load a file
1443 while (mainFrame == null || !mainFrame.isVisible()) {
1444 try {
1445 Thread.sleep(25);
1446 } catch (InterruptedException e) {
1447 Logging.warn(e);
1448 Thread.currentThread().interrupt();
1449 }
1450 }
1451 super.realRun();
1452 }
1453 });
1454 }
1455
1456 @Override
1457 public boolean handleQuitRequest() {
1458 return MainApplication.exitJosm(false, 0, null);
1459 }
1460
1461 @Override
1462 public void handleAbout() {
1463 MainApplication.getMenu().about.actionPerformed(null);
1464 }
1465
1466 @Override
1467 public void handlePreferences() {
1468 MainApplication.getMenu().preferences.actionPerformed(null);
1469 }
1470 }
1471
1472 static void notifyNewMessages(UserInfo userInfo) {
1473 GuiHelper.runInEDT(() -> {
1474 JPanel panel = new JPanel(new GridBagLayout());
1475 panel.add(new JLabel(trn("You have {0} unread message.", "You have {0} unread messages.",
1476 userInfo.getUnreadMessages(), userInfo.getUnreadMessages())),
1477 GBC.eol());
1478 panel.add(new UrlLabel(Config.getUrls().getBaseUserUrl() + '/' + userInfo.getDisplayName() + "/inbox",
1479 tr("Click here to see your inbox.")), GBC.eol());
1480 panel.setOpaque(false);
1481 new Notification().setContent(panel)
1482 .setIcon(JOptionPane.INFORMATION_MESSAGE)
1483 .setDuration(Notification.TIME_LONG)
1484 .show();
1485 });
1486 }
1487}
Note: See TracBrowser for help on using the repository browser.