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

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 "AutomaticMatchingWidget.h"
0007 #include "Settings.h"
0008 
0009 // KDE includes
0010 #include <KLocalizedString>
0011 
0012 // Qt includes
0013 #include <QVBoxLayout>
0014 #include <QHBoxLayout>
0015 #include <QGridLayout>
0016 #include <QDebug>
0017 #include <QLabel>
0018 #include <QPushButton>
0019 #include <QSpinBox>
0020 #include <QCheckBox>
0021 #include <QGroupBox>
0022 #include <QApplication>
0023 #include <QStyle>
0024 #include <QMessageBox>
0025 #include <QMenu>
0026 #include <QAction>
0027 #include <QDebug>
0028 
0029 // C++ includes
0030 #include <functional>
0031 
0032 AutomaticMatchingWidget::AutomaticMatchingWidget(Settings *settings, QWidget *parent)
0033     : QWidget(parent), m_settings(settings)
0034 {
0035     auto *layout = new QVBoxLayout(this);
0036 
0037     // Exact matching
0038 
0039     auto *exactMatchBox = new QGroupBox(i18n("Search for exact matches"));
0040     exactMatchBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
0041     auto *exactMatchBoxLayout = new QVBoxLayout(exactMatchBox);
0042     layout->addWidget(exactMatchBox);
0043 
0044     auto *exactMatchToleranceLabel = new QLabel(i18n(
0045         "Maximum deviation of the image's time from a GPS point's time to be considered an exact "
0046         "match:"));
0047     exactMatchToleranceLabel->setWordWrap(true);
0048     exactMatchBoxLayout->addWidget(exactMatchToleranceLabel);
0049 
0050     auto *secondsLayout = new QHBoxLayout;
0051     exactMatchBoxLayout->addLayout(secondsLayout);
0052 
0053     m_exactMatchTolerance = new QSpinBox;
0054     m_exactMatchTolerance->setMinimum(0);
0055     m_exactMatchTolerance->setMaximum(300);
0056     m_exactMatchTolerance->setValue(m_settings->exactMatchTolerance());
0057     secondsLayout->addWidget(m_exactMatchTolerance);
0058 
0059     secondsLayout->addWidget(new QLabel(i18n("seconds")));
0060 
0061     secondsLayout->addStretch();
0062 
0063     // Interpolated matching
0064 
0065     auto *interpolatedMatchBox = new QGroupBox(i18n("Search for interpolated matches"));
0066     interpolatedMatchBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
0067     auto *interpolatedMatchBoxLayout = new QVBoxLayout(interpolatedMatchBox);
0068     layout->addWidget(interpolatedMatchBox);
0069 
0070     auto *interpolatedMatchLabel = new QLabel(i18n(
0071         "Spatial and temporal boundaries for two coordinates used to calculate an interpolated "
0072         "match:"));
0073     interpolatedMatchLabel->setWordWrap(true);
0074     interpolatedMatchBoxLayout->addWidget(interpolatedMatchLabel);
0075 
0076     auto boundariesWrapperLayout = new QHBoxLayout;
0077     interpolatedMatchBoxLayout->addLayout(boundariesWrapperLayout);
0078 
0079     auto *boundariesLayout = new QGridLayout;
0080     boundariesWrapperLayout->addLayout(boundariesLayout);
0081 
0082     m_enableMaximumInterpolationInterval = new QCheckBox(i18n("Maximum interval:"));
0083     boundariesLayout->addWidget(m_enableMaximumInterpolationInterval, 0, 0);
0084 
0085     m_maximumInterpolationInterval = new QSpinBox;
0086     m_maximumInterpolationInterval->setMinimum(0);
0087     m_maximumInterpolationInterval->setMaximum(86400);
0088     boundariesLayout->addWidget(m_maximumInterpolationInterval, 0, 1);
0089 
0090     boundariesLayout->addWidget(new QLabel(i18n("seconds")), 0, 2);
0091 
0092     const int interval = m_settings->maximumInterpolationInterval();
0093     if (interval == -1) {
0094         enableMaximumInterpolationInterval(false);
0095     } else {
0096         m_enableMaximumInterpolationInterval->setChecked(true);
0097         m_maximumInterpolationInterval->setValue(interval);
0098     }
0099 
0100     connect(m_enableMaximumInterpolationInterval, &QCheckBox::toggled,
0101             this, &AutomaticMatchingWidget::enableMaximumInterpolationInterval);
0102 
0103     m_enableMaximumInterpolationDistance = new QCheckBox(i18n("Maximum distance:"));
0104     boundariesLayout->addWidget(m_enableMaximumInterpolationDistance, 1, 0);
0105 
0106     m_maximumInterpolationDistance = new QSpinBox;
0107     m_maximumInterpolationDistance->setRange(0, 1000000);
0108     boundariesLayout->addWidget(m_maximumInterpolationDistance, 1, 1);
0109 
0110     boundariesLayout->addWidget(new QLabel(i18n("meters")), 1, 2);
0111 
0112     const int distance = m_settings->maximumInterpolationDistance();
0113     if (distance == -1) {
0114         enableMaximumInterpolationDistance(false);
0115     } else {
0116         m_enableMaximumInterpolationDistance->setChecked(true);
0117         m_maximumInterpolationDistance->setValue(distance);
0118     }
0119 
0120     connect(m_enableMaximumInterpolationDistance, &QCheckBox::toggled,
0121             this, &AutomaticMatchingWidget::enableMaximumInterpolationDistance);
0122 
0123     boundariesWrapperLayout->addStretch();
0124 
0125     // Bottom buttons
0126 
0127     layout->addStretch();
0128 
0129     auto *buttonsLayout = new QHBoxLayout;
0130     layout->addLayout(buttonsLayout);
0131 
0132     auto *saveButton = new QPushButton(i18n("Save settings"));
0133     saveButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogSaveButton));
0134     connect(saveButton, &QPushButton::clicked, this, &AutomaticMatchingWidget::saveSettings);
0135     buttonsLayout->addWidget(saveButton);
0136 
0137     buttonsLayout->addStretch();
0138 
0139     auto *reassignButton = new QPushButton(i18n("(Re)Assign all images"));
0140 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
0141     // FIXME:
0142     // QStyle::SP_DialogRetryButton wasn't there until Qt 5.14. KGeoTag actually can be built
0143     // against Qt 5.12, which is included in Ubuntu LTS 20.04. Let's keep this until the last-ish
0144     // Ubuntu LTS version has Qt 5.15.
0145     reassignButton->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogRetryButton));
0146 #endif
0147 
0148     auto *reassignMenu = new QMenu(this);
0149 
0150     auto *combinedSearch = reassignMenu->addAction(i18n("Combined match search"));
0151     connect(combinedSearch, &QAction::triggered,
0152             this, std::bind(&AutomaticMatchingWidget::requestReassignment, this,
0153                             KGeoTag::CombinedMatchSearch));
0154 
0155     reassignMenu->addSeparator();
0156 
0157     auto *exactSearch = reassignMenu->addAction(i18n("Search exact matches only"));
0158     connect(exactSearch, &QAction::triggered,
0159             this, std::bind(&AutomaticMatchingWidget::requestReassignment, this,
0160                             KGeoTag::ExactMatchSearch));
0161 
0162     auto *interpolatedSearch = reassignMenu->addAction(i18n("Search interpolated matches only"));
0163     connect(interpolatedSearch, &QAction::triggered,
0164             this, std::bind(&AutomaticMatchingWidget::requestReassignment, this,
0165                             KGeoTag::InterpolatedMatchSearch));
0166 
0167     reassignMenu->addSeparator();
0168 
0169     m_excludeManuallyTagged = reassignMenu->addAction(i18n("Exclude manually tagged images"));
0170     m_excludeManuallyTagged->setCheckable(true);
0171     m_excludeManuallyTagged->setChecked(m_settings->excludeManuallyTaggedWhenReassigning());
0172 
0173     reassignButton->setMenu(reassignMenu);
0174     buttonsLayout->addWidget(reassignButton);
0175 }
0176 
0177 void AutomaticMatchingWidget::enableMaximumInterpolationInterval(bool state)
0178 {
0179     m_maximumInterpolationInterval->setEnabled(state);
0180     if (! state) {
0181         m_maximumInterpolationInterval->setValue(3600);
0182     }
0183 }
0184 
0185 void AutomaticMatchingWidget::enableMaximumInterpolationDistance(bool state)
0186 {
0187     m_maximumInterpolationDistance->setEnabled(state);
0188     if (! state) {
0189         m_maximumInterpolationDistance->setValue(500);
0190     }
0191 }
0192 
0193 void AutomaticMatchingWidget::saveSettings()
0194 {
0195     m_settings->saveExactMatchTolerance(m_exactMatchTolerance->value());
0196     m_settings->saveMaximumInterpolationInterval(m_enableMaximumInterpolationInterval->isChecked()
0197         ? m_maximumInterpolationInterval->value() : -1);
0198     m_settings->saveMaximumInterpolationDistance(m_enableMaximumInterpolationDistance->isChecked()
0199         ? m_maximumInterpolationDistance->value() : -1);
0200     m_settings->saveExcludeManuallyTaggedWhenReassigning(m_excludeManuallyTagged->isChecked());
0201 
0202     QMessageBox::information(this, i18n("Save as default"), i18n("Settings saved!"));
0203 }
0204 
0205 bool AutomaticMatchingWidget::excludeManuallyTagged() const
0206 {
0207     return m_excludeManuallyTagged->isChecked();
0208 }
0209 
0210 int AutomaticMatchingWidget::exactMatchTolerance() const
0211 {
0212     return m_exactMatchTolerance->value();
0213 }
0214 
0215 int AutomaticMatchingWidget::maximumInterpolationInterval() const
0216 {
0217     return m_enableMaximumInterpolationInterval->isChecked()
0218                ? m_maximumInterpolationInterval->value()
0219                : -1;
0220 }
0221 
0222 int AutomaticMatchingWidget::maximumInterpolationDistance() const
0223 {
0224     return m_enableMaximumInterpolationDistance->isChecked()
0225                ? m_maximumInterpolationDistance->value()
0226                : -1;
0227 }