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"