File indexing completed on 2025-01-05 03:58:32

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-05-07
0007  * Description : Context menu for GPS list view.
0008  *
0009  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText: 2009-2014 by Michael G. Hansen <mike at mghansen dot de>
0011  *
0012  * SPDX-License-Identifier: GPL-2.0-or-later
0013  *
0014  * ============================================================ */
0015 
0016 #include "gpsitemlistcontextmenu.h"
0017 
0018 // Qt includes:
0019 
0020 #include <QEvent>
0021 #include <QContextMenuEvent>
0022 #include <QClipboard>
0023 #include <QApplication>
0024 #include <QDomDocument>
0025 #include <QPointer>
0026 #include <QAction>
0027 #include <QMenu>
0028 #include <QMessageBox>
0029 #include <QMimeData>
0030 
0031 // KDE includes
0032 
0033 #include <klocalizedstring.h>
0034 
0035 // Local includes
0036 
0037 #include "gpsundocommand.h"
0038 #include "gpscommon.h"
0039 #include "gpsitemcontainer.h"
0040 #include "lookupfactory.h"
0041 #include "gpsbookmarkowner.h"
0042 #include "digikam_debug.h"
0043 
0044 namespace Digikam
0045 {
0046 
0047 class Q_DECL_HIDDEN GPSItemListContextMenu::Private
0048 {
0049 public:
0050 
0051     explicit Private()
0052       : enabled                     (true),
0053         actionBookmark              (nullptr),
0054         bookmarkOwner               (nullptr),
0055         actionCopy                  (nullptr),
0056         actionPaste                 (nullptr),
0057         actionPasteSwap             (nullptr),
0058         actionRemoveCoordinates     (nullptr),
0059         actionRemoveAltitude        (nullptr),
0060         actionRemoveUncertainty     (nullptr),
0061         actionRemoveSpeed           (nullptr),
0062         actionLookupMissingAltitudes(nullptr),
0063         imagesList                  (nullptr),
0064         altitudeLookup              (),
0065         altitudeUndoCommand         (nullptr),
0066         altitudeRequestedCount      (0),
0067         altitudeReceivedCount       (0)
0068     {
0069     }
0070 
0071     bool                     enabled;
0072 
0073     QAction*                 actionBookmark;
0074     GPSBookmarkOwner*        bookmarkOwner;
0075 
0076     QAction*                 actionCopy;
0077     QAction*                 actionPaste;
0078     QAction*                 actionPasteSwap;
0079     QAction*                 actionRemoveCoordinates;
0080     QAction*                 actionRemoveAltitude;
0081     QAction*                 actionRemoveUncertainty;
0082     QAction*                 actionRemoveSpeed;
0083     QAction*                 actionLookupMissingAltitudes;
0084 
0085     GPSItemList*             imagesList;
0086 
0087     /// Altitude lookup
0088     QPointer<LookupAltitude> altitudeLookup;
0089     GPSUndoCommand*          altitudeUndoCommand;
0090     int                      altitudeRequestedCount;
0091     int                      altitudeReceivedCount;
0092 };
0093 
0094 GPSItemListContextMenu::GPSItemListContextMenu(GPSItemList* const imagesList,
0095                                                GPSBookmarkOwner* const bookmarkOwner)
0096     : QObject(imagesList),
0097       d(new Private)
0098 {
0099     d->imagesList                   = imagesList;
0100 
0101     d->actionCopy                   = new QAction(i18n("Copy coordinates"),                this);
0102     d->actionCopy->setIcon(QIcon::fromTheme(QLatin1String("edit-copy")));
0103     d->actionPaste                  = new QAction(i18n("Paste coordinates"),               this);
0104     d->actionPaste->setIcon(QIcon::fromTheme(QLatin1String("edit-paste")));
0105     d->actionPasteSwap              = new QAction(i18n("Paste coordinates swapped"),       this);
0106     d->actionPasteSwap->setIcon(QIcon::fromTheme(QLatin1String("edit-paste")));
0107     d->actionRemoveCoordinates      = new QAction(i18n("Remove coordinates"),              this);
0108     d->actionRemoveAltitude         = new QAction(i18n("Remove altitude"),                 this);
0109     d->actionRemoveUncertainty      = new QAction(i18n("Remove uncertainty"),              this);
0110     d->actionRemoveSpeed            = new QAction(i18n("Remove speed"),                    this);
0111     d->actionLookupMissingAltitudes = new QAction(i18n("Look up missing altitude values"), this);
0112 
0113     connect(d->actionCopy, SIGNAL(triggered()),
0114             this, SLOT(copyActionTriggered()));
0115 
0116     connect(d->actionPaste, SIGNAL(triggered()),
0117             this, SLOT(pasteActionTriggered()));
0118 
0119     connect(d->actionPasteSwap, SIGNAL(triggered()),
0120             this, SLOT(pasteSwapActionTriggered()));
0121 
0122     connect(d->actionRemoveCoordinates, SIGNAL(triggered()),
0123             this, SLOT(slotRemoveCoordinates()));
0124 
0125     connect(d->actionRemoveAltitude, SIGNAL(triggered()),
0126             this, SLOT(slotRemoveAltitude()));
0127 
0128     connect(d->actionRemoveUncertainty, SIGNAL(triggered()),
0129             this, SLOT(slotRemoveUncertainty()));
0130 
0131     connect(d->actionRemoveSpeed, SIGNAL(triggered()),
0132             this, SLOT(slotRemoveSpeed()));
0133 
0134     connect(d->actionLookupMissingAltitudes, SIGNAL(triggered()),
0135             this, SLOT(slotLookupMissingAltitudes()));
0136 
0137     if (bookmarkOwner)
0138     {
0139         d->bookmarkOwner   = bookmarkOwner;
0140         d->actionBookmark  = new QAction(i18n("Bookmarks"), this);
0141         d->actionBookmark->setMenu(d->bookmarkOwner->getMenu());
0142 
0143         connect(d->bookmarkOwner, SIGNAL(positionSelected(GPSDataContainer)),
0144                 this, SLOT(slotBookmarkSelected(GPSDataContainer)));
0145     }
0146 
0147     d->imagesList->installEventFilter(this);
0148 }
0149 
0150 GPSItemListContextMenu::~GPSItemListContextMenu()
0151 {
0152     delete d->altitudeUndoCommand;
0153     delete d;
0154 }
0155 
0156 bool GPSItemListContextMenu::eventFilter(QObject* watched, QEvent* event)
0157 {
0158     // We are only interested in context-menu events.
0159 
0160     if ((event->type() == QEvent::ContextMenu) && d->enabled)
0161     {
0162         // enable or disable the actions
0163 
0164         GPSItemModel* const imageModel           = d->imagesList->getModel();
0165         QItemSelectionModel* const selectionModel = d->imagesList->getSelectionModel();
0166         const QList<QModelIndex> selectedIndices  = selectionModel->selectedRows();
0167         const int nSelected                       = selectedIndices.size();
0168 
0169         // "copy" are only available for one selected image with geo data:
0170 
0171         bool copyAvailable                   = (nSelected == 1);
0172         bool removeAltitudeAvailable         = false;
0173         bool removeCoordinatesAvailable      = false;
0174         bool removeUncertaintyAvailable      = false;
0175         bool removeSpeedAvailable            = false;
0176         bool lookupMissingAltitudesAvailable = false;
0177 
0178         for (int i = 0 ; i < nSelected ; ++i)
0179         {
0180             GPSItemContainer* const gpsItem = imageModel->itemFromIndex(selectedIndices.at(i));
0181 
0182             if (gpsItem)
0183             {
0184                 const GPSDataContainer gpsData   = gpsItem->gpsData();
0185                 const bool itemHasCoordinates    = gpsData.getCoordinates().hasCoordinates();
0186                 copyAvailable                   &= itemHasCoordinates;
0187                 removeCoordinatesAvailable      |= itemHasCoordinates;
0188                 removeAltitudeAvailable         |= gpsData.getCoordinates().hasAltitude();
0189                 removeUncertaintyAvailable      |= gpsData.hasNSatellites() | gpsData.hasDop() | gpsData.hasFixType();
0190                 removeSpeedAvailable            |= gpsData.hasSpeed();
0191                 lookupMissingAltitudesAvailable |= itemHasCoordinates && !gpsData.getCoordinates().hasAltitude();
0192             }
0193         }
0194 
0195         d->actionCopy->setEnabled(copyAvailable);
0196         d->actionRemoveAltitude->setEnabled(removeAltitudeAvailable);
0197         d->actionRemoveCoordinates->setEnabled(removeCoordinatesAvailable);
0198         d->actionRemoveUncertainty->setEnabled(removeUncertaintyAvailable);
0199         d->actionRemoveSpeed->setEnabled(removeSpeedAvailable);
0200         d->actionLookupMissingAltitudes->setEnabled(lookupMissingAltitudesAvailable);
0201 
0202         if (d->bookmarkOwner)
0203         {
0204             d->bookmarkOwner->changeAddBookmark(copyAvailable);
0205             GPSDataContainer position;
0206             QUrl             itemUrl;
0207             getCurrentItemPositionAndUrl(&position, &itemUrl);
0208             const QString itemFileName = itemUrl.fileName();
0209             d->bookmarkOwner->setPositionAndTitle(position.getCoordinates(), itemFileName);
0210         }
0211 
0212         // "paste" is only available if there is geo data in the clipboard
0213         // and at least one photo is selected:
0214 
0215         bool pasteAvailable     = (nSelected >= 1);
0216         bool pasteSwapAvailable = true;
0217 
0218         if (pasteAvailable)
0219         {
0220             QClipboard* const clipboard = QApplication::clipboard();
0221             const QMimeData* mimedata   = clipboard->mimeData();
0222             bool hasXmlFormat           = mimedata->hasFormat(QLatin1String("application/gpx+xml"));
0223             pasteAvailable              = hasXmlFormat || mimedata->hasText();
0224             pasteSwapAvailable          = !hasXmlFormat && mimedata->hasText();
0225         }
0226 
0227         d->actionPaste->setEnabled(pasteAvailable);
0228         d->actionPasteSwap->setEnabled(pasteSwapAvailable);
0229 
0230         // construct the context-menu:
0231 
0232         QMenu* const menu = new QMenu(d->imagesList);
0233         menu->addAction(d->actionCopy);
0234         menu->addAction(d->actionPaste);
0235         menu->addAction(d->actionPasteSwap);
0236         menu->addSeparator();
0237         menu->addAction(d->actionRemoveCoordinates);
0238         menu->addAction(d->actionRemoveAltitude);
0239         menu->addAction(d->actionRemoveUncertainty);
0240         menu->addAction(d->actionRemoveSpeed);
0241         menu->addAction(d->actionLookupMissingAltitudes);
0242 
0243         if (d->actionBookmark)
0244         {
0245             menu->addSeparator();
0246             menu->addAction(d->actionBookmark);
0247             d->actionBookmark->setEnabled(nSelected >= 1);
0248         }
0249 
0250         QContextMenuEvent* const e = static_cast<QContextMenuEvent*>(event);
0251         menu->exec(e->globalPos());
0252 
0253         delete menu;
0254 
0255         return true;
0256     }
0257     else
0258     {
0259         return QObject::eventFilter(watched, event);
0260     }
0261 
0262 }
0263 
0264 bool GPSItemListContextMenu::getCurrentItemPositionAndUrl(GPSDataContainer* const gpsInfo,
0265                                                            QUrl* const itemUrl)
0266 {
0267     // NOTE: currentIndex does not seem to work any more since we use KLinkItemSelectionModel
0268 
0269     GPSItemModel* const imageModel           = d->imagesList->getModel();
0270     QItemSelectionModel* const selectionModel = d->imagesList->getSelectionModel();
0271     const QList<QModelIndex> selectedIndices  = selectionModel->selectedRows();
0272 
0273     if (selectedIndices.count() != 1)
0274     {
0275         return false;
0276     }
0277 
0278     const QModelIndex currentIndex = selectedIndices.first();
0279 
0280     if (!currentIndex.isValid())
0281     {
0282         return false;
0283     }
0284 
0285     GPSItemContainer* const gpsItem = imageModel->itemFromIndex(currentIndex);
0286 
0287     if (gpsItem)
0288     {
0289         if (gpsInfo)
0290         {
0291             *gpsInfo = gpsItem->gpsData();
0292         }
0293 
0294         if (itemUrl)
0295         {
0296             *itemUrl = gpsItem->url();
0297         }
0298 
0299         return true;
0300     }
0301 
0302     return false;
0303 }
0304 
0305 void GPSItemListContextMenu::copyActionTriggered()
0306 {
0307     GPSDataContainer gpsInfo;
0308     QUrl itemUrl;
0309 
0310     if (!getCurrentItemPositionAndUrl(&gpsInfo, &itemUrl))
0311     {
0312         return;
0313     }
0314 
0315     coordinatesToClipboard(gpsInfo.getCoordinates(), itemUrl, QString());
0316 }
0317 
0318 void GPSItemListContextMenu::pasteSwapActionTriggered()
0319 {
0320     pasteActionTriggered(true);
0321 }
0322 
0323 void GPSItemListContextMenu::pasteActionTriggered(bool swap)
0324 {
0325     // extract the coordinates from the clipboard:
0326 
0327     QClipboard* const clipboard = QApplication::clipboard();
0328     const QMimeData* mimedata   = clipboard->mimeData();
0329 
0330     GPSDataContainer gpsData;
0331     bool foundData              = false;
0332 
0333     if (mimedata->hasFormat(QLatin1String("application/gpx+xml")))
0334     {
0335         const QByteArray data = mimedata->data(QLatin1String("application/gpx+xml"));
0336         bool xmlOkay          = true;
0337         bool foundDoubleData  = false;
0338 
0339         // code adapted from gpsdataparser.cpp
0340 
0341         QDomDocument gpxDoc(QLatin1String("gpx"));
0342 
0343         if (!gpxDoc.setContent(data))
0344         {
0345             xmlOkay = false;
0346         }
0347 
0348         if (xmlOkay)
0349         {
0350             const QDomElement gpxDocElem = gpxDoc.documentElement();
0351 
0352             if (gpxDocElem.tagName() != QLatin1String("gpx"))
0353             {
0354                 xmlOkay = false;
0355             }
0356 
0357             if (xmlOkay)
0358             {
0359                 for (QDomNode nWpt = gpxDocElem.firstChild() ; !nWpt.isNull() ; nWpt = nWpt.nextSibling())
0360                 {
0361                     const QDomElement wptElem = nWpt.toElement();
0362 
0363                     if (wptElem.isNull())
0364                     {
0365                         continue;
0366                     }
0367 
0368                     if (wptElem.tagName() != QLatin1String("wpt"))
0369                     {
0370                         continue;
0371                     }
0372 
0373                     double ptAltitude  = 0.0;
0374                     double ptLatitude  = 0.0;
0375                     double ptLongitude = 0.0;
0376                     bool haveAltitude  = false;
0377 
0378                     // Get GPS position. If not available continue to next point.
0379 
0380                     const QString lat  = wptElem.attribute(QLatin1String("lat"));
0381                     const QString lon  = wptElem.attribute(QLatin1String("lon"));
0382 
0383                     if (lat.isEmpty() || lon.isEmpty())
0384                     {
0385                         continue;
0386                     }
0387 
0388                     ptLatitude         = lat.toDouble();
0389                     ptLongitude        = lon.toDouble();
0390 
0391                     if (foundData)
0392                     {
0393                         foundDoubleData = true;
0394                         break;
0395                     }
0396 
0397                     // Get metadata of way point (altitude and time stamp)
0398 
0399                     for (QDomNode nWptMeta = wptElem.firstChild() ; !nWptMeta.isNull() ; nWptMeta = nWptMeta.nextSibling())
0400                     {
0401                         const QDomElement wptMetaElem = nWptMeta.toElement();
0402 
0403                         if (wptMetaElem.isNull())
0404                         {
0405                             continue;
0406                         }
0407 
0408                         if (wptMetaElem.tagName() == QLatin1String("ele"))
0409                         {
0410                             // Get GPS point altitude. If not available continue to next point.
0411 
0412                             QString ele = wptMetaElem.text();
0413 
0414                             if (!ele.isEmpty())
0415                             {
0416                                 ptAltitude = ele.toDouble(&haveAltitude);
0417                                 break;
0418                             }
0419                         }
0420                     }
0421 
0422                     foundData = true;
0423                     GeoCoordinates coordinates(ptLatitude, ptLongitude);
0424 
0425                     if (haveAltitude)
0426                     {
0427                         coordinates.setAlt(ptAltitude);
0428                     }
0429 
0430                     gpsData.setCoordinates(coordinates);
0431                 }
0432             }
0433         }
0434 
0435         if (foundDoubleData)
0436         {
0437             QMessageBox::information(d->imagesList,
0438                                      i18nc("@title:window", "Geolocation Editor"),
0439                                      i18n("Found more than one point on the clipboard - can only assign one point at a time."));
0440         }
0441     }
0442 
0443     if (!foundData && mimedata->hasText())
0444     {
0445         const QString textdata         = mimedata->text();
0446         bool foundGeoUrl               = false;
0447         GeoCoordinates testCoordinates = GeoCoordinates::fromGeoUrl(textdata, &foundGeoUrl);
0448 
0449         if (foundGeoUrl)
0450         {
0451             gpsData.setCoordinates(testCoordinates);
0452             foundData = true;
0453         }
0454         else
0455         {
0456             /// @todo this is legacy code from before we used geo-url
0457 
0458             QLocale locale;
0459             QString cordText = textdata.trimmed();
0460             QStringList separators({QLatin1String(","),
0461                                     QLatin1String("/"),
0462                                     QLatin1String(":"),
0463                                     QLatin1String(";"),
0464                                     QLatin1String(" ")});
0465             int firstPoint   =  cordText.indexOf(QLatin1Char('.'));
0466 
0467             if ((firstPoint == -1) || (firstPoint > 4))
0468             {
0469                 locale = QLocale(QLocale::French);
0470 
0471                 if (cordText.count(QLatin1Char(',')) > 1)
0472                 {
0473                     separators.removeOne(QLatin1String(","));
0474                 }
0475             }
0476             else
0477             {
0478                 locale = QLocale(QLocale::C);
0479             }
0480 
0481             QStringList parts;
0482             bool foundParts = false;
0483 
0484             while (!separators.isEmpty())
0485             {
0486                 parts = cordText.split(separators.takeFirst(), QT_SKIP_EMPTY_PARTS);
0487 
0488                 if ((parts.size() == 3) || (parts.size() == 2))
0489                 {
0490                     foundParts = true;
0491                     break;
0492                 }
0493             }
0494 
0495             if (foundParts)
0496             {
0497                 double ptLongitude = 0.0;
0498                 double ptAltitude  = 0.0;
0499                 bool haveAltitude  = false;
0500                 bool okay          = true;
0501                 double ptLatitude  = locale.toDouble(parts[0], &okay);
0502 
0503                 if (okay)
0504                 {
0505                     ptLongitude = locale.toDouble(parts[1], &okay);
0506                 }
0507 
0508                 if (okay && (parts.size() == 3))
0509                 {
0510                     ptAltitude   = locale.toDouble(parts[2], &okay);
0511                     haveAltitude = okay;
0512                 }
0513 
0514                 foundData = okay;
0515 
0516                 if (okay)
0517                 {
0518                     if (swap)
0519                     {
0520                         std::swap(ptLatitude, ptLongitude);
0521                     }
0522 
0523                     GeoCoordinates coordinates(ptLatitude, ptLongitude);
0524 
0525                     if (haveAltitude)
0526                     {
0527                         coordinates.setAlt(ptAltitude);
0528                     }
0529 
0530                     gpsData.setCoordinates(coordinates);
0531                 }
0532             }
0533         }
0534     }
0535 
0536     if (!foundData)
0537     {
0538         QMessageBox::information(d->imagesList,
0539                                  i18nc("@title:window", "Geolocation Editor"),
0540                                  i18n("Could not find any coordinates on the clipboard."));
0541         return;
0542     }
0543 
0544     setGPSDataForSelectedItems(gpsData, i18n("Coordinates pasted"));
0545 }
0546 
0547 void GPSItemListContextMenu::setGPSDataForSelectedItems(const GPSDataContainer& gpsData,
0548                                                          const QString& undoDescription)
0549 {
0550     GPSItemModel* const imageModel            = d->imagesList->getModel();
0551     QItemSelectionModel* const selectionModel = d->imagesList->getSelectionModel();
0552     const QList<QModelIndex> selectedIndices  = selectionModel->selectedRows();
0553     const int nSelected                       = selectedIndices.size();
0554     GPSUndoCommand* const undoCommand         = new GPSUndoCommand();
0555 
0556     for (int i = 0 ; i < nSelected ; ++i)
0557     {
0558         const QModelIndex itemIndex     = selectedIndices.at(i);
0559         GPSItemContainer* const gpsItem = imageModel->itemFromIndex(itemIndex);
0560 
0561         GPSUndoCommand::UndoInfo undoInfo(itemIndex);
0562         undoInfo.readOldDataFromItem(gpsItem);
0563 
0564         gpsItem->setGPSData(gpsData);
0565         undoInfo.readNewDataFromItem(gpsItem);
0566 
0567         undoCommand->addUndoInfo(undoInfo);
0568     }
0569 
0570     undoCommand->setText(undoDescription);
0571     Q_EMIT signalUndoCommand(undoCommand);
0572 }
0573 
0574 void GPSItemListContextMenu::slotBookmarkSelected(const GPSDataContainer& position)
0575 {
0576     setGPSDataForSelectedItems(position, i18n("Bookmark selected"));
0577 }
0578 
0579 bool GPSItemListContextMenu::getCurrentPosition(GPSDataContainer* position, void* mydata)
0580 {
0581     if (!position || !mydata)
0582     {
0583         return false;
0584     }
0585 
0586     GPSItemListContextMenu* const me = reinterpret_cast<GPSItemListContextMenu*>(mydata);
0587 
0588     return me->getCurrentItemPositionAndUrl(position, nullptr);
0589 }
0590 
0591 void GPSItemListContextMenu::removeInformationFromSelectedImages(const GPSDataContainer::HasFlags flagsToClear, const QString& undoDescription)
0592 {
0593     // enable or disable the actions
0594 
0595     GPSItemModel* const imageModel            = d->imagesList->getModel();
0596     QItemSelectionModel* const selectionModel = d->imagesList->getSelectionModel();
0597     const QList<QModelIndex> selectedIndices  = selectionModel->selectedRows();
0598     const int nSelected                       = selectedIndices.size();
0599     GPSUndoCommand* const undoCommand         = new GPSUndoCommand();
0600 
0601     for (int i = 0 ; i < nSelected ; ++i)
0602     {
0603         const QModelIndex itemIndex     = selectedIndices.at(i);
0604         GPSItemContainer* const gpsItem = imageModel->itemFromIndex(itemIndex);
0605 
0606         GPSUndoCommand::UndoInfo undoInfo(itemIndex);
0607         undoInfo.readOldDataFromItem(gpsItem);
0608 
0609         GPSDataContainer newGPSData     = gpsItem->gpsData();
0610         bool didSomething               = false;
0611 
0612         if (flagsToClear.testFlag(GPSDataContainer::HasCoordinates))
0613         {
0614             if (newGPSData.hasCoordinates())
0615             {
0616                 didSomething = true;
0617                 newGPSData.clear();
0618             }
0619         }
0620 
0621         if (flagsToClear.testFlag(GPSDataContainer::HasAltitude))
0622         {
0623             if (newGPSData.hasAltitude())
0624             {
0625                 didSomething = true;
0626                 newGPSData.clearAltitude();
0627             }
0628         }
0629 
0630         if (flagsToClear.testFlag(GPSDataContainer::HasNSatellites))
0631         {
0632             if (newGPSData.hasNSatellites())
0633             {
0634                 didSomething = true;
0635                 newGPSData.clearNSatellites();
0636             }
0637         }
0638 
0639         if (flagsToClear.testFlag(GPSDataContainer::HasDop))
0640         {
0641             if (newGPSData.hasDop())
0642             {
0643                 didSomething = true;
0644                 newGPSData.clearDop();
0645             }
0646         }
0647 
0648         if (flagsToClear.testFlag(GPSDataContainer::HasFixType))
0649         {
0650             if (newGPSData.hasFixType())
0651             {
0652                 didSomething = true;
0653                 newGPSData.clearFixType();
0654             }
0655         }
0656 
0657         if (flagsToClear.testFlag(GPSDataContainer::HasSpeed))
0658         {
0659             if (newGPSData.hasSpeed())
0660             {
0661                 didSomething = true;
0662                 newGPSData.clearSpeed();
0663             }
0664         }
0665 
0666         if (didSomething)
0667         {
0668             gpsItem->setGPSData(newGPSData);
0669             undoInfo.readNewDataFromItem(gpsItem);
0670             undoCommand->addUndoInfo(undoInfo);
0671         }
0672     }
0673 
0674     if (undoCommand->affectedItemCount() > 0)
0675     {
0676         undoCommand->setText(undoDescription);
0677         Q_EMIT signalUndoCommand(undoCommand);
0678     }
0679     else
0680     {
0681         delete undoCommand;
0682     }
0683 }
0684 
0685 void GPSItemListContextMenu::slotRemoveCoordinates()
0686 {
0687     removeInformationFromSelectedImages(GPSDataContainer::HasCoordinates, i18n("Remove coordinates information"));
0688 }
0689 
0690 void GPSItemListContextMenu::slotRemoveAltitude()
0691 {
0692     removeInformationFromSelectedImages(GPSDataContainer::HasAltitude, i18n("Remove altitude information"));
0693 }
0694 
0695 void GPSItemListContextMenu::slotRemoveUncertainty()
0696 {
0697     removeInformationFromSelectedImages(GPSDataContainer::HasNSatellites|GPSDataContainer::HasDop|GPSDataContainer::HasFixType,
0698                                         i18n("Remove uncertainty information"));
0699 }
0700 
0701 void GPSItemListContextMenu::setEnabled(const bool state)
0702 {
0703     d->enabled = state;
0704 }
0705 
0706 void GPSItemListContextMenu::slotRemoveSpeed()
0707 {
0708     removeInformationFromSelectedImages(GPSDataContainer::HasSpeed, i18n("Remove speed"));
0709 }
0710 
0711 void GPSItemListContextMenu::slotLookupMissingAltitudes()
0712 {
0713     GPSItemModel* const imageModel            = d->imagesList->getModel();
0714     QItemSelectionModel* const selectionModel = d->imagesList->getSelectionModel();
0715     const QList<QModelIndex> selectedIndices  = selectionModel->selectedRows();
0716 /*
0717     const int nSelected                       = selectedIndices.size();
0718 */
0719     // find the indices which have coordinates but no altitude
0720 
0721     LookupAltitude::Request::List altitudeQueries;
0722 
0723     Q_FOREACH (const QModelIndex& currentIndex, selectedIndices)
0724     {
0725         GPSItemContainer* const gpsItem = imageModel->itemFromIndex(currentIndex);
0726 
0727         if (!gpsItem)
0728         {
0729             continue;
0730         }
0731 
0732         const GPSDataContainer gpsData   = gpsItem->gpsData();
0733         const GeoCoordinates coordinates = gpsData.getCoordinates();
0734 
0735         if ((!coordinates.hasCoordinates()) || coordinates.hasAltitude())
0736         {
0737             continue;
0738         }
0739 
0740         // the item has coordinates but no altitude, create a query
0741 
0742         LookupAltitude::Request myLookup;
0743         myLookup.coordinates = coordinates;
0744         myLookup.data        = QVariant::fromValue(QPersistentModelIndex(currentIndex));
0745 
0746         altitudeQueries << myLookup;
0747     }
0748 
0749     if (altitudeQueries.isEmpty())
0750     {
0751         return;
0752     }
0753 
0754     d->altitudeLookup = LookupFactory::getAltitudeLookup(QLatin1String("geonames"), this);
0755 
0756     connect(d->altitudeLookup, SIGNAL(signalRequestsReady(QList<int>)),
0757             this, SLOT(slotAltitudeLookupReady(QList<int>)));
0758 
0759     connect(d->altitudeLookup, SIGNAL(signalDone()),
0760             this, SLOT(slotAltitudeLookupDone()));
0761 
0762     Q_EMIT signalSetUIEnabled(false, this, QString::fromUtf8(SLOT(slotAltitudeLookupCancel())));
0763     Q_EMIT signalProgressSetup(altitudeQueries.count(), i18n("Looking up altitudes"));
0764 
0765     d->altitudeUndoCommand    = new GPSUndoCommand();
0766     d->altitudeRequestedCount = altitudeQueries.count();
0767     d->altitudeReceivedCount  = 0;
0768     d->altitudeLookup->addRequests(altitudeQueries);
0769     d->altitudeLookup->startLookup();
0770 }
0771 
0772 void GPSItemListContextMenu::slotAltitudeLookupReady(const QList<int>& readyRequests)
0773 {
0774     GPSItemModel* const imageModel = d->imagesList->getModel();
0775 
0776     Q_FOREACH (const int requestIndex, readyRequests)
0777     {
0778         const LookupAltitude::Request myLookup  = d->altitudeLookup->getRequest(requestIndex);
0779         const QPersistentModelIndex markerIndex = myLookup.data.value<QPersistentModelIndex>();
0780 
0781         if (!markerIndex.isValid())
0782         {
0783             continue;
0784         }
0785 
0786         GPSItemContainer* const gpsItem         = imageModel->itemFromIndex(markerIndex);
0787 
0788         if (!gpsItem)
0789         {
0790             continue;
0791         }
0792 
0793         GPSUndoCommand::UndoInfo undoInfo(markerIndex);
0794         undoInfo.readOldDataFromItem(gpsItem);
0795 
0796         GPSDataContainer gpsData                = gpsItem->gpsData();
0797         gpsData.setCoordinates(myLookup.coordinates);
0798         gpsItem->setGPSData(gpsData);
0799         undoInfo.readNewDataFromItem(gpsItem);
0800 
0801         d->altitudeUndoCommand->addUndoInfo(undoInfo);
0802         d->altitudeReceivedCount++;
0803     }
0804 
0805     Q_EMIT signalProgressChanged(d->altitudeReceivedCount);
0806 }
0807 
0808 void GPSItemListContextMenu::slotAltitudeLookupDone()
0809 {
0810     LookupAltitude::StatusAltitude requestStatus = d->altitudeLookup->getStatus();
0811 
0812     if (requestStatus == LookupAltitude::StatusError)
0813     {
0814         const QString errorMessage = i18n("Altitude lookup failed:\n%1", d->altitudeLookup->errorMessage());
0815         QMessageBox::information(d->imagesList, i18nc("@title:window", "Geolocation Editor"), errorMessage);
0816     }
0817 
0818     if (d->altitudeReceivedCount > 0)
0819     {
0820         // at least some queries returned a result, save the undo command
0821 
0822         d->altitudeUndoCommand->setText(i18n("Altitude looked up"));
0823         Q_EMIT signalUndoCommand(d->altitudeUndoCommand);
0824     }
0825     else
0826     {
0827         delete d->altitudeUndoCommand;
0828     }
0829 
0830     d->altitudeUndoCommand = nullptr;
0831     d->altitudeLookup->deleteLater();
0832 
0833     Q_EMIT signalSetUIEnabled(true);
0834 }
0835 
0836 void GPSItemListContextMenu::slotAltitudeLookupCancel()
0837 {
0838     if (d->altitudeLookup)
0839     {
0840         d->altitudeLookup->cancel();
0841     }
0842 }
0843 
0844 } // namespace Digikam
0845 
0846 #include "moc_gpsitemlistcontextmenu.cpp"