001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.BorderLayout;
007import java.awt.event.KeyEvent;
008import java.util.Arrays;
009import java.util.Collection;
010import java.util.Collections;
011import java.util.List;
012import java.util.Objects;
013import java.util.concurrent.Callable;
014
015import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
016import org.openstreetmap.gui.jmapviewer.FeatureAdapter.SettingsAdapter;
017import org.openstreetmap.josm.data.UndoRedoHandler;
018import org.openstreetmap.josm.data.coor.conversion.CoordinateFormatManager;
019import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
020import org.openstreetmap.josm.data.coor.conversion.ICoordinateFormat;
021import org.openstreetmap.josm.data.validation.OsmValidator;
022import org.openstreetmap.josm.gui.layer.ImageryLayer;
023import org.openstreetmap.josm.gui.layer.TMSLayer;
024import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference;
025import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference;
026import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
027import org.openstreetmap.josm.gui.util.GuiHelper;
028import org.openstreetmap.josm.io.FileWatcher;
029import org.openstreetmap.josm.io.OsmApi;
030import org.openstreetmap.josm.io.OsmApiInitializationException;
031import org.openstreetmap.josm.io.OsmTransferCanceledException;
032import org.openstreetmap.josm.io.imagery.ApiKeyProvider;
033import org.openstreetmap.josm.spi.lifecycle.InitializationSequence;
034import org.openstreetmap.josm.spi.lifecycle.InitializationTask;
035import org.openstreetmap.josm.spi.preferences.Config;
036import org.openstreetmap.josm.tools.I18n;
037import org.openstreetmap.josm.tools.ImageProvider;
038import org.openstreetmap.josm.tools.Logging;
039import org.openstreetmap.josm.tools.OpenBrowser;
040import org.openstreetmap.josm.tools.PlatformManager;
041import org.openstreetmap.josm.tools.Shortcut;
042import org.openstreetmap.josm.tools.Tag2Link;
043import org.openstreetmap.josm.tools.Territories;
044import org.openstreetmap.josm.tools.Utils;
045
046/**
047 * JOSM initialization sequence.
048 * @since 14139
049 */
050public class MainInitialization implements InitializationSequence {
051
052    private final MainApplication application;
053
054    /**
055     * Constructs a new {@code MainInitialization}
056     * @param application Main application. Must not be null
057     */
058    public MainInitialization(MainApplication application) {
059        this.application = Objects.requireNonNull(application);
060    }
061
062    @Override
063    public List<InitializationTask> beforeInitializationTasks() {
064        return Arrays.asList(
065            new InitializationTask(tr("Initializing coordinate format"), () -> {
066                ICoordinateFormat fmt = CoordinateFormatManager.getCoordinateFormat(Config.getPref().get("coordinates"));
067                if (fmt == null) {
068                    fmt = DecimalDegreesCoordinateFormat.INSTANCE;
069                }
070                CoordinateFormatManager.setCoordinateFormat(fmt);
071            }),
072            new InitializationTask(tr("Starting file watcher"), FileWatcher.getDefaultInstance()::start),
073            new InitializationTask(tr("Executing platform startup hook"),
074                    () -> PlatformManager.getPlatform().startupHook(MainApplication::askUpdateJava, MainApplication::askMigrateWebStart)),
075            new InitializationTask(tr("Building main menu"), application::initializeMainWindow),
076            new InitializationTask(tr("Updating user interface"), () -> {
077                UndoRedoHandler.getInstance().addCommandQueueListener(application.redoUndoListener);
078                // creating toolbar
079                GuiHelper.runInEDTAndWait(() -> MainApplication.contentPanePrivate.add(MainApplication.toolbar.control, BorderLayout.NORTH));
080                // help shortcut
081                MainApplication.registerActionShortcut(MainApplication.menu.help,
082                        Shortcut.registerShortcut("system:help", tr("Help: {0}", tr("Help")), KeyEvent.VK_F1, Shortcut.DIRECT));
083            }),
084            new InitializationTask(tr("Initializing internal boundaries data"), () -> {
085                Territories.initialize();
086                if (Config.getPref().getBoolean("override.numbering.format", true)) {
087                    I18n.initializeNumberingFormat();
088                }
089            })
090        );
091    }
092
093    @Override
094    public Collection<InitializationTask> parallelInitializationTasks() {
095        return Arrays.asList(
096            new InitializationTask(tr("Initializing OSM API"), () -> {
097                    OsmApi.addOsmApiInitializationListener(api -> {
098                        // This checks if there are any layers currently displayed that are now on the blacklist, and removes them.
099                        // This is a rare situation - probably only occurs if the user changes the API URL in the preferences menu.
100                        // Otherwise they would not have been able to load the layers in the first place because they would have been disabled
101                        if (MainApplication.isDisplayingMapView()) {
102                            for (ImageryLayer l : MainApplication.getLayerManager().getLayersOfType(ImageryLayer.class)) {
103                                if (l.getInfo().isBlacklisted()) {
104                                    Logging.info(tr("Removed layer {0} because it is not allowed by the configured API.", l.getName()));
105                                    MainApplication.getLayerManager().removeLayer(l);
106                                }
107                            }
108                        }
109                    });
110                    // We try to establish an API connection early, so that any API
111                    // capabilities are already known to the editor instance. However
112                    // if it goes wrong that's not critical at this stage.
113                    try {
114                        OsmApi.getOsmApi().initialize(null, true);
115                    } catch (OsmTransferCanceledException | OsmApiInitializationException | SecurityException e) {
116                        Logging.warn(Logging.getErrorMessage(Utils.getRootCause(e)));
117                    }
118                }),
119            new InitializationTask(tr("Initializing validator"), OsmValidator::initialize),
120            new InitializationTask(tr("Initializing presets"), TaggingPresets::initialize),
121            new InitializationTask(tr("Initializing map styles"), MapPaintPreference::initialize),
122            new InitializationTask(tr("Initializing Tag2Link rules"), Tag2Link::initialize),
123            new InitializationTask(tr("Loading imagery preferences"), ImageryPreference::initialize)
124        );
125    }
126
127    @Override
128    public List<Callable<?>> asynchronousCallableTasks() {
129        return Collections.emptyList();
130    }
131
132    @Override
133    public List<Runnable> asynchronousRunnableTasks() {
134        return Arrays.asList(
135                TMSLayer::getCache,
136                OsmValidator::initializeTests
137            );
138    }
139
140    @Override
141    public List<InitializationTask> afterInitializationTasks() {
142        return Arrays.asList(
143            new InitializationTask(tr("Updating user interface"), () -> GuiHelper.runInEDTAndWait(() -> {
144                // hooks for the jmapviewer component
145                FeatureAdapter.registerApiKeyAdapter(ApiKeyProvider::retrieveApiKey);
146                FeatureAdapter.registerBrowserAdapter(OpenBrowser::displayUrl);
147                FeatureAdapter.registerImageAdapter(ImageProvider::read);
148                FeatureAdapter.registerTranslationAdapter(I18n::tr);
149                FeatureAdapter.registerLoggingAdapter(name -> Logging.getLogger());
150                FeatureAdapter.registerSettingsAdapter(new JosmSettingsAdapter());
151                // UI update
152                MainApplication.toolbar.refreshToolbarControl();
153                MainApplication.toolbar.control.updateUI();
154                MainApplication.contentPanePrivate.updateUI();
155            }))
156        );
157    }
158
159    private static class JosmSettingsAdapter implements SettingsAdapter {
160
161        @Override
162        public String get(String key, String def) {
163            return Config.getPref().get(key, def);
164        }
165
166        @Override
167        public boolean put(String key, String value) {
168            return Config.getPref().put(key, value);
169        }
170    }
171}