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

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

see #15182 - deprecate Main.main.menu. Replacement: gui.MainApplication.getMenu()

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