source: josm/trunk/src/org/openstreetmap/josm/Main.java@ 12784

Last change on this file since 12784 was 12777, checked in by bastiK, 7 years ago

see #15229 - add registry for NTV2 grid file sources

removes dependency of NTV2GridShiftFileWrapper on Main

  • Property svn:eol-style set to native
File size: 37.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.GraphicsEnvironment;
8import java.io.IOException;
9import java.lang.ref.WeakReference;
10import java.net.URL;
11import java.text.MessageFormat;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.EnumSet;
16import java.util.HashMap;
17import java.util.Iterator;
18import java.util.List;
19import java.util.Map;
20import java.util.Objects;
21import java.util.Set;
22import java.util.concurrent.Callable;
23import java.util.concurrent.ExecutionException;
24import java.util.concurrent.ExecutorService;
25import java.util.concurrent.Executors;
26import java.util.concurrent.Future;
27
28import javax.swing.Action;
29
30import org.openstreetmap.josm.actions.JosmAction;
31import org.openstreetmap.josm.data.Bounds;
32import org.openstreetmap.josm.data.Preferences;
33import org.openstreetmap.josm.data.UndoRedoHandler;
34import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
35import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
36import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat;
37import org.openstreetmap.josm.data.osm.DataSet;
38import org.openstreetmap.josm.data.osm.OsmPrimitive;
39import org.openstreetmap.josm.data.projection.Projection;
40import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
41import org.openstreetmap.josm.gui.MainApplication;
42import org.openstreetmap.josm.gui.MainMenu;
43import org.openstreetmap.josm.gui.MainPanel;
44import org.openstreetmap.josm.gui.MapFrame;
45import org.openstreetmap.josm.gui.MapFrameListener;
46import org.openstreetmap.josm.gui.layer.MainLayerManager;
47import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
48import org.openstreetmap.josm.io.FileWatcher;
49import org.openstreetmap.josm.io.OnlineResource;
50import org.openstreetmap.josm.io.OsmApi;
51import org.openstreetmap.josm.tools.CheckParameterUtil;
52import org.openstreetmap.josm.tools.ImageProvider;
53import org.openstreetmap.josm.tools.JosmRuntimeException;
54import org.openstreetmap.josm.tools.Logging;
55import org.openstreetmap.josm.tools.Platform;
56import org.openstreetmap.josm.tools.PlatformHook;
57import org.openstreetmap.josm.tools.PlatformHookOsx;
58import org.openstreetmap.josm.tools.PlatformHookWindows;
59import org.openstreetmap.josm.tools.Shortcut;
60import org.openstreetmap.josm.tools.Utils;
61import org.openstreetmap.josm.tools.bugreport.BugReport;
62
63/**
64 * Abstract class holding various static global variables and methods used in large parts of JOSM application.
65 * @since 98
66 */
67public abstract class Main {
68
69 /**
70 * The JOSM website URL.
71 * @since 6897 (was public from 6143 to 6896)
72 */
73 private static final String JOSM_WEBSITE = "https://josm.openstreetmap.de";
74
75 /**
76 * The OSM website URL.
77 * @since 6897 (was public from 6453 to 6896)
78 */
79 private static final String OSM_WEBSITE = "https://www.openstreetmap.org";
80
81 /**
82 * Replies true if JOSM currently displays a map view. False, if it doesn't, i.e. if
83 * it only shows the MOTD panel.
84 * <p>
85 * You do not need this when accessing the layer manager. The layer manager will be empty if no map view is shown.
86 *
87 * @return <code>true</code> if JOSM currently displays a map view
88 * @deprecated use {@link org.openstreetmap.josm.gui.MainApplication#isDisplayingMapView()}
89 */
90 @Deprecated
91 public static boolean isDisplayingMapView() {
92 return MainApplication.isDisplayingMapView();
93 }
94
95 /**
96 * Global parent component for all dialogs and message boxes
97 */
98 public static Component parent;
99
100 /**
101 * Global application.
102 */
103 public static volatile Main main;
104
105 /**
106 * The worker thread slave. This is for executing all long and intensive
107 * calculations. The executed runnables are guaranteed to be executed separately and sequential.
108 * @deprecated use {@link MainApplication#worker} instead
109 */
110 @Deprecated
111 public static ExecutorService worker;
112
113 /**
114 * Global application preferences
115 */
116 public static final Preferences pref = new Preferences();
117
118 /**
119 * The MapFrame.
120 * <p>
121 * There should be no need to access this to access any map data. Use {@link MainApplication#getLayerManager} instead.
122 *
123 * @deprecated Use {@link MainApplication#getMap()} instead
124 * @see MainPanel
125 */
126 @Deprecated
127 public static MapFrame map;
128
129 /**
130 * The toolbar preference control to register new actions.
131 * @deprecated Use {@link MainApplication#getToolbar} instead
132 */
133 @Deprecated
134 public static volatile ToolbarPreferences toolbar;
135
136 /**
137 * The commands undo/redo handler.
138 */
139 public final UndoRedoHandler undoRedo = new UndoRedoHandler();
140
141 /**
142 * The main menu bar at top of screen.
143 * @deprecated Use {@link MainApplication#getMenu} instead
144 */
145 @Deprecated
146 public MainMenu menu;
147
148 /**
149 * The main panel.
150 * @deprecated Use {@link MainApplication#getMainPanel} instead
151 * @since 12125
152 */
153 @Deprecated
154 public MainPanel panel;
155
156 /**
157 * The file watcher service.
158 */
159 public static final FileWatcher fileWatcher = new FileWatcher();
160
161 private static final Map<String, Throwable> NETWORK_ERRORS = new HashMap<>();
162
163 private static final Set<OnlineResource> OFFLINE_RESOURCES = EnumSet.noneOf(OnlineResource.class);
164
165 /**
166 * Logging level (5 = trace, 4 = debug, 3 = info, 2 = warn, 1 = error, 0 = none).
167 * @since 6248
168 * @deprecated Use {@link Logging} class.
169 */
170 @Deprecated
171 public static int logLevel = 3;
172
173 /**
174 * Replies the first lines of last 5 error and warning messages, used for bug reports
175 * @return the first lines of last 5 error and warning messages
176 * @since 7420
177 * @deprecated Use {@link Logging#getLastErrorAndWarnings}.
178 */
179 @Deprecated
180 public static final Collection<String> getLastErrorAndWarnings() {
181 return Logging.getLastErrorAndWarnings();
182 }
183
184 /**
185 * Clears the list of last error and warning messages.
186 * @since 8959
187 * @deprecated Use {@link Logging#clearLastErrorAndWarnings}.
188 */
189 @Deprecated
190 public static void clearLastErrorAndWarnings() {
191 Logging.clearLastErrorAndWarnings();
192 }
193
194 /**
195 * Prints an error message if logging is on.
196 * @param msg The message to print.
197 * @since 6248
198 * @deprecated Use {@link Logging#error(String)}.
199 */
200 @Deprecated
201 public static void error(String msg) {
202 Logging.error(msg);
203 }
204
205 /**
206 * Prints a warning message if logging is on.
207 * @param msg The message to print.
208 * @deprecated Use {@link Logging#warn(String)}.
209 */
210 @Deprecated
211 public static void warn(String msg) {
212 Logging.warn(msg);
213 }
214
215 /**
216 * Prints an informational message if logging is on.
217 * @param msg The message to print.
218 * @deprecated Use {@link Logging#info(String)}.
219 */
220 @Deprecated
221 public static void info(String msg) {
222 Logging.info(msg);
223 }
224
225 /**
226 * Prints a debug message if logging is on.
227 * @param msg The message to print.
228 * @deprecated Use {@link Logging#debug(String)}.
229 */
230 @Deprecated
231 public static void debug(String msg) {
232 Logging.debug(msg);
233 }
234
235 /**
236 * Prints a trace message if logging is on.
237 * @param msg The message to print.
238 * @deprecated Use {@link Logging#trace(String)}.
239 */
240 @Deprecated
241 public static void trace(String msg) {
242 Logging.trace(msg);
243 }
244
245 /**
246 * Determines if debug log level is enabled.
247 * Useful to avoid costly construction of debug messages when not enabled.
248 * @return {@code true} if log level is at least debug, {@code false} otherwise
249 * @since 6852
250 * @deprecated Use {@link Logging#isDebugEnabled}.
251 */
252 @Deprecated
253 public static boolean isDebugEnabled() {
254 return Logging.isDebugEnabled();
255 }
256
257 /**
258 * Determines if trace log level is enabled.
259 * Useful to avoid costly construction of trace messages when not enabled.
260 * @return {@code true} if log level is at least trace, {@code false} otherwise
261 * @since 6852
262 * @deprecated Use {@link Logging#isTraceEnabled}.
263 */
264 @Deprecated
265 public static boolean isTraceEnabled() {
266 return Logging.isTraceEnabled();
267 }
268
269 /**
270 * Prints a formatted error message if logging is on. Calls {@link MessageFormat#format}
271 * function to format text.
272 * @param msg The formatted message to print.
273 * @param objects The objects to insert into format string.
274 * @since 6248
275 * @deprecated Use {@link Logging#error(String, Object...)}.
276 */
277 @Deprecated
278 public static void error(String msg, Object... objects) {
279 Logging.error(msg, objects);
280 }
281
282 /**
283 * Prints a formatted warning message if logging is on. Calls {@link MessageFormat#format}
284 * function to format text.
285 * @param msg The formatted message to print.
286 * @param objects The objects to insert into format string.
287 * @deprecated Use {@link Logging#warn(String, Object...)}.
288 */
289 @Deprecated
290 public static void warn(String msg, Object... objects) {
291 Logging.warn(msg, objects);
292 }
293
294 /**
295 * Prints a formatted informational message if logging is on. Calls {@link MessageFormat#format}
296 * function to format text.
297 * @param msg The formatted message to print.
298 * @param objects The objects to insert into format string.
299 * @deprecated Use {@link Logging#info(String, Object...)}.
300 */
301 @Deprecated
302 public static void info(String msg, Object... objects) {
303 Logging.info(msg, objects);
304 }
305
306 /**
307 * Prints a formatted debug message if logging is on. Calls {@link MessageFormat#format}
308 * function to format text.
309 * @param msg The formatted message to print.
310 * @param objects The objects to insert into format string.
311 * @deprecated Use {@link Logging#debug(String, Object...)}.
312 */
313 @Deprecated
314 public static void debug(String msg, Object... objects) {
315 Logging.debug(msg, objects);
316 }
317
318 /**
319 * Prints a formatted trace message if logging is on. Calls {@link MessageFormat#format}
320 * function to format text.
321 * @param msg The formatted message to print.
322 * @param objects The objects to insert into format string.
323 * @deprecated Use {@link Logging#trace(String, Object...)}.
324 */
325 @Deprecated
326 public static void trace(String msg, Object... objects) {
327 Logging.trace(msg, objects);
328 }
329
330 /**
331 * Prints an error message for the given Throwable.
332 * @param t The throwable object causing the error
333 * @since 6248
334 * @deprecated Use {@link Logging#error(Throwable)}.
335 */
336 @Deprecated
337 public static void error(Throwable t) {
338 Logging.error(t);
339 }
340
341 /**
342 * Prints a warning message for the given Throwable.
343 * @param t The throwable object causing the error
344 * @since 6248
345 * @deprecated Use {@link Logging#warn(Throwable)}.
346 */
347 @Deprecated
348 public static void warn(Throwable t) {
349 Logging.warn(t);
350 }
351
352 /**
353 * Prints a debug message for the given Throwable. Useful for exceptions usually ignored
354 * @param t The throwable object causing the error
355 * @since 10420
356 * @deprecated Use {@link Logging#debug(Throwable)}.
357 */
358 @Deprecated
359 public static void debug(Throwable t) {
360 Logging.debug(t);
361 }
362
363 /**
364 * Prints a trace message for the given Throwable. Useful for exceptions usually ignored
365 * @param t The throwable object causing the error
366 * @since 10420
367 * @deprecated Use {@link Logging#trace(Throwable)}.
368 */
369 @Deprecated
370 public static void trace(Throwable t) {
371 Logging.trace(t);
372 }
373
374 /**
375 * Prints an error message for the given Throwable.
376 * @param t The throwable object causing the error
377 * @param stackTrace {@code true}, if the stacktrace should be displayed
378 * @since 6642
379 * @deprecated Use {@link Logging#log(java.util.logging.Level, Throwable)}
380 * or {@link Logging#logWithStackTrace(java.util.logging.Level, Throwable)}.
381 */
382 @Deprecated
383 public static void error(Throwable t, boolean stackTrace) {
384 if (stackTrace) {
385 Logging.log(Logging.LEVEL_ERROR, t);
386 } else {
387 Logging.logWithStackTrace(Logging.LEVEL_ERROR, t);
388 }
389 }
390
391 /**
392 * Prints an error message for the given Throwable.
393 * @param t The throwable object causing the error
394 * @param message additional error message
395 * @since 10420
396 * @deprecated Use {@link Logging#log(java.util.logging.Level, String, Throwable)}.
397 */
398 @Deprecated
399 public static void error(Throwable t, String message) {
400 Logging.log(Logging.LEVEL_ERROR, message, t);
401 }
402
403 /**
404 * Prints a warning message for the given Throwable.
405 * @param t The throwable object causing the error
406 * @param stackTrace {@code true}, if the stacktrace should be displayed
407 * @since 6642
408 * @deprecated Use {@link Logging#log(java.util.logging.Level, Throwable)}
409 * or {@link Logging#logWithStackTrace(java.util.logging.Level, Throwable)}.
410 */
411 @Deprecated
412 public static void warn(Throwable t, boolean stackTrace) {
413 if (stackTrace) {
414 Logging.log(Logging.LEVEL_WARN, t);
415 } else {
416 Logging.logWithStackTrace(Logging.LEVEL_WARN, t);
417 }
418 }
419
420 /**
421 * Prints a warning message for the given Throwable.
422 * @param t The throwable object causing the error
423 * @param message additional error message
424 * @since 10420
425 * @deprecated Use {@link Logging#log(java.util.logging.Level, String, Throwable)}.
426 */
427 @Deprecated
428 public static void warn(Throwable t, String message) {
429 Logging.log(Logging.LEVEL_WARN, message, t);
430 }
431
432 /**
433 * Returns a human-readable message of error, also usable for developers.
434 * @param t The error
435 * @return The human-readable error message
436 * @since 6642
437 * @deprecated Use {@link Logging#getErrorMessage}.
438 */
439 @Deprecated
440 public static String getErrorMessage(Throwable t) {
441 if (t == null) {
442 return null;
443 } else {
444 return Logging.getErrorMessage(t);
445 }
446 }
447
448 /**
449 * Platform specific code goes in here.
450 * Plugins may replace it, however, some hooks will be called before any plugins have been loaded.
451 * So if you need to hook into those early ones, split your class and send the one with the early hooks
452 * to the JOSM team for inclusion.
453 */
454 public static volatile PlatformHook platform;
455
456 private static volatile InitStatusListener initListener;
457
458 /**
459 * Initialization task listener.
460 */
461 public interface InitStatusListener {
462
463 /**
464 * Called when an initialization task updates its status.
465 * @param event task name
466 * @return new status
467 */
468 Object updateStatus(String event);
469
470 /**
471 * Called when an initialization task completes.
472 * @param status final status
473 */
474 void finish(Object status);
475 }
476
477 /**
478 * Sets initialization task listener.
479 * @param listener initialization task listener
480 */
481 public static void setInitStatusListener(InitStatusListener listener) {
482 CheckParameterUtil.ensureParameterNotNull(listener);
483 initListener = listener;
484 }
485
486 /**
487 * Constructs new {@code Main} object.
488 * @see #initialize()
489 */
490 protected Main() {
491 setInstance(this);
492 }
493
494 private static void setInstance(Main instance) {
495 main = instance;
496 }
497
498 /**
499 * Initializes the main object. A lot of global variables are initialized here.
500 * @since 10340
501 */
502 public void initialize() {
503 // Initializes tasks that must be run before parallel tasks
504 runInitializationTasks(beforeInitializationTasks());
505
506 // Initializes tasks to be executed (in parallel) by a ExecutorService
507 try {
508 ExecutorService service = Executors.newFixedThreadPool(
509 Runtime.getRuntime().availableProcessors(), Utils.newThreadFactory("main-init-%d", Thread.NORM_PRIORITY));
510 for (Future<Void> i : service.invokeAll(parallelInitializationTasks())) {
511 i.get();
512 }
513 // asynchronous initializations to be completed eventually
514 asynchronousRunnableTasks().forEach(service::submit);
515 asynchronousCallableTasks().forEach(service::submit);
516 service.shutdown();
517 } catch (InterruptedException | ExecutionException ex) {
518 throw new JosmRuntimeException(ex);
519 }
520
521 // Initializes tasks that must be run after parallel tasks
522 runInitializationTasks(afterInitializationTasks());
523 }
524
525 private static void runInitializationTasks(List<InitializationTask> tasks) {
526 for (InitializationTask task : tasks) {
527 try {
528 task.call();
529 } catch (JosmRuntimeException e) {
530 // Can happen if the current projection needs NTV2 grid which is not available
531 // In this case we want the user be able to change his projection
532 BugReport.intercept(e).warn();
533 }
534 }
535 }
536
537 /**
538 * Returns tasks that must be run before parallel tasks.
539 * @return tasks that must be run before parallel tasks
540 * @see #afterInitializationTasks
541 * @see #parallelInitializationTasks
542 */
543 protected List<InitializationTask> beforeInitializationTasks() {
544 return Collections.emptyList();
545 }
546
547 /**
548 * Returns tasks to be executed (in parallel) by a ExecutorService.
549 * @return tasks to be executed (in parallel) by a ExecutorService
550 */
551 protected Collection<InitializationTask> parallelInitializationTasks() {
552 return Collections.emptyList();
553 }
554
555 /**
556 * Returns asynchronous callable initializations to be completed eventually
557 * @return asynchronous callable initializations to be completed eventually
558 */
559 protected List<Callable<?>> asynchronousCallableTasks() {
560 return Collections.emptyList();
561 }
562
563 /**
564 * Returns asynchronous runnable initializations to be completed eventually
565 * @return asynchronous runnable initializations to be completed eventually
566 */
567 protected List<Runnable> asynchronousRunnableTasks() {
568 return Collections.emptyList();
569 }
570
571 /**
572 * Returns tasks that must be run after parallel tasks.
573 * @return tasks that must be run after parallel tasks
574 * @see #beforeInitializationTasks
575 * @see #parallelInitializationTasks
576 */
577 protected List<InitializationTask> afterInitializationTasks() {
578 return Collections.emptyList();
579 }
580
581 protected static final class InitializationTask implements Callable<Void> {
582
583 private final String name;
584 private final Runnable task;
585
586 /**
587 * Constructs a new {@code InitializationTask}.
588 * @param name translated name to be displayed to user
589 * @param task runnable initialization task
590 */
591 public InitializationTask(String name, Runnable task) {
592 this.name = name;
593 this.task = task;
594 }
595
596 @Override
597 public Void call() {
598 Object status = null;
599 if (initListener != null) {
600 status = initListener.updateStatus(name);
601 }
602 task.run();
603 if (initListener != null) {
604 initListener.finish(status);
605 }
606 return null;
607 }
608 }
609
610 /**
611 * Returns the main layer manager that is used by the map view.
612 * @return The layer manager. The value returned will never change.
613 * @since 10279
614 * @deprecated use {@link MainApplication#getLayerManager} instead
615 */
616 @Deprecated
617 public static MainLayerManager getLayerManager() {
618 return MainApplication.getLayerManager();
619 }
620
621 /**
622 * Replies the current selected primitives, from a end-user point of view.
623 * It is not always technically the same collection of primitives than {@link DataSet#getSelected()}.
624 * @return The current selected primitives, from a end-user point of view. Can be {@code null}.
625 * @since 6546
626 */
627 public Collection<OsmPrimitive> getInProgressSelection() {
628 return Collections.emptyList();
629 }
630
631 /**
632 * Gets the active edit data set.
633 * @return That data set, <code>null</code>.
634 * @since 12691
635 */
636 public abstract DataSet getEditDataSet();
637
638 /**
639 * Sets the active data set.
640 * @param ds New edit data set, or <code>null</code>
641 * @since 12718
642 */
643 public abstract void setEditDataSet(DataSet ds);
644
645 /**
646 * Determines if the list of data sets managed by JOSM contains {@code ds}.
647 * @param ds the data set to look for
648 * @return {@code true} if the list of data sets managed by JOSM contains {@code ds}
649 * @since 12718
650 */
651 public abstract boolean containsDataSet(DataSet ds);
652
653 /**
654 * Registers a {@code JosmAction} and its shortcut.
655 * @param action action defining its own shortcut
656 * @deprecated use {@link MainApplication#registerActionShortcut(JosmAction)} instead
657 */
658 @Deprecated
659 public static void registerActionShortcut(JosmAction action) {
660 MainApplication.registerActionShortcut(action);
661 }
662
663 /**
664 * Registers an action and its shortcut.
665 * @param action action to register
666 * @param shortcut shortcut to associate to {@code action}
667 * @deprecated use {@link MainApplication#registerActionShortcut(Action, Shortcut)} instead
668 */
669 @Deprecated
670 public static void registerActionShortcut(Action action, Shortcut shortcut) {
671 MainApplication.registerActionShortcut(action, shortcut);
672 }
673
674 /**
675 * Unregisters a shortcut.
676 * @param shortcut shortcut to unregister
677 * @deprecated use {@link MainApplication#unregisterShortcut(Shortcut)} instead
678 */
679 @Deprecated
680 public static void unregisterShortcut(Shortcut shortcut) {
681 MainApplication.unregisterShortcut(shortcut);
682 }
683
684 /**
685 * Unregisters a {@code JosmAction} and its shortcut.
686 * @param action action to unregister
687 * @deprecated use {@link MainApplication#unregisterActionShortcut(JosmAction)} instead
688 */
689 @Deprecated
690 public static void unregisterActionShortcut(JosmAction action) {
691 MainApplication.unregisterActionShortcut(action);
692 }
693
694 /**
695 * Unregisters an action and its shortcut.
696 * @param action action to unregister
697 * @param shortcut shortcut to unregister
698 * @deprecated use {@link MainApplication#unregisterActionShortcut(Action, Shortcut)} instead
699 */
700 @Deprecated
701 public static void unregisterActionShortcut(Action action, Shortcut shortcut) {
702 MainApplication.unregisterActionShortcut(action, shortcut);
703 }
704
705 /**
706 * Replies the registered action for the given shortcut
707 * @param shortcut The shortcut to look for
708 * @return the registered action for the given shortcut
709 * @deprecated use {@link MainApplication#getRegisteredActionShortcut(Shortcut)} instead
710 * @since 5696
711 */
712 @Deprecated
713 public static Action getRegisteredActionShortcut(Shortcut shortcut) {
714 return MainApplication.getRegisteredActionShortcut(shortcut);
715 }
716
717 ///////////////////////////////////////////////////////////////////////////
718 // Implementation part
719 ///////////////////////////////////////////////////////////////////////////
720
721 /**
722 * Should be called before the main constructor to setup some parameter stuff
723 */
724 public static void preConstructorInit() {
725 // init default coordinate format
726 ICoordinateFormat fmt = CoordinateFormatManager.getCoordinateFormat(Main.pref.get("coordinates"));
727 if (fmt == null) {
728 fmt = DecimalDegreesCoordinateFormat.INSTANCE;
729 }
730 CoordinateFormatManager.setCoordinateFormat(fmt);
731 }
732
733 /**
734 * Closes JOSM and optionally terminates the Java Virtual Machine (JVM).
735 * @param exit If {@code true}, the JVM is terminated by running {@link System#exit} with a given return code.
736 * @param exitCode The return code
737 * @return {@code true}
738 * @since 12636
739 */
740 public static boolean exitJosm(boolean exit, int exitCode) {
741 if (Main.main != null) {
742 Main.main.shutdown();
743 }
744
745 if (exit) {
746 System.exit(exitCode);
747 }
748 return true;
749 }
750
751 /**
752 * Shutdown JOSM.
753 */
754 protected void shutdown() {
755 if (!GraphicsEnvironment.isHeadless()) {
756 ImageProvider.shutdown(false);
757 }
758 try {
759 pref.saveDefaults();
760 } catch (IOException ex) {
761 Logging.log(Logging.LEVEL_WARN, tr("Failed to save default preferences."), ex);
762 }
763 if (!GraphicsEnvironment.isHeadless()) {
764 ImageProvider.shutdown(true);
765 }
766 }
767
768 /**
769 * Identifies the current operating system family and initializes the platform hook accordingly.
770 * @since 1849
771 */
772 public static void determinePlatformHook() {
773 platform = Platform.determinePlatform().accept(PlatformHook.CONSTRUCT_FROM_PLATFORM);
774 }
775
776 /* ----------------------------------------------------------------------------------------- */
777 /* projection handling - Main is a registry for a single, global projection instance */
778 /* */
779 /* TODO: For historical reasons the registry is implemented by Main. An alternative approach */
780 /* would be a singleton org.openstreetmap.josm.data.projection.ProjectionRegistry class. */
781 /* ----------------------------------------------------------------------------------------- */
782 /**
783 * The projection method used.
784 * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
785 * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
786 */
787 private static volatile Projection proj;
788
789 /**
790 * Replies the current projection.
791 *
792 * @return the currently active projection
793 */
794 public static Projection getProjection() {
795 return proj;
796 }
797
798 /**
799 * Sets the current projection
800 *
801 * @param p the projection
802 */
803 public static void setProjection(Projection p) {
804 CheckParameterUtil.ensureParameterNotNull(p);
805 Projection oldValue = proj;
806 Bounds b = main != null ? main.getRealBounds() : null;
807 proj = p;
808 fireProjectionChanged(oldValue, proj, b);
809 }
810
811 /**
812 * Returns the bounds for the current projection. Used for projection events.
813 * @return the bounds for the current projection
814 * @see #restoreOldBounds
815 */
816 protected Bounds getRealBounds() {
817 // To be overriden
818 return null;
819 }
820
821 /**
822 * Restore clean state corresponding to old bounds after a projection change event.
823 * @param oldBounds bounds previously returned by {@link #getRealBounds}, before the change of projection
824 * @see #getRealBounds
825 */
826 protected void restoreOldBounds(Bounds oldBounds) {
827 // To be overriden
828 }
829
830 /*
831 * Keep WeakReferences to the listeners. This relieves clients from the burden of
832 * explicitly removing the listeners and allows us to transparently register every
833 * created dataset as projection change listener.
834 */
835 private static final List<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<>();
836
837 private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
838 if ((newValue == null ^ oldValue == null)
839 || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
840 synchronized (Main.class) {
841 Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator();
842 while (it.hasNext()) {
843 WeakReference<ProjectionChangeListener> wr = it.next();
844 ProjectionChangeListener listener = wr.get();
845 if (listener == null) {
846 it.remove();
847 continue;
848 }
849 listener.projectionChanged(oldValue, newValue);
850 }
851 }
852 if (newValue != null && oldBounds != null && main != null) {
853 main.restoreOldBounds(oldBounds);
854 }
855 /* TODO - remove layers with fixed projection */
856 }
857 }
858
859 /**
860 * Register a projection change listener.
861 * The listener is registered to be weak, so keep a reference of it if you want it to be preserved.
862 *
863 * @param listener the listener. Ignored if <code>null</code>.
864 */
865 public static void addProjectionChangeListener(ProjectionChangeListener listener) {
866 if (listener == null) return;
867 synchronized (Main.class) {
868 for (WeakReference<ProjectionChangeListener> wr : listeners) {
869 // already registered ? => abort
870 if (wr.get() == listener) return;
871 }
872 listeners.add(new WeakReference<>(listener));
873 }
874 }
875
876 /**
877 * Removes a projection change listener.
878 *
879 * @param listener the listener. Ignored if <code>null</code>.
880 */
881 public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
882 if (listener == null) return;
883 synchronized (Main.class) {
884 // remove the listener - and any other listener which got garbage
885 // collected in the meantime
886 listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
887 }
888 }
889
890 /**
891 * Listener for window switch events.
892 *
893 * These are events, when the user activates a window of another application
894 * or comes back to JOSM. Window switches from one JOSM window to another
895 * are not reported.
896 */
897 public interface WindowSwitchListener {
898 /**
899 * Called when the user activates a window of another application.
900 */
901 void toOtherApplication();
902
903 /**
904 * Called when the user comes from a window of another application back to JOSM.
905 */
906 void fromOtherApplication();
907 }
908
909 /**
910 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
911 * <p>
912 * It will fire an initial mapFrameInitialized event when the MapFrame is present.
913 * Otherwise will only fire when the MapFrame is created or destroyed.
914 * @param listener The MapFrameListener
915 * @return {@code true} if the listeners collection changed as a result of the call
916 * @see #addMapFrameListener
917 * @deprecated use {@link MainApplication#addAndFireMapFrameListener} instead
918 * @since 11904
919 */
920 @Deprecated
921 public static boolean addAndFireMapFrameListener(MapFrameListener listener) {
922 return MainApplication.addAndFireMapFrameListener(listener);
923 }
924
925 /**
926 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
927 * @param listener The MapFrameListener
928 * @return {@code true} if the listeners collection changed as a result of the call
929 * @see #addAndFireMapFrameListener
930 * @deprecated use {@link MainApplication#addMapFrameListener} instead
931 * @since 5957
932 */
933 @Deprecated
934 public static boolean addMapFrameListener(MapFrameListener listener) {
935 return MainApplication.addMapFrameListener(listener);
936 }
937
938 /**
939 * Unregisters the given {@code MapFrameListener} from MapFrame changes
940 * @param listener The MapFrameListener
941 * @return {@code true} if the listeners collection changed as a result of the call
942 * @deprecated use {@link MainApplication#removeMapFrameListener} instead
943 * @since 5957
944 */
945 @Deprecated
946 public static boolean removeMapFrameListener(MapFrameListener listener) {
947 return MainApplication.removeMapFrameListener(listener);
948 }
949
950 /**
951 * Adds a new network error that occur to give a hint about broken Internet connection.
952 * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
953 *
954 * @param url The accessed URL that caused the error
955 * @param t The network error
956 * @return The previous error associated to the given resource, if any. Can be {@code null}
957 * @since 6642
958 */
959 public static Throwable addNetworkError(URL url, Throwable t) {
960 if (url != null && t != null) {
961 Throwable old = addNetworkError(url.toExternalForm(), t);
962 if (old != null) {
963 Logging.warn("Already here "+old);
964 }
965 return old;
966 }
967 return null;
968 }
969
970 /**
971 * Adds a new network error that occur to give a hint about broken Internet connection.
972 * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
973 *
974 * @param url The accessed URL that caused the error
975 * @param t The network error
976 * @return The previous error associated to the given resource, if any. Can be {@code null}
977 * @since 6642
978 */
979 public static Throwable addNetworkError(String url, Throwable t) {
980 if (url != null && t != null) {
981 return NETWORK_ERRORS.put(url, t);
982 }
983 return null;
984 }
985
986 /**
987 * Returns the network errors that occured until now.
988 * @return the network errors that occured until now, indexed by URL
989 * @since 6639
990 */
991 public static Map<String, Throwable> getNetworkErrors() {
992 return new HashMap<>(NETWORK_ERRORS);
993 }
994
995 /**
996 * Clears the network errors cache.
997 * @since 12011
998 */
999 public static void clearNetworkErrors() {
1000 NETWORK_ERRORS.clear();
1001 }
1002
1003 /**
1004 * Returns the JOSM website URL.
1005 * @return the josm website URL
1006 * @since 6897
1007 */
1008 public static String getJOSMWebsite() {
1009 if (Main.pref != null)
1010 return Main.pref.get("josm.url", JOSM_WEBSITE);
1011 return JOSM_WEBSITE;
1012 }
1013
1014 /**
1015 * Returns the JOSM XML URL.
1016 * @return the josm XML URL
1017 * @since 6897
1018 */
1019 public static String getXMLBase() {
1020 // Always return HTTP (issues reported with HTTPS)
1021 return "http://josm.openstreetmap.de";
1022 }
1023
1024 /**
1025 * Returns the OSM website URL.
1026 * @return the OSM website URL
1027 * @since 6897
1028 */
1029 public static String getOSMWebsite() {
1030 if (Main.pref != null)
1031 return Main.pref.get("osm.url", OSM_WEBSITE);
1032 return OSM_WEBSITE;
1033 }
1034
1035 /**
1036 * Returns the OSM website URL depending on the selected {@link OsmApi}.
1037 * @return the OSM website URL depending on the selected {@link OsmApi}
1038 */
1039 private static String getOSMWebsiteDependingOnSelectedApi() {
1040 final String api = OsmApi.getOsmApi().getServerUrl();
1041 if (OsmApi.DEFAULT_API_URL.equals(api)) {
1042 return getOSMWebsite();
1043 } else {
1044 return api.replaceAll("/api$", "");
1045 }
1046 }
1047
1048 /**
1049 * Replies the base URL for browsing information about a primitive.
1050 * @return the base URL, i.e. https://www.openstreetmap.org
1051 * @since 7678
1052 */
1053 public static String getBaseBrowseUrl() {
1054 if (Main.pref != null)
1055 return Main.pref.get("osm-browse.url", getOSMWebsiteDependingOnSelectedApi());
1056 return getOSMWebsiteDependingOnSelectedApi();
1057 }
1058
1059 /**
1060 * Replies the base URL for browsing information about a user.
1061 * @return the base URL, i.e. https://www.openstreetmap.org/user
1062 * @since 7678
1063 */
1064 public static String getBaseUserUrl() {
1065 if (Main.pref != null)
1066 return Main.pref.get("osm-user.url", getOSMWebsiteDependingOnSelectedApi() + "/user");
1067 return getOSMWebsiteDependingOnSelectedApi() + "/user";
1068 }
1069
1070 /**
1071 * Determines if we are currently running on OSX.
1072 * @return {@code true} if we are currently running on OSX
1073 * @since 6957
1074 */
1075 public static boolean isPlatformOsx() {
1076 return Main.platform instanceof PlatformHookOsx;
1077 }
1078
1079 /**
1080 * Determines if we are currently running on Windows.
1081 * @return {@code true} if we are currently running on Windows
1082 * @since 7335
1083 */
1084 public static boolean isPlatformWindows() {
1085 return Main.platform instanceof PlatformHookWindows;
1086 }
1087
1088 /**
1089 * Determines if the given online resource is currently offline.
1090 * @param r the online resource
1091 * @return {@code true} if {@code r} is offline and should not be accessed
1092 * @since 7434
1093 */
1094 public static boolean isOffline(OnlineResource r) {
1095 return OFFLINE_RESOURCES.contains(r) || OFFLINE_RESOURCES.contains(OnlineResource.ALL);
1096 }
1097
1098 /**
1099 * Sets the given online resource to offline state.
1100 * @param r the online resource
1101 * @return {@code true} if {@code r} was not already offline
1102 * @since 7434
1103 */
1104 public static boolean setOffline(OnlineResource r) {
1105 return OFFLINE_RESOURCES.add(r);
1106 }
1107
1108 /**
1109 * Sets the given online resource to online state.
1110 * @param r the online resource
1111 * @return {@code true} if {@code r} was offline
1112 * @since 8506
1113 */
1114 public static boolean setOnline(OnlineResource r) {
1115 return OFFLINE_RESOURCES.remove(r);
1116 }
1117
1118 /**
1119 * Replies the set of online resources currently offline.
1120 * @return the set of online resources currently offline
1121 * @since 7434
1122 */
1123 public static Set<OnlineResource> getOfflineResources() {
1124 return EnumSet.copyOf(OFFLINE_RESOURCES);
1125 }
1126}
Note: See TracBrowser for help on using the repository browser.