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

0001 // SPDX-FileCopyrightText: 2020-2024 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 "SettingsDialog.h"
0007 #include "Settings.h"
0008 
0009 // KDE includes
0010 #include <KLocalizedString>
0011 
0012 // Qt includes
0013 #include <QVBoxLayout>
0014 #include <QDialogButtonBox>
0015 #include <QDebug>
0016 #include <QGroupBox>
0017 #include <QGridLayout>
0018 #include <QLabel>
0019 #include <QComboBox>
0020 #include <QSpinBox>
0021 #include <QPushButton>
0022 #include <QColorDialog>
0023 #include <QCheckBox>
0024 #include <QScrollArea>
0025 #include <QScrollBar>
0026 #include <QMessageBox>
0027 #include <QHBoxLayout>
0028 
0029 SettingsDialog::SettingsDialog(Settings *settings, QWidget *parent)
0030     : QDialog(parent),
0031       m_settings(settings)
0032 {
0033     setAttribute(Qt::WA_DeleteOnClose, true);
0034     setWindowTitle(i18n("KGeoTag: Settings"));
0035 
0036     auto *mainLayout = new QVBoxLayout(this);
0037 
0038     // Header
0039 
0040     auto *header = new QLabel(i18n("KGeoTag settings"));
0041     header->setStyleSheet(QStringLiteral("QLabel { font-weight: bold; font-size: %1pt; }").arg(
0042         (int) double(header->font().pointSize()) * 1.2));
0043     header->setAlignment(Qt::AlignCenter);
0044     mainLayout->addWidget(header);
0045 
0046     // Settings
0047 
0048     setStyleSheet(QStringLiteral("QGroupBox { font-weight: bold; }"));
0049 
0050     auto *settingsWidget = new QWidget;
0051 
0052     int row;
0053 
0054     auto *layout = new QVBoxLayout(settingsWidget);
0055 
0056     // Image lists
0057 
0058     auto *listsBox = new QGroupBox(i18n("Image lists"));
0059     layout->addWidget(listsBox);
0060 
0061     auto *listsBoxLayout = new QVBoxLayout(listsBox);
0062 
0063     auto *listsModeLabel = new QLabel(i18n(
0064         "<p>Loaded images can either be listed in two different lists (one for all images without "
0065         "and one for images with coordinates), or using a combined consecutive list for all images."
0066         "</p>"
0067         "<p>Use the following images list(s) mode:</p>"));
0068     listsModeLabel->setWordWrap(true);
0069     listsBoxLayout->addWidget(listsModeLabel);
0070 
0071     m_imageListsMode = new QComboBox;
0072     m_imageListsMode->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
0073 
0074     m_imageListsMode->addItem(i18n("Separate \"Assigned\" and \"Unassigned\" list"));
0075     m_imageListsMode->addItem(i18n("One combined list for all images"));
0076 
0077     m_imageListsMode->setCurrentIndex(m_settings->splitImagesList() ? 0 : 1);
0078 
0079     listsBoxLayout->addWidget(m_imageListsMode);
0080 
0081     // Automatic matching
0082 
0083     auto *searchMatchesBox = new QGroupBox(i18n("Image Assignment"));
0084     layout->addWidget(searchMatchesBox);
0085 
0086     auto *searchMatchesBoxLayout = new QVBoxLayout(searchMatchesBox);
0087 
0088     auto *matchModeLabel = new QLabel(i18n("Search type for the main menu \"Assign images to GPS "
0089                                            "data\" entry"));
0090     matchModeLabel->setWordWrap(true);
0091     searchMatchesBoxLayout->addWidget(matchModeLabel);
0092 
0093     m_automaticMatchingMode = new QComboBox;
0094     m_automaticMatchingMode->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
0095 
0096     m_automaticMatchingMode->addItem(i18n("Combined match search"), KGeoTag::CombinedMatchSearch);
0097     m_automaticMatchingMode->addItem(i18n("Search exact matches only"), KGeoTag::ExactMatchSearch);
0098     m_automaticMatchingMode->addItem(i18n("Search interpolated matches only"),
0099                                      KGeoTag::InterpolatedMatchSearch);
0100 
0101     m_automaticMatchingMode->setCurrentIndex(
0102         m_automaticMatchingMode->findData(m_settings->defaultMatchingMode()));
0103 
0104     searchMatchesBoxLayout->addWidget(m_automaticMatchingMode);
0105 
0106     auto *matchModeNoteLabel = new QLabel(i18n(
0107         "This triggers an automatic (re-)assignment of all images, respecting the \"Exclude "
0108         "manually tagged images\" setting from the \"Automatic assignment\" dock."));
0109     matchModeNoteLabel->setWordWrap(true);
0110     searchMatchesBoxLayout->addWidget(matchModeNoteLabel);
0111 
0112     // Images
0113 
0114     auto *imagesBox = new QGroupBox(i18n("Thumbnails and previews"));
0115     auto *imagesBoxLayout = new QVBoxLayout(imagesBox);
0116     layout->addWidget(imagesBox);
0117 
0118     auto *sizesLayoutWrapper = new QHBoxLayout;
0119     imagesBoxLayout->addLayout(sizesLayoutWrapper);
0120     auto *sizesLayout = new QGridLayout;
0121     sizesLayoutWrapper->addLayout(sizesLayout);
0122     row = -1;
0123 
0124     sizesLayout->addWidget(new QLabel(i18n("Thumbnail size:")), ++row, 0);
0125     m_thumbnailSize = new QSpinBox;
0126     m_thumbnailSize->setMinimum(16);
0127     m_thumbnailSize->setMaximum(512);
0128     m_originalThumbnailSizeValue = m_settings->thumbnailSize();
0129     m_thumbnailSize->setValue(m_originalThumbnailSizeValue);
0130     sizesLayout->addWidget(m_thumbnailSize, row, 1);
0131     sizesLayout->addWidget(new QLabel(i18n("px")), row, 2);
0132 
0133     sizesLayout->addWidget(new QLabel(i18n("Preview size:")), ++row, 0);
0134     m_previewSize = new QSpinBox;
0135     m_previewSize->setMinimum(100);
0136     m_previewSize->setMaximum(1920);
0137     m_originalPreviewSizeValue = m_settings->previewSize();
0138     m_previewSize->setValue(m_originalPreviewSizeValue);
0139     sizesLayout->addWidget(m_previewSize, row, 1);
0140     sizesLayout->addWidget(new QLabel(i18n("px")), row, 2);
0141 
0142     sizesLayoutWrapper->addStretch();
0143 
0144     auto *imagesChangesLabel = new QLabel(i18n("Please restart the program after changes to these "
0145                                                "values so that they are applied and become "
0146                                                "visible."));
0147     imagesChangesLabel->setWordWrap(true);
0148     imagesBoxLayout->addWidget(imagesChangesLabel);
0149 
0150     // GPX track rendering
0151 
0152     auto *trackBox = new QGroupBox(i18n("GPX track rendering"));
0153     auto *trackBoxLayout = new QHBoxLayout(trackBox);
0154     layout->addWidget(trackBox);
0155 
0156     auto *renderingLayout = new QGridLayout;
0157     trackBoxLayout->addLayout(renderingLayout);
0158     row = -1;
0159 
0160     renderingLayout->addWidget(new QLabel(i18n("Color:")), ++row, 0);
0161     m_trackColor = new QPushButton;
0162     m_trackColor->setFlat(true);
0163     m_currentTrackColor = m_settings->trackColor();
0164     connect(m_trackColor, &QPushButton::clicked, this, &SettingsDialog::setTrackColor);
0165     renderingLayout->addWidget(m_trackColor, row, 1);
0166 
0167     m_trackOpacity = new QLabel;
0168     renderingLayout->addWidget(m_trackOpacity, row, 2);
0169 
0170     updateTrackColor();
0171 
0172     renderingLayout->addWidget(new QLabel(i18n("Line width:")), ++row, 0);
0173     m_trackWidth = new QSpinBox;
0174     m_trackWidth->setMinimum(1);
0175     m_trackWidth->setMaximum(50);
0176     m_trackWidth->setValue(m_settings->trackWidth());
0177     renderingLayout->addWidget(m_trackWidth, row, 1);
0178 
0179     renderingLayout->addWidget(new QLabel(i18n("Line style:")), ++row, 0);
0180     m_trackStyle = new QComboBox;
0181     m_trackStyle->addItem(i18n("Solid"), static_cast<int>(Qt::SolidLine));
0182     m_trackStyle->addItem(i18n("Dashes"), static_cast<int>(Qt::DashLine));
0183     m_trackStyle->addItem(i18n("Dots"), static_cast<int>(Qt::DotLine));
0184     m_trackStyle->addItem(i18n("Dash-Dot"), static_cast<int>(Qt::DashDotLine));
0185     m_trackStyle->addItem(i18n("Dash-Dot-Dot"), static_cast<int>(Qt::DashDotDotLine));
0186     m_trackStyle->setCurrentIndex(
0187         m_trackStyle->findData(static_cast<int>(m_settings->trackStyle())));
0188     renderingLayout->addWidget(m_trackStyle, row, 1);
0189 
0190     trackBoxLayout->addStretch();
0191 
0192     // Elevation lookup
0193 
0194     auto *elevationBox = new QGroupBox(i18n("Elevation lookup"));
0195     auto *elevationBoxLayout = new QVBoxLayout(elevationBox);
0196     layout->addWidget(elevationBox);
0197 
0198     auto *lookupLabel = new QLabel(i18n("Elevations can be looked up using opentopodata.org's web "
0199                                         "API."));
0200     lookupLabel->setWordWrap(true);
0201     elevationBoxLayout->addWidget(lookupLabel);
0202 
0203     auto *datasetLayout = new QHBoxLayout;
0204     elevationBoxLayout->addLayout(datasetLayout);
0205 
0206     datasetLayout->addWidget(new QLabel(i18n("Elevation dataset:")));
0207 
0208     m_elevationDataset = new QComboBox;
0209 
0210     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "ASTER"),
0211                                 QStringLiteral("aster30m"));
0212     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "ETOPO1"),
0213                                 QStringLiteral("etopo1"));
0214     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "EU-DEM"),
0215                                 QStringLiteral("eudem25m"));
0216     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "Mapzen"),
0217                                 QStringLiteral("mapzen"));
0218     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "NED"),
0219                                 QStringLiteral("ned10m"));
0220     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "NZ DEM"),
0221                                 QStringLiteral("nzdem8m"));
0222     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "SRTM (30 m)"),
0223                                 QStringLiteral("srtm30m"));
0224     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset", "SRTM (90 m)"),
0225                                 QStringLiteral("srtm90m"));
0226     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset",
0227                                       "EMODnet 2018 Bathymetry"),
0228                                 QStringLiteral("emod2018"));
0229     m_elevationDataset->addItem(i18nc("opentopodata.org elevation dataset",
0230                                       "GEBCO 2020 Bathymetry"),
0231                                 QStringLiteral("gebco2020"));
0232 
0233     m_elevationDataset->setCurrentIndex(
0234         m_elevationDataset->findData(m_settings->elevationDataset()));
0235 
0236     datasetLayout->addWidget(m_elevationDataset);
0237 
0238     datasetLayout->addStretch();
0239 
0240     auto *datasetInfoLabel = new QLabel(i18n("Cf. <a href=\"https://www.opentopodata.org/\">"
0241                                              "https://www.opentopodata.org/</a> for further "
0242                                              "information about the available datasets, like the "
0243                                              "respective coverage!"));
0244     datasetInfoLabel->setWordWrap(true);
0245     datasetInfoLabel->setOpenExternalLinks(true);
0246     elevationBoxLayout->addWidget(datasetInfoLabel);
0247 
0248     m_lookupElevationAutomatically = new QCheckBox(i18n("Request and set altitudes automatically"));
0249     m_lookupElevationAutomatically->setChecked(m_settings->lookupElevationAutomatically());
0250     elevationBoxLayout->addWidget(m_lookupElevationAutomatically);
0251 
0252     // Data saving
0253 
0254     auto *saveBox = new QGroupBox(i18n("Saving"));
0255     auto *saveBoxLayout = new QVBoxLayout(saveBox);
0256     layout->addWidget(saveBox);
0257 
0258     auto *saveTargetLayout = new QHBoxLayout;
0259     saveBoxLayout->addLayout(saveTargetLayout);
0260 
0261     saveTargetLayout->addWidget(new QLabel(i18n("Write changes to:")));
0262 
0263     m_writeMode = new QComboBox;
0264 
0265     m_writeMode->addItem(i18n("Exif header"),
0266                          QStringLiteral("WRITETOIMAGEONLY"));
0267     m_writeMode->addItem(i18n("XMP sidecar file"),
0268                          QStringLiteral("WRITETOSIDECARONLY"));
0269     m_writeMode->addItem(i18n("Exif header and XMP sidecar file"),
0270                          QStringLiteral("WRITETOSIDECARANDIMAGE"));
0271 
0272     m_writeMode->setCurrentIndex(m_writeMode->findData(m_settings->writeMode()));
0273 
0274     saveTargetLayout->addWidget(m_writeMode);
0275 
0276     saveTargetLayout->addStretch();
0277 
0278     m_allowWriteRawFiles = new QCheckBox(i18n("Allow altering Exif headers of RAW images"));
0279     m_allowWriteRawFiles->setChecked(m_settings->allowWriteRawFiles());
0280     saveBoxLayout->addWidget(m_allowWriteRawFiles);
0281 
0282     auto *label = new QLabel(i18n(
0283         "<b>Caution!</b> This is experimental. By default, regardless of the setting for \"Write "
0284         "changes to\", all changes to RAW images are written to XMP sidecar files. Have backups "
0285         "and check your Exif data to be still complete and correct when using this!"
0286     ));
0287     label->setWordWrap(true);
0288     label->setIndent(30);
0289     saveBoxLayout->addWidget(label);
0290 
0291     m_createBackups = new QCheckBox(i18n("Create a backup before altering a file"));
0292     m_createBackups->setChecked(m_settings->createBackups());
0293     saveBoxLayout->addWidget(m_createBackups);
0294 
0295     // Scroll area
0296 
0297     auto *scrollArea = new QScrollArea;
0298     scrollArea->setWidgetResizable(true);
0299     const int styleAddition = scrollArea->width() - scrollArea->viewport()->width();
0300     scrollArea->setWidget(settingsWidget);
0301     mainLayout->addWidget(scrollArea);
0302 
0303     show();
0304     const int widgetWidth = settingsWidget->width() + scrollArea->verticalScrollBar()->width()
0305                             + styleAddition;
0306     scrollArea->setMinimumWidth(widgetWidth);
0307 
0308     // Button box
0309 
0310     auto *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Close);
0311     connect(buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::accept);
0312     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0313     mainLayout->addWidget(buttonBox);
0314 }
0315 
0316 void SettingsDialog::updateTrackColor()
0317 {
0318     m_trackColor->setStyleSheet(QStringLiteral("border: none; background-color: %1;").arg(
0319                                                m_currentTrackColor.name()));
0320     m_trackOpacity->setText(i18n("Opacity: %1%", int(m_currentTrackColor.alphaF() * 100.0)));
0321 }
0322 
0323 void SettingsDialog::setTrackColor()
0324 {
0325     QColorDialog dialog(m_currentTrackColor);
0326     dialog.setOption(QColorDialog::ShowAlphaChannel, true);
0327 
0328     if (! dialog.exec()) {
0329         return;
0330     }
0331 
0332     m_currentTrackColor = dialog.currentColor();
0333     updateTrackColor();
0334 }
0335 
0336 void SettingsDialog::accept()
0337 {
0338     const auto splitImagesList = m_imageListsMode->currentIndex() == 0;
0339     m_settings->saveSplitImagesList(splitImagesList);
0340 
0341     m_settings->saveDefaultMatchingMode(static_cast<KGeoTag::SearchType>(
0342         m_automaticMatchingMode->currentData().toInt()));
0343 
0344     const auto thumbnailSize = m_thumbnailSize->value();
0345     m_settings->saveThumbnailSize(thumbnailSize);
0346     const auto previewSize = m_previewSize->value();
0347     m_settings->savePreviewSize(previewSize);
0348 
0349     m_settings->saveTrackColor(m_currentTrackColor);
0350     m_settings->saveTrackWidth(m_trackWidth->value());
0351     m_settings->saveTrackStyle(static_cast<Qt::PenStyle>(m_trackStyle->currentData().toInt()));
0352 
0353     m_settings->saveLookupElevationAutomatically(m_lookupElevationAutomatically->isChecked());
0354     m_settings->saveElevationDataset(m_elevationDataset->currentData().toString());
0355 
0356     m_settings->saveWriteMode(m_writeMode->currentData().toString());
0357     m_settings->saveAllowWriteRawFiles(m_allowWriteRawFiles->isChecked());
0358     m_settings->saveCreateBackups(m_createBackups->isChecked());
0359 
0360     if (   thumbnailSize != m_originalThumbnailSizeValue
0361         || previewSize != m_originalPreviewSizeValue) {
0362 
0363         QMessageBox::information(this, i18n("Settings changed"),
0364             i18n("Please restart KGeoTag to apply the changed settings and make them visible!"));
0365     }
0366 
0367     if (splitImagesList != m_originalSplitImagesListValue) {
0368         Q_EMIT imagesListsModeChanged();
0369     }
0370 
0371     QDialog::accept();
0372 }