File indexing completed on 2025-01-19 03:59:24
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2012-05-22 0007 * Description : Qt item model for camera entries 0008 * 0009 * SPDX-FileCopyrightText: 2012 by Islam Wazery <wazery at ubuntu dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "importimagemodel.h" 0016 0017 // Qt includes 0018 0019 #include <QHash> 0020 0021 // Local includes 0022 0023 #include "digikam_debug.h" 0024 #include "coredbdownloadhistory.h" 0025 #include "cameracontroller.h" 0026 0027 namespace Digikam 0028 { 0029 0030 class Q_DECL_HIDDEN ImportItemModel::Private 0031 { 0032 public: 0033 0034 explicit Private() 0035 : controller (nullptr), 0036 keepFileUrlCache (false), 0037 refreshing (false), 0038 reAdding (false), 0039 incrementalRefreshRequested (false), 0040 sendRemovalSignals (false), 0041 incrementalUpdater (nullptr) 0042 { 0043 } 0044 0045 inline bool isValid(const QModelIndex& index) 0046 { 0047 return ( 0048 index.isValid() && 0049 (index.row() >= 0) && 0050 (index.row() < infos.size()) 0051 ); 0052 } 0053 0054 public: 0055 0056 CameraController* controller; 0057 CamItemInfoList infos; 0058 CamItemInfo camItemInfo; 0059 0060 QMultiHash<qlonglong, int> idHash; 0061 QHash<QString, qlonglong> fileUrlHash; 0062 0063 bool keepFileUrlCache; 0064 0065 bool refreshing; 0066 bool reAdding; 0067 bool incrementalRefreshRequested; 0068 0069 bool sendRemovalSignals; 0070 0071 class ImportItemModelIncrementalUpdater* incrementalUpdater; 0072 }; 0073 0074 // ---------------------------------------------------------------------------------------------------- 0075 0076 typedef QPair<int, int> IntPair; 0077 typedef QList<IntPair> IntPairList; 0078 0079 class Q_DECL_HIDDEN ImportItemModelIncrementalUpdater 0080 { 0081 public: 0082 0083 explicit ImportItemModelIncrementalUpdater(ImportItemModel::Private* const d); 0084 0085 void appendInfos(const QList<CamItemInfo>& infos); 0086 void aboutToBeRemovedInModel(const IntPairList& aboutToBeRemoved); 0087 QList<IntPair> oldIndexes(); 0088 0089 static QList<IntPair> toContiguousPairs(const QList<int>& ids); 0090 0091 public: 0092 0093 QMultiHash<qlonglong, int> oldIds; 0094 QList<CamItemInfo> newInfos; 0095 QList<IntPairList> modelRemovals; 0096 }; 0097 0098 // ---------------------------------------------------------------------------------------------------- 0099 0100 ImportItemModel::ImportItemModel(QObject* const parent) 0101 : QAbstractListModel(parent), 0102 d (new Private) 0103 { 0104 } 0105 0106 ImportItemModel::~ImportItemModel() 0107 { 0108 delete d; 0109 } 0110 0111 void ImportItemModel::setCameraThumbsController(CameraThumbsCtrl* const thumbsCtrl) 0112 { 0113 d->controller = thumbsCtrl->cameraController(); 0114 0115 connect(d->controller, SIGNAL(signalFileList(CamItemInfoList)), 0116 SLOT(addCamItemInfos(CamItemInfoList))); 0117 0118 connect(d->controller, SIGNAL(signalDeleted(QString,QString,bool)), 0119 SLOT(slotFileDeleted(QString,QString,bool))); 0120 0121 connect(d->controller, SIGNAL(signalUploaded(CamItemInfo)), 0122 SLOT(slotFileUploaded(CamItemInfo))); 0123 } 0124 0125 void ImportItemModel::setKeepsFileUrlCache(bool keepCache) 0126 { 0127 d->keepFileUrlCache = keepCache; 0128 } 0129 0130 bool ImportItemModel::keepsFileUrlCache() const 0131 { 0132 return d->keepFileUrlCache; 0133 } 0134 0135 bool ImportItemModel::isEmpty() const 0136 { 0137 return d->infos.isEmpty(); 0138 } 0139 0140 CamItemInfo ImportItemModel::camItemInfo(const QModelIndex& index) const 0141 { 0142 if (!d->isValid(index)) 0143 { 0144 return CamItemInfo(); 0145 } 0146 0147 return d->infos.at(index.row()); 0148 } 0149 0150 CamItemInfo& ImportItemModel::camItemInfoRef(const QModelIndex& index) const 0151 { 0152 if (!d->isValid(index)) 0153 { 0154 return d->camItemInfo; 0155 } 0156 0157 return d->infos[index.row()]; 0158 } 0159 0160 qlonglong ImportItemModel::camItemId(const QModelIndex& index) const 0161 { 0162 if (!d->isValid(index)) 0163 { 0164 return -1; 0165 } 0166 0167 return d->infos.at(index.row()).id; 0168 } 0169 0170 QList<CamItemInfo> ImportItemModel::camItemInfos(const QList<QModelIndex>& indexes) const 0171 { 0172 QList<CamItemInfo> infos; 0173 0174 Q_FOREACH (const QModelIndex& index, indexes) 0175 { 0176 infos << camItemInfo(index); 0177 } 0178 0179 return infos; 0180 } 0181 0182 QList<qlonglong> ImportItemModel::camItemIds(const QList<QModelIndex>& indexes) const 0183 { 0184 QList<qlonglong> ids; 0185 0186 Q_FOREACH (const QModelIndex& index, indexes) 0187 { 0188 ids << camItemId(index); 0189 } 0190 0191 return ids; 0192 } 0193 0194 CamItemInfo ImportItemModel::camItemInfo(int row) const 0195 { 0196 if ((row < 0) || (row >= d->infos.size())) 0197 { 0198 return CamItemInfo(); 0199 } 0200 0201 return d->infos.at(row); 0202 } 0203 0204 CamItemInfo& ImportItemModel::camItemInfoRef(int row) const 0205 { 0206 if ((row < 0) || (row >= d->infos.size())) 0207 { 0208 return d->camItemInfo; 0209 } 0210 0211 return d->infos[row]; 0212 } 0213 0214 qlonglong ImportItemModel::camItemId(int row) const 0215 { 0216 if ((row < 0) || (row >= d->infos.size())) 0217 { 0218 return -1; 0219 } 0220 0221 return d->infos.at(row).id; 0222 } 0223 0224 QModelIndex ImportItemModel::indexForCamItemInfo(const CamItemInfo& info) const 0225 { 0226 return indexForCamItemId(info.id); 0227 } 0228 0229 QList<QModelIndex> ImportItemModel::indexesForCamItemInfo(const CamItemInfo& info) const 0230 { 0231 return indexesForCamItemId(info.id); 0232 } 0233 0234 QModelIndex ImportItemModel::indexForCamItemId(qlonglong id) const 0235 { 0236 int index = d->idHash.value(id, -1); 0237 0238 if (index == -1) 0239 { 0240 return QModelIndex(); 0241 } 0242 0243 return createIndex(index, 0); 0244 } 0245 0246 QList<QModelIndex> ImportItemModel::indexesForCamItemId(qlonglong id) const 0247 { 0248 QList<QModelIndex> indexes; 0249 0250 QMultiHash<qlonglong, int>::const_iterator it; 0251 0252 for (it = d->idHash.constFind(id) ; it != d->idHash.constEnd() && it.key() == id ; ++it) 0253 { 0254 indexes << createIndex(it.value(), 0); 0255 } 0256 0257 return indexes; 0258 } 0259 0260 int ImportItemModel::numberOfIndexesForCamItemInfo(const CamItemInfo& info) const 0261 { 0262 return numberOfIndexesForCamItemId(info.id); 0263 } 0264 0265 int ImportItemModel::numberOfIndexesForCamItemId(qlonglong id) const 0266 { 0267 int count = 0; 0268 QMultiHash<qlonglong,int>::const_iterator it; 0269 0270 for (it = d->idHash.constFind(id) ; it != d->idHash.constEnd() && it.key() == id ; ++it) 0271 { 0272 ++count; 0273 } 0274 0275 return count; 0276 } 0277 0278 // static method 0279 CamItemInfo ImportItemModel::retrieveCamItemInfo(const QModelIndex& index) 0280 { 0281 if (!index.isValid()) 0282 { 0283 return CamItemInfo(); 0284 } 0285 0286 ImportItemModel* const model = index.data(ImportItemModelPointerRole).value<ImportItemModel*>(); 0287 int row = index.data(ImportItemModelInternalId).toInt(); 0288 0289 if (!model) 0290 { 0291 return CamItemInfo(); 0292 } 0293 0294 return model->camItemInfo(row); 0295 } 0296 0297 // static method 0298 qlonglong ImportItemModel::retrieveCamItemId(const QModelIndex& index) 0299 { 0300 if (!index.isValid()) 0301 { 0302 return -1; 0303 } 0304 0305 ImportItemModel* const model = index.data(ImportItemModelPointerRole).value<ImportItemModel*>(); 0306 int row = index.data(ImportItemModelInternalId).toInt(); 0307 0308 if (!model) 0309 { 0310 return -1; 0311 } 0312 0313 return model->camItemId(row); 0314 } 0315 0316 QModelIndex ImportItemModel::indexForUrl(const QUrl& fileUrl) const 0317 { 0318 if (d->keepFileUrlCache) 0319 { 0320 return indexForCamItemId(d->fileUrlHash.value(fileUrl.toLocalFile())); 0321 } 0322 else 0323 { 0324 const int size = d->infos.size(); 0325 0326 for (int i = 0 ; i < size ; ++i) 0327 { 0328 if (d->infos.at(i).url() == fileUrl) 0329 { 0330 return createIndex(i, 0); 0331 } 0332 } 0333 } 0334 0335 return QModelIndex(); 0336 } 0337 0338 QList<QModelIndex> ImportItemModel::indexesForUrl(const QUrl& fileUrl) const 0339 { 0340 if (d->keepFileUrlCache) 0341 { 0342 return indexesForCamItemId(d->fileUrlHash.value(fileUrl.toLocalFile())); 0343 } 0344 else 0345 { 0346 QList<QModelIndex> indexes; 0347 const int size = d->infos.size(); 0348 0349 for (int i = 0 ; i < size ; ++i) 0350 { 0351 if (d->infos.at(i).url() == fileUrl) 0352 { 0353 indexes << createIndex(i, 0); 0354 } 0355 } 0356 0357 return indexes; 0358 } 0359 } 0360 0361 CamItemInfo ImportItemModel::camItemInfo(const QUrl& fileUrl) const 0362 { 0363 if (d->keepFileUrlCache) 0364 { 0365 qlonglong id = d->fileUrlHash.value(fileUrl.toLocalFile(), -1); 0366 0367 if (id != -1) 0368 { 0369 int index = d->idHash.value(id, -1); 0370 0371 if (index != -1) 0372 { 0373 return d->infos.at(index); 0374 } 0375 } 0376 } 0377 else 0378 { 0379 Q_FOREACH (const CamItemInfo& info, d->infos) 0380 { 0381 if (info.url() == fileUrl) 0382 { // cppcheck-suppress useStlAlgorithm 0383 return info; 0384 } 0385 } 0386 } 0387 0388 return CamItemInfo(); 0389 } 0390 0391 QList<CamItemInfo> ImportItemModel::camItemInfos(const QUrl& fileUrl) const 0392 { 0393 QList<CamItemInfo> infos; 0394 0395 if (d->keepFileUrlCache) 0396 { 0397 qlonglong id = d->fileUrlHash.value(fileUrl.toLocalFile(), -1); 0398 0399 if (id != -1) 0400 { 0401 Q_FOREACH (int index, d->idHash.values(id)) 0402 { 0403 infos << d->infos.at(index); 0404 } 0405 } 0406 } 0407 else 0408 { 0409 Q_FOREACH (const CamItemInfo& info, d->infos) 0410 { 0411 if (info.url() == fileUrl) 0412 { 0413 infos << info; 0414 } 0415 } 0416 } 0417 0418 return infos; 0419 } 0420 0421 void ImportItemModel::addCamItemInfo(const CamItemInfo& info) 0422 { 0423 addCamItemInfos(QList<CamItemInfo>() << info); 0424 } 0425 0426 void ImportItemModel::addCamItemInfos(const CamItemInfoList& infos) 0427 { 0428 if (infos.isEmpty()) 0429 { 0430 return; 0431 } 0432 0433 if (d->incrementalUpdater) 0434 { 0435 d->incrementalUpdater->appendInfos(infos); 0436 } 0437 else 0438 { 0439 appendInfos(infos); 0440 } 0441 } 0442 0443 void ImportItemModel::addCamItemInfoSynchronously(const CamItemInfo& info) 0444 { 0445 addCamItemInfosSynchronously(QList<CamItemInfo>() << info); 0446 } 0447 0448 void ImportItemModel::addCamItemInfosSynchronously(const CamItemInfoList& infos) 0449 { 0450 if (infos.isEmpty()) 0451 { 0452 return; 0453 } 0454 0455 publiciseInfos(infos); 0456 Q_EMIT processAdded(infos); 0457 } 0458 0459 void ImportItemModel::clearCamItemInfos() 0460 { 0461 beginResetModel(); 0462 0463 d->infos.clear(); 0464 d->idHash.clear(); 0465 d->fileUrlHash.clear(); 0466 0467 delete d->incrementalUpdater; 0468 0469 d->incrementalUpdater = nullptr; 0470 d->reAdding = false; 0471 d->refreshing = false; 0472 d->incrementalRefreshRequested = false; 0473 0474 camItemInfosCleared(); 0475 endResetModel(); 0476 } 0477 0478 // TODO unused 0479 void ImportItemModel::setCamItemInfos(const CamItemInfoList& infos) 0480 { 0481 clearCamItemInfos(); 0482 addCamItemInfos(infos); 0483 } 0484 0485 QList<CamItemInfo> ImportItemModel::camItemInfos() const 0486 { 0487 return d->infos; 0488 } 0489 0490 QList<qlonglong> ImportItemModel::camItemIds() const 0491 { 0492 return d->idHash.keys(); 0493 } 0494 0495 QList<CamItemInfo> ImportItemModel::uniqueCamItemInfos() const 0496 { 0497 QList<CamItemInfo> uniqueInfos; 0498 const int size = d->infos.size(); 0499 0500 for (int i = 0 ; i < size ; ++i) 0501 { 0502 const CamItemInfo& info = d->infos.at(i); 0503 0504 if (d->idHash.value(info.id) == i) 0505 { 0506 uniqueInfos << info; 0507 } 0508 } 0509 0510 return uniqueInfos; 0511 } 0512 0513 bool ImportItemModel::hasImage(qlonglong id) const 0514 { 0515 return d->idHash.contains(id); 0516 } 0517 0518 bool ImportItemModel::hasImage(const CamItemInfo& info) const 0519 { 0520 return d->fileUrlHash.contains(info.url().toLocalFile()); 0521 } 0522 0523 void ImportItemModel::emitDataChangedForAll() 0524 { 0525 if (d->infos.isEmpty()) 0526 { 0527 return; 0528 } 0529 0530 QModelIndex first = createIndex(0, 0); 0531 QModelIndex last = createIndex(d->infos.size() - 1, 0); 0532 Q_EMIT dataChanged(first, last); 0533 } 0534 0535 void ImportItemModel::emitDataChangedForSelections(const QItemSelection& selection) 0536 { 0537 if (!selection.isEmpty()) 0538 { 0539 Q_FOREACH (const QItemSelectionRange& range, selection) 0540 { 0541 Q_EMIT dataChanged(range.topLeft(), range.bottomRight()); 0542 } 0543 } 0544 } 0545 0546 void ImportItemModel::appendInfos(const CamItemInfoList& infos) 0547 { 0548 if (infos.isEmpty()) 0549 { 0550 return; 0551 } 0552 0553 publiciseInfos(infos); 0554 } 0555 0556 void ImportItemModel::reAddCamItemInfos(const CamItemInfoList& infos) 0557 { 0558 publiciseInfos(infos); 0559 } 0560 0561 void ImportItemModel::reAddingFinished() 0562 { 0563 d->reAdding = false; 0564 cleanSituationChecks(); 0565 } 0566 0567 void ImportItemModel::slotFileDeleted(const QString& folder, const QString& file, bool status) 0568 { 0569 Q_UNUSED(status) 0570 0571 QUrl url = QUrl::fromLocalFile(folder); 0572 url = url.adjusted(QUrl::StripTrailingSlash); 0573 url.setPath(url.path() + QLatin1Char('/') + file); 0574 CamItemInfo info = camItemInfo(url); 0575 removeCamItemInfo(info); 0576 } 0577 0578 void ImportItemModel::slotFileUploaded(const CamItemInfo& info) 0579 { 0580 addCamItemInfo(info); 0581 } 0582 0583 void ImportItemModel::startRefresh() 0584 { 0585 d->refreshing = true; 0586 } 0587 0588 void ImportItemModel::finishRefresh() 0589 { 0590 d->refreshing = false; 0591 cleanSituationChecks(); 0592 } 0593 0594 bool ImportItemModel::isRefreshing() const 0595 { 0596 return d->refreshing; 0597 } 0598 0599 void ImportItemModel::cleanSituationChecks() 0600 { 0601 // For starting an incremental refresh we want a clear situation: 0602 // Any remaining batches from non-incremental refreshing subclasses have been received in appendInfos(), 0603 // any batches sent to preprocessor for re-adding have been re-added. 0604 0605 if (d->refreshing || d->reAdding) 0606 { 0607 return; 0608 } 0609 0610 if (d->incrementalRefreshRequested) 0611 { 0612 d->incrementalRefreshRequested = false; 0613 Q_EMIT readyForIncrementalRefresh(); 0614 } 0615 else 0616 { 0617 Q_EMIT allRefreshingFinished(); 0618 } 0619 } 0620 0621 void ImportItemModel::publiciseInfos(const CamItemInfoList& infos) 0622 { 0623 if (infos.isEmpty()) 0624 { 0625 return; 0626 } 0627 0628 Q_EMIT itemInfosAboutToBeAdded(infos); 0629 0630 const int firstNewIndex = d->infos.size(); 0631 const int lastNewIndex = d->infos.size() + infos.size() -1; 0632 beginInsertRows(QModelIndex(), firstNewIndex, lastNewIndex); 0633 d->infos << infos; 0634 0635 for (int i = firstNewIndex ; i <= lastNewIndex ; ++i) 0636 { 0637 CamItemInfo& info = d->infos[i]; 0638 0639 // TODO move this to a separate thread, see CameraHistoryUpdater 0640 // TODO can we/do we want to differentiate at all between whether the status is unknown and not downloaded? 0641 0642 info.downloaded = CoreDbDownloadHistory::status(QString::fromUtf8(d->controller->cameraMD5ID()), 0643 info.name, info.size, info.ctime); 0644 0645 // TODO is this safe? if so, is there a need to store this inside idHash separately? 0646 0647 info.id = i; 0648 qlonglong id = info.id; 0649 d->idHash.insert(id, i); 0650 0651 if (d->keepFileUrlCache) 0652 { 0653 d->fileUrlHash[info.url().toLocalFile()] = id; 0654 } 0655 } 0656 0657 endInsertRows(); 0658 Q_EMIT processAdded(infos); 0659 Q_EMIT itemInfosAdded(infos); 0660 } 0661 0662 void ImportItemModel::requestIncrementalRefresh() 0663 { 0664 if (d->reAdding) 0665 { 0666 d->incrementalRefreshRequested = true; 0667 } 0668 else 0669 { 0670 Q_EMIT readyForIncrementalRefresh(); 0671 } 0672 } 0673 0674 bool ImportItemModel::hasIncrementalRefreshPending() const 0675 { 0676 return d->incrementalRefreshRequested; 0677 } 0678 0679 void ImportItemModel::startIncrementalRefresh() 0680 { 0681 delete d->incrementalUpdater; 0682 0683 d->incrementalUpdater = new ImportItemModelIncrementalUpdater(d); 0684 } 0685 0686 void ImportItemModel::finishIncrementalRefresh() 0687 { 0688 if (!d->incrementalUpdater) 0689 { 0690 return; 0691 } 0692 0693 // remove old entries 0694 0695 QList<QPair<int, int> > pairs = d->incrementalUpdater->oldIndexes(); 0696 removeRowPairs(pairs); 0697 0698 // add new indexes 0699 0700 appendInfos(d->incrementalUpdater->newInfos); 0701 0702 delete d->incrementalUpdater; 0703 d->incrementalUpdater = nullptr; 0704 } 0705 /* 0706 template <class List, typename T> 0707 static bool pairsContain(const List& list, T value) 0708 { 0709 typename List::const_iterator middle; 0710 typename List::const_iterator begin = list.begin(); 0711 typename List::const_iterator end = list.end(); 0712 int n = int(end - begin); 0713 0714 while (n > 0) 0715 { 0716 int half = (n >> 1); 0717 middle = begin + half; 0718 0719 if ((middle->first <= value) && (middle->second >= value)) 0720 { 0721 return true; 0722 } 0723 else if (middle->second < value) 0724 { 0725 begin = middle + 1; 0726 n -= half + 1; 0727 } 0728 else 0729 { 0730 n = half; 0731 } 0732 } 0733 0734 return false; 0735 } 0736 */ 0737 void ImportItemModel::removeIndex(const QModelIndex& index) 0738 { 0739 removeIndexs(QList<QModelIndex>() << index); 0740 } 0741 0742 void ImportItemModel::removeIndexs(const QList<QModelIndex>& indexes) 0743 { 0744 QList<int> indexesList; 0745 0746 Q_FOREACH (const QModelIndex& index, indexes) 0747 { 0748 if (d->isValid(index)) 0749 { 0750 indexesList << index.row(); 0751 } 0752 } 0753 0754 if (indexesList.isEmpty()) 0755 { 0756 return; 0757 } 0758 0759 removeRowPairsWithCheck(ImportItemModelIncrementalUpdater::toContiguousPairs(indexesList)); 0760 } 0761 0762 void ImportItemModel::removeCamItemInfo(const CamItemInfo& info) 0763 { 0764 removeCamItemInfos(QList<CamItemInfo>() << info); 0765 } 0766 0767 void ImportItemModel::removeCamItemInfos(const QList<CamItemInfo>& infos) 0768 { 0769 QList<int> indexesList; 0770 0771 Q_FOREACH (const CamItemInfo& info, infos) 0772 { 0773 QModelIndex index = indexForCamItemId(info.id); 0774 0775 if (index.isValid()) 0776 { 0777 indexesList << index.row(); 0778 } 0779 } 0780 0781 removeRowPairsWithCheck(ImportItemModelIncrementalUpdater::toContiguousPairs(indexesList)); 0782 } 0783 0784 void ImportItemModel::setSendRemovalSignals(bool send) 0785 { 0786 d->sendRemovalSignals = send; 0787 } 0788 0789 void ImportItemModel::removeRowPairsWithCheck(const QList<QPair<int, int> >& toRemove) 0790 { 0791 if (d->incrementalUpdater) 0792 { 0793 d->incrementalUpdater->aboutToBeRemovedInModel(toRemove); 0794 } 0795 0796 removeRowPairs(toRemove); 0797 } 0798 0799 void ImportItemModel::removeRowPairs(const QList<QPair<int, int> >& toRemove) 0800 { 0801 if (toRemove.isEmpty()) 0802 { 0803 return; 0804 } 0805 0806 // Remove old indexes 0807 // Keep in mind that when calling beginRemoveRows all structures announced to be removed 0808 // must still be valid, and this includes our hashes as well, which limits what we can optimize 0809 0810 int removedRows = 0; 0811 int offset = 0; 0812 QList<qlonglong> removeFileUrls; 0813 0814 Q_FOREACH (const IntPair& pair, toRemove) 0815 { 0816 const int begin = pair.first - offset; 0817 const int end = pair.second - offset; 0818 removedRows = end - begin + 1; 0819 0820 // when removing from the list, all subsequent indexes are affected 0821 0822 offset += removedRows; 0823 0824 QList<CamItemInfo> removedInfos; 0825 0826 if (d->sendRemovalSignals) 0827 { 0828 // cppcheck-suppress knownEmptyContainer 0829 std::copy(d->infos.begin() + begin, d->infos.begin() + end, removedInfos.begin()); 0830 Q_EMIT itemInfosAboutToBeRemoved(removedInfos); 0831 } 0832 0833 itemInfosAboutToBeRemoved(begin, end); 0834 beginRemoveRows(QModelIndex(), begin, end); 0835 0836 // update idHash - which points to indexes of d->infos 0837 0838 QMultiHash<qlonglong, int>::iterator it; 0839 0840 for (it = d->idHash.begin() ; it != d->idHash.end() ; ) 0841 { 0842 if (it.value() >= begin) 0843 { 0844 if (it.value() > end) 0845 { 0846 // after the removed interval, adjust index 0847 0848 it.value() -= removedRows; 0849 } 0850 else 0851 { 0852 // in the removed interval 0853 0854 removeFileUrls << it.key(); 0855 it = d->idHash.erase(it); 0856 continue; 0857 } 0858 } 0859 0860 ++it; 0861 } 0862 0863 // remove from list 0864 0865 d->infos.erase(d->infos.begin() + begin, d->infos.begin() + (end + 1)); 0866 0867 endRemoveRows(); 0868 0869 if (d->sendRemovalSignals) 0870 { 0871 Q_EMIT itemInfosRemoved(removedInfos); 0872 } 0873 } 0874 0875 // tidy up: remove old indexes from file path hash now 0876 0877 if (d->keepFileUrlCache) 0878 { 0879 QHash<QString, qlonglong>::iterator it; 0880 0881 for (it = d->fileUrlHash.begin() ; it != d->fileUrlHash.end() ; ) 0882 { 0883 if (removeFileUrls.contains(it.value())) 0884 { 0885 it = d->fileUrlHash.erase(it); 0886 } 0887 else 0888 { 0889 ++it; 0890 } 0891 } 0892 } 0893 } 0894 0895 // ------------ ImportItemModelIncrementalUpdater ------------ 0896 0897 ImportItemModelIncrementalUpdater::ImportItemModelIncrementalUpdater(ImportItemModel::Private* const d) 0898 : oldIds(d->idHash) 0899 { 0900 } 0901 0902 void ImportItemModelIncrementalUpdater::aboutToBeRemovedInModel(const IntPairList& toRemove) 0903 { 0904 modelRemovals << toRemove; 0905 } 0906 0907 void ImportItemModelIncrementalUpdater::appendInfos(const QList<CamItemInfo>& infos) 0908 { 0909 for (int i = 0 ; i < infos.size() ; ++i) 0910 { 0911 const CamItemInfo& info = infos.at(i); 0912 bool found = false; 0913 QMultiHash<qlonglong, int>::iterator it; 0914 0915 for (it = oldIds.find(info.id) ; it != oldIds.end() ; ++it) 0916 { 0917 if (it.key() == info.id) 0918 { 0919 found = true; 0920 break; 0921 } 0922 } 0923 0924 if (found) 0925 { 0926 oldIds.erase(it); 0927 } 0928 else 0929 { 0930 newInfos << info; 0931 } 0932 } 0933 } 0934 0935 QList<QPair<int, int> > ImportItemModelIncrementalUpdater::toContiguousPairs(const QList<int>& unsorted) 0936 { 0937 // Take the given indices and return them as contiguous pairs [begin, end] 0938 0939 QList<QPair<int, int> > pairs; 0940 0941 if (unsorted.isEmpty()) 0942 { 0943 return pairs; 0944 } 0945 0946 QList<int> indices(unsorted); 0947 std::sort(indices.begin(), indices.end()); 0948 0949 QPair<int, int> pair(indices.first(), indices.first()); 0950 0951 for (int i = 1 ; i < indices.size() ; ++i) 0952 { 0953 const int& index = indices.at(i); 0954 0955 if (index == (pair.second + 1)) 0956 { 0957 pair.second = index; 0958 continue; 0959 } 0960 0961 pairs << pair; // insert last pair 0962 pair.first = index; 0963 pair.second = index; 0964 } 0965 0966 pairs << pair; 0967 0968 return pairs; 0969 } 0970 0971 QList<QPair<int, int> > ImportItemModelIncrementalUpdater::oldIndexes() 0972 { 0973 // first, apply all changes to indexes by direct removal in model 0974 // while the updater was active 0975 0976 Q_FOREACH (const IntPairList& list, modelRemovals) 0977 { 0978 int removedRows = 0; 0979 int offset = 0; 0980 0981 Q_FOREACH (const IntPair& pair, list) 0982 { 0983 const int begin = pair.first - offset; 0984 const int end = pair.second - offset; // inclusive 0985 removedRows = end - begin + 1; 0986 0987 // when removing from the list, all subsequent indexes are affected 0988 0989 offset += removedRows; 0990 0991 // update idHash - which points to indexes of d->infos, and these change now! 0992 0993 QMultiHash<qlonglong, int>::iterator it; 0994 0995 for (it = oldIds.begin() ; it != oldIds.end() ; ) 0996 { 0997 if (it.value() >= begin) 0998 { 0999 if (it.value() > end) 1000 { 1001 // after the removed interval: adjust index 1002 1003 it.value() -= removedRows; 1004 } 1005 else 1006 { 1007 // in the removed interval 1008 1009 it = oldIds.erase(it); 1010 continue; 1011 } 1012 } 1013 1014 ++it; 1015 } 1016 } 1017 } 1018 1019 modelRemovals.clear(); 1020 1021 return toContiguousPairs(oldIds.values()); 1022 } 1023 1024 // ------------ QAbstractItemModel implementation ------------- 1025 1026 QVariant ImportItemModel::data(const QModelIndex& index, int role) const 1027 { 1028 if (!d->isValid(index)) 1029 { 1030 return QVariant(); 1031 } 1032 1033 switch (role) 1034 { 1035 case Qt::DisplayRole: 1036 case Qt::ToolTipRole: 1037 { 1038 return d->infos.at(index.row()).name; 1039 } 1040 1041 case ImportItemModelPointerRole: 1042 { 1043 return QVariant::fromValue(const_cast<ImportItemModel*>(this)); 1044 } 1045 1046 case ImportItemModelInternalId: 1047 { 1048 return index.row(); 1049 } 1050 } 1051 1052 return QVariant(); 1053 } 1054 1055 QVariant ImportItemModel::headerData(int section, Qt::Orientation orientation, int role) const 1056 { 1057 Q_UNUSED(section) 1058 Q_UNUSED(orientation) 1059 Q_UNUSED(role) 1060 1061 return QVariant(); 1062 } 1063 1064 int ImportItemModel::rowCount(const QModelIndex& parent) const 1065 { 1066 if (parent.isValid()) 1067 { 1068 return 0; 1069 } 1070 1071 return d->infos.size(); 1072 } 1073 1074 Qt::ItemFlags ImportItemModel::flags(const QModelIndex& index) const 1075 { 1076 if (!d->isValid(index)) 1077 { 1078 return Qt::NoItemFlags; 1079 } 1080 1081 Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled; 1082 1083 f |= dragDropFlags(index); 1084 1085 return f; 1086 } 1087 1088 QModelIndex ImportItemModel::index(int row, int column, const QModelIndex& parent) const 1089 { 1090 if ((column != 0) || (row < 0) || parent.isValid() || (row >= d->infos.size())) 1091 { 1092 return QModelIndex(); 1093 } 1094 1095 return createIndex(row, 0); 1096 } 1097 1098 } // namespace Digikam 1099 1100 #include "moc_importimagemodel.cpp"