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

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

SpotBugs - fix some warnings

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