File indexing completed on 2025-01-19 03:50:46
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2010-07-15 0007 * Description : central Map view 0008 * 0009 * SPDX-FileCopyrightText: 2010 by Gabriel Voicu <ping dot gabi at gmail dot com> 0010 * SPDX-FileCopyrightText: 2010 by Michael G. Hansen <mike at mghansen dot de> 0011 * SPDX-FileCopyrightText: 2014-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "mapwidgetview.h" 0018 0019 // Qt includes 0020 0021 #include <QVBoxLayout> 0022 #include <QApplication> 0023 #include <QItemSelectionModel> 0024 #include <QAbstractItemModel> 0025 #include <QPersistentModelIndex> 0026 #include <QScopedPointer> 0027 #include <QTimer> 0028 #include <QDir> 0029 0030 // KDE includes 0031 0032 #include <kconfiggroup.h> 0033 0034 // Local includes 0035 0036 #include "mapwidget.h" 0037 #include "trackmanager.h" 0038 #include "itemmarkertiler.h" 0039 #include "digikam_debug.h" 0040 #include "camerathumbsctrl.h" 0041 #include "itemposition.h" 0042 #include "iteminfo.h" 0043 #include "itemmodel.h" 0044 #include "importfiltermodel.h" 0045 #include "importimagemodel.h" 0046 #include "coredbwatch.h" 0047 #include "coredbfields.h" 0048 #include "itempropertiessidebardb.h" 0049 #include "gpsiteminfosorter.h" 0050 #include "importui.h" 0051 0052 namespace Digikam 0053 { 0054 0055 class ItemAlbumModel; 0056 class ItemFilterModel; 0057 0058 /** 0059 * @class MapWidgetView 0060 * 0061 * @brief Class containing digiKam's central map view. 0062 */ 0063 class Q_DECL_HIDDEN MapWidgetView::Private 0064 { 0065 public: 0066 0067 explicit Private() 0068 : mapWidget (nullptr), 0069 imageModel (nullptr), 0070 imageFilterModel (nullptr), 0071 importModel (nullptr), 0072 importFilterModel (nullptr), 0073 selectionModel (nullptr), 0074 mapViewModelHelper (nullptr), 0075 gpsItemInfoSorter (nullptr), 0076 trackManager (nullptr), 0077 modelTimer (nullptr), 0078 application (MapWidgetView::ApplicationDigikam), 0079 boundariesShouldBeAdjusted (false) 0080 { 0081 } 0082 0083 MapWidget* mapWidget; 0084 0085 ItemAlbumModel* imageModel; 0086 ItemFilterModel* imageFilterModel; 0087 ImportItemModel* importModel; 0088 ImportFilterModel* importFilterModel; 0089 QItemSelectionModel* selectionModel; 0090 0091 MapViewModelHelper* mapViewModelHelper; 0092 GPSItemInfoSorter* gpsItemInfoSorter; 0093 TrackManager* trackManager; 0094 0095 QTimer* modelTimer; 0096 0097 MapWidgetView::Application application; 0098 0099 bool boundariesShouldBeAdjusted; 0100 }; 0101 0102 /** 0103 * @brief Constructor 0104 * @param selectionModel digiKam's selection model 0105 * @param imageFilterModel digikam's filter model 0106 * @param parent the parent object 0107 */ 0108 MapWidgetView::MapWidgetView(QItemSelectionModel* const selectionModel, 0109 DCategorizedSortFilterProxyModel* const imageFilterModel, 0110 QWidget* const parent, 0111 const MapWidgetView::Application application) 0112 : QWidget (parent), 0113 StateSavingObject(this), 0114 d (new Private()) 0115 { 0116 d->application = application; 0117 d->selectionModel = selectionModel; 0118 0119 d->modelTimer = new QTimer(this); 0120 d->modelTimer->setSingleShot(true); 0121 d->modelTimer->setInterval(50); 0122 0123 switch (d->application) 0124 { 0125 case ApplicationDigikam: 0126 { 0127 d->imageFilterModel = dynamic_cast<ItemFilterModel*>(imageFilterModel); 0128 d->imageModel = dynamic_cast<ItemAlbumModel*>(imageFilterModel->sourceModel()); 0129 d->mapViewModelHelper = new MapViewModelHelper(d->selectionModel, 0130 d->imageFilterModel, 0131 this, ApplicationDigikam); 0132 0133 connect(d->imageModel, SIGNAL(allRefreshingFinished()), 0134 d->modelTimer, SLOT(start())); 0135 break; 0136 } 0137 0138 case ApplicationImportUI: 0139 { 0140 d->importFilterModel = dynamic_cast<ImportFilterModel*>(imageFilterModel); 0141 d->importModel = dynamic_cast<ImportItemModel*>(imageFilterModel->sourceModel()); 0142 d->mapViewModelHelper = new MapViewModelHelper(d->selectionModel, 0143 d->importFilterModel, 0144 this, ApplicationImportUI); 0145 0146 connect(d->importModel, SIGNAL(allRefreshingFinished()), 0147 d->modelTimer, SLOT(start())); 0148 break; 0149 } 0150 } 0151 0152 connect(d->modelTimer, SIGNAL(timeout()), 0153 this, SLOT(slotModelChanged())); 0154 0155 QVBoxLayout* const vBoxLayout = new QVBoxLayout(this); 0156 d->mapWidget = new MapWidget(this); 0157 d->mapWidget->setAvailableMouseModes(MouseModePan | MouseModeZoomIntoGroup | MouseModeSelectThumbnail); 0158 d->mapWidget->setVisibleMouseModes(MouseModePan | MouseModeZoomIntoGroup | MouseModeSelectThumbnail); 0159 0160 if (d->application == ApplicationDigikam) 0161 { 0162 d->mapWidget->setVisibleExtraActions(ExtraLoadTracks); 0163 0164 connect(d->mapWidget, &MapWidget::signalLoadTracksFromAlbums, 0165 this, &MapWidgetView::slotLoadTracksFromAlbums); 0166 } 0167 0168 ItemMarkerTiler* const geoifaceMarkerModel = new ItemMarkerTiler(d->mapViewModelHelper, this); 0169 d->mapWidget->setGroupedModel(geoifaceMarkerModel); 0170 d->mapWidget->setBackend(QLatin1String("marble")); 0171 0172 d->gpsItemInfoSorter = new GPSItemInfoSorter(this); 0173 d->gpsItemInfoSorter->addToMapWidget(d->mapWidget); 0174 0175 d->trackManager = new TrackManager(this); 0176 d->mapWidget->setTrackManager(d->trackManager); 0177 0178 connect(d->trackManager, &TrackManager::signalAllTrackFilesReady, 0179 this, &MapWidgetView::slotAllTrackFilesReady); 0180 0181 vBoxLayout->addWidget(d->mapWidget); 0182 vBoxLayout->addWidget(d->mapWidget->getControlWidget()); 0183 vBoxLayout->setContentsMargins(QMargins()); 0184 } 0185 0186 /** 0187 * @brief Destructor 0188 */ 0189 MapWidgetView::~MapWidgetView() 0190 { 0191 } 0192 0193 void MapWidgetView::doLoadState() 0194 { 0195 KConfigGroup group = getConfigGroup(); 0196 0197 d->gpsItemInfoSorter->setSortOptions(GPSItemInfoSorter::SortOptions(group.readEntry(QLatin1String("Sort Order"), 0198 int(d->gpsItemInfoSorter->getSortOptions())))); 0199 0200 const KConfigGroup groupCentralMap = KConfigGroup(&group, QLatin1String("Central Map Widget")); 0201 d->mapWidget->readSettingsFromGroup(&groupCentralMap); 0202 } 0203 0204 void MapWidgetView::doSaveState() 0205 { 0206 KConfigGroup group = getConfigGroup(); 0207 0208 group.writeEntry(QLatin1String("Sort Order"), int(d->gpsItemInfoSorter->getSortOptions())); 0209 0210 KConfigGroup groupCentralMap = KConfigGroup(&group, QLatin1String("Central Map Widget")); 0211 d->mapWidget->saveSettingsToGroup(&groupCentralMap); 0212 0213 group.sync(); 0214 } 0215 0216 /** 0217 * @brief Set the map active/inactive. 0218 * @param state If true, the map is active. 0219 */ 0220 void MapWidgetView::setActive(const bool state) 0221 { 0222 d->mapWidget->setActive(state); 0223 0224 if (state && d->boundariesShouldBeAdjusted) 0225 { 0226 d->modelTimer->start(); 0227 } 0228 } 0229 0230 /** 0231 * @return The map's active state 0232 */ 0233 bool MapWidgetView::getActiveState() const 0234 { 0235 return d->mapWidget->getActiveState(); 0236 } 0237 0238 /** 0239 * @brief Returns the ItemInfo for the current image 0240 */ 0241 ItemInfo MapWidgetView::currentItemInfo() const 0242 { 0243 /// @todo Have geoifacewidget honor the 'current index' 0244 0245 QModelIndex currentIndex = d->selectionModel->currentIndex(); 0246 0247 if (!currentIndex.isValid()) 0248 { 0249 /// @todo This is temporary until geoifacewidget marks a 'current index' 0250 0251 if (!d->selectionModel->hasSelection()) 0252 { 0253 return ItemInfo(); 0254 } 0255 0256 currentIndex = d->selectionModel->selectedIndexes().first(); 0257 } 0258 0259 return d->imageFilterModel->imageInfo(currentIndex); 0260 } 0261 0262 /** 0263 * @brief Returns the CamItemInfo for the current image 0264 */ 0265 CamItemInfo MapWidgetView::currentCamItemInfo() const 0266 { 0267 /// @todo Have geoifacewidget honor the 'current index' 0268 0269 QModelIndex currentIndex = d->selectionModel->currentIndex(); 0270 0271 if (!currentIndex.isValid()) 0272 { 0273 /// @todo This is temporary until geoifacewidget marks a 'current index' 0274 0275 if (!d->selectionModel->hasSelection()) 0276 { 0277 return CamItemInfo(); 0278 } 0279 0280 currentIndex = d->selectionModel->selectedIndexes().first(); 0281 } 0282 0283 return d->importFilterModel->camItemInfo(currentIndex); 0284 } 0285 0286 void MapWidgetView::slotModelChanged() 0287 { 0288 if (!d->mapWidget->getActiveState()) 0289 { 0290 d->boundariesShouldBeAdjusted = true; 0291 return; 0292 } 0293 0294 d->trackManager->setVisibility(false); 0295 d->trackManager->clear(); 0296 0297 bool hasCoordinates = false; 0298 0299 switch (d->application) 0300 { 0301 case ApplicationDigikam: 0302 { 0303 Q_FOREACH (const ItemInfo& info, d->imageModel->imageInfos()) 0304 { 0305 if (info.hasCoordinates()) 0306 { 0307 hasCoordinates = true; 0308 break; 0309 } 0310 } 0311 0312 break; 0313 } 0314 0315 case ApplicationImportUI: 0316 { 0317 Q_FOREACH (const CamItemInfo& info, d->importModel->camItemInfos()) 0318 { 0319 QScopedPointer<DMetadata> meta(new DMetadata(info.url().toLocalFile())); 0320 double lat, lng; 0321 0322 if (meta->getGPSLatitudeNumber(&lat) && 0323 meta->getGPSLongitudeNumber(&lng)) 0324 { 0325 hasCoordinates = true; 0326 break; 0327 } 0328 } 0329 0330 break; 0331 } 0332 } 0333 0334 if (!hasCoordinates) 0335 { 0336 setEnabled(false); 0337 d->mapWidget->setCenter(GeoCoordinates(52.0, 6.0)); 0338 d->mapWidget->setZoom(QLatin1String("marble:1108")); 0339 } 0340 else 0341 { 0342 setEnabled(true); 0343 d->mapWidget->adjustBoundariesToGroupedMarkers(); 0344 } 0345 0346 d->boundariesShouldBeAdjusted = false; 0347 } 0348 0349 void MapWidgetView::slotAllTrackFilesReady() 0350 { 0351 if (d->trackManager->getTrackList().isEmpty()) 0352 { 0353 return; 0354 } 0355 0356 const TrackManager::Track track = d->trackManager->getTrackList().constFirst(); 0357 0358 if ((track.id != 0) && (track.points.size() > 0)) 0359 { 0360 d->mapWidget->setCenter(track.points.at(0).coordinates); 0361 d->mapWidget->setZoom(QLatin1String("marble:2220")); 0362 } 0363 } 0364 0365 void MapWidgetView::slotLoadTracksFromAlbums() 0366 { 0367 QApplication::setOverrideCursor(Qt::WaitCursor); 0368 0369 d->trackManager->setVisibility(false); 0370 d->trackManager->clear(); 0371 0372 QList<QUrl> fileUrls; 0373 0374 Q_FOREACH (const ItemInfo& info, d->imageModel->imageInfos()) 0375 { 0376 if (info.hasCoordinates()) 0377 { 0378 QUrl url = info.fileUrl().adjusted(QUrl::RemoveFilename | 0379 QUrl::StripTrailingSlash); 0380 0381 if (!fileUrls.contains(url)) 0382 { 0383 fileUrls << url; 0384 } 0385 } 0386 } 0387 0388 if (fileUrls.isEmpty()) 0389 { 0390 QApplication::restoreOverrideCursor(); 0391 0392 return; 0393 } 0394 0395 QList<QUrl> gpxList; 0396 const QStringList filter({QLatin1String("*.gpx")}); 0397 0398 Q_FOREACH (const QUrl& url, fileUrls) 0399 { 0400 QDir dir(url.toLocalFile()); 0401 0402 Q_FOREACH (const QFileInfo& finfo, dir.entryInfoList(filter, QDir::Files)) 0403 { 0404 gpxList << QUrl::fromLocalFile(finfo.filePath()); 0405 } 0406 } 0407 0408 QApplication::restoreOverrideCursor(); 0409 0410 if (!gpxList.isEmpty()) 0411 { 0412 d->trackManager->loadTrackFiles(gpxList); 0413 d->trackManager->setVisibility(true); 0414 } 0415 } 0416 0417 //------------------------------------------------------------------------------------------------------------- 0418 0419 class Q_DECL_HIDDEN MapViewModelHelper::Private 0420 { 0421 public: 0422 0423 explicit Private() 0424 : model (nullptr), 0425 importModel (nullptr), 0426 selectionModel (nullptr), 0427 thumbnailLoadThread(nullptr), 0428 application (MapWidgetView::ApplicationDigikam) 0429 { 0430 } 0431 0432 ItemFilterModel* model; 0433 ImportFilterModel* importModel; 0434 QItemSelectionModel* selectionModel; 0435 ThumbnailLoadThread* thumbnailLoadThread; 0436 MapWidgetView::Application application; 0437 }; 0438 0439 MapViewModelHelper::MapViewModelHelper(QItemSelectionModel* const selection, 0440 DCategorizedSortFilterProxyModel* const filterModel, 0441 QObject* const parent, 0442 const MapWidgetView::Application application) 0443 : GeoModelHelper(parent), 0444 d(new Private()) 0445 { 0446 d->selectionModel = selection; 0447 d->application = application; 0448 0449 switch (d->application) 0450 { 0451 case MapWidgetView::ApplicationDigikam: 0452 { 0453 d->model = dynamic_cast<ItemFilterModel*>(filterModel); 0454 d->thumbnailLoadThread = new ThumbnailLoadThread(this); 0455 0456 connect(d->thumbnailLoadThread, SIGNAL(signalThumbnailLoaded(LoadingDescription,QPixmap)), 0457 this, SLOT(slotThumbnailLoaded(LoadingDescription,QPixmap))); 0458 0459 // Note: Here we only monitor changes to the database, because changes to the model 0460 // are also sent when thumbnails are generated, and we don't want to update 0461 // the marker tiler for that! 0462 0463 connect(CoreDbAccess::databaseWatch(), SIGNAL(imageChange(ImageChangeset)), 0464 this, SLOT(slotImageChange(ImageChangeset)), Qt::QueuedConnection); 0465 0466 break; 0467 } 0468 0469 case MapWidgetView::ApplicationImportUI: 0470 { 0471 d->importModel = dynamic_cast<ImportFilterModel*>(filterModel); 0472 0473 connect(ImportUI::instance()->getCameraThumbsCtrl(), SIGNAL(signalThumbInfoReady(CamItemInfo)), 0474 this, SLOT(slotThumbnailLoaded(CamItemInfo))); 0475 0476 break; 0477 } 0478 } 0479 } 0480 0481 /** 0482 * @brief Destructor 0483 */ 0484 MapViewModelHelper::~MapViewModelHelper() 0485 { 0486 } 0487 0488 /** 0489 * @return Returns digiKam's filter model. 0490 */ 0491 QAbstractItemModel* MapViewModelHelper::model() const 0492 { 0493 switch (d->application) 0494 { 0495 case MapWidgetView::ApplicationDigikam: 0496 { 0497 return d->model; 0498 } 0499 0500 case MapWidgetView::ApplicationImportUI: 0501 { 0502 return d->importModel; 0503 } 0504 } 0505 0506 return nullptr; 0507 } 0508 0509 /** 0510 * @return Returns digiKam's selection model. 0511 */ 0512 QItemSelectionModel* MapViewModelHelper::selectionModel() const 0513 { 0514 return d->selectionModel; 0515 } 0516 0517 /** 0518 * @brief Gets the coordinates of a marker found at current model index. 0519 * @param index Current model index. 0520 * @param coordinates Here will be returned the coordinates of the current marker. 0521 * @return True, if the marker has coordinates. 0522 */ 0523 bool MapViewModelHelper::itemCoordinates(const QModelIndex& index, GeoCoordinates* const coordinates) const 0524 { 0525 switch (d->application) 0526 { 0527 case MapWidgetView::ApplicationDigikam: 0528 { 0529 const ItemInfo info = d->model->imageInfo(index); 0530 0531 if (info.isNull() || !info.hasCoordinates()) 0532 { 0533 return false; 0534 } 0535 0536 *coordinates = GeoCoordinates(info.latitudeNumber(), info.longitudeNumber()); 0537 break; 0538 } 0539 0540 case MapWidgetView::ApplicationImportUI: 0541 { 0542 const CamItemInfo info = d->importModel->camItemInfo(index); 0543 0544 if (info.isNull()) 0545 { 0546 return false; 0547 } 0548 0549 QScopedPointer<DMetadata> meta(new DMetadata(info.url().toLocalFile())); 0550 double lat, lng; 0551 const bool haveCoordinates = meta->getGPSLatitudeNumber(&lat) && meta->getGPSLongitudeNumber(&lng); 0552 0553 if (!haveCoordinates) 0554 { 0555 return false; 0556 } 0557 0558 GeoCoordinates tmpCoordinates(lat, lng); 0559 0560 double alt; 0561 const bool haveAlt = meta->getGPSAltitude(&alt); 0562 0563 if (haveAlt) 0564 { 0565 tmpCoordinates.setAlt(alt); 0566 } 0567 0568 *coordinates = tmpCoordinates; 0569 break; 0570 } 0571 } 0572 0573 return true; 0574 } 0575 0576 /** 0577 * @brief This function retrieves the thumbnail for an index. 0578 * @param index The marker's index. 0579 * @param size The size of the thumbnail. 0580 * @return If the thumbnail has been loaded in the ThumbnailLoadThread instance, it is returned. 0581 * If not, a QPixmap is returned and ThumbnailLoadThread's signal named signalThumbnailLoaded is emitted when the thumbnail becomes available. 0582 */ 0583 QPixmap MapViewModelHelper::pixmapFromRepresentativeIndex(const QPersistentModelIndex& index, const QSize& size) 0584 { 0585 if (index == QPersistentModelIndex()) 0586 { 0587 return QPixmap(); 0588 } 0589 0590 switch (d->application) 0591 { 0592 case MapWidgetView::ApplicationDigikam: 0593 { 0594 const ItemInfo info = d->model->imageInfo(index); 0595 0596 if (!info.isNull()) 0597 { 0598 QPixmap thumbnail; 0599 0600 if (d->thumbnailLoadThread->find(info.thumbnailIdentifier(), thumbnail, qMax(size.width()+2, size.height()+2))) 0601 { 0602 return thumbnail.copy(1, 1, thumbnail.size().width()-2, thumbnail.size().height()-2); 0603 } 0604 else 0605 { 0606 return QPixmap(); 0607 } 0608 } 0609 0610 break; 0611 } 0612 0613 case MapWidgetView::ApplicationImportUI: 0614 { 0615 QPixmap thumbnail = index.data(ImportItemModel::ThumbnailRole).value<QPixmap>(); 0616 0617 return thumbnail.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0618 } 0619 } 0620 0621 return QPixmap(); 0622 } 0623 0624 /** 0625 * @brief This function finds the best representative marker from a group of markers. This is needed to display a thumbnail for a marker group. 0626 * @param indices A list containing markers. 0627 * @param sortKey Determines the sorting options and is actually of type GPSItemInfoSorter::SortOptions 0628 * @return Returns the index of the marker. 0629 */ 0630 QPersistentModelIndex MapViewModelHelper::bestRepresentativeIndexFromList(const QList<QPersistentModelIndex>& list, 0631 const int sortKey) 0632 { 0633 if (list.isEmpty()) 0634 { 0635 return QPersistentModelIndex(); 0636 } 0637 0638 // first convert from QPersistentModelIndex to QModelIndex 0639 0640 QList<QModelIndex> indexList; 0641 QModelIndex bestIndex; 0642 0643 for (int i = 0 ; i < list.count() ; ++i) 0644 { 0645 const QModelIndex newIndex(list.at(i)); 0646 indexList.append(newIndex); 0647 } 0648 0649 switch (d->application) 0650 { 0651 case MapWidgetView::ApplicationDigikam: 0652 { 0653 // now get the ItemInfos and convert them to GPSItemInfos 0654 0655 const QList<ItemInfo> imageInfoList = d->model->imageInfos(indexList); 0656 GPSItemInfo::List gpsItemInfoList; 0657 0658 Q_FOREACH (const ItemInfo& imageInfo, imageInfoList) 0659 { 0660 GPSItemInfo gpsItemInfo; 0661 0662 if (ItemPropertiesSideBarDB::GPSItemInfofromItemInfo(imageInfo, &gpsItemInfo)) 0663 { 0664 gpsItemInfoList << gpsItemInfo; 0665 } 0666 } 0667 0668 if (gpsItemInfoList.size() != indexList.size()) 0669 { 0670 // this is a problem, and unexpected 0671 0672 return indexList.first(); 0673 } 0674 0675 // now determine the best available index 0676 0677 bestIndex = indexList.first(); 0678 GPSItemInfo bestGPSItemInfo = gpsItemInfoList.first(); 0679 0680 for (int i = 1 ; i < gpsItemInfoList.count() ; ++i) 0681 { 0682 const GPSItemInfo& currentInfo = gpsItemInfoList.at(i); 0683 0684 if (GPSItemInfoSorter::fitsBetter(bestGPSItemInfo, SelectedNone, 0685 currentInfo, SelectedNone, 0686 SelectedNone, GPSItemInfoSorter::SortOptions(sortKey))) 0687 { 0688 bestIndex = indexList.at(i); 0689 bestGPSItemInfo = currentInfo; 0690 } 0691 } 0692 0693 break; 0694 } 0695 0696 case MapWidgetView::ApplicationImportUI: 0697 { 0698 // now get the CamItemInfo and convert them to GPSItemInfos 0699 0700 const QList<CamItemInfo> imageInfoList = d->importModel->camItemInfos(indexList); 0701 GPSItemInfo::List gpsItemInfoList; 0702 0703 Q_FOREACH (const CamItemInfo& imageInfo, imageInfoList) 0704 { 0705 QScopedPointer<DMetadata> meta(new DMetadata(imageInfo.url().toLocalFile())); 0706 double lat, lng; 0707 const bool hasCoordinates = meta->getGPSLatitudeNumber(&lat) && meta->getGPSLongitudeNumber(&lng); 0708 0709 if (!hasCoordinates) 0710 { 0711 continue; 0712 } 0713 0714 GeoCoordinates coordinates(lat, lng); 0715 0716 double alt; 0717 const bool haveAlt = meta->getGPSAltitude(&alt); 0718 0719 if (haveAlt) 0720 { 0721 coordinates.setAlt(alt); 0722 } 0723 0724 GPSItemInfo gpsItemInfo; 0725 gpsItemInfo.coordinates = coordinates; 0726 gpsItemInfo.dateTime = meta->getItemDateTime(); 0727 gpsItemInfo.rating = meta->getItemRating(); 0728 gpsItemInfo.url = imageInfo.url(); 0729 gpsItemInfoList << gpsItemInfo; 0730 } 0731 0732 if (gpsItemInfoList.size() != indexList.size()) 0733 { 0734 // this is a problem, and unexpected 0735 0736 return indexList.first(); 0737 } 0738 0739 // now determine the best available index 0740 0741 bestIndex = indexList.first(); 0742 GPSItemInfo bestGPSItemInfo = gpsItemInfoList.first(); 0743 0744 for (int i = 1 ; i < gpsItemInfoList.count() ; ++i) 0745 { 0746 const GPSItemInfo& currentInfo = gpsItemInfoList.at(i); 0747 0748 if (GPSItemInfoSorter::fitsBetter(bestGPSItemInfo, SelectedNone, 0749 currentInfo, SelectedNone, 0750 SelectedNone, GPSItemInfoSorter::SortOptions(sortKey))) 0751 { 0752 bestIndex = indexList.at(i); 0753 bestGPSItemInfo = currentInfo; 0754 } 0755 } 0756 0757 break; 0758 } 0759 } 0760 0761 // and return the index 0762 0763 return QPersistentModelIndex(bestIndex); 0764 } 0765 0766 /** 0767 * @brief Because of a call to pixmapFromRepresentativeIndex, some thumbnails are not yet loaded at the time of requesting. 0768 * When each thumbnail loads, this slot is called and emits a signal that announces the map that the thumbnail is available. 0769 * 0770 * This slot is used in the Digikam main application UI. 0771 */ 0772 void MapViewModelHelper::slotThumbnailLoaded(const LoadingDescription& loadingDescription, const QPixmap& thumb) 0773 { 0774 if (thumb.isNull()) 0775 { 0776 return; 0777 } 0778 0779 const QModelIndex currentIndex = d->model->indexForPath(loadingDescription.filePath); 0780 0781 if (currentIndex.isValid()) 0782 { 0783 QPersistentModelIndex goodIndex(currentIndex); 0784 Q_EMIT signalThumbnailAvailableForIndex(goodIndex, thumb.copy(1, 1, thumb.size().width()-2, thumb.size().height()-2)); 0785 } 0786 } 0787 0788 /** 0789 * @brief Because of a call to pixmapFromRepresentativeIndex, some thumbnails are not yet loaded at the time of requesting. 0790 * When each thumbnail loads, this slot is called and emits a signal that announces to the map that the thumbnail is available. 0791 * 0792 * This slot is used in the ImportUI interface. 0793 */ 0794 void MapViewModelHelper::slotThumbnailLoaded(const CamItemInfo& info) 0795 { 0796 CachedItem item; 0797 ImportUI::instance()->getCameraThumbsCtrl()->getThumbInfo(info, item); 0798 0799 QPixmap pix = item.second; 0800 0801 if (pix.isNull()) 0802 { 0803 return; 0804 } 0805 0806 const QModelIndex currentIndex = d->importModel->indexForPath(info.folder + info.name); 0807 0808 if (currentIndex.isValid()) 0809 { 0810 QPersistentModelIndex goodIndex(currentIndex); 0811 Q_EMIT signalThumbnailAvailableForIndex(goodIndex, pix.copy(1, 1, pix.size().width()-2, pix.size().height()-2)); 0812 } 0813 } 0814 0815 /** 0816 * This functions is called when one clicks on a thumbnail. 0817 * @param clickedIndices A list containing the marker indices belonging the group whose thumbnail has been clicked. 0818 */ 0819 void MapViewModelHelper::onIndicesClicked(const QList<QPersistentModelIndex>& clickedIndices) 0820 { 0821 /// @todo What do we do when an image is clicked? 0822 0823 #if 0 0824 0825 QList<QModelIndex> indexList; 0826 0827 for (int i = 0 ; i < clickedIndices.count() ; ++i) 0828 { 0829 const QModelIndex newIndex(clickedIndices.at(i)); 0830 indexList.append(newIndex); 0831 } 0832 0833 const QList<ItemInfo> imageInfoList = d->model->imageInfos(indexList); 0834 0835 QList<qlonglong> imagesIdList; 0836 0837 for (int i = 0 ; i < imageInfoList.count() ; ++i) 0838 { 0839 imagesIdList.append(imageInfoList[i].id()); 0840 } 0841 0842 Q_EMIT signalFilteredImages(imagesIdList); 0843 0844 #else 0845 0846 Q_UNUSED(clickedIndices); 0847 0848 #endif 0849 0850 } 0851 0852 void MapViewModelHelper::slotImageChange(const ImageChangeset& changeset) 0853 { 0854 const DatabaseFields::Set changes = changeset.changes(); 0855 // const DatabaseFields::ItemPositions imagePositionChanges = changes; 0856 0857 /// @todo More detailed check 0858 0859 if ((changes & DatabaseFields::LatitudeNumber) || 0860 (changes & DatabaseFields::LongitudeNumber) || 0861 (changes & DatabaseFields::Altitude)) 0862 { 0863 Q_FOREACH (const qlonglong& id, changeset.ids()) 0864 { 0865 const QModelIndex index = d->model->indexForImageId(id); 0866 0867 if (index.isValid()) 0868 { 0869 Q_EMIT signalModelChangedDrastically(); 0870 break; 0871 } 0872 } 0873 } 0874 } 0875 0876 } // namespace Digikam 0877 0878 #include "moc_mapwidgetview.cpp"