File indexing completed on 2025-01-19 03:51:18

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2010-08-06
0007  * Description : A widget to show details about images
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2010      by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "gpsitemdetails.h"
0017 
0018 // C++ includes
0019 
0020 #include <limits.h>
0021 #include <math.h>
0022 
0023 // Qt includes
0024 
0025 #include <QCheckBox>
0026 #include <QDoubleValidator>
0027 #include <QFormLayout>
0028 #include <QIntValidator>
0029 #include <QPushButton>
0030 #include <QVBoxLayout>
0031 #include <QComboBox>
0032 #include <QLineEdit>
0033 
0034 // KDE includes
0035 
0036 #include <kconfiggroup.h>
0037 #include <klocalizedstring.h>
0038 
0039 // Local includes
0040 
0041 #include "digikam_debug.h"
0042 #include "dlayoutbox.h"
0043 #include "graphicsdimgview.h"
0044 #include "dimgpreviewitem.h"
0045 #include "dexpanderbox.h"
0046 
0047 namespace DigikamGenericGeolocationEditPlugin
0048 {
0049 
0050 class Q_DECL_HIDDEN GPSItemDetails::Private
0051 {
0052 public:
0053 
0054     explicit Private()
0055         : imageModel            (nullptr),
0056           previewManager        (nullptr),
0057           cbCoordinates         (nullptr),
0058           leLatitude            (nullptr),
0059           leLongitude           (nullptr),
0060           cbAltitude            (nullptr),
0061           leAltitude            (nullptr),
0062           cbSpeed               (nullptr),
0063           leSpeed               (nullptr),
0064           cbNSatellites         (nullptr),
0065           leNSatellites         (nullptr),
0066           cbFixType             (nullptr),
0067           comboFixType          (nullptr),
0068           cbDop                 (nullptr),
0069           leDop                 (nullptr),
0070           pbApply               (nullptr),
0071           externalEnabledState  (true),
0072           activeState           (false),
0073           haveDelayedState      (false)
0074     {
0075     }
0076 
0077     GPSItemModel*                imageModel;
0078     GraphicsDImgView*            previewManager;
0079 
0080     QCheckBox*                   cbCoordinates;
0081     QLineEdit*                   leLatitude;
0082     QLineEdit*                   leLongitude;
0083     QCheckBox*                   cbAltitude;
0084     QLineEdit*                   leAltitude;
0085     QCheckBox*                   cbSpeed;
0086     QLineEdit*                   leSpeed;
0087     QCheckBox*                   cbNSatellites;
0088     QLineEdit*                   leNSatellites;
0089     QCheckBox*                   cbFixType;
0090     QComboBox*                   comboFixType;
0091     QCheckBox*                   cbDop;
0092     QLineEdit*                   leDop;
0093 
0094     QPushButton*                 pbApply;
0095 
0096     QPersistentModelIndex        imageIndex;
0097     GPSDataContainer             infoOld;
0098     bool                         externalEnabledState;
0099     bool                         activeState;
0100     bool                         haveDelayedState;
0101 };
0102 
0103 GPSItemDetails::GPSItemDetails(QWidget* const parent, GPSItemModel* const imageModel)
0104     : QWidget(parent),
0105       d      (new Private())
0106 {
0107     d->imageModel = imageModel;
0108 
0109     // TODO: subscribe to changes in the model to update the display
0110 
0111     QVBoxLayout* const layout1 = new QVBoxLayout(this);
0112 
0113     // ----------------------------------
0114 
0115     QFormLayout* const formLayout = new QFormLayout();
0116     layout1->addLayout(formLayout);
0117 
0118     d->cbCoordinates = new QCheckBox(i18n("Coordinates"), this);
0119     formLayout->setWidget(formLayout->rowCount(), QFormLayout::LabelRole, d->cbCoordinates);
0120 
0121     d->leLatitude = new QLineEdit(this);
0122     d->leLatitude->setValidator(new QDoubleValidator(-90.0, 90.0, 12, this));
0123     d->leLatitude->setClearButtonEnabled(true);
0124     formLayout->addRow(i18n("Latitude"), d->leLatitude);
0125     d->leLongitude = new QLineEdit(this);
0126     d->leLongitude->setValidator(new QDoubleValidator(-180.0, 180.0, 12, this));
0127     d->leLongitude->setClearButtonEnabled(true);
0128     formLayout->addRow(i18n("Longitude"), d->leLongitude);
0129 
0130     d->cbAltitude = new QCheckBox(i18n("Altitude"), this);
0131     d->leAltitude = new QLineEdit(this);
0132     d->leAltitude->setClearButtonEnabled(true);
0133     d->leAltitude->setValidator(new QDoubleValidator(this));
0134     formLayout->addRow(d->cbAltitude, d->leAltitude);
0135 
0136     d->cbSpeed = new QCheckBox(i18n("Speed"), this);
0137     d->leSpeed = new QLineEdit(this);
0138     d->leSpeed->setClearButtonEnabled(true);
0139     d->leSpeed->setValidator(new QDoubleValidator(0, HUGE_VAL, 12, this));
0140     formLayout->addRow(d->cbSpeed, d->leSpeed);
0141 
0142     d->cbNSatellites = new QCheckBox(i18n("# satellites"), this);
0143     d->leNSatellites = new QLineEdit(this);
0144     d->leNSatellites->setClearButtonEnabled(true);
0145     d->leNSatellites->setValidator(new QIntValidator(0, 2000, this));
0146     formLayout->addRow(d->cbNSatellites, d->leNSatellites);
0147 
0148     d->cbFixType = new QCheckBox(i18n("Fix type"), this);
0149     d->comboFixType = new QComboBox(this);
0150     d->comboFixType->addItem(i18n("2-d"), QVariant(2));
0151     d->comboFixType->addItem(i18n("3-d"), QVariant(3));
0152     formLayout->addRow(d->cbFixType, d->comboFixType);
0153 
0154     d->cbDop = new QCheckBox(i18n("DOP"), this);
0155     d->leDop = new QLineEdit(this);
0156     d->leDop->setClearButtonEnabled(true);
0157     d->leDop->setValidator(new QDoubleValidator(0, HUGE_VAL, 2, this));
0158     formLayout->addRow(d->cbDop, d->leDop);
0159 
0160     d->pbApply = new QPushButton(i18n("Apply"), this);
0161     formLayout->setWidget(formLayout->rowCount(), QFormLayout::SpanningRole, d->pbApply);
0162 
0163     layout1->addWidget(new DLineWidget(Qt::Horizontal, this));
0164 
0165     // ----------------------------------
0166 
0167     d->previewManager = new GraphicsDImgView(this);
0168     d->previewManager->setItem(new DImgPreviewItem());
0169     d->previewManager->setMinimumSize(QSize(200, 200));
0170     layout1->addWidget(d->previewManager);
0171 
0172     // ----------------------------------
0173 
0174     connect(d->cbCoordinates, SIGNAL(stateChanged(int)),
0175             this, SLOT(updateUIState()));
0176 
0177     connect(d->cbAltitude, SIGNAL(stateChanged(int)),
0178             this, SLOT(updateUIState()));
0179 
0180     connect(d->cbSpeed, SIGNAL(stateChanged(int)),
0181             this, SLOT(updateUIState()));
0182 
0183     connect(d->cbNSatellites, SIGNAL(stateChanged(int)),
0184             this, SLOT(updateUIState()));
0185 
0186     connect(d->cbFixType, SIGNAL(stateChanged(int)),
0187             this, SLOT(updateUIState()));
0188 
0189     connect(d->cbDop, SIGNAL(stateChanged(int)),
0190             this, SLOT(updateUIState()));
0191 
0192     connect(d->imageModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0193             this, SLOT(slotModelDataChanged(QModelIndex,QModelIndex)));
0194 
0195     connect(d->pbApply, SIGNAL(clicked()),
0196             this, SLOT(slotApply()));
0197 
0198     updateUIState();
0199 }
0200 
0201 GPSItemDetails::~GPSItemDetails()
0202 {
0203     delete d;
0204 }
0205 
0206 void GPSItemDetails::setUIEnabledExternal(const bool state)
0207 {
0208     d->externalEnabledState = state;
0209     updateUIState();
0210 }
0211 
0212 void GPSItemDetails::saveSettingsToGroup(KConfigGroup* const /*group*/)
0213 {
0214 }
0215 
0216 void GPSItemDetails::readSettingsFromGroup(const KConfigGroup* const /*group*/)
0217 {
0218 }
0219 
0220 void GPSItemDetails::updateUIState()
0221 {
0222     const bool externalEnabled = d->externalEnabledState && d->imageIndex.isValid();
0223     const bool haveCoordinates = d->cbCoordinates->isChecked();
0224 
0225     d->cbCoordinates->setEnabled(externalEnabled);
0226 
0227     d->leLatitude->setEnabled(haveCoordinates && externalEnabled);
0228     d->leLongitude->setEnabled(haveCoordinates && externalEnabled);
0229 
0230     /* altitude */
0231     d->cbAltitude->setEnabled(haveCoordinates && externalEnabled);
0232     const bool haveAltitude = d->cbAltitude->isChecked();
0233     d->leAltitude->setEnabled(haveAltitude && haveCoordinates && externalEnabled);
0234 
0235     /* speed */
0236     d->cbSpeed->setEnabled(haveCoordinates && externalEnabled);
0237     d->leSpeed->setEnabled(d->cbSpeed->isChecked() && haveCoordinates && externalEnabled);
0238 
0239     /* NSatellites */
0240     d->cbNSatellites->setEnabled(haveCoordinates && externalEnabled);
0241     d->leNSatellites->setEnabled(d->cbNSatellites->isChecked() && haveCoordinates && externalEnabled);
0242 
0243     /* fix type */
0244     d->cbFixType->setEnabled(haveCoordinates && externalEnabled);
0245     d->comboFixType->setEnabled(d->cbFixType->isChecked() && haveCoordinates && externalEnabled);
0246 
0247     /* dop */
0248     d->cbDop->setEnabled(haveCoordinates && externalEnabled);
0249     d->leDop->setEnabled(d->cbDop->isChecked() && haveCoordinates && externalEnabled);
0250 
0251     /* apply */
0252     d->pbApply->setEnabled(externalEnabled);
0253 }
0254 
0255 void GPSItemDetails::displayGPSDataContainer(const GPSDataContainer* const gpsData)
0256 {
0257     d->cbAltitude->setChecked(false);
0258     d->cbSpeed->setChecked(false);
0259     d->leLatitude->clear();
0260     d->leLongitude->clear();
0261     d->leAltitude->clear();
0262     d->leSpeed->clear();
0263     d->leNSatellites->clear();
0264     d->leDop->clear();
0265     d->cbCoordinates->setChecked(gpsData->hasCoordinates());
0266 
0267     if (gpsData->hasCoordinates())
0268     {
0269         d->leLatitude->setText(QLocale().toString(gpsData->getCoordinates().lat(), 'f', 12));
0270         d->leLongitude->setText(QLocale().toString(gpsData->getCoordinates().lon(), 'f', 12));
0271 
0272         const bool haveAltitude = gpsData->hasAltitude();
0273         d->cbAltitude->setChecked(haveAltitude);
0274 
0275         if (haveAltitude)
0276         {
0277             d->leAltitude->setText(QLocale().toString(gpsData->getCoordinates().alt(), 'f', 12));
0278         }
0279 
0280         const bool haveSpeed = gpsData->hasSpeed();
0281         d->cbSpeed->setChecked(haveSpeed);
0282 
0283         if (haveSpeed)
0284         {
0285             d->leSpeed->setText(QLocale().toString(gpsData->getSpeed(), 'f', 12));
0286         }
0287 
0288         const bool haveNSatellites = gpsData->hasNSatellites();
0289         d->cbNSatellites->setChecked(haveNSatellites);
0290 
0291         if (haveNSatellites)
0292         {
0293             /// @todo Is this enough for simple integers or do we have to use QLocale?
0294             d->leNSatellites->setText(QString::number(gpsData->getNSatellites()));
0295         }
0296 
0297         const int haveFixType = gpsData->hasFixType();
0298         d->cbFixType->setChecked(haveFixType);
0299 
0300         if (haveFixType)
0301         {
0302             const int fixType      = gpsData->getFixType();
0303             const int fixTypeIndex = d->comboFixType->findData(QVariant(fixType));
0304 
0305             if (fixTypeIndex<0)
0306             {
0307                 d->cbFixType->setChecked(false);
0308             }
0309             else
0310             {
0311                 d->comboFixType->setCurrentIndex(fixTypeIndex);
0312             }
0313         }
0314 
0315         const bool haveDop = gpsData->hasDop();
0316         d->cbDop->setChecked(haveDop);
0317 
0318         if (haveDop)
0319         {
0320             d->leDop->setText(QString::fromLatin1("%1").arg(gpsData->getDop(), 0, 'f', 2));
0321         }
0322     }
0323 
0324     updateUIState();
0325 }
0326 
0327 void GPSItemDetails::slotSetCurrentImage(const QModelIndex& index)
0328 {
0329     // TODO: slotSetActive may call this function with d->imageIndex as a parameter
0330     // since we get the index as a reference, we overwrite index when changing d->imageIndex
0331     QModelIndex indexCopy = index;
0332     d->imageIndex         = indexCopy;
0333 
0334     if (!d->activeState)
0335     {
0336         d->haveDelayedState = true;
0337         return;
0338     }
0339 
0340     d->haveDelayedState = false;
0341 
0342     GPSDataContainer gpsData;
0343 
0344     if (index.isValid())
0345     {
0346         GPSItemContainer* const item = d->imageModel->itemFromIndex(index);
0347         qCDebug(DIGIKAM_DPLUGIN_GENERIC_LOG) << item;
0348 
0349         if (item)
0350         {
0351             d->previewManager->previewItem()->setPath(item->url().toLocalFile(), true);
0352             gpsData = item->gpsData();
0353         }
0354     }
0355 
0356     d->infoOld = gpsData;
0357     displayGPSDataContainer(&gpsData);
0358 }
0359 
0360 void GPSItemDetails::slotModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
0361 {
0362     if (!d->imageIndex.isValid())
0363     {
0364         return;
0365     }
0366 
0367     if (((topLeft.row()    <= d->imageIndex.row())    && (bottomRight.row()    >= d->imageIndex.row())) &&
0368         ((topLeft.column() <= d->imageIndex.column()) && (bottomRight.column() >= d->imageIndex.column())))
0369     {
0370         if (!d->activeState)
0371         {
0372             d->haveDelayedState = true;
0373             return;
0374         }
0375 
0376         GPSDataContainer gpsData;
0377         GPSItemContainer* const item = d->imageModel->itemFromIndex(d->imageIndex);
0378 
0379         if (item)
0380         {
0381             d->previewManager->previewItem()->setPath(item->url().toLocalFile(), true);
0382             gpsData = item->gpsData();
0383         }
0384 
0385         d->infoOld = gpsData;
0386         displayGPSDataContainer(&gpsData);
0387     }
0388 }
0389 
0390 void GPSItemDetails::slotApply()
0391 {
0392     GPSDataContainer newData;
0393 
0394     if (d->cbCoordinates->isChecked())
0395     {
0396         const double lat = QLocale().toDouble(d->leLatitude->text());
0397         const double lon = QLocale().toDouble(d->leLongitude->text());
0398         newData.setCoordinates(GeoCoordinates(lat, lon));
0399 
0400         if (d->cbAltitude->isChecked())
0401         {
0402             const qreal alt = static_cast<qreal>(QLocale().toDouble(d->leAltitude->text()));
0403             newData.setAltitude(alt);
0404         }
0405 
0406         if (d->cbSpeed->isChecked())
0407         {
0408             const qreal speed = static_cast<qreal>(QLocale().toDouble(d->leSpeed->text()));
0409             newData.setSpeed(speed);
0410         }
0411 
0412         if (d->cbNSatellites->isChecked())
0413         {
0414             const int nSatellites = d->leNSatellites->text().toInt();
0415             newData.setNSatellites(nSatellites);
0416         }
0417 
0418         if (d->cbFixType->isChecked())
0419         {
0420             const int fixType = d->comboFixType->itemData(d->comboFixType->currentIndex()).toInt();
0421             newData.setFixType(fixType);
0422         }
0423 
0424         if (d->cbDop->isChecked())
0425         {
0426             const qreal dop = static_cast<qreal>(QLocale().toDouble(d->leDop->text()));
0427             newData.setDop(dop);
0428         }
0429     }
0430 
0431     GPSItemContainer* const gpsItem   = d->imageModel->itemFromIndex(d->imageIndex);
0432     GPSUndoCommand* const undoCommand = new GPSUndoCommand();
0433 
0434     GPSUndoCommand::UndoInfo undoInfo(d->imageIndex);
0435     undoInfo.readOldDataFromItem(gpsItem);
0436     gpsItem->setGPSData(newData);
0437     undoInfo.readNewDataFromItem(gpsItem);
0438     undoCommand->addUndoInfo(undoInfo);
0439     undoCommand->setText(i18n("Details changed"));
0440     Q_EMIT signalUndoCommand(undoCommand);
0441 }
0442 
0443 void GPSItemDetails::slotSetActive(const bool state)
0444 {
0445     d->activeState = state;
0446 
0447     if (state && d->haveDelayedState)
0448     {
0449         d->haveDelayedState = false;
0450         slotSetCurrentImage(d->imageIndex);
0451     }
0452 }
0453 
0454 } // namespace DigikamGenericGeolocationEditPlugin
0455 
0456 #include "moc_gpsitemdetails.cpp"