diff --git a/libs/iojobs/iojob.cpp b/libs/iojobs/iojob.cpp index de5be0097f..3f348736c8 100644 --- a/libs/iojobs/iojob.cpp +++ b/libs/iojobs/iojob.cpp @@ -1,388 +1,395 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2015-06-15 * Description : IO Jobs for file systems jobs * * Copyright (C) 2015 by Mohamed Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "iojob.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "dtrash.h" #include "coredb.h" #include "coredbaccess.h" #include "collectionmanager.h" #include "albummanager.h" namespace Digikam { IOJob::IOJob() { } // -------------------------------------------- CopyJob::CopyJob(const QUrl& src, const QUrl& dest, bool isMove) { m_src = src; m_dest = dest; m_isMove = isMove; } bool CopyJob::copyFolderRecursively(const QString& srcPath, const QString& dstPath) { QDir srcDir(srcPath); QString newCopyPath = dstPath + QLatin1Char('/') + srcDir.dirName(); if (!srcDir.mkpath(newCopyPath)) { return false; } foreach (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Files)) { QString copyPath = newCopyPath + QLatin1Char('/') + fileInfo.fileName(); if (!QFile::copy(fileInfo.filePath(), copyPath)) return false; } foreach (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { copyFolderRecursively(fileInfo.filePath(), newCopyPath); } return true; } void CopyJob::run() { QFileInfo srcInfo(m_src.toLocalFile()); QDir dstDir(m_dest.toLocalFile()); if (!srcInfo.exists()) { emit error(i18n("File/Folder %1 does not exist anymore", srcInfo.baseName())); emit signalDone(); return; } if (!dstDir.exists()) { emit error(i18n("Album %1 does not exist anymore", dstDir.dirName())); emit signalDone(); return; } // Checking if there is a file with the same name in destination folder QString destenationName = srcInfo.isFile() ? srcInfo.fileName() : srcInfo.dir().dirName(); QString destenation = dstDir.path() + QLatin1Char('/') + destenationName; QFileInfo fileInfoForDestination(destenation); if (fileInfoForDestination.exists()) { emit error(i18n("A file or folder named %1 already exists in %2", srcInfo.baseName(), QDir::toNativeSeparators(dstDir.path()))); emit signalDone(); return; } if (m_isMove) { if (srcInfo.isDir()) { QDir srcDir(srcInfo.filePath()); if (!srcDir.rename(srcDir.path(), destenation)) { // If QDir::rename fails, try copy and remove. if (!copyFolderRecursively(srcDir.path(), dstDir.path())) { emit error(i18n("Could not move folder %1 to album %2", QDir::toNativeSeparators(srcDir.path()), QDir::toNativeSeparators(dstDir.path()))); } else if (!srcDir.removeRecursively()) { emit error(i18n("Could not move folder %1 to album %2. " "The folder %1 was copied as well to album %2", QDir::toNativeSeparators(srcDir.path()), QDir::toNativeSeparators(dstDir.path()))); } } } else { QFile srcFile(srcInfo.filePath()); if (!srcFile.rename(destenation)) { emit error(i18n("Could not move file %1 to album %2", - srcInfo.filePath(), QDir::toNativeSeparators(dstDir.path()))); + srcInfo.filePath(), + QDir::toNativeSeparators(dstDir.path()))); } } } else { if (srcInfo.isDir()) { QDir srcDir(srcInfo.filePath()); if (!copyFolderRecursively(srcDir.path(), dstDir.path())) { emit error(i18n("Could not copy folder %1 to album %2", QDir::toNativeSeparators(srcDir.path()), QDir::toNativeSeparators(dstDir.path()))); } } else { if (!QFile::copy(srcInfo.filePath(), destenation)) { emit error(i18n("Could not copy file %1 to album %2", QDir::toNativeSeparators(srcInfo.path()), QDir::toNativeSeparators(dstDir.path()))); } } } emit signalDone(); } // -------------------------------------------- DeleteJob::DeleteJob(const QUrl& srcToDelete, bool useTrash, bool markAsObsolete) { m_srcToDelete = srcToDelete; m_useTrash = useTrash; m_markAsObsolete = markAsObsolete; } void DeleteJob::run() { QFileInfo fileInfo(m_srcToDelete.toLocalFile()); qCDebug(DIGIKAM_IOJOB_LOG) << "DELETING: " << fileInfo.filePath() << "\n" << "FILE EXISTS? " << fileInfo.exists() << "\n" << "IS TO TRASH? " << m_useTrash; if (!fileInfo.exists()) { emit error(i18n("File/Folder %1 does not exist", QDir::toNativeSeparators(fileInfo.filePath()))); emit signalDone(); return; } if (m_useTrash) { if (fileInfo.isDir()) { if (!DTrash::deleteDirRecursivley(m_srcToDelete.toLocalFile())) { emit error(i18n("Couldn't move folder %1 to collection trash", QDir::toNativeSeparators(fileInfo.path()))); } } else { if (!DTrash::deleteImage(m_srcToDelete.toLocalFile())) { emit error(i18n("Couldn't move image %1 to collection trash", QDir::toNativeSeparators(fileInfo.filePath()))); } } } else { if (fileInfo.isDir()) { QDir dir(fileInfo.filePath()); if (!dir.removeRecursively()) { emit error(i18n("Album %1 could not be removed", QDir::toNativeSeparators(fileInfo.path()))); } else if (m_markAsObsolete) { CoreDbAccess access; // If the images in the directory should be marked as obsolete // get all files recursively and remove all image information // for which the file path leads to an image id. QList imageIds; QDirIterator iter(dir); + while (iter.hasNext()) { // get the next path and advance the iterator QString filePath = iter.next(); // Use the file info to get the file type QFileInfo info = iter.fileInfo(); + if (info.isFile()) { qlonglong imageId = getItemFromUrl(QUrl::fromLocalFile(filePath)); + if (imageId != -1) { imageIds << imageId; } } } // Mark all image ids as obsolete. foreach(qlonglong imageId, imageIds) { access.db()->setItemStatus(imageId, DatabaseItem::Status::Obsolete); } } } else { QFile file(fileInfo.filePath()); if (!file.remove()) { emit error(i18n("Image %1 could not be removed", QDir::toNativeSeparators(fileInfo.filePath()))); } else if (m_markAsObsolete) { // Mark the image info of the removed file as obsolete CoreDbAccess access; qlonglong imageId = getItemFromUrl(QUrl::fromLocalFile(fileInfo.filePath())); + if (imageId != -1) { access.db()->setItemStatus(imageId, DatabaseItem::Status::Obsolete); } } } } emit signalDone(); } qlonglong DeleteJob::getItemFromUrl(const QUrl& url) { - QString fileName = url.fileName(); + QString fileName = url.fileName(); // Get the album path, i.e. collection + album. For this, // get the n leftmost characters where n is the complete path without the size of the filename QString completePath = url.toLocalFile(); QString albumPath = CollectionManager::instance()->album(completePath); - qlonglong imageId = -1; + qlonglong imageId = -1; // Get the album and with this the image id of the image to trash. - PAlbum* pAlbum = AlbumManager::instance()->findPAlbum(QUrl::fromLocalFile(completePath)); + PAlbum* const pAlbum = AlbumManager::instance()->findPAlbum(QUrl::fromLocalFile(completePath)); + if (pAlbum) { imageId = CoreDbAccess().db()->getItemFromAlbum(pAlbum->id(),fileName); } + return imageId; } // -------------------------------------------- RenameFileJob::RenameFileJob(const QUrl& srcToRename, const QUrl& newUrl) { m_srcToRename = srcToRename; m_newUrl = newUrl; } void RenameFileJob::run() { if (m_newUrl.isEmpty()) { emit signalDone(); return; } qCDebug(DIGIKAM_IOJOB_LOG) << "Destination Url: " << m_newUrl << "\n" << "Destination Url path: " << m_newUrl.toLocalFile(); if (QFileInfo(m_newUrl.toLocalFile()).exists()) { qCDebug(DIGIKAM_IOJOB_LOG) << "File with the same name exists!"; emit error(i18n("Image with the same name %1 already there", QDir::toNativeSeparators(m_newUrl.toLocalFile()))); emit signalDone(); return; } QFile file(m_srcToRename.toLocalFile()); qCDebug(DIGIKAM_IOJOB_LOG) << "Trying to rename" << m_srcToRename.toLocalFile() << "\nto " << m_newUrl.toLocalFile(); if (!file.rename(m_newUrl.toLocalFile())) { qCDebug(DIGIKAM_IOJOB_LOG) << "File couldn't be renamed!"; emit error(i18n("Image %1 could not be renamed", QDir::toNativeSeparators(m_srcToRename.toLocalFile()))); emit signalDone(); return; } emit signalRenamed(m_srcToRename, m_newUrl); emit signalDone(); } // ---------------------------------------------- DTrashItemsListingJob::DTrashItemsListingJob(const QString& collectionPath) { m_collectionPath = collectionPath; } void DTrashItemsListingJob::run() { DTrashItemInfo itemInfo; QString collectionTrashFilesPath = m_collectionPath + QLatin1Char('/') + DTrash::TRASH_FOLDER + QLatin1Char('/') + DTrash::FILES_FOLDER; qCDebug(DIGIKAM_IOJOB_LOG) << "collectionTrashFilesPath: " << collectionTrashFilesPath; QDir filesDir(collectionTrashFilesPath); foreach (const QFileInfo& fileInfo, filesDir.entryInfoList(QDir::Files)) { qCDebug(DIGIKAM_IOJOB_LOG) << "file in trash: " << fileInfo.filePath(); itemInfo.trashPath = fileInfo.filePath(); DTrash::extractJsonForItem(m_collectionPath, fileInfo.baseName(), itemInfo); emit trashItemInfo(itemInfo); } emit signalDone(); } } // namespace Digikam diff --git a/libs/iojobs/iojob.h b/libs/iojobs/iojob.h index 49c207d752..50edfa70fc 100644 --- a/libs/iojobs/iojob.h +++ b/libs/iojobs/iojob.h @@ -1,150 +1,152 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2015-06-15 * Description : IO Jobs for file systems jobs * * Copyright (C) 2015 by Mohamed Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef IOJOB_H #define IOJOB_H // Qt includes #include // Local includes #include "dtrashiteminfo.h" #include "dtrash.h" #include "actionthreadbase.h" #include "digikam_export.h" namespace Digikam { class ImageInfo; class DIGIKAM_EXPORT IOJob : public ActionJob { Q_OBJECT protected: IOJob(); Q_SIGNALS: void error(const QString& errMsg); }; // --------------------------------------- class DIGIKAM_EXPORT CopyJob : public IOJob { Q_OBJECT public: CopyJob(const QUrl& src, const QUrl& dest, bool isMove); protected: void run(); bool copyFolderRecursively(const QString& srcPath, const QString& dstPath); private: QUrl m_src; QUrl m_dest; bool m_isMove; }; // --------------------------------------- class DIGIKAM_EXPORT DeleteJob : public IOJob { Q_OBJECT public: DeleteJob(const QUrl& srcToDelete, bool useTrash, bool markAsObsolete=false); protected: void run(); private: qlonglong getItemFromUrl(const QUrl& url); +private: + QUrl m_srcToDelete; bool m_useTrash; bool m_markAsObsolete; }; // --------------------------------------- class DIGIKAM_EXPORT RenameFileJob : public IOJob { Q_OBJECT public: RenameFileJob(const QUrl& srcToRename, const QUrl& newName); Q_SIGNALS: void signalRenamed(const QUrl& oldUrl, const QUrl& newUrl); protected: void run(); private: QUrl m_srcToRename; QUrl m_newUrl; }; // ---------------------------------------------- class DIGIKAM_EXPORT DTrashItemsListingJob : public IOJob { Q_OBJECT public: DTrashItemsListingJob(const QString& collectionPath); Q_SIGNALS: void trashItemInfo(const DTrashItemInfo& info); private: void run(); private: QString m_collectionPath; }; } // namespace Digikam #endif // IOJOB_H diff --git a/libs/iojobs/iojobsthread.cpp b/libs/iojobs/iojobsthread.cpp index d2daea32f3..2154b30baf 100644 --- a/libs/iojobs/iojobsthread.cpp +++ b/libs/iojobs/iojobsthread.cpp @@ -1,313 +1,313 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2015-06-15 * Description : IO Jobs thread for file system jobs * * Copyright (C) 2015 by Mohamed Anwer * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // Qt includes #include #include // Local includes #include "iojobsthread.h" #include "iojob.h" #include "digikam_debug.h" #include "dtrashiteminfo.h" #include "coredb.h" #include "coredbaccess.h" namespace Digikam { class IOJobsThread::Private { public: Private() : jobsCount(0), isCanceled(false), isRenameThread(false), keepErrors(true) { } int jobsCount; bool isCanceled; bool isRenameThread; QUrl oldUrl; bool keepErrors; QList errorsList; }; IOJobsThread::IOJobsThread(QObject* const parent) : ActionThreadBase(parent), d(new Private) { } IOJobsThread::~IOJobsThread() { delete d; } void IOJobsThread::copy(const QList& srcFiles, const QUrl destAlbum) { ActionJobCollection collection; foreach (const QUrl& url, srcFiles) { CopyJob* const j = new CopyJob(url, destAlbum, false); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::move(const QList& srcFiles, const QUrl destAlbum) { ActionJobCollection collection; foreach (const QUrl& url, srcFiles) { CopyJob* const j = new CopyJob(url, destAlbum, true); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::del(const QList& srcsToDelete, bool useTrash) { ActionJobCollection collection; foreach (const QUrl& url, srcsToDelete) { DeleteJob* const j = new DeleteJob(url, useTrash); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::deleteFiles(const QList& srcsToDelete, bool useTrash) { ActionJobCollection collection; foreach (const QUrl& url, srcsToDelete) { DeleteJob* const j = new DeleteJob(url, useTrash,true); connectOneJob(j); collection.insert(j, 0); d->jobsCount++; } appendJobs(collection); } void IOJobsThread::listDTrashItems(const QString& collectionPath) { ActionJobCollection collection; DTrashItemsListingJob* const j = new DTrashItemsListingJob(collectionPath); connect(j, SIGNAL(trashItemInfo(DTrashItemInfo)), this, SIGNAL(collectionTrashItemInfo(DTrashItemInfo))); connect(j, SIGNAL(signalDone()), this, SIGNAL(finished())); collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } void IOJobsThread::restoreDTrashItems(const DTrashItemInfoList& items) { QList listOfJsonFilesToRemove; QList listOfUsedUrls; foreach (const DTrashItemInfo& item, items) { QUrl srcToRename = QUrl::fromLocalFile(item.trashPath); QUrl newName = getAvailableQUrlToRestoreInCollection(item.collectionPath, listOfUsedUrls); listOfUsedUrls << newName; QFileInfo fi(item.collectionPath); if (!fi.dir().exists()) { fi.dir().mkpath(fi.dir().path()); } renameFile(srcToRename, newName); listOfJsonFilesToRemove << QUrl::fromLocalFile(item.jsonFilePath); } del(listOfJsonFilesToRemove, false); } void IOJobsThread::deleteDTrashItems(const DTrashItemInfoList& items) { QList urlsToDelete; - CoreDbAccess access; + foreach (const DTrashItemInfo& item, items) { urlsToDelete << QUrl::fromLocalFile(item.trashPath); urlsToDelete << QUrl::fromLocalFile(item.jsonFilePath); // Set the status of the image id to obsolete, i.e. to remove. access.db()->setItemStatus(item.imageId,DatabaseItem::Status::Obsolete); } del(urlsToDelete, false); } void IOJobsThread::renameFile(const QUrl& srcToRename, const QUrl& newName) { ActionJobCollection collection; RenameFileJob* const j = new RenameFileJob(srcToRename, newName); connectOneJob(j); connect(j, SIGNAL(signalRenamed(QUrl,QUrl)), this, SIGNAL(renamed(QUrl,QUrl))); d->isRenameThread = true; d->oldUrl = srcToRename; collection.insert(j, 0); d->jobsCount++; appendJobs(collection); } bool IOJobsThread::isRenameThread() { return d->isRenameThread; } QUrl IOJobsThread::oldUrlToRename() { return d->oldUrl; } void IOJobsThread::cancel() { d->isCanceled = true; ActionThreadBase::cancel(); } bool IOJobsThread::isCanceled() { return d->isCanceled; } bool IOJobsThread::hasErrors() { return !d->errorsList.isEmpty(); } void IOJobsThread::setKeepErrors(bool keepErrors) { d->keepErrors = keepErrors; } bool IOJobsThread::isKeepingErrors() { return d->keepErrors; } QList& IOJobsThread::errorsList() { return d->errorsList; } void IOJobsThread::connectOneJob(IOJob* const j) { connect(j, SIGNAL(error(QString)), this, SLOT(error(QString))); connect(j, SIGNAL(signalDone()), this, SLOT(oneJobFinished())); } QUrl IOJobsThread::getAvailableQUrlToRestoreInCollection(const QString& fileColPath, QList& usedUrls, int version) { QFileInfo fileInfo(fileColPath); if (version != 0) { QString dir = fileInfo.dir().path() + QLatin1Char('/'); - QString baseName = fileInfo.baseName() + QString::number(version); - QString suffix = QLatin1String(".") + fileInfo.completeSuffix(); + QString baseName = fileInfo.baseName() + QString::number(version); + QString suffix = QLatin1String(".") + fileInfo.completeSuffix(); fileInfo.setFile(dir + baseName + suffix); } QUrl url = QUrl::fromLocalFile(fileInfo.filePath()); qCDebug(DIGIKAM_IOJOB_LOG) << "URL USED BEFORE: " << usedUrls.contains(url); if (!fileInfo.exists() && !usedUrls.contains(url)) { return url; } else { return getAvailableQUrlToRestoreInCollection(fileColPath, usedUrls, ++version); } } void IOJobsThread::oneJobFinished() { d->jobsCount--; if (d->jobsCount == 0) { emit finished(); qCDebug(DIGIKAM_IOJOB_LOG) << "Thread Finished"; } } void IOJobsThread::error(const QString& errString) { d->errorsList.append(errString); } } // namespace Digikam