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

Last change on this file since 12990 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

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