File indexing completed on 2024-04-28 15:39:08

0001 // SPDX-FileCopyrightText: 2020-2022 Tobias Leupold <tl at stonemx dot de>
0002 //
0003 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 // Local includes
0006 #include "Settings.h"
0007 
0008 // KDE includes
0009 #include <KConfigGroup>
0010 
0011 // Qt includes
0012 #include <QDebug>
0013 #include <QJsonArray>
0014 #include <QJsonObject>
0015 #include <QJsonDocument>
0016 
0017 // Main
0018 static const QLatin1String s_main("main");
0019 static const QLatin1String s_windowState("windowState");
0020 static const QLatin1String s_lastOpenPath("lastOpenPath");
0021 static const QLatin1String s_splitImagesList("splitImagesList");
0022 
0023 // Map
0024 static const QLatin1String s_map("map");
0025 static const QLatin1String s_showCrosshairs("showCrosshairs");
0026 static const QLatin1String s_centerLon("centerLon");
0027 static const QLatin1String s_centerLat("centerLat");
0028 static const QLatin1String s_zoom("zoom");
0029 
0030 // Floaters visibility
0031 static const QLatin1String s_floatersVisibility("floatersVisibility");
0032 
0033 // Tracks
0034 static const QLatin1String s_track("tracks");
0035 static const QLatin1String s_color("color");
0036 static const QLatin1String s_width("width");
0037 static const QLatin1String s_style("style");
0038 static const QVector<Qt::PenStyle> s_trackStyleEnum {
0039     Qt::SolidLine,
0040     Qt::DashLine,
0041     Qt::DotLine,
0042     Qt::DashDotLine,
0043     Qt::DashDotDotLine
0044 };
0045 static const QVector<QString> s_trackStyleString {
0046     QStringLiteral("solid"),
0047     QStringLiteral("dashes"),
0048     QStringLiteral("dots"),
0049     QStringLiteral("dash_dot"),
0050     QStringLiteral("dash_dot_dot")
0051 };
0052 
0053 // Images
0054 static const QLatin1String s_images("images");
0055 static const QLatin1String s_thumnailSize("thumbnailSize");
0056 static const QLatin1String s_previewSize("previewSize");
0057 
0058 // Assignment
0059 
0060 static const QLatin1String s_assignment("assignment");
0061 
0062 static const QLatin1String s_exactMatchTolerance("exactMatchTolerance");
0063 static const QLatin1String s_maximumInterpolationInterval("maximumInterpolationInterval");
0064 static const QLatin1String s_maximumInterpolationDistance("maximumInterpolationDistance");
0065 static const QLatin1String s_excludeManuallyTaggedWhenReassigning(
0066                                "excludeManuallyTaggedWhenReassigning");
0067 
0068 static const QLatin1String s_defaultMatchingMode("defaultMatchingMode");
0069 static const QVector<KGeoTag::SearchType> s_defaultMatchingModeEnum {
0070     KGeoTag::CombinedMatchSearch,
0071     KGeoTag::ExactMatchSearch,
0072     KGeoTag::InterpolatedMatchSearch
0073 };
0074 static const QVector<QString> s_defaultMatchingModeString {
0075     QStringLiteral("combinedSearch"),
0076     QStringLiteral("exactSearch"),
0077     QStringLiteral("interpolatedSearch")
0078 };
0079 static const QString &s_defaultMatchingModePreset = s_defaultMatchingModeString.at(0);
0080 
0081 // Elevation lookup
0082 static const QLatin1String s_elevationLookup("elevationLookup");
0083 static const QLatin1String s_lookupElevationAutomatically("lookupElevationAutomatically");
0084 static const QVector<QString> s_elevationDatasets = {
0085     QStringLiteral("aster30m"),
0086     QStringLiteral("etopo1"),
0087     QStringLiteral("eudem25m"),
0088     QStringLiteral("mapzen"),
0089     QStringLiteral("ned10m"),
0090     QStringLiteral("nzdem8m"),
0091     QStringLiteral("srtm30m"),
0092     QStringLiteral("srtm90m"),
0093     QStringLiteral("emod2018"),
0094     QStringLiteral("gebco2020")
0095 };
0096 static const QString &s_defaultElevationDataset = s_elevationDatasets.at(0);
0097 static const QLatin1String s_dataset("dataset");
0098 
0099 // Saving
0100 static const QLatin1String s_saving("saving");
0101 static const QVector<QString> s_writeModes = {
0102     QStringLiteral("WRITETOIMAGEONLY"),
0103     QStringLiteral("WRITETOSIDECARONLY"),
0104     QStringLiteral("WRITETOSIDECARANDIMAGE")
0105 };
0106 static const QString &s_defaultWriteMode = s_writeModes.at(0);
0107 static const QLatin1String s_writeMode("writeMode");
0108 static const QLatin1String s_allowWriteRawFiles("allowWriteRawFiles");
0109 static const QLatin1String s_createBackups("createBackups");
0110 
0111 // Bookmarks
0112 
0113 static const QLatin1String s_bookmarks("bookmarks");
0114 static const QLatin1String s_version("version");
0115 static const QLatin1String s_data("data");
0116 
0117 static constexpr int s_bookmarksDataVersion = 1;
0118 static const QString s_bookmarksDataLabel = QStringLiteral("label");
0119 static const QString s_bookmarksDataLon = QStringLiteral("lon");
0120 static const QString s_bookmarksDataLat = QStringLiteral("lat");
0121 static const QString s_bookmarksDataAlt = QStringLiteral("alt");
0122 
0123 Settings::Settings(QObject *parent) : QObject(parent)
0124 {
0125     m_config = KSharedConfig::openConfig();
0126 }
0127 
0128 // Main
0129 
0130 void Settings::saveMainWindowState(const QByteArray &data)
0131 {
0132     auto group = m_config->group(s_main);
0133     group.writeEntry(s_windowState, data);
0134     group.sync();
0135 }
0136 
0137 QByteArray Settings::mainWindowState() const
0138 {
0139     auto group = m_config->group(s_main);
0140     return group.readEntry(s_windowState, QByteArray());
0141 }
0142 
0143 void Settings::saveLastOpenPath(const QString &path)
0144 {
0145     auto group = m_config->group(s_main);
0146     group.writeEntry(s_lastOpenPath, path);
0147     group.sync();
0148 }
0149 
0150 QString Settings::lastOpenPath() const
0151 {
0152     auto group = m_config->group(s_main);
0153     return group.readEntry(s_lastOpenPath, QString());
0154 }
0155 
0156 void Settings::saveSplitImagesList(bool state)
0157 {
0158     auto group = m_config->group(s_main);
0159     group.writeEntry(s_splitImagesList, state);
0160     group.sync();
0161 }
0162 
0163 bool Settings::splitImagesList() const
0164 {
0165     auto group = m_config->group(s_main);
0166     return group.readEntry(s_splitImagesList, true);
0167 }
0168 
0169 // Map
0170 
0171 void Settings::saveShowCrosshairs(bool state)
0172 {
0173     auto group = m_config->group(s_map);
0174     group.writeEntry(s_showCrosshairs, state);
0175     group.sync();
0176 }
0177 
0178 bool Settings::showCrosshairs() const
0179 {
0180     auto group = m_config->group(s_map);
0181     return group.readEntry(s_showCrosshairs, true);
0182 }
0183 
0184 void Settings::saveMapCenter(const Coordinates &coordinates)
0185 {
0186     auto group = m_config->group(s_map);
0187     group.writeEntry(s_centerLon, coordinates.lon());
0188     group.writeEntry(s_centerLat, coordinates.lat());
0189     group.sync();
0190 }
0191 
0192 Coordinates Settings::mapCenter() const
0193 {
0194     auto group = m_config->group(s_map);
0195     return Coordinates(group.readEntry(s_centerLon, 0.0),
0196                        group.readEntry(s_centerLat, 0.0),
0197                        0.0,
0198                        true);
0199 }
0200 
0201 void Settings::saveZoom(int zoom)
0202 {
0203     auto group = m_config->group(s_map);
0204     group.writeEntry(s_zoom, zoom);
0205     group.sync();
0206 }
0207 
0208 int Settings::zoom() const
0209 {
0210     auto group = m_config->group(s_map);
0211     return group.readEntry(s_zoom, 1520);
0212 }
0213 
0214 // Floaters visibility
0215 
0216 void Settings::saveFloatersVisibility(const QHash<QString, bool> &data)
0217 {
0218     auto group = m_config->group(s_floatersVisibility);
0219     const auto keys = data.keys();
0220     for (const auto &key : keys) {
0221         group.writeEntry(key, data.value(key));
0222     }
0223     group.sync();
0224 }
0225 
0226 QHash<QString, bool> Settings::floatersVisibility()
0227 {
0228     QHash<QString, bool> data;
0229     auto group = m_config->group(s_floatersVisibility);
0230 
0231     const auto keys = group.keyList();
0232     for (const auto &key : keys) {
0233         data.insert(key, group.readEntry(key, true));
0234     }
0235 
0236     return data;
0237 }
0238 
0239 // Tracks
0240 
0241 void Settings::saveTrackColor(const QColor &color)
0242 {
0243     auto group = m_config->group(s_track);
0244     group.writeEntry(s_color, color);
0245     group.sync();
0246 }
0247 
0248 QColor Settings::trackColor() const
0249 {
0250     auto group = m_config->group(s_track);
0251     return group.readEntry(s_color, QColor(255, 0, 255, 150));
0252 }
0253 
0254 void Settings::saveTrackStyle(Qt::PenStyle style)
0255 {
0256     auto group = m_config->group(s_track);
0257     group.writeEntry(s_style, s_trackStyleString.at(s_trackStyleEnum.indexOf(style)));
0258     group.sync();
0259 }
0260 
0261 Qt::PenStyle Settings::trackStyle() const
0262 {
0263     auto group = m_config->group(s_track);
0264     QString styleString = group.readEntry(s_style, QStringLiteral("dots"));
0265     if (s_trackStyleString.contains(styleString)) {
0266         return s_trackStyleEnum.at(s_trackStyleString.indexOf(styleString));
0267     } else {
0268         return Qt::DotLine;
0269     }
0270 }
0271 
0272 void Settings::saveTrackWidth(int width)
0273 {
0274     auto group = m_config->group(s_track);
0275     group.writeEntry(s_width, width);
0276     group.sync();
0277 }
0278 
0279 int Settings::trackWidth() const
0280 {
0281     auto group = m_config->group(s_track);
0282     return group.readEntry(s_width, 3);
0283 }
0284 
0285 // Images
0286 
0287 void Settings::saveThumbnailSize(int size)
0288 {
0289     auto group = m_config->group(s_images);
0290     group.writeEntry(s_thumnailSize, size);
0291     group.sync();
0292 }
0293 
0294 int Settings::thumbnailSize() const
0295 {
0296     auto group = m_config->group(s_images);
0297     return group.readEntry(s_thumnailSize, 32);
0298 }
0299 
0300 void Settings::savePreviewSize(int size)
0301 {
0302     auto group = m_config->group(s_images);
0303     group.writeEntry(s_previewSize, size);
0304     group.sync();
0305 }
0306 
0307 int Settings::previewSize() const
0308 {
0309     auto group = m_config->group(s_images);
0310     return group.readEntry(s_previewSize, 400);
0311 }
0312 
0313 // Assignment
0314 
0315 void Settings::saveExactMatchTolerance(int seconds)
0316 {
0317     auto group = m_config->group(s_assignment);
0318     group.writeEntry(s_exactMatchTolerance, seconds);
0319     group.sync();
0320 }
0321 
0322 int Settings::exactMatchTolerance() const
0323 {
0324     auto group = m_config->group(s_assignment);
0325     return group.readEntry(s_exactMatchTolerance, 10);
0326 }
0327 
0328 void Settings::saveMaximumInterpolationInterval(int seconds)
0329 {
0330     auto group = m_config->group(s_assignment);
0331     group.writeEntry(s_maximumInterpolationInterval, seconds);
0332     group.sync();
0333 }
0334 
0335 int Settings::maximumInterpolationInterval() const
0336 {
0337     auto group = m_config->group(s_assignment);
0338     return group.readEntry(s_maximumInterpolationInterval, -1);
0339 }
0340 
0341 void Settings::saveMaximumInterpolationDistance(int meters)
0342 {
0343     auto group = m_config->group(s_assignment);
0344     group.writeEntry(s_maximumInterpolationDistance, meters);
0345     group.sync();
0346 }
0347 
0348 int Settings::maximumInterpolationDistance() const
0349 {
0350     auto group = m_config->group(s_assignment);
0351     return group.readEntry(s_maximumInterpolationDistance, -1);
0352 }
0353 
0354 void Settings::saveExcludeManuallyTaggedWhenReassigning(bool state)
0355 {
0356     auto group = m_config->group(s_assignment);
0357     group.writeEntry(s_excludeManuallyTaggedWhenReassigning, state);
0358     group.sync();
0359 }
0360 
0361 bool Settings::excludeManuallyTaggedWhenReassigning() const
0362 {
0363     auto group = m_config->group(s_assignment);
0364     return group.readEntry(s_excludeManuallyTaggedWhenReassigning, true);
0365 }
0366 
0367 // Elevation lookup
0368 
0369 void Settings::saveLookupElevationAutomatically(bool state)
0370 {
0371     auto group = m_config->group(s_elevationLookup);
0372     group.writeEntry(s_lookupElevationAutomatically, state);
0373     group.sync();
0374 }
0375 
0376 bool Settings::lookupElevationAutomatically() const
0377 {
0378     auto group = m_config->group(s_elevationLookup);
0379     return group.readEntry(s_lookupElevationAutomatically, false);
0380 }
0381 
0382 void Settings::saveElevationDataset(const QString &id)
0383 {
0384     auto group = m_config->group(s_elevationLookup);
0385     group.writeEntry(s_dataset, id);
0386     group.sync();
0387 }
0388 
0389 QString Settings::elevationDataset() const
0390 {
0391     auto group = m_config->group(s_elevationLookup);
0392     const auto dataset = group.readEntry(s_dataset, s_defaultElevationDataset);
0393     return s_elevationDatasets.contains(dataset) ? dataset : s_defaultElevationDataset;
0394 }
0395 
0396 // Saving
0397 
0398 void Settings::saveWriteMode(const QString &writeMode)
0399 {
0400     auto group = m_config->group(s_saving);
0401     group.writeEntry(s_writeMode, writeMode);
0402     group.sync();
0403 }
0404 
0405 QString Settings::writeMode() const
0406 {
0407     auto group = m_config->group(s_saving);
0408     const auto mode = group.readEntry(s_writeMode, s_defaultWriteMode);
0409     return s_writeModes.contains(mode) ? mode : s_defaultWriteMode;
0410 }
0411 
0412 void Settings::saveCreateBackups(bool state)
0413 {
0414     auto group = m_config->group(s_saving);
0415     group.writeEntry(s_createBackups, state);
0416     group.sync();
0417 }
0418 
0419 bool Settings::createBackups() const
0420 {
0421     auto group = m_config->group(s_saving);
0422     return group.readEntry(s_createBackups, true);
0423 }
0424 
0425 void Settings::saveAllowWriteRawFiles(bool state)
0426 {
0427     auto group = m_config->group(s_saving);
0428     group.writeEntry(s_allowWriteRawFiles, state);
0429     group.sync();
0430 }
0431 
0432 bool Settings::allowWriteRawFiles() const
0433 {
0434     auto group = m_config->group(s_saving);
0435     return group.readEntry(s_allowWriteRawFiles, false);
0436 }
0437 
0438 // Bookmarks
0439 
0440 void Settings::saveBookmarks(const QHash<QString, Coordinates> *bookmarks)
0441 {
0442     QJsonArray data;
0443 
0444     const auto labels = bookmarks->keys();
0445     for (const auto &label : labels) {
0446         const auto coordinates = bookmarks->value(label);
0447         data.append(QJsonObject { { s_bookmarksDataLabel, label },
0448                                   { s_bookmarksDataLon, coordinates.lon() },
0449                                   { s_bookmarksDataLat, coordinates.lat() },
0450                                   { s_bookmarksDataAlt, coordinates.alt() } });
0451     }
0452 
0453     auto group = m_config->group(s_bookmarks);
0454     group.writeEntry(s_version, s_bookmarksDataVersion);
0455     group.writeEntry(s_data, QJsonDocument(data).toJson(QJsonDocument::Compact));
0456     group.sync();
0457 }
0458 
0459 QHash<QString, Coordinates> Settings::bookmarks() const
0460 {
0461     auto group = m_config->group(s_bookmarks);
0462 
0463     const auto version = group.readEntry(s_version, 0);
0464     if (version != s_bookmarksDataVersion) {
0465         return {};
0466     }
0467 
0468     QJsonParseError error;
0469     const auto document = QJsonDocument::fromJson(group.readEntry(s_data, QByteArray()), &error);
0470     if (error.error != QJsonParseError::NoError || ! document.isArray()) {
0471         return {};
0472     }
0473 
0474     QHash<QString, Coordinates> bookmarks;
0475 
0476     const auto data = document.array();
0477     for (const auto &entry : data) {
0478         auto entryData = entry.toObject();
0479         const auto label = entryData.value(s_bookmarksDataLabel);
0480         const auto lon = entryData.value(s_bookmarksDataLon);
0481         const auto lat = entryData.value(s_bookmarksDataLat);
0482         const auto alt = entryData.value(s_bookmarksDataAlt);
0483 
0484         if (! label.isString() || ! lon.isDouble() || ! lat.isDouble() || ! alt.isDouble()) {
0485             return {};
0486         }
0487 
0488         bookmarks.insert(label.toString(),
0489                          Coordinates(lon.toDouble(), lat.toDouble(), alt.toDouble(), true));
0490     }
0491 
0492     return bookmarks;
0493 }
0494 
0495 void Settings::saveDefaultMatchingMode(KGeoTag::SearchType mode)
0496 {
0497     auto group = m_config->group(s_assignment);
0498     group.writeEntry(s_defaultMatchingMode,
0499         s_defaultMatchingModeString.at(s_defaultMatchingModeEnum.indexOf(mode)));
0500     group.sync();
0501 }
0502 
0503 KGeoTag::SearchType Settings::defaultMatchingMode() const
0504 {
0505     auto group = m_config->group(s_assignment);
0506     const auto modeString = group.readEntry(s_defaultMatchingMode, s_defaultMatchingModePreset);
0507     if (s_defaultMatchingModeString.contains(modeString)) {
0508         return s_defaultMatchingModeEnum.at(s_defaultMatchingModeString.indexOf(modeString));
0509     } else {
0510         return KGeoTag::CombinedMatchSearch;
0511     }
0512 }