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