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