File indexing completed on 2025-01-19 03:53:47
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2005-05-17 0007 * Description : low level files management interface. 0008 * 0009 * SPDX-FileCopyrightText: 2005 by Renchi Raju <renchi dot raju at gmail dot com> 0010 * SPDX-FileCopyrightText: 2012-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0011 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0012 * SPDX-FileCopyrightText: 2018 by Maik Qualmann <metzpinguin at gmail dot com> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "dio.h" 0019 0020 // Qt includes 0021 0022 #include <QDir> 0023 #include <QFileInfo> 0024 #include <QMessageBox> 0025 #include <QMutexLocker> 0026 #include <QApplication> 0027 #include <QAbstractButton> 0028 0029 // KDE includes 0030 0031 #include <klocalizedstring.h> 0032 0033 // Local includes 0034 0035 #include "digikam_debug.h" 0036 #include "iteminfo.h" 0037 #include "diofinders.h" 0038 #include "albummanager.h" 0039 #include "coredb.h" 0040 #include "coredbaccess.h" 0041 #include "coredbtransaction.h" 0042 #include "album.h" 0043 #include "dmetadata.h" 0044 #include "metaenginesettings.h" 0045 #include "scancontroller.h" 0046 #include "itemscanner.h" 0047 #include "thumbsdb.h" 0048 #include "thumbsdbaccess.h" 0049 #include "iojobsmanager.h" 0050 #include "collectionmanager.h" 0051 #include "collectionlocation.h" 0052 #include "dnotificationwrapper.h" 0053 #include "loadingcacheinterface.h" 0054 #include "progressmanager.h" 0055 #include "digikamapp.h" 0056 #include "iojobdata.h" 0057 0058 namespace Digikam 0059 { 0060 0061 class Q_DECL_HIDDEN DIOCreator 0062 { 0063 public: 0064 0065 DIO object; 0066 }; 0067 0068 Q_GLOBAL_STATIC(DIOCreator, creator) 0069 0070 // ------------------------------------------------------------------------------------------------ 0071 0072 DIO* DIO::instance() 0073 { 0074 return &creator->object; 0075 } 0076 0077 DIO::DIO() 0078 { 0079 qRegisterMetaType<QMap<QString,int>>("QMap<QString,int>"); 0080 0081 m_processingCount = 0; 0082 } 0083 0084 DIO::~DIO() 0085 { 0086 } 0087 0088 void DIO::cleanUp() 0089 { 0090 } 0091 0092 bool DIO::itemsUnderProcessing() 0093 { 0094 return instance()->m_processingCount; 0095 } 0096 0097 // Album -> Album ----------------------------------------------------- 0098 0099 void DIO::copy(PAlbum* const src, PAlbum* const dest) 0100 { 0101 if (!src || !dest) 0102 { 0103 return; 0104 } 0105 0106 instance()->processJob(new IOJobData(IOJobData::CopyAlbum, src, dest)); 0107 } 0108 0109 void DIO::move(PAlbum* const src, PAlbum* const dest) 0110 { 0111 if (!src || !dest) 0112 { 0113 return; 0114 } 0115 0116 #ifdef Q_OS_WIN 0117 0118 AlbumManager::instance()->removeWatchedPAlbums(src); 0119 0120 #endif 0121 0122 instance()->processJob(new IOJobData(IOJobData::MoveAlbum, src, dest)); 0123 } 0124 0125 // Images -> Album ---------------------------------------------------- 0126 0127 void DIO::copy(const QList<ItemInfo>& infos, PAlbum* const dest) 0128 { 0129 if (!dest) 0130 { 0131 return; 0132 } 0133 0134 instance()->processJob(new IOJobData(IOJobData::CopyImage, infos, dest)); 0135 } 0136 0137 void DIO::move(const QList<ItemInfo>& infos, PAlbum* const dest) 0138 { 0139 if (!dest) 0140 { 0141 return; 0142 } 0143 0144 instance()->processJob(new IOJobData(IOJobData::MoveImage, infos, dest)); 0145 } 0146 0147 // External files -> album -------------------------------------------- 0148 0149 void DIO::copy(const QUrl& src, PAlbum* const dest) 0150 { 0151 copy(QList<QUrl>() << src, dest); 0152 } 0153 0154 void DIO::copy(const QList<QUrl>& srcList, PAlbum* const dest) 0155 { 0156 if (!dest) 0157 { 0158 return; 0159 } 0160 0161 instance()->processJob(new IOJobData(IOJobData::CopyFiles, srcList, dest)); 0162 } 0163 0164 void DIO::move(const QUrl& src, PAlbum* const dest) 0165 { 0166 move(QList<QUrl>() << src, dest); 0167 } 0168 0169 void DIO::move(const QList<QUrl>& srcList, PAlbum* const dest) 0170 { 0171 if (!dest) 0172 { 0173 return; 0174 } 0175 0176 instance()->processJob(new IOJobData(IOJobData::MoveFiles, srcList, dest)); 0177 } 0178 0179 // Images -> External ------------------------------------------------- 0180 0181 void DIO::copy(const QList<ItemInfo>& infos, const QUrl& dest) 0182 { 0183 instance()->processJob(new IOJobData(IOJobData::CopyToExt, infos, dest)); 0184 } 0185 0186 // Rename ------------------------------------------------------------- 0187 0188 void DIO::rename(const QUrl& src, const QString& newName, bool overwrite) 0189 { 0190 if (src.isEmpty() || newName.isEmpty()) 0191 { 0192 return; 0193 } 0194 0195 ItemInfo info = ItemInfo::fromUrl(src); 0196 0197 instance()->processJob(new IOJobData(IOJobData::Rename, info, newName, overwrite)); 0198 } 0199 0200 // Delete ------------------------------------------------------------- 0201 0202 void DIO::del(const QList<ItemInfo>& infos, bool useTrash) 0203 { 0204 instance()->processJob(new IOJobData(useTrash ? IOJobData::Trash 0205 : IOJobData::Delete, infos)); 0206 } 0207 0208 void DIO::del(const ItemInfo& info, bool useTrash) 0209 { 0210 del(QList<ItemInfo>() << info, useTrash); 0211 } 0212 0213 void DIO::del(PAlbum* const album, bool useTrash) 0214 { 0215 if (!album) 0216 { 0217 return; 0218 } 0219 0220 #ifdef Q_OS_WIN 0221 0222 AlbumManager::instance()->removeWatchedPAlbums(album); 0223 0224 #endif 0225 0226 instance()->createJob(new IOJobData(useTrash ? IOJobData::Trash 0227 : IOJobData::Delete, album)); 0228 } 0229 0230 // Get Trash Counter -------------------------------------------------- 0231 0232 int DIO::getTrashCounter(const QString& albumRootPath) 0233 { 0234 QUrl url = QUrl::fromLocalFile(albumRootPath); 0235 url = url.adjusted(QUrl::StripTrailingSlash); 0236 0237 QMutexLocker locker(&instance()->m_trashCounterMutex); 0238 0239 return instance()->m_trashCounterMap.value(url.toLocalFile(), 0); 0240 } 0241 0242 // Restore Trash ------------------------------------------------------ 0243 0244 void DIO::restoreTrash(const DTrashItemInfoList& infos) 0245 { 0246 instance()->createJob(new IOJobData(IOJobData::Restore, infos)); 0247 } 0248 0249 // Empty Trash -------------------------------------------------------- 0250 0251 void DIO::emptyTrash(const DTrashItemInfoList& infos) 0252 { 0253 instance()->createJob(new IOJobData(IOJobData::Empty, infos)); 0254 } 0255 0256 // Build Trash Counters ----------------------------------------------- 0257 0258 void DIO::buildCollectionTrashCounters() 0259 { 0260 IOJobsThread* const jobThread = IOJobsManager::instance()->buildCollectionTrashCounters(); 0261 0262 connect(jobThread, SIGNAL(signalTrashCountersMap(QMap<QString,int>)), 0263 instance(), SLOT(slotTrashCounterMap(QMap<QString,int>))); 0264 } 0265 0266 // ------------------------------------------------------------------------------------------------ 0267 0268 void DIO::processJob(IOJobData* const data) 0269 { 0270 const int operation = data->operation(); 0271 0272 if ((operation == IOJobData::CopyImage) || 0273 (operation == IOJobData::MoveImage)) 0274 { 0275 // this is a fast db operation, do here 0276 0277 GroupedImagesFinder finder(data->itemInfos()); 0278 data->setItemInfos(finder.infos); 0279 } 0280 else if ((operation == IOJobData::Trash) || 0281 (operation == IOJobData::Delete)) 0282 { 0283 SidecarFinder finder(data->sourceUrls()); 0284 data->setSourceUrls(finder.localFiles); 0285 0286 qCDebug(DIGIKAM_DATABASE_LOG) << "Number of files to be deleted:" << data->sourceUrls().count(); 0287 } 0288 else if (operation == IOJobData::Rename) 0289 { 0290 SidecarFinder finder(data->sourceUrls()); 0291 data->setSourceUrls(finder.localFiles); 0292 0293 for (int i = 0 ; i < finder.localFiles.size() ; ++i) 0294 { 0295 if (finder.localFileModes.at(i)) 0296 { 0297 data->setDestUrl(finder.localFiles.at(i), 0298 QUrl::fromLocalFile(data->destUrl().toLocalFile() + 0299 finder.localFileSuffixes.at(i))); 0300 } 0301 else 0302 { 0303 QFileInfo basInfo(data->destUrl().toLocalFile()); 0304 0305 data->setDestUrl(finder.localFiles.at(i), 0306 QUrl::fromLocalFile(basInfo.path() + 0307 QLatin1Char('/') + 0308 basInfo.completeBaseName() + 0309 finder.localFileSuffixes.at(i))); 0310 } 0311 } 0312 } 0313 0314 createJob(data); 0315 } 0316 0317 void DIO::createJob(IOJobData* const data) 0318 { 0319 if (data->sourceUrls().isEmpty()) 0320 { 0321 delete data; 0322 0323 return; 0324 } 0325 0326 const int operation = data->operation(); 0327 0328 if ((operation == IOJobData::CopyImage) || (operation == IOJobData::CopyAlbum) || 0329 (operation == IOJobData::CopyFiles) || (operation == IOJobData::CopyToExt) || 0330 (operation == IOJobData::MoveImage) || (operation == IOJobData::MoveAlbum) || 0331 (operation == IOJobData::MoveFiles)) 0332 { 0333 CollectionLocation location = CollectionManager::instance()->locationForUrl(data->destUrl()); 0334 Qt::CaseSensitivity caseSensitivity = location.asQtCaseSensitivity(); 0335 QDir dir(data->destUrl().toLocalFile()); 0336 0337 const QStringList& dirList = dir.entryList(QDir::Dirs | 0338 QDir::Files | 0339 QDir::NoDotAndDotDot); 0340 0341 Q_FOREACH (const QUrl& url, data->sourceUrls()) 0342 { 0343 if (dirList.contains(url.adjusted(QUrl::StripTrailingSlash).fileName(), caseSensitivity)) 0344 { 0345 QPointer<QMessageBox> msgBox = new QMessageBox(QMessageBox::Warning, 0346 i18nc("@title:window", "File Conflict"), 0347 i18n("Files or folders with the same name already exist in the target folder.\n\n" 0348 "What action is applied in the event of a file conflict?\n\n" 0349 "Available options are:\n" 0350 "Rename automatically: conflicting files will be renamed.\n" 0351 "Overwrite automatically: conflicting files will be overwritten.\n" 0352 "Skip automatically: conflicting files will be skipped."), 0353 QMessageBox::Yes | QMessageBox::No | QMessageBox::Ok | QMessageBox::Cancel, 0354 qApp->activeWindow()); 0355 0356 msgBox->button(QMessageBox::Yes)->setText(i18nc("@action:button", "Rename Automatically")); 0357 msgBox->button(QMessageBox::Yes)->setIcon(QIcon::fromTheme(QLatin1String("document-edit"))); 0358 msgBox->button(QMessageBox::No)->setText(i18nc("@action:button", "Overwrite Automatically")); 0359 msgBox->button(QMessageBox::No)->setIcon(QIcon::fromTheme(QLatin1String("edit-copy"))); 0360 msgBox->button(QMessageBox::Ok)->setText(i18nc("@action:button", "Skip Automatically")); 0361 msgBox->button(QMessageBox::Ok)->setIcon(QIcon::fromTheme(QLatin1String("go-next"))); 0362 0363 if ((operation == IOJobData::CopyAlbum) || (operation == IOJobData::MoveAlbum)) 0364 { 0365 msgBox->button(QMessageBox::No)->hide(); 0366 } 0367 0368 int result = msgBox->exec(); 0369 delete msgBox; 0370 0371 if (result == QMessageBox::Cancel) 0372 { 0373 delete data; 0374 0375 return; 0376 } 0377 else if (result == QMessageBox::Yes) 0378 { 0379 data->setFileConflict(IOJobData::AutoRename); 0380 } 0381 else if (result == QMessageBox::No) 0382 { 0383 data->setFileConflict(IOJobData::Overwrite); 0384 } 0385 0386 break; 0387 } 0388 } 0389 } 0390 0391 ProgressItem* item = nullptr; 0392 QString itemString = getItemString(data); 0393 0394 if (!itemString.isEmpty()) 0395 { 0396 item = ProgressManager::instance()->createProgressItem(itemString, 0397 QString(), true, false); 0398 item->setTotalItems(data->sourceUrls().count()); 0399 data->setProgressId(item->id()); 0400 } 0401 0402 IOJobsThread* const jobThread = IOJobsManager::instance()->startIOJobs(data); 0403 0404 connect(jobThread, SIGNAL(signalOneProccessed(QUrl)), 0405 this, SLOT(slotOneProccessed(QUrl))); 0406 0407 connect(jobThread, SIGNAL(signalFinished()), 0408 this, SLOT(slotResult())); 0409 0410 if (operation == IOJobData::Rename) 0411 { 0412 connect(jobThread, SIGNAL(signalRenameFailed(QUrl)), 0413 this, SIGNAL(signalRenameFailed(QUrl))); 0414 0415 connect(jobThread, SIGNAL(signalFinished()), 0416 this, SIGNAL(signalRenameFinished())); 0417 } 0418 0419 if ((operation == IOJobData::Empty) || 0420 (operation == IOJobData::Restore)) 0421 { 0422 connect(jobThread, SIGNAL(signalFinished()), 0423 this, SIGNAL(signalTrashFinished())); 0424 } 0425 0426 if (item) 0427 { 0428 connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), 0429 jobThread, SLOT(slotCancel())); 0430 0431 connect(item, SIGNAL(progressItemCanceled(ProgressItem*)), 0432 this, SLOT(slotCancel(ProgressItem*))); 0433 } 0434 0435 ++m_processingCount; 0436 } 0437 0438 void DIO::slotResult() 0439 { 0440 IOJobsThread* const jobThread = dynamic_cast<IOJobsThread*>(sender()); 0441 0442 if (!jobThread || !jobThread->jobData()) 0443 { 0444 return; 0445 } 0446 0447 IOJobData* const data = jobThread->jobData(); 0448 const int operation = data->operation(); 0449 0450 if (jobThread->hasErrors() && operation != IOJobData::Rename) 0451 { 0452 // Pop-up a message about the error. 0453 0454 QString errors = jobThread->errorsList().join(QLatin1Char('\n')); 0455 DNotificationWrapper(QString(), errors, DigikamApp::instance(), 0456 DigikamApp::instance()->windowTitle()); 0457 } 0458 else 0459 { 0460 if ((operation == IOJobData::CopyImage) || 0461 (operation == IOJobData::MoveImage)) 0462 { 0463 if (data->destAlbum()) 0464 { 0465 updateAlbumDate(data->destAlbum()->id()); 0466 } 0467 } 0468 0469 if ((operation == IOJobData::Trash) || 0470 (operation == IOJobData::Delete) || 0471 (operation == IOJobData::MoveImage)) 0472 { 0473 Q_FOREACH (int albumID, data->srcAlbumIds()) 0474 { 0475 updateAlbumDate(albumID); 0476 } 0477 } 0478 0479 if (data->errorOrCancel()) 0480 { 0481 if ((operation == IOJobData::CopyAlbum) || 0482 (operation == IOJobData::MoveAlbum)) 0483 { 0484 QString scanPath = data->destUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0485 ScanController::instance()->scheduleCollectionScanRelaxed(scanPath); 0486 } 0487 } 0488 0489 if ((operation == IOJobData::Trash) || 0490 (operation == IOJobData::Empty) || 0491 (operation == IOJobData::Restore)) 0492 { 0493 buildCollectionTrashCounters(); 0494 } 0495 } 0496 0497 if (m_processingCount) 0498 { 0499 --m_processingCount; 0500 } 0501 0502 slotCancel(getProgressItem(data)); 0503 } 0504 0505 void DIO::slotOneProccessed(const QUrl& url) 0506 { 0507 IOJobsThread* const jobThread = dynamic_cast<IOJobsThread*>(sender()); 0508 0509 if (!jobThread || !jobThread->jobData()) 0510 { 0511 return; 0512 } 0513 0514 IOJobData* const data = jobThread->jobData(); 0515 const int operation = data->operation(); 0516 0517 switch (operation) 0518 { 0519 case IOJobData::CopyImage: 0520 { 0521 ItemInfo info = data->findItemInfo(url); 0522 0523 if (!info.isNull() && data->destAlbum()) 0524 { 0525 CoreDbAccess access; 0526 CoreDbTransaction transaction(&access); 0527 0528 qlonglong id = access.db()->copyItem(info.albumId(), info.name(), 0529 data->destAlbum()->id(), data->destName(url)); 0530 0531 // Remove grouping for copied items. 0532 0533 access.db()->removeAllImageRelationsFrom(id, DatabaseRelation::Grouped); 0534 access.db()->removeAllImageRelationsTo(id, DatabaseRelation::Grouped); 0535 } 0536 0537 break; 0538 } 0539 0540 case IOJobData::MoveImage: 0541 { 0542 ItemInfo info = data->findItemInfo(url); 0543 0544 if (!info.isNull() && data->destAlbum()) 0545 { 0546 CoreDbAccess().db()->moveItem(info.albumId(), info.name(), 0547 data->destAlbum()->id(), data->destName(url)); 0548 } 0549 0550 break; 0551 } 0552 0553 case IOJobData::CopyFiles: 0554 case IOJobData::MoveFiles: 0555 { 0556 QString scanPath = data->destUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0557 scanPath += QLatin1Char('/') + data->destName(url); 0558 0559 QFileInfo scanInfo(scanPath); 0560 0561 if (scanInfo.isDir()) 0562 { 0563 ScanController::instance()->scheduleCollectionScanRelaxed(scanPath); 0564 } 0565 else 0566 { 0567 ScanController::instance()->scannedInfo(scanPath); 0568 } 0569 0570 break; 0571 0572 } 0573 0574 case IOJobData::CopyToExt: 0575 { 0576 CollectionLocation location = CollectionManager::instance()->locationForUrl(data->destUrl()); 0577 0578 // The target of the copy is within the digiKam collections? 0579 0580 if (!location.isNull()) 0581 { 0582 QString filePath = data->destUrl().adjusted(QUrl::StripTrailingSlash).toLocalFile(); 0583 filePath += QLatin1Char('/') + data->destName(url); 0584 ScanController::instance()->scannedInfo(filePath); 0585 } 0586 0587 break; 0588 } 0589 0590 case IOJobData::CopyAlbum: 0591 { 0592 if (data->srcAlbum() && data->destAlbum()) 0593 { 0594 CoreDbAccess access; 0595 QList<int> albumsToCopy; 0596 QString newName = data->destName(url); 0597 QString basePath = data->srcAlbum()->albumPath(); 0598 QString destPath = data->destAlbum()->albumPath(); 0599 0600 if (!destPath.endsWith(QLatin1Char('/'))) 0601 { 0602 destPath.append(QLatin1Char('/')); 0603 } 0604 0605 addAlbumChildrenToList(albumsToCopy, data->srcAlbum()); 0606 0607 Q_FOREACH (int albumId, albumsToCopy) 0608 { 0609 QString relativePath = access.db()->getAlbumRelativePath(albumId); 0610 relativePath = relativePath.section(basePath, 1, -1); 0611 relativePath = destPath + newName + relativePath; 0612 0613 int copyId = access.db()->addAlbum(data->srcAlbum()->albumRootId(), 0614 relativePath, QString(), QDate(), QString()); 0615 access.db()->copyAlbumProperties(albumId, copyId); 0616 const QList<qlonglong>& imageIds = access.db()->getItemIDsInAlbum(albumId); 0617 0618 Q_FOREACH (const qlonglong& id, imageIds) 0619 { 0620 QString name = access.db()->getItemName(id); 0621 qlonglong itemId = access.db()->copyItem(albumId, name, copyId, name); 0622 0623 // Remove grouping for copied items. 0624 0625 access.db()->removeAllImageRelationsFrom(itemId, DatabaseRelation::Grouped); 0626 access.db()->removeAllImageRelationsTo(itemId, DatabaseRelation::Grouped); 0627 } 0628 } 0629 } 0630 0631 break; 0632 } 0633 0634 case IOJobData::MoveAlbum: 0635 { 0636 if (data->srcAlbum() && data->destAlbum()) 0637 { 0638 CoreDbAccess access; 0639 QList<int> albumsToMove; 0640 QString newName = data->destName(url); 0641 QString basePath = data->srcAlbum()->albumPath(); 0642 QString destPath = data->destAlbum()->albumPath(); 0643 0644 if (!destPath.endsWith(QLatin1Char('/'))) 0645 { 0646 destPath.append(QLatin1Char('/')); 0647 } 0648 0649 addAlbumChildrenToList(albumsToMove, data->srcAlbum()); 0650 0651 Q_FOREACH (int albumId, albumsToMove) 0652 { 0653 QString relativePath = access.db()->getAlbumRelativePath(albumId); 0654 relativePath = relativePath.section(basePath, 1, -1); 0655 relativePath = destPath + newName + relativePath; 0656 access.db()->renameAlbum(albumId, data->destAlbum()->albumRootId(), relativePath); 0657 } 0658 } 0659 0660 break; 0661 } 0662 0663 case IOJobData::Trash: 0664 case IOJobData::Delete: 0665 { 0666 // Mark the images as obsolete and remove them 0667 // from their album and from the grouped 0668 0669 PAlbum* const album = data->srcAlbum(); 0670 0671 if (album) 0672 { 0673 // Get all deleted albums 0674 0675 CoreDbAccess access; 0676 CoreDbTransaction transaction(&access); 0677 0678 QList<int> albumsToDelete; 0679 QList<qlonglong> imagesToRemove; 0680 0681 addAlbumChildrenToList(albumsToDelete, album); 0682 0683 Q_FOREACH (int albumId, albumsToDelete) 0684 { 0685 imagesToRemove << access.db()->getItemIDsInAlbum(albumId); 0686 } 0687 0688 Q_FOREACH (const qlonglong& removeId, imagesToRemove) 0689 { 0690 ItemInfo info(removeId); 0691 0692 if (info.isNull()) 0693 { 0694 continue; 0695 } 0696 0697 const QList<qlonglong>& imageIdsFrom = access.db()-> 0698 getImagesRelatedFrom(info.id(), DatabaseRelation::DerivedFrom); 0699 0700 access.db()->removeAllImageRelationsFrom(info.id(), 0701 DatabaseRelation::Grouped); 0702 0703 if (operation == IOJobData::Trash) 0704 { 0705 access.db()->removeItems(QList<qlonglong>() << info.id(), 0706 QList<int>() << info.albumId()); 0707 } 0708 else 0709 { 0710 access.db()->removeItemsPermanently(QList<qlonglong>() << info.id(), 0711 QList<int>() << info.albumId()); 0712 } 0713 0714 Q_FOREACH (const qlonglong& id, imageIdsFrom) 0715 { 0716 ItemScanner::resolveImageHistory(id); 0717 ItemScanner::tagItemHistoryGraph(id); 0718 } 0719 } 0720 0721 Q_FOREACH (int albumId, albumsToDelete) 0722 { 0723 if (operation == IOJobData::Trash) 0724 { 0725 access.db()->makeStaleAlbum(albumId); 0726 } 0727 else 0728 { 0729 access.db()->deleteAlbum(albumId); 0730 } 0731 } 0732 } 0733 else 0734 { 0735 ItemInfo info = data->findItemInfo(url); 0736 0737 if (!info.isNull()) 0738 { 0739 CoreDbAccess access; 0740 0741 const QList<qlonglong>& imageIdsFrom = access.db()-> 0742 getImagesRelatedFrom(info.id(), DatabaseRelation::DerivedFrom); 0743 0744 access.db()->removeAllImageRelationsFrom(info.id(), 0745 DatabaseRelation::Grouped); 0746 0747 if (operation == IOJobData::Trash) 0748 { 0749 access.db()->removeItems(QList<qlonglong>() << info.id(), 0750 QList<int>() << info.albumId()); 0751 } 0752 else 0753 { 0754 access.db()->removeItemsPermanently(QList<qlonglong>() << info.id(), 0755 QList<int>() << info.albumId()); 0756 } 0757 0758 Q_FOREACH (const qlonglong& id, imageIdsFrom) 0759 { 0760 ItemScanner::resolveImageHistory(id); 0761 ItemScanner::tagItemHistoryGraph(id); 0762 } 0763 } 0764 } 0765 0766 break; 0767 } 0768 0769 case IOJobData::Rename: 0770 { 0771 ItemInfo info = data->findItemInfo(url); 0772 0773 if (!info.isNull()) 0774 { 0775 QString oldPath = url.toLocalFile(); 0776 QString newName = data->destUrl(url).fileName(); 0777 QString newPath = data->destUrl(url).toLocalFile(); 0778 0779 if (data->fileConflict() == IOJobData::Overwrite) 0780 { 0781 ThumbsDbAccess().db()->removeByFilePath(newPath); 0782 LoadingCacheInterface::fileChanged(newPath, false); 0783 CoreDbAccess().db()->deleteItem(info.albumId(), newName); 0784 } 0785 0786 ThumbsDbAccess().db()->renameByFilePath(oldPath, newPath); 0787 0788 // Remove old thumbnails and images from the cache 0789 0790 LoadingCacheInterface::fileChanged(oldPath, false); 0791 0792 // Rename in ItemInfo and database 0793 0794 info.setName(newName); 0795 } 0796 0797 break; 0798 } 0799 0800 case IOJobData::Restore: 0801 { 0802 ItemInfo info = ScanController::instance()->scannedInfo(url.toLocalFile()); 0803 0804 if (!info.isNull()) 0805 { 0806 ItemScanner::resolveImageHistory(info.id()); 0807 ItemScanner::tagItemHistoryGraph(info.id()); 0808 } 0809 0810 break; 0811 } 0812 0813 default: 0814 { 0815 break; 0816 } 0817 } 0818 0819 ProgressItem* const item = getProgressItem(data); 0820 0821 if (item) 0822 { 0823 item->advance(1); 0824 } 0825 } 0826 0827 void DIO::updateAlbumDate(int albumID) 0828 { 0829 QDate newAlbumDate; 0830 MetaEngineSettingsContainer settings = MetaEngineSettings::instance()->settings(); 0831 0832 if (settings.albumDateFrom == MetaEngineSettingsContainer::OldestItemDate) 0833 { 0834 newAlbumDate = CoreDbAccess().db()->getAlbumLowestDate(albumID); 0835 } 0836 else if (settings.albumDateFrom == MetaEngineSettingsContainer::NewestItemDate) 0837 { 0838 newAlbumDate = CoreDbAccess().db()->getAlbumHighestDate(albumID); 0839 } 0840 else if (settings.albumDateFrom == MetaEngineSettingsContainer::AverageDate) 0841 { 0842 newAlbumDate = CoreDbAccess().db()->getAlbumAverageDate(albumID); 0843 } 0844 0845 if (newAlbumDate.isValid()) 0846 { 0847 CoreDbAccess().db()->setAlbumDate(albumID, newAlbumDate); 0848 } 0849 } 0850 0851 QString DIO::getItemString(IOJobData* const data) const 0852 { 0853 switch (data->operation()) 0854 { 0855 case IOJobData::CopyAlbum: 0856 { 0857 return i18n("Copy Album"); 0858 } 0859 0860 case IOJobData::CopyImage: 0861 { 0862 return i18n("Copy Images"); 0863 } 0864 0865 case IOJobData::CopyFiles: 0866 case IOJobData::CopyToExt: 0867 { 0868 return i18n("Copy Files"); 0869 } 0870 0871 case IOJobData::MoveAlbum: 0872 { 0873 return i18n("Move Album"); 0874 } 0875 0876 case IOJobData::MoveImage: 0877 { 0878 return i18n("Move Images"); 0879 } 0880 0881 case IOJobData::MoveFiles: 0882 { 0883 return i18n("Move Files"); 0884 } 0885 0886 case IOJobData::Delete: 0887 { 0888 return i18n("Delete"); 0889 } 0890 0891 case IOJobData::Trash: 0892 { 0893 return i18n("Trash"); 0894 } 0895 0896 case IOJobData::Restore: 0897 { 0898 return i18n("Restore Trash"); 0899 } 0900 0901 case IOJobData::Empty: 0902 { 0903 return i18n("Empty Trash"); 0904 } 0905 0906 default: 0907 { 0908 break; 0909 } 0910 } 0911 0912 return QString(); 0913 } 0914 0915 ProgressItem* DIO::getProgressItem(IOJobData* const data) const 0916 { 0917 QString itemId = data->getProgressId(); 0918 0919 if (itemId.isEmpty()) 0920 { 0921 return nullptr; 0922 } 0923 0924 return ProgressManager::instance()->findItembyId(itemId); 0925 } 0926 0927 void DIO::slotCancel(ProgressItem* item) 0928 { 0929 if (item) 0930 { 0931 item->setComplete(); 0932 } 0933 } 0934 0935 void DIO::slotTrashCounterMap(const QMap<QString, int>& counterMap) 0936 { 0937 { 0938 QMutexLocker locker(&m_trashCounterMutex); 0939 0940 m_trashCounterMap = counterMap; 0941 } 0942 0943 Q_EMIT signalTrashCounters(); 0944 } 0945 0946 void DIO::addAlbumChildrenToList(QList<int>& list, Album* const album) 0947 { 0948 // simple recursive helper function 0949 0950 if (album) 0951 { 0952 if (!list.contains(album->id())) 0953 { 0954 list.append(album->id()); 0955 } 0956 0957 AlbumIterator it(album); 0958 0959 while (it.current()) 0960 { 0961 addAlbumChildrenToList(list, *it); 0962 ++it; 0963 } 0964 } 0965 } 0966 0967 } // namespace Digikam 0968 0969 #include "moc_dio.cpp"