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

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

see #15229 - see #15182 - make Main to not depend on JCS

  • Property svn:eol-style set to native
File size: 38.3 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.coor.conversion.CoordinateFormatManager;
36import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
37import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat;
38import org.openstreetmap.josm.data.osm.DataSet;
39import org.openstreetmap.josm.data.osm.OsmPrimitive;
40import org.openstreetmap.josm.data.projection.Projection;
41import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
42import org.openstreetmap.josm.gui.MainApplication;
43import org.openstreetmap.josm.gui.MainMenu;
44import org.openstreetmap.josm.gui.MainPanel;
45import org.openstreetmap.josm.gui.MapFrame;
46import org.openstreetmap.josm.gui.MapFrameListener;
47import org.openstreetmap.josm.gui.layer.MainLayerManager;
48import org.openstreetmap.josm.gui.preferences.ToolbarPreferences;
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.ImageProvider;
54import org.openstreetmap.josm.tools.JosmRuntimeException;
55import org.openstreetmap.josm.tools.Logging;
56import org.openstreetmap.josm.tools.PlatformHook;
57import org.openstreetmap.josm.tools.PlatformHookOsx;
58import org.openstreetmap.josm.tools.PlatformHookUnixoid;
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(Main.pref.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 String os = System.getProperty("os.name");
775 if (os == null) {
776 Logging.warn("Your operating system has no name, so I'm guessing its some kind of *nix.");
777 platform = new PlatformHookUnixoid();
778 } else if (os.toLowerCase(Locale.ENGLISH).startsWith("windows")) {
779 platform = new PlatformHookWindows();
780 } else if ("Linux".equals(os) || "Solaris".equals(os) ||
781 "SunOS".equals(os) || "AIX".equals(os) ||
782 "FreeBSD".equals(os) || "NetBSD".equals(os) || "OpenBSD".equals(os)) {
783 platform = new PlatformHookUnixoid();
784 } else if (os.toLowerCase(Locale.ENGLISH).startsWith("mac os x")) {
785 platform = new PlatformHookOsx();
786 } else {
787 Logging.warn("I don't know your operating system '"+os+"', so I'm guessing its some kind of *nix.");
788 platform = new PlatformHookUnixoid();
789 }
790 }
791
792 /* ----------------------------------------------------------------------------------------- */
793 /* projection handling - Main is a registry for a single, global projection instance */
794 /* */
795 /* TODO: For historical reasons the registry is implemented by Main. An alternative approach */
796 /* would be a singleton org.openstreetmap.josm.data.projection.ProjectionRegistry class. */
797 /* ----------------------------------------------------------------------------------------- */
798 /**
799 * The projection method used.
800 * Use {@link #getProjection()} and {@link #setProjection(Projection)} for access.
801 * Use {@link #setProjection(Projection)} in order to trigger a projection change event.
802 */
803 private static volatile Projection proj;
804
805 /**
806 * Replies the current projection.
807 *
808 * @return the currently active projection
809 */
810 public static Projection getProjection() {
811 return proj;
812 }
813
814 /**
815 * Sets the current projection
816 *
817 * @param p the projection
818 */
819 public static void setProjection(Projection p) {
820 CheckParameterUtil.ensureParameterNotNull(p);
821 Projection oldValue = proj;
822 Bounds b = main != null ? main.getRealBounds() : null;
823 proj = p;
824 fireProjectionChanged(oldValue, proj, b);
825 }
826
827 /**
828 * Returns the bounds for the current projection. Used for projection events.
829 * @return the bounds for the current projection
830 * @see #restoreOldBounds
831 */
832 protected Bounds getRealBounds() {
833 // To be overriden
834 return null;
835 }
836
837 /**
838 * Restore clean state corresponding to old bounds after a projection change event.
839 * @param oldBounds bounds previously returned by {@link #getRealBounds}, before the change of projection
840 * @see #getRealBounds
841 */
842 protected void restoreOldBounds(Bounds oldBounds) {
843 // To be overriden
844 }
845
846 /*
847 * Keep WeakReferences to the listeners. This relieves clients from the burden of
848 * explicitly removing the listeners and allows us to transparently register every
849 * created dataset as projection change listener.
850 */
851 private static final List<WeakReference<ProjectionChangeListener>> listeners = new ArrayList<>();
852
853 private static void fireProjectionChanged(Projection oldValue, Projection newValue, Bounds oldBounds) {
854 if ((newValue == null ^ oldValue == null)
855 || (newValue != null && oldValue != null && !Objects.equals(newValue.toCode(), oldValue.toCode()))) {
856 synchronized (Main.class) {
857 Iterator<WeakReference<ProjectionChangeListener>> it = listeners.iterator();
858 while (it.hasNext()) {
859 WeakReference<ProjectionChangeListener> wr = it.next();
860 ProjectionChangeListener listener = wr.get();
861 if (listener == null) {
862 it.remove();
863 continue;
864 }
865 listener.projectionChanged(oldValue, newValue);
866 }
867 }
868 if (newValue != null && oldBounds != null && main != null) {
869 main.restoreOldBounds(oldBounds);
870 }
871 /* TODO - remove layers with fixed projection */
872 }
873 }
874
875 /**
876 * Register a projection change listener.
877 * The listener is registered to be weak, so keep a reference of it if you want it to be preserved.
878 *
879 * @param listener the listener. Ignored if <code>null</code>.
880 */
881 public static void addProjectionChangeListener(ProjectionChangeListener listener) {
882 if (listener == null) return;
883 synchronized (Main.class) {
884 for (WeakReference<ProjectionChangeListener> wr : listeners) {
885 // already registered ? => abort
886 if (wr.get() == listener) return;
887 }
888 listeners.add(new WeakReference<>(listener));
889 }
890 }
891
892 /**
893 * Removes a projection change listener.
894 *
895 * @param listener the listener. Ignored if <code>null</code>.
896 */
897 public static void removeProjectionChangeListener(ProjectionChangeListener listener) {
898 if (listener == null) return;
899 synchronized (Main.class) {
900 // remove the listener - and any other listener which got garbage
901 // collected in the meantime
902 listeners.removeIf(wr -> wr.get() == null || wr.get() == listener);
903 }
904 }
905
906 /**
907 * Listener for window switch events.
908 *
909 * These are events, when the user activates a window of another application
910 * or comes back to JOSM. Window switches from one JOSM window to another
911 * are not reported.
912 */
913 public interface WindowSwitchListener {
914 /**
915 * Called when the user activates a window of another application.
916 */
917 void toOtherApplication();
918
919 /**
920 * Called when the user comes from a window of another application back to JOSM.
921 */
922 void fromOtherApplication();
923 }
924
925 /**
926 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
927 * <p>
928 * It will fire an initial mapFrameInitialized event when the MapFrame is present.
929 * Otherwise will only fire when the MapFrame is created or destroyed.
930 * @param listener The MapFrameListener
931 * @return {@code true} if the listeners collection changed as a result of the call
932 * @see #addMapFrameListener
933 * @deprecated use {@link MainApplication#addAndFireMapFrameListener} instead
934 * @since 11904
935 */
936 @Deprecated
937 public static boolean addAndFireMapFrameListener(MapFrameListener listener) {
938 return MainApplication.addAndFireMapFrameListener(listener);
939 }
940
941 /**
942 * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
943 * @param listener The MapFrameListener
944 * @return {@code true} if the listeners collection changed as a result of the call
945 * @see #addAndFireMapFrameListener
946 * @deprecated use {@link MainApplication#addMapFrameListener} instead
947 * @since 5957
948 */
949 @Deprecated
950 public static boolean addMapFrameListener(MapFrameListener listener) {
951 return MainApplication.addMapFrameListener(listener);
952 }
953
954 /**
955 * Unregisters the given {@code MapFrameListener} from MapFrame changes
956 * @param listener The MapFrameListener
957 * @return {@code true} if the listeners collection changed as a result of the call
958 * @deprecated use {@link MainApplication#removeMapFrameListener} instead
959 * @since 5957
960 */
961 @Deprecated
962 public static boolean removeMapFrameListener(MapFrameListener listener) {
963 return MainApplication.removeMapFrameListener(listener);
964 }
965
966 /**
967 * Adds a new network error that occur to give a hint about broken Internet connection.
968 * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
969 *
970 * @param url The accessed URL that caused the error
971 * @param t The network error
972 * @return The previous error associated to the given resource, if any. Can be {@code null}
973 * @since 6642
974 */
975 public static Throwable addNetworkError(URL url, Throwable t) {
976 if (url != null && t != null) {
977 Throwable old = addNetworkError(url.toExternalForm(), t);
978 if (old != null) {
979 Logging.warn("Already here "+old);
980 }
981 return old;
982 }
983 return null;
984 }
985
986 /**
987 * Adds a new network error that occur to give a hint about broken Internet connection.
988 * Do not use this method for errors known for sure thrown because of a bad proxy configuration.
989 *
990 * @param url The accessed URL that caused the error
991 * @param t The network error
992 * @return The previous error associated to the given resource, if any. Can be {@code null}
993 * @since 6642
994 */
995 public static Throwable addNetworkError(String url, Throwable t) {
996 if (url != null && t != null) {
997 return NETWORK_ERRORS.put(url, t);
998 }
999 return null;
1000 }
1001
1002 /**
1003 * Returns the network errors that occured until now.
1004 * @return the network errors that occured until now, indexed by URL
1005 * @since 6639
1006 */
1007 public static Map<String, Throwable> getNetworkErrors() {
1008 return new HashMap<>(NETWORK_ERRORS);
1009 }
1010
1011 /**
1012 * Clears the network errors cache.
1013 * @since 12011
1014 */
1015 public static void clearNetworkErrors() {
1016 NETWORK_ERRORS.clear();
1017 }
1018
1019 /**
1020 * Returns the JOSM website URL.
1021 * @return the josm website URL
1022 * @since 6897
1023 */
1024 public static String getJOSMWebsite() {
1025 if (Main.pref != null)
1026 return Main.pref.get("josm.url", JOSM_WEBSITE);
1027 return JOSM_WEBSITE;
1028 }
1029
1030 /**
1031 * Returns the JOSM XML URL.
1032 * @return the josm XML URL
1033 * @since 6897
1034 */
1035 public static String getXMLBase() {
1036 // Always return HTTP (issues reported with HTTPS)
1037 return "http://josm.openstreetmap.de";
1038 }
1039
1040 /**
1041 * Returns the OSM website URL.
1042 * @return the OSM website URL
1043 * @since 6897
1044 */
1045 public static String getOSMWebsite() {
1046 if (Main.pref != null)
1047 return Main.pref.get("osm.url", OSM_WEBSITE);
1048 return OSM_WEBSITE;
1049 }
1050
1051 /**
1052 * Returns the OSM website URL depending on the selected {@link OsmApi}.
1053 * @return the OSM website URL depending on the selected {@link OsmApi}
1054 */
1055 private static String getOSMWebsiteDependingOnSelectedApi() {
1056 final String api = OsmApi.getOsmApi().getServerUrl();
1057 if (OsmApi.DEFAULT_API_URL.equals(api)) {
1058 return getOSMWebsite();
1059 } else {
1060 return api.replaceAll("/api$", "");
1061 }
1062 }
1063
1064 /**
1065 * Replies the base URL for browsing information about a primitive.
1066 * @return the base URL, i.e. https://www.openstreetmap.org
1067 * @since 7678
1068 */
1069 public static String getBaseBrowseUrl() {
1070 if (Main.pref != null)
1071 return Main.pref.get("osm-browse.url", getOSMWebsiteDependingOnSelectedApi());
1072 return getOSMWebsiteDependingOnSelectedApi();
1073 }
1074
1075 /**
1076 * Replies the base URL for browsing information about a user.
1077 * @return the base URL, i.e. https://www.openstreetmap.org/user
1078 * @since 7678
1079 */
1080 public static String getBaseUserUrl() {
1081 if (Main.pref != null)
1082 return Main.pref.get("osm-user.url", getOSMWebsiteDependingOnSelectedApi() + "/user");
1083 return getOSMWebsiteDependingOnSelectedApi() + "/user";
1084 }
1085
1086 /**
1087 * Determines if we are currently running on OSX.
1088 * @return {@code true} if we are currently running on OSX
1089 * @since 6957
1090 */
1091 public static boolean isPlatformOsx() {
1092 return Main.platform instanceof PlatformHookOsx;
1093 }
1094
1095 /**
1096 * Determines if we are currently running on Windows.
1097 * @return {@code true} if we are currently running on Windows
1098 * @since 7335
1099 */
1100 public static boolean isPlatformWindows() {
1101 return Main.platform instanceof PlatformHookWindows;
1102 }
1103
1104 /**
1105 * Determines if the given online resource is currently offline.
1106 * @param r the online resource
1107 * @return {@code true} if {@code r} is offline and should not be accessed
1108 * @since 7434
1109 */
1110 public static boolean isOffline(OnlineResource r) {
1111 return OFFLINE_RESOURCES.contains(r) || OFFLINE_RESOURCES.contains(OnlineResource.ALL);
1112 }
1113
1114 /**
1115 * Sets the given online resource to offline state.
1116 * @param r the online resource
1117 * @return {@code true} if {@code r} was not already offline
1118 * @since 7434
1119 */
1120 public static boolean setOffline(OnlineResource r) {
1121 return OFFLINE_RESOURCES.add(r);
1122 }
1123
1124 /**
1125 * Sets the given online resource to online state.
1126 * @param r the online resource
1127 * @return {@code true} if {@code r} was offline
1128 * @since 8506
1129 */
1130 public static boolean setOnline(OnlineResource r) {
1131 return OFFLINE_RESOURCES.remove(r);
1132 }
1133
1134 /**
1135 * Replies the set of online resources currently offline.
1136 * @return the set of online resources currently offline
1137 * @since 7434
1138 */
1139 public static Set<OnlineResource> getOfflineResources() {
1140 return EnumSet.copyOf(OFFLINE_RESOURCES);
1141 }
1142}
Note: See TracBrowser for help on using the repository browser.